From 12e34c39e946c8267d828df89f277ab18240cb72 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 10 Oct 2017 13:59:32 +0300
Subject: [PATCH 001/337] fixes

---
 Consumption/QueueConsumer.php | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 561314d..112f46b 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -258,14 +258,19 @@ protected function doConsume(ExtensionInterface $extension, Context $context)
             throw new ConsumptionInterruptedException();
         }
 
-        if ($context->getPsrMessage() || $message = $consumer->receive($this->receiveTimeout)) {
+        $message = $context->getPsrMessage();
+        if (false == $message) {
+            if ($message = $consumer->receive($this->receiveTimeout)) {
+                $context->setPsrMessage($message);
+            }
+        }
+
+        if ($message) {
             $logger->info('Message received from the queue: '.$context->getPsrQueue()->getQueueName());
             $logger->debug('Headers: {headers}', ['headers' => new VarExport($message->getHeaders())]);
             $logger->debug('Properties: {properties}', ['properties' => new VarExport($message->getProperties())]);
             $logger->debug('Payload: {payload}', ['payload' => new VarExport($message->getBody())]);
 
-            $context->setPsrMessage($message);
-
             $extension->onPreReceived($context);
             if (!$context->getResult()) {
                 $result = $processor->process($message, $this->psrContext);

From 42ab38a01e003420b70cf62fdfe87e285af9df7e Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 10 Oct 2017 15:32:38 +0300
Subject: [PATCH 002/337] [consumption] fix echange cound not be decalred, no
 channel.

---
 Consumption/QueueConsumer.php | 27 +++++++++++++++------------
 1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 112f46b..53c74db 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -3,7 +3,6 @@
 namespace Enqueue\Consumption;
 
 use Enqueue\AmqpExt\AmqpConsumer;
-use Enqueue\AmqpExt\AmqpContext;
 use Enqueue\Consumption\Exception\ConsumptionInterruptedException;
 use Enqueue\Consumption\Exception\InvalidArgumentException;
 use Enqueue\Consumption\Exception\LogicException;
@@ -146,6 +145,10 @@ public function bind($queue, $processor)
      */
     public function consume(ExtensionInterface $runtimeExtension = null)
     {
+        if (empty($this->boundProcessors)) {
+            throw new \LogicException('There is nothing to consume. It is required to bind something before calling consume method.');
+        }
+
         /** @var PsrConsumer[] $consumers */
         $consumers = [];
         /** @var PsrQueue $queue */
@@ -153,12 +156,6 @@ public function consume(ExtensionInterface $runtimeExtension = null)
             $consumers[$queue->getQueueName()] = $this->psrContext->createConsumer($queue);
         }
 
-        foreach ($consumers as $consumer) {
-            if ($consumer instanceof AmqpConsumer) {
-                $consumer->basicConsume($this->receiveTimeout, null);
-            }
-        }
-
         $extension = $this->extension ?: new ChainExtension([]);
         if ($runtimeExtension) {
             $extension = new ChainExtension([$extension, $runtimeExtension]);
@@ -170,13 +167,19 @@ public function consume(ExtensionInterface $runtimeExtension = null)
         $logger = $context->getLogger() ?: new NullLogger();
         $logger->info('Start consuming');
 
+        $amqpConsumer = null;
+        foreach ($consumers as $consumer) {
+            if ($consumer instanceof AmqpConsumer) {
+                $consumer->basicConsume($this->receiveTimeout, null);
+
+                $amqpConsumer = $consumer;
+            }
+        }
+
         while (true) {
             try {
-                if ($this->psrContext instanceof AmqpContext) {
-                    reset($consumers);
-                    /** @var AmqpConsumer $consumer */
-                    $consumer = current($consumers);
-                    $consumer->basicConsume($this->receiveTimeout, function (AmqpMessage $message, AmqpConsumer $consumer) use ($extension, $logger) {
+                if ($amqpConsumer) {
+                    $amqpConsumer->basicConsume($this->receiveTimeout, function (AmqpMessage $message, AmqpConsumer $consumer) use ($extension, $logger) {
                         $currentProcessor = null;
 
                         /** @var PsrQueue $queue */

From 5cf6bbe4da3f7065c479cf0b634b6b6e94b35920 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 10 Oct 2017 15:55:51 +0300
Subject: [PATCH 003/337] [client] Add --skip option to consume command.

---
 Symfony/Client/ConsumeMessagesCommand.php     | 12 +++-
 .../Client/ConsumeMessagesCommandTest.php     | 62 ++++++++++++++++++-
 2 files changed, 71 insertions(+), 3 deletions(-)

diff --git a/Symfony/Client/ConsumeMessagesCommand.php b/Symfony/Client/ConsumeMessagesCommand.php
index 2ec98e2..a3817b3 100644
--- a/Symfony/Client/ConsumeMessagesCommand.php
+++ b/Symfony/Client/ConsumeMessagesCommand.php
@@ -4,6 +4,7 @@
 
 use Enqueue\Client\DelegateProcessor;
 use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Meta\QueueMeta;
 use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Extension\LoggerExtension;
@@ -13,6 +14,7 @@
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputArgument;
 use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Logger\ConsoleLogger;
 use Symfony\Component\Console\Output\OutputInterface;
 
@@ -78,6 +80,7 @@ protected function configure()
                 'By default it connects to default queue. '.
                 'It select an appropriate message processor based on a message headers')
             ->addArgument('client-queue-names', InputArgument::IS_ARRAY, 'Queues to consume messages from')
+            ->addOption('skip', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Queues to skip consumption of messages from', [])
         ;
     }
 
@@ -94,7 +97,14 @@ protected function execute(InputInterface $input, OutputInterface $output)
                 $queueMetas[] = $this->queueMetaRegistry->getQueueMeta($clientQueueName);
             }
         } else {
-            $queueMetas = $this->queueMetaRegistry->getQueuesMeta();
+            /** @var QueueMeta[] $queueMetas */
+            $queueMetas = iterator_to_array($this->queueMetaRegistry->getQueuesMeta());
+
+            foreach ($queueMetas as $index => $queueMeta) {
+                if (in_array($queueMeta->getClientName(), $input->getOption('skip'), true)) {
+                    unset($queueMetas[$index]);
+                }
+            }
         }
 
         foreach ($queueMetas as $queueMeta) {
diff --git a/Tests/Symfony/Client/ConsumeMessagesCommandTest.php b/Tests/Symfony/Client/ConsumeMessagesCommandTest.php
index b6017ab..e87f4df 100644
--- a/Tests/Symfony/Client/ConsumeMessagesCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeMessagesCommandTest.php
@@ -61,16 +61,17 @@ public function testShouldHaveExpectedOptions()
 
         $options = $command->getDefinition()->getOptions();
 
-        $this->assertCount(6, $options);
+        $this->assertCount(7, $options);
         $this->assertArrayHasKey('memory-limit', $options);
         $this->assertArrayHasKey('message-limit', $options);
         $this->assertArrayHasKey('time-limit', $options);
         $this->assertArrayHasKey('setup-broker', $options);
         $this->assertArrayHasKey('idle-timeout', $options);
         $this->assertArrayHasKey('receive-timeout', $options);
+        $this->assertArrayHasKey('skip', $options);
     }
 
-    public function testShouldHaveExpectedAttributes()
+    public function testShouldHaveExpectedArguments()
     {
         $command = new ConsumeMessagesCommand(
             $this->createQueueConsumerMock(),
@@ -171,6 +172,63 @@ public function testShouldExecuteConsumptionAndUseCustomClientDestinationName()
         ]);
     }
 
+    public function testShouldSkipQueueConsumptionAndUseCustomClientDestinationName()
+    {
+        $queue = new NullQueue('');
+
+        $processor = $this->createDelegateProcessorMock();
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->never())
+            ->method('close')
+        ;
+
+        $consumer = $this->createQueueConsumerMock();
+        $consumer
+            ->expects($this->exactly(2))
+            ->method('bind')
+        ;
+        $consumer
+            ->expects($this->once())
+            ->method('consume')
+            ->with($this->isInstanceOf(ChainExtension::class))
+        ;
+
+        $queueMetaRegistry = $this->createQueueMetaRegistry([
+            'fooQueue' => [
+                'transportName' => 'fooTransportQueue',
+            ],
+            'barQueue' => [
+                'transportName' => 'barTransportQueue',
+            ],
+            'ololoQueue' => [
+                'transportName' => 'ololoTransportQueue',
+            ],
+        ]);
+
+        $driver = $this->createDriverMock();
+        $driver
+            ->expects($this->at(0))
+            ->method('createQueue')
+            ->with('fooQueue')
+            ->willReturn($queue)
+        ;
+        $driver
+            ->expects($this->at(1))
+            ->method('createQueue')
+            ->with('ololoQueue')
+            ->willReturn($queue)
+        ;
+
+        $command = new ConsumeMessagesCommand($consumer, $processor, $queueMetaRegistry, $driver);
+
+        $tester = new CommandTester($command);
+        $tester->execute([
+            '--skip' => ['barQueue'],
+        ]);
+    }
+
     /**
      * @param array $destinationNames
      *

From 4ff2b48ef216201e240f97294e9fe169dabb5439 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 10 Oct 2017 20:53:48 +0300
Subject: [PATCH 004/337] [amqp-bunny] implement basic consume related methods.

---
 Consumption/QueueConsumer.php | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 53c74db..fb42e36 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -7,6 +7,7 @@
 use Enqueue\Consumption\Exception\InvalidArgumentException;
 use Enqueue\Consumption\Exception\LogicException;
 use Enqueue\Util\VarExport;
+use Interop\Amqp\AmqpContext;
 use Interop\Amqp\AmqpMessage;
 use Interop\Queue\PsrConsumer;
 use Interop\Queue\PsrContext;
@@ -167,19 +168,10 @@ public function consume(ExtensionInterface $runtimeExtension = null)
         $logger = $context->getLogger() ?: new NullLogger();
         $logger->info('Start consuming');
 
-        $amqpConsumer = null;
-        foreach ($consumers as $consumer) {
-            if ($consumer instanceof AmqpConsumer) {
-                $consumer->basicConsume($this->receiveTimeout, null);
-
-                $amqpConsumer = $consumer;
-            }
-        }
-
         while (true) {
             try {
-                if ($amqpConsumer) {
-                    $amqpConsumer->basicConsume($this->receiveTimeout, function (AmqpMessage $message, AmqpConsumer $consumer) use ($extension, $logger) {
+                if ($this->psrContext instanceof AmqpContext) {
+                    $callback = function (AmqpMessage $message, AmqpConsumer $consumer) use ($extension, $logger) {
                         $currentProcessor = null;
 
                         /** @var PsrQueue $queue */
@@ -203,7 +195,15 @@ public function consume(ExtensionInterface $runtimeExtension = null)
                         $this->doConsume($extension, $context);
 
                         return true;
-                    });
+                    };
+
+                    foreach ($consumers as $consumer) {
+                        /* @var AmqpConsumer $consumer */
+
+                        $this->psrContext->subscribe($consumer, $callback);
+                    }
+
+                    $this->psrContext->consume($this->receiveTimeout);
                 } else {
                     /** @var PsrQueue $queue */
                     foreach ($this->boundProcessors as list($queue, $processor)) {

From b4d45f69ee57caf4c82294f2e4e2deb43b2f4c78 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Wed, 11 Oct 2017 10:05:17 +0300
Subject: [PATCH 005/337] [amqp-lib] Add basic consume support.

---
 Consumption/QueueConsumer.php | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index fb42e36..fca52e2 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -221,6 +221,14 @@ public function consume(ExtensionInterface $runtimeExtension = null)
             } catch (ConsumptionInterruptedException $e) {
                 $logger->info(sprintf('Consuming interrupted'));
 
+                if ($this->psrContext instanceof AmqpContext) {
+                    foreach ($consumers as $consumer) {
+                        /* @var AmqpConsumer $consumer */
+
+                        $this->psrContext->unsubscribe($consumer);
+                    }
+                }
+
                 $context->setExecutionInterrupted(true);
 
                 $extension->onInterrupted($context);

From 2a34f552f1f458515adeae0750469f36d0ff0877 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Wed, 11 Oct 2017 13:49:37 +0300
Subject: [PATCH 006/337] fix amqp+bunny dsn

---
 functions.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/functions.php b/functions.php
index fef445f..bd336fb 100644
--- a/functions.php
+++ b/functions.php
@@ -38,7 +38,7 @@ function dsn_to_connection_factory($dsn)
     if (class_exists(AmqpBunnyConnectionFactory::class)) {
         $map['amqp+lib'] = AmqpLibConnectionFactory::class;
     }
-    if (class_exists(AmqpLibConnectionFactory::class)) {
+    if (class_exists(AmqpBunnyConnectionFactory::class)) {
         $map['amqp+bunny'] = AmqpBunnyConnectionFactory::class;
     }
 

From 3ba8ba6559884d54af599acbb1e592b1c071d295 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Wed, 11 Oct 2017 14:56:31 +0300
Subject: [PATCH 007/337] Fix type error

Type error: Argument 2 passed to
Enqueue\Consumption\QueueConsumer::Enqueue
jmc_4       |   \Consumption\{closure}() must be an instance of
Enqueue\AmqpExt\AmqpConsume
jmc_4       |   r, instance of Enqueue\AmqpBunny\AmqpConsumer given
---
 Consumption/QueueConsumer.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index fca52e2..62a7103 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -2,11 +2,11 @@
 
 namespace Enqueue\Consumption;
 
-use Enqueue\AmqpExt\AmqpConsumer;
 use Enqueue\Consumption\Exception\ConsumptionInterruptedException;
 use Enqueue\Consumption\Exception\InvalidArgumentException;
 use Enqueue\Consumption\Exception\LogicException;
 use Enqueue\Util\VarExport;
+use Interop\Amqp\AmqpConsumer;
 use Interop\Amqp\AmqpContext;
 use Interop\Amqp\AmqpMessage;
 use Interop\Queue\PsrConsumer;

From f232a497ce159faae50a625a987c78835f5015d7 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 12 Oct 2017 17:34:25 +0300
Subject: [PATCH 008/337] Update functions.php

---
 functions.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/functions.php b/functions.php
index bd336fb..25d4438 100644
--- a/functions.php
+++ b/functions.php
@@ -35,7 +35,7 @@ function dsn_to_connection_factory($dsn)
     if (class_exists(AmqpExtConnectionFactory::class)) {
         $map['amqp+ext'] = AmqpExtConnectionFactory::class;
     }
-    if (class_exists(AmqpBunnyConnectionFactory::class)) {
+    if (class_exists(AmqpLibConnectionFactory::class)) {
         $map['amqp+lib'] = AmqpLibConnectionFactory::class;
     }
     if (class_exists(AmqpBunnyConnectionFactory::class)) {

From d4ca61931a383dc66a7e623dccf7ed7391ef82cf Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Sat, 14 Oct 2017 00:03:34 +0300
Subject: [PATCH 009/337] Introduce generic amqp\rabbitmq transport factories.

---
 Symfony/AmqpTransportFactory.php              | 174 ++++++++++
 Symfony/DefaultTransportFactory.php           |  19 +-
 Symfony/RabbitMqAmqpTransportFactory.php      |  69 ++++
 Tests/Symfony/AmqpTransportFactoryTest.php    | 298 ++++++++++++++++++
 Tests/Symfony/DefaultTransportFactoryTest.php |   6 +-
 .../RabbitMqAmqpTransportFactoryTest.php      | 139 ++++++++
 6 files changed, 686 insertions(+), 19 deletions(-)
 create mode 100644 Symfony/AmqpTransportFactory.php
 create mode 100644 Symfony/RabbitMqAmqpTransportFactory.php
 create mode 100644 Tests/Symfony/AmqpTransportFactoryTest.php
 create mode 100644 Tests/Symfony/RabbitMqAmqpTransportFactoryTest.php

diff --git a/Symfony/AmqpTransportFactory.php b/Symfony/AmqpTransportFactory.php
new file mode 100644
index 0000000..bef8dea
--- /dev/null
+++ b/Symfony/AmqpTransportFactory.php
@@ -0,0 +1,174 @@
+<?php
+
+namespace Enqueue\Symfony;
+
+use Enqueue\Client\Amqp\AmqpDriver;
+use Interop\Amqp\AmqpContext;
+use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+
+class AmqpTransportFactory implements TransportFactoryInterface, DriverFactoryInterface
+{
+    /**
+     * @var string
+     */
+    private $amqpConnectionFactoryClass;
+
+    /**
+     * @var string
+     */
+    private $name;
+
+    /**
+     * @param string $amqpConnectionFactoryClass
+     * @param string $name
+     */
+    public function __construct($amqpConnectionFactoryClass, $name = 'amqp')
+    {
+        $this->amqpConnectionFactoryClass = $amqpConnectionFactoryClass;
+        $this->name = $name;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addConfiguration(ArrayNodeDefinition $builder)
+    {
+        $builder
+            ->beforeNormalization()
+            ->ifString()
+                ->then(function ($v) {
+                    return ['dsn' => $v];
+                })
+            ->end()
+            ->children()
+                ->scalarNode('dsn')
+                    ->info('The connection to AMQP broker set as a string. Other parameters could be used as defaults')
+                ->end()
+                ->scalarNode('host')
+                    ->info('The host to connect too. Note: Max 1024 characters')
+                ->end()
+                ->scalarNode('port')
+                    ->info('Port on the host.')
+                ->end()
+                ->scalarNode('user')
+                    ->info('The user name to use. Note: Max 128 characters.')
+                ->end()
+                ->scalarNode('pass')
+                    ->info('Password. Note: Max 128 characters.')
+                ->end()
+                ->scalarNode('vhost')
+                    ->info('The virtual host on the host. Note: Max 128 characters.')
+                ->end()
+                ->floatNode('connection_timeout')
+                    ->min(0)
+                    ->info('Connection timeout. Note: 0 or greater seconds. May be fractional.')
+                ->end()
+                ->floatNode('read_timeout')
+                    ->min(0)
+                    ->info('Timeout in for income activity. Note: 0 or greater seconds. May be fractional.')
+                ->end()
+                ->floatNode('write_timeout')
+                    ->min(0)
+                    ->info('Timeout in for outcome activity. Note: 0 or greater seconds. May be fractional.')
+                ->end()
+                ->floatNode('heartbeat')
+                    ->min(0)
+                    ->info('How often to send heartbeat. 0 means off.')
+                ->end()
+                ->booleanNode('persisted')->end()
+                ->booleanNode('lazy')->end()
+                ->enumNode('receive_method')
+                    ->values(['basic_get', 'basic_consume'])
+                    ->info('The receive strategy to be used. We suggest to use basic_consume as it is more performant. Though you need AMQP extension 1.9.1 or higher')
+                ->end()
+                ->floatNode('qos_prefetch_size')
+                    ->min(0)
+                    ->info('The server will send a message in advance if it is equal to or smaller in size than the available prefetch size. May be set to zero, meaning "no specific limit"')
+                ->end()
+                ->floatNode('qos_prefetch_count')
+                    ->min(0)
+                    ->info('Specifies a prefetch window in terms of whole messages')
+                ->end()
+                ->booleanNode('qos_global')
+                    ->info('If "false" the QoS settings apply to the current channel only. If this field is "true", they are applied to the entire connection.')
+                ->end()
+                ->variableNode('driver_options')
+                    ->info('The options that are specific to the amqp transport you chose. For example amqp+lib have insist, keepalive, stream options. amqp+bunny has tcp_nodelay extra option.')
+                ->end()
+        ;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createConnectionFactory(ContainerBuilder $container, array $config)
+    {
+        if (array_key_exists('driver_options', $config) && is_array($config['driver_options'])) {
+            $driverOptions = $config['driver_options'];
+            unset($config['driver_options']);
+
+            $config = array_replace($driverOptions, $config);
+        }
+
+        $factory = new Definition($this->amqpConnectionFactoryClass);
+        $factory->setArguments([$config]);
+
+        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
+        $container->setDefinition($factoryId, $factory);
+
+        return $factoryId;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createContext(ContainerBuilder $container, array $config)
+    {
+        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
+
+        $context = new Definition(AmqpContext::class);
+        $context->setFactory([new Reference($factoryId), 'createContext']);
+
+        $contextId = sprintf('enqueue.transport.%s.context', $this->getName());
+        $container->setDefinition($contextId, $context);
+
+        return $contextId;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createDriver(ContainerBuilder $container, array $config)
+    {
+        $driver = new Definition(AmqpDriver::class);
+        $driver->setArguments([
+            new Reference(sprintf('enqueue.transport.%s.context', $this->getName())),
+            new Reference('enqueue.client.config'),
+            new Reference('enqueue.client.meta.queue_meta_registry'),
+        ]);
+
+        $driverId = sprintf('enqueue.client.%s.driver', $this->getName());
+        $container->setDefinition($driverId, $driver);
+
+        return $driverId;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * @return string
+     */
+    public function getAmqpConnectionFactoryClass()
+    {
+        return $this->amqpConnectionFactoryClass;
+    }
+}
diff --git a/Symfony/DefaultTransportFactory.php b/Symfony/DefaultTransportFactory.php
index 2e33444..5911dc4 100644
--- a/Symfony/DefaultTransportFactory.php
+++ b/Symfony/DefaultTransportFactory.php
@@ -2,12 +2,6 @@
 
 namespace Enqueue\Symfony;
 
-use Enqueue\AmqpBunny\AmqpConnectionFactory as AmqpBunnyConnectionFactory;
-use Enqueue\AmqpBunny\Symfony\AmqpBunnyTransportFactory;
-use Enqueue\AmqpExt\AmqpConnectionFactory as AmqpExtConnectionFactory;
-use Enqueue\AmqpExt\Symfony\AmqpTransportFactory;
-use Enqueue\AmqpLib\AmqpConnectionFactory as AmqpLibConnectionFactory;
-use Enqueue\AmqpLib\Symfony\AmqpLibTransportFactory;
 use Enqueue\Dbal\DbalConnectionFactory;
 use Enqueue\Dbal\Symfony\DbalTransportFactory;
 use Enqueue\Fs\FsConnectionFactory;
@@ -22,6 +16,7 @@
 use Enqueue\Sqs\Symfony\SqsTransportFactory;
 use Enqueue\Stomp\StompConnectionFactory;
 use Enqueue\Stomp\Symfony\StompTransportFactory;
+use Interop\Amqp\AmqpConnectionFactory;
 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use function Enqueue\dsn_to_connection_factory;
@@ -181,16 +176,8 @@ private function findFactory($dsn)
     {
         $factory = dsn_to_connection_factory($dsn);
 
-        if ($factory instanceof AmqpExtConnectionFactory) {
-            return new AmqpTransportFactory('default_amqp_ext');
-        }
-
-        if ($factory instanceof AmqpLibConnectionFactory) {
-            return new AmqpLibTransportFactory('default_amqp_lib');
-        }
-
-        if ($factory instanceof AmqpBunnyConnectionFactory) {
-            return new AmqpBunnyTransportFactory('default_amqp_bunny');
+        if ($factory instanceof AmqpConnectionFactory) {
+            return new AmqpTransportFactory(get_class($factory), 'default_amqp');
         }
 
         if ($factory instanceof FsConnectionFactory) {
diff --git a/Symfony/RabbitMqAmqpTransportFactory.php b/Symfony/RabbitMqAmqpTransportFactory.php
new file mode 100644
index 0000000..cfc4d24
--- /dev/null
+++ b/Symfony/RabbitMqAmqpTransportFactory.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Enqueue\Symfony;
+
+use Enqueue\AmqpTools\DelayStrategyTransportFactoryTrait;
+use Enqueue\Client\Amqp\RabbitMqDriver;
+use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+
+class RabbitMqAmqpTransportFactory extends AmqpTransportFactory
+{
+    use DelayStrategyTransportFactoryTrait;
+
+    /**
+     * @param string $amqpConnectionFactoryClass
+     * @param string $name
+     */
+    public function __construct($amqpConnectionFactoryClass, $name = 'rabbitmq_amqp')
+    {
+        parent::__construct($amqpConnectionFactoryClass, $name);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addConfiguration(ArrayNodeDefinition $builder)
+    {
+        parent::addConfiguration($builder);
+
+        $builder
+            ->children()
+                ->scalarNode('delay_strategy')
+                    ->defaultValue('dlx')
+                    ->info('The delay strategy to be used. Possible values are "dlx", "delayed_message_plugin" or service id')
+                ->end()
+        ;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createConnectionFactory(ContainerBuilder $container, array $config)
+    {
+        $factoryId = parent::createConnectionFactory($container, $config);
+
+        $this->registerDelayStrategy($container, $config, $factoryId, $this->getName());
+
+        return $factoryId;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createDriver(ContainerBuilder $container, array $config)
+    {
+        $driver = new Definition(RabbitMqDriver::class);
+        $driver->setArguments([
+            new Reference(sprintf('enqueue.transport.%s.context', $this->getName())),
+            new Reference('enqueue.client.config'),
+            new Reference('enqueue.client.meta.queue_meta_registry'),
+        ]);
+        $driverId = sprintf('enqueue.client.%s.driver', $this->getName());
+        $container->setDefinition($driverId, $driver);
+
+        return $driverId;
+    }
+}
diff --git a/Tests/Symfony/AmqpTransportFactoryTest.php b/Tests/Symfony/AmqpTransportFactoryTest.php
new file mode 100644
index 0000000..5c93bce
--- /dev/null
+++ b/Tests/Symfony/AmqpTransportFactoryTest.php
@@ -0,0 +1,298 @@
+<?php
+
+namespace Enqueue\Tests\Symfony;
+
+use Enqueue\Client\Amqp\AmqpDriver;
+use Enqueue\Symfony\AmqpTransportFactory;
+use Enqueue\Symfony\TransportFactoryInterface;
+use Enqueue\Test\ClassExtensionTrait;
+use Interop\Amqp\AmqpConnectionFactory;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Definition\Builder\TreeBuilder;
+use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
+use Symfony\Component\Config\Definition\Processor;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+class AmqpTransportFactoryTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementTransportFactoryInterface()
+    {
+        $this->assertClassImplements(TransportFactoryInterface::class, AmqpTransportFactory::class);
+    }
+
+    public function testCouldBeConstructedWithDefaultName()
+    {
+        $transport = new AmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+
+        $this->assertEquals('amqp', $transport->getName());
+    }
+
+    public function testCouldBeConstructedWithCustomName()
+    {
+        $transport = new AmqpTransportFactory($this->createAmqpConnectionFactoryClass(), 'theCustomName');
+
+        $this->assertEquals('theCustomName', $transport->getName());
+    }
+
+    public function testShouldAllowAddConfiguration()
+    {
+        $transport = new AmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+        $config = $processor->process($tb->buildTree(), [[
+            'host' => 'localhost',
+            'port' => 5672,
+            'user' => 'guest',
+            'pass' => 'guest',
+            'vhost' => '/',
+            'read_timeout' => 3.,
+            'write_timeout' => 3.,
+            'connection_timeout' => 3.,
+            'heartbeat' => 0,
+            'persisted' => false,
+            'lazy' => true,
+            'qos_global' => false,
+            'qos_prefetch_size' => 0,
+            'qos_prefetch_count' => 1,
+            'receive_method' => 'basic_get',
+        ]]);
+
+        $this->assertEquals([
+            'host' => 'localhost',
+            'port' => 5672,
+            'user' => 'guest',
+            'pass' => 'guest',
+            'vhost' => '/',
+            'read_timeout' => 3.,
+            'write_timeout' => 3.,
+            'connection_timeout' => 3.,
+            'heartbeat' => 0,
+            'persisted' => false,
+            'lazy' => true,
+            'qos_global' => false,
+            'qos_prefetch_size' => 0,
+            'qos_prefetch_count' => 1,
+            'receive_method' => 'basic_get',
+        ], $config);
+    }
+
+    public function testShouldAllowAddConfigurationWithDriverOptions()
+    {
+        $transport = new AmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+        $config = $processor->process($tb->buildTree(), [[
+            'host' => 'localhost',
+            'driver_options' => [
+                'foo' => 'fooVal',
+            ],
+        ]]);
+
+        $this->assertEquals([
+            'host' => 'localhost',
+            'driver_options' => [
+                'foo' => 'fooVal',
+            ],
+        ], $config);
+    }
+
+    public function testShouldAllowAddConfigurationAsString()
+    {
+        $transport = new AmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+        $config = $processor->process($tb->buildTree(), ['amqpDSN']);
+
+        $this->assertEquals([
+            'dsn' => 'amqpDSN',
+        ], $config);
+    }
+
+    public function testThrowIfInvalidReceiveMethodIsSet()
+    {
+        $transport = new AmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+
+        $this->expectException(InvalidConfigurationException::class);
+        $this->expectExceptionMessage('The value "anInvalidMethod" is not allowed for path "foo.receive_method". Permissible values: "basic_get", "basic_consume"');
+        $processor->process($tb->buildTree(), [[
+            'receive_method' => 'anInvalidMethod',
+        ]]);
+    }
+
+    public function testShouldAllowChangeReceiveMethod()
+    {
+        $transport = new AmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+        $config = $processor->process($tb->buildTree(), [[
+            'receive_method' => 'basic_consume',
+        ]]);
+
+        $this->assertEquals([
+            'receive_method' => 'basic_consume',
+        ], $config);
+    }
+
+    public function testShouldCreateConnectionFactoryForEmptyConfig()
+    {
+        $container = new ContainerBuilder();
+
+        $expectedClass = $this->createAmqpConnectionFactoryClass();
+
+        $transport = new AmqpTransportFactory($expectedClass);
+
+        $serviceId = $transport->createConnectionFactory($container, []);
+
+        $this->assertTrue($container->hasDefinition($serviceId));
+        $factory = $container->getDefinition($serviceId);
+        $this->assertEquals($expectedClass, $factory->getClass());
+        $this->assertSame([[]], $factory->getArguments());
+    }
+
+    public function testShouldCreateConnectionFactoryFromDsnString()
+    {
+        $container = new ContainerBuilder();
+
+        $expectedClass = $this->createAmqpConnectionFactoryClass();
+
+        $transport = new AmqpTransportFactory($expectedClass);
+
+        $serviceId = $transport->createConnectionFactory($container, [
+            'dsn' => 'theConnectionDSN',
+        ]);
+
+        $this->assertTrue($container->hasDefinition($serviceId));
+        $factory = $container->getDefinition($serviceId);
+        $this->assertEquals($expectedClass, $factory->getClass());
+        $this->assertSame([['dsn' => 'theConnectionDSN']], $factory->getArguments());
+    }
+
+    public function testShouldCreateConnectionFactoryAndMergeDriverOptionsIfSet()
+    {
+        $container = new ContainerBuilder();
+
+        $expectedClass = $this->createAmqpConnectionFactoryClass();
+
+        $transport = new AmqpTransportFactory($expectedClass);
+
+        $serviceId = $transport->createConnectionFactory($container, [
+            'host' => 'aHost',
+            'driver_options' => [
+                'foo' => 'fooVal',
+            ],
+        ]);
+
+        $this->assertTrue($container->hasDefinition($serviceId));
+        $factory = $container->getDefinition($serviceId);
+        $this->assertEquals($expectedClass, $factory->getClass());
+        $this->assertSame([['foo' => 'fooVal', 'host' => 'aHost']], $factory->getArguments());
+    }
+
+    public function testShouldCreateConnectionFactoryFromDsnStringPlushArrayOptions()
+    {
+        $container = new ContainerBuilder();
+
+        $expectedClass = $this->createAmqpConnectionFactoryClass();
+
+        $transport = new AmqpTransportFactory($expectedClass);
+
+        $serviceId = $transport->createConnectionFactory($container, [
+            'dsn' => 'theConnectionDSN',
+            'host' => 'localhost',
+            'port' => 5672,
+            'user' => 'guest',
+            'pass' => 'guest',
+            'vhost' => '/',
+            'persisted' => false,
+        ]);
+
+        $this->assertTrue($container->hasDefinition($serviceId));
+        $factory = $container->getDefinition($serviceId);
+        $this->assertEquals($expectedClass, $factory->getClass());
+        $this->assertSame([[
+            'dsn' => 'theConnectionDSN',
+            'host' => 'localhost',
+            'port' => 5672,
+            'user' => 'guest',
+            'pass' => 'guest',
+            'vhost' => '/',
+            'persisted' => false,
+        ]], $factory->getArguments());
+    }
+
+    public function testShouldCreateContext()
+    {
+        $container = new ContainerBuilder();
+
+        $transport = new AmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+
+        $serviceId = $transport->createContext($container, [
+            'host' => 'localhost',
+            'port' => 5672,
+            'user' => 'guest',
+            'pass' => 'guest',
+            'vhost' => '/',
+            'persisted' => false,
+        ]);
+
+        $this->assertEquals('enqueue.transport.amqp.context', $serviceId);
+        $this->assertTrue($container->hasDefinition($serviceId));
+
+        $context = $container->getDefinition('enqueue.transport.amqp.context');
+        $this->assertInstanceOf(Reference::class, $context->getFactory()[0]);
+        $this->assertEquals('enqueue.transport.amqp.connection_factory', (string) $context->getFactory()[0]);
+        $this->assertEquals('createContext', $context->getFactory()[1]);
+    }
+
+    public function testShouldCreateDriver()
+    {
+        $container = new ContainerBuilder();
+
+        $transport = new AmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+
+        $serviceId = $transport->createDriver($container, []);
+
+        $this->assertEquals('enqueue.client.amqp.driver', $serviceId);
+        $this->assertTrue($container->hasDefinition($serviceId));
+
+        $driver = $container->getDefinition($serviceId);
+        $this->assertSame(AmqpDriver::class, $driver->getClass());
+
+        $this->assertInstanceOf(Reference::class, $driver->getArgument(0));
+        $this->assertEquals('enqueue.transport.amqp.context', (string) $driver->getArgument(0));
+
+        $this->assertInstanceOf(Reference::class, $driver->getArgument(1));
+        $this->assertEquals('enqueue.client.config', (string) $driver->getArgument(1));
+
+        $this->assertInstanceOf(Reference::class, $driver->getArgument(2));
+        $this->assertEquals('enqueue.client.meta.queue_meta_registry', (string) $driver->getArgument(2));
+    }
+
+    /**
+     * @return string
+     */
+    private function createAmqpConnectionFactoryClass()
+    {
+        return $this->getMockClass(AmqpConnectionFactory::class);
+    }
+}
diff --git a/Tests/Symfony/DefaultTransportFactoryTest.php b/Tests/Symfony/DefaultTransportFactoryTest.php
index 1c7b0bd..7f84d6d 100644
--- a/Tests/Symfony/DefaultTransportFactoryTest.php
+++ b/Tests/Symfony/DefaultTransportFactoryTest.php
@@ -250,11 +250,11 @@ public function testShouldCreateDriverFromDsn($dsn, $expectedName)
 
     public static function provideDSNs()
     {
-        yield ['amqp+ext:', 'default_amqp_ext'];
+        yield ['amqp+ext:', 'default_amqp'];
 
-        yield ['amqp+lib:', 'default_amqp_lib'];
+        yield ['amqp+lib:', 'default_amqp'];
 
-        yield ['amqp+bunny:', 'default_amqp_bunny'];
+        yield ['amqp+bunny:', 'default_amqp'];
 
         yield ['null:', 'default_null'];
 
diff --git a/Tests/Symfony/RabbitMqAmqpTransportFactoryTest.php b/Tests/Symfony/RabbitMqAmqpTransportFactoryTest.php
new file mode 100644
index 0000000..9d16eb5
--- /dev/null
+++ b/Tests/Symfony/RabbitMqAmqpTransportFactoryTest.php
@@ -0,0 +1,139 @@
+<?php
+
+namespace Enqueue\Tests\Symfony;
+
+use Enqueue\Client\Amqp\RabbitMqDriver;
+use Enqueue\Symfony\AmqpTransportFactory;
+use Enqueue\Symfony\RabbitMqAmqpTransportFactory;
+use Enqueue\Symfony\TransportFactoryInterface;
+use Enqueue\Test\ClassExtensionTrait;
+use Interop\Amqp\AmqpConnectionFactory;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Definition\Builder\TreeBuilder;
+use Symfony\Component\Config\Definition\Processor;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+class RabbitMqAmqpTransportFactoryTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementTransportFactoryInterface()
+    {
+        $this->assertClassImplements(TransportFactoryInterface::class, RabbitMqAmqpTransportFactory::class);
+    }
+
+    public function testShouldExtendAmqpTransportFactoryClass()
+    {
+        $this->assertClassExtends(AmqpTransportFactory::class, RabbitMqAmqpTransportFactory::class);
+    }
+
+    public function testCouldBeConstructedWithDefaultName()
+    {
+        $transport = new RabbitMqAmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+
+        $this->assertEquals('rabbitmq_amqp', $transport->getName());
+    }
+
+    public function testCouldBeConstructedWithCustomName()
+    {
+        $transport = new RabbitMqAmqpTransportFactory($this->createAmqpConnectionFactoryClass(), 'theCustomName');
+
+        $this->assertEquals('theCustomName', $transport->getName());
+    }
+
+    public function testShouldAllowAddConfiguration()
+    {
+        $transport = new RabbitMqAmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+        $config = $processor->process($tb->buildTree(), []);
+
+        $this->assertEquals([
+            'delay_strategy' => 'dlx',
+        ], $config);
+    }
+
+    public function testShouldCreateConnectionFactory()
+    {
+        $container = new ContainerBuilder();
+
+        $expectedClass = $this->createAmqpConnectionFactoryClass();
+
+        $transport = new RabbitMqAmqpTransportFactory($expectedClass);
+
+        $serviceId = $transport->createConnectionFactory($container, [
+            'host' => 'localhost',
+            'port' => 5672,
+            'user' => 'guest',
+            'pass' => 'guest',
+            'vhost' => '/',
+            'persisted' => false,
+            'delay_strategy' => null,
+        ]);
+
+        $this->assertTrue($container->hasDefinition($serviceId));
+        $factory = $container->getDefinition($serviceId);
+        $this->assertEquals($expectedClass, $factory->getClass());
+        $this->assertSame([[
+            'host' => 'localhost',
+            'port' => 5672,
+            'user' => 'guest',
+            'pass' => 'guest',
+            'vhost' => '/',
+            'persisted' => false,
+            'delay_strategy' => null,
+        ]], $factory->getArguments());
+    }
+
+    public function testShouldCreateContext()
+    {
+        $container = new ContainerBuilder();
+
+        $transport = new RabbitMqAmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+
+        $serviceId = $transport->createContext($container, [
+            'host' => 'localhost',
+            'port' => 5672,
+            'user' => 'guest',
+            'pass' => 'guest',
+            'vhost' => '/',
+            'persisted' => false,
+            'delay_strategy' => null,
+        ]);
+
+        $this->assertEquals('enqueue.transport.rabbitmq_amqp.context', $serviceId);
+        $this->assertTrue($container->hasDefinition($serviceId));
+
+        $context = $container->getDefinition('enqueue.transport.rabbitmq_amqp.context');
+        $this->assertInstanceOf(Reference::class, $context->getFactory()[0]);
+        $this->assertEquals('enqueue.transport.rabbitmq_amqp.connection_factory', (string) $context->getFactory()[0]);
+        $this->assertEquals('createContext', $context->getFactory()[1]);
+    }
+
+    public function testShouldCreateDriver()
+    {
+        $container = new ContainerBuilder();
+
+        $transport = new RabbitMqAmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+
+        $serviceId = $transport->createDriver($container, []);
+
+        $this->assertEquals('enqueue.client.rabbitmq_amqp.driver', $serviceId);
+        $this->assertTrue($container->hasDefinition($serviceId));
+
+        $driver = $container->getDefinition($serviceId);
+        $this->assertSame(RabbitMqDriver::class, $driver->getClass());
+    }
+
+    /**
+     * @return string
+     */
+    private function createAmqpConnectionFactoryClass()
+    {
+        return $this->getMockClass(AmqpConnectionFactory::class);
+    }
+}

From a35d4036bcd5b25f2522b77a24a1f6dd2816f32f Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 17 Oct 2017 14:44:42 +0300
Subject: [PATCH 010/337] [amqp] One single transport factory for all supported
 amqp implementations.

---
 Symfony/AmqpTransportFactory.php              |  67 +++++++---
 Symfony/DefaultTransportFactory.php           |   2 +-
 Symfony/RabbitMqAmqpTransportFactory.php      |   5 +-
 Tests/Symfony/AmqpTransportFactoryTest.php    | 116 +++++++++++++-----
 .../RabbitMqAmqpTransportFactoryTest.php      |  24 ++--
 5 files changed, 144 insertions(+), 70 deletions(-)

diff --git a/Symfony/AmqpTransportFactory.php b/Symfony/AmqpTransportFactory.php
index bef8dea..a3351e9 100644
--- a/Symfony/AmqpTransportFactory.php
+++ b/Symfony/AmqpTransportFactory.php
@@ -2,32 +2,30 @@
 
 namespace Enqueue\Symfony;
 
+use Enqueue\AmqpBunny\AmqpConnectionFactory as AmqpBunnyConnectionFactory;
+use Enqueue\AmqpExt\AmqpConnectionFactory as AmqpExtConnectionFactory;
+use Enqueue\AmqpLib\AmqpConnectionFactory as AmqpLibConnectionFactory;
 use Enqueue\Client\Amqp\AmqpDriver;
+use Interop\Amqp\AmqpConnectionFactory;
 use Interop\Amqp\AmqpContext;
 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Definition;
 use Symfony\Component\DependencyInjection\Reference;
+use function Enqueue\dsn_to_connection_factory;
 
 class AmqpTransportFactory implements TransportFactoryInterface, DriverFactoryInterface
 {
-    /**
-     * @var string
-     */
-    private $amqpConnectionFactoryClass;
-
     /**
      * @var string
      */
     private $name;
 
     /**
-     * @param string $amqpConnectionFactoryClass
      * @param string $name
      */
-    public function __construct($amqpConnectionFactoryClass, $name = 'amqp')
+    public function __construct($name = 'amqp')
     {
-        $this->amqpConnectionFactoryClass = $amqpConnectionFactoryClass;
         $this->name = $name;
     }
 
@@ -36,14 +34,32 @@ public function __construct($amqpConnectionFactoryClass, $name = 'amqp')
      */
     public function addConfiguration(ArrayNodeDefinition $builder)
     {
+        $drivers = [];
+        if (class_exists(AmqpExtConnectionFactory::class)) {
+            $drivers[] = 'ext';
+        }
+        if (class_exists(AmqpLibConnectionFactory::class)) {
+            $drivers[] = 'lib';
+        }
+        if (class_exists(AmqpBunnyConnectionFactory::class)) {
+            $drivers[] = 'bunny';
+        }
+
         $builder
             ->beforeNormalization()
-            ->ifString()
+                ->ifEmpty()
+                ->then(function ($v) {
+                    return ['dsn' => 'amqp:'];
+                })
+                ->ifString()
                 ->then(function ($v) {
                     return ['dsn' => $v];
                 })
             ->end()
             ->children()
+                ->enumNode('driver')
+                    ->values($drivers)
+                ->end()
                 ->scalarNode('dsn')
                     ->info('The connection to AMQP broker set as a string. Other parameters could be used as defaults')
                 ->end()
@@ -113,7 +129,8 @@ public function createConnectionFactory(ContainerBuilder $container, array $conf
             $config = array_replace($driverOptions, $config);
         }
 
-        $factory = new Definition($this->amqpConnectionFactoryClass);
+        $factory = new Definition(AmqpConnectionFactory::class);
+        $factory->setFactory([self::class, 'createConnectionFactoryFactory']);
         $factory->setArguments([$config]);
 
         $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
@@ -164,11 +181,31 @@ public function getName()
         return $this->name;
     }
 
-    /**
-     * @return string
-     */
-    public function getAmqpConnectionFactoryClass()
+    public static function createConnectionFactoryFactory(array $config)
     {
-        return $this->amqpConnectionFactoryClass;
+        if (array_key_exists('driver', $config)) {
+            if ('ext' == $config['driver']) {
+                return new AmqpExtConnectionFactory($config);
+            }
+            if ('lib' == $config['driver']) {
+                return new AmqpLibConnectionFactory($config);
+            }
+            if ('bunny' == $config['driver']) {
+                return new AmqpBunnyConnectionFactory($config);
+            }
+
+            throw new \LogicException(sprintf('Unexpected driver given "%s"', $config['driver']));
+        }
+
+        $dsn = array_key_exists('dsn', $config) ? $config['dsn'] : 'amqp:';
+        $factory = dsn_to_connection_factory($dsn);
+
+        if (false == $factory instanceof  AmqpConnectionFactory) {
+            throw new \LogicException(sprintf('Factory must be instance of "%s" but got "%s"', AmqpConnectionFactory::class, get_class($factory)));
+        }
+
+        $factoryClass = get_class($factory);
+
+        return new $factoryClass($config);
     }
 }
diff --git a/Symfony/DefaultTransportFactory.php b/Symfony/DefaultTransportFactory.php
index 5911dc4..2497298 100644
--- a/Symfony/DefaultTransportFactory.php
+++ b/Symfony/DefaultTransportFactory.php
@@ -177,7 +177,7 @@ private function findFactory($dsn)
         $factory = dsn_to_connection_factory($dsn);
 
         if ($factory instanceof AmqpConnectionFactory) {
-            return new AmqpTransportFactory(get_class($factory), 'default_amqp');
+            return new AmqpTransportFactory('default_amqp');
         }
 
         if ($factory instanceof FsConnectionFactory) {
diff --git a/Symfony/RabbitMqAmqpTransportFactory.php b/Symfony/RabbitMqAmqpTransportFactory.php
index cfc4d24..2bd98e5 100644
--- a/Symfony/RabbitMqAmqpTransportFactory.php
+++ b/Symfony/RabbitMqAmqpTransportFactory.php
@@ -14,12 +14,11 @@ class RabbitMqAmqpTransportFactory extends AmqpTransportFactory
     use DelayStrategyTransportFactoryTrait;
 
     /**
-     * @param string $amqpConnectionFactoryClass
      * @param string $name
      */
-    public function __construct($amqpConnectionFactoryClass, $name = 'rabbitmq_amqp')
+    public function __construct($name = 'rabbitmq_amqp')
     {
-        parent::__construct($amqpConnectionFactoryClass, $name);
+        parent::__construct($name);
     }
 
     /**
diff --git a/Tests/Symfony/AmqpTransportFactoryTest.php b/Tests/Symfony/AmqpTransportFactoryTest.php
index 5c93bce..d468b33 100644
--- a/Tests/Symfony/AmqpTransportFactoryTest.php
+++ b/Tests/Symfony/AmqpTransportFactoryTest.php
@@ -25,21 +25,28 @@ public function testShouldImplementTransportFactoryInterface()
 
     public function testCouldBeConstructedWithDefaultName()
     {
-        $transport = new AmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+        $transport = new AmqpTransportFactory();
 
         $this->assertEquals('amqp', $transport->getName());
     }
 
     public function testCouldBeConstructedWithCustomName()
     {
-        $transport = new AmqpTransportFactory($this->createAmqpConnectionFactoryClass(), 'theCustomName');
+        $transport = new AmqpTransportFactory('theCustomName');
+
+        $this->assertEquals('theCustomName', $transport->getName());
+    }
+
+    public function testThrowIfCouldBeConstructedWithCustomName()
+    {
+        $transport = new AmqpTransportFactory('theCustomName');
 
         $this->assertEquals('theCustomName', $transport->getName());
     }
 
     public function testShouldAllowAddConfiguration()
     {
-        $transport = new AmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+        $transport = new AmqpTransportFactory();
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
@@ -84,7 +91,7 @@ public function testShouldAllowAddConfiguration()
 
     public function testShouldAllowAddConfigurationWithDriverOptions()
     {
-        $transport = new AmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+        $transport = new AmqpTransportFactory();
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
@@ -107,7 +114,7 @@ public function testShouldAllowAddConfigurationWithDriverOptions()
 
     public function testShouldAllowAddConfigurationAsString()
     {
-        $transport = new AmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+        $transport = new AmqpTransportFactory();
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
@@ -122,7 +129,7 @@ public function testShouldAllowAddConfigurationAsString()
 
     public function testThrowIfInvalidReceiveMethodIsSet()
     {
-        $transport = new AmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+        $transport = new AmqpTransportFactory();
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
@@ -138,7 +145,7 @@ public function testThrowIfInvalidReceiveMethodIsSet()
 
     public function testShouldAllowChangeReceiveMethod()
     {
-        $transport = new AmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+        $transport = new AmqpTransportFactory();
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
@@ -157,15 +164,14 @@ public function testShouldCreateConnectionFactoryForEmptyConfig()
     {
         $container = new ContainerBuilder();
 
-        $expectedClass = $this->createAmqpConnectionFactoryClass();
-
-        $transport = new AmqpTransportFactory($expectedClass);
+        $transport = new AmqpTransportFactory();
 
         $serviceId = $transport->createConnectionFactory($container, []);
 
         $this->assertTrue($container->hasDefinition($serviceId));
         $factory = $container->getDefinition($serviceId);
-        $this->assertEquals($expectedClass, $factory->getClass());
+        $this->assertEquals(AmqpConnectionFactory::class, $factory->getClass());
+
         $this->assertSame([[]], $factory->getArguments());
     }
 
@@ -173,27 +179,23 @@ public function testShouldCreateConnectionFactoryFromDsnString()
     {
         $container = new ContainerBuilder();
 
-        $expectedClass = $this->createAmqpConnectionFactoryClass();
-
-        $transport = new AmqpTransportFactory($expectedClass);
+        $transport = new AmqpTransportFactory();
 
         $serviceId = $transport->createConnectionFactory($container, [
-            'dsn' => 'theConnectionDSN',
+            'dsn' => 'theConnectionDSN:',
         ]);
 
         $this->assertTrue($container->hasDefinition($serviceId));
         $factory = $container->getDefinition($serviceId);
-        $this->assertEquals($expectedClass, $factory->getClass());
-        $this->assertSame([['dsn' => 'theConnectionDSN']], $factory->getArguments());
+        $this->assertEquals(AmqpConnectionFactory::class, $factory->getClass());
+        $this->assertSame([['dsn' => 'theConnectionDSN:']], $factory->getArguments());
     }
 
     public function testShouldCreateConnectionFactoryAndMergeDriverOptionsIfSet()
     {
         $container = new ContainerBuilder();
 
-        $expectedClass = $this->createAmqpConnectionFactoryClass();
-
-        $transport = new AmqpTransportFactory($expectedClass);
+        $transport = new AmqpTransportFactory();
 
         $serviceId = $transport->createConnectionFactory($container, [
             'host' => 'aHost',
@@ -204,7 +206,7 @@ public function testShouldCreateConnectionFactoryAndMergeDriverOptionsIfSet()
 
         $this->assertTrue($container->hasDefinition($serviceId));
         $factory = $container->getDefinition($serviceId);
-        $this->assertEquals($expectedClass, $factory->getClass());
+        $this->assertEquals(AmqpConnectionFactory::class, $factory->getClass());
         $this->assertSame([['foo' => 'fooVal', 'host' => 'aHost']], $factory->getArguments());
     }
 
@@ -212,12 +214,9 @@ public function testShouldCreateConnectionFactoryFromDsnStringPlushArrayOptions(
     {
         $container = new ContainerBuilder();
 
-        $expectedClass = $this->createAmqpConnectionFactoryClass();
-
-        $transport = new AmqpTransportFactory($expectedClass);
+        $transport = new AmqpTransportFactory();
 
         $serviceId = $transport->createConnectionFactory($container, [
-            'dsn' => 'theConnectionDSN',
             'host' => 'localhost',
             'port' => 5672,
             'user' => 'guest',
@@ -228,9 +227,8 @@ public function testShouldCreateConnectionFactoryFromDsnStringPlushArrayOptions(
 
         $this->assertTrue($container->hasDefinition($serviceId));
         $factory = $container->getDefinition($serviceId);
-        $this->assertEquals($expectedClass, $factory->getClass());
+        $this->assertEquals(AmqpConnectionFactory::class, $factory->getClass());
         $this->assertSame([[
-            'dsn' => 'theConnectionDSN',
             'host' => 'localhost',
             'port' => 5672,
             'user' => 'guest',
@@ -244,7 +242,7 @@ public function testShouldCreateContext()
     {
         $container = new ContainerBuilder();
 
-        $transport = new AmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+        $transport = new AmqpTransportFactory();
 
         $serviceId = $transport->createContext($container, [
             'host' => 'localhost',
@@ -268,7 +266,7 @@ public function testShouldCreateDriver()
     {
         $container = new ContainerBuilder();
 
-        $transport = new AmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+        $transport = new AmqpTransportFactory();
 
         $serviceId = $transport->createDriver($container, []);
 
@@ -288,11 +286,61 @@ public function testShouldCreateDriver()
         $this->assertEquals('enqueue.client.meta.queue_meta_registry', (string) $driver->getArgument(2));
     }
 
-    /**
-     * @return string
-     */
-    private function createAmqpConnectionFactoryClass()
+    public function testShouldCreateAmqpExtConnectionFactoryBySetDriver()
     {
-        return $this->getMockClass(AmqpConnectionFactory::class);
+        $factory = AmqpTransportFactory::createConnectionFactoryFactory(['driver' => 'ext']);
+
+        $this->assertInstanceOf(\Enqueue\AmqpExt\AmqpConnectionFactory::class, $factory);
+    }
+
+    public function testShouldCreateAmqpLibConnectionFactoryBySetDriver()
+    {
+        $factory = AmqpTransportFactory::createConnectionFactoryFactory(['driver' => 'lib']);
+
+        $this->assertInstanceOf(\Enqueue\AmqpLib\AmqpConnectionFactory::class, $factory);
+    }
+
+    public function testShouldCreateAmqpBunnyConnectionFactoryBySetDriver()
+    {
+        $factory = AmqpTransportFactory::createConnectionFactoryFactory(['driver' => 'bunny']);
+
+        $this->assertInstanceOf(\Enqueue\AmqpBunny\AmqpConnectionFactory::class, $factory);
+    }
+
+    public function testShouldCreateAmqpExtFromConfigWithoutDriverAndDsn()
+    {
+        $factory = AmqpTransportFactory::createConnectionFactoryFactory(['host' => 'aHost']);
+
+        $this->assertInstanceOf(\Enqueue\AmqpExt\AmqpConnectionFactory::class, $factory);
+    }
+
+    public function testThrowIfInvalidDriverGiven()
+    {
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Unexpected driver given "invalidDriver"');
+
+        AmqpTransportFactory::createConnectionFactoryFactory(['driver' => 'invalidDriver']);
+    }
+
+    public function testShouldCreateAmqpExtFromDsn()
+    {
+        $factory = AmqpTransportFactory::createConnectionFactoryFactory(['dsn' => 'amqp:']);
+
+        $this->assertInstanceOf(\Enqueue\AmqpExt\AmqpConnectionFactory::class, $factory);
+    }
+
+    public function testShouldCreateAmqpBunnyFromDsnWithDriver()
+    {
+        $factory = AmqpTransportFactory::createConnectionFactoryFactory(['dsn' => 'amqp+bunny:']);
+
+        $this->assertInstanceOf(\Enqueue\AmqpBunny\AmqpConnectionFactory::class, $factory);
+    }
+
+    public function testThrowIfNotAmqpDsnProvided()
+    {
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Factory must be instance of "Interop\Amqp\AmqpConnectionFactory" but got "Enqueue\Sqs\SqsConnectionFactory"');
+
+        AmqpTransportFactory::createConnectionFactoryFactory(['dsn' => 'sqs:']);
     }
 }
diff --git a/Tests/Symfony/RabbitMqAmqpTransportFactoryTest.php b/Tests/Symfony/RabbitMqAmqpTransportFactoryTest.php
index 9d16eb5..cfe77e6 100644
--- a/Tests/Symfony/RabbitMqAmqpTransportFactoryTest.php
+++ b/Tests/Symfony/RabbitMqAmqpTransportFactoryTest.php
@@ -30,21 +30,21 @@ public function testShouldExtendAmqpTransportFactoryClass()
 
     public function testCouldBeConstructedWithDefaultName()
     {
-        $transport = new RabbitMqAmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+        $transport = new RabbitMqAmqpTransportFactory();
 
         $this->assertEquals('rabbitmq_amqp', $transport->getName());
     }
 
     public function testCouldBeConstructedWithCustomName()
     {
-        $transport = new RabbitMqAmqpTransportFactory($this->createAmqpConnectionFactoryClass(), 'theCustomName');
+        $transport = new RabbitMqAmqpTransportFactory('theCustomName');
 
         $this->assertEquals('theCustomName', $transport->getName());
     }
 
     public function testShouldAllowAddConfiguration()
     {
-        $transport = new RabbitMqAmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+        $transport = new RabbitMqAmqpTransportFactory();
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
@@ -61,9 +61,7 @@ public function testShouldCreateConnectionFactory()
     {
         $container = new ContainerBuilder();
 
-        $expectedClass = $this->createAmqpConnectionFactoryClass();
-
-        $transport = new RabbitMqAmqpTransportFactory($expectedClass);
+        $transport = new RabbitMqAmqpTransportFactory();
 
         $serviceId = $transport->createConnectionFactory($container, [
             'host' => 'localhost',
@@ -77,7 +75,7 @@ public function testShouldCreateConnectionFactory()
 
         $this->assertTrue($container->hasDefinition($serviceId));
         $factory = $container->getDefinition($serviceId);
-        $this->assertEquals($expectedClass, $factory->getClass());
+        $this->assertEquals(AmqpConnectionFactory::class, $factory->getClass());
         $this->assertSame([[
             'host' => 'localhost',
             'port' => 5672,
@@ -93,7 +91,7 @@ public function testShouldCreateContext()
     {
         $container = new ContainerBuilder();
 
-        $transport = new RabbitMqAmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+        $transport = new RabbitMqAmqpTransportFactory();
 
         $serviceId = $transport->createContext($container, [
             'host' => 'localhost',
@@ -118,7 +116,7 @@ public function testShouldCreateDriver()
     {
         $container = new ContainerBuilder();
 
-        $transport = new RabbitMqAmqpTransportFactory($this->createAmqpConnectionFactoryClass());
+        $transport = new RabbitMqAmqpTransportFactory();
 
         $serviceId = $transport->createDriver($container, []);
 
@@ -128,12 +126,4 @@ public function testShouldCreateDriver()
         $driver = $container->getDefinition($serviceId);
         $this->assertSame(RabbitMqDriver::class, $driver->getClass());
     }
-
-    /**
-     * @return string
-     */
-    private function createAmqpConnectionFactoryClass()
-    {
-        return $this->getMockClass(AmqpConnectionFactory::class);
-    }
 }

From 7ab631ee2c078144b33293288218978124fdbb3c Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 17 Oct 2017 15:27:13 +0300
Subject: [PATCH 011/337] [consumption][amqp] move beforeReceive call at the
 end of the cycle for amqp.

---
 Consumption/QueueConsumer.php | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 62a7103..4e59abc 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -263,7 +263,9 @@ protected function doConsume(ExtensionInterface $extension, Context $context)
         $consumer = $context->getPsrConsumer();
         $logger = $context->getLogger();
 
-        $extension->onBeforeReceive($context);
+        if (false == $context->getPsrMessage() instanceof AmqpContext) {
+            $extension->onBeforeReceive($context);
+        }
 
         if ($context->isExecutionInterrupted()) {
             throw new ConsumptionInterruptedException();
@@ -307,6 +309,10 @@ protected function doConsume(ExtensionInterface $extension, Context $context)
             $logger->info(sprintf('Message processed: %s', $context->getResult()));
 
             $extension->onPostReceived($context);
+
+            if ($context->getPsrMessage() instanceof AmqpContext) {
+                $extension->onBeforeReceive($context);
+            }
         } else {
             usleep($this->idleTimeout * 1000);
             $extension->onIdle($context);

From bddd869210e8fea982eb6f5b6dd5b3130c6eed8d Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 19 Oct 2017 12:28:50 +0300
Subject: [PATCH 012/337] try fixing tests on travis.

---
 Symfony/AmqpTransportFactory.php | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Symfony/AmqpTransportFactory.php b/Symfony/AmqpTransportFactory.php
index a3351e9..8b64a61 100644
--- a/Symfony/AmqpTransportFactory.php
+++ b/Symfony/AmqpTransportFactory.php
@@ -47,7 +47,9 @@ public function addConfiguration(ArrayNodeDefinition $builder)
 
         $builder
             ->beforeNormalization()
-                ->ifEmpty()
+                ->ifTrue(function ($v) {
+                    return empty($v);
+                })
                 ->then(function ($v) {
                     return ['dsn' => 'amqp:'];
                 })

From 44921588d99d2ee6a7104e1fa4f65be8c467da54 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 27 Oct 2017 15:54:00 +0300
Subject: [PATCH 013/337] [enqueue] amqps schemes to dsn function.

---
 .../DsnToConnectionFactoryFunctionTest.php          | 13 ++++++++++++-
 functions.php                                       |  8 ++++++++
 2 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/Tests/Functions/DsnToConnectionFactoryFunctionTest.php b/Tests/Functions/DsnToConnectionFactoryFunctionTest.php
index 59c5333..d210ec5 100644
--- a/Tests/Functions/DsnToConnectionFactoryFunctionTest.php
+++ b/Tests/Functions/DsnToConnectionFactoryFunctionTest.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Tests\Functions;
 
 use Enqueue\AmqpExt\AmqpConnectionFactory;
+use Enqueue\AmqpLib\AmqpConnectionFactory as AmqpLibConnectionFactory;
 use Enqueue\Dbal\DbalConnectionFactory;
 use Enqueue\Fs\FsConnectionFactory;
 use Enqueue\Gearman\GearmanConnectionFactory;
@@ -58,6 +59,16 @@ public static function provideDSNs()
     {
         yield ['amqp:', AmqpConnectionFactory::class];
 
+        yield ['amqps:', AmqpConnectionFactory::class];
+
+        yield ['amqp+ext:', AmqpConnectionFactory::class];
+
+        yield ['amqps+ext:', AmqpConnectionFactory::class];
+
+        yield ['amqp+lib:', AmqpLibConnectionFactory::class];
+
+        yield ['amqps+lib:', AmqpLibConnectionFactory::class];
+
         yield ['amqp://user:pass@foo/vhost', AmqpConnectionFactory::class];
 
         yield ['file:', FsConnectionFactory::class];
@@ -72,7 +83,7 @@ public static function provideDSNs()
 
         yield ['beanstalk:', PheanstalkConnectionFactory::class];
 
-        //        yield ['gearman:', GearmanConnectionFactory::class];
+        yield ['gearman:', GearmanConnectionFactory::class];
 
         yield ['kafka:', RdKafkaConnectionFactory::class];
 
diff --git a/functions.php b/functions.php
index 25d4438..8960785 100644
--- a/functions.php
+++ b/functions.php
@@ -34,9 +34,11 @@ function dsn_to_connection_factory($dsn)
 
     if (class_exists(AmqpExtConnectionFactory::class)) {
         $map['amqp+ext'] = AmqpExtConnectionFactory::class;
+        $map['amqps+ext'] = AmqpExtConnectionFactory::class;
     }
     if (class_exists(AmqpLibConnectionFactory::class)) {
         $map['amqp+lib'] = AmqpLibConnectionFactory::class;
+        $map['amqps+lib'] = AmqpLibConnectionFactory::class;
     }
     if (class_exists(AmqpBunnyConnectionFactory::class)) {
         $map['amqp+bunny'] = AmqpBunnyConnectionFactory::class;
@@ -50,6 +52,12 @@ function dsn_to_connection_factory($dsn)
         $map['amqp'] = AmqpLibConnectionFactory::class;
     }
 
+    if (class_exists(AmqpExtConnectionFactory::class)) {
+        $map['amqps'] = AmqpExtConnectionFactory::class;
+    } elseif (class_exists(AmqpLibConnectionFactory::class)) {
+        $map['amqps'] = AmqpLibConnectionFactory::class;
+    }
+
     if (class_exists(NullConnectionFactory::class)) {
         $map['null'] = NullConnectionFactory::class;
     }

From c1d214f5dddc2282424b849b62c05a0c0681584b Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 27 Oct 2017 16:28:49 +0300
Subject: [PATCH 014/337] [bundle] add amqps: support to Symfony bundle.

---
 Symfony/AmqpTransportFactory.php              | 15 +++++++++++
 .../DsnToConnectionFactoryFunctionTest.php    | 15 ++++++-----
 Tests/Symfony/AmqpTransportFactoryTest.php    | 25 +++++++++++++++++++
 3 files changed, 49 insertions(+), 6 deletions(-)

diff --git a/Symfony/AmqpTransportFactory.php b/Symfony/AmqpTransportFactory.php
index 8b64a61..d098762 100644
--- a/Symfony/AmqpTransportFactory.php
+++ b/Symfony/AmqpTransportFactory.php
@@ -116,6 +116,21 @@ public function addConfiguration(ArrayNodeDefinition $builder)
                 ->variableNode('driver_options')
                     ->info('The options that are specific to the amqp transport you chose. For example amqp+lib have insist, keepalive, stream options. amqp+bunny has tcp_nodelay extra option.')
                 ->end()
+                ->booleanNode('ssl_on')
+                    ->info('Should be true if you want to use secure connections. False by default')
+                ->end()
+                ->booleanNode('ssl_verify')
+                    ->info('This option determines whether ssl client verifies that the server cert is for the server it is known as. True by default.')
+                ->end()
+                ->scalarNode('ssl_cacert')
+                    ->info('Location of Certificate Authority file on local filesystem which should be used with the verify_peer context option to authenticate the identity of the remote peer. A string.')
+                ->end()
+                ->scalarNode('ssl_cert')
+                    ->info('Path to local certificate file on filesystem. It must be a PEM encoded file which contains your certificate and private key. A string')
+                ->end()
+                ->scalarNode('ssl_key')
+                    ->info('Path to local private key file on filesystem in case of separate files for certificate (local_cert) and private key. A string.')
+                ->end()
         ;
     }
 
diff --git a/Tests/Functions/DsnToConnectionFactoryFunctionTest.php b/Tests/Functions/DsnToConnectionFactoryFunctionTest.php
index d210ec5..6596a79 100644
--- a/Tests/Functions/DsnToConnectionFactoryFunctionTest.php
+++ b/Tests/Functions/DsnToConnectionFactoryFunctionTest.php
@@ -2,7 +2,8 @@
 
 namespace Enqueue\Tests\Functions;
 
-use Enqueue\AmqpExt\AmqpConnectionFactory;
+use Enqueue\AmqpBunny\AmqpConnectionFactory as AmqpBunnyConnectionFactory;
+use Enqueue\AmqpExt\AmqpConnectionFactory as AmqpExtConnectionFactory;
 use Enqueue\AmqpLib\AmqpConnectionFactory as AmqpLibConnectionFactory;
 use Enqueue\Dbal\DbalConnectionFactory;
 use Enqueue\Fs\FsConnectionFactory;
@@ -57,19 +58,21 @@ public function testReturnsExpectedFactoryInstance($dsn, $expectedFactoryClass)
 
     public static function provideDSNs()
     {
-        yield ['amqp:', AmqpConnectionFactory::class];
+        yield ['amqp:', AmqpExtConnectionFactory::class];
 
-        yield ['amqps:', AmqpConnectionFactory::class];
+        yield ['amqps:', AmqpExtConnectionFactory::class];
 
-        yield ['amqp+ext:', AmqpConnectionFactory::class];
+        yield ['amqp+ext:', AmqpExtConnectionFactory::class];
 
-        yield ['amqps+ext:', AmqpConnectionFactory::class];
+        yield ['amqps+ext:', AmqpExtConnectionFactory::class];
 
         yield ['amqp+lib:', AmqpLibConnectionFactory::class];
 
         yield ['amqps+lib:', AmqpLibConnectionFactory::class];
 
-        yield ['amqp://user:pass@foo/vhost', AmqpConnectionFactory::class];
+        yield ['amqp+bunny:', AmqpBunnyConnectionFactory::class];
+
+        yield ['amqp://user:pass@foo/vhost', AmqpExtConnectionFactory::class];
 
         yield ['file:', FsConnectionFactory::class];
 
diff --git a/Tests/Symfony/AmqpTransportFactoryTest.php b/Tests/Symfony/AmqpTransportFactoryTest.php
index d468b33..d8bb5e1 100644
--- a/Tests/Symfony/AmqpTransportFactoryTest.php
+++ b/Tests/Symfony/AmqpTransportFactoryTest.php
@@ -112,6 +112,31 @@ public function testShouldAllowAddConfigurationWithDriverOptions()
         ], $config);
     }
 
+    public function testShouldAllowAddSslOptions()
+    {
+        $transport = new AmqpTransportFactory();
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+        $config = $processor->process($tb->buildTree(), [[
+            'ssl_on' => true,
+            'ssl_verify' => false,
+            'ssl_cacert' => '/path/to/cacert.pem',
+            'ssl_cert' => '/path/to/cert.pem',
+            'ssl_key' => '/path/to/key.pem',
+        ]]);
+
+        $this->assertEquals([
+            'ssl_on' => true,
+            'ssl_verify' => false,
+            'ssl_cacert' => '/path/to/cacert.pem',
+            'ssl_cert' => '/path/to/cert.pem',
+            'ssl_key' => '/path/to/key.pem',
+        ], $config);
+    }
+
     public function testShouldAllowAddConfigurationAsString()
     {
         $transport = new AmqpTransportFactory();

From 6158342e97f8e462282f0b20301f8c6eb42fbe9b Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 27 Oct 2017 19:52:01 +0300
Subject: [PATCH 015/337] skip gearman test

---
 Tests/Functions/DsnToConnectionFactoryFunctionTest.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/Functions/DsnToConnectionFactoryFunctionTest.php b/Tests/Functions/DsnToConnectionFactoryFunctionTest.php
index 6596a79..f123b11 100644
--- a/Tests/Functions/DsnToConnectionFactoryFunctionTest.php
+++ b/Tests/Functions/DsnToConnectionFactoryFunctionTest.php
@@ -86,7 +86,7 @@ public static function provideDSNs()
 
         yield ['beanstalk:', PheanstalkConnectionFactory::class];
 
-        yield ['gearman:', GearmanConnectionFactory::class];
+//        yield ['gearman:', GearmanConnectionFactory::class];
 
         yield ['kafka:', RdKafkaConnectionFactory::class];
 

From c88571db747cdc7f0ba189085458bc1bddc07e99 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Parmentier?=
 <jerome.parmentier@acensi.fr>
Date: Mon, 30 Oct 2017 11:00:21 +0100
Subject: [PATCH 016/337] Renamed exposed services' name to classes' FQCN

---
 Symfony/Client/ContainerAwareProcessorRegistry.php | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/Symfony/Client/ContainerAwareProcessorRegistry.php b/Symfony/Client/ContainerAwareProcessorRegistry.php
index 2b42cec..b378eb4 100644
--- a/Symfony/Client/ContainerAwareProcessorRegistry.php
+++ b/Symfony/Client/ContainerAwareProcessorRegistry.php
@@ -6,6 +6,7 @@
 use Interop\Queue\PsrProcessor;
 use Symfony\Component\DependencyInjection\ContainerAwareInterface;
 use Symfony\Component\DependencyInjection\ContainerAwareTrait;
+use Symfony\Component\HttpKernel\Kernel;
 
 class ContainerAwareProcessorRegistry implements ProcessorRegistryInterface, ContainerAwareInterface
 {
@@ -38,6 +39,12 @@ public function set($processorName, $serviceId)
      */
     public function get($processorName)
     {
+        if (30300 > Kernel::VERSION_ID) {
+            // Symfony 3.2 and below make service identifiers lowercase, so we do the same.
+            // To be removed when dropping support for Symfony < 3.3.
+            $processorName = strtolower($processorName);
+        }
+
         if (false == isset($this->processors[$processorName])) {
             throw new \LogicException(sprintf('Processor was not found. processorName: "%s"', $processorName));
         }

From 1d664d5df147460bd0fdcaeb3c80c229b08f3b5a Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 30 Oct 2017 14:20:21 +0200
Subject: [PATCH 017/337] amqp transport fixes.

---
 Symfony/AmqpTransportFactory.php | 65 +++++++++++++++++++++-----------
 1 file changed, 43 insertions(+), 22 deletions(-)

diff --git a/Symfony/AmqpTransportFactory.php b/Symfony/AmqpTransportFactory.php
index d098762..8a2a20e 100644
--- a/Symfony/AmqpTransportFactory.php
+++ b/Symfony/AmqpTransportFactory.php
@@ -34,16 +34,7 @@ public function __construct($name = 'amqp')
      */
     public function addConfiguration(ArrayNodeDefinition $builder)
     {
-        $drivers = [];
-        if (class_exists(AmqpExtConnectionFactory::class)) {
-            $drivers[] = 'ext';
-        }
-        if (class_exists(AmqpLibConnectionFactory::class)) {
-            $drivers[] = 'lib';
-        }
-        if (class_exists(AmqpBunnyConnectionFactory::class)) {
-            $drivers[] = 'bunny';
-        }
+        $transportsMap = static::getAvailableTransportsMap();
 
         $builder
             ->beforeNormalization()
@@ -59,8 +50,19 @@ public function addConfiguration(ArrayNodeDefinition $builder)
                 })
             ->end()
             ->children()
-                ->enumNode('driver')
-                    ->values($drivers)
+                ->scalarNode('driver')
+                    ->validate()
+                    ->always(function ($v) use ($transportsMap) {
+                        $drivers = array_keys($transportsMap);
+                        if (empty($transportsMap)) {
+                            throw new \InvalidArgumentException('There is no amqp driver available. Please consider installing one of the packages: enqueue/amqp-ext, enqueue/amqp-lib, enqueue/amqp-bunny.');
+                        }
+
+                        if (isset($v['driver']) && false == in_array($v['driver'], $drivers, true)) {
+                            throw new \InvalidArgumentException(sprintf('Unexpected driver given "invalidDriver". Available are "%s"', implode('", "', $drivers)));
+                        }
+                    })
+                    ->end()
                 ->end()
                 ->scalarNode('dsn')
                     ->info('The connection to AMQP broker set as a string. Other parameters could be used as defaults')
@@ -200,18 +202,18 @@ public function getName()
 
     public static function createConnectionFactoryFactory(array $config)
     {
-        if (array_key_exists('driver', $config)) {
-            if ('ext' == $config['driver']) {
-                return new AmqpExtConnectionFactory($config);
-            }
-            if ('lib' == $config['driver']) {
-                return new AmqpLibConnectionFactory($config);
-            }
-            if ('bunny' == $config['driver']) {
-                return new AmqpBunnyConnectionFactory($config);
+        if (false == empty($config['driver'])) {
+            $transportsMap = static::getAvailableTransportsMap();
+
+            if (false == array_key_exists($config['driver'], $transportsMap)) {
+                throw new \InvalidArgumentException(sprintf('Unexpected driver given "invalidDriver". Available are "%s"', implode('", "', array_keys($transportsMap))));
             }
 
-            throw new \LogicException(sprintf('Unexpected driver given "%s"', $config['driver']));
+            $connectionFactoryClass = $transportsMap[$config['driver']];
+
+            unset($config['driver']);
+
+            return new $connectionFactoryClass($config);
         }
 
         $dsn = array_key_exists('dsn', $config) ? $config['dsn'] : 'amqp:';
@@ -225,4 +227,23 @@ public static function createConnectionFactoryFactory(array $config)
 
         return new $factoryClass($config);
     }
+
+    /**
+     * @return string[]
+     */
+    private static function getAvailableTransportsMap()
+    {
+        $map = [];
+        if (class_exists(AmqpExtConnectionFactory::class)) {
+            $map['ext'] = AmqpExtConnectionFactory::class;
+        }
+        if (class_exists(AmqpLibConnectionFactory::class)) {
+            $map['lib'] = AmqpLibConnectionFactory::class;
+        }
+        if (class_exists(AmqpBunnyConnectionFactory::class)) {
+            $map['bunny'] = AmqpBunnyConnectionFactory::class;
+        }
+
+        return $map;
+    }
 }

From 3c6ca7a720dd7631e07cc83bc132da1e74c165f1 Mon Sep 17 00:00:00 2001
From: Joeri Verdeyen <info@jverdeyen.be>
Date: Thu, 2 Nov 2017 16:02:38 +0100
Subject: [PATCH 018/337] Add Command processor and Command creator

---
 Symfony/Creator/JobCommandCreator.php     | 66 +++++++++++++++++++++++
 Symfony/Processor/RunCommandProcessor.php | 44 +++++++++++++++
 2 files changed, 110 insertions(+)
 create mode 100644 Symfony/Creator/JobCommandCreator.php
 create mode 100644 Symfony/Processor/RunCommandProcessor.php

diff --git a/Symfony/Creator/JobCommandCreator.php b/Symfony/Creator/JobCommandCreator.php
new file mode 100644
index 0000000..2003795
--- /dev/null
+++ b/Symfony/Creator/JobCommandCreator.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace Enqueue\Symfony\Creator;
+
+use Enqueue\Client\ProducerInterface;
+
+class JobCommandCreator
+{
+    /**
+     * @var ProducerInterface
+     */
+    protected $producer;
+
+    /**
+     * @var string
+     */
+    protected $env;
+
+    /**
+     * CommandJobCreator constructor.
+     *
+     * @param ProducerInterface $producer
+     * @param string            $env
+     */
+    public function __construct(ProducerInterface $producer, $env)
+    {
+        $this->producer = $producer;
+        $this->env = $env;
+    }
+
+    /**
+     * @param $command
+     * @param mixed $args
+     *
+     * @return \Enqueue\Rpc\Promise|null
+     */
+    public function scheduleCommand($command, $args = [])
+    {
+        $argumentString = $this->createArgumentString($args);
+
+        return $this->producer->sendCommand('run_command', sprintf('%s %s', $command, $argumentString));
+    }
+
+    /**
+     * @param array $arguments
+     *
+     * @return string
+     */
+    public function createArgumentString(array $arguments)
+    {
+        $optionList = [];
+
+        foreach ($arguments as $key => $value) {
+            if (!is_int($key)) {
+                $optionList[] = sprintf('--%s=%s', $key, $value);
+                continue;
+            }
+
+            $optionList[] = sprintf('%s', $value);
+        }
+
+        $optionList[] = sprintf('--env=%s', $this->env);
+
+        return implode(' ', $optionList);
+    }
+}
diff --git a/Symfony/Processor/RunCommandProcessor.php b/Symfony/Processor/RunCommandProcessor.php
new file mode 100644
index 0000000..0fb70a5
--- /dev/null
+++ b/Symfony/Processor/RunCommandProcessor.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Enqueue\Symfony\Processor;
+
+use Enqueue\Client\CommandSubscriberInterface;
+use Enqueue\Consumption\Result;
+use Interop\Queue\PsrContext;
+use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrProcessor;
+use Symfony\Component\Process\Exception\ProcessFailedException;
+use Symfony\Component\Process\Process;
+
+class RunCommandProcessor implements PsrProcessor, CommandSubscriberInterface
+{
+    /**
+     * @var string
+     */
+    private $projectDir;
+
+    public function __construct($projectDir)
+    {
+        $this->projectDir = $projectDir;
+    }
+
+    public function process(PsrMessage $message, PsrContext $context)
+    {
+        $commandline = $message->getBody();
+
+        $process = new Process('./bin/console '.$commandline, $this->projectDir);
+
+        try {
+            $process->mustRun();
+
+            return Result::ACK;
+        } catch (ProcessFailedException $e) {
+            return Result::reject(sprintf('The process failed with exception: "%s" in %s at %s', $e->getMessage(), $e->getFile(), $e->getLine()));
+        }
+    }
+
+    public static function getSubscribedCommand()
+    {
+        return 'run_command';
+    }
+}

From 98680dd908f31a135e73cf544e62f85c4d783874 Mon Sep 17 00:00:00 2001
From: Iain Mckay <me@iainmckay.co.uk>
Date: Fri, 10 Nov 2017 13:58:39 +0100
Subject: [PATCH 019/337] Changes SetRouterPropertiesExtension to use the
 driver to generate the queue name

---
 Client/ConsumptionExtension/SetRouterPropertiesExtension.php | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
index 44b7ea4..d294a9d 100644
--- a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
+++ b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
@@ -36,7 +36,8 @@ public function onPreReceived(Context $context)
         }
 
         $config = $this->driver->getConfig();
-        if ($context->getPsrQueue()->getQueueName() != $config->createTransportQueueName($config->getRouterQueueName())) {
+        $queue = $this->driver->createQueue($config->getRouterQueueName());
+        if ($context->getPsrQueue()->getQueueName() != $queue->getQueueName()) {
             return;
         }
 

From acb6318bacdd3674583ca4f373ffa9c4b367ac25 Mon Sep 17 00:00:00 2001
From: Iain Mckay <me@iainmckay.co.uk>
Date: Fri, 10 Nov 2017 14:15:24 +0100
Subject: [PATCH 020/337] Fixed test

---
 .../SetRouterPropertiesExtensionTest.php           | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
index d344ed6..1563330 100644
--- a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
@@ -30,6 +30,7 @@ public function testCouldBeConstructedWithRequiredArguments()
     public function testShouldSetRouterProcessorPropertyIfNotSetAndOnRouterQueue()
     {
         $config = Config::create('test', '', '', 'router-queue', '', 'router-processor-name');
+        $queue = new NullQueue('test.router-queue');
 
         $driver = $this->createDriverMock();
         $driver
@@ -38,6 +39,12 @@ public function testShouldSetRouterProcessorPropertyIfNotSetAndOnRouterQueue()
             ->willReturn($config)
         ;
 
+        $driver
+            ->expects($this->once())
+            ->method('createQueue')
+            ->willReturn($queue)
+        ;        
+
         $message = new NullMessage();
 
         $context = new Context($this->createPsrContextMock());
@@ -56,6 +63,7 @@ public function testShouldSetRouterProcessorPropertyIfNotSetAndOnRouterQueue()
     public function testShouldNotSetRouterProcessorPropertyIfNotSetAndNotOnRouterQueue()
     {
         $config = Config::create('test', '', '', 'router-queue', '', 'router-processor-name');
+        $queue = new NullQueue('test.router-queue');
 
         $driver = $this->createDriverMock();
         $driver
@@ -64,6 +72,12 @@ public function testShouldNotSetRouterProcessorPropertyIfNotSetAndNotOnRouterQue
             ->willReturn($config)
         ;
 
+        $driver
+            ->expects($this->once())
+            ->method('createQueue')
+            ->willReturn($queue)
+        ;
+
         $message = new NullMessage();
 
         $context = new Context($this->createPsrContextMock());

From 64eaaf594c2649cf657b9cc02d287907823fa961 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 10 Nov 2017 16:22:25 +0200
Subject: [PATCH 021/337] fix cs

---
 .../ConsumptionExtension/SetRouterPropertiesExtensionTest.php   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
index 1563330..6dc723c 100644
--- a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
@@ -43,7 +43,7 @@ public function testShouldSetRouterProcessorPropertyIfNotSetAndOnRouterQueue()
             ->expects($this->once())
             ->method('createQueue')
             ->willReturn($queue)
-        ;        
+        ;
 
         $message = new NullMessage();
 

From 1878ff21c1354c3d2bb3cc2206799e411d4c4d47 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 10 Nov 2017 17:29:09 +0200
Subject: [PATCH 022/337] [consumption] fix context is missing message on
 exception.

---
 Consumption/QueueConsumer.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 4e59abc..275a189 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -171,7 +171,7 @@ public function consume(ExtensionInterface $runtimeExtension = null)
         while (true) {
             try {
                 if ($this->psrContext instanceof AmqpContext) {
-                    $callback = function (AmqpMessage $message, AmqpConsumer $consumer) use ($extension, $logger) {
+                    $callback = function (AmqpMessage $message, AmqpConsumer $consumer) use ($extension, $logger, &$context) {
                         $currentProcessor = null;
 
                         /** @var PsrQueue $queue */

From 54e93c7693a07c16bd1f8af7aecce0316743e424 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 10 Nov 2017 18:22:47 +0200
Subject: [PATCH 023/337] [consumption][amqp] onIdle is never called.

---
 Consumption/QueueConsumer.php | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 4e59abc..d45704e 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -204,6 +204,9 @@ public function consume(ExtensionInterface $runtimeExtension = null)
                     }
 
                     $this->psrContext->consume($this->receiveTimeout);
+
+                    usleep($this->idleTimeout * 1000);
+                    $extension->onIdle($context);
                 } else {
                     /** @var PsrQueue $queue */
                     foreach ($this->boundProcessors as list($queue, $processor)) {

From 26921e4a3d3f6fae5274337c8d2668dc11b9d300 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Wed, 15 Nov 2017 15:36:43 +0200
Subject: [PATCH 024/337] add homepage link to composer.json

---
 composer.json | 1 +
 1 file changed, 1 insertion(+)

diff --git a/composer.json b/composer.json
index 638efea..8b1d0ec 100644
--- a/composer.json
+++ b/composer.json
@@ -3,6 +3,7 @@
     "type": "library",
     "description": "Message Queue Library",
     "keywords": ["messaging", "queue", "amqp", "rabbitmq"],
+    "homepage": "https://github.com/php-enqueue/enqueue-dev",
     "license": "MIT",
     "require": {
         "php": ">=5.6",

From 3ba6c77d35517ed81b065c32e09bce8345072171 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Wed, 15 Nov 2017 12:18:06 +0200
Subject: [PATCH 025/337] Add Symfony4 support.

---
 Symfony/AmqpTransportFactory.php         |  2 ++
 Symfony/DefaultTransportFactory.php      | 13 +++++++------
 Symfony/RabbitMqAmqpTransportFactory.php |  1 +
 composer.json                            | 16 ++++++++--------
 4 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/Symfony/AmqpTransportFactory.php b/Symfony/AmqpTransportFactory.php
index 8a2a20e..0a749f7 100644
--- a/Symfony/AmqpTransportFactory.php
+++ b/Symfony/AmqpTransportFactory.php
@@ -166,6 +166,7 @@ public function createContext(ContainerBuilder $container, array $config)
         $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
 
         $context = new Definition(AmqpContext::class);
+        $context->setPublic(true);
         $context->setFactory([new Reference($factoryId), 'createContext']);
 
         $contextId = sprintf('enqueue.transport.%s.context', $this->getName());
@@ -180,6 +181,7 @@ public function createContext(ContainerBuilder $container, array $config)
     public function createDriver(ContainerBuilder $container, array $config)
     {
         $driver = new Definition(AmqpDriver::class);
+        $driver->setPublic(true);
         $driver->setArguments([
             new Reference(sprintf('enqueue.transport.%s.context', $this->getName())),
             new Reference('enqueue.client.config'),
diff --git a/Symfony/DefaultTransportFactory.php b/Symfony/DefaultTransportFactory.php
index 2497298..97734b8 100644
--- a/Symfony/DefaultTransportFactory.php
+++ b/Symfony/DefaultTransportFactory.php
@@ -18,6 +18,7 @@
 use Enqueue\Stomp\Symfony\StompTransportFactory;
 use Interop\Amqp\AmqpConnectionFactory;
 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
+use Symfony\Component\DependencyInjection\Alias;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use function Enqueue\dsn_to_connection_factory;
 
@@ -83,8 +84,8 @@ public function createConnectionFactory(ContainerBuilder $container, array $conf
 
         $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
 
-        $container->setAlias($factoryId, $aliasId);
-        $container->setAlias('enqueue.transport.connection_factory', $factoryId);
+        $container->setAlias($factoryId, new Alias($aliasId, true));
+        $container->setAlias('enqueue.transport.connection_factory', new Alias($factoryId, true));
 
         return $factoryId;
     }
@@ -104,8 +105,8 @@ public function createContext(ContainerBuilder $container, array $config)
 
         $contextId = sprintf('enqueue.transport.%s.context', $this->getName());
 
-        $container->setAlias($contextId, $aliasId);
-        $container->setAlias('enqueue.transport.context', $contextId);
+        $container->setAlias($contextId, new Alias($aliasId, true));
+        $container->setAlias('enqueue.transport.context', new Alias($contextId, true));
 
         return $contextId;
     }
@@ -125,8 +126,8 @@ public function createDriver(ContainerBuilder $container, array $config)
 
         $driverId = sprintf('enqueue.client.%s.driver', $this->getName());
 
-        $container->setAlias($driverId, $aliasId);
-        $container->setAlias('enqueue.client.driver', $driverId);
+        $container->setAlias($driverId, new Alias($aliasId, true));
+        $container->setAlias('enqueue.client.driver', new Alias($driverId, true));
 
         return $driverId;
     }
diff --git a/Symfony/RabbitMqAmqpTransportFactory.php b/Symfony/RabbitMqAmqpTransportFactory.php
index 2bd98e5..94ee923 100644
--- a/Symfony/RabbitMqAmqpTransportFactory.php
+++ b/Symfony/RabbitMqAmqpTransportFactory.php
@@ -55,6 +55,7 @@ public function createConnectionFactory(ContainerBuilder $container, array $conf
     public function createDriver(ContainerBuilder $container, array $config)
     {
         $driver = new Definition(RabbitMqDriver::class);
+        $driver->setPublic(true);
         $driver->setArguments([
             new Reference(sprintf('enqueue.transport.%s.context', $this->getName())),
             new Reference('enqueue.client.config'),
diff --git a/composer.json b/composer.json
index 8b1d0ec..8c25bb6 100644
--- a/composer.json
+++ b/composer.json
@@ -14,11 +14,11 @@
     },
     "require-dev": {
         "phpunit/phpunit": "~5.5",
-        "symfony/console": "^2.8|^3",
-        "symfony/dependency-injection": "^2.8|^3",
-        "symfony/config": "^2.8|^3",
-        "symfony/event-dispatcher": "^2.8|^3",
-        "symfony/http-kernel": "^2.8|^3",
+        "symfony/console": "^2.8|^3|^4",
+        "symfony/dependency-injection": "^2.8|^3|^4",
+        "symfony/config": "^2.8|^3|^4",
+        "symfony/event-dispatcher": "^2.8|^3|^4",
+        "symfony/http-kernel": "^2.8|^3|^4",
         "enqueue/amqp-ext": "^0.8@dev",
         "enqueue/amqp-lib": "^0.8@dev",
         "enqueue/amqp-bunny": "^0.8@dev",
@@ -36,9 +36,9 @@
         "empi89/php-amqp-stubs": "*@dev"
     },
     "suggest": {
-        "symfony/console": "^2.8|^3 If you want to use li commands",
-        "symfony/dependency-injection": "^2.8|^3",
-        "symfony/config": "^2.8|^3",
+        "symfony/console": "^2.8|^3|^4 If you want to use li commands",
+        "symfony/dependency-injection": "^2.8|^3|^4",
+        "symfony/config": "^2.8|^3|^4",
         "enqueue/amqp-ext": "AMQP transport (based on php extension)",
         "enqueue/stomp": "STOMP transport",
         "enqueue/fs": "Filesystem transport",

From dad79b517bcb21f298b3d4fa0efa1cee1bcc5523 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 14 Dec 2017 14:20:06 +0200
Subject: [PATCH 026/337] [composer] add support section.

fixes https://github.com/php-enqueue/enqueue-dev/issues/288
---
 composer.json | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/composer.json b/composer.json
index 8c25bb6..4fb3be5 100644
--- a/composer.json
+++ b/composer.json
@@ -46,6 +46,13 @@
         "enqueue/dbal": "Doctrine DBAL transport",
         "enqueue/sqs": "Amazon AWS SQS transport"
     },
+    "support": {
+        "email": "opensource@forma-pro.com",
+        "issues": "https://github.com/php-enqueue/enqueue-dev/issues",
+        "forum": "https://gitter.im/php-enqueue/Lobby",
+        "source": "https://github.com/php-enqueue/enqueue-dev",
+        "docs": "https://github.com/php-enqueue/enqueue-dev/blob/master/docs/index.md"
+    },
     "autoload": {
         "psr-4": { "Enqueue\\": "" },
         "files": ["functions_include.php"],

From 315f5e6ab0fef66d41a698fa7a8595a53cb31595 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 14 Dec 2017 14:31:06 +0200
Subject: [PATCH 027/337] [consumption] Add ability to overwrite logger.

---
 Consumption/Extension/LoggerExtension.php     | 12 ++++++++--
 .../Extension/LoggerExtensionTest.php         | 23 +++++++++++++++++++
 2 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/Consumption/Extension/LoggerExtension.php b/Consumption/Extension/LoggerExtension.php
index 1322c28..36cf01b 100644
--- a/Consumption/Extension/LoggerExtension.php
+++ b/Consumption/Extension/LoggerExtension.php
@@ -31,8 +31,16 @@ public function __construct(LoggerInterface $logger)
      */
     public function onStart(Context $context)
     {
-        $context->setLogger($this->logger);
-        $this->logger->debug(sprintf('Set context\'s logger %s', get_class($this->logger)));
+        if ($context->getLogger()) {
+            $context->getLogger()->debug(sprintf(
+                'Skip setting context\'s logger "%s". Another one "%s" has already been set.',
+                get_class($context->getLogger()),
+                get_class($this->logger)
+            ));
+        } else {
+            $context->setLogger($this->logger);
+            $this->logger->debug(sprintf('Set context\'s logger "%s"', get_class($this->logger)));
+        }
     }
 
     /**
diff --git a/Tests/Consumption/Extension/LoggerExtensionTest.php b/Tests/Consumption/Extension/LoggerExtensionTest.php
index 0414a92..68bc700 100644
--- a/Tests/Consumption/Extension/LoggerExtensionTest.php
+++ b/Tests/Consumption/Extension/LoggerExtensionTest.php
@@ -151,6 +151,29 @@ public function testShouldNotLogAckMessageStatusIfReasonIsEmpty()
         $extension->onPostReceived($context);
     }
 
+    public function testShouldNotSetLoggerIfOneHasBeenSetOnStart()
+    {
+        $logger = $this->createLogger();
+
+        $alreadySetLogger = $this->createLogger();
+        $alreadySetLogger
+            ->expects($this->once())
+            ->method('debug')
+            ->with(sprintf(
+                'Skip setting context\'s logger "%s". Another one "%s" has already been set.',
+                get_class($logger),
+                get_class($alreadySetLogger)
+            ))
+        ;
+
+        $extension = new LoggerExtension($logger);
+
+        $context = new Context($this->createPsrContextMock());
+        $context->setLogger($alreadySetLogger);
+
+        $extension->onStart($context);
+    }
+
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext
      */

From c15ac736abf58362fde4995c5dee2cc8787d4a4b Mon Sep 17 00:00:00 2001
From: Pascal de Vink <pascal.de.vink@gmail.com>
Date: Wed, 20 Dec 2017 15:22:27 +0100
Subject: [PATCH 028/337] Check if logger exists

When handling a signal, a logger is used to log the catched signals. However, the logger can sometimes be uninitialised, which resulted in errors.
---
 Consumption/Extension/SignalExtension.php | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/Consumption/Extension/SignalExtension.php b/Consumption/Extension/SignalExtension.php
index a3bab85..7b08f09 100644
--- a/Consumption/Extension/SignalExtension.php
+++ b/Consumption/Extension/SignalExtension.php
@@ -18,7 +18,7 @@ class SignalExtension implements ExtensionInterface
     protected $interruptConsumption = false;
 
     /**
-     * @var LoggerInterface
+     * @var LoggerInterface|null
      */
     protected $logger;
 
@@ -84,7 +84,10 @@ public function onIdle(Context $context)
     public function interruptExecutionIfNeeded(Context $context)
     {
         if (false == $context->isExecutionInterrupted() && $this->interruptConsumption) {
-            $this->logger->debug('[SignalExtension] Interrupt execution');
+            if ($this->logger) {
+                $this->logger->debug('[SignalExtension] Interrupt execution');
+            }
+            
             $context->setExecutionInterrupted($this->interruptConsumption);
 
             $this->interruptConsumption = false;
@@ -104,7 +107,10 @@ public function handleSignal($signal)
             case SIGTERM:  // 15 : supervisor default stop
             case SIGQUIT:  // 3  : kill -s QUIT
             case SIGINT:   // 2  : ctrl+c
-                $this->logger->debug('[SignalExtension] Interrupt consumption');
+                if ($this->logger) {
+                    $this->logger->debug('[SignalExtension] Interrupt consumption');
+                }
+                
                 $this->interruptConsumption = true;
                 break;
             default:

From b98945ab66fa056f275987dfbbb1c2b083190cce Mon Sep 17 00:00:00 2001
From: Pascal de Vink <pascal.de.vink@gmail.com>
Date: Wed, 20 Dec 2017 16:12:33 +0100
Subject: [PATCH 029/337] Remove whitespace on blank lines

---
 Consumption/Extension/SignalExtension.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Consumption/Extension/SignalExtension.php b/Consumption/Extension/SignalExtension.php
index 7b08f09..aeeadff 100644
--- a/Consumption/Extension/SignalExtension.php
+++ b/Consumption/Extension/SignalExtension.php
@@ -87,7 +87,7 @@ public function interruptExecutionIfNeeded(Context $context)
             if ($this->logger) {
                 $this->logger->debug('[SignalExtension] Interrupt execution');
             }
-            
+
             $context->setExecutionInterrupted($this->interruptConsumption);
 
             $this->interruptConsumption = false;
@@ -110,7 +110,7 @@ public function handleSignal($signal)
                 if ($this->logger) {
                     $this->logger->debug('[SignalExtension] Interrupt consumption');
                 }
-                
+
                 $this->interruptConsumption = true;
                 break;
             default:

From 148f48fd23bef10ca90df171c99476cb92d81eb8 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 4 Jan 2018 11:46:31 +0200
Subject: [PATCH 030/337] Add link to site.

---
 README.md     | 1 +
 composer.json | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index bb0b7c8..b8cb04c 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,7 @@ Read more about it in documentation.
 
 ## Resources
 
+* [Site](https://enqueue.forma-pro.com/)
 * [Documentation](https://github.com/php-enqueue/enqueue-dev/blob/master/docs/index.md)
 * [Questions](https://gitter.im/php-enqueue/Lobby)
 * [Issue Tracker](https://github.com/php-enqueue/enqueue-dev/issues)
diff --git a/composer.json b/composer.json
index 4fb3be5..180d594 100644
--- a/composer.json
+++ b/composer.json
@@ -3,7 +3,7 @@
     "type": "library",
     "description": "Message Queue Library",
     "keywords": ["messaging", "queue", "amqp", "rabbitmq"],
-    "homepage": "https://github.com/php-enqueue/enqueue-dev",
+    "homepage": "https://enqueue.forma-pro.com/",
     "license": "MIT",
     "require": {
         "php": ">=5.6",

From 6eb7f7d7fabf0ae7000b7449a696d8f1afcfc436 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 4 Jan 2018 13:21:23 +0200
Subject: [PATCH 031/337] [consumption] Fix signal handling when AMQP is used.

---
 Consumption/QueueConsumer.php | 70 ++++++++++++++++++-----------------
 1 file changed, 36 insertions(+), 34 deletions(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index bea21e5..49d0d06 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -168,39 +168,47 @@ public function consume(ExtensionInterface $runtimeExtension = null)
         $logger = $context->getLogger() ?: new NullLogger();
         $logger->info('Start consuming');
 
-        while (true) {
-            try {
-                if ($this->psrContext instanceof AmqpContext) {
-                    $callback = function (AmqpMessage $message, AmqpConsumer $consumer) use ($extension, $logger, &$context) {
-                        $currentProcessor = null;
+        if ($this->psrContext instanceof AmqpContext) {
+            $callback = function (AmqpMessage $message, AmqpConsumer $consumer) use ($extension, $logger, &$context) {
+                $currentProcessor = null;
+
+                /** @var PsrQueue $queue */
+                foreach ($this->boundProcessors as list($queue, $processor)) {
+                    if ($queue->getQueueName() === $consumer->getQueue()->getQueueName()) {
+                        $currentProcessor = $processor;
+                    }
+                }
 
-                        /** @var PsrQueue $queue */
-                        foreach ($this->boundProcessors as list($queue, $processor)) {
-                            if ($queue->getQueueName() === $consumer->getQueue()->getQueueName()) {
-                                $currentProcessor = $processor;
-                            }
-                        }
+                if (false == $currentProcessor) {
+                    throw new \LogicException(sprintf('The processor for the queue "%s" could not be found.', $consumer->getQueue()->getQueueName()));
+                }
 
-                        if (false == $currentProcessor) {
-                            throw new \LogicException(sprintf('The processor for the queue "%s" could not be found.', $consumer->getQueue()->getQueueName()));
-                        }
+                $context = new Context($this->psrContext);
+                $context->setLogger($logger);
+                $context->setPsrQueue($consumer->getQueue());
+                $context->setPsrConsumer($consumer);
+                $context->setPsrProcessor($currentProcessor);
+                $context->setPsrMessage($message);
 
-                        $context = new Context($this->psrContext);
-                        $context->setLogger($logger);
-                        $context->setPsrQueue($consumer->getQueue());
-                        $context->setPsrConsumer($consumer);
-                        $context->setPsrProcessor($currentProcessor);
-                        $context->setPsrMessage($message);
+                $this->doConsume($extension, $context);
 
-                        $this->doConsume($extension, $context);
+                return true;
+            };
 
-                        return true;
-                    };
+            foreach ($consumers as $consumer) {
+                /* @var AmqpConsumer $consumer */
 
-                    foreach ($consumers as $consumer) {
-                        /* @var AmqpConsumer $consumer */
+                $this->psrContext->subscribe($consumer, $callback);
+            }
+        }
 
-                        $this->psrContext->subscribe($consumer, $callback);
+        while (true) {
+            try {
+                if ($this->psrContext instanceof AmqpContext) {
+                    $extension->onBeforeReceive($context);
+
+                    if ($context->isExecutionInterrupted()) {
+                        throw new ConsumptionInterruptedException();
                     }
 
                     $this->psrContext->consume($this->receiveTimeout);
@@ -266,16 +274,14 @@ protected function doConsume(ExtensionInterface $extension, Context $context)
         $consumer = $context->getPsrConsumer();
         $logger = $context->getLogger();
 
-        if (false == $context->getPsrMessage() instanceof AmqpContext) {
-            $extension->onBeforeReceive($context);
-        }
-
         if ($context->isExecutionInterrupted()) {
             throw new ConsumptionInterruptedException();
         }
 
         $message = $context->getPsrMessage();
         if (false == $message) {
+            $extension->onBeforeReceive($context);
+
             if ($message = $consumer->receive($this->receiveTimeout)) {
                 $context->setPsrMessage($message);
             }
@@ -312,10 +318,6 @@ protected function doConsume(ExtensionInterface $extension, Context $context)
             $logger->info(sprintf('Message processed: %s', $context->getResult()));
 
             $extension->onPostReceived($context);
-
-            if ($context->getPsrMessage() instanceof AmqpContext) {
-                $extension->onBeforeReceive($context);
-            }
         } else {
             usleep($this->idleTimeout * 1000);
             $extension->onIdle($context);

From 376c848538b1ddad584cd1c0d8a4af003dbf74d1 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 9 Jan 2018 16:28:28 +0200
Subject: [PATCH 032/337] [amqp] Fix socket and signal issue.

---
 Consumption/Extension/SignalExtension.php | 2 ++
 Symfony/AmqpTransportFactory.php          | 6 ++++--
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/Consumption/Extension/SignalExtension.php b/Consumption/Extension/SignalExtension.php
index aeeadff..de5b3fe 100644
--- a/Consumption/Extension/SignalExtension.php
+++ b/Consumption/Extension/SignalExtension.php
@@ -31,6 +31,8 @@ public function onStart(Context $context)
             throw new LogicException('The pcntl extension is required in order to catch signals.');
         }
 
+        pcntl_async_signals(true);
+
         pcntl_signal(SIGTERM, [$this, 'handleSignal']);
         pcntl_signal(SIGQUIT, [$this, 'handleSignal']);
         pcntl_signal(SIGINT, [$this, 'handleSignal']);
diff --git a/Symfony/AmqpTransportFactory.php b/Symfony/AmqpTransportFactory.php
index 0a749f7..e69bf44 100644
--- a/Symfony/AmqpTransportFactory.php
+++ b/Symfony/AmqpTransportFactory.php
@@ -58,9 +58,11 @@ public function addConfiguration(ArrayNodeDefinition $builder)
                             throw new \InvalidArgumentException('There is no amqp driver available. Please consider installing one of the packages: enqueue/amqp-ext, enqueue/amqp-lib, enqueue/amqp-bunny.');
                         }
 
-                        if (isset($v['driver']) && false == in_array($v['driver'], $drivers, true)) {
-                            throw new \InvalidArgumentException(sprintf('Unexpected driver given "invalidDriver". Available are "%s"', implode('", "', $drivers)));
+                        if ($v && false == in_array($v, $drivers, true)) {
+                            throw new \InvalidArgumentException(sprintf('Unexpected driver given "%s". Available are "%s"', $v, implode('", "', $drivers)));
                         }
+
+                        return $v;
                     })
                     ->end()
                 ->end()

From 9dc346d202668564855dda27f27a17d5aeea11b9 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 9 Jan 2018 18:20:39 +0200
Subject: [PATCH 033/337] [amqp] Add test for SignalSocketHelper.

---
 Consumption/Extension/SignalExtension.php | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/Consumption/Extension/SignalExtension.php b/Consumption/Extension/SignalExtension.php
index de5b3fe..a8b53e8 100644
--- a/Consumption/Extension/SignalExtension.php
+++ b/Consumption/Extension/SignalExtension.php
@@ -31,7 +31,9 @@ public function onStart(Context $context)
             throw new LogicException('The pcntl extension is required in order to catch signals.');
         }
 
-        pcntl_async_signals(true);
+        if (function_exists('pcntl_async_signals')) {
+            pcntl_async_signals(true);
+        }
 
         pcntl_signal(SIGTERM, [$this, 'handleSignal']);
         pcntl_signal(SIGQUIT, [$this, 'handleSignal']);
@@ -47,7 +49,7 @@ public function onBeforeReceive(Context $context)
     {
         $this->logger = $context->getLogger();
 
-        pcntl_signal_dispatch();
+        $this->dispatchSignal();
 
         $this->interruptExecutionIfNeeded($context);
     }
@@ -65,7 +67,7 @@ public function onPreReceived(Context $context)
      */
     public function onPostReceived(Context $context)
     {
-        pcntl_signal_dispatch();
+        $this->dispatchSignal();
 
         $this->interruptExecutionIfNeeded($context);
     }
@@ -75,7 +77,7 @@ public function onPostReceived(Context $context)
      */
     public function onIdle(Context $context)
     {
-        pcntl_signal_dispatch();
+        $this->dispatchSignal();
 
         $this->interruptExecutionIfNeeded($context);
     }
@@ -119,4 +121,11 @@ public function handleSignal($signal)
                 break;
         }
     }
+
+    private function dispatchSignal()
+    {
+        if (false == function_exists('pcntl_async_signals')) {
+            pcntl_signal_dispatch();
+        }
+    }
 }

From d2e5c5a7c278eb5ebdf1262ad0a5a066d0d7778b Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 9 Jan 2018 18:40:53 +0200
Subject: [PATCH 034/337] [amqp][symfony] Fix driver is not properly set bug.

---
 Tests/Symfony/AmqpTransportFactoryTest.php | 33 ++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/Tests/Symfony/AmqpTransportFactoryTest.php b/Tests/Symfony/AmqpTransportFactoryTest.php
index d8bb5e1..b27f5b5 100644
--- a/Tests/Symfony/AmqpTransportFactoryTest.php
+++ b/Tests/Symfony/AmqpTransportFactoryTest.php
@@ -137,6 +137,39 @@ public function testShouldAllowAddSslOptions()
         ], $config);
     }
 
+    public function testThrowIfNotSupportedDriverSet()
+    {
+        $transport = new AmqpTransportFactory();
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+
+        $this->expectException(InvalidConfigurationException::class);
+        $this->expectExceptionMessage('Invalid configuration for path "foo.driver": Unexpected driver given "invalidDriver"');
+        $processor->process($tb->buildTree(), [[
+            'driver' => 'invalidDriver',
+        ]]);
+    }
+
+    public function testShouldAllowSetDriver()
+    {
+        $transport = new AmqpTransportFactory();
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+        $config = $processor->process($tb->buildTree(), [[
+            'driver' => 'ext',
+        ]]);
+
+        $this->assertEquals([
+            'driver' => 'ext',
+        ], $config);
+    }
+
     public function testShouldAllowAddConfigurationAsString()
     {
         $transport = new AmqpTransportFactory();

From e46505f206310d99649f2a2043611e2945ff5ea4 Mon Sep 17 00:00:00 2001
From: Max Kotliar <kotlyar.maksim@gmail.com>
Date: Thu, 11 Jan 2018 14:35:46 +0200
Subject: [PATCH 035/337] [consumption] Correct message in LoggerExtension

---
 Consumption/Extension/LoggerExtension.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Consumption/Extension/LoggerExtension.php b/Consumption/Extension/LoggerExtension.php
index 36cf01b..0779c90 100644
--- a/Consumption/Extension/LoggerExtension.php
+++ b/Consumption/Extension/LoggerExtension.php
@@ -34,8 +34,8 @@ public function onStart(Context $context)
         if ($context->getLogger()) {
             $context->getLogger()->debug(sprintf(
                 'Skip setting context\'s logger "%s". Another one "%s" has already been set.',
-                get_class($context->getLogger()),
-                get_class($this->logger)
+                get_class($this->logger),
+                get_class($context->getLogger())
             ));
         } else {
             $context->setLogger($this->logger);

From 075f381a7347a80b39d821fd0615c23c720ee92d Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Wed, 17 Jan 2018 09:57:24 +0200
Subject: [PATCH 036/337] 0.9

---
 composer.json | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/composer.json b/composer.json
index 180d594..09346d5 100644
--- a/composer.json
+++ b/composer.json
@@ -8,7 +8,7 @@
     "require": {
         "php": ">=5.6",
         "queue-interop/queue-interop": "^0.6@dev",
-        "enqueue/null": "^0.8@dev",
+        "enqueue/null": "^0.9@dev",
         "ramsey/uuid": "^2|^3.5",
         "psr/log": "^1"
     },
@@ -19,20 +19,20 @@
         "symfony/config": "^2.8|^3|^4",
         "symfony/event-dispatcher": "^2.8|^3|^4",
         "symfony/http-kernel": "^2.8|^3|^4",
-        "enqueue/amqp-ext": "^0.8@dev",
-        "enqueue/amqp-lib": "^0.8@dev",
-        "enqueue/amqp-bunny": "^0.8@dev",
-        "enqueue/pheanstalk": "^0.8@dev",
-        "enqueue/gearman": "^0.8@dev",
-        "enqueue/rdkafka": "^0.8@dev",
-        "enqueue/dbal": "^0.8@dev",
-        "enqueue/fs": "^0.8@dev",
-        "enqueue/gps": "^0.8@dev",
-        "enqueue/redis": "^0.8@dev",
-        "enqueue/sqs": "^0.8@dev",
-        "enqueue/stomp": "^0.8@dev",
-        "enqueue/test": "^0.8@dev",
-        "enqueue/simple-client": "^0.8@dev",
+        "enqueue/amqp-ext": "^0.9@dev",
+        "enqueue/amqp-lib": "^0.9@dev",
+        "enqueue/amqp-bunny": "^0.9@dev",
+        "enqueue/pheanstalk": "^0.9@dev",
+        "enqueue/gearman": "^0.9@dev",
+        "enqueue/rdkafka": "^0.9@dev",
+        "enqueue/dbal": "^0.9@dev",
+        "enqueue/fs": "^0.9@dev",
+        "enqueue/gps": "^0.9@dev",
+        "enqueue/redis": "^0.9@dev",
+        "enqueue/sqs": "^0.9@dev",
+        "enqueue/stomp": "^0.9@dev",
+        "enqueue/test": "^0.9@dev",
+        "enqueue/simple-client": "^0.9@dev",
         "empi89/php-amqp-stubs": "*@dev"
     },
     "suggest": {
@@ -63,7 +63,7 @@
     "minimum-stability": "dev",
     "extra": {
         "branch-alias": {
-            "dev-master": "0.8.x-dev"
+            "dev-master": "0.9.x-dev"
         }
     }
 }

From ce4ee98f1e3b99f7ae7613088a5439b50db08d41 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 18 Jan 2018 13:09:31 +0200
Subject: [PATCH 037/337] [bundle][dx] Add a message that suggest intalling a
 pkg to use the transport.

---
 Symfony/MissingTransportFactory.php           | 87 +++++++++++++++++++
 Tests/Symfony/MissingTransportFactoryTest.php | 56 ++++++++++++
 2 files changed, 143 insertions(+)
 create mode 100644 Symfony/MissingTransportFactory.php
 create mode 100644 Tests/Symfony/MissingTransportFactoryTest.php

diff --git a/Symfony/MissingTransportFactory.php b/Symfony/MissingTransportFactory.php
new file mode 100644
index 0000000..4ce3982
--- /dev/null
+++ b/Symfony/MissingTransportFactory.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace Enqueue\Symfony;
+
+use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+class MissingTransportFactory implements TransportFactoryInterface, DriverFactoryInterface
+{
+    /**
+     * @var string
+     */
+    private $name;
+    /**
+     * @var string[]
+     */
+    private $packages;
+
+    /**
+     * @param string   $name
+     * @param string[] $packages
+     */
+    public function __construct($name, array $packages)
+    {
+        $this->name = $name;
+        $this->packages = $packages;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addConfiguration(ArrayNodeDefinition $builder)
+    {
+        if (1 == count($this->packages)) {
+            $message = sprintf(
+                'In order to use the transport "%s" install a package "%s"',
+                $this->getName(),
+                implode('", "', $this->packages)
+            );
+        } else {
+            $message = sprintf(
+                'In order to use the transport "%s" install one of the packages "%s"',
+                $this->getName(),
+                implode('", "', $this->packages)
+            );
+        }
+
+        $builder
+            ->info($message)
+            ->validate()->always(function () use ($message) {
+                throw new \InvalidArgumentException($message);
+            })
+        ;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createConnectionFactory(ContainerBuilder $container, array $config)
+    {
+        throw new \LogicException('Should not be called');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createContext(ContainerBuilder $container, array $config)
+    {
+        throw new \LogicException('Should not be called');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createDriver(ContainerBuilder $container, array $config)
+    {
+        throw new \LogicException('Should not be called');
+    }
+
+    /**
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+}
diff --git a/Tests/Symfony/MissingTransportFactoryTest.php b/Tests/Symfony/MissingTransportFactoryTest.php
new file mode 100644
index 0000000..a296953
--- /dev/null
+++ b/Tests/Symfony/MissingTransportFactoryTest.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Enqueue\Tests\Symfony;
+
+use Enqueue\Symfony\MissingTransportFactory;
+use Enqueue\Symfony\TransportFactoryInterface;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Definition\Builder\TreeBuilder;
+use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
+use Symfony\Component\Config\Definition\Processor;
+
+class MissingTransportFactoryTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementTransportFactoryInterface()
+    {
+        $this->assertClassImplements(TransportFactoryInterface::class, MissingTransportFactory::class);
+    }
+
+    public function testCouldBeConstructedWithNameAndPackages()
+    {
+        $transport = new MissingTransportFactory('aMissingTransportName', ['aPackage', 'anotherPackage']);
+
+        $this->assertEquals('aMissingTransportName', $transport->getName());
+    }
+
+    public function testThrowOnProcessForOnePackageToInstall()
+    {
+        $transport = new MissingTransportFactory('aMissingTransportName', ['aFooPackage']);
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+
+        $this->expectException(InvalidConfigurationException::class);
+        $this->expectExceptionMessage('Invalid configuration for path "foo": In order to use the transport "aMissingTransportName" install a package "aFooPackage"');
+        $processor->process($tb->buildTree(), [[]]);
+    }
+
+    public function testThrowOnProcessForSeveralPackagesToInstall()
+    {
+        $transport = new MissingTransportFactory('aMissingTransportName', ['aFooPackage', 'aBarPackage']);
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+
+        $this->expectException(InvalidConfigurationException::class);
+        $this->expectExceptionMessage('Invalid configuration for path "foo": In order to use the transport "aMissingTransportName" install one of the packages "aFooPackage", "aBarPackage"');
+        $processor->process($tb->buildTree(), [[]]);
+    }
+}

From 9eeff42a34f4428e3f131eab431ac97f5d05f3b8 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 18 Jan 2018 13:28:52 +0200
Subject: [PATCH 038/337] [bundle] Remove any config options.

---
 Symfony/MissingTransportFactory.php           | 13 ++++++++++---
 Tests/Symfony/MissingTransportFactoryTest.php | 17 +++++++++++++++++
 2 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/Symfony/MissingTransportFactory.php b/Symfony/MissingTransportFactory.php
index 4ce3982..25e6791 100644
--- a/Symfony/MissingTransportFactory.php
+++ b/Symfony/MissingTransportFactory.php
@@ -47,9 +47,16 @@ public function addConfiguration(ArrayNodeDefinition $builder)
 
         $builder
             ->info($message)
-            ->validate()->always(function () use ($message) {
-                throw new \InvalidArgumentException($message);
-            })
+            ->beforeNormalization()
+                ->always(function () {
+                    return [];
+                })
+                ->end()
+            ->validate()
+                ->always(function () use ($message) {
+                    throw new \InvalidArgumentException($message);
+                })
+                ->end()
         ;
     }
 
diff --git a/Tests/Symfony/MissingTransportFactoryTest.php b/Tests/Symfony/MissingTransportFactoryTest.php
index a296953..b466a5b 100644
--- a/Tests/Symfony/MissingTransportFactoryTest.php
+++ b/Tests/Symfony/MissingTransportFactoryTest.php
@@ -53,4 +53,21 @@ public function testThrowOnProcessForSeveralPackagesToInstall()
         $this->expectExceptionMessage('Invalid configuration for path "foo": In order to use the transport "aMissingTransportName" install one of the packages "aFooPackage", "aBarPackage"');
         $processor->process($tb->buildTree(), [[]]);
     }
+
+    public function testThrowEvenIfThereAreSomeOptionsPassed()
+    {
+        $transport = new MissingTransportFactory('aMissingTransportName', ['aFooPackage', 'aBarPackage']);
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+
+        $this->expectException(InvalidConfigurationException::class);
+        $this->expectExceptionMessage('In order to use the transport "aMissingTransportName"');
+        $processor->process($tb->buildTree(), [[
+            'foo' => 'fooVal',
+            'bar' => 'barVal',
+        ]]);
+    }
 }

From 8875954380fc574d8b5873175b4ae1e921470383 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 18 Jan 2018 13:49:16 +0200
Subject: [PATCH 039/337] [consumption] Make QueueConsumer final

---
 Consumption/QueueConsumer.php | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 49d0d06..6f39c01 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -15,7 +15,7 @@
 use Interop\Queue\PsrQueue;
 use Psr\Log\NullLogger;
 
-class QueueConsumer
+final class QueueConsumer
 {
     /**
      * @var PsrContext
@@ -268,7 +268,7 @@ public function consume(ExtensionInterface $runtimeExtension = null)
      *
      * @return bool
      */
-    protected function doConsume(ExtensionInterface $extension, Context $context)
+    private function doConsume(ExtensionInterface $extension, Context $context)
     {
         $processor = $context->getPsrProcessor();
         $consumer = $context->getPsrConsumer();
@@ -334,7 +334,7 @@ protected function doConsume(ExtensionInterface $extension, Context $context)
      *
      * @throws \Exception
      */
-    protected function onInterruptionByException(ExtensionInterface $extension, Context $context)
+    private function onInterruptionByException(ExtensionInterface $extension, Context $context)
     {
         $logger = $context->getLogger();
         $logger->error(sprintf('Consuming interrupted by exception'));

From 30dbfcfe893c69d825411467b1555d83f20e3def Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 18 Jan 2018 18:05:04 +0200
Subject: [PATCH 040/337] [consumption] Prepare QueueConsumer for changes in
 0.9

---
 Consumption/QueueConsumer.php | 132 +++++++++++++++++++---------------
 1 file changed, 75 insertions(+), 57 deletions(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 49d0d06..a446403 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -11,8 +11,10 @@
 use Interop\Amqp\AmqpMessage;
 use Interop\Queue\PsrConsumer;
 use Interop\Queue\PsrContext;
+use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrProcessor;
 use Interop\Queue\PsrQueue;
+use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
 class QueueConsumer
@@ -23,9 +25,9 @@ class QueueConsumer
     private $psrContext;
 
     /**
-     * @var ExtensionInterface|ChainExtension|null
+     * @var ExtensionInterface|ChainExtension
      */
-    private $extension;
+    private $staticExtension;
 
     /**
      * [
@@ -46,6 +48,16 @@ class QueueConsumer
      */
     private $receiveTimeout;
 
+    /**
+     * @var ExtensionInterface|ChainExtension
+     */
+    private $extension;
+
+    /**
+     * @var LoggerInterface
+     */
+    private $logger;
+
     /**
      * @param PsrContext                             $psrContext
      * @param ExtensionInterface|ChainExtension|null $extension
@@ -59,11 +71,12 @@ public function __construct(
         $receiveTimeout = 10
     ) {
         $this->psrContext = $psrContext;
-        $this->extension = $extension;
+        $this->staticExtension = $extension ?: new ChainExtension([]);
         $this->idleTimeout = $idleTimeout;
         $this->receiveTimeout = $receiveTimeout;
 
         $this->boundProcessors = [];
+        $this->logger = new NullLogger();
     }
 
     /**
@@ -157,19 +170,19 @@ public function consume(ExtensionInterface $runtimeExtension = null)
             $consumers[$queue->getQueueName()] = $this->psrContext->createConsumer($queue);
         }
 
-        $extension = $this->extension ?: new ChainExtension([]);
-        if ($runtimeExtension) {
-            $extension = new ChainExtension([$extension, $runtimeExtension]);
-        }
+        $this->extension = $runtimeExtension ?
+            new ChainExtension([$this->staticExtension, $runtimeExtension]) :
+            $this->staticExtension
+        ;
 
         $context = new Context($this->psrContext);
-        $extension->onStart($context);
+        $this->extension->onStart($context);
 
-        $logger = $context->getLogger() ?: new NullLogger();
-        $logger->info('Start consuming');
+        $this->logger = $context->getLogger() ?: new NullLogger();
+        $this->logger->info('Start consuming');
 
         if ($this->psrContext instanceof AmqpContext) {
-            $callback = function (AmqpMessage $message, AmqpConsumer $consumer) use ($extension, $logger, &$context) {
+            $callback = function (AmqpMessage $message, AmqpConsumer $consumer) use (&$context) {
                 $currentProcessor = null;
 
                 /** @var PsrQueue $queue */
@@ -184,13 +197,13 @@ public function consume(ExtensionInterface $runtimeExtension = null)
                 }
 
                 $context = new Context($this->psrContext);
-                $context->setLogger($logger);
+                $context->setLogger($this->logger);
                 $context->setPsrQueue($consumer->getQueue());
                 $context->setPsrConsumer($consumer);
                 $context->setPsrProcessor($currentProcessor);
                 $context->setPsrMessage($message);
 
-                $this->doConsume($extension, $context);
+                $this->doConsume($this->extension, $context);
 
                 return true;
             };
@@ -205,7 +218,7 @@ public function consume(ExtensionInterface $runtimeExtension = null)
         while (true) {
             try {
                 if ($this->psrContext instanceof AmqpContext) {
-                    $extension->onBeforeReceive($context);
+                    $this->extension->onBeforeReceive($context);
 
                     if ($context->isExecutionInterrupted()) {
                         throw new ConsumptionInterruptedException();
@@ -214,23 +227,23 @@ public function consume(ExtensionInterface $runtimeExtension = null)
                     $this->psrContext->consume($this->receiveTimeout);
 
                     usleep($this->idleTimeout * 1000);
-                    $extension->onIdle($context);
+                    $this->extension->onIdle($context);
                 } else {
                     /** @var PsrQueue $queue */
                     foreach ($this->boundProcessors as list($queue, $processor)) {
                         $consumer = $consumers[$queue->getQueueName()];
 
                         $context = new Context($this->psrContext);
-                        $context->setLogger($logger);
+                        $context->setLogger($this->logger);
                         $context->setPsrQueue($queue);
                         $context->setPsrConsumer($consumer);
                         $context->setPsrProcessor($processor);
 
-                        $this->doConsume($extension, $context);
+                        $this->doConsume($this->extension, $context);
                     }
                 }
             } catch (ConsumptionInterruptedException $e) {
-                $logger->info(sprintf('Consuming interrupted'));
+                $this->logger->info(sprintf('Consuming interrupted'));
 
                 if ($this->psrContext instanceof AmqpContext) {
                     foreach ($consumers as $consumer) {
@@ -242,7 +255,7 @@ public function consume(ExtensionInterface $runtimeExtension = null)
 
                 $context->setExecutionInterrupted(true);
 
-                $extension->onInterrupted($context);
+                $this->extension->onInterrupted($context);
 
                 return;
             } catch (\Exception $exception) {
@@ -250,7 +263,7 @@ public function consume(ExtensionInterface $runtimeExtension = null)
                 $context->setException($exception);
 
                 try {
-                    $this->onInterruptionByException($extension, $context);
+                    $this->onInterruptionByException($this->extension, $context);
                 } catch (\Exception $e) {
                     // for some reason finally does not work here on php5.5
 
@@ -272,7 +285,7 @@ protected function doConsume(ExtensionInterface $extension, Context $context)
     {
         $processor = $context->getPsrProcessor();
         $consumer = $context->getPsrConsumer();
-        $logger = $context->getLogger();
+        $this->logger = $context->getLogger();
 
         if ($context->isExecutionInterrupted()) {
             throw new ConsumptionInterruptedException();
@@ -280,7 +293,7 @@ protected function doConsume(ExtensionInterface $extension, Context $context)
 
         $message = $context->getPsrMessage();
         if (false == $message) {
-            $extension->onBeforeReceive($context);
+            $this->extension->onBeforeReceive($context);
 
             if ($message = $consumer->receive($this->receiveTimeout)) {
                 $context->setPsrMessage($message);
@@ -288,39 +301,10 @@ protected function doConsume(ExtensionInterface $extension, Context $context)
         }
 
         if ($message) {
-            $logger->info('Message received from the queue: '.$context->getPsrQueue()->getQueueName());
-            $logger->debug('Headers: {headers}', ['headers' => new VarExport($message->getHeaders())]);
-            $logger->debug('Properties: {properties}', ['properties' => new VarExport($message->getProperties())]);
-            $logger->debug('Payload: {payload}', ['payload' => new VarExport($message->getBody())]);
-
-            $extension->onPreReceived($context);
-            if (!$context->getResult()) {
-                $result = $processor->process($message, $this->psrContext);
-                $context->setResult($result);
-            }
-
-            $extension->onResult($context);
-
-            switch ($context->getResult()) {
-                case Result::ACK:
-                    $consumer->acknowledge($message);
-                    break;
-                case Result::REJECT:
-                    $consumer->reject($message, false);
-                    break;
-                case Result::REQUEUE:
-                    $consumer->reject($message, true);
-                    break;
-                default:
-                    throw new \LogicException(sprintf('Status is not supported: %s', $context->getResult()));
-            }
-
-            $logger->info(sprintf('Message processed: %s', $context->getResult()));
-
-            $extension->onPostReceived($context);
+            $this->processMessage($consumer, $processor, $message, $context);
         } else {
             usleep($this->idleTimeout * 1000);
-            $extension->onIdle($context);
+            $this->extension->onIdle($context);
         }
 
         if ($context->isExecutionInterrupted()) {
@@ -336,16 +320,16 @@ protected function doConsume(ExtensionInterface $extension, Context $context)
      */
     protected function onInterruptionByException(ExtensionInterface $extension, Context $context)
     {
-        $logger = $context->getLogger();
-        $logger->error(sprintf('Consuming interrupted by exception'));
+        $this->logger = $context->getLogger();
+        $this->logger->error(sprintf('Consuming interrupted by exception'));
 
         $exception = $context->getException();
 
         try {
-            $extension->onInterrupted($context);
+            $this->extension->onInterrupted($context);
         } catch (\Exception $e) {
             // logic is similar to one in Symfony's ExceptionListener::onKernelException
-            $logger->error(sprintf(
+            $this->logger->error(sprintf(
                 'Exception thrown when handling an exception (%s: %s at %s line %s)',
                 get_class($e),
                 $e->getMessage(),
@@ -369,4 +353,38 @@ protected function onInterruptionByException(ExtensionInterface $extension, Cont
 
         throw $exception;
     }
+
+    private function processMessage(PsrConsumer $consumer, PsrProcessor $processor, PsrMessage $message, Context $context)
+    {
+        $this->logger->info('Message received from the queue: '.$context->getPsrQueue()->getQueueName());
+        $this->logger->debug('Headers: {headers}', ['headers' => new VarExport($message->getHeaders())]);
+        $this->logger->debug('Properties: {properties}', ['properties' => new VarExport($message->getProperties())]);
+        $this->logger->debug('Payload: {payload}', ['payload' => new VarExport($message->getBody())]);
+
+        $this->extension->onPreReceived($context);
+        if (!$context->getResult()) {
+            $result = $processor->process($message, $this->psrContext);
+            $context->setResult($result);
+        }
+
+        $this->extension->onResult($context);
+
+        switch ($context->getResult()) {
+            case Result::ACK:
+                $consumer->acknowledge($message);
+                break;
+            case Result::REJECT:
+                $consumer->reject($message, false);
+                break;
+            case Result::REQUEUE:
+                $consumer->reject($message, true);
+                break;
+            default:
+                throw new \LogicException(sprintf('Status is not supported: %s', $context->getResult()));
+        }
+
+        $this->logger->info(sprintf('Message processed: %s', $context->getResult()));
+
+        $this->extension->onPostReceived($context);
+    }
 }

From c8edd4880f9d6eff27306a66dd652c8a47e71b08 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Wed, 7 Feb 2018 09:18:58 +0200
Subject: [PATCH 041/337] [bundle] DefaultTransportFactory should accept DSN
 like foo:

---
 Tests/Symfony/DefaultTransportFactoryTest.php | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/Tests/Symfony/DefaultTransportFactoryTest.php b/Tests/Symfony/DefaultTransportFactoryTest.php
index 7f84d6d..9a013fd 100644
--- a/Tests/Symfony/DefaultTransportFactoryTest.php
+++ b/Tests/Symfony/DefaultTransportFactoryTest.php
@@ -72,6 +72,24 @@ public function testShouldAllowAddConfigurationAsDsn()
         $this->assertEquals(['dsn' => 'dsn://'], $config);
     }
 
+    /**
+     * @see https://github.com/php-enqueue/enqueue-dev/issues/356
+     *
+     * @group bug
+     */
+    public function testShouldAllowAddConfigurationAsDsnWithoutSlashes()
+    {
+        $transport = new DefaultTransportFactory();
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+        $config = $processor->process($tb->buildTree(), ['dsn:']);
+
+        $this->assertEquals(['dsn' => 'dsn:'], $config);
+    }
+
     public function testShouldSetNullTransportByDefault()
     {
         $transport = new DefaultTransportFactory();

From f86efa503d9e8bb26e6b29da99318dd06d99c083 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Wed, 7 Feb 2018 09:24:17 +0200
Subject: [PATCH 042/337] [bundle] fix dsn

---
 Symfony/DefaultTransportFactory.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Symfony/DefaultTransportFactory.php b/Symfony/DefaultTransportFactory.php
index 97734b8..20fa1c9 100644
--- a/Symfony/DefaultTransportFactory.php
+++ b/Symfony/DefaultTransportFactory.php
@@ -58,7 +58,7 @@ public function addConfiguration(ArrayNodeDefinition $builder)
                     }
 
                     if (is_string($v)) {
-                        return false !== strpos($v, '://') || false !== strpos($v, 'env_') ?
+                        return false !== strpos($v, ':') || false !== strpos($v, 'env_') ?
                             ['dsn' => $v] :
                             ['alias' => $v];
                     }

From 06c956cb2bc08335567e4fdc5a5c11efd46d104b Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 9 Feb 2018 09:22:50 +0200
Subject: [PATCH 043/337] [enqueue] fix build on travis.

---
 composer.json | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/composer.json b/composer.json
index 180d594..62326bd 100644
--- a/composer.json
+++ b/composer.json
@@ -60,6 +60,11 @@
             "/Tests/"
         ]
     },
+    "config": {
+        "platform": {
+            "ext-amqp": "1.9.3"
+        }
+    },
     "minimum-stability": "dev",
     "extra": {
         "branch-alias": {

From 7025c4a042131dd209775be968bfba2f9c377166 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 9 Feb 2018 09:26:37 +0200
Subject: [PATCH 044/337] test travis build branch

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 62326bd..ff50939 100644
--- a/composer.json
+++ b/composer.json
@@ -62,7 +62,7 @@
     },
     "config": {
         "platform": {
-            "ext-amqp": "1.9.3"
+             "ext-amqp": "1.9.3"
         }
     },
     "minimum-stability": "dev",

From ffb4214e01985e51d6471d029871d16a4d8ef4f4 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 9 Feb 2018 09:26:50 +0200
Subject: [PATCH 045/337] test travis build branch

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index ff50939..62326bd 100644
--- a/composer.json
+++ b/composer.json
@@ -62,7 +62,7 @@
     },
     "config": {
         "platform": {
-             "ext-amqp": "1.9.3"
+            "ext-amqp": "1.9.3"
         }
     },
     "minimum-stability": "dev",

From a4cecd87e12a12ea80a994e49cc05513380986d7 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 16 Feb 2018 13:02:58 +0200
Subject: [PATCH 046/337] php cs fixes.

---
 Client/Producer.php              |  4 ++--
 Tests/Consumption/ResultTest.php | 10 +++++-----
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/Client/Producer.php b/Client/Producer.php
index a3dfacf..8fda47f 100644
--- a/Client/Producer.php
+++ b/Client/Producer.php
@@ -151,7 +151,7 @@ private function prepareBody(Message $message)
             $contentType = $contentType ?: 'text/plain';
             $body = (string) $body;
         } elseif (is_array($body)) {
-            if ($contentType && $contentType !== 'application/json') {
+            if ($contentType && 'application/json' !== $contentType) {
                 throw new \LogicException(sprintf('Content type "application/json" only allowed when body is array'));
             }
 
@@ -168,7 +168,7 @@ private function prepareBody(Message $message)
             $contentType = 'application/json';
             $body = JSON::encode($body);
         } elseif ($body instanceof \JsonSerializable) {
-            if ($contentType && $contentType !== 'application/json') {
+            if ($contentType && 'application/json' !== $contentType) {
                 throw new \LogicException(sprintf('Content type "application/json" only allowed when body is array'));
             }
 
diff --git a/Tests/Consumption/ResultTest.php b/Tests/Consumption/ResultTest.php
index 94abb9e..26b8c78 100644
--- a/Tests/Consumption/ResultTest.php
+++ b/Tests/Consumption/ResultTest.php
@@ -14,13 +14,13 @@ public function testCouldBeConstructedWithExpectedArguments()
 
         $this->assertSame('theStatus', $result->getStatus());
         $this->assertSame('', $result->getReason());
-        $this->assertSame(null, $result->getReply());
+        $this->assertNull($result->getReply());
 
         $result = new Result('theStatus', 'theReason');
 
         $this->assertSame('theStatus', $result->getStatus());
         $this->assertSame('theReason', $result->getReason());
-        $this->assertSame(null, $result->getReply());
+        $this->assertNull($result->getReply());
     }
 
     public function testCouldConstructedWithAckFactoryMethod()
@@ -30,7 +30,7 @@ public function testCouldConstructedWithAckFactoryMethod()
         $this->assertInstanceOf(Result::class, $result);
         $this->assertSame(Result::ACK, $result->getStatus());
         $this->assertSame('theReason', $result->getReason());
-        $this->assertSame(null, $result->getReply());
+        $this->assertNull($result->getReply());
     }
 
     public function testCouldConstructedWithRejectFactoryMethod()
@@ -40,7 +40,7 @@ public function testCouldConstructedWithRejectFactoryMethod()
         $this->assertInstanceOf(Result::class, $result);
         $this->assertSame(Result::REJECT, $result->getStatus());
         $this->assertSame('theReason', $result->getReason());
-        $this->assertSame(null, $result->getReply());
+        $this->assertNull($result->getReply());
     }
 
     public function testCouldConstructedWithRequeueFactoryMethod()
@@ -50,7 +50,7 @@ public function testCouldConstructedWithRequeueFactoryMethod()
         $this->assertInstanceOf(Result::class, $result);
         $this->assertSame(Result::REQUEUE, $result->getStatus());
         $this->assertSame('theReason', $result->getReason());
-        $this->assertSame(null, $result->getReply());
+        $this->assertNull($result->getReply());
     }
 
     public function testCouldConstructedWithReplyFactoryMethodAndAckStatusByDefault()

From b185069135994ba28f1a1c9c0d9cf2293f8e3b5c Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 6 Mar 2018 12:46:03 +0200
Subject: [PATCH 047/337] Allow queue interop 1.0 alpha.

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 62326bd..86673b9 100644
--- a/composer.json
+++ b/composer.json
@@ -7,7 +7,7 @@
     "license": "MIT",
     "require": {
         "php": ">=5.6",
-        "queue-interop/queue-interop": "^0.6@dev",
+        "queue-interop/queue-interop": "^0.6@dev|^1.0.0-alpha1",
         "enqueue/null": "^0.8@dev",
         "ramsey/uuid": "^2|^3.5",
         "psr/log": "^1"

From 1277155cc39ac5952348a63760c28a47f995a210 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Sat, 10 Mar 2018 19:40:28 +0200
Subject: [PATCH 048/337] [symfony] Async commands.

---
 Symfony/Creator/JobCommandCreator.php     | 66 -----------------------
 Symfony/Processor/RunCommandProcessor.php | 44 ---------------
 2 files changed, 110 deletions(-)
 delete mode 100644 Symfony/Creator/JobCommandCreator.php
 delete mode 100644 Symfony/Processor/RunCommandProcessor.php

diff --git a/Symfony/Creator/JobCommandCreator.php b/Symfony/Creator/JobCommandCreator.php
deleted file mode 100644
index 2003795..0000000
--- a/Symfony/Creator/JobCommandCreator.php
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-
-namespace Enqueue\Symfony\Creator;
-
-use Enqueue\Client\ProducerInterface;
-
-class JobCommandCreator
-{
-    /**
-     * @var ProducerInterface
-     */
-    protected $producer;
-
-    /**
-     * @var string
-     */
-    protected $env;
-
-    /**
-     * CommandJobCreator constructor.
-     *
-     * @param ProducerInterface $producer
-     * @param string            $env
-     */
-    public function __construct(ProducerInterface $producer, $env)
-    {
-        $this->producer = $producer;
-        $this->env = $env;
-    }
-
-    /**
-     * @param $command
-     * @param mixed $args
-     *
-     * @return \Enqueue\Rpc\Promise|null
-     */
-    public function scheduleCommand($command, $args = [])
-    {
-        $argumentString = $this->createArgumentString($args);
-
-        return $this->producer->sendCommand('run_command', sprintf('%s %s', $command, $argumentString));
-    }
-
-    /**
-     * @param array $arguments
-     *
-     * @return string
-     */
-    public function createArgumentString(array $arguments)
-    {
-        $optionList = [];
-
-        foreach ($arguments as $key => $value) {
-            if (!is_int($key)) {
-                $optionList[] = sprintf('--%s=%s', $key, $value);
-                continue;
-            }
-
-            $optionList[] = sprintf('%s', $value);
-        }
-
-        $optionList[] = sprintf('--env=%s', $this->env);
-
-        return implode(' ', $optionList);
-    }
-}
diff --git a/Symfony/Processor/RunCommandProcessor.php b/Symfony/Processor/RunCommandProcessor.php
deleted file mode 100644
index 0fb70a5..0000000
--- a/Symfony/Processor/RunCommandProcessor.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-namespace Enqueue\Symfony\Processor;
-
-use Enqueue\Client\CommandSubscriberInterface;
-use Enqueue\Consumption\Result;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProcessor;
-use Symfony\Component\Process\Exception\ProcessFailedException;
-use Symfony\Component\Process\Process;
-
-class RunCommandProcessor implements PsrProcessor, CommandSubscriberInterface
-{
-    /**
-     * @var string
-     */
-    private $projectDir;
-
-    public function __construct($projectDir)
-    {
-        $this->projectDir = $projectDir;
-    }
-
-    public function process(PsrMessage $message, PsrContext $context)
-    {
-        $commandline = $message->getBody();
-
-        $process = new Process('./bin/console '.$commandline, $this->projectDir);
-
-        try {
-            $process->mustRun();
-
-            return Result::ACK;
-        } catch (ProcessFailedException $e) {
-            return Result::reject(sprintf('The process failed with exception: "%s" in %s at %s', $e->getMessage(), $e->getFile(), $e->getLine()));
-        }
-    }
-
-    public static function getSubscribedCommand()
-    {
-        return 'run_command';
-    }
-}

From 5f74ada67f4073dbe378e5c984b66e2663dc991e Mon Sep 17 00:00:00 2001
From: Pascal Borreli <pascal@borreli.com>
Date: Fri, 23 Mar 2018 11:52:55 +0100
Subject: [PATCH 049/337] Fixed typos

---
 Tests/Rpc/PromiseTest.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Tests/Rpc/PromiseTest.php b/Tests/Rpc/PromiseTest.php
index b2cb42a..7202e67 100644
--- a/Tests/Rpc/PromiseTest.php
+++ b/Tests/Rpc/PromiseTest.php
@@ -31,10 +31,10 @@ public function testOnReceiveShouldCallReceiveCallBack()
         $receiveInvoked = false;
         $receivePromise = null;
         $receiveTimeout = null;
-        $receivecb = function ($promise, $timout) use (&$receiveInvoked, &$receivePromise, &$receiveTimeout) {
+        $receivecb = function ($promise, $timeout) use (&$receiveInvoked, &$receivePromise, &$receiveTimeout) {
             $receiveInvoked = true;
             $receivePromise = $promise;
-            $receiveTimeout = $timout;
+            $receiveTimeout = $timeout;
         };
 
         $promise = new Promise($receivecb, function () {}, function () {});

From 1fc59fc535269e520be16f68c6c26448a706d6a0 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 5 Apr 2018 10:25:20 +0300
Subject: [PATCH 050/337] Drop PHP5 support, Require Queue Interop with PHP7
 features.

---
 composer.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/composer.json b/composer.json
index 353c9a9..83b015b 100644
--- a/composer.json
+++ b/composer.json
@@ -6,8 +6,8 @@
     "homepage": "https://enqueue.forma-pro.com/",
     "license": "MIT",
     "require": {
-        "php": ">=5.6",
-        "queue-interop/queue-interop": "^0.6@dev|^1.0.0-alpha1",
+        "php": "^7.1.3",
+        "queue-interop/queue-interop": "^0.7@dev",
         "enqueue/null": "^0.9@dev",
         "ramsey/uuid": "^2|^3.5",
         "psr/log": "^1"

From 5e6a31bc42da0afe60d68db3b2cf359481d803d4 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 5 Apr 2018 10:30:01 +0300
Subject: [PATCH 051/337] Drop Symfony 2.x support, set Symfony 3.4 minimum
 supported version.

---
 composer.json | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/composer.json b/composer.json
index 83b015b..11f9def 100644
--- a/composer.json
+++ b/composer.json
@@ -14,11 +14,11 @@
     },
     "require-dev": {
         "phpunit/phpunit": "~5.5",
-        "symfony/console": "^2.8|^3|^4",
-        "symfony/dependency-injection": "^2.8|^3|^4",
-        "symfony/config": "^2.8|^3|^4",
-        "symfony/event-dispatcher": "^2.8|^3|^4",
-        "symfony/http-kernel": "^2.8|^3|^4",
+        "symfony/console": "^3.4|^4",
+        "symfony/dependency-injection": "^3.4|^4",
+        "symfony/config": "^3.4|^4",
+        "symfony/event-dispatcher": "^3.4|^4",
+        "symfony/http-kernel": "^3.4|^4",
         "enqueue/amqp-ext": "^0.9@dev",
         "enqueue/amqp-lib": "^0.9@dev",
         "enqueue/amqp-bunny": "^0.9@dev",
@@ -37,8 +37,8 @@
     },
     "suggest": {
         "symfony/console": "^2.8|^3|^4 If you want to use li commands",
-        "symfony/dependency-injection": "^2.8|^3|^4",
-        "symfony/config": "^2.8|^3|^4",
+        "symfony/dependency-injection": "^3.4|^4",
+        "symfony/config": "^3.4|^4",
         "enqueue/amqp-ext": "AMQP transport (based on php extension)",
         "enqueue/stomp": "STOMP transport",
         "enqueue/fs": "Filesystem transport",

From cb3afc15bfb63a107858b99295dfd4d4cac033ca Mon Sep 17 00:00:00 2001
From: Max Kotliar <kotlyar.maksim@gmail.com>
Date: Fri, 13 Apr 2018 19:52:17 +0300
Subject: [PATCH 052/337] Update doc block. return value should be "self"

---
 Consumption/Result.php | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Consumption/Result.php b/Consumption/Result.php
index 6be4d51..ff11721 100644
--- a/Consumption/Result.php
+++ b/Consumption/Result.php
@@ -90,7 +90,7 @@ public function setReply(PsrMessage $reply = null)
     /**
      * @param string $reason
      *
-     * @return Result
+     * @return self
      */
     public static function ack($reason = '')
     {
@@ -100,7 +100,7 @@ public static function ack($reason = '')
     /**
      * @param string $reason
      *
-     * @return Result
+     * @return self
      */
     public static function reject($reason)
     {
@@ -110,7 +110,7 @@ public static function reject($reason)
     /**
      * @param string $reason
      *
-     * @return Result
+     * @return self
      */
     public static function requeue($reason = '')
     {
@@ -122,7 +122,7 @@ public static function requeue($reason = '')
      * @param string      $status
      * @param string|null $reason
      *
-     * @return Result
+     * @return self
      */
     public static function reply(PsrMessage $replyMessage, $status = self::ACK, $reason = null)
     {

From c7b0b3725cb644d6af887994973ea1102dfb0356 Mon Sep 17 00:00:00 2001
From: Max Kotliar <kotlyar.maksim@gmail.com>
Date: Fri, 13 Apr 2018 20:05:57 +0300
Subject: [PATCH 053/337] Update Result.php

---
 Consumption/Result.php | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Consumption/Result.php b/Consumption/Result.php
index ff11721..b78f997 100644
--- a/Consumption/Result.php
+++ b/Consumption/Result.php
@@ -90,7 +90,7 @@ public function setReply(PsrMessage $reply = null)
     /**
      * @param string $reason
      *
-     * @return self
+     * @return static
      */
     public static function ack($reason = '')
     {
@@ -100,7 +100,7 @@ public static function ack($reason = '')
     /**
      * @param string $reason
      *
-     * @return self
+     * @return static
      */
     public static function reject($reason)
     {
@@ -110,7 +110,7 @@ public static function reject($reason)
     /**
      * @param string $reason
      *
-     * @return self
+     * @return static
      */
     public static function requeue($reason = '')
     {
@@ -122,7 +122,7 @@ public static function requeue($reason = '')
      * @param string      $status
      * @param string|null $reason
      *
-     * @return self
+     * @return static
      */
     public static function reply(PsrMessage $replyMessage, $status = self::ACK, $reason = null)
     {

From d1a19655cca7322bdf5770e6b8dbc183b524eac0 Mon Sep 17 00:00:00 2001
From: Dave Heineman <dheineman@live.nl>
Date: Thu, 26 Apr 2018 15:03:47 +0200
Subject: [PATCH 054/337] Add symfony intergation for kafka transport

---
 Symfony/DefaultTransportFactory.php           | 6 ++++++
 Tests/Symfony/DefaultTransportFactoryTest.php | 2 ++
 2 files changed, 8 insertions(+)

diff --git a/Symfony/DefaultTransportFactory.php b/Symfony/DefaultTransportFactory.php
index 20fa1c9..65b877a 100644
--- a/Symfony/DefaultTransportFactory.php
+++ b/Symfony/DefaultTransportFactory.php
@@ -10,6 +10,8 @@
 use Enqueue\Gps\Symfony\GpsTransportFactory;
 use Enqueue\Null\NullConnectionFactory;
 use Enqueue\Null\Symfony\NullTransportFactory;
+use Enqueue\RdKafka\RdKafkaConnectionFactory;
+use Enqueue\RdKafka\Symfony\RdKafkaTransportFactory;
 use Enqueue\Redis\RedisConnectionFactory;
 use Enqueue\Redis\Symfony\RedisTransportFactory;
 use Enqueue\Sqs\SqsConnectionFactory;
@@ -209,6 +211,10 @@ private function findFactory($dsn)
             return new StompTransportFactory('default_stomp');
         }
 
+        if ($factory instanceof RdKafkaConnectionFactory) {
+            return new RdKafkaTransportFactory('default_kafka');
+        }
+
         throw new \LogicException(sprintf(
             'There is no supported transport factory for the connection factory "%s" created from DSN "%s"',
             get_class($factory),
diff --git a/Tests/Symfony/DefaultTransportFactoryTest.php b/Tests/Symfony/DefaultTransportFactoryTest.php
index 9a013fd..8f2df54 100644
--- a/Tests/Symfony/DefaultTransportFactoryTest.php
+++ b/Tests/Symfony/DefaultTransportFactoryTest.php
@@ -289,5 +289,7 @@ public static function provideDSNs()
         yield ['redis:', 'default_redis'];
 
         yield ['stomp:', 'default_stomp'];
+
+        yield ['kafka:', 'default_kafka'];
     }
 }

From 5dfdb1c4db38753475c50466b95772386a44b744 Mon Sep 17 00:00:00 2001
From: Max Kotliar <kotlyar.maksim@gmail.com>
Date: Fri, 13 Apr 2018 19:52:17 +0300
Subject: [PATCH 055/337] Update doc block. return value should be "self"

---
 Consumption/Result.php | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Consumption/Result.php b/Consumption/Result.php
index 6be4d51..ff11721 100644
--- a/Consumption/Result.php
+++ b/Consumption/Result.php
@@ -90,7 +90,7 @@ public function setReply(PsrMessage $reply = null)
     /**
      * @param string $reason
      *
-     * @return Result
+     * @return self
      */
     public static function ack($reason = '')
     {
@@ -100,7 +100,7 @@ public static function ack($reason = '')
     /**
      * @param string $reason
      *
-     * @return Result
+     * @return self
      */
     public static function reject($reason)
     {
@@ -110,7 +110,7 @@ public static function reject($reason)
     /**
      * @param string $reason
      *
-     * @return Result
+     * @return self
      */
     public static function requeue($reason = '')
     {
@@ -122,7 +122,7 @@ public static function requeue($reason = '')
      * @param string      $status
      * @param string|null $reason
      *
-     * @return Result
+     * @return self
      */
     public static function reply(PsrMessage $replyMessage, $status = self::ACK, $reason = null)
     {

From 1802c0de2a8d616f6c9eaf0af0d308db15fb1dcc Mon Sep 17 00:00:00 2001
From: Max Kotliar <kotlyar.maksim@gmail.com>
Date: Fri, 13 Apr 2018 20:05:57 +0300
Subject: [PATCH 056/337] Update Result.php

---
 Consumption/Result.php | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Consumption/Result.php b/Consumption/Result.php
index ff11721..b78f997 100644
--- a/Consumption/Result.php
+++ b/Consumption/Result.php
@@ -90,7 +90,7 @@ public function setReply(PsrMessage $reply = null)
     /**
      * @param string $reason
      *
-     * @return self
+     * @return static
      */
     public static function ack($reason = '')
     {
@@ -100,7 +100,7 @@ public static function ack($reason = '')
     /**
      * @param string $reason
      *
-     * @return self
+     * @return static
      */
     public static function reject($reason)
     {
@@ -110,7 +110,7 @@ public static function reject($reason)
     /**
      * @param string $reason
      *
-     * @return self
+     * @return static
      */
     public static function requeue($reason = '')
     {
@@ -122,7 +122,7 @@ public static function requeue($reason = '')
      * @param string      $status
      * @param string|null $reason
      *
-     * @return self
+     * @return static
      */
     public static function reply(PsrMessage $replyMessage, $status = self::ACK, $reason = null)
     {

From 8552642bf1d94f4b7816c7d223e165f5216f514d Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 3 May 2018 14:07:42 +0300
Subject: [PATCH 057/337] [symfony] add mongodb to default transport and
 dsn_to_function

---
 Symfony/DefaultTransportFactory.php                    | 6 ++++++
 Tests/Functions/DsnToConnectionFactoryFunctionTest.php | 3 +++
 Tests/Symfony/DefaultTransportFactoryTest.php          | 2 ++
 functions.php                                          | 5 +++++
 4 files changed, 16 insertions(+)

diff --git a/Symfony/DefaultTransportFactory.php b/Symfony/DefaultTransportFactory.php
index 65b877a..fa45d25 100644
--- a/Symfony/DefaultTransportFactory.php
+++ b/Symfony/DefaultTransportFactory.php
@@ -8,6 +8,8 @@
 use Enqueue\Fs\Symfony\FsTransportFactory;
 use Enqueue\Gps\GpsConnectionFactory;
 use Enqueue\Gps\Symfony\GpsTransportFactory;
+use Enqueue\Mongodb\MongodbConnectionFactory;
+use Enqueue\Mongodb\Symfony\MongodbTransportFactory;
 use Enqueue\Null\NullConnectionFactory;
 use Enqueue\Null\Symfony\NullTransportFactory;
 use Enqueue\RdKafka\RdKafkaConnectionFactory;
@@ -215,6 +217,10 @@ private function findFactory($dsn)
             return new RdKafkaTransportFactory('default_kafka');
         }
 
+        if ($factory instanceof MongodbConnectionFactory) {
+            return new MongodbTransportFactory('default_mongodb');
+        }
+
         throw new \LogicException(sprintf(
             'There is no supported transport factory for the connection factory "%s" created from DSN "%s"',
             get_class($factory),
diff --git a/Tests/Functions/DsnToConnectionFactoryFunctionTest.php b/Tests/Functions/DsnToConnectionFactoryFunctionTest.php
index f123b11..2c65eb6 100644
--- a/Tests/Functions/DsnToConnectionFactoryFunctionTest.php
+++ b/Tests/Functions/DsnToConnectionFactoryFunctionTest.php
@@ -9,6 +9,7 @@
 use Enqueue\Fs\FsConnectionFactory;
 use Enqueue\Gearman\GearmanConnectionFactory;
 use Enqueue\Gps\GpsConnectionFactory;
+use Enqueue\Mongodb\MongodbConnectionFactory;
 use Enqueue\Null\NullConnectionFactory;
 use Enqueue\Pheanstalk\PheanstalkConnectionFactory;
 use Enqueue\RdKafka\RdKafkaConnectionFactory;
@@ -97,5 +98,7 @@ public static function provideDSNs()
         yield ['sqs:', SqsConnectionFactory::class];
 
         yield ['gps:', GpsConnectionFactory::class];
+
+        yield ['mongodb:', MongodbConnectionFactory::class];
     }
 }
diff --git a/Tests/Symfony/DefaultTransportFactoryTest.php b/Tests/Symfony/DefaultTransportFactoryTest.php
index 8f2df54..a97fb86 100644
--- a/Tests/Symfony/DefaultTransportFactoryTest.php
+++ b/Tests/Symfony/DefaultTransportFactoryTest.php
@@ -291,5 +291,7 @@ public static function provideDSNs()
         yield ['stomp:', 'default_stomp'];
 
         yield ['kafka:', 'default_kafka'];
+
+        yield ['mongodb:', 'default_mongodb'];
     }
 }
diff --git a/functions.php b/functions.php
index 8960785..537f996 100644
--- a/functions.php
+++ b/functions.php
@@ -10,6 +10,7 @@
 use Enqueue\Fs\FsConnectionFactory;
 use Enqueue\Gearman\GearmanConnectionFactory;
 use Enqueue\Gps\GpsConnectionFactory;
+use Enqueue\Mongodb\MongodbConnectionFactory;
 use Enqueue\Null\NullConnectionFactory;
 use Enqueue\Pheanstalk\PheanstalkConnectionFactory;
 use Enqueue\RdKafka\RdKafkaConnectionFactory;
@@ -108,6 +109,10 @@ function dsn_to_connection_factory($dsn)
         $map['gps'] = GpsConnectionFactory::class;
     }
 
+    if (class_exists(MongodbConnectionFactory::class)) {
+        $map['mongodb'] = MongodbConnectionFactory::class;
+    }
+
     list($scheme) = explode(':', $dsn, 2);
     if (false == $scheme || false === strpos($dsn, ':')) {
         throw new \LogicException(sprintf('The scheme could not be parsed from DSN "%s"', $dsn));

From d637e699d15b5343daa5c16d3b5b3641bc502c0b Mon Sep 17 00:00:00 2001
From: Max Kotliar <kotlyar.maksim@gmail.com>
Date: Fri, 4 May 2018 08:47:00 +0300
Subject: [PATCH 058/337] [mongodb] add mongodb to enqueue dev deps.

---
 composer.json | 1 +
 1 file changed, 1 insertion(+)

diff --git a/composer.json b/composer.json
index 86673b9..fffef75 100644
--- a/composer.json
+++ b/composer.json
@@ -33,6 +33,7 @@
         "enqueue/stomp": "^0.8@dev",
         "enqueue/test": "^0.8@dev",
         "enqueue/simple-client": "^0.8@dev",
+        "enqueue/mongodb": "^0.8@dev",
         "empi89/php-amqp-stubs": "*@dev"
     },
     "suggest": {

From 065715c235d0782c776ecdbe268ac43afdad0f3f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ram=C5=ABnas=20Dronga?= <ieskok@ramuno.lt>
Date: Sun, 24 Jun 2018 13:42:39 +0300
Subject: [PATCH 059/337] [consumption] add process niceness extension

---
 Consumption/Extension/NicenessExtension.php   | 50 +++++++++++++++++++
 .../LimitsExtensionsCommandTrait.php          |  9 +++-
 .../Extension/NicenessExtensionTest.php       | 47 +++++++++++++++++
 .../Client/ConsumeMessagesCommandTest.php     |  3 +-
 .../ConsumeMessagesCommandTest.php            |  3 +-
 ...ntainerAwareConsumeMessagesCommandTest.php |  3 +-
 .../LimitsExtensionsCommandTraitTest.php      | 21 +++++++-
 7 files changed, 130 insertions(+), 6 deletions(-)
 create mode 100644 Consumption/Extension/NicenessExtension.php
 create mode 100644 Tests/Consumption/Extension/NicenessExtensionTest.php

diff --git a/Consumption/Extension/NicenessExtension.php b/Consumption/Extension/NicenessExtension.php
new file mode 100644
index 0000000..01a5383
--- /dev/null
+++ b/Consumption/Extension/NicenessExtension.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Enqueue\Consumption\Extension;
+
+use Enqueue\Consumption\Context;
+use Enqueue\Consumption\EmptyExtensionTrait;
+use Enqueue\Consumption\ExtensionInterface;
+
+class NicenessExtension implements ExtensionInterface
+{
+    use EmptyExtensionTrait;
+
+    /**
+     * @var int
+     */
+    protected $niceness = 0;
+
+    /**
+     * @param int $niceness
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function __construct($niceness)
+    {
+        if (false === is_int($niceness)) {
+            throw new \InvalidArgumentException(sprintf(
+                'Expected niceness value is int but got: "%s"',
+                is_object($niceness) ? get_class($niceness) : gettype($niceness)
+            ));
+        }
+
+        $this->niceness = $niceness;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function onStart(Context $context)
+    {
+        if (0 !== $this->niceness) {
+            $changed = @proc_nice($this->niceness);
+            if (!$changed) {
+                throw new \InvalidArgumentException(sprintf(
+                    'Cannot change process niceness, got warning: "%s"',
+                    error_get_last()['message']
+                ));
+            }
+        }
+    }
+}
diff --git a/Symfony/Consumption/LimitsExtensionsCommandTrait.php b/Symfony/Consumption/LimitsExtensionsCommandTrait.php
index 34bc76d..2a75618 100644
--- a/Symfony/Consumption/LimitsExtensionsCommandTrait.php
+++ b/Symfony/Consumption/LimitsExtensionsCommandTrait.php
@@ -5,6 +5,7 @@
 use Enqueue\Consumption\Extension\LimitConsumedMessagesExtension;
 use Enqueue\Consumption\Extension\LimitConsumerMemoryExtension;
 use Enqueue\Consumption\Extension\LimitConsumptionTimeExtension;
+use Enqueue\Consumption\Extension\NicenessExtension;
 use Enqueue\Consumption\ExtensionInterface;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputOption;
@@ -20,7 +21,8 @@ protected function configureLimitsExtensions()
         $this
             ->addOption('message-limit', null, InputOption::VALUE_REQUIRED, 'Consume n messages and exit')
             ->addOption('time-limit', null, InputOption::VALUE_REQUIRED, 'Consume messages during this time')
-            ->addOption('memory-limit', null, InputOption::VALUE_REQUIRED, 'Consume messages until process reaches this memory limit in MB');
+            ->addOption('memory-limit', null, InputOption::VALUE_REQUIRED, 'Consume messages until process reaches this memory limit in MB')
+            ->addOption('niceness', null, InputOption::VALUE_REQUIRED, 'Set process niceness');
     }
 
     /**
@@ -58,6 +60,11 @@ protected function getLimitsExtensions(InputInterface $input, OutputInterface $o
             $extensions[] = new LimitConsumerMemoryExtension($memoryLimit);
         }
 
+        $niceness = $input->getOption('niceness');
+        if ($niceness) {
+            $extensions[] = new NicenessExtension($niceness);
+        }
+
         return $extensions;
     }
 }
diff --git a/Tests/Consumption/Extension/NicenessExtensionTest.php b/Tests/Consumption/Extension/NicenessExtensionTest.php
new file mode 100644
index 0000000..1cabdaf
--- /dev/null
+++ b/Tests/Consumption/Extension/NicenessExtensionTest.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Enqueue\Tests\Consumption\Extension;
+
+use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Extension\NicenessExtension;
+use Interop\Queue\PsrConsumer;
+use Interop\Queue\PsrContext;
+use Interop\Queue\PsrProcessor;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+
+class NicenessExtensionTest extends TestCase
+{
+    public function testCouldBeConstructedWithRequiredArguments()
+    {
+        new NicenessExtension(0);
+    }
+
+    public function testShouldThrowExceptionOnInvalidArgument()
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        new NicenessExtension('1');
+    }
+
+    public function testShouldThrowWarningOnInvalidArgument()
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('proc_nice(): Only a super user may attempt to increase the priority of a process');
+
+        $extension = new NicenessExtension(-1);
+        $extension->onStart($this->createContext());
+    }
+
+    /**
+     * @return Context
+     */
+    protected function createContext()
+    {
+        $context = new Context($this->createMock(PsrContext::class));
+        $context->setLogger($this->createMock(LoggerInterface::class));
+        $context->setPsrConsumer($this->createMock(PsrConsumer::class));
+        $context->setPsrProcessor($this->createMock(PsrProcessor::class));
+
+        return $context;
+    }
+}
diff --git a/Tests/Symfony/Client/ConsumeMessagesCommandTest.php b/Tests/Symfony/Client/ConsumeMessagesCommandTest.php
index e87f4df..396417e 100644
--- a/Tests/Symfony/Client/ConsumeMessagesCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeMessagesCommandTest.php
@@ -61,7 +61,7 @@ public function testShouldHaveExpectedOptions()
 
         $options = $command->getDefinition()->getOptions();
 
-        $this->assertCount(7, $options);
+        $this->assertCount(8, $options);
         $this->assertArrayHasKey('memory-limit', $options);
         $this->assertArrayHasKey('message-limit', $options);
         $this->assertArrayHasKey('time-limit', $options);
@@ -69,6 +69,7 @@ public function testShouldHaveExpectedOptions()
         $this->assertArrayHasKey('idle-timeout', $options);
         $this->assertArrayHasKey('receive-timeout', $options);
         $this->assertArrayHasKey('skip', $options);
+        $this->assertArrayHasKey('niceness', $options);
     }
 
     public function testShouldHaveExpectedArguments()
diff --git a/Tests/Symfony/Consumption/ConsumeMessagesCommandTest.php b/Tests/Symfony/Consumption/ConsumeMessagesCommandTest.php
index d83505a..aad0a29 100644
--- a/Tests/Symfony/Consumption/ConsumeMessagesCommandTest.php
+++ b/Tests/Symfony/Consumption/ConsumeMessagesCommandTest.php
@@ -29,12 +29,13 @@ public function testShouldHaveExpectedOptions()
 
         $options = $command->getDefinition()->getOptions();
 
-        $this->assertCount(5, $options);
+        $this->assertCount(6, $options);
         $this->assertArrayHasKey('memory-limit', $options);
         $this->assertArrayHasKey('message-limit', $options);
         $this->assertArrayHasKey('time-limit', $options);
         $this->assertArrayHasKey('idle-timeout', $options);
         $this->assertArrayHasKey('receive-timeout', $options);
+        $this->assertArrayHasKey('niceness', $options);
     }
 
     public function testShouldHaveExpectedAttributes()
diff --git a/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php b/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php
index b451dc7..78929e0 100644
--- a/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php
+++ b/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php
@@ -33,13 +33,14 @@ public function testShouldHaveExpectedOptions()
 
         $options = $command->getDefinition()->getOptions();
 
-        $this->assertCount(6, $options);
+        $this->assertCount(7, $options);
         $this->assertArrayHasKey('memory-limit', $options);
         $this->assertArrayHasKey('message-limit', $options);
         $this->assertArrayHasKey('time-limit', $options);
         $this->assertArrayHasKey('queue', $options);
         $this->assertArrayHasKey('idle-timeout', $options);
         $this->assertArrayHasKey('receive-timeout', $options);
+        $this->assertArrayHasKey('niceness', $options);
     }
 
     public function testShouldHaveExpectedAttributes()
diff --git a/Tests/Symfony/Consumption/LimitsExtensionsCommandTraitTest.php b/Tests/Symfony/Consumption/LimitsExtensionsCommandTraitTest.php
index d9704d3..5495274 100644
--- a/Tests/Symfony/Consumption/LimitsExtensionsCommandTraitTest.php
+++ b/Tests/Symfony/Consumption/LimitsExtensionsCommandTraitTest.php
@@ -5,6 +5,7 @@
 use Enqueue\Consumption\Extension\LimitConsumedMessagesExtension;
 use Enqueue\Consumption\Extension\LimitConsumerMemoryExtension;
 use Enqueue\Consumption\Extension\LimitConsumptionTimeExtension;
+use Enqueue\Consumption\Extension\NicenessExtension;
 use Enqueue\Tests\Symfony\Consumption\Mock\LimitsExtensionsCommand;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\Console\Tester\CommandTester;
@@ -17,10 +18,11 @@ public function testShouldAddExtensionsOptions()
 
         $options = $trait->getDefinition()->getOptions();
 
-        $this->assertCount(3, $options);
+        $this->assertCount(4, $options);
         $this->assertArrayHasKey('memory-limit', $options);
         $this->assertArrayHasKey('message-limit', $options);
         $this->assertArrayHasKey('time-limit', $options);
+        $this->assertArrayHasKey('niceness', $options);
     }
 
     public function testShouldAddMessageLimitExtension()
@@ -57,7 +59,8 @@ public function testShouldAddTimeLimitExtension()
 
     public function testShouldThrowExceptionIfTimeLimitExpressionIsNotValid()
     {
-        $this->setExpectedException(\Exception::class, 'Failed to parse time string (time is not valid) at position');
+        $this->expectException(\Exception::class);
+        $this->expectExceptionMessage('Failed to parse time string (time is not valid) at position');
 
         $command = new LimitsExtensionsCommand('name');
 
@@ -104,4 +107,18 @@ public function testShouldAddThreeLimitExtensions()
         $this->assertInstanceOf(LimitConsumptionTimeExtension::class, $result[1]);
         $this->assertInstanceOf(LimitConsumerMemoryExtension::class, $result[2]);
     }
+
+    public function testShouldAddNicenessExtension()
+    {
+        $command = new LimitsExtensionsCommand('name');
+        $tester = new CommandTester($command);
+        $tester->execute([
+            '--niceness' => 1,
+        ]);
+
+        $result = $command->getExtensions();
+        $this->assertCount(1, $result);
+
+        $this->assertInstanceOf(NicenessExtension::class, $result[0]);
+    }
 }

From 0200aeaa9a501556434f39b1cc77c3a205f6ac84 Mon Sep 17 00:00:00 2001
From: Max Kotliar <kotlyar.maksim@gmail.com>
Date: Thu, 26 Jul 2018 18:13:43 +0300
Subject: [PATCH 060/337] Fix call debug method on null

---
 Consumption/QueueConsumer.php | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index a446403..7f48404 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -178,7 +178,13 @@ public function consume(ExtensionInterface $runtimeExtension = null)
         $context = new Context($this->psrContext);
         $this->extension->onStart($context);
 
-        $this->logger = $context->getLogger() ?: new NullLogger();
+        if ($context->getLogger()) {
+            $this->logger = $context->getLogger();
+        } else {
+            $this->logger = new NullLogger();
+            $context->setLogger($this->logger);
+        }
+
         $this->logger->info('Start consuming');
 
         if ($this->psrContext instanceof AmqpContext) {

From 1871c06343d663033e8ad39682f593346c70f01e Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 6 Aug 2018 16:20:20 +0300
Subject: [PATCH 061/337] add fallback subscription consumer, add subscription
 consumer support to queue consumer.

---
 Consumption/FallbackSubscriptionConsumer.php  | 124 ++++++++
 Consumption/QueueConsumer.php                 |  79 +++++-
 .../FallbackSubscriptionConsumerTest.php      | 267 ++++++++++++++++++
 3 files changed, 467 insertions(+), 3 deletions(-)
 create mode 100644 Consumption/FallbackSubscriptionConsumer.php
 create mode 100644 Tests/Consumption/FallbackSubscriptionConsumerTest.php

diff --git a/Consumption/FallbackSubscriptionConsumer.php b/Consumption/FallbackSubscriptionConsumer.php
new file mode 100644
index 0000000..f4176a6
--- /dev/null
+++ b/Consumption/FallbackSubscriptionConsumer.php
@@ -0,0 +1,124 @@
+<?php
+
+namespace Enqueue\Consumption;
+
+use Interop\Queue\PsrConsumer;
+use Interop\Queue\PsrSubscriptionConsumer;
+
+final class FallbackSubscriptionConsumer implements PsrSubscriptionConsumer
+{
+    /**
+     * an item contains an array: [PsrConsumer $consumer, callable $callback];.
+     * an item key is a queue name.
+     *
+     * @var array
+     */
+    private $subscribers;
+
+    /**
+     * @var int|float the time in milliseconds the consumer waits if no message has been received
+     */
+    private $idleTime = 0;
+
+    public function __construct()
+    {
+        $this->subscribers = [];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function consume($timeout = 0)
+    {
+        if (empty($this->subscribers)) {
+            throw new \LogicException('No subscribers');
+        }
+
+        $timeout /= 1000;
+        $endAt = microtime(true) + $timeout;
+
+        while (true) {
+            /**
+             * @var string
+             * @var PsrConsumer $consumer
+             * @var callable    $processor
+             */
+            foreach ($this->subscribers as $queueName => list($consumer, $callback)) {
+                $message = $consumer->receiveNoWait();
+
+                if ($message) {
+                    if (false === call_user_func($callback, $message, $consumer)) {
+                        return;
+                    }
+                } else {
+                    if ($timeout && microtime(true) >= $endAt) {
+                        return;
+                    }
+
+                    $this->idleTime && usleep($this->idleTime);
+                }
+
+                if ($timeout && microtime(true) >= $endAt) {
+                    return;
+                }
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function subscribe(PsrConsumer $consumer, callable $callback)
+    {
+        $queueName = $consumer->getQueue()->getQueueName();
+        if (array_key_exists($queueName, $this->subscribers)) {
+            if ($this->subscribers[$queueName][0] === $consumer && $this->subscribers[$queueName][1] === $callback) {
+                return;
+            }
+
+            throw new \InvalidArgumentException(sprintf('There is a consumer subscribed to queue: "%s"', $queueName));
+        }
+
+        $this->subscribers[$queueName] = [$consumer, $callback];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function unsubscribe(PsrConsumer $consumer)
+    {
+        if (false == array_key_exists($consumer->getQueue()->getQueueName(), $this->subscribers)) {
+            return;
+        }
+
+        if ($this->subscribers[$consumer->getQueue()->getQueueName()][0] !== $consumer) {
+            return;
+        }
+
+        unset($this->subscribers[$consumer->getQueue()->getQueueName()]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function unsubscribeAll()
+    {
+        $this->subscribers = [];
+    }
+
+    /**
+     * @return float|int
+     */
+    public function getIdleTime()
+    {
+        return $this->idleTime;
+    }
+
+    /**
+     * @param float|int $idleTime
+     */
+    public function setIdleTime($idleTime)
+    {
+        $this->idleTime = $idleTime;
+    }
+}
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 7f48404..c41028b 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -14,6 +14,7 @@
 use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrProcessor;
 use Interop\Queue\PsrQueue;
+use Interop\Queue\PsrSubscriptionConsumerAwareContext;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -58,6 +59,13 @@ class QueueConsumer
      */
     private $logger;
 
+    /**
+     * @deprecated added as BC layer, will be a default behavior in 0.9 version.
+     *
+     * @var bool
+     */
+    private $enableSubscriptionConsumer;
+
     /**
      * @param PsrContext                             $psrContext
      * @param ExtensionInterface|ChainExtension|null $extension
@@ -77,6 +85,8 @@ public function __construct(
 
         $this->boundProcessors = [];
         $this->logger = new NullLogger();
+
+        $this->enableSubscriptionConsumer = false;
     }
 
     /**
@@ -187,7 +197,45 @@ public function consume(ExtensionInterface $runtimeExtension = null)
 
         $this->logger->info('Start consuming');
 
-        if ($this->psrContext instanceof AmqpContext) {
+        $subscriptionConsumer = null;
+        if ($this->enableSubscriptionConsumer) {
+            $subscriptionConsumer = new FallbackSubscriptionConsumer();
+            if ($context instanceof PsrSubscriptionConsumerAwareContext) {
+                $subscriptionConsumer = $context->createSubscriptionConsumer();
+            }
+
+            $callback = function (PsrMessage $message, PsrConsumer $consumer) use (&$context) {
+                $currentProcessor = null;
+
+                /** @var PsrQueue $queue */
+                foreach ($this->boundProcessors as list($queue, $processor)) {
+                    if ($queue->getQueueName() === $consumer->getQueue()->getQueueName()) {
+                        $currentProcessor = $processor;
+                    }
+                }
+
+                if (false == $currentProcessor) {
+                    throw new \LogicException(sprintf('The processor for the queue "%s" could not be found.', $consumer->getQueue()->getQueueName()));
+                }
+
+                $context = new Context($this->psrContext);
+                $context->setLogger($this->logger);
+                $context->setPsrQueue($consumer->getQueue());
+                $context->setPsrConsumer($consumer);
+                $context->setPsrProcessor($currentProcessor);
+                $context->setPsrMessage($message);
+
+                $this->doConsume($this->extension, $context);
+
+                return true;
+            };
+
+            foreach ($consumers as $consumer) {
+                /* @var AmqpConsumer $consumer */
+
+                $subscriptionConsumer->subscribe($consumer, $callback);
+            }
+        } elseif ($this->psrContext instanceof AmqpContext) {
             $callback = function (AmqpMessage $message, AmqpConsumer $consumer) use (&$context) {
                 $currentProcessor = null;
 
@@ -223,7 +271,18 @@ public function consume(ExtensionInterface $runtimeExtension = null)
 
         while (true) {
             try {
-                if ($this->psrContext instanceof AmqpContext) {
+                if ($this->enableSubscriptionConsumer) {
+                    $this->extension->onBeforeReceive($context);
+
+                    if ($context->isExecutionInterrupted()) {
+                        throw new ConsumptionInterruptedException();
+                    }
+
+                    $subscriptionConsumer->consume($this->receiveTimeout);
+
+                    usleep($this->idleTimeout * 1000);
+                    $this->extension->onIdle($context);
+                } elseif ($this->psrContext instanceof AmqpContext) {
                     $this->extension->onBeforeReceive($context);
 
                     if ($context->isExecutionInterrupted()) {
@@ -251,7 +310,13 @@ public function consume(ExtensionInterface $runtimeExtension = null)
             } catch (ConsumptionInterruptedException $e) {
                 $this->logger->info(sprintf('Consuming interrupted'));
 
-                if ($this->psrContext instanceof AmqpContext) {
+                if ($this->enableSubscriptionConsumer) {
+                    foreach ($consumers as $consumer) {
+                        /* @var PsrConsumer $consumer */
+
+                        $subscriptionConsumer->unsubscribe($consumer);
+                    }
+                } elseif ($this->psrContext instanceof AmqpContext) {
                     foreach ($consumers as $consumer) {
                         /* @var AmqpConsumer $consumer */
 
@@ -279,6 +344,14 @@ public function consume(ExtensionInterface $runtimeExtension = null)
         }
     }
 
+    /**
+     * @param bool $enableSubscriptionConsumer
+     */
+    public function enableSubscriptionConsumer(bool $enableSubscriptionConsumer)
+    {
+        $this->enableSubscriptionConsumer = $enableSubscriptionConsumer;
+    }
+
     /**
      * @param ExtensionInterface $extension
      * @param Context            $context
diff --git a/Tests/Consumption/FallbackSubscriptionConsumerTest.php b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
new file mode 100644
index 0000000..b054180
--- /dev/null
+++ b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
@@ -0,0 +1,267 @@
+<?php
+
+namespace Enqueue\Tests\Consumption;
+
+use Enqueue\Consumption\FallbackSubscriptionConsumer;
+use Interop\Queue\PsrConsumer;
+use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrQueue;
+use Interop\Queue\PsrSubscriptionConsumer;
+
+class FallbackSubscriptionConsumerTest extends \PHPUnit_Framework_TestCase
+{
+    public function testShouldImplementPsrSubscriptionConsumerInterface()
+    {
+        $rc = new \ReflectionClass(FallbackSubscriptionConsumer::class);
+
+        $this->assertTrue($rc->implementsInterface(PsrSubscriptionConsumer::class));
+    }
+
+    public function testCouldBeConstructedWithoutAnyArguments()
+    {
+        new FallbackSubscriptionConsumer();
+    }
+
+    public function testShouldInitSubscribersPropertyWithEmptyArray()
+    {
+        $subscriptionConsumer = new FallbackSubscriptionConsumer();
+
+        $this->assertAttributeSame([], 'subscribers', $subscriptionConsumer);
+    }
+
+    public function testShouldAddConsumerAndCallbackToSubscribersPropertyOnSubscribe()
+    {
+        $subscriptionConsumer = new FallbackSubscriptionConsumer();
+
+        $fooCallback = function () {};
+        $fooConsumer = $this->createConsumerStub('foo_queue');
+
+        $barCallback = function () {};
+        $barConsumer = $this->createConsumerStub('bar_queue');
+
+        $subscriptionConsumer->subscribe($fooConsumer, $fooCallback);
+        $subscriptionConsumer->subscribe($barConsumer, $barCallback);
+
+        $this->assertAttributeSame([
+            'foo_queue' => [$fooConsumer, $fooCallback],
+            'bar_queue' => [$barConsumer, $barCallback],
+        ], 'subscribers', $subscriptionConsumer);
+    }
+
+    public function testThrowsIfTrySubscribeAnotherConsumerToAlreadySubscribedQueue()
+    {
+        $subscriptionConsumer = new FallbackSubscriptionConsumer();
+
+        $fooCallback = function () {};
+        $fooConsumer = $this->createConsumerStub('foo_queue');
+
+        $barCallback = function () {};
+        $barConsumer = $this->createConsumerStub('foo_queue');
+
+        $subscriptionConsumer->subscribe($fooConsumer, $fooCallback);
+
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('There is a consumer subscribed to queue: "foo_queue"');
+        $subscriptionConsumer->subscribe($barConsumer, $barCallback);
+    }
+
+    public function testShouldAllowSubscribeSameConsumerAndCallbackSecondTime()
+    {
+        $subscriptionConsumer = new FallbackSubscriptionConsumer();
+
+        $fooCallback = function () {};
+        $fooConsumer = $this->createConsumerStub('foo_queue');
+
+        $subscriptionConsumer->subscribe($fooConsumer, $fooCallback);
+        $subscriptionConsumer->subscribe($fooConsumer, $fooCallback);
+    }
+
+    public function testShouldRemoveSubscribedConsumerOnUnsubscribeCall()
+    {
+        $subscriptionConsumer = new FallbackSubscriptionConsumer();
+
+        $fooConsumer = $this->createConsumerStub('foo_queue');
+        $barConsumer = $this->createConsumerStub('bar_queue');
+
+        $subscriptionConsumer->subscribe($fooConsumer, function () {});
+        $subscriptionConsumer->subscribe($barConsumer, function () {});
+
+        // guard
+        $this->assertAttributeCount(2, 'subscribers', $subscriptionConsumer);
+
+        $subscriptionConsumer->unsubscribe($fooConsumer);
+
+        $this->assertAttributeCount(1, 'subscribers', $subscriptionConsumer);
+    }
+
+    public function testShouldDoNothingIfTryUnsubscribeNotSubscribedQueueName()
+    {
+        $subscriptionConsumer = new FallbackSubscriptionConsumer();
+
+        $subscriptionConsumer->subscribe($this->createConsumerStub('foo_queue'), function () {});
+
+        // guard
+        $this->assertAttributeCount(1, 'subscribers', $subscriptionConsumer);
+
+        $subscriptionConsumer->unsubscribe($this->createConsumerStub('bar_queue'));
+
+        $this->assertAttributeCount(1, 'subscribers', $subscriptionConsumer);
+    }
+
+    public function testShouldDoNothingIfTryUnsubscribeNotSubscribedConsumer()
+    {
+        $subscriptionConsumer = new FallbackSubscriptionConsumer();
+
+        $subscriptionConsumer->subscribe($this->createConsumerStub('foo_queue'), function () {});
+
+        // guard
+        $this->assertAttributeCount(1, 'subscribers', $subscriptionConsumer);
+
+        $subscriptionConsumer->unsubscribe($this->createConsumerStub('foo_queue'));
+
+        $this->assertAttributeCount(1, 'subscribers', $subscriptionConsumer);
+    }
+
+    public function testShouldRemoveAllSubscriberOnUnsubscribeAllCall()
+    {
+        $subscriptionConsumer = new FallbackSubscriptionConsumer();
+
+        $subscriptionConsumer->subscribe($this->createConsumerStub('foo_queue'), function () {});
+        $subscriptionConsumer->subscribe($this->createConsumerStub('bar_queue'), function () {});
+
+        // guard
+        $this->assertAttributeCount(2, 'subscribers', $subscriptionConsumer);
+
+        $subscriptionConsumer->unsubscribeAll();
+
+        $this->assertAttributeCount(0, 'subscribers', $subscriptionConsumer);
+    }
+
+    public function testShouldConsumeMessagesFromTwoQueuesInExpectedOrder()
+    {
+        $firstMessage = $this->createMessageStub('first');
+        $secondMessage = $this->createMessageStub('second');
+        $thirdMessage = $this->createMessageStub('third');
+        $fourthMessage = $this->createMessageStub('fourth');
+        $fifthMessage = $this->createMessageStub('fifth');
+
+        $fooMessages = [null, $firstMessage, null, $secondMessage, $thirdMessage];
+
+        $fooConsumer = $this->createConsumerStub('foo_queue');
+        $fooConsumer
+            ->expects($this->any())
+            ->method('receiveNoWait')
+            ->willReturnCallback(function () use (&$fooMessages) {
+                if (empty($fooMessages)) {
+                    return null;
+                }
+
+                return array_shift($fooMessages);
+            })
+        ;
+
+        $barMessages = [$fourthMessage, null, null, $fifthMessage];
+
+        $barConsumer = $this->createConsumerStub('bar_queue');
+        $barConsumer
+            ->expects($this->any())
+            ->method('receiveNoWait')
+            ->willReturnCallback(function () use (&$barMessages) {
+                if (empty($barMessages)) {
+                    return null;
+                }
+
+                return array_shift($barMessages);
+            })
+        ;
+
+        $actualOrder = [];
+        $callback = function (PsrMessage $message, PsrConsumer $consumer) use (&$actualOrder) {
+            $actualOrder[] = [$message->getBody(), $consumer->getQueue()->getQueueName()];
+        };
+
+        $subscriptionConsumer = new FallbackSubscriptionConsumer();
+
+        $subscriptionConsumer->subscribe($fooConsumer, $callback);
+        $subscriptionConsumer->subscribe($barConsumer, $callback);
+
+        $subscriptionConsumer->consume(100);
+
+        $this->assertEquals([
+            ['fourth', 'bar_queue'],
+            ['first', 'foo_queue'],
+            ['second', 'foo_queue'],
+            ['fifth', 'bar_queue'],
+            ['third', 'foo_queue'],
+        ], $actualOrder);
+    }
+
+    public function testThrowsIfTryConsumeWithoutSubscribers()
+    {
+        $subscriptionConsumer = new FallbackSubscriptionConsumer();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('No subscribers');
+        $subscriptionConsumer->consume();
+    }
+
+    public function testShouldConsumeTillTimeoutIsReached()
+    {
+        $fooConsumer = $this->createConsumerStub('foo_queue');
+        $fooConsumer
+            ->expects($this->any())
+            ->method('receiveNoWait')
+            ->willReturn(null)
+        ;
+
+        $subscriptionConsumer = new FallbackSubscriptionConsumer();
+
+        $subscriptionConsumer->subscribe($fooConsumer, function () {});
+
+        $startAt = microtime(true);
+        $subscriptionConsumer->consume(500);
+        $endAt = microtime(true);
+
+        $this->assertGreaterThan(0.49, $endAt - $startAt);
+    }
+
+    /**
+     * @param null|mixed $body
+     *
+     * @return PsrMessage|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createMessageStub($body = null)
+    {
+        $messageMock = $this->createMock(PsrMessage::class);
+        $messageMock
+            ->expects($this->any())
+            ->method('getBody')
+            ->willReturn($body)
+        ;
+
+        return $messageMock;
+    }
+
+    /**
+     * @param null|mixed $queueName
+     *
+     * @return PsrConsumer|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createConsumerStub($queueName = null)
+    {
+        $queueMock = $this->createMock(PsrQueue::class);
+        $queueMock
+            ->expects($this->any())
+            ->method('getQueueName')
+            ->willReturn($queueName);
+
+        $consumerMock = $this->createMock(PsrConsumer::class);
+        $consumerMock
+            ->expects($this->any())
+            ->method('getQueue')
+            ->willReturn($queueMock)
+        ;
+
+        return $consumerMock;
+    }
+}

From 6b4136c7c0e63e57171431a3472c2a5dcf9d0787 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 6 Aug 2018 22:26:00 +0300
Subject: [PATCH 062/337] prepare release.

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index fffef75..4f3632d 100644
--- a/composer.json
+++ b/composer.json
@@ -7,7 +7,7 @@
     "license": "MIT",
     "require": {
         "php": ">=5.6",
-        "queue-interop/queue-interop": "^0.6@dev|^1.0.0-alpha1",
+        "queue-interop/queue-interop": "^0.6.2@dev",
         "enqueue/null": "^0.8@dev",
         "ramsey/uuid": "^2|^3.5",
         "psr/log": "^1"

From 1a4f30056cce602d4d35a77737efdb104f212898 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 7 Aug 2018 09:28:39 +0300
Subject: [PATCH 063/337] remove bc layer.

---
 Consumption/QueueConsumer.php | 189 +++++++---------------------------
 1 file changed, 39 insertions(+), 150 deletions(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index d415c73..0ca7956 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -7,8 +7,6 @@
 use Enqueue\Consumption\Exception\LogicException;
 use Enqueue\Util\VarExport;
 use Interop\Amqp\AmqpConsumer;
-use Interop\Amqp\AmqpContext;
-use Interop\Amqp\AmqpMessage;
 use Interop\Queue\PsrConsumer;
 use Interop\Queue\PsrContext;
 use Interop\Queue\PsrMessage;
@@ -59,13 +57,6 @@ final class QueueConsumer
      */
     private $logger;
 
-    /**
-     * @deprecated added as BC layer, will be a default behavior in 0.9 version.
-     *
-     * @var bool
-     */
-    private $enableSubscriptionConsumer;
-
     /**
      * @param PsrContext                             $psrContext
      * @param ExtensionInterface|ChainExtension|null $extension
@@ -76,7 +67,7 @@ public function __construct(
         PsrContext $psrContext,
         ExtensionInterface $extension = null,
         $idleTimeout = 0,
-        $receiveTimeout = 10
+        $receiveTimeout = 10000
     ) {
         $this->psrContext = $psrContext;
         $this->staticExtension = $extension ?: new ChainExtension([]);
@@ -85,8 +76,6 @@ public function __construct(
 
         $this->boundProcessors = [];
         $this->logger = new NullLogger();
-
-        $this->enableSubscriptionConsumer = false;
     }
 
     /**
@@ -197,131 +186,66 @@ public function consume(ExtensionInterface $runtimeExtension = null)
 
         $this->logger->info('Start consuming');
 
-        $subscriptionConsumer = null;
-        if ($this->enableSubscriptionConsumer) {
-            $subscriptionConsumer = new FallbackSubscriptionConsumer();
-            if ($context instanceof PsrSubscriptionConsumerAwareContext) {
-                $subscriptionConsumer = $context->createSubscriptionConsumer();
-            }
-
-            $callback = function (PsrMessage $message, PsrConsumer $consumer) use (&$context) {
-                $currentProcessor = null;
+        $subscriptionConsumer = new FallbackSubscriptionConsumer();
+        if ($context instanceof PsrSubscriptionConsumerAwareContext) {
+            $subscriptionConsumer = $context->createSubscriptionConsumer();
+        }
 
-                /** @var PsrQueue $queue */
-                foreach ($this->boundProcessors as list($queue, $processor)) {
-                    if ($queue->getQueueName() === $consumer->getQueue()->getQueueName()) {
-                        $currentProcessor = $processor;
-                    }
-                }
+        $callback = function (PsrMessage $message, PsrConsumer $consumer) use (&$context) {
+            $currentProcessor = null;
 
-                if (false == $currentProcessor) {
-                    throw new \LogicException(sprintf('The processor for the queue "%s" could not be found.', $consumer->getQueue()->getQueueName()));
+            /** @var PsrQueue $queue */
+            foreach ($this->boundProcessors as list($queue, $processor)) {
+                if ($queue->getQueueName() === $consumer->getQueue()->getQueueName()) {
+                    $currentProcessor = $processor;
                 }
+            }
 
-                $context = new Context($this->psrContext);
-                $context->setLogger($this->logger);
-                $context->setPsrQueue($consumer->getQueue());
-                $context->setPsrConsumer($consumer);
-                $context->setPsrProcessor($currentProcessor);
-                $context->setPsrMessage($message);
-
-                $this->doConsume($this->extension, $context);
-
-                return true;
-            };
-
-            foreach ($consumers as $consumer) {
-                /* @var AmqpConsumer $consumer */
-
-                $subscriptionConsumer->subscribe($consumer, $callback);
+            if (false == $currentProcessor) {
+                throw new \LogicException(sprintf('The processor for the queue "%s" could not be found.', $consumer->getQueue()->getQueueName()));
             }
-        } elseif ($this->psrContext instanceof AmqpContext) {
-            $callback = function (AmqpMessage $message, AmqpConsumer $consumer) use (&$context) {
-                $currentProcessor = null;
-
-                /** @var PsrQueue $queue */
-                foreach ($this->boundProcessors as list($queue, $processor)) {
-                    if ($queue->getQueueName() === $consumer->getQueue()->getQueueName()) {
-                        $currentProcessor = $processor;
-                    }
-                }
 
-                if (false == $currentProcessor) {
-                    throw new \LogicException(sprintf('The processor for the queue "%s" could not be found.', $consumer->getQueue()->getQueueName()));
-                }
+            $context = new Context($this->psrContext);
+            $context->setLogger($this->logger);
+            $context->setPsrQueue($consumer->getQueue());
+            $context->setPsrConsumer($consumer);
+            $context->setPsrProcessor($currentProcessor);
+            $context->setPsrMessage($message);
 
-                $context = new Context($this->psrContext);
-                $context->setLogger($this->logger);
-                $context->setPsrQueue($consumer->getQueue());
-                $context->setPsrConsumer($consumer);
-                $context->setPsrProcessor($currentProcessor);
-                $context->setPsrMessage($message);
+            $this->processMessage($consumer, $currentProcessor, $message, $context);
 
-                $this->doConsume($this->extension, $context);
+            if ($context->isExecutionInterrupted()) {
+                throw new ConsumptionInterruptedException();
+            }
 
-                return true;
-            };
+            return true;
+        };
 
-            foreach ($consumers as $consumer) {
-                /* @var AmqpConsumer $consumer */
+        foreach ($consumers as $consumer) {
+            /* @var AmqpConsumer $consumer */
 
-                $this->psrContext->subscribe($consumer, $callback);
-            }
+            $subscriptionConsumer->subscribe($consumer, $callback);
         }
 
         while (true) {
             try {
-                if ($this->enableSubscriptionConsumer) {
-                    $this->extension->onBeforeReceive($context);
-
-                    if ($context->isExecutionInterrupted()) {
-                        throw new ConsumptionInterruptedException();
-                    }
-
-                    $subscriptionConsumer->consume($this->receiveTimeout);
-
-                    usleep($this->idleTimeout * 1000);
-                    $this->extension->onIdle($context);
-                } elseif ($this->psrContext instanceof AmqpContext) {
-                    $this->extension->onBeforeReceive($context);
+                $this->extension->onBeforeReceive($context);
 
-                    if ($context->isExecutionInterrupted()) {
-                        throw new ConsumptionInterruptedException();
-                    }
-
-                    $this->psrContext->consume($this->receiveTimeout);
-
-                    usleep($this->idleTimeout * 1000);
-                    $this->extension->onIdle($context);
-                } else {
-                    /** @var PsrQueue $queue */
-                    foreach ($this->boundProcessors as list($queue, $processor)) {
-                        $consumer = $consumers[$queue->getQueueName()];
+                if ($context->isExecutionInterrupted()) {
+                    throw new ConsumptionInterruptedException();
+                }
 
-                        $context = new Context($this->psrContext);
-                        $context->setLogger($this->logger);
-                        $context->setPsrQueue($queue);
-                        $context->setPsrConsumer($consumer);
-                        $context->setPsrProcessor($processor);
+                $subscriptionConsumer->consume($this->receiveTimeout);
 
-                        $this->doConsume($this->extension, $context);
-                    }
-                }
+                usleep($this->idleTimeout * 1000);
+                $this->extension->onIdle($context);
             } catch (ConsumptionInterruptedException $e) {
                 $this->logger->info(sprintf('Consuming interrupted'));
 
-                if ($this->enableSubscriptionConsumer) {
-                    foreach ($consumers as $consumer) {
-                        /* @var PsrConsumer $consumer */
-
-                        $subscriptionConsumer->unsubscribe($consumer);
-                    }
-                } elseif ($this->psrContext instanceof AmqpContext) {
-                    foreach ($consumers as $consumer) {
-                        /* @var AmqpConsumer $consumer */
+                foreach ($consumers as $consumer) {
+                    /* @var PsrConsumer $consumer */
 
-                        $this->psrContext->unsubscribe($consumer);
-                    }
+                    $subscriptionConsumer->unsubscribe($consumer);
                 }
 
                 $context->setExecutionInterrupted(true);
@@ -344,14 +268,6 @@ public function consume(ExtensionInterface $runtimeExtension = null)
         }
     }
 
-    /**
-     * @param bool $enableSubscriptionConsumer
-     */
-    public function enableSubscriptionConsumer(bool $enableSubscriptionConsumer)
-    {
-        $this->enableSubscriptionConsumer = $enableSubscriptionConsumer;
-    }
-
     /**
      * @param ExtensionInterface $extension
      * @param Context            $context
@@ -362,33 +278,6 @@ public function enableSubscriptionConsumer(bool $enableSubscriptionConsumer)
      */
     private function doConsume(ExtensionInterface $extension, Context $context)
     {
-        $processor = $context->getPsrProcessor();
-        $consumer = $context->getPsrConsumer();
-        $this->logger = $context->getLogger();
-
-        if ($context->isExecutionInterrupted()) {
-            throw new ConsumptionInterruptedException();
-        }
-
-        $message = $context->getPsrMessage();
-        if (false == $message) {
-            $this->extension->onBeforeReceive($context);
-
-            if ($message = $consumer->receive($this->receiveTimeout)) {
-                $context->setPsrMessage($message);
-            }
-        }
-
-        if ($message) {
-            $this->processMessage($consumer, $processor, $message, $context);
-        } else {
-            usleep($this->idleTimeout * 1000);
-            $this->extension->onIdle($context);
-        }
-
-        if ($context->isExecutionInterrupted()) {
-            throw new ConsumptionInterruptedException();
-        }
     }
 
     /**

From 3e2f68983dae41446004a672e9b600720885a5d5 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 7 Aug 2018 10:49:04 +0300
Subject: [PATCH 064/337] fix queue consumer tests. (not all for now)

---
 Consumption/QueueConsumer.php                 |  36 +++---
 .../Mock/DummySubscriptionConsumer.php        |  48 ++++++++
 Tests/Consumption/QueueConsumerTest.php       | 109 +++++++++++-------
 3 files changed, 136 insertions(+), 57 deletions(-)
 create mode 100644 Tests/Consumption/Mock/DummySubscriptionConsumer.php

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 0ca7956..328bac9 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -6,12 +6,12 @@
 use Enqueue\Consumption\Exception\InvalidArgumentException;
 use Enqueue\Consumption\Exception\LogicException;
 use Enqueue\Util\VarExport;
-use Interop\Amqp\AmqpConsumer;
 use Interop\Queue\PsrConsumer;
 use Interop\Queue\PsrContext;
 use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrProcessor;
 use Interop\Queue\PsrQueue;
+use Interop\Queue\PsrSubscriptionConsumer;
 use Interop\Queue\PsrSubscriptionConsumerAwareContext;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
@@ -57,6 +57,11 @@ final class QueueConsumer
      */
     private $logger;
 
+    /**
+     * @var PsrSubscriptionConsumer
+     */
+    private $fallbackSubscriptionConsumer;
+
     /**
      * @param PsrContext                             $psrContext
      * @param ExtensionInterface|ChainExtension|null $extension
@@ -76,6 +81,7 @@ public function __construct(
 
         $this->boundProcessors = [];
         $this->logger = new NullLogger();
+        $this->fallbackSubscriptionConsumer = new FallbackSubscriptionConsumer();
     }
 
     /**
@@ -186,7 +192,7 @@ public function consume(ExtensionInterface $runtimeExtension = null)
 
         $this->logger->info('Start consuming');
 
-        $subscriptionConsumer = new FallbackSubscriptionConsumer();
+        $subscriptionConsumer = $this->fallbackSubscriptionConsumer;
         if ($context instanceof PsrSubscriptionConsumerAwareContext) {
             $subscriptionConsumer = $context->createSubscriptionConsumer();
         }
@@ -222,7 +228,7 @@ public function consume(ExtensionInterface $runtimeExtension = null)
         };
 
         foreach ($consumers as $consumer) {
-            /* @var AmqpConsumer $consumer */
+            /* @var PsrConsumer $consumer */
 
             $subscriptionConsumer->subscribe($consumer, $callback);
         }
@@ -237,7 +243,7 @@ public function consume(ExtensionInterface $runtimeExtension = null)
 
                 $subscriptionConsumer->consume($this->receiveTimeout);
 
-                usleep($this->idleTimeout * 1000);
+                $this->idleTimeout && usleep($this->idleTimeout * 1000);
                 $this->extension->onIdle($context);
             } catch (ConsumptionInterruptedException $e) {
                 $this->logger->info(sprintf('Consuming interrupted'));
@@ -253,31 +259,25 @@ public function consume(ExtensionInterface $runtimeExtension = null)
                 $this->extension->onInterrupted($context);
 
                 return;
-            } catch (\Exception $exception) {
+            } catch (\Throwable $exception) {
                 $context->setExecutionInterrupted(true);
                 $context->setException($exception);
 
-                try {
-                    $this->onInterruptionByException($this->extension, $context);
-                } catch (\Exception $e) {
-                    // for some reason finally does not work here on php5.5
+                $this->onInterruptionByException($this->extension, $context);
 
-                    throw $e;
-                }
+                return;
             }
         }
     }
 
     /**
-     * @param ExtensionInterface $extension
-     * @param Context            $context
-     *
-     * @throws ConsumptionInterruptedException
+     * @internal
      *
-     * @return bool
+     * @param PsrSubscriptionConsumer $fallbackSubscriptionConsumer
      */
-    private function doConsume(ExtensionInterface $extension, Context $context)
+    public function setFallbackSubscriptionConsumer(PsrSubscriptionConsumer $fallbackSubscriptionConsumer): void
     {
+        $this->fallbackSubscriptionConsumer = $fallbackSubscriptionConsumer;
     }
 
     /**
@@ -331,7 +331,7 @@ private function processMessage(PsrConsumer $consumer, PsrProcessor $processor,
 
         $this->extension->onPreReceived($context);
         if (!$context->getResult()) {
-            $result = $processor->process($message, $this->psrContext);
+            $result = $processor->process($message, $context->getPsrContext());
             $context->setResult($result);
         }
 
diff --git a/Tests/Consumption/Mock/DummySubscriptionConsumer.php b/Tests/Consumption/Mock/DummySubscriptionConsumer.php
new file mode 100644
index 0000000..eb2bece
--- /dev/null
+++ b/Tests/Consumption/Mock/DummySubscriptionConsumer.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Enqueue\Tests\Consumption\Mock;
+
+use Interop\Queue\PsrConsumer;
+use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrSubscriptionConsumer;
+
+class DummySubscriptionConsumer implements PsrSubscriptionConsumer
+{
+    private $subscriptions = [];
+
+    private $messages = [];
+
+    /**
+     * @param float|int $timeout milliseconds 1000 is 1 second, a zero is consume endlessly
+     */
+    public function consume($timeout = 0)
+    {
+        foreach ($this->messages as list($message, $queueName)) {
+            /** @var PsrMessage $message */
+            /** @var string $queueName */
+            if (false == call_user_func($this->subscriptions[$queueName][1], $message, $this->subscriptions[$queueName][0])) {
+                return;
+            }
+        }
+    }
+
+    public function subscribe(PsrConsumer $consumer, callable $callback)
+    {
+        $this->subscriptions[$consumer->getQueue()->getQueueName()] = [$consumer, $callback];
+    }
+
+    public function unsubscribe(PsrConsumer $consumer)
+    {
+        unset($this->subscriptions[$consumer->getQueue()->getQueueName()]);
+    }
+
+    public function unsubscribeAll()
+    {
+        $this->subscriptions = [];
+    }
+
+    public function addMessage(PsrMessage $message, string $queueName)
+    {
+        $this->messages[] = [$message, $queueName];
+    }
+}
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 196f755..c0813ed 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -11,11 +11,13 @@
 use Enqueue\Consumption\Result;
 use Enqueue\Null\NullQueue;
 use Enqueue\Tests\Consumption\Mock\BreakCycleExtension;
+use Enqueue\Tests\Consumption\Mock\DummySubscriptionConsumer;
 use Interop\Queue\PsrConsumer;
 use Interop\Queue\PsrContext;
 use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrProcessor;
 use Interop\Queue\PsrQueue;
+use Interop\Queue\PsrSubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -171,10 +173,10 @@ public function testShouldSubscribeToGivenQueueWithExpectedTimeout()
     {
         $expectedQueue = new NullQueue('theQueueName');
 
-        $messageConsumerMock = $this->createMock(PsrConsumer::class);
-        $messageConsumerMock
+        $subscriptionConsumerMock = $this->createSubscriptionConsumerMock();
+        $subscriptionConsumerMock
             ->expects($this->once())
-            ->method('receive')
+            ->method('consume')
             ->with(12345)
             ->willReturn(null)
         ;
@@ -184,7 +186,7 @@ public function testShouldSubscribeToGivenQueueWithExpectedTimeout()
             ->expects($this->once())
             ->method('createConsumer')
             ->with($this->identicalTo($expectedQueue))
-            ->willReturn($messageConsumerMock)
+            ->willReturn($this->createConsumerStub())
         ;
 
         $processorMock = $this->createProcessorMock();
@@ -194,6 +196,7 @@ public function testShouldSubscribeToGivenQueueWithExpectedTimeout()
         ;
 
         $queueConsumer = new QueueConsumer($contextMock, new BreakCycleExtension(1), 0, 12345);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind($expectedQueue, $processorMock);
         $queueConsumer->consume();
     }
@@ -202,10 +205,10 @@ public function testShouldSubscribeToGivenQueueAndQuitAfterFifthIdleCycle()
     {
         $expectedQueue = new NullQueue('theQueueName');
 
-        $messageConsumerMock = $this->createMock(PsrConsumer::class);
-        $messageConsumerMock
+        $subscriptionConsumerMock = $this->createSubscriptionConsumerMock();
+        $subscriptionConsumerMock
             ->expects($this->exactly(5))
-            ->method('receive')
+            ->method('consume')
             ->willReturn(null)
         ;
 
@@ -214,7 +217,7 @@ public function testShouldSubscribeToGivenQueueAndQuitAfterFifthIdleCycle()
             ->expects($this->once())
             ->method('createConsumer')
             ->with($this->identicalTo($expectedQueue))
-            ->willReturn($messageConsumerMock)
+            ->willReturn($this->createConsumerStub())
         ;
 
         $processorMock = $this->createProcessorMock();
@@ -224,16 +227,29 @@ public function testShouldSubscribeToGivenQueueAndQuitAfterFifthIdleCycle()
         ;
 
         $queueConsumer = new QueueConsumer($contextMock, new BreakCycleExtension(5), 0);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind($expectedQueue, $processorMock);
         $queueConsumer->consume();
     }
 
     public function testShouldProcessFiveMessagesAndQuit()
     {
-        $messageMock = $this->createMessageMock();
-        $messageConsumerStub = $this->createMessageConsumerStub($messageMock);
+        $fooQueue = new NullQueue('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $firstMessageMock = $this->createMessageMock();
+        $secondMessageMock = $this->createMessageMock();
+        $thirdMessageMock = $this->createMessageMock();
+        $fourthMessageMock = $this->createMessageMock();
+        $fifthMessageMock = $this->createMessageMock();
+
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($firstMessageMock, 'foo_queue');
+        $subscriptionConsumerMock->addMessage($secondMessageMock, 'foo_queue');
+        $subscriptionConsumerMock->addMessage($thirdMessageMock, 'foo_queue');
+        $subscriptionConsumerMock->addMessage($fourthMessageMock, 'foo_queue');
+        $subscriptionConsumerMock->addMessage($fifthMessageMock, 'foo_queue');
+
+        $contextStub = $this->createPsrContextStub();
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -242,8 +258,9 @@ public function testShouldProcessFiveMessagesAndQuit()
             ->willReturn(Result::ACK)
         ;
 
-        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(5), 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(5));
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind($fooQueue, $processorMock);
 
         $queueConsumer->consume();
     }
@@ -251,7 +268,11 @@ public function testShouldProcessFiveMessagesAndQuit()
     public function testShouldAckMessageIfProcessorReturnSuchStatus()
     {
         $messageMock = $this->createMessageMock();
-        $messageConsumerStub = $this->createMessageConsumerStub($messageMock);
+
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($messageMock, 'foo_queue');
+
+        $messageConsumerStub = $this->createConsumerStub(new NullQueue('foo_queue'));
         $messageConsumerStub
             ->expects($this->once())
             ->method('acknowledge')
@@ -269,7 +290,8 @@ public function testShouldAckMessageIfProcessorReturnSuchStatus()
         ;
 
         $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1), 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $queueConsumer->consume();
     }
@@ -1112,40 +1134,26 @@ public function testShouldCallEachQueueOneByOne()
     }
 
     /**
-     * @param null|mixed $message
-     *
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrConsumer
-     */
-    protected function createMessageConsumerStub($message = null)
-    {
-        $messageConsumerMock = $this->createMock(PsrConsumer::class);
-        $messageConsumerMock
-            ->expects($this->any())
-            ->method('receive')
-            ->willReturn($message)
-        ;
-
-        return $messageConsumerMock;
-    }
-
-    /**
-     * @param null|mixed $messageConsumer
-     *
      * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext
      */
-    protected function createPsrContextStub($messageConsumer = null)
+    protected function createPsrContextStub(PsrConsumer $consumer = null)
     {
         $context = $this->createMock(PsrContext::class);
         $context
             ->expects($this->any())
-            ->method('createConsumer')
-            ->willReturn($messageConsumer)
+            ->method('createQueue')
+            ->willReturnCallback(function (string $queueName) {
+                return new NullQueue($queueName);
+            })
         ;
         $context
             ->expects($this->any())
-            ->method('createQueue')
-            ->willReturn($this->createMock(PsrQueue::class))
+            ->method('createConsumer')
+            ->willReturnCallback(function (PsrQueue $queue) use ($consumer) {
+                return $consumer ?: $this->createConsumerStub($queue);
+            })
         ;
+
         $context
             ->expects($this->any())
             ->method('close')
@@ -1192,4 +1200,27 @@ protected function createExtension()
     {
         return $this->createMock(ExtensionInterface::class);
     }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|PsrConsumer
+     */
+    protected function createConsumerStub(PsrQueue $queue = null)
+    {
+        $consumerMock = $this->createMock(PsrConsumer::class);
+        $consumerMock
+            ->expects($this->any())
+            ->method('getQueue')
+            ->willReturn($queue)
+        ;
+
+        return $consumerMock;
+    }
+
+    /**
+     * @return PsrSubscriptionConsumer|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createSubscriptionConsumerMock()
+    {
+        return $this->createMock(PsrSubscriptionConsumer::class);
+    }
 }

From 41721696ae457e861c250541ba555d5ab99c6aeb Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 7 Aug 2018 12:18:45 +0300
Subject: [PATCH 065/337] fix queue consumer tests.

---
 Consumption/Context.php                 |   6 +-
 Consumption/QueueConsumer.php           |   4 +
 Tests/Consumption/QueueConsumerTest.php | 352 ++++++++++++++++--------
 3 files changed, 243 insertions(+), 119 deletions(-)

diff --git a/Consumption/Context.php b/Consumption/Context.php
index 09332b9..0625e60 100644
--- a/Consumption/Context.php
+++ b/Consumption/Context.php
@@ -136,7 +136,7 @@ public function setPsrProcessor(PsrProcessor $psrProcessor)
     }
 
     /**
-     * @return \Exception
+     * @return \Throwable
      */
     public function getException()
     {
@@ -144,9 +144,9 @@ public function getException()
     }
 
     /**
-     * @param \Exception $exception
+     * @param \Throwable $exception
      */
-    public function setException(\Exception $exception)
+    public function setException(\Throwable $exception)
     {
         $this->exception = $exception;
     }
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 328bac9..92f4b15 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -245,6 +245,10 @@ public function consume(ExtensionInterface $runtimeExtension = null)
 
                 $this->idleTimeout && usleep($this->idleTimeout * 1000);
                 $this->extension->onIdle($context);
+
+                if ($context->isExecutionInterrupted()) {
+                    throw new ConsumptionInterruptedException();
+                }
             } catch (ConsumptionInterruptedException $e) {
                 $this->logger->info(sprintf('Consuming interrupted'));
 
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index c0813ed..196513d 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -166,7 +166,7 @@ public function testShouldReturnSelfOnBind()
 
         $consumer = new QueueConsumer($this->createPsrContextStub(), null, 0);
 
-        $this->assertSame($consumer, $consumer->bind(new NullQueue('aQueueName'), $processorMock));
+        $this->assertSame($consumer, $consumer->bind(new NullQueue('foo_queue'), $processorMock));
     }
 
     public function testShouldSubscribeToGivenQueueWithExpectedTimeout()
@@ -272,14 +272,14 @@ public function testShouldAckMessageIfProcessorReturnSuchStatus()
         $subscriptionConsumerMock = new DummySubscriptionConsumer();
         $subscriptionConsumerMock->addMessage($messageMock, 'foo_queue');
 
-        $messageConsumerStub = $this->createConsumerStub(new NullQueue('foo_queue'));
-        $messageConsumerStub
+        $consumerStub = $this->createConsumerStub('foo_queue');
+        $consumerStub
             ->expects($this->once())
             ->method('acknowledge')
             ->with($this->identicalTo($messageMock))
         ;
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -299,9 +299,13 @@ public function testShouldAckMessageIfProcessorReturnSuchStatus()
     public function testThrowIfProcessorReturnNull()
     {
         $messageMock = $this->createMessageMock();
-        $messageConsumerStub = $this->createMessageConsumerStub($messageMock);
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($messageMock, 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -312,7 +316,8 @@ public function testThrowIfProcessorReturnNull()
         ;
 
         $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1), 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('Status is not supported');
@@ -322,14 +327,18 @@ public function testThrowIfProcessorReturnNull()
     public function testShouldRejectMessageIfProcessorReturnSuchStatus()
     {
         $messageMock = $this->createMessageMock();
-        $messageConsumerStub = $this->createMessageConsumerStub($messageMock);
-        $messageConsumerStub
+
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($messageMock, 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
+        $consumerStub
             ->expects($this->once())
             ->method('reject')
             ->with($this->identicalTo($messageMock), false)
         ;
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -340,7 +349,8 @@ public function testShouldRejectMessageIfProcessorReturnSuchStatus()
         ;
 
         $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1), 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $queueConsumer->consume();
     }
@@ -348,14 +358,18 @@ public function testShouldRejectMessageIfProcessorReturnSuchStatus()
     public function testShouldRequeueMessageIfProcessorReturnSuchStatus()
     {
         $messageMock = $this->createMessageMock();
-        $messageConsumerStub = $this->createMessageConsumerStub($messageMock);
-        $messageConsumerStub
+
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($messageMock, 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
+        $consumerStub
             ->expects($this->once())
             ->method('reject')
             ->with($this->identicalTo($messageMock), true)
         ;
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -366,7 +380,8 @@ public function testShouldRequeueMessageIfProcessorReturnSuchStatus()
         ;
 
         $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1), 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $queueConsumer->consume();
     }
@@ -374,9 +389,13 @@ public function testShouldRequeueMessageIfProcessorReturnSuchStatus()
     public function testThrowIfProcessorReturnInvalidStatus()
     {
         $messageMock = $this->createMessageMock();
-        $messageConsumerStub = $this->createMessageConsumerStub($messageMock);
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($messageMock, 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -387,7 +406,8 @@ public function testThrowIfProcessorReturnInvalidStatus()
         ;
 
         $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1), 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('Status is not supported: invalidStatus');
@@ -407,9 +427,13 @@ public function testShouldNotPassMessageToProcessorIfItWasProcessedByExtension()
         ;
 
         $messageMock = $this->createMessageMock();
-        $messageConsumerStub = $this->createMessageConsumerStub($messageMock);
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($messageMock, 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -419,16 +443,17 @@ public function testShouldNotPassMessageToProcessorIfItWasProcessedByExtension()
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
         $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $queueConsumer->consume();
     }
 
     public function testShouldCallOnStartExtensionMethod()
     {
-        $messageConsumerStub = $this->createMessageConsumerStub($message = null);
+        $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
 
@@ -452,16 +477,17 @@ public function testShouldCallOnStartExtensionMethod()
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
         $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer(new DummySubscriptionConsumer());
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $queueConsumer->consume();
     }
 
     public function testShouldCallOnIdleExtensionMethod()
     {
-        $messageConsumerStub = $this->createMessageConsumerStub($message = null);
+        $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
 
@@ -472,13 +498,13 @@ public function testShouldCallOnIdleExtensionMethod()
             ->with($this->isInstanceOf(Context::class))
             ->willReturnCallback(function (Context $context) use (
                 $contextStub,
-                $messageConsumerStub,
+                $consumerStub,
                 $processorMock
             ) {
                 $this->assertSame($contextStub, $context->getPsrContext());
-                $this->assertSame($messageConsumerStub, $context->getPsrConsumer());
-                $this->assertSame($processorMock, $context->getPsrProcessor());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
+                $this->assertNull($context->getPsrProcessor());
+                $this->assertNull($context->getPsrConsumer());
                 $this->assertNull($context->getPsrMessage());
                 $this->assertNull($context->getException());
                 $this->assertNull($context->getResult());
@@ -488,21 +514,21 @@ public function testShouldCallOnIdleExtensionMethod()
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
         $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer(new DummySubscriptionConsumer());
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $queueConsumer->consume();
     }
 
     public function testShouldCallOnBeforeReceiveExtensionMethod()
     {
-        $expectedMessage = $this->createMessageMock();
-        $messageConsumerStub = $this->createMessageConsumerStub($expectedMessage);
+        $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorStub();
 
-        $queue = new NullQueue('aQueueName');
+        $queue = new NullQueue('foo_queue');
 
         $extension = $this->createExtension();
         $extension
@@ -511,24 +537,25 @@ public function testShouldCallOnBeforeReceiveExtensionMethod()
             ->with($this->isInstanceOf(Context::class))
             ->willReturnCallback(function (Context $context) use (
                 $contextStub,
-                $messageConsumerStub,
+                $consumerStub,
                 $processorMock,
                 $queue
             ) {
                 $this->assertSame($contextStub, $context->getPsrContext());
-                $this->assertSame($messageConsumerStub, $context->getPsrConsumer());
-                $this->assertSame($processorMock, $context->getPsrProcessor());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
+                $this->assertNull($context->getPsrProcessor());
+                $this->assertNull($context->getPsrConsumer());
                 $this->assertNull($context->getPsrMessage());
                 $this->assertNull($context->getException());
                 $this->assertNull($context->getResult());
+                $this->assertNull($context->getPsrQueue());
                 $this->assertFalse($context->isExecutionInterrupted());
-                $this->assertSame($queue, $context->getPsrQueue());
             })
         ;
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
         $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
+        $queueConsumer->setFallbackSubscriptionConsumer(new DummySubscriptionConsumer());
         $queueConsumer->bind($queue, $processorMock);
 
         $queueConsumer->consume();
@@ -537,9 +564,13 @@ public function testShouldCallOnBeforeReceiveExtensionMethod()
     public function testShouldCallOnPreReceivedExtensionMethodWithExpectedContext()
     {
         $expectedMessage = $this->createMessageMock();
-        $messageConsumerStub = $this->createMessageConsumerStub($expectedMessage);
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($expectedMessage, 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorStub();
 
@@ -550,12 +581,12 @@ public function testShouldCallOnPreReceivedExtensionMethodWithExpectedContext()
             ->with($this->isInstanceOf(Context::class))
             ->willReturnCallback(function (Context $context) use (
                 $contextStub,
-                $messageConsumerStub,
+                $consumerStub,
                 $processorMock,
                 $expectedMessage
             ) {
                 $this->assertSame($contextStub, $context->getPsrContext());
-                $this->assertSame($messageConsumerStub, $context->getPsrConsumer());
+                $this->assertSame($consumerStub, $context->getPsrConsumer());
                 $this->assertSame($processorMock, $context->getPsrProcessor());
                 $this->assertSame($expectedMessage, $context->getPsrMessage());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
@@ -567,7 +598,8 @@ public function testShouldCallOnPreReceivedExtensionMethodWithExpectedContext()
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
         $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $queueConsumer->consume();
     }
@@ -575,9 +607,13 @@ public function testShouldCallOnPreReceivedExtensionMethodWithExpectedContext()
     public function testShouldCallOnResultExtensionMethodWithExpectedContext()
     {
         $expectedMessage = $this->createMessageMock();
-        $messageConsumerStub = $this->createMessageConsumerStub($expectedMessage);
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($expectedMessage, 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorStub();
 
@@ -588,12 +624,12 @@ public function testShouldCallOnResultExtensionMethodWithExpectedContext()
             ->with($this->isInstanceOf(Context::class))
             ->willReturnCallback(function (Context $context) use (
                 $contextStub,
-                $messageConsumerStub,
+                $consumerStub,
                 $processorMock,
                 $expectedMessage
             ) {
                 $this->assertSame($contextStub, $context->getPsrContext());
-                $this->assertSame($messageConsumerStub, $context->getPsrConsumer());
+                $this->assertSame($consumerStub, $context->getPsrConsumer());
                 $this->assertSame($processorMock, $context->getPsrProcessor());
                 $this->assertSame($expectedMessage, $context->getPsrMessage());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
@@ -605,7 +641,8 @@ public function testShouldCallOnResultExtensionMethodWithExpectedContext()
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
         $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $queueConsumer->consume();
     }
@@ -613,9 +650,13 @@ public function testShouldCallOnResultExtensionMethodWithExpectedContext()
     public function testShouldCallOnPostReceivedExtensionMethodWithExpectedContext()
     {
         $expectedMessage = $this->createMessageMock();
-        $messageConsumerStub = $this->createMessageConsumerStub($expectedMessage);
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($expectedMessage, 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorStub();
 
@@ -626,12 +667,12 @@ public function testShouldCallOnPostReceivedExtensionMethodWithExpectedContext()
             ->with($this->isInstanceOf(Context::class))
             ->willReturnCallback(function (Context $context) use (
                 $contextStub,
-                $messageConsumerStub,
+                $consumerStub,
                 $processorMock,
                 $expectedMessage
             ) {
                 $this->assertSame($contextStub, $context->getPsrContext());
-                $this->assertSame($messageConsumerStub, $context->getPsrConsumer());
+                $this->assertSame($consumerStub, $context->getPsrConsumer());
                 $this->assertSame($processorMock, $context->getPsrProcessor());
                 $this->assertSame($expectedMessage, $context->getPsrMessage());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
@@ -643,16 +684,17 @@ public function testShouldCallOnPostReceivedExtensionMethodWithExpectedContext()
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
         $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $queueConsumer->consume();
     }
 
     public function testShouldAllowInterruptConsumingOnIdle()
     {
-        $messageConsumerStub = $this->createMessageConsumerStub($message = null);
+        $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
 
@@ -671,13 +713,13 @@ public function testShouldAllowInterruptConsumingOnIdle()
             ->with($this->isInstanceOf(Context::class))
             ->willReturnCallback(function (Context $context) use (
                 $contextStub,
-                $messageConsumerStub,
+                $consumerStub,
                 $processorMock
             ) {
                 $this->assertSame($contextStub, $context->getPsrContext());
-                $this->assertSame($messageConsumerStub, $context->getPsrConsumer());
-                $this->assertSame($processorMock, $context->getPsrProcessor());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
+                $this->assertNull($context->getPsrConsumer());
+                $this->assertNull($context->getPsrProcessor());
                 $this->assertNull($context->getPsrMessage());
                 $this->assertNull($context->getException());
                 $this->assertNull($context->getResult());
@@ -687,16 +729,17 @@ public function testShouldAllowInterruptConsumingOnIdle()
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
         $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer(new DummySubscriptionConsumer());
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $queueConsumer->consume();
     }
 
     public function testShouldNotCloseContextWhenConsumptionInterrupted()
     {
-        $messageConsumerStub = $this->createMessageConsumerStub($message = null);
+        $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $contextStub = $this->createPsrContextStub($consumerStub);
         $contextStub
             ->expects($this->never())
             ->method('close')
@@ -716,7 +759,8 @@ public function testShouldNotCloseContextWhenConsumptionInterrupted()
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
         $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer(new DummySubscriptionConsumer());
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $queueConsumer->consume();
     }
@@ -725,9 +769,12 @@ public function testShouldNotCloseContextWhenConsumptionInterruptedByException()
     {
         $expectedException = new \Exception();
 
-        $messageConsumerStub = $this->createMessageConsumerStub($message = $this->createMessageMock());
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($this->createMessageMock(), 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $contextStub = $this->createPsrContextStub($consumerStub);
         $contextStub
             ->expects($this->never())
             ->method('close')
@@ -741,7 +788,8 @@ public function testShouldNotCloseContextWhenConsumptionInterruptedByException()
         ;
 
         $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1), 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         try {
             $queueConsumer->consume();
@@ -760,9 +808,12 @@ public function testShouldSetMainExceptionAsPreviousToExceptionThrownOnInterrupt
         $mainException = new \Exception();
         $expectedException = new \Exception();
 
-        $messageConsumerStub = $this->createMessageConsumerStub($message = $this->createMessageMock());
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($this->createMessageMock(), 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -780,7 +831,8 @@ public function testShouldSetMainExceptionAsPreviousToExceptionThrownOnInterrupt
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
         $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         try {
             $queueConsumer->consume();
@@ -797,9 +849,13 @@ public function testShouldSetMainExceptionAsPreviousToExceptionThrownOnInterrupt
     public function testShouldAllowInterruptConsumingOnPreReceiveButProcessCurrentMessage()
     {
         $expectedMessage = $this->createMessageMock();
-        $messageConsumerStub = $this->createMessageConsumerStub($expectedMessage);
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($expectedMessage, 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -823,12 +879,12 @@ public function testShouldAllowInterruptConsumingOnPreReceiveButProcessCurrentMe
             ->with($this->isInstanceOf(Context::class))
             ->willReturnCallback(function (Context $context) use (
                 $contextStub,
-                $messageConsumerStub,
+                $consumerStub,
                 $processorMock,
                 $expectedMessage
             ) {
                 $this->assertSame($contextStub, $context->getPsrContext());
-                $this->assertSame($messageConsumerStub, $context->getPsrConsumer());
+                $this->assertSame($consumerStub, $context->getPsrConsumer());
                 $this->assertSame($processorMock, $context->getPsrProcessor());
                 $this->assertSame($expectedMessage, $context->getPsrMessage());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
@@ -840,7 +896,8 @@ public function testShouldAllowInterruptConsumingOnPreReceiveButProcessCurrentMe
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
         $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $queueConsumer->consume();
     }
@@ -848,9 +905,13 @@ public function testShouldAllowInterruptConsumingOnPreReceiveButProcessCurrentMe
     public function testShouldAllowInterruptConsumingOnResult()
     {
         $expectedMessage = $this->createMessageMock();
-        $messageConsumerStub = $this->createMessageConsumerStub($expectedMessage);
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($expectedMessage, 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -874,12 +935,12 @@ public function testShouldAllowInterruptConsumingOnResult()
             ->with($this->isInstanceOf(Context::class))
             ->willReturnCallback(function (Context $context) use (
                 $contextStub,
-                $messageConsumerStub,
+                $consumerStub,
                 $processorMock,
                 $expectedMessage
             ) {
                 $this->assertSame($contextStub, $context->getPsrContext());
-                $this->assertSame($messageConsumerStub, $context->getPsrConsumer());
+                $this->assertSame($consumerStub, $context->getPsrConsumer());
                 $this->assertSame($processorMock, $context->getPsrProcessor());
                 $this->assertSame($expectedMessage, $context->getPsrMessage());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
@@ -891,7 +952,8 @@ public function testShouldAllowInterruptConsumingOnResult()
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
         $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $queueConsumer->consume();
     }
@@ -899,9 +961,13 @@ public function testShouldAllowInterruptConsumingOnResult()
     public function testShouldAllowInterruptConsumingOnPostReceive()
     {
         $expectedMessage = $this->createMessageMock();
-        $messageConsumerStub = $this->createMessageConsumerStub($expectedMessage);
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($expectedMessage, 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -925,12 +991,12 @@ public function testShouldAllowInterruptConsumingOnPostReceive()
             ->with($this->isInstanceOf(Context::class))
             ->willReturnCallback(function (Context $context) use (
                 $contextStub,
-                $messageConsumerStub,
+                $consumerStub,
                 $processorMock,
                 $expectedMessage
             ) {
                 $this->assertSame($contextStub, $context->getPsrContext());
-                $this->assertSame($messageConsumerStub, $context->getPsrConsumer());
+                $this->assertSame($consumerStub, $context->getPsrConsumer());
                 $this->assertSame($processorMock, $context->getPsrProcessor());
                 $this->assertSame($expectedMessage, $context->getPsrMessage());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
@@ -942,7 +1008,8 @@ public function testShouldAllowInterruptConsumingOnPostReceive()
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
         $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $queueConsumer->consume();
     }
@@ -951,9 +1018,13 @@ public function testShouldCallOnInterruptedIfExceptionThrow()
     {
         $expectedException = new \Exception('Process failed');
         $expectedMessage = $this->createMessageMock();
-        $messageConsumerStub = $this->createMessageConsumerStub($expectedMessage);
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($expectedMessage, 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -969,13 +1040,13 @@ public function testShouldCallOnInterruptedIfExceptionThrow()
             ->with($this->isInstanceOf(Context::class))
             ->willReturnCallback(function (Context $context) use (
                 $contextStub,
-                $messageConsumerStub,
+                $consumerStub,
                 $processorMock,
                 $expectedMessage,
                 $expectedException
             ) {
                 $this->assertSame($contextStub, $context->getPsrContext());
-                $this->assertSame($messageConsumerStub, $context->getPsrConsumer());
+                $this->assertSame($consumerStub, $context->getPsrConsumer());
                 $this->assertSame($processorMock, $context->getPsrProcessor());
                 $this->assertSame($expectedMessage, $context->getPsrMessage());
                 $this->assertSame($expectedException, $context->getException());
@@ -987,7 +1058,8 @@ public function testShouldCallOnInterruptedIfExceptionThrow()
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
         $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $this->expectException(\Exception::class);
         $this->expectExceptionMessage('Process failed');
@@ -997,9 +1069,13 @@ public function testShouldCallOnInterruptedIfExceptionThrow()
     public function testShouldCallExtensionPassedOnRuntime()
     {
         $expectedMessage = $this->createMessageMock();
-        $messageConsumerStub = $this->createMessageConsumerStub($expectedMessage);
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($expectedMessage, 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -1036,7 +1112,8 @@ public function testShouldCallExtensionPassedOnRuntime()
         ;
 
         $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1), 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $queueConsumer->consume(new ChainExtension([$runtimeExtension]));
     }
@@ -1044,9 +1121,13 @@ public function testShouldCallExtensionPassedOnRuntime()
     public function testShouldChangeLoggerOnStart()
     {
         $expectedMessage = $this->createMessageMock();
-        $messageConsumerStub = $this->createMessageConsumerStub($expectedMessage);
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($expectedMessage, 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createPsrContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -1086,51 +1167,84 @@ public function testShouldChangeLoggerOnStart()
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
 
         $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
-        $queueConsumer->bind(new NullQueue('aQueueName'), $processorMock);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $queueConsumer->consume();
     }
 
-    public function testShouldCallEachQueueOneByOne()
+    public function testShouldCallProcessorAsMessageComeAlong()
     {
-        $expectedMessage = $this->createMessageMock();
-        $messageConsumerStub = $this->createMessageConsumerStub($expectedMessage);
+        $queue1 = new NullQueue('foo_queue');
+        $queue2 = new NullQueue('bar_queue');
 
-        $contextStub = $this->createPsrContextStub($messageConsumerStub);
+        $firstMessage = $this->createMessageMock();
+        $secondMessage = $this->createMessageMock();
+        $thirdMessage = $this->createMessageMock();
+
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($firstMessage, 'foo_queue');
+        $subscriptionConsumerMock->addMessage($secondMessage, 'bar_queue');
+        $subscriptionConsumerMock->addMessage($thirdMessage, 'foo_queue');
+
+        $fooConsumerStub = $this->createConsumerStub($queue1);
+        $barConsumerStub = $this->createConsumerStub($queue2);
+
+        $consumers = [
+            'foo_queue' => $fooConsumerStub,
+            'bar_queue' => $barConsumerStub,
+        ];
+
+        $contextStub = $this->createMock(PsrContext::class);
+        $contextStub
+            ->expects($this->any())
+            ->method('createQueue')
+            ->willReturnCallback(function (string $queueName) {
+                return new NullQueue($queueName);
+            })
+        ;
+        $contextStub
+            ->expects($this->any())
+            ->method('createConsumer')
+            ->willReturnCallback(function (PsrQueue $queue) use ($consumers) {
+                return $consumers[$queue->getQueueName()];
+            })
+        ;
 
         $processorMock = $this->createProcessorStub();
         $anotherProcessorMock = $this->createProcessorStub();
 
-        $queue1 = new NullQueue('aQueueName');
-        $queue2 = new NullQueue('aAnotherQueueName');
+        /** @var Context[] $actualContexts */
+        $actualContexts = [];
 
         $extension = $this->createExtension();
         $extension
-            ->expects($this->at(1))
-            ->method('onBeforeReceive')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use ($processorMock, $queue1) {
-                $this->assertSame($processorMock, $context->getPsrProcessor());
-                $this->assertSame($queue1, $context->getPsrQueue());
-            })
-        ;
-        $extension
-            ->expects($this->at(5))
-            ->method('onBeforeReceive')
+            ->expects($this->any())
+            ->method('onPreReceived')
             ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use ($anotherProcessorMock, $queue2) {
-                $this->assertSame($anotherProcessorMock, $context->getPsrProcessor());
-                $this->assertSame($queue2, $context->getPsrQueue());
+            ->willReturnCallback(function (Context $context) use ($processorMock, $queue1, &$actualContexts) {
+                $actualContexts[] = clone $context;
             })
         ;
 
-        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(2), 0);
+        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(3), 0);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer
             ->bind($queue1, $processorMock)
             ->bind($queue2, $anotherProcessorMock)
         ;
 
         $queueConsumer->consume(new ChainExtension([$extension]));
+
+        $this->assertCount(3, $actualContexts);
+
+        $this->assertSame($firstMessage, $actualContexts[0]->getPsrMessage());
+        $this->assertSame($secondMessage, $actualContexts[1]->getPsrMessage());
+        $this->assertSame($thirdMessage, $actualContexts[2]->getPsrMessage());
+
+        $this->assertSame($fooConsumerStub, $actualContexts[0]->getPsrConsumer());
+        $this->assertSame($barConsumerStub, $actualContexts[1]->getPsrConsumer());
+        $this->assertSame($fooConsumerStub, $actualContexts[2]->getPsrConsumer());
     }
 
     /**
@@ -1202,10 +1316,16 @@ protected function createExtension()
     }
 
     /**
+     * @param null|mixed $queue
+     *
      * @return \PHPUnit_Framework_MockObject_MockObject|PsrConsumer
      */
-    protected function createConsumerStub(PsrQueue $queue = null)
+    protected function createConsumerStub($queue = null)
     {
+        if (is_string($queue)) {
+            $queue = new NullQueue($queue);
+        }
+
         $consumerMock = $this->createMock(PsrConsumer::class);
         $consumerMock
             ->expects($this->any())

From c07a1cdd3fed4dcf6a6466635beca6c3be87ea50 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 7 Aug 2018 12:31:31 +0300
Subject: [PATCH 066/337] revert final

---
 Consumption/QueueConsumer.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 92f4b15..0b82830 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -16,7 +16,7 @@
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
-final class QueueConsumer
+class QueueConsumer
 {
     /**
      * @var PsrContext

From d62f18a55eced7a12e5a906590c6851e1de292ae Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Wed, 8 Aug 2018 12:34:35 +0300
Subject: [PATCH 067/337] 0.7@dev to 0.7.x-dev

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 46e9301..2f5419e 100644
--- a/composer.json
+++ b/composer.json
@@ -7,7 +7,7 @@
     "license": "MIT",
     "require": {
         "php": "^7.1.3",
-        "queue-interop/queue-interop": "^0.6.2@dev|^0.7@dev",
+        "queue-interop/queue-interop": "^0.6.2@dev|0.7.x-dev",
         "enqueue/null": "^0.9@dev",
         "ramsey/uuid": "^2|^3.5",
         "psr/log": "^1"

From 4e235fc74b84d486e467513c7c30142cf3960b89 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Wed, 8 Aug 2018 17:32:42 +0300
Subject: [PATCH 068/337] fix req.

---
 composer.json | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/composer.json b/composer.json
index 2f5419e..0e6819d 100644
--- a/composer.json
+++ b/composer.json
@@ -8,7 +8,7 @@
     "require": {
         "php": "^7.1.3",
         "queue-interop/queue-interop": "^0.6.2@dev|0.7.x-dev",
-        "enqueue/null": "^0.9@dev",
+        "enqueue/null": "0.9.x-dev",
         "ramsey/uuid": "^2|^3.5",
         "psr/log": "^1"
     },
@@ -19,21 +19,21 @@
         "symfony/config": "^3.4|^4",
         "symfony/event-dispatcher": "^3.4|^4",
         "symfony/http-kernel": "^3.4|^4",
-        "enqueue/amqp-ext": "^0.9@dev",
-        "enqueue/amqp-lib": "^0.9@dev",
-        "enqueue/amqp-bunny": "^0.9@dev",
-        "enqueue/pheanstalk": "^0.9@dev",
-        "enqueue/gearman": "^0.9@dev",
-        "enqueue/rdkafka": "^0.9@dev",
-        "enqueue/dbal": "^0.9@dev",
-        "enqueue/fs": "^0.9@dev",
-        "enqueue/gps": "^0.9@dev",
-        "enqueue/redis": "^0.9@dev",
-        "enqueue/sqs": "^0.9@dev",
-        "enqueue/stomp": "^0.9@dev",
-        "enqueue/test": "^0.9@dev",
-        "enqueue/simple-client": "^0.9@dev",
-        "enqueue/mongodb": "^0.9@dev",
+        "enqueue/amqp-ext": "0.9.x-dev",
+        "enqueue/amqp-lib": "0.9.x-dev",
+        "enqueue/amqp-bunny": "0.9.x-dev",
+        "enqueue/pheanstalk": "0.9.x-dev",
+        "enqueue/gearman": "0.9.x-dev",
+        "enqueue/rdkafka": "0.9.x-dev",
+        "enqueue/dbal": "0.9.x-dev",
+        "enqueue/fs": "0.9.x-dev",
+        "enqueue/gps": "0.9.x-dev",
+        "enqueue/redis": "0.9.x-dev",
+        "enqueue/sqs": "0.9.x-dev",
+        "enqueue/stomp": "0.9.x-dev",
+        "enqueue/test": "0.9.x-dev",
+        "enqueue/simple-client": "0.9.x-dev",
+        "enqueue/mongodb": "0.9.x-dev",
         "empi89/php-amqp-stubs": "*@dev"
     },
     "suggest": {

From bf4065ed6fec5468916e65c7dfa1c6c3d46c47a1 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Wed, 8 Aug 2018 17:47:01 +0300
Subject: [PATCH 069/337] add mongodb ext to travis

---
 .travis.yml | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 658dcab..2c830d0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,8 +6,13 @@ git:
 language: php
 
 php:
-    - '5.6'
-    - '7.0'
+    - '7.1'
+
+services:
+  - mongodb
+
+before_install:
+    - echo "extension = mongodb.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
 
 cache:
     directories:

From beae16559ffd713c83044cee13f1c4a5993cc240 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 9 Aug 2018 09:51:49 +0300
Subject: [PATCH 070/337] req queue interop ^0.6.2 and queue interop ^0.7.4

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 0e6819d..5d603a8 100644
--- a/composer.json
+++ b/composer.json
@@ -7,7 +7,7 @@
     "license": "MIT",
     "require": {
         "php": "^7.1.3",
-        "queue-interop/queue-interop": "^0.6.2@dev|0.7.x-dev",
+        "queue-interop/queue-interop": "^0.6.2@dev",
         "enqueue/null": "0.9.x-dev",
         "ramsey/uuid": "^2|^3.5",
         "psr/log": "^1"

From b99a4aac6f0a74a1e30a94619e2936b1b7af1e6d Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 9 Aug 2018 10:00:21 +0300
Subject: [PATCH 071/337] fix phpstan

---
 Tests/Consumption/QueueConsumerTest.php | 21 ++++-----------------
 1 file changed, 4 insertions(+), 17 deletions(-)

diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 196513d..752da02 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -496,11 +496,7 @@ public function testShouldCallOnIdleExtensionMethod()
             ->expects($this->once())
             ->method('onIdle')
             ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use (
-                $contextStub,
-                $consumerStub,
-                $processorMock
-            ) {
+            ->willReturnCallback(function (Context $context) use ($contextStub) {
                 $this->assertSame($contextStub, $context->getPsrContext());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
                 $this->assertNull($context->getPsrProcessor());
@@ -535,12 +531,7 @@ public function testShouldCallOnBeforeReceiveExtensionMethod()
             ->expects($this->once())
             ->method('onBeforeReceive')
             ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use (
-                $contextStub,
-                $consumerStub,
-                $processorMock,
-                $queue
-            ) {
+            ->willReturnCallback(function (Context $context) use ($contextStub) {
                 $this->assertSame($contextStub, $context->getPsrContext());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
                 $this->assertNull($context->getPsrProcessor());
@@ -711,11 +702,7 @@ public function testShouldAllowInterruptConsumingOnIdle()
             ->expects($this->once())
             ->method('onInterrupted')
             ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use (
-                $contextStub,
-                $consumerStub,
-                $processorMock
-            ) {
+            ->willReturnCallback(function (Context $context) use ($contextStub) {
                 $this->assertSame($contextStub, $context->getPsrContext());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
                 $this->assertNull($context->getPsrConsumer());
@@ -1222,7 +1209,7 @@ public function testShouldCallProcessorAsMessageComeAlong()
             ->expects($this->any())
             ->method('onPreReceived')
             ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use ($processorMock, $queue1, &$actualContexts) {
+            ->willReturnCallback(function (Context $context) use (&$actualContexts) {
                 $actualContexts[] = clone $context;
             })
         ;

From 31c1cdeadc51547cca506d827ac4af10be7102f4 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 13 Aug 2018 16:15:21 +0300
Subject: [PATCH 072/337] Remove support of old Symfony versions.

---
 Symfony/Client/ContainerAwareProcessorRegistry.php | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/Symfony/Client/ContainerAwareProcessorRegistry.php b/Symfony/Client/ContainerAwareProcessorRegistry.php
index b378eb4..2b42cec 100644
--- a/Symfony/Client/ContainerAwareProcessorRegistry.php
+++ b/Symfony/Client/ContainerAwareProcessorRegistry.php
@@ -6,7 +6,6 @@
 use Interop\Queue\PsrProcessor;
 use Symfony\Component\DependencyInjection\ContainerAwareInterface;
 use Symfony\Component\DependencyInjection\ContainerAwareTrait;
-use Symfony\Component\HttpKernel\Kernel;
 
 class ContainerAwareProcessorRegistry implements ProcessorRegistryInterface, ContainerAwareInterface
 {
@@ -39,12 +38,6 @@ public function set($processorName, $serviceId)
      */
     public function get($processorName)
     {
-        if (30300 > Kernel::VERSION_ID) {
-            // Symfony 3.2 and below make service identifiers lowercase, so we do the same.
-            // To be removed when dropping support for Symfony < 3.3.
-            $processorName = strtolower($processorName);
-        }
-
         if (false == isset($this->processors[$processorName])) {
             throw new \LogicException(sprintf('Processor was not found. processorName: "%s"', $processorName));
         }

From 0b7967b86042078eed259d661972a9b4caa7f857 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 14 Aug 2018 15:36:24 +0300
Subject: [PATCH 073/337] [Consumption] Add QueueConsumerInterface, make
 QueueConsumer final.

---
 Consumption/QueueConsumer.php                 | 58 +++++++++----------
 Symfony/Client/ConsumeMessagesCommand.php     | 14 ++---
 .../Consumption/ConsumeMessagesCommand.php    |  8 +--
 .../ContainerAwareConsumeMessagesCommand.php  |  8 +--
 .../QueueConsumerOptionsCommandTrait.php      | 12 ++--
 Tests/Consumption/QueueConsumerTest.php       |  8 +--
 .../Client/ConsumeMessagesCommandTest.php     |  6 +-
 .../ConsumeMessagesCommandTest.php            |  6 +-
 ...ntainerAwareConsumeMessagesCommandTest.php |  6 +-
 .../Mock/QueueConsumerOptionsCommand.php      |  6 +-
 .../QueueConsumerOptionsCommandTraitTest.php  | 14 ++---
 functions.php                                 |  6 +-
 12 files changed, 74 insertions(+), 78 deletions(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 0b82830..8c21625 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -16,7 +16,7 @@
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
-class QueueConsumer
+final class QueueConsumer implements QueueConsumerInterface
 {
     /**
      * @var PsrContext
@@ -71,8 +71,8 @@ class QueueConsumer
     public function __construct(
         PsrContext $psrContext,
         ExtensionInterface $extension = null,
-        $idleTimeout = 0,
-        $receiveTimeout = 10000
+        float $idleTimeout = 0.,
+        float $receiveTimeout = 10000.
     ) {
         $this->psrContext = $psrContext;
         $this->staticExtension = $extension ?: new ChainExtension([]);
@@ -85,62 +85,55 @@ public function __construct(
     }
 
     /**
-     * @param int $timeout
+     * {@inheritdoc}
      */
-    public function setIdleTimeout($timeout)
+    public function setIdleTimeout(float $timeout): void
     {
-        $this->idleTimeout = (int) $timeout;
+        $this->idleTimeout = $timeout;
     }
 
     /**
-     * @return int
+     * {@inheritdoc}
      */
-    public function getIdleTimeout()
+    public function getIdleTimeout(): float
     {
         return $this->idleTimeout;
     }
 
     /**
-     * @param int $timeout
+     * {@inheritdoc}
      */
-    public function setReceiveTimeout($timeout)
+    public function setReceiveTimeout(float $timeout): void
     {
-        $this->receiveTimeout = (int) $timeout;
+        $this->receiveTimeout = $timeout;
     }
 
     /**
-     * @return int
+     * {@inheritdoc}
      */
-    public function getReceiveTimeout()
+    public function getReceiveTimeout(): float
     {
         return $this->receiveTimeout;
     }
 
     /**
-     * @return PsrContext
+     * {@inheritdoc}
      */
-    public function getPsrContext()
+    public function getPsrContext(): PsrContext
     {
         return $this->psrContext;
     }
 
     /**
-     * @param PsrQueue|string       $queue
-     * @param PsrProcessor|callable $processor
-     *
-     * @return QueueConsumer
+     * {@inheritdoc}
      */
-    public function bind($queue, $processor)
+    public function bind($queue, PsrProcessor $processor): QueueConsumerInterface
     {
         if (is_string($queue)) {
             $queue = $this->psrContext->createQueue($queue);
         }
-        if (is_callable($processor)) {
-            $processor = new CallbackProcessor($processor);
-        }
 
         InvalidArgumentException::assertInstanceOf($queue, PsrQueue::class);
-        InvalidArgumentException::assertInstanceOf($processor, PsrProcessor::class);
 
         if (empty($queue->getQueueName())) {
             throw new LogicException('The queue name must be not empty.');
@@ -155,14 +148,17 @@ public function bind($queue, $processor)
     }
 
     /**
-     * Runtime extension - is an extension or a collection of extensions which could be set on runtime.
-     * Here's a good example: @see LimitsExtensionsCommandTrait.
-     *
-     * @param ExtensionInterface|ChainExtension|null $runtimeExtension
-     *
-     * @throws \Exception
+     * {@inheritdoc}
+     */
+    public function bindCallback($queue, callable $processor): QueueConsumerInterface
+    {
+        return $this->bind($queue, new CallbackProcessor($processor));
+    }
+
+    /**
+     * {@inheritdoc}
      */
-    public function consume(ExtensionInterface $runtimeExtension = null)
+    public function consume(ExtensionInterface $runtimeExtension = null): void
     {
         if (empty($this->boundProcessors)) {
             throw new \LogicException('There is nothing to consume. It is required to bind something before calling consume method.');
diff --git a/Symfony/Client/ConsumeMessagesCommand.php b/Symfony/Client/ConsumeMessagesCommand.php
index a3817b3..b41bad6 100644
--- a/Symfony/Client/ConsumeMessagesCommand.php
+++ b/Symfony/Client/ConsumeMessagesCommand.php
@@ -8,7 +8,7 @@
 use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Extension\LoggerExtension;
-use Enqueue\Consumption\QueueConsumer;
+use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Symfony\Consumption\LimitsExtensionsCommandTrait;
 use Enqueue\Symfony\Consumption\QueueConsumerOptionsCommandTrait;
 use Symfony\Component\Console\Command\Command;
@@ -25,7 +25,7 @@ class ConsumeMessagesCommand extends Command
     use QueueConsumerOptionsCommandTrait;
 
     /**
-     * @var QueueConsumer
+     * @var QueueConsumerInterface
      */
     private $consumer;
 
@@ -45,13 +45,13 @@ class ConsumeMessagesCommand extends Command
     private $driver;
 
     /**
-     * @param QueueConsumer     $consumer
-     * @param DelegateProcessor $processor
-     * @param QueueMetaRegistry $queueMetaRegistry
-     * @param DriverInterface   $driver
+     * @param QueueConsumerInterface $consumer
+     * @param DelegateProcessor      $processor
+     * @param QueueMetaRegistry      $queueMetaRegistry
+     * @param DriverInterface        $driver
      */
     public function __construct(
-        QueueConsumer $consumer,
+        QueueConsumerInterface $consumer,
         DelegateProcessor $processor,
         QueueMetaRegistry $queueMetaRegistry,
         DriverInterface $driver
diff --git a/Symfony/Consumption/ConsumeMessagesCommand.php b/Symfony/Consumption/ConsumeMessagesCommand.php
index c2c7695..cbf39c8 100644
--- a/Symfony/Consumption/ConsumeMessagesCommand.php
+++ b/Symfony/Consumption/ConsumeMessagesCommand.php
@@ -4,7 +4,7 @@
 
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Extension\LoggerExtension;
-use Enqueue\Consumption\QueueConsumer;
+use Enqueue\Consumption\QueueConsumerInterface;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Logger\ConsoleLogger;
@@ -19,14 +19,14 @@ class ConsumeMessagesCommand extends Command implements ContainerAwareInterface
     use QueueConsumerOptionsCommandTrait;
 
     /**
-     * @var QueueConsumer
+     * @var QueueConsumerInterface
      */
     protected $consumer;
 
     /**
-     * @param QueueConsumer $consumer
+     * @param QueueConsumerInterface $consumer
      */
-    public function __construct(QueueConsumer $consumer)
+    public function __construct(QueueConsumerInterface $consumer)
     {
         parent::__construct(null);
 
diff --git a/Symfony/Consumption/ContainerAwareConsumeMessagesCommand.php b/Symfony/Consumption/ContainerAwareConsumeMessagesCommand.php
index 297dfa5..61cc41f 100644
--- a/Symfony/Consumption/ContainerAwareConsumeMessagesCommand.php
+++ b/Symfony/Consumption/ContainerAwareConsumeMessagesCommand.php
@@ -4,7 +4,7 @@
 
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Extension\LoggerExtension;
-use Enqueue\Consumption\QueueConsumer;
+use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Consumption\QueueSubscriberInterface;
 use Interop\Queue\PsrProcessor;
 use Symfony\Component\Console\Command\Command;
@@ -23,16 +23,16 @@ class ContainerAwareConsumeMessagesCommand extends Command implements ContainerA
     use QueueConsumerOptionsCommandTrait;
 
     /**
-     * @var QueueConsumer
+     * @var QueueConsumerInterface
      */
     protected $consumer;
 
     /**
      * ConsumeMessagesCommand constructor.
      *
-     * @param QueueConsumer $consumer
+     * @param QueueConsumerInterface $consumer
      */
-    public function __construct(QueueConsumer $consumer)
+    public function __construct(QueueConsumerInterface $consumer)
     {
         parent::__construct(null);
 
diff --git a/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php b/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php
index c6ffd98..00f75f0 100644
--- a/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php
+++ b/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Symfony\Consumption;
 
-use Enqueue\Consumption\QueueConsumer;
+use Enqueue\Consumption\QueueConsumerInterface;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputOption;
 
@@ -20,17 +20,17 @@ protected function configureQueueConsumerOptions()
     }
 
     /**
-     * @param QueueConsumer  $consumer
-     * @param InputInterface $input
+     * @param QueueConsumerInterface $consumer
+     * @param InputInterface         $input
      */
-    protected function setQueueConsumerOptions(QueueConsumer $consumer, InputInterface $input)
+    protected function setQueueConsumerOptions(QueueConsumerInterface $consumer, InputInterface $input)
     {
         if (null !== $idleTimeout = $input->getOption('idle-timeout')) {
-            $consumer->setIdleTimeout((int) $idleTimeout);
+            $consumer->setIdleTimeout((float) $idleTimeout);
         }
 
         if (null !== $receiveTimeout = $input->getOption('receive-timeout')) {
-            $consumer->setReceiveTimeout((int) $receiveTimeout);
+            $consumer->setReceiveTimeout((float) $receiveTimeout);
         }
     }
 }
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 752da02..8c7b47f 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -114,18 +114,18 @@ public function testCouldSetGetIdleTimeout()
     {
         $consumer = new QueueConsumer($this->createPsrContextStub(), null, 0);
 
-        $consumer->setIdleTimeout(123456);
+        $consumer->setIdleTimeout(123456.1);
 
-        $this->assertSame(123456, $consumer->getIdleTimeout());
+        $this->assertSame(123456.1, $consumer->getIdleTimeout());
     }
 
     public function testCouldSetGetReceiveTimeout()
     {
         $consumer = new QueueConsumer($this->createPsrContextStub(), null, 0);
 
-        $consumer->setReceiveTimeout(123456);
+        $consumer->setReceiveTimeout(123456.1);
 
-        $this->assertSame(123456, $consumer->getReceiveTimeout());
+        $this->assertSame(123456.1, $consumer->getReceiveTimeout());
     }
 
     public function testShouldAllowBindCallbackToQueueName()
diff --git a/Tests/Symfony/Client/ConsumeMessagesCommandTest.php b/Tests/Symfony/Client/ConsumeMessagesCommandTest.php
index 396417e..d2a08df 100644
--- a/Tests/Symfony/Client/ConsumeMessagesCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeMessagesCommandTest.php
@@ -7,7 +7,7 @@
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\Consumption\ChainExtension;
-use Enqueue\Consumption\QueueConsumer;
+use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Null\NullQueue;
 use Enqueue\Symfony\Client\ConsumeMessagesCommand;
 use Interop\Queue\PsrContext;
@@ -266,11 +266,11 @@ private function createDelegateProcessorMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumer
+     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumerInterface
      */
     private function createQueueConsumerMock()
     {
-        return $this->createMock(QueueConsumer::class);
+        return $this->createMock(QueueConsumerInterface::class);
     }
 
     /**
diff --git a/Tests/Symfony/Consumption/ConsumeMessagesCommandTest.php b/Tests/Symfony/Consumption/ConsumeMessagesCommandTest.php
index aad0a29..a01fd8f 100644
--- a/Tests/Symfony/Consumption/ConsumeMessagesCommandTest.php
+++ b/Tests/Symfony/Consumption/ConsumeMessagesCommandTest.php
@@ -3,7 +3,7 @@
 namespace Enqueue\Tests\Symfony\Consumption;
 
 use Enqueue\Consumption\ChainExtension;
-use Enqueue\Consumption\QueueConsumer;
+use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Symfony\Consumption\ConsumeMessagesCommand;
 use Interop\Queue\PsrContext;
 use PHPUnit\Framework\TestCase;
@@ -77,10 +77,10 @@ private function createContextMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumer
+     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumerInterface
      */
     private function createQueueConsumerMock()
     {
-        return $this->createMock(QueueConsumer::class);
+        return $this->createMock(QueueConsumerInterface::class);
     }
 }
diff --git a/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php b/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php
index 78929e0..6a587ab 100644
--- a/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php
+++ b/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php
@@ -3,7 +3,7 @@
 namespace Enqueue\Tests\Symfony\Consumption;
 
 use Enqueue\Consumption\ChainExtension;
-use Enqueue\Consumption\QueueConsumer;
+use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Symfony\Consumption\ContainerAwareConsumeMessagesCommand;
 use Enqueue\Tests\Symfony\Consumption\Mock\QueueSubscriberProcessor;
 use Interop\Queue\PsrContext;
@@ -199,10 +199,10 @@ protected function createProcessor()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumer
+     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumerInterface
      */
     protected function createQueueConsumerMock()
     {
-        return $this->createMock(QueueConsumer::class);
+        return $this->createMock(QueueConsumerInterface::class);
     }
 }
diff --git a/Tests/Symfony/Consumption/Mock/QueueConsumerOptionsCommand.php b/Tests/Symfony/Consumption/Mock/QueueConsumerOptionsCommand.php
index 88a0d8c..289a5e7 100644
--- a/Tests/Symfony/Consumption/Mock/QueueConsumerOptionsCommand.php
+++ b/Tests/Symfony/Consumption/Mock/QueueConsumerOptionsCommand.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Tests\Symfony\Consumption\Mock;
 
-use Enqueue\Consumption\QueueConsumer;
+use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Symfony\Consumption\QueueConsumerOptionsCommandTrait;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputInterface;
@@ -13,11 +13,11 @@ class QueueConsumerOptionsCommand extends Command
     use QueueConsumerOptionsCommandTrait;
 
     /**
-     * @var QueueConsumer
+     * @var QueueConsumerInterface
      */
     private $consumer;
 
-    public function __construct(QueueConsumer $consumer)
+    public function __construct(QueueConsumerInterface $consumer)
     {
         parent::__construct('queue-consumer-options');
 
diff --git a/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php b/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php
index 57106aa..f26324c 100644
--- a/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php
+++ b/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Tests\Symfony\Consumption;
 
-use Enqueue\Consumption\QueueConsumer;
+use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Tests\Symfony\Consumption\Mock\QueueConsumerOptionsCommand;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\Console\Tester\CommandTester;
@@ -26,28 +26,28 @@ public function testShouldSetQueueConsumerOptions()
         $consumer
             ->expects($this->once())
             ->method('setIdleTimeout')
-            ->with($this->identicalTo(123))
+            ->with($this->identicalTo(123.1))
         ;
         $consumer
             ->expects($this->once())
             ->method('setReceiveTimeout')
-            ->with($this->identicalTo(456))
+            ->with($this->identicalTo(456.1))
         ;
 
         $trait = new QueueConsumerOptionsCommand($consumer);
 
         $tester = new CommandTester($trait);
         $tester->execute([
-            '--idle-timeout' => '123',
-            '--receive-timeout' => '456',
+            '--idle-timeout' => '123.1',
+            '--receive-timeout' => '456.1',
         ]);
     }
 
     /**
-     * @return QueueConsumer|\PHPUnit_Framework_MockObject_MockObject|QueueConsumer
+     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumerInterface
      */
     private function createQueueConsumer()
     {
-        return $this->createMock(QueueConsumer::class);
+        return $this->createMock(QueueConsumerInterface::class);
     }
 }
diff --git a/functions.php b/functions.php
index 537f996..2fce134 100644
--- a/functions.php
+++ b/functions.php
@@ -169,13 +169,13 @@ function send_queue(PsrContext $c, $queue, $body)
 
 /**
  * @param PsrContext $c
- * @param string     $queue
+ * @param string     $queueName
  * @param callable   $callback
  */
-function consume(PsrContext $c, $queue, callable $callback)
+function consume(PsrContext $c, string $queueName, callable $callback)
 {
     $queueConsumer = new QueueConsumer($c);
-    $queueConsumer->bind($queue, $callback);
+    $queueConsumer->bindCallback($queueName, $callback);
 
     $queueConsumer->consume();
 }

From c74422bf9c89d7261c8798cba05cf2c41b3c5680 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 14 Aug 2018 15:51:23 +0300
Subject: [PATCH 074/337] add queue consumer interface

---
 Consumption/QueueConsumerInterface.php | 47 ++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)
 create mode 100644 Consumption/QueueConsumerInterface.php

diff --git a/Consumption/QueueConsumerInterface.php b/Consumption/QueueConsumerInterface.php
new file mode 100644
index 0000000..0add8ab
--- /dev/null
+++ b/Consumption/QueueConsumerInterface.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Enqueue\Consumption;
+
+use Interop\Queue\PsrContext;
+use Interop\Queue\PsrProcessor;
+use Interop\Queue\PsrQueue;
+
+interface QueueConsumerInterface
+{
+    /**
+     * Milliseconds.
+     */
+    public function setIdleTimeout(float $timeout): void;
+
+    public function getIdleTimeout(): float;
+
+    /**
+     * Milliseconds.
+     */
+    public function setReceiveTimeout(float $timeout): void;
+
+    public function getReceiveTimeout(): float;
+
+    public function getPsrContext(): PsrContext;
+
+    /**
+     * @param string|PsrQueue $queueName
+     */
+    public function bind($queueName, PsrProcessor $processor): self;
+
+    /**
+     * @param string|PsrQueue $queueName
+     * @param mixed           $queue
+     */
+    public function bindCallback($queue, callable $processor): self;
+
+    /**
+     * Runtime extension - is an extension or a collection of extensions which could be set on runtime.
+     * Here's a good example: @see LimitsExtensionsCommandTrait.
+     *
+     * @param ExtensionInterface|ChainExtension|null $runtimeExtension
+     *
+     * @throws \Exception
+     */
+    public function consume(ExtensionInterface $runtimeExtension = null): void;
+}

From eab1b40e6b7369f6d3c184d53a24cb5234feb9dd Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 14 Aug 2018 16:00:52 +0300
Subject: [PATCH 075/337] fix tests.

---
 Tests/Consumption/QueueConsumerTest.php | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 8c7b47f..b5282d7 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -101,15 +101,6 @@ public function testThrowIfQueueNeitherInstanceOfQueueNorString()
         $consumer->bind(new \stdClass(), $processorMock);
     }
 
-    public function testThrowIfProcessorNeitherInstanceOfProcessorNorCallable()
-    {
-        $consumer = new QueueConsumer($this->createPsrContextStub(), null, 0);
-
-        $this->expectException(InvalidArgumentException::class);
-        $this->expectExceptionMessage('The argument must be an instance of Interop\Queue\PsrProcessor but got stdClass.');
-        $consumer->bind(new NullQueue(''), new \stdClass());
-    }
-
     public function testCouldSetGetIdleTimeout()
     {
         $consumer = new QueueConsumer($this->createPsrContextStub(), null, 0);
@@ -146,7 +137,7 @@ public function testShouldAllowBindCallbackToQueueName()
 
         $consumer = new QueueConsumer($context, null, 0);
 
-        $consumer->bind($queueName, $callback);
+        $consumer->bindCallback($queueName, $callback);
 
         $boundProcessors = $this->readAttribute($consumer, 'boundProcessors');
 

From 2968e57df1e2f73cca3277e87495087fa7b3e971 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 14 Aug 2018 16:09:11 +0300
Subject: [PATCH 076/337] [symfony] Add default command name. Make commands
 lazily loaded.

---
 Symfony/Client/ConsumeMessagesCommand.php                    | 5 +++--
 Symfony/Client/Meta/QueuesCommand.php                        | 5 +++--
 Symfony/Client/Meta/TopicsCommand.php                        | 5 +++--
 Symfony/Client/ProduceMessageCommand.php                     | 5 +++--
 Symfony/Client/SetupBrokerCommand.php                        | 5 +++--
 Symfony/Consumption/ConsumeMessagesCommand.php               | 5 +++--
 Symfony/Consumption/ContainerAwareConsumeMessagesCommand.php | 5 +++--
 7 files changed, 21 insertions(+), 14 deletions(-)

diff --git a/Symfony/Client/ConsumeMessagesCommand.php b/Symfony/Client/ConsumeMessagesCommand.php
index a3817b3..c28bfe5 100644
--- a/Symfony/Client/ConsumeMessagesCommand.php
+++ b/Symfony/Client/ConsumeMessagesCommand.php
@@ -24,6 +24,8 @@ class ConsumeMessagesCommand extends Command
     use SetupBrokerExtensionCommandTrait;
     use QueueConsumerOptionsCommandTrait;
 
+    protected static $defaultName = 'enqueue:consume';
+
     /**
      * @var QueueConsumer
      */
@@ -56,7 +58,7 @@ public function __construct(
         QueueMetaRegistry $queueMetaRegistry,
         DriverInterface $driver
     ) {
-        parent::__construct(null);
+        parent::__construct(static::$defaultName);
 
         $this->consumer = $consumer;
         $this->processor = $processor;
@@ -74,7 +76,6 @@ protected function configure()
         $this->configureQueueConsumerOptions();
 
         $this
-            ->setName('enqueue:consume')
             ->setAliases(['enq:c'])
             ->setDescription('A client\'s worker that processes messages. '.
                 'By default it connects to default queue. '.
diff --git a/Symfony/Client/Meta/QueuesCommand.php b/Symfony/Client/Meta/QueuesCommand.php
index 02d263d..12c459d 100644
--- a/Symfony/Client/Meta/QueuesCommand.php
+++ b/Symfony/Client/Meta/QueuesCommand.php
@@ -11,6 +11,8 @@
 
 class QueuesCommand extends Command
 {
+    protected static $defaultName = 'enqueue:queues';
+
     /**
      * @var QueueMetaRegistry
      */
@@ -21,7 +23,7 @@ class QueuesCommand extends Command
      */
     public function __construct(QueueMetaRegistry $queueRegistry)
     {
-        parent::__construct(null);
+        parent::__construct(static::$defaultName);
 
         $this->queueMetaRegistry = $queueRegistry;
     }
@@ -32,7 +34,6 @@ public function __construct(QueueMetaRegistry $queueRegistry)
     protected function configure()
     {
         $this
-            ->setName('enqueue:queues')
             ->setAliases([
                 'enq:m:q',
                 'debug:enqueue:queues',
diff --git a/Symfony/Client/Meta/TopicsCommand.php b/Symfony/Client/Meta/TopicsCommand.php
index 8314fb9..63b28bc 100644
--- a/Symfony/Client/Meta/TopicsCommand.php
+++ b/Symfony/Client/Meta/TopicsCommand.php
@@ -11,6 +11,8 @@
 
 class TopicsCommand extends Command
 {
+    protected static $defaultName = 'enqueue:topics';
+
     /**
      * @var TopicMetaRegistry
      */
@@ -21,7 +23,7 @@ class TopicsCommand extends Command
      */
     public function __construct(TopicMetaRegistry $topicRegistry)
     {
-        parent::__construct(null);
+        parent::__construct(static::$defaultName);
 
         $this->topicRegistry = $topicRegistry;
     }
@@ -32,7 +34,6 @@ public function __construct(TopicMetaRegistry $topicRegistry)
     protected function configure()
     {
         $this
-            ->setName('enqueue:topics')
             ->setAliases([
                 'enq:m:t',
                 'debug:enqueue:topics',
diff --git a/Symfony/Client/ProduceMessageCommand.php b/Symfony/Client/ProduceMessageCommand.php
index a9f9ef9..5e83903 100644
--- a/Symfony/Client/ProduceMessageCommand.php
+++ b/Symfony/Client/ProduceMessageCommand.php
@@ -10,6 +10,8 @@
 
 class ProduceMessageCommand extends Command
 {
+    protected static $defaultName = 'enqueue:produce';
+
     /**
      * @var ProducerInterface
      */
@@ -20,7 +22,7 @@ class ProduceMessageCommand extends Command
      */
     public function __construct(ProducerInterface $producer)
     {
-        parent::__construct(null);
+        parent::__construct(static::$defaultName);
 
         $this->producer = $producer;
     }
@@ -31,7 +33,6 @@ public function __construct(ProducerInterface $producer)
     protected function configure()
     {
         $this
-            ->setName('enqueue:produce')
             ->setAliases(['enq:p'])
             ->setDescription('A command to send a message to topic')
             ->addArgument('topic', InputArgument::REQUIRED, 'A topic to send message to')
diff --git a/Symfony/Client/SetupBrokerCommand.php b/Symfony/Client/SetupBrokerCommand.php
index c825bd2..9bd8ee6 100644
--- a/Symfony/Client/SetupBrokerCommand.php
+++ b/Symfony/Client/SetupBrokerCommand.php
@@ -10,6 +10,8 @@
 
 class SetupBrokerCommand extends Command
 {
+    protected static $defaultName = 'enqueue:setup-broker';
+
     /**
      * @var DriverInterface
      */
@@ -20,7 +22,7 @@ class SetupBrokerCommand extends Command
      */
     public function __construct(DriverInterface $driver)
     {
-        parent::__construct(null);
+        parent::__construct(static::$defaultName);
 
         $this->driver = $driver;
     }
@@ -31,7 +33,6 @@ public function __construct(DriverInterface $driver)
     protected function configure()
     {
         $this
-            ->setName('enqueue:setup-broker')
             ->setAliases(['enq:sb'])
             ->setDescription('Creates all required queues')
         ;
diff --git a/Symfony/Consumption/ConsumeMessagesCommand.php b/Symfony/Consumption/ConsumeMessagesCommand.php
index c2c7695..0f6b54d 100644
--- a/Symfony/Consumption/ConsumeMessagesCommand.php
+++ b/Symfony/Consumption/ConsumeMessagesCommand.php
@@ -18,6 +18,8 @@ class ConsumeMessagesCommand extends Command implements ContainerAwareInterface
     use LimitsExtensionsCommandTrait;
     use QueueConsumerOptionsCommandTrait;
 
+    protected static $defaultName = 'enqueue:transport:consume';
+
     /**
      * @var QueueConsumer
      */
@@ -28,7 +30,7 @@ class ConsumeMessagesCommand extends Command implements ContainerAwareInterface
      */
     public function __construct(QueueConsumer $consumer)
     {
-        parent::__construct(null);
+        parent::__construct(static::$defaultName);
 
         $this->consumer = $consumer;
     }
@@ -42,7 +44,6 @@ protected function configure()
         $this->configureQueueConsumerOptions();
 
         $this
-            ->setName('enqueue:transport:consume')
             ->setDescription('A worker that consumes message from a broker. '.
                 'To use this broker you have to configure queue consumer before adding to the command')
         ;
diff --git a/Symfony/Consumption/ContainerAwareConsumeMessagesCommand.php b/Symfony/Consumption/ContainerAwareConsumeMessagesCommand.php
index 297dfa5..6475ee2 100644
--- a/Symfony/Consumption/ContainerAwareConsumeMessagesCommand.php
+++ b/Symfony/Consumption/ContainerAwareConsumeMessagesCommand.php
@@ -22,6 +22,8 @@ class ContainerAwareConsumeMessagesCommand extends Command implements ContainerA
     use LimitsExtensionsCommandTrait;
     use QueueConsumerOptionsCommandTrait;
 
+    protected static $defaultName = 'enqueue:transport:consume';
+
     /**
      * @var QueueConsumer
      */
@@ -34,7 +36,7 @@ class ContainerAwareConsumeMessagesCommand extends Command implements ContainerA
      */
     public function __construct(QueueConsumer $consumer)
     {
-        parent::__construct(null);
+        parent::__construct(static::$defaultName);
 
         $this->consumer = $consumer;
     }
@@ -48,7 +50,6 @@ protected function configure()
         $this->configureQueueConsumerOptions();
 
         $this
-            ->setName('enqueue:transport:consume')
             ->setDescription('A worker that consumes message from a broker. '.
                 'To use this broker you have to explicitly set a queue to consume from '.
                 'and a message processor service')

From 5668ebb8c0c204710e98740df4cb27d94393b199 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 20 Aug 2018 11:01:01 +0300
Subject: [PATCH 077/337] Better work with DSN provided as env vars, build
 factory in runtime.

---
 Client/DriverFactory.php            |  84 +++++++++++++++
 Client/Resources.php                | 109 +++++++++++++++++++
 ConnectionFactoryFactory.php        |  55 ++++++++++
 Resources.php                       | 153 ++++++++++++++++++++++++++
 Symfony/DefaultTransportFactory.php | 159 ++++++----------------------
 functions.php                       | 123 +--------------------
 6 files changed, 438 insertions(+), 245 deletions(-)
 create mode 100644 Client/DriverFactory.php
 create mode 100644 Client/Resources.php
 create mode 100644 ConnectionFactoryFactory.php
 create mode 100644 Resources.php

diff --git a/Client/DriverFactory.php b/Client/DriverFactory.php
new file mode 100644
index 0000000..f23693f
--- /dev/null
+++ b/Client/DriverFactory.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace Enqueue\Client;
+
+use Enqueue\Client\Amqp\RabbitMqDriver;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Dsn\Dsn;
+use Enqueue\Stomp\Client\ManagementClient;
+use Enqueue\Stomp\Client\RabbitMqStompDriver;
+use Interop\Queue\PsrConnectionFactory;
+
+class DriverFactory
+{
+    /**
+     * @var Config
+     */
+    private $config;
+
+    /**
+     * @var QueueMetaRegistry
+     */
+    private $queueMetaRegistry;
+
+    public function __construct(Config $config, QueueMetaRegistry $queueMetaRegistry)
+    {
+        $this->config = $config;
+        $this->queueMetaRegistry = $queueMetaRegistry;
+    }
+
+    public function create(PsrConnectionFactory $factory, string $dsn, array $config): DriverInterface
+    {
+        $dsn = new Dsn($dsn);
+        $protocol = $dsn->getSchemeProtocol();
+        $extensions = $dsn->getSchemeExtensions();
+
+        $foundDriverClass = null;
+        foreach (Resources::getAvailableDrivers() as $driverClass => $info) {
+            if (false == in_array($protocol, $info['supportedSchemeProtocols'], true)) {
+                continue;
+            }
+
+            if ($info['requiredSchemeExtensions']) {
+                if (empty($extensions)) {
+                    continue;
+                }
+
+                foreach ($info['requiredSchemeExtensions'] as $requiredSchemeExtension) {
+                    if (false == in_array($requiredSchemeExtension, $extensions, true)) {
+                        continue;
+                    }
+                }
+
+                $foundDriverClass = $driverClass;
+
+                break;
+            }
+
+            $foundDriverClass = $driverClass;
+            break;
+        }
+
+        if (false == $foundDriverClass) {
+            throw new \LogicException(sprintf('The driver class that supports scheme %s could not be found', $dsn->getScheme()));
+        }
+
+        if (RabbitMqDriver::class === $foundDriverClass) {
+            return new RabbitMqDriver($factory->createContext(), $this->config, $this->queueMetaRegistry);
+        }
+
+        if (RabbitMqStompDriver::class === $foundDriverClass) {
+            $managementClient = ManagementClient::create(
+                ltrim($dsn->getPath(), '/'),
+                $dsn->getHost(),
+                $config['management_plugin_port'],
+                $dsn->getUser(),
+                $dsn->getPassword()
+            );
+
+            return new RabbitMqStompDriver($factory->createContext(), $this->config, $this->queueMetaRegistry, $managementClient);
+        }
+
+        return new $foundDriverClass($factory->createContext(), $this->config, $this->queueMetaRegistry);
+    }
+}
diff --git a/Client/Resources.php b/Client/Resources.php
new file mode 100644
index 0000000..fd262c0
--- /dev/null
+++ b/Client/Resources.php
@@ -0,0 +1,109 @@
+<?php
+
+namespace Enqueue\Client;
+
+use Enqueue\Client\Amqp\AmqpDriver;
+use Enqueue\Client\Amqp\RabbitMqDriver;
+use Enqueue\Dbal\Client\DbalDriver;
+use Enqueue\Fs\Client\FsDriver;
+use Enqueue\Gps\Client\GpsDriver;
+use Enqueue\Mongodb\Client\MongodbDriver;
+use Enqueue\Null\Client\NullDriver;
+use Enqueue\RdKafka\Client\RdKafkaDriver;
+use Enqueue\Redis\Client\RedisDriver;
+use Enqueue\Sqs\Client\SqsDriver;
+use Enqueue\Stomp\Client\RabbitMqStompDriver;
+use Enqueue\Stomp\Client\StompDriver;
+
+final class Resources
+{
+    /**
+     * [client driver class => [
+     *   schemes => [schemes strings],
+     *   package => package name,
+     * ].
+     *
+     * @var array
+     */
+    private static $knownDrivers = null;
+
+    private function __construct()
+    {
+    }
+
+    public static function getAvailableDrivers(): array
+    {
+        $map = self::getKnownDrivers();
+
+        $availableMap = [];
+        foreach ($map as $driverClass => $item) {
+            if (class_exists($driverClass)) {
+                $availableMap[$driverClass] = $item;
+            }
+        }
+
+        return $availableMap;
+    }
+
+    public static function getKnownDrivers(): array
+    {
+        if (null === self::$knownDrivers) {
+            $map = [];
+
+            $map[AmqpDriver::class] = ['supportedSchemeProtocols' => ['amqp'], 'requiredSchemeExtensions' => []];
+            $map[RabbitMqDriver::class] = ['supportedSchemeProtocols' => ['amqp'], 'requiredSchemeExtensions' => ['rabbitmq']];
+            $map[FsDriver::class] = ['supportedSchemeProtocols' => ['file'], 'requiredSchemeExtensions' => []];
+            $map[NullDriver::class] = ['supportedSchemeProtocols' => ['null'], 'requiredSchemeExtensions' => []];
+            $map[GpsDriver::class] = ['supportedSchemeProtocols' => ['gps'], 'requiredSchemeExtensions' => []];
+            $map[RedisDriver::class] = ['supportedSchemeProtocols' => ['redis'], 'requiredSchemeExtensions' => []];
+            $map[SqsDriver::class] = ['supportedSchemeProtocols' => ['sqs'], 'requiredSchemeExtensions' => []];
+            $map[StompDriver::class] = ['supportedSchemeProtocols' => ['stomp'], 'requiredSchemeExtensions' => []];
+            $map[RabbitMqStompDriver::class] = ['supportedSchemeProtocols' => ['stomp'], 'requiredSchemeExtensions' => ['rabbitmq']];
+            $map[RdKafkaDriver::class] = ['supportedSchemeProtocols' => ['kafka', 'rdkafka'], 'requiredSchemeExtensions' => []];
+            $map[MongodbDriver::class] = ['supportedSchemeProtocols' => ['mongodb'], 'requiredSchemeExtensions' => []];
+            $map[DbalDriver::class] = [
+                'supportedSchemeProtocols' => [
+                    'db2',
+                    'ibm_db2',
+                    'mssql',
+                    'pdo_sqlsrv',
+                    'mysql',
+                    'mysql2',
+                    'pdo_mysql',
+                    'pgsql',
+                    'postgres',
+                    'pdo_pgsql',
+                    'sqlite',
+                    'sqlite3',
+                    'pdo_sqlite',
+                ],
+                'requiredSchemeExtensions' => [],
+            ];
+
+            self::$knownDrivers = $map;
+        }
+
+        return self::$knownDrivers;
+    }
+
+    public static function addDriver(string $driverClass, array $schemes, array $requiredExtensions): void
+    {
+        if (class_exists($driverClass)) {
+            throw new \InvalidArgumentException(sprintf('The driver class "%s" does not exist.', $driverClass));
+        }
+
+        if (false == (new \ReflectionClass($driverClass))->implementsInterface(DriverInterface::class)) {
+            throw new \InvalidArgumentException(sprintf('The driver class "%s" must implement "%s" interface.', $driverClass, DriverInterface::class));
+        }
+
+        if (empty($schemes)) {
+            throw new \InvalidArgumentException('Schemes could not be empty');
+        }
+        if (empty($package)) {
+            throw new \InvalidArgumentException('Package name could not be empty');
+        }
+
+        self::getKnownDrivers();
+        self::$knownDrivers[$driverClass] = ['supportedSchemeProtocols' => $schemes, 'requiredSchemeExtensions' => $requiredExtensions];
+    }
+}
diff --git a/ConnectionFactoryFactory.php b/ConnectionFactoryFactory.php
new file mode 100644
index 0000000..43fcd93
--- /dev/null
+++ b/ConnectionFactoryFactory.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Enqueue;
+
+use Enqueue\Dsn\Dsn;
+use Interop\Queue\PsrConnectionFactory;
+
+class ConnectionFactoryFactory
+{
+    /**
+     * @param string|array $config
+     *
+     * @return PsrConnectionFactory
+     */
+    public function create($config): PsrConnectionFactory
+    {
+        if (is_string($config)) {
+            $config = ['dsn' => $config];
+        }
+
+        if (false == is_array($config)) {
+            throw new \InvalidArgumentException(sprintf('Config must be either string or array. Got %s', gettype($config)));
+        }
+
+        if (false == array_key_exists('dsn', $config)) {
+            throw new \InvalidArgumentException('The config must have dsn field set.');
+        }
+
+        $dsn = new Dsn($config['dsn']);
+
+        $availableSchemes = Resources::getAvailableSchemes();
+
+        if (array_key_exists($dsn->getScheme(), $availableSchemes)) {
+            $factoryClass = $availableSchemes[$dsn->getScheme()];
+
+            return new $factoryClass($config);
+        }
+
+        $knownSchemes = Resources::getKnownSchemes();
+        if (array_key_exists($dsn->getScheme(), $knownSchemes)) {
+            $knownConnections = Resources::getKnownConnections();
+
+            throw new \LogicException(sprintf(
+                'A transport "%s" is not installed. Run "composer req %s" to add it.',
+                $knownSchemes[$dsn->getScheme()],
+                $knownConnections['package']
+            ));
+        }
+
+        throw new \LogicException(sprintf(
+            'A transport is not known. Make sure you registered it with "%s" if it is custom one.',
+            Resources::class
+        ));
+    }
+}
diff --git a/Resources.php b/Resources.php
new file mode 100644
index 0000000..5caaee1
--- /dev/null
+++ b/Resources.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace Enqueue;
+
+use Enqueue\AmqpBunny\AmqpConnectionFactory as AmqpBunnyConnectionFactory;
+use Enqueue\AmqpExt\AmqpConnectionFactory as AmqpExtConnectionFactory;
+use Enqueue\AmqpLib\AmqpConnectionFactory as AmqpLibConnectionFactory;
+use Enqueue\Dbal\DbalConnectionFactory;
+use Enqueue\Fs\FsConnectionFactory;
+use Enqueue\Gearman\GearmanConnectionFactory;
+use Enqueue\Gps\GpsConnectionFactory;
+use Enqueue\Mongodb\MongodbConnectionFactory;
+use Enqueue\Null\NullConnectionFactory;
+use Enqueue\Pheanstalk\PheanstalkConnectionFactory;
+use Enqueue\RdKafka\RdKafkaConnectionFactory;
+use Enqueue\Redis\RedisConnectionFactory;
+use Enqueue\Sqs\SqsConnectionFactory;
+use Enqueue\Stomp\StompConnectionFactory;
+use Interop\Queue\PsrConnectionFactory;
+
+final class Resources
+{
+    /**
+     * [psr connection factory class => [
+     *   schemes => [schemes strings],
+     *   package => package name,
+     * ].
+     *
+     * @var array
+     */
+    private static $knownConnections = null;
+
+    private function __construct()
+    {
+    }
+
+    public static function getAvailableConnections(): array
+    {
+        $map = self::getKnownConnections();
+
+        $availableMap = [];
+        foreach ($map as $connectionClass => $item) {
+            if (class_exists($connectionClass)) {
+                $availableMap[$connectionClass] = $item;
+            }
+        }
+
+        return $availableMap;
+    }
+
+    public static function getKnownSchemes(): array
+    {
+        $map = self::getKnownConnections();
+
+        $schemes = [];
+        foreach ($map as $connectionClass => $item) {
+            foreach ($item['schemes'] as $scheme) {
+                $schemes[$scheme] = $connectionClass;
+            }
+        }
+
+        return $schemes;
+    }
+
+    public static function getAvailableSchemes(): array
+    {
+        $map = self::getAvailableConnections();
+
+        $schemes = [];
+        foreach ($map as $connectionClass => $item) {
+            foreach ($item['schemes'] as $scheme) {
+                $schemes[$scheme] = $connectionClass;
+            }
+        }
+
+        return $schemes;
+    }
+
+    public static function getKnownConnections(): array
+    {
+        if (null === self::$knownConnections) {
+            $map = [];
+
+            $map[FsConnectionFactory::class] = ['schemes' => ['file'], 'package' => 'enqueue/fs'];
+            $map[AmqpExtConnectionFactory::class] = [
+                'schemes' => ['amqp+ext', 'amqp', 'amqps', 'amqps+ext', 'amqp+rabbitmq', 'amqps+rabbitmq', 'amqp+rabbitmq+ext', 'amqps+rabbitmq+ext'],
+                'package' => 'enqueue/amqp-ext',
+            ];
+            $map[AmqpBunnyConnectionFactory::class] = [
+                'schemes' => ['amqp+bunny', 'amqp', 'rabbitmq', 'amqp+rabbitmq', 'amqp+rabbitmq+bunny'],
+                'package' => 'enqueue/amqp-bunny',
+            ];
+            $map[AmqpLibConnectionFactory::class] = [
+                'schemes' => ['amqp+lib', 'amqp', 'amqps', 'amqps+lib', 'amqp+rabbitmq', 'amqps+rabbitmq', 'amqp+rabbitmq+lib', 'amqps+rabbitmq+lib'],
+                'package' => 'enqueue/amqp-lib',
+            ];
+
+            $map[DbalConnectionFactory::class] = [
+                'schemes' => [
+                    'db2',
+                    'ibm_db2',
+                    'mssql',
+                    'pdo_sqlsrv',
+                    'mysql',
+                    'mysql2',
+                    'pdo_mysql',
+                    'pgsql',
+                    'postgres',
+                    'pdo_pgsql',
+                    'sqlite',
+                    'sqlite3',
+                    'pdo_sqlite',
+                ],
+                'package' => 'enqueue/dbal',
+            ];
+
+            $map[NullConnectionFactory::class] = ['schemes' => ['null'], 'package' => 'enqueue/null'];
+            $map[GearmanConnectionFactory::class] = ['schemes' => ['gearman'], 'package' => 'enqueue/gearman'];
+            $map[PheanstalkConnectionFactory::class] = ['schemes' => ['beanstalk'], 'package' => 'enqueue/pheanstalk'];
+            $map[RdKafkaConnectionFactory::class] = ['schemes' => ['kafka', 'rdkafka'], 'package' => 'enqueue/rdkafka'];
+            $map[RedisConnectionFactory::class] = ['schemes' => ['redis'], 'package' => 'enqueue/redis'];
+            $map[StompConnectionFactory::class] = ['schemes' => ['stomp', 'stomp+rabbitmq'], 'package' => 'enqueue/stomp'];
+            $map[SqsConnectionFactory::class] = ['schemes' => ['sqs'], 'package' => 'enqueue/sqs'];
+            $map[GpsConnectionFactory::class] = ['schemes' => ['gps'], 'package' => 'enqueue/gps'];
+            $map[MongodbConnectionFactory::class] = ['schemes' => ['mongodb'], 'package' => 'enqueue/mongodb'];
+
+            self::$knownConnections = $map;
+        }
+
+        return self::$knownConnections;
+    }
+
+    public static function addConnection(string $connectionFactoryClass, array $schemes, string $package): void
+    {
+        if (class_exists($connectionFactoryClass)) {
+            throw new \InvalidArgumentException(sprintf('The connection factory class "%s" does not exist.', $connectionFactoryClass));
+        }
+
+        if (false == (new \ReflectionClass($connectionFactoryClass))->implementsInterface(PsrConnectionFactory::class)) {
+            throw new \InvalidArgumentException(sprintf('The connection factory class "%s" must implement "%s" interface.', $connectionFactoryClass, PsrConnectionFactory::class));
+        }
+
+        if (empty($schemes)) {
+            throw new \InvalidArgumentException('Schemes could not be empty');
+        }
+        if (empty($package)) {
+            throw new \InvalidArgumentException('Package name could not be empty');
+        }
+
+        self::getKnownConnections();
+        self::$knownConnections[$connectionFactoryClass] = ['schemes' => $schemes, 'package' => $package];
+    }
+}
diff --git a/Symfony/DefaultTransportFactory.php b/Symfony/DefaultTransportFactory.php
index fa45d25..e87ff10 100644
--- a/Symfony/DefaultTransportFactory.php
+++ b/Symfony/DefaultTransportFactory.php
@@ -2,29 +2,13 @@
 
 namespace Enqueue\Symfony;
 
-use Enqueue\Dbal\DbalConnectionFactory;
-use Enqueue\Dbal\Symfony\DbalTransportFactory;
-use Enqueue\Fs\FsConnectionFactory;
-use Enqueue\Fs\Symfony\FsTransportFactory;
-use Enqueue\Gps\GpsConnectionFactory;
-use Enqueue\Gps\Symfony\GpsTransportFactory;
-use Enqueue\Mongodb\MongodbConnectionFactory;
-use Enqueue\Mongodb\Symfony\MongodbTransportFactory;
-use Enqueue\Null\NullConnectionFactory;
-use Enqueue\Null\Symfony\NullTransportFactory;
-use Enqueue\RdKafka\RdKafkaConnectionFactory;
-use Enqueue\RdKafka\Symfony\RdKafkaTransportFactory;
-use Enqueue\Redis\RedisConnectionFactory;
-use Enqueue\Redis\Symfony\RedisTransportFactory;
-use Enqueue\Sqs\SqsConnectionFactory;
-use Enqueue\Sqs\Symfony\SqsTransportFactory;
-use Enqueue\Stomp\StompConnectionFactory;
-use Enqueue\Stomp\Symfony\StompTransportFactory;
-use Interop\Amqp\AmqpConnectionFactory;
+use Enqueue\Client\DriverInterface;
+use Interop\Queue\PsrConnectionFactory;
+use Interop\Queue\PsrContext;
 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
 use Symfony\Component\DependencyInjection\Alias;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
-use function Enqueue\dsn_to_connection_factory;
+use Symfony\Component\DependencyInjection\Reference;
 
 class DefaultTransportFactory implements TransportFactoryInterface, DriverFactoryInterface
 {
@@ -58,14 +42,17 @@ public function addConfiguration(ArrayNodeDefinition $builder)
                     }
 
                     if (empty($v)) {
-                        return ['dsn' => 'null:'];
+                        return 'null:';
                     }
 
                     if (is_string($v)) {
                         return false !== strpos($v, ':') || false !== strpos($v, 'env_') ?
-                            ['dsn' => $v] :
-                            ['alias' => $v];
+                            $v :
+                            ['alias' => $v]
+                        ;
                     }
+
+                    throw new \LogicException(sprintf('The value must be array, null or string. Got "%s"', gettype($v)));
                 })
             ->end()
             ->children()
@@ -78,17 +65,18 @@ public function addConfiguration(ArrayNodeDefinition $builder)
 
     public function createConnectionFactory(ContainerBuilder $container, array $config)
     {
+        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
+
         if (isset($config['alias'])) {
             $aliasId = sprintf('enqueue.transport.%s.connection_factory', $config['alias']);
+            $container->setAlias($factoryId, new Alias($aliasId, true));
         } else {
-            $dsn = $this->resolveDSN($container, $config['dsn']);
-
-            $aliasId = $this->findFactory($dsn)->createConnectionFactory($container, $config);
+            $container->register($factoryId, PsrConnectionFactory::class)
+                ->setFactory([new Reference('enqueue.connection_factory_factory'), 'create'])
+                ->addArgument($config)
+            ;
         }
 
-        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
-
-        $container->setAlias($factoryId, new Alias($aliasId, true));
         $container->setAlias('enqueue.transport.connection_factory', new Alias($factoryId, true));
 
         return $factoryId;
@@ -99,17 +87,18 @@ public function createConnectionFactory(ContainerBuilder $container, array $conf
      */
     public function createContext(ContainerBuilder $container, array $config)
     {
+        $contextId = sprintf('enqueue.transport.%s.context', $this->getName());
+        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
+
         if (isset($config['alias'])) {
             $aliasId = sprintf('enqueue.transport.%s.context', $config['alias']);
+            $container->setAlias($contextId, new Alias($aliasId, true));
         } else {
-            $dsn = $this->resolveDSN($container, $config['dsn']);
-
-            $aliasId = $this->findFactory($dsn)->createContext($container, $config);
+            $container->register($contextId, PsrContext::class)
+                ->setFactory([new Reference($factoryId), 'createContext'])
+            ;
         }
 
-        $contextId = sprintf('enqueue.transport.%s.context', $this->getName());
-
-        $container->setAlias($contextId, new Alias($aliasId, true));
         $container->setAlias('enqueue.transport.context', new Alias($contextId, true));
 
         return $contextId;
@@ -120,17 +109,21 @@ public function createContext(ContainerBuilder $container, array $config)
      */
     public function createDriver(ContainerBuilder $container, array $config)
     {
+        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
+        $driverId = sprintf('enqueue.client.%s.driver', $this->getName());
+
         if (isset($config['alias'])) {
             $aliasId = sprintf('enqueue.client.%s.driver', $config['alias']);
+            $container->setAlias($driverId, new Alias($aliasId, true));
         } else {
-            $dsn = $this->resolveDSN($container, $config['dsn']);
-
-            $aliasId = $this->findFactory($dsn)->createDriver($container, $config);
+            $container->register($driverId, DriverInterface::class)
+                ->setFactory([new Reference('enqueue.client.driver_factory'), 'create'])
+                ->addArgument(new Reference($factoryId))
+                ->addArgument(is_string($config) ? $config : $config['dsn'])
+                ->addArgument($config)
+            ;
         }
 
-        $driverId = sprintf('enqueue.client.%s.driver', $this->getName());
-
-        $container->setAlias($driverId, new Alias($aliasId, true));
         $container->setAlias('enqueue.client.driver', new Alias($driverId, true));
 
         return $driverId;
@@ -143,88 +136,4 @@ public function getName()
     {
         return $this->name;
     }
-
-    /**
-     * This is a quick fix to the exception "Incompatible use of dynamic environment variables "ENQUEUE_DSN" found in parameters."
-     * TODO: We'll have to come up with a better solution.
-     *
-     * @param ContainerBuilder $container
-     * @param $dsn
-     *
-     * @return array|false|string
-     */
-    private function resolveDSN(ContainerBuilder $container, $dsn)
-    {
-        if (method_exists($container, 'resolveEnvPlaceholders')) {
-            $dsn = $container->resolveEnvPlaceholders($dsn);
-
-            $matches = [];
-            if (preg_match('/%env\((.*?)\)/', $dsn, $matches)) {
-                if (false === $realDsn = getenv($matches[1])) {
-                    throw new \LogicException(sprintf('The env "%s" var is not defined', $matches[1]));
-                }
-
-                return $realDsn;
-            }
-        }
-
-        return $dsn;
-    }
-
-    /**
-     * @param string
-     * @param mixed $dsn
-     *
-     * @return TransportFactoryInterface
-     */
-    private function findFactory($dsn)
-    {
-        $factory = dsn_to_connection_factory($dsn);
-
-        if ($factory instanceof AmqpConnectionFactory) {
-            return new AmqpTransportFactory('default_amqp');
-        }
-
-        if ($factory instanceof FsConnectionFactory) {
-            return new FsTransportFactory('default_fs');
-        }
-
-        if ($factory instanceof DbalConnectionFactory) {
-            return new DbalTransportFactory('default_dbal');
-        }
-
-        if ($factory instanceof NullConnectionFactory) {
-            return new NullTransportFactory('default_null');
-        }
-
-        if ($factory instanceof GpsConnectionFactory) {
-            return new GpsTransportFactory('default_gps');
-        }
-
-        if ($factory instanceof RedisConnectionFactory) {
-            return new RedisTransportFactory('default_redis');
-        }
-
-        if ($factory instanceof SqsConnectionFactory) {
-            return new SqsTransportFactory('default_sqs');
-        }
-
-        if ($factory instanceof StompConnectionFactory) {
-            return new StompTransportFactory('default_stomp');
-        }
-
-        if ($factory instanceof RdKafkaConnectionFactory) {
-            return new RdKafkaTransportFactory('default_kafka');
-        }
-
-        if ($factory instanceof MongodbConnectionFactory) {
-            return new MongodbTransportFactory('default_mongodb');
-        }
-
-        throw new \LogicException(sprintf(
-            'There is no supported transport factory for the connection factory "%s" created from DSN "%s"',
-            get_class($factory),
-            $dsn
-        ));
-    }
 }
diff --git a/functions.php b/functions.php
index 2fce134..0b4c6f9 100644
--- a/functions.php
+++ b/functions.php
@@ -2,133 +2,16 @@
 
 namespace Enqueue;
 
-use Enqueue\AmqpBunny\AmqpConnectionFactory as AmqpBunnyConnectionFactory;
-use Enqueue\AmqpExt\AmqpConnectionFactory as AmqpExtConnectionFactory;
-use Enqueue\AmqpLib\AmqpConnectionFactory as AmqpLibConnectionFactory;
 use Enqueue\Consumption\QueueConsumer;
-use Enqueue\Dbal\DbalConnectionFactory;
-use Enqueue\Fs\FsConnectionFactory;
-use Enqueue\Gearman\GearmanConnectionFactory;
-use Enqueue\Gps\GpsConnectionFactory;
-use Enqueue\Mongodb\MongodbConnectionFactory;
-use Enqueue\Null\NullConnectionFactory;
-use Enqueue\Pheanstalk\PheanstalkConnectionFactory;
-use Enqueue\RdKafka\RdKafkaConnectionFactory;
-use Enqueue\Redis\RedisConnectionFactory;
-use Enqueue\Sqs\SqsConnectionFactory;
-use Enqueue\Stomp\StompConnectionFactory;
 use Interop\Queue\PsrConnectionFactory;
 use Interop\Queue\PsrContext;
 
 /**
- * @param string $dsn
- *
- * @return PsrConnectionFactory
+ * @param string|array $config
  */
-function dsn_to_connection_factory($dsn)
+function dsn_to_connection_factory($config): PsrConnectionFactory
 {
-    $map = [];
-
-    if (class_exists(FsConnectionFactory::class)) {
-        $map['file'] = FsConnectionFactory::class;
-    }
-
-    if (class_exists(AmqpExtConnectionFactory::class)) {
-        $map['amqp+ext'] = AmqpExtConnectionFactory::class;
-        $map['amqps+ext'] = AmqpExtConnectionFactory::class;
-    }
-    if (class_exists(AmqpLibConnectionFactory::class)) {
-        $map['amqp+lib'] = AmqpLibConnectionFactory::class;
-        $map['amqps+lib'] = AmqpLibConnectionFactory::class;
-    }
-    if (class_exists(AmqpBunnyConnectionFactory::class)) {
-        $map['amqp+bunny'] = AmqpBunnyConnectionFactory::class;
-    }
-
-    if (class_exists(AmqpExtConnectionFactory::class)) {
-        $map['amqp'] = AmqpExtConnectionFactory::class;
-    } elseif (class_exists(AmqpBunnyConnectionFactory::class)) {
-        $map['amqp'] = AmqpBunnyConnectionFactory::class;
-    } elseif (class_exists(AmqpLibConnectionFactory::class)) {
-        $map['amqp'] = AmqpLibConnectionFactory::class;
-    }
-
-    if (class_exists(AmqpExtConnectionFactory::class)) {
-        $map['amqps'] = AmqpExtConnectionFactory::class;
-    } elseif (class_exists(AmqpLibConnectionFactory::class)) {
-        $map['amqps'] = AmqpLibConnectionFactory::class;
-    }
-
-    if (class_exists(NullConnectionFactory::class)) {
-        $map['null'] = NullConnectionFactory::class;
-    }
-
-    if (class_exists(DbalConnectionFactory::class)) {
-        $map['db2'] = DbalConnectionFactory::class;
-        $map['ibm_db2'] = DbalConnectionFactory::class;
-        $map['mssql'] = DbalConnectionFactory::class;
-        $map['pdo_sqlsrv'] = DbalConnectionFactory::class;
-        $map['mysql'] = DbalConnectionFactory::class;
-        $map['mysql2'] = DbalConnectionFactory::class;
-        $map['pdo_mysql'] = DbalConnectionFactory::class;
-        $map['pgsql'] = DbalConnectionFactory::class;
-        $map['postgres'] = DbalConnectionFactory::class;
-        $map['postgresql'] = DbalConnectionFactory::class;
-        $map['pdo_pgsql'] = DbalConnectionFactory::class;
-        $map['sqlite'] = DbalConnectionFactory::class;
-        $map['sqlite3'] = DbalConnectionFactory::class;
-        $map['pdo_sqlite'] = DbalConnectionFactory::class;
-    }
-
-    if (class_exists(GearmanConnectionFactory::class)) {
-        $map['gearman'] = GearmanConnectionFactory::class;
-    }
-
-    if (class_exists(PheanstalkConnectionFactory::class)) {
-        $map['beanstalk'] = PheanstalkConnectionFactory::class;
-    }
-
-    if (class_exists(RdKafkaConnectionFactory::class)) {
-        $map['kafka'] = RdKafkaConnectionFactory::class;
-        $map['rdkafka'] = RdKafkaConnectionFactory::class;
-    }
-
-    if (class_exists(RedisConnectionFactory::class)) {
-        $map['redis'] = RedisConnectionFactory::class;
-    }
-
-    if (class_exists(StompConnectionFactory::class)) {
-        $map['stomp'] = StompConnectionFactory::class;
-    }
-
-    if (class_exists(SqsConnectionFactory::class)) {
-        $map['sqs'] = SqsConnectionFactory::class;
-    }
-
-    if (class_exists(GpsConnectionFactory::class)) {
-        $map['gps'] = GpsConnectionFactory::class;
-    }
-
-    if (class_exists(MongodbConnectionFactory::class)) {
-        $map['mongodb'] = MongodbConnectionFactory::class;
-    }
-
-    list($scheme) = explode(':', $dsn, 2);
-    if (false == $scheme || false === strpos($dsn, ':')) {
-        throw new \LogicException(sprintf('The scheme could not be parsed from DSN "%s"', $dsn));
-    }
-
-    if (false == array_key_exists($scheme, $map)) {
-        throw new \LogicException(sprintf(
-            'The scheme "%s" is not supported. Supported "%s"',
-            $scheme,
-            implode('", "', array_keys($map))
-        ));
-    }
-
-    $factoryClass = $map[$scheme];
-
-    return new $factoryClass($dsn);
+    return (new ConnectionFactoryFactory())->create($config);
 }
 
 /**

From 7387c7f5fa73ff3326545340c17905c67586a73a Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 20 Aug 2018 13:51:56 +0300
Subject: [PATCH 078/337] add tests, remove functions.

---
 ConnectionFactoryFactory.php                  |  65 +++++----
 Resources.php                                 |  81 +++++++++---
 Symfony/AmqpTransportFactory.php              |   4 +-
 Symfony/DefaultTransportFactory.php           |   9 +-
 .../DsnToConnectionFactoryFunctionTest.php    | 104 ---------------
 Tests/Functions/DsnToContextFunctionTest.php  |  73 -----------
 Tests/ResourcesTest.php                       | 124 ++++++++++++++++++
 composer.json                                 |   1 -
 functions.php                                 |  64 ---------
 functions_include.php                         |   6 -
 10 files changed, 231 insertions(+), 300 deletions(-)
 delete mode 100644 Tests/Functions/DsnToConnectionFactoryFunctionTest.php
 delete mode 100644 Tests/Functions/DsnToContextFunctionTest.php
 create mode 100644 Tests/ResourcesTest.php
 delete mode 100644 functions.php
 delete mode 100644 functions_include.php

diff --git a/ConnectionFactoryFactory.php b/ConnectionFactoryFactory.php
index 43fcd93..dd91fd2 100644
--- a/ConnectionFactoryFactory.php
+++ b/ConnectionFactoryFactory.php
@@ -8,48 +8,59 @@
 class ConnectionFactoryFactory
 {
     /**
-     * @param string|array $config
+     * @param string
      *
      * @return PsrConnectionFactory
      */
-    public function create($config): PsrConnectionFactory
+    public function create(string $dsn): PsrConnectionFactory
     {
-        if (is_string($config)) {
-            $config = ['dsn' => $config];
-        }
+        $dsn = new Dsn($dsn);
 
-        if (false == is_array($config)) {
-            throw new \InvalidArgumentException(sprintf('Config must be either string or array. Got %s', gettype($config)));
-        }
+        $availableSchemes = Resources::getAvailableSchemes();
 
-        if (false == array_key_exists('dsn', $config)) {
-            throw new \InvalidArgumentException('The config must have dsn field set.');
-        }
+        if (false == array_key_exists($dsn->getScheme(), $availableSchemes)) {
+            $knownSchemes = Resources::getKnownSchemes();
+            if (array_key_exists($dsn->getScheme(), $knownSchemes)) {
+                $knownConnections = Resources::getKnownConnections();
 
-        $dsn = new Dsn($config['dsn']);
+                throw new \LogicException(sprintf(
+                    'A transport "%s" is not installed. Run "composer req %s" to add it.',
+                    $knownSchemes[$dsn->getScheme()],
+                    $knownConnections['package']
+                ));
+            }
 
-        $availableSchemes = Resources::getAvailableSchemes();
+            throw new \LogicException(sprintf(
+                'A transport is not known. Make sure you registered it with "%s" if it is custom one.',
+                Resources::class
+            ));
+        }
 
-        if (array_key_exists($dsn->getScheme(), $availableSchemes)) {
+        $dsnSchemeExtensions = $dsn->getSchemeExtensions();
+        if (false == $dsnSchemeExtensions) {
             $factoryClass = $availableSchemes[$dsn->getScheme()];
 
-            return new $factoryClass($config);
+            return new $factoryClass((string) $dsn);
         }
 
-        $knownSchemes = Resources::getKnownSchemes();
-        if (array_key_exists($dsn->getScheme(), $knownSchemes)) {
-            $knownConnections = Resources::getKnownConnections();
+        $protocol = $dsn->getSchemeProtocol();
+        foreach ($availableSchemes as $driverClass => $info) {
+            if (false == in_array($protocol, $info['schemes'], true)) {
+                continue;
+            }
 
-            throw new \LogicException(sprintf(
-                'A transport "%s" is not installed. Run "composer req %s" to add it.',
-                $knownSchemes[$dsn->getScheme()],
-                $knownConnections['package']
-            ));
+            if (empty($info['supportedSchemeExtensions'])) {
+                continue;
+            }
+
+            $diff = array_diff($dsnSchemeExtensions, $info['supportedSchemeExtensions']);
+            if (empty($diff)) {
+                $factoryClass = $availableSchemes[$dsn->getScheme()];
+
+                return new $factoryClass((string) $dsn);
+            }
         }
 
-        throw new \LogicException(sprintf(
-            'A transport is not known. Make sure you registered it with "%s" if it is custom one.',
-            Resources::class
-        ));
+        throw new \LogicException(sprintf('There is no factory that supports scheme "%s"', $dsn->getScheme()));
     }
 }
diff --git a/Resources.php b/Resources.php
index 5caaee1..9676cbe 100644
--- a/Resources.php
+++ b/Resources.php
@@ -81,17 +81,24 @@ public static function getKnownConnections(): array
         if (null === self::$knownConnections) {
             $map = [];
 
-            $map[FsConnectionFactory::class] = ['schemes' => ['file'], 'package' => 'enqueue/fs'];
+            $map[FsConnectionFactory::class] = [
+                'schemes' => ['file'],
+                'supportedSchemeExtensions' => [],
+                'package' => 'enqueue/fs',
+            ];
             $map[AmqpExtConnectionFactory::class] = [
-                'schemes' => ['amqp+ext', 'amqp', 'amqps', 'amqps+ext', 'amqp+rabbitmq', 'amqps+rabbitmq', 'amqp+rabbitmq+ext', 'amqps+rabbitmq+ext'],
+                'schemes' => ['amqp', 'amqps'],
+                'supportedSchemeExtensions' => ['ext', 'rabiitmq'],
                 'package' => 'enqueue/amqp-ext',
             ];
             $map[AmqpBunnyConnectionFactory::class] = [
-                'schemes' => ['amqp+bunny', 'amqp', 'rabbitmq', 'amqp+rabbitmq', 'amqp+rabbitmq+bunny'],
+                'schemes' => ['amqp'],
+                'supportedSchemeExtensions' => ['bunny', 'rabiitmq'],
                 'package' => 'enqueue/amqp-bunny',
             ];
             $map[AmqpLibConnectionFactory::class] = [
-                'schemes' => ['amqp+lib', 'amqp', 'amqps', 'amqps+lib', 'amqp+rabbitmq', 'amqps+rabbitmq', 'amqp+rabbitmq+lib', 'amqps+rabbitmq+lib'],
+                'schemes' => ['amqp', 'amqps'],
+                'supportedSchemeExtensions' => ['lib', 'rabiitmq'],
                 'package' => 'enqueue/amqp-lib',
             ];
 
@@ -111,18 +118,52 @@ public static function getKnownConnections(): array
                     'sqlite3',
                     'pdo_sqlite',
                 ],
+                'supportedSchemeExtensions' => [],
                 'package' => 'enqueue/dbal',
             ];
 
-            $map[NullConnectionFactory::class] = ['schemes' => ['null'], 'package' => 'enqueue/null'];
-            $map[GearmanConnectionFactory::class] = ['schemes' => ['gearman'], 'package' => 'enqueue/gearman'];
-            $map[PheanstalkConnectionFactory::class] = ['schemes' => ['beanstalk'], 'package' => 'enqueue/pheanstalk'];
-            $map[RdKafkaConnectionFactory::class] = ['schemes' => ['kafka', 'rdkafka'], 'package' => 'enqueue/rdkafka'];
-            $map[RedisConnectionFactory::class] = ['schemes' => ['redis'], 'package' => 'enqueue/redis'];
-            $map[StompConnectionFactory::class] = ['schemes' => ['stomp', 'stomp+rabbitmq'], 'package' => 'enqueue/stomp'];
-            $map[SqsConnectionFactory::class] = ['schemes' => ['sqs'], 'package' => 'enqueue/sqs'];
-            $map[GpsConnectionFactory::class] = ['schemes' => ['gps'], 'package' => 'enqueue/gps'];
-            $map[MongodbConnectionFactory::class] = ['schemes' => ['mongodb'], 'package' => 'enqueue/mongodb'];
+            $map[NullConnectionFactory::class] = [
+                'schemes' => ['null'],
+                'supportedSchemeExtensions' => [],
+                'package' => 'enqueue/null',
+            ];
+            $map[GearmanConnectionFactory::class] = [
+                'schemes' => ['gearman'],
+                'supportedSchemeExtensions' => [],
+                'package' => 'enqueue/gearman',
+            ];
+            $map[PheanstalkConnectionFactory::class] = [
+                'schemes' => ['beanstalk'],
+                'supportedSchemeExtensions' => ['pheanstalk'],
+                'package' => 'enqueue/pheanstalk',
+            ];
+            $map[RdKafkaConnectionFactory::class] = [
+                'schemes' => ['kafka', 'rdkafka'],
+                'supportedSchemeExtensions' => ['rdkafka'],
+                'package' => 'enqueue/rdkafka',
+            ];
+            $map[RedisConnectionFactory::class] = [
+                'schemes' => ['redis'],
+                'supportedSchemeExtensions' => ['predis', 'phpredis'],
+                'package' => 'enqueue/redis',
+            ];
+            $map[StompConnectionFactory::class] = [
+                'schemes' => ['stomp'],
+                'supportedSchemeExtensions' => ['rabbitmq'],
+                'package' => 'enqueue/stomp', ];
+            $map[SqsConnectionFactory::class] = [
+                'schemes' => ['sqs'],
+                'supportedSchemeExtensions' => [],
+                'package' => 'enqueue/sqs', ];
+            $map[GpsConnectionFactory::class] = [
+                'schemes' => ['gps'],
+                'supportedSchemeExtensions' => [],
+                'package' => 'enqueue/gps', ];
+            $map[MongodbConnectionFactory::class] = [
+                'schemes' => ['mongodb'],
+                'supportedSchemeExtensions' => [],
+                'package' => 'enqueue/mongodb',
+            ];
 
             self::$knownConnections = $map;
         }
@@ -130,9 +171,9 @@ public static function getKnownConnections(): array
         return self::$knownConnections;
     }
 
-    public static function addConnection(string $connectionFactoryClass, array $schemes, string $package): void
+    public static function addConnection(string $connectionFactoryClass, array $schemes, array $extensions, string $package): void
     {
-        if (class_exists($connectionFactoryClass)) {
+        if (false == class_exists($connectionFactoryClass)) {
             throw new \InvalidArgumentException(sprintf('The connection factory class "%s" does not exist.', $connectionFactoryClass));
         }
 
@@ -141,13 +182,17 @@ public static function addConnection(string $connectionFactoryClass, array $sche
         }
 
         if (empty($schemes)) {
-            throw new \InvalidArgumentException('Schemes could not be empty');
+            throw new \InvalidArgumentException('Schemes could not be empty.');
         }
         if (empty($package)) {
-            throw new \InvalidArgumentException('Package name could not be empty');
+            throw new \InvalidArgumentException('Package name could not be empty.');
         }
 
         self::getKnownConnections();
-        self::$knownConnections[$connectionFactoryClass] = ['schemes' => $schemes, 'package' => $package];
+        self::$knownConnections[$connectionFactoryClass] = [
+            'schemes' => $schemes,
+            'supportedSchemeExtensions' => $extensions,
+            'package' => $package,
+        ];
     }
 }
diff --git a/Symfony/AmqpTransportFactory.php b/Symfony/AmqpTransportFactory.php
index e69bf44..cf6f343 100644
--- a/Symfony/AmqpTransportFactory.php
+++ b/Symfony/AmqpTransportFactory.php
@@ -6,13 +6,13 @@
 use Enqueue\AmqpExt\AmqpConnectionFactory as AmqpExtConnectionFactory;
 use Enqueue\AmqpLib\AmqpConnectionFactory as AmqpLibConnectionFactory;
 use Enqueue\Client\Amqp\AmqpDriver;
+use Enqueue\ConnectionFactoryFactory;
 use Interop\Amqp\AmqpConnectionFactory;
 use Interop\Amqp\AmqpContext;
 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Definition;
 use Symfony\Component\DependencyInjection\Reference;
-use function Enqueue\dsn_to_connection_factory;
 
 class AmqpTransportFactory implements TransportFactoryInterface, DriverFactoryInterface
 {
@@ -221,7 +221,7 @@ public static function createConnectionFactoryFactory(array $config)
         }
 
         $dsn = array_key_exists('dsn', $config) ? $config['dsn'] : 'amqp:';
-        $factory = dsn_to_connection_factory($dsn);
+        $factory = (new ConnectionFactoryFactory())->create($dsn);
 
         if (false == $factory instanceof  AmqpConnectionFactory) {
             throw new \LogicException(sprintf('Factory must be instance of "%s" but got "%s"', AmqpConnectionFactory::class, get_class($factory)));
diff --git a/Symfony/DefaultTransportFactory.php b/Symfony/DefaultTransportFactory.php
index e87ff10..a65f2bf 100644
--- a/Symfony/DefaultTransportFactory.php
+++ b/Symfony/DefaultTransportFactory.php
@@ -42,12 +42,12 @@ public function addConfiguration(ArrayNodeDefinition $builder)
                     }
 
                     if (empty($v)) {
-                        return 'null:';
+                        return ['dsn' => 'null:'];
                     }
 
                     if (is_string($v)) {
                         return false !== strpos($v, ':') || false !== strpos($v, 'env_') ?
-                            $v :
+                            ['dsn' => $v] :
                             ['alias' => $v]
                         ;
                     }
@@ -66,14 +66,13 @@ public function addConfiguration(ArrayNodeDefinition $builder)
     public function createConnectionFactory(ContainerBuilder $container, array $config)
     {
         $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
-
         if (isset($config['alias'])) {
             $aliasId = sprintf('enqueue.transport.%s.connection_factory', $config['alias']);
             $container->setAlias($factoryId, new Alias($aliasId, true));
         } else {
             $container->register($factoryId, PsrConnectionFactory::class)
                 ->setFactory([new Reference('enqueue.connection_factory_factory'), 'create'])
-                ->addArgument($config)
+                ->addArgument($config['dsn'])
             ;
         }
 
@@ -119,7 +118,7 @@ public function createDriver(ContainerBuilder $container, array $config)
             $container->register($driverId, DriverInterface::class)
                 ->setFactory([new Reference('enqueue.client.driver_factory'), 'create'])
                 ->addArgument(new Reference($factoryId))
-                ->addArgument(is_string($config) ? $config : $config['dsn'])
+                ->addArgument($config['dsn'])
                 ->addArgument($config)
             ;
         }
diff --git a/Tests/Functions/DsnToConnectionFactoryFunctionTest.php b/Tests/Functions/DsnToConnectionFactoryFunctionTest.php
deleted file mode 100644
index 2c65eb6..0000000
--- a/Tests/Functions/DsnToConnectionFactoryFunctionTest.php
+++ /dev/null
@@ -1,104 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Functions;
-
-use Enqueue\AmqpBunny\AmqpConnectionFactory as AmqpBunnyConnectionFactory;
-use Enqueue\AmqpExt\AmqpConnectionFactory as AmqpExtConnectionFactory;
-use Enqueue\AmqpLib\AmqpConnectionFactory as AmqpLibConnectionFactory;
-use Enqueue\Dbal\DbalConnectionFactory;
-use Enqueue\Fs\FsConnectionFactory;
-use Enqueue\Gearman\GearmanConnectionFactory;
-use Enqueue\Gps\GpsConnectionFactory;
-use Enqueue\Mongodb\MongodbConnectionFactory;
-use Enqueue\Null\NullConnectionFactory;
-use Enqueue\Pheanstalk\PheanstalkConnectionFactory;
-use Enqueue\RdKafka\RdKafkaConnectionFactory;
-use Enqueue\Redis\RedisConnectionFactory;
-use Enqueue\Sqs\SqsConnectionFactory;
-use Enqueue\Stomp\StompConnectionFactory;
-use PHPUnit\Framework\TestCase;
-
-class DsnToConnectionFactoryFunctionTest extends TestCase
-{
-    public function testThrowIfDsnEmpty()
-    {
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The scheme could not be parsed from DSN ""');
-
-        \Enqueue\dsn_to_connection_factory('');
-    }
-
-    public function testThrowIfDsnMissingScheme()
-    {
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The scheme could not be parsed from DSN "dsnMissingScheme"');
-
-        \Enqueue\dsn_to_connection_factory('dsnMissingScheme');
-    }
-
-    public function testThrowIfDsnNotSupported()
-    {
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The scheme "http" is not supported. Supported "file", "amqp+ext"');
-
-        \Enqueue\dsn_to_connection_factory('http://schemeNotSupported');
-    }
-
-    /**
-     * @dataProvider provideDSNs
-     *
-     * @param mixed $dsn
-     * @param mixed $expectedFactoryClass
-     */
-    public function testReturnsExpectedFactoryInstance($dsn, $expectedFactoryClass)
-    {
-        $factory = \Enqueue\dsn_to_connection_factory($dsn);
-
-        $this->assertInstanceOf($expectedFactoryClass, $factory);
-    }
-
-    public static function provideDSNs()
-    {
-        yield ['amqp:', AmqpExtConnectionFactory::class];
-
-        yield ['amqps:', AmqpExtConnectionFactory::class];
-
-        yield ['amqp+ext:', AmqpExtConnectionFactory::class];
-
-        yield ['amqps+ext:', AmqpExtConnectionFactory::class];
-
-        yield ['amqp+lib:', AmqpLibConnectionFactory::class];
-
-        yield ['amqps+lib:', AmqpLibConnectionFactory::class];
-
-        yield ['amqp+bunny:', AmqpBunnyConnectionFactory::class];
-
-        yield ['amqp://user:pass@foo/vhost', AmqpExtConnectionFactory::class];
-
-        yield ['file:', FsConnectionFactory::class];
-
-        yield ['file:///foo/bar/baz', FsConnectionFactory::class];
-
-        yield ['null:', NullConnectionFactory::class];
-
-        yield ['mysql:', DbalConnectionFactory::class];
-
-        yield ['pgsql:', DbalConnectionFactory::class];
-
-        yield ['beanstalk:', PheanstalkConnectionFactory::class];
-
-//        yield ['gearman:', GearmanConnectionFactory::class];
-
-        yield ['kafka:', RdKafkaConnectionFactory::class];
-
-        yield ['redis:', RedisConnectionFactory::class];
-
-        yield ['stomp:', StompConnectionFactory::class];
-
-        yield ['sqs:', SqsConnectionFactory::class];
-
-        yield ['gps:', GpsConnectionFactory::class];
-
-        yield ['mongodb:', MongodbConnectionFactory::class];
-    }
-}
diff --git a/Tests/Functions/DsnToContextFunctionTest.php b/Tests/Functions/DsnToContextFunctionTest.php
deleted file mode 100644
index 2a8bc83..0000000
--- a/Tests/Functions/DsnToContextFunctionTest.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Functions;
-
-use Enqueue\AmqpExt\AmqpContext;
-use Enqueue\Fs\FsContext;
-use Enqueue\Gps\GpsContext;
-use Enqueue\Null\NullContext;
-use Enqueue\Redis\RedisContext;
-use Enqueue\Sqs\SqsContext;
-use Enqueue\Stomp\StompContext;
-use PHPUnit\Framework\TestCase;
-
-class DsnToContextFunctionTest extends TestCase
-{
-    public function testThrowIfDsnEmpty()
-    {
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The scheme could not be parsed from DSN ""');
-
-        \Enqueue\dsn_to_context('');
-    }
-
-    public function testThrowIfDsnMissingScheme()
-    {
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The scheme could not be parsed from DSN "dsnMissingScheme"');
-
-        \Enqueue\dsn_to_context('dsnMissingScheme');
-    }
-
-    public function testThrowIfDsnNotSupported()
-    {
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The scheme "http" is not supported. Supported "file", "amqp+ext"');
-
-        \Enqueue\dsn_to_context('http://schemeNotSupported');
-    }
-
-    /**
-     * @dataProvider provideDSNs
-     *
-     * @param mixed $dsn
-     * @param mixed $expectedFactoryClass
-     */
-    public function testReturnsExpectedFactoryInstance($dsn, $expectedFactoryClass)
-    {
-        $factory = \Enqueue\dsn_to_context($dsn);
-
-        $this->assertInstanceOf($expectedFactoryClass, $factory);
-    }
-
-    public static function provideDSNs()
-    {
-        yield ['amqp:', AmqpContext::class];
-
-        yield ['amqp://user:pass@foo/vhost', AmqpContext::class];
-
-        yield ['file:', FsContext::class];
-
-        yield ['file://'.sys_get_temp_dir(), FsContext::class];
-
-        yield ['null:', NullContext::class];
-
-        yield ['redis:', RedisContext::class];
-
-        yield ['stomp:', StompContext::class];
-
-        yield ['sqs:', SqsContext::class];
-
-        yield ['gps:', GpsContext::class];
-    }
-}
diff --git a/Tests/ResourcesTest.php b/Tests/ResourcesTest.php
new file mode 100644
index 0000000..fabd28a
--- /dev/null
+++ b/Tests/ResourcesTest.php
@@ -0,0 +1,124 @@
+<?php
+
+namespace Enqueue\Tests;
+
+use Enqueue\Redis\RedisConnectionFactory;
+use Enqueue\Resources;
+use Interop\Queue\PsrConnectionFactory;
+use PHPUnit\Framework\TestCase;
+
+class ResourcesTest extends TestCase
+{
+    public function testShouldBeFinal()
+    {
+        $rc = new \ReflectionClass(Resources::class);
+
+        $this->assertTrue($rc->isFinal());
+    }
+
+    public function testShouldConstructorBePrivate()
+    {
+        $rc = new \ReflectionClass(Resources::class);
+
+        $this->assertTrue($rc->getConstructor()->isPrivate());
+    }
+
+    public function testShouldGetAvailableConnectionsInExpectedFormat()
+    {
+        $availableConnections = Resources::getAvailableConnections();
+
+        $this->assertInternalType('array', $availableConnections);
+        $this->assertArrayHasKey(RedisConnectionFactory::class, $availableConnections);
+
+        $connectionInfo = $availableConnections[RedisConnectionFactory::class];
+        $this->assertArrayHasKey('schemes', $connectionInfo);
+        $this->assertSame(['redis'], $connectionInfo['schemes']);
+
+        $this->assertArrayHasKey('supportedSchemeExtensions', $connectionInfo);
+        $this->assertSame(['predis', 'phpredis'], $connectionInfo['supportedSchemeExtensions']);
+
+        $this->assertArrayHasKey('package', $connectionInfo);
+        $this->assertSame('enqueue/redis', $connectionInfo['package']);
+    }
+
+    public function testShouldGetKnownConnectionsInExpectedFormat()
+    {
+        $availableConnections = Resources::getKnownConnections();
+
+        $this->assertInternalType('array', $availableConnections);
+        $this->assertArrayHasKey(RedisConnectionFactory::class, $availableConnections);
+
+        $connectionInfo = $availableConnections[RedisConnectionFactory::class];
+        $this->assertArrayHasKey('schemes', $connectionInfo);
+        $this->assertSame(['redis'], $connectionInfo['schemes']);
+
+        $this->assertArrayHasKey('supportedSchemeExtensions', $connectionInfo);
+        $this->assertSame(['predis', 'phpredis'], $connectionInfo['supportedSchemeExtensions']);
+
+        $this->assertArrayHasKey('package', $connectionInfo);
+        $this->assertSame('enqueue/redis', $connectionInfo['package']);
+    }
+
+    public function testThrowsIfConnectionClassNotExistsOnAddConnection()
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The connection factory class "classNotExist" does not exist.');
+
+        Resources::addConnection('classNotExist', [], [], 'foo');
+    }
+
+    public function testThrowsIfConnectionClassNotImplementConnectionFactoryInterfaceOnAddConnection()
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The connection factory class "stdClass" must implement "Interop\Queue\PsrConnectionFactory" interface.');
+
+        Resources::addConnection(\stdClass::class, [], [], 'foo');
+    }
+
+    public function testThrowsIfNoSchemesProvidedOnAddConnection()
+    {
+        $connectionClass = $this->getMockClass(PsrConnectionFactory::class);
+
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Schemes could not be empty.');
+
+        Resources::addConnection($connectionClass, [], [], 'foo');
+    }
+
+    public function testThrowsIfNoPackageProvidedOnAddConnection()
+    {
+        $connectionClass = $this->getMockClass(PsrConnectionFactory::class);
+
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Package name could not be empty.');
+
+        Resources::addConnection($connectionClass, ['foo'], [], '');
+    }
+
+    public function testShouldAllowGetPreviouslyRegisteredConnection()
+    {
+        $connectionClass = $this->getMockClass(PsrConnectionFactory::class);
+
+        Resources::addConnection(
+            $connectionClass,
+            ['fooscheme', 'barscheme'],
+            ['fooextension', 'barextension'],
+            'foo/bar'
+        );
+
+        $availableConnections = Resources::getKnownConnections();
+
+        $this->assertInternalType('array', $availableConnections);
+        $this->assertArrayHasKey($connectionClass, $availableConnections);
+
+        $connectionInfo = $availableConnections[$connectionClass];
+        $this->assertArrayHasKey('schemes', $connectionInfo);
+        $this->assertSame(['fooscheme', 'barscheme'], $connectionInfo['schemes']);
+
+        $this->assertArrayHasKey('supportedSchemeExtensions', $connectionInfo);
+        $this->assertSame(['fooextension', 'barextension'], $connectionInfo['supportedSchemeExtensions']);
+
+        $this->assertArrayHasKey('package', $connectionInfo);
+        $this->assertSame('foo/bar', $connectionInfo['package']);
+    }
+}
diff --git a/composer.json b/composer.json
index 5d603a8..e61270c 100644
--- a/composer.json
+++ b/composer.json
@@ -56,7 +56,6 @@
     },
     "autoload": {
         "psr-4": { "Enqueue\\": "" },
-        "files": ["functions_include.php"],
         "exclude-from-classmap": [
             "/Tests/"
         ]
diff --git a/functions.php b/functions.php
deleted file mode 100644
index 0b4c6f9..0000000
--- a/functions.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-
-namespace Enqueue;
-
-use Enqueue\Consumption\QueueConsumer;
-use Interop\Queue\PsrConnectionFactory;
-use Interop\Queue\PsrContext;
-
-/**
- * @param string|array $config
- */
-function dsn_to_connection_factory($config): PsrConnectionFactory
-{
-    return (new ConnectionFactoryFactory())->create($config);
-}
-
-/**
- * @param string $dsn
- *
- * @return PsrContext
- */
-function dsn_to_context($dsn)
-{
-    return dsn_to_connection_factory($dsn)->createContext();
-}
-
-/**
- * @param PsrContext $c
- * @param string     $topic
- * @param string     $body
- */
-function send_topic(PsrContext $c, $topic, $body)
-{
-    $topic = $c->createTopic($topic);
-    $message = $c->createMessage($body);
-
-    $c->createProducer()->send($topic, $message);
-}
-
-/**
- * @param PsrContext $c
- * @param string     $queue
- * @param string     $body
- */
-function send_queue(PsrContext $c, $queue, $body)
-{
-    $queue = $c->createQueue($queue);
-    $message = $c->createMessage($body);
-
-    $c->createProducer()->send($queue, $message);
-}
-
-/**
- * @param PsrContext $c
- * @param string     $queueName
- * @param callable   $callback
- */
-function consume(PsrContext $c, string $queueName, callable $callback)
-{
-    $queueConsumer = new QueueConsumer($c);
-    $queueConsumer->bindCallback($queueName, $callback);
-
-    $queueConsumer->consume();
-}
diff --git a/functions_include.php b/functions_include.php
deleted file mode 100644
index cf5502a..0000000
--- a/functions_include.php
+++ /dev/null
@@ -1,6 +0,0 @@
-<?php
-
-// Don't redefine the functions if included multiple times.
-if (false == function_exists('Enqueue\dsn_to_connection_factory')) {
-    require __DIR__.'/functions.php';
-}

From 419aefda7aac0b166e1f48958fbecae4d095392e Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 20 Aug 2018 16:01:05 +0300
Subject: [PATCH 079/337] fix&add tests.

---
 ConnectionFactoryFactory.php                  |  58 ++++----
 ConnectionFactoryFactoryInterface.php         |  10 ++
 Resources.php                                 |  27 ++--
 Tests/Client/ResourcesTest.php                |  23 +++
 Tests/ConnectionFactoryFactoryTest.php        | 132 ++++++++++++++++++
 Tests/ResourcesTest.php                       |  24 ++--
 Tests/Symfony/DefaultTransportFactoryTest.php | 120 +++++++---------
 7 files changed, 265 insertions(+), 129 deletions(-)
 create mode 100644 ConnectionFactoryFactoryInterface.php
 create mode 100644 Tests/Client/ResourcesTest.php
 create mode 100644 Tests/ConnectionFactoryFactoryTest.php

diff --git a/ConnectionFactoryFactory.php b/ConnectionFactoryFactory.php
index dd91fd2..1dcf3ff 100644
--- a/ConnectionFactoryFactory.php
+++ b/ConnectionFactoryFactory.php
@@ -5,62 +5,54 @@
 use Enqueue\Dsn\Dsn;
 use Interop\Queue\PsrConnectionFactory;
 
-class ConnectionFactoryFactory
+final class ConnectionFactoryFactory implements ConnectionFactoryFactoryInterface
 {
-    /**
-     * @param string
-     *
-     * @return PsrConnectionFactory
-     */
     public function create(string $dsn): PsrConnectionFactory
     {
         $dsn = new Dsn($dsn);
 
-        $availableSchemes = Resources::getAvailableSchemes();
-
-        if (false == array_key_exists($dsn->getScheme(), $availableSchemes)) {
-            $knownSchemes = Resources::getKnownSchemes();
-            if (array_key_exists($dsn->getScheme(), $knownSchemes)) {
-                $knownConnections = Resources::getKnownConnections();
-
-                throw new \LogicException(sprintf(
-                    'A transport "%s" is not installed. Run "composer req %s" to add it.',
-                    $knownSchemes[$dsn->getScheme()],
-                    $knownConnections['package']
-                ));
-            }
+        if ($factoryClass = $this->findFactory($dsn, Resources::getAvailableConnections())) {
+            return new $factoryClass((string) $dsn);
+        }
 
+        $knownConnections = Resources::getKnownConnections();
+        if ($factoryClass = $this->findFactory($dsn, $knownConnections)) {
             throw new \LogicException(sprintf(
-                'A transport is not known. Make sure you registered it with "%s" if it is custom one.',
-                Resources::class
+                'To use given scheme "%s" a package has to be installed. Run "composer req %s" to add it.',
+                $dsn->getScheme(),
+                $knownConnections[$factoryClass]['package']
             ));
         }
 
-        $dsnSchemeExtensions = $dsn->getSchemeExtensions();
-        if (false == $dsnSchemeExtensions) {
-            $factoryClass = $availableSchemes[$dsn->getScheme()];
-
-            return new $factoryClass((string) $dsn);
-        }
+        throw new \LogicException(sprintf(
+            'A given scheme "%s" is not supported. Maybe it is a custom connection, make sure you registered it with "%s::addConnection".',
+            $dsn->getScheme(),
+            Resources::class
+        ));
+    }
 
+    private function findFactory(Dsn $dsn, array $factories): ?string
+    {
         $protocol = $dsn->getSchemeProtocol();
-        foreach ($availableSchemes as $driverClass => $info) {
+        foreach ($factories as $connectionClass => $info) {
             if (false == in_array($protocol, $info['schemes'], true)) {
                 continue;
             }
 
+            if (false == $dsn->getSchemeExtensions()) {
+                return $connectionClass;
+            }
+
             if (empty($info['supportedSchemeExtensions'])) {
                 continue;
             }
 
-            $diff = array_diff($dsnSchemeExtensions, $info['supportedSchemeExtensions']);
+            $diff = array_diff($dsn->getSchemeExtensions(), $info['supportedSchemeExtensions']);
             if (empty($diff)) {
-                $factoryClass = $availableSchemes[$dsn->getScheme()];
-
-                return new $factoryClass((string) $dsn);
+                return $connectionClass;
             }
         }
 
-        throw new \LogicException(sprintf('There is no factory that supports scheme "%s"', $dsn->getScheme()));
+        return null;
     }
 }
diff --git a/ConnectionFactoryFactoryInterface.php b/ConnectionFactoryFactoryInterface.php
new file mode 100644
index 0000000..4c74a5e
--- /dev/null
+++ b/ConnectionFactoryFactoryInterface.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace Enqueue;
+
+use Interop\Queue\PsrConnectionFactory;
+
+interface ConnectionFactoryFactoryInterface
+{
+    public function create(string $dsn): PsrConnectionFactory;
+}
diff --git a/Resources.php b/Resources.php
index 9676cbe..897ec7f 100644
--- a/Resources.php
+++ b/Resources.php
@@ -88,37 +88,36 @@ public static function getKnownConnections(): array
             ];
             $map[AmqpExtConnectionFactory::class] = [
                 'schemes' => ['amqp', 'amqps'],
-                'supportedSchemeExtensions' => ['ext', 'rabiitmq'],
+                'supportedSchemeExtensions' => ['ext', 'rabbitmq'],
                 'package' => 'enqueue/amqp-ext',
             ];
             $map[AmqpBunnyConnectionFactory::class] = [
                 'schemes' => ['amqp'],
-                'supportedSchemeExtensions' => ['bunny', 'rabiitmq'],
+                'supportedSchemeExtensions' => ['bunny', 'rabbitmq'],
                 'package' => 'enqueue/amqp-bunny',
             ];
             $map[AmqpLibConnectionFactory::class] = [
                 'schemes' => ['amqp', 'amqps'],
-                'supportedSchemeExtensions' => ['lib', 'rabiitmq'],
+                'supportedSchemeExtensions' => ['lib', 'rabbitmq'],
                 'package' => 'enqueue/amqp-lib',
             ];
 
             $map[DbalConnectionFactory::class] = [
                 'schemes' => [
                     'db2',
-                    'ibm_db2',
+                    'ibm-db2',
                     'mssql',
-                    'pdo_sqlsrv',
+                    'sqlsrv',
                     'mysql',
                     'mysql2',
-                    'pdo_mysql',
+                    'mysql',
                     'pgsql',
                     'postgres',
-                    'pdo_pgsql',
                     'sqlite',
                     'sqlite3',
-                    'pdo_sqlite',
+                    'sqlite',
                 ],
-                'supportedSchemeExtensions' => [],
+                'supportedSchemeExtensions' => ['pdo'],
                 'package' => 'enqueue/dbal',
             ];
 
@@ -173,12 +172,10 @@ public static function getKnownConnections(): array
 
     public static function addConnection(string $connectionFactoryClass, array $schemes, array $extensions, string $package): void
     {
-        if (false == class_exists($connectionFactoryClass)) {
-            throw new \InvalidArgumentException(sprintf('The connection factory class "%s" does not exist.', $connectionFactoryClass));
-        }
-
-        if (false == (new \ReflectionClass($connectionFactoryClass))->implementsInterface(PsrConnectionFactory::class)) {
-            throw new \InvalidArgumentException(sprintf('The connection factory class "%s" must implement "%s" interface.', $connectionFactoryClass, PsrConnectionFactory::class));
+        if (class_exists($connectionFactoryClass)) {
+            if (false == (new \ReflectionClass($connectionFactoryClass))->implementsInterface(PsrConnectionFactory::class)) {
+                throw new \InvalidArgumentException(sprintf('The connection factory class "%s" must implement "%s" interface.', $connectionFactoryClass, PsrConnectionFactory::class));
+            }
         }
 
         if (empty($schemes)) {
diff --git a/Tests/Client/ResourcesTest.php b/Tests/Client/ResourcesTest.php
new file mode 100644
index 0000000..53fe8fe
--- /dev/null
+++ b/Tests/Client/ResourcesTest.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Enqueue\Tests\Client;
+
+use Enqueue\Client\Resources;
+use PHPUnit\Framework\TestCase;
+
+class ResourcesTest extends TestCase
+{
+    public function testShouldBeFinal()
+    {
+        $rc = new \ReflectionClass(Resources::class);
+
+        $this->assertTrue($rc->isFinal());
+    }
+
+    public function testShouldConstructorBePrivate()
+    {
+        $rc = new \ReflectionClass(Resources::class);
+
+        $this->assertTrue($rc->getConstructor()->isPrivate());
+    }
+}
diff --git a/Tests/ConnectionFactoryFactoryTest.php b/Tests/ConnectionFactoryFactoryTest.php
new file mode 100644
index 0000000..588376f
--- /dev/null
+++ b/Tests/ConnectionFactoryFactoryTest.php
@@ -0,0 +1,132 @@
+<?php
+
+namespace Enqueue\Tests;
+
+use Enqueue\AmqpBunny\AmqpConnectionFactory as AmqpBunnyConnectionFactory;
+use Enqueue\AmqpExt\AmqpConnectionFactory as AmqpExtConnectionFactory;
+use Enqueue\AmqpLib\AmqpConnectionFactory as AmqpLibConnectionFactory;
+use Enqueue\ConnectionFactoryFactory;
+use Enqueue\ConnectionFactoryFactoryInterface;
+use Enqueue\Dbal\DbalConnectionFactory;
+use Enqueue\Fs\FsConnectionFactory;
+use Enqueue\Gearman\GearmanConnectionFactory;
+use Enqueue\Gps\GpsConnectionFactory;
+use Enqueue\Mongodb\MongodbConnectionFactory;
+use Enqueue\Null\NullConnectionFactory;
+use Enqueue\Pheanstalk\PheanstalkConnectionFactory;
+use Enqueue\RdKafka\RdKafkaConnectionFactory;
+use Enqueue\Redis\RedisConnectionFactory;
+use Enqueue\Resources;
+use Enqueue\Sqs\SqsConnectionFactory;
+use Enqueue\Stomp\StompConnectionFactory;
+use PHPUnit\Framework\TestCase;
+
+class ConnectionFactoryFactoryTest extends TestCase
+{
+    public function testShouldImplementConnectionFactoryFactoryInterface()
+    {
+        $rc = new \ReflectionClass(ConnectionFactoryFactory::class);
+
+        $this->assertTrue($rc->implementsInterface(ConnectionFactoryFactoryInterface::class));
+    }
+
+    public function testShouldBeFinal()
+    {
+        $rc = new \ReflectionClass(ConnectionFactoryFactory::class);
+
+        $this->assertTrue($rc->isFinal());
+    }
+
+    public function testCouldBeConstructedWithoutAnyArguments()
+    {
+        new ConnectionFactoryFactory();
+    }
+
+    public function testThrowIfPackageThatSupportSchemeNotInstalled()
+    {
+        $scheme = 'scheme5b7aa7d7cd213';
+        $class = 'ConnectionClass5b7aa7d7cd213';
+
+        Resources::addConnection($class, [$scheme], [], 'thePackage');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('To use given scheme "scheme5b7aa7d7cd213" a package has to be installed. Run "composer req thePackage" to add it.');
+        (new ConnectionFactoryFactory())->create($scheme.'://foo');
+    }
+
+    public function testThrowIfSchemeIsNotKnown()
+    {
+        $scheme = 'scheme5b7aa862e70a5';
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('A given scheme "scheme5b7aa862e70a5" is not supported. Maybe it is a custom connection, make sure you registered it with "Enqueue\Resources::addConnection".');
+        (new ConnectionFactoryFactory())->create($scheme.'://foo');
+    }
+
+    public function testThrowIfDsnInvalid()
+    {
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The DSN is invalid. It does not have scheme separator ":".');
+
+        (new ConnectionFactoryFactory())->create('invalid-scheme');
+    }
+
+    /**
+     * @dataProvider provideDSN
+     */
+    public function testReturnsExpectedFactories(string $dsn, string $expectedFactoryClass)
+    {
+        $connectionFactory = (new ConnectionFactoryFactory())->create($dsn);
+
+        $this->assertInstanceOf($expectedFactoryClass, $connectionFactory);
+    }
+
+    public static function provideDSN()
+    {
+        yield ['null:', NullConnectionFactory::class];
+
+        yield ['amqp:', AmqpExtConnectionFactory::class];
+
+        yield ['amqp+ext:', AmqpExtConnectionFactory::class];
+
+//        yield ['amqp+rabbitmq:', AmqpExtConnectionFactory::class];
+
+//        yield ['amqp+rabbitmq+ext:', AmqpExtConnectionFactory::class];
+
+        yield ['amqps:', AmqpExtConnectionFactory::class];
+
+        yield ['amqps+ext:', AmqpExtConnectionFactory::class];
+
+//        yield ['amqps+rabbitmq:', AmqpExtConnectionFactory::class];
+
+//        yield ['amqps+ext+rabbitmq:', AmqpExtConnectionFactory::class];
+
+        yield ['amqp+bunny:', AmqpBunnyConnectionFactory::class];
+
+        yield ['amqp+lib:', AmqpLibConnectionFactory::class];
+
+        yield ['mssql:', DbalConnectionFactory::class];
+
+        yield ['mysql:', DbalConnectionFactory::class];
+
+        yield ['pgsql:', DbalConnectionFactory::class];
+
+        yield ['file:', FsConnectionFactory::class];
+
+        yield ['gearman:', GearmanConnectionFactory::class];
+
+        yield ['gps:', GpsConnectionFactory::class];
+
+        yield ['mongodb:', MongodbConnectionFactory::class];
+
+        yield ['beanstalk:', PheanstalkConnectionFactory::class];
+
+        yield ['kafka:', RdKafkaConnectionFactory::class];
+
+        yield ['redis:', RedisConnectionFactory::class];
+
+        yield ['sqs:', SqsConnectionFactory::class];
+
+        yield ['stomp:', StompConnectionFactory::class];
+    }
+}
diff --git a/Tests/ResourcesTest.php b/Tests/ResourcesTest.php
index fabd28a..0131b4c 100644
--- a/Tests/ResourcesTest.php
+++ b/Tests/ResourcesTest.php
@@ -59,14 +59,6 @@ public function testShouldGetKnownConnectionsInExpectedFormat()
         $this->assertSame('enqueue/redis', $connectionInfo['package']);
     }
 
-    public function testThrowsIfConnectionClassNotExistsOnAddConnection()
-    {
-        $this->expectException(\InvalidArgumentException::class);
-        $this->expectExceptionMessage('The connection factory class "classNotExist" does not exist.');
-
-        Resources::addConnection('classNotExist', [], [], 'foo');
-    }
-
     public function testThrowsIfConnectionClassNotImplementConnectionFactoryInterfaceOnAddConnection()
     {
         $this->expectException(\InvalidArgumentException::class);
@@ -95,6 +87,20 @@ public function testThrowsIfNoPackageProvidedOnAddConnection()
         Resources::addConnection($connectionClass, ['foo'], [], '');
     }
 
+    public function testShouldAllowRegisterConnectionThatIsNotInstalled()
+    {
+        Resources::addConnection('theConnectionClass', ['foo'], [], 'foo');
+
+        $knownConnections = Resources::getKnownConnections();
+        $this->assertInternalType('array', $knownConnections);
+        $this->assertArrayHasKey('theConnectionClass', $knownConnections);
+
+        $availableConnections = Resources::getAvailableConnections();
+
+        $this->assertInternalType('array', $availableConnections);
+        $this->assertArrayNotHasKey('theConnectionClass', $availableConnections);
+    }
+
     public function testShouldAllowGetPreviouslyRegisteredConnection()
     {
         $connectionClass = $this->getMockClass(PsrConnectionFactory::class);
@@ -106,7 +112,7 @@ public function testShouldAllowGetPreviouslyRegisteredConnection()
             'foo/bar'
         );
 
-        $availableConnections = Resources::getKnownConnections();
+        $availableConnections = Resources::getAvailableConnections();
 
         $this->assertInternalType('array', $availableConnections);
         $this->assertArrayHasKey($connectionClass, $availableConnections);
diff --git a/Tests/Symfony/DefaultTransportFactoryTest.php b/Tests/Symfony/DefaultTransportFactoryTest.php
index a97fb86..5b480d8 100644
--- a/Tests/Symfony/DefaultTransportFactoryTest.php
+++ b/Tests/Symfony/DefaultTransportFactoryTest.php
@@ -9,6 +9,7 @@
 use Symfony\Component\Config\Definition\Builder\TreeBuilder;
 use Symfony\Component\Config\Definition\Processor;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
 
 class DefaultTransportFactoryTest extends TestCase
 {
@@ -181,27 +182,27 @@ public function testShouldCreateDriverFromAlias()
         $this->assertEquals($driverId, (string) $context);
     }
 
-    /**
-     * @dataProvider provideDSNs
-     *
-     * @param mixed $dsn
-     * @param mixed $expectedName
-     */
-    public function testShouldCreateConnectionFactoryFromDSN($dsn, $expectedName)
+    public function testShouldCreateConnectionFactoryFromDSN()
     {
         $container = new ContainerBuilder();
 
         $transport = new DefaultTransportFactory();
 
-        $serviceId = $transport->createConnectionFactory($container, ['dsn' => $dsn]);
+        $serviceId = $transport->createConnectionFactory($container, ['dsn' => 'foo://bar/baz']);
 
         $this->assertEquals('enqueue.transport.default.connection_factory', $serviceId);
 
-        $this->assertTrue($container->hasAlias('enqueue.transport.default.connection_factory'));
+        $this->assertTrue($container->hasDefinition('enqueue.transport.default.connection_factory'));
+
+        $this->assertNotEmpty($container->getDefinition('enqueue.transport.default.connection_factory')->getFactory());
         $this->assertEquals(
-            sprintf('enqueue.transport.%s.connection_factory', $expectedName),
-            (string) $container->getAlias('enqueue.transport.default.connection_factory')
-        );
+            [new Reference('enqueue.connection_factory_factory'), 'create'],
+            $container->getDefinition('enqueue.transport.default.connection_factory')->getFactory())
+        ;
+        $this->assertSame(
+            ['foo://bar/baz'],
+            $container->getDefinition('enqueue.transport.default.connection_factory')->getArguments())
+        ;
 
         $this->assertTrue($container->hasAlias('enqueue.transport.connection_factory'));
         $this->assertEquals(
@@ -210,88 +211,63 @@ public function testShouldCreateConnectionFactoryFromDSN($dsn, $expectedName)
         );
     }
 
-    /**
-     * @dataProvider provideDSNs
-     *
-     * @param mixed $dsn
-     * @param mixed $expectedName
-     */
-    public function testShouldCreateContextFromDsn($dsn, $expectedName)
+    public function testShouldCreateContextFromDsn()
     {
         $container = new ContainerBuilder();
 
         $transport = new DefaultTransportFactory();
 
-        $serviceId = $transport->createContext($container, ['dsn' => $dsn]);
+        $serviceId = $transport->createContext($container, ['dsn' => 'foo://bar/baz']);
 
         $this->assertEquals('enqueue.transport.default.context', $serviceId);
 
-        $this->assertTrue($container->hasAlias($serviceId));
-        $context = $container->getAlias($serviceId);
+        $this->assertNotEmpty($container->getDefinition('enqueue.transport.default.context')->getFactory());
         $this->assertEquals(
-            sprintf('enqueue.transport.%s.context', $expectedName),
-            (string) $context
-        );
+            [new Reference('enqueue.transport.default.connection_factory'), 'createContext'],
+            $container->getDefinition('enqueue.transport.default.context')->getFactory())
+        ;
+        $this->assertSame(
+            [],
+            $container->getDefinition('enqueue.transport.default.context')->getArguments())
+        ;
 
         $this->assertTrue($container->hasAlias('enqueue.transport.context'));
-        $context = $container->getAlias('enqueue.transport.context');
-        $this->assertEquals($serviceId, (string) $context);
+        $this->assertEquals(
+            'enqueue.transport.default.context',
+            (string) $container->getAlias('enqueue.transport.context')
+        );
     }
 
-    /**
-     * @dataProvider provideDSNs
-     *
-     * @param mixed $dsn
-     * @param mixed $expectedName
-     */
-    public function testShouldCreateDriverFromDsn($dsn, $expectedName)
+    public function testShouldCreateDriverFromDsn()
     {
         $container = new ContainerBuilder();
 
         $transport = new DefaultTransportFactory();
 
-        $driverId = $transport->createDriver($container, ['dsn' => $dsn]);
+        $serviceId = $transport->createDriver($container, ['dsn' => 'foo://bar/baz', 'foo' => 'fooVal']);
 
-        $this->assertEquals('enqueue.client.default.driver', $driverId);
+        $this->assertEquals('enqueue.client.default.driver', $serviceId);
 
-        $this->assertTrue($container->hasAlias($driverId));
-        $context = $container->getAlias($driverId);
+        $this->assertTrue($container->hasDefinition('enqueue.client.default.driver'));
+
+        $this->assertNotEmpty($container->getDefinition('enqueue.client.default.driver')->getFactory());
         $this->assertEquals(
-            sprintf('enqueue.client.%s.driver', $expectedName),
-            (string) $context
-        );
+            [new Reference('enqueue.client.driver_factory'), 'create'],
+            $container->getDefinition('enqueue.client.default.driver')->getFactory())
+        ;
+        $this->assertEquals(
+            [
+                new Reference('enqueue.transport.default.connection_factory'),
+                'foo://bar/baz',
+                ['dsn' => 'foo://bar/baz', 'foo' => 'fooVal'],
+            ],
+            $container->getDefinition('enqueue.client.default.driver')->getArguments())
+        ;
 
         $this->assertTrue($container->hasAlias('enqueue.client.driver'));
-        $context = $container->getAlias('enqueue.client.driver');
-        $this->assertEquals($driverId, (string) $context);
-    }
-
-    public static function provideDSNs()
-    {
-        yield ['amqp+ext:', 'default_amqp'];
-
-        yield ['amqp+lib:', 'default_amqp'];
-
-        yield ['amqp+bunny:', 'default_amqp'];
-
-        yield ['null:', 'default_null'];
-
-        yield ['file:', 'default_fs'];
-
-        yield ['mysql:', 'default_dbal'];
-
-        yield ['pgsql:', 'default_dbal'];
-
-        yield ['gps:', 'default_gps'];
-
-        yield ['sqs:', 'default_sqs'];
-
-        yield ['redis:', 'default_redis'];
-
-        yield ['stomp:', 'default_stomp'];
-
-        yield ['kafka:', 'default_kafka'];
-
-        yield ['mongodb:', 'default_mongodb'];
+        $this->assertEquals(
+            'enqueue.client.default.driver',
+            (string) $container->getAlias('enqueue.client.driver')
+        );
     }
 }

From b51b578fee2ccc50dfdd4b9f233009409dc5bfff Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 20 Aug 2018 16:19:12 +0300
Subject: [PATCH 080/337] add tests

---
 Client/Resources.php           |  89 ++++++++++++++++++------
 Tests/Client/ResourcesTest.php | 123 +++++++++++++++++++++++++++++++++
 2 files changed, 191 insertions(+), 21 deletions(-)

diff --git a/Client/Resources.php b/Client/Resources.php
index fd262c0..33ee458 100644
--- a/Client/Resources.php
+++ b/Client/Resources.php
@@ -50,19 +50,63 @@ public static function getKnownDrivers(): array
         if (null === self::$knownDrivers) {
             $map = [];
 
-            $map[AmqpDriver::class] = ['supportedSchemeProtocols' => ['amqp'], 'requiredSchemeExtensions' => []];
-            $map[RabbitMqDriver::class] = ['supportedSchemeProtocols' => ['amqp'], 'requiredSchemeExtensions' => ['rabbitmq']];
-            $map[FsDriver::class] = ['supportedSchemeProtocols' => ['file'], 'requiredSchemeExtensions' => []];
-            $map[NullDriver::class] = ['supportedSchemeProtocols' => ['null'], 'requiredSchemeExtensions' => []];
-            $map[GpsDriver::class] = ['supportedSchemeProtocols' => ['gps'], 'requiredSchemeExtensions' => []];
-            $map[RedisDriver::class] = ['supportedSchemeProtocols' => ['redis'], 'requiredSchemeExtensions' => []];
-            $map[SqsDriver::class] = ['supportedSchemeProtocols' => ['sqs'], 'requiredSchemeExtensions' => []];
-            $map[StompDriver::class] = ['supportedSchemeProtocols' => ['stomp'], 'requiredSchemeExtensions' => []];
-            $map[RabbitMqStompDriver::class] = ['supportedSchemeProtocols' => ['stomp'], 'requiredSchemeExtensions' => ['rabbitmq']];
-            $map[RdKafkaDriver::class] = ['supportedSchemeProtocols' => ['kafka', 'rdkafka'], 'requiredSchemeExtensions' => []];
-            $map[MongodbDriver::class] = ['supportedSchemeProtocols' => ['mongodb'], 'requiredSchemeExtensions' => []];
+            $map[AmqpDriver::class] = [
+                'schemes' => ['amqp'],
+                'requiredSchemeExtensions' => [],
+                'package' => 'enqueue/enqueue',
+            ];
+            $map[RabbitMqDriver::class] = [
+                'schemes' => ['amqp'],
+                'requiredSchemeExtensions' => ['rabbitmq'],
+                'package' => 'enqueue/enqueue',
+            ];
+            $map[FsDriver::class] = [
+                'schemes' => ['file'],
+                'requiredSchemeExtensions' => [],
+                'package' => 'enqueue/fs',
+            ];
+            $map[NullDriver::class] = [
+                'schemes' => ['null'],
+                'requiredSchemeExtensions' => [],
+                'package' => 'enqueue/null',
+            ];
+            $map[GpsDriver::class] = [
+                'schemes' => ['gps'],
+                'requiredSchemeExtensions' => [],
+                'package' => 'enqueue/gps',
+            ];
+            $map[RedisDriver::class] = [
+                'schemes' => ['redis'],
+                'requiredSchemeExtensions' => [],
+                'package' => 'enqueue/redis',
+            ];
+            $map[SqsDriver::class] = [
+                'schemes' => ['sqs'],
+                'requiredSchemeExtensions' => [],
+                'package' => 'enqueue/sqs',
+            ];
+            $map[StompDriver::class] = [
+                'schemes' => ['stomp'],
+                'requiredSchemeExtensions' => [],
+                'package' => 'enqueue/stomp',
+            ];
+            $map[RabbitMqStompDriver::class] = [
+                'schemes' => ['stomp'],
+                'requiredSchemeExtensions' => ['rabbitmq'],
+                'package' => 'enqueue/stomp',
+            ];
+            $map[RdKafkaDriver::class] = [
+                'schemes' => ['kafka', 'rdkafka'],
+                'requiredSchemeExtensions' => [],
+                'package' => 'enqueue/rdkafka',
+            ];
+            $map[MongodbDriver::class] = [
+                'schemes' => ['mongodb'],
+                'requiredSchemeExtensions' => [],
+                'package' => 'enqueue/mongodb',
+            ];
             $map[DbalDriver::class] = [
-                'supportedSchemeProtocols' => [
+                'schemes' => [
                     'db2',
                     'ibm_db2',
                     'mssql',
@@ -78,6 +122,7 @@ public static function getKnownDrivers(): array
                     'pdo_sqlite',
                 ],
                 'requiredSchemeExtensions' => [],
+                'package' => 'enqueue/dbal',
             ];
 
             self::$knownDrivers = $map;
@@ -86,24 +131,26 @@ public static function getKnownDrivers(): array
         return self::$knownDrivers;
     }
 
-    public static function addDriver(string $driverClass, array $schemes, array $requiredExtensions): void
+    public static function addDriver(string $driverClass, array $schemes, array $requiredExtensions, string $package): void
     {
         if (class_exists($driverClass)) {
-            throw new \InvalidArgumentException(sprintf('The driver class "%s" does not exist.', $driverClass));
-        }
-
-        if (false == (new \ReflectionClass($driverClass))->implementsInterface(DriverInterface::class)) {
-            throw new \InvalidArgumentException(sprintf('The driver class "%s" must implement "%s" interface.', $driverClass, DriverInterface::class));
+            if (false == (new \ReflectionClass($driverClass))->implementsInterface(DriverInterface::class)) {
+                throw new \InvalidArgumentException(sprintf('The driver class "%s" must implement "%s" interface.', $driverClass, DriverInterface::class));
+            }
         }
 
         if (empty($schemes)) {
-            throw new \InvalidArgumentException('Schemes could not be empty');
+            throw new \InvalidArgumentException('Schemes could not be empty.');
         }
         if (empty($package)) {
-            throw new \InvalidArgumentException('Package name could not be empty');
+            throw new \InvalidArgumentException('Package name could not be empty.');
         }
 
         self::getKnownDrivers();
-        self::$knownDrivers[$driverClass] = ['supportedSchemeProtocols' => $schemes, 'requiredSchemeExtensions' => $requiredExtensions];
+        self::$knownDrivers[$driverClass] = [
+            'schemes' => $schemes,
+            'requiredSchemeExtensions' => $requiredExtensions,
+            'package' => $package,
+        ];
     }
 }
diff --git a/Tests/Client/ResourcesTest.php b/Tests/Client/ResourcesTest.php
index 53fe8fe..55ff951 100644
--- a/Tests/Client/ResourcesTest.php
+++ b/Tests/Client/ResourcesTest.php
@@ -2,7 +2,10 @@
 
 namespace Enqueue\Tests\Client;
 
+use Enqueue\Client\Amqp\RabbitMqDriver;
+use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Resources;
+use Enqueue\Redis\Client\RedisDriver;
 use PHPUnit\Framework\TestCase;
 
 class ResourcesTest extends TestCase
@@ -20,4 +23,124 @@ public function testShouldConstructorBePrivate()
 
         $this->assertTrue($rc->getConstructor()->isPrivate());
     }
+
+    public function testShouldGetAvailableDriverInExpectedFormat()
+    {
+        $availableDrivers = Resources::getAvailableDrivers();
+
+        $this->assertInternalType('array', $availableDrivers);
+        $this->assertArrayHasKey(RedisDriver::class, $availableDrivers);
+
+        $driverInfo = $availableDrivers[RedisDriver::class];
+        $this->assertArrayHasKey('schemes', $driverInfo);
+        $this->assertSame(['redis'], $driverInfo['schemes']);
+
+        $this->assertArrayHasKey('requiredSchemeExtensions', $driverInfo);
+        $this->assertSame([], $driverInfo['requiredSchemeExtensions']);
+
+        $this->assertArrayHasKey('package', $driverInfo);
+        $this->assertSame('enqueue/redis', $driverInfo['package']);
+    }
+
+    public function testShouldGetAvailableDriverWithRequiredExtensionInExpectedFormat()
+    {
+        $availableDrivers = Resources::getAvailableDrivers();
+
+        $this->assertInternalType('array', $availableDrivers);
+        $this->assertArrayHasKey(RabbitMqDriver::class, $availableDrivers);
+
+        $driverInfo = $availableDrivers[RabbitMqDriver::class];
+        $this->assertArrayHasKey('schemes', $driverInfo);
+        $this->assertSame(['amqp'], $driverInfo['schemes']);
+
+        $this->assertArrayHasKey('requiredSchemeExtensions', $driverInfo);
+        $this->assertSame(['rabbitmq'], $driverInfo['requiredSchemeExtensions']);
+
+        $this->assertArrayHasKey('package', $driverInfo);
+        $this->assertSame('enqueue/enqueue', $driverInfo['package']);
+    }
+
+    public function testShouldGetKnownDriversInExpectedFormat()
+    {
+        $knownDrivers = Resources::getKnownDrivers();
+
+        $this->assertInternalType('array', $knownDrivers);
+        $this->assertArrayHasKey(RedisDriver::class, $knownDrivers);
+
+        $driverInfo = $knownDrivers[RedisDriver::class];
+        $this->assertArrayHasKey('schemes', $driverInfo);
+        $this->assertSame(['redis'], $driverInfo['schemes']);
+
+        $this->assertArrayHasKey('requiredSchemeExtensions', $driverInfo);
+        $this->assertSame([], $driverInfo['requiredSchemeExtensions']);
+    }
+
+    public function testThrowsIfDriverClassNotImplementDriverFactoryInterfaceOnAddDriver()
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The driver class "stdClass" must implement "Enqueue\Client\DriverInterface" interface.');
+
+        Resources::addDriver(\stdClass::class, [], [], 'foo');
+    }
+
+    public function testThrowsIfNoSchemesProvidedOnAddDriver()
+    {
+        $driverClass = $this->getMockClass(DriverInterface::class);
+
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Schemes could not be empty.');
+
+        Resources::addDriver($driverClass, [], [], 'foo');
+    }
+
+    public function testThrowsIfNoPackageProvidedOnAddDriver()
+    {
+        $driverClass = $this->getMockClass(DriverInterface::class);
+
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Package name could not be empty.');
+
+        Resources::addDriver($driverClass, ['foo'], [], '');
+    }
+
+    public function testShouldAllowRegisterDriverThatIsNotInstalled()
+    {
+        Resources::addDriver('theDriverClass', ['foo'], ['barExtension'], 'foo');
+
+        $knownDrivers = Resources::getKnownDrivers();
+        $this->assertInternalType('array', $knownDrivers);
+        $this->assertArrayHasKey('theDriverClass', $knownDrivers);
+
+        $availableDrivers = Resources::getAvailableDrivers();
+
+        $this->assertInternalType('array', $availableDrivers);
+        $this->assertArrayNotHasKey('theDriverClass', $availableDrivers);
+    }
+
+    public function testShouldAllowGetPreviouslyRegisteredDriver()
+    {
+        $driverClass = $this->getMockClass(DriverInterface::class);
+
+        Resources::addDriver(
+            $driverClass,
+            ['fooscheme', 'barscheme'],
+            ['fooextension', 'barextension'],
+            'foo/bar'
+        );
+
+        $availableDrivers = Resources::getAvailableDrivers();
+
+        $this->assertInternalType('array', $availableDrivers);
+        $this->assertArrayHasKey($driverClass, $availableDrivers);
+
+        $driverInfo = $availableDrivers[$driverClass];
+        $this->assertArrayHasKey('schemes', $driverInfo);
+        $this->assertSame(['fooscheme', 'barscheme'], $driverInfo['schemes']);
+
+        $this->assertArrayHasKey('requiredSchemeExtensions', $driverInfo);
+        $this->assertSame(['fooextension', 'barextension'], $driverInfo['requiredSchemeExtensions']);
+
+        $this->assertArrayHasKey('package', $driverInfo);
+        $this->assertSame('foo/bar', $driverInfo['package']);
+    }
 }

From 0fb1637054a0af9fccc7d9266a95072c2985a237 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 20 Aug 2018 20:28:26 +0300
Subject: [PATCH 081/337] Add driver factorty tests

---
 Client/DriverFactory.php               | 108 ++++++++++-----
 Client/DriverFactoryInterface.php      |  10 ++
 Client/Resources.php                   |  12 +-
 ConnectionFactoryFactory.php           |   6 +-
 Tests/Client/DriverFactoryTest.php     | 179 +++++++++++++++++++++++++
 Tests/ConnectionFactoryFactoryTest.php |   3 +-
 6 files changed, 274 insertions(+), 44 deletions(-)
 create mode 100644 Client/DriverFactoryInterface.php
 create mode 100644 Tests/Client/DriverFactoryTest.php

diff --git a/Client/DriverFactory.php b/Client/DriverFactory.php
index f23693f..a82ec93 100644
--- a/Client/DriverFactory.php
+++ b/Client/DriverFactory.php
@@ -7,9 +7,11 @@
 use Enqueue\Dsn\Dsn;
 use Enqueue\Stomp\Client\ManagementClient;
 use Enqueue\Stomp\Client\RabbitMqStompDriver;
+use Enqueue\Stomp\StompConnectionFactory;
+use Interop\Amqp\AmqpConnectionFactory;
 use Interop\Queue\PsrConnectionFactory;
 
-class DriverFactory
+final class DriverFactory implements DriverFactoryInterface
 {
     /**
      * @var Config
@@ -30,55 +32,93 @@ public function __construct(Config $config, QueueMetaRegistry $queueMetaRegistry
     public function create(PsrConnectionFactory $factory, string $dsn, array $config): DriverInterface
     {
         $dsn = new Dsn($dsn);
-        $protocol = $dsn->getSchemeProtocol();
-        $extensions = $dsn->getSchemeExtensions();
 
-        $foundDriverClass = null;
-        foreach (Resources::getAvailableDrivers() as $driverClass => $info) {
-            if (false == in_array($protocol, $info['supportedSchemeProtocols'], true)) {
-                continue;
+        if ($driverClass = $this->findDriverClass($dsn, Resources::getAvailableDrivers())) {
+            if (RabbitMqDriver::class === $driverClass) {
+                if (false == $factory instanceof AmqpConnectionFactory) {
+                    throw new \LogicException(sprintf(
+                        'The factory must be instance of "%s", got "%s"',
+                        AmqpConnectionFactory::class,
+                        get_class($factory)
+                    ));
+                }
+
+                return new RabbitMqDriver($factory->createContext(), $this->config, $this->queueMetaRegistry);
             }
 
-            if ($info['requiredSchemeExtensions']) {
-                if (empty($extensions)) {
-                    continue;
+            if (RabbitMqStompDriver::class === $driverClass) {
+                if (false == $factory instanceof StompConnectionFactory) {
+                    throw new \LogicException(sprintf(
+                        'The factory must be instance of "%s", got "%s"',
+                        StompConnectionFactory::class,
+                        get_class($factory)
+                    ));
                 }
 
-                foreach ($info['requiredSchemeExtensions'] as $requiredSchemeExtension) {
-                    if (false == in_array($requiredSchemeExtension, $extensions, true)) {
-                        continue;
-                    }
+                if (isset($config['rabbitmq_management_dsn'])) {
+                    $managementDsn = new Dsn($config['rabbitmq_management_dsn']);
+
+                    $managementClient = ManagementClient::create(
+                        ltrim($managementDsn->getPath(), '/'),
+                        $managementDsn->getHost(),
+                        $managementDsn->getPort(),
+                        $managementDsn->getUser(),
+                        $managementDsn->getPassword()
+                    );
+                } else {
+                    $managementClient = ManagementClient::create(
+                        ltrim($dsn->getPath(), '/'),
+                        $dsn->getHost(),
+                        isset($config['management_plugin_port']) ? $config['management_plugin_port'] : 15672,
+                        $dsn->getUser(),
+                        $dsn->getPassword()
+                    );
                 }
 
-                $foundDriverClass = $driverClass;
-
-                break;
+                return new RabbitMqStompDriver($factory->createContext(), $this->config, $this->queueMetaRegistry, $managementClient);
             }
 
-            $foundDriverClass = $driverClass;
-            break;
+            return new $driverClass($factory->createContext(), $this->config, $this->queueMetaRegistry);
         }
 
-        if (false == $foundDriverClass) {
-            throw new \LogicException(sprintf('The driver class that supports scheme %s could not be found', $dsn->getScheme()));
+        $knownDrivers = Resources::getKnownDrivers();
+        if ($driverClass = $this->findDriverClass($dsn, $knownDrivers)) {
+            throw new \LogicException(sprintf(
+                'To use given scheme "%s" a package has to be installed. Run "composer req %s" to add it.',
+                $dsn->getScheme(),
+                $knownDrivers[$driverClass]['package']
+            ));
         }
 
-        if (RabbitMqDriver::class === $foundDriverClass) {
-            return new RabbitMqDriver($factory->createContext(), $this->config, $this->queueMetaRegistry);
-        }
+        throw new \LogicException(sprintf(
+            'A given scheme "%s" is not supported. Maybe it is a custom driver, make sure you registered it with "%s::addDriver".',
+            $dsn->getScheme(),
+            Resources::class
+        ));
+    }
 
-        if (RabbitMqStompDriver::class === $foundDriverClass) {
-            $managementClient = ManagementClient::create(
-                ltrim($dsn->getPath(), '/'),
-                $dsn->getHost(),
-                $config['management_plugin_port'],
-                $dsn->getUser(),
-                $dsn->getPassword()
-            );
+    private function findDriverClass(Dsn $dsn, array $factories): ?string
+    {
+        $protocol = $dsn->getSchemeProtocol();
+        foreach ($factories as $driverClass => $info) {
+            if (false == in_array($protocol, $info['schemes'], true)) {
+                continue;
+            }
+
+            if (false == $dsn->getSchemeExtensions()) {
+                return $driverClass;
+            }
 
-            return new RabbitMqStompDriver($factory->createContext(), $this->config, $this->queueMetaRegistry, $managementClient);
+            if (empty($info['requiredSchemeExtensions'])) {
+                continue;
+            }
+
+            $diff = array_diff($dsn->getSchemeExtensions(), $info['requiredSchemeExtensions']);
+            if (empty($diff)) {
+                return $driverClass;
+            }
         }
 
-        return new $foundDriverClass($factory->createContext(), $this->config, $this->queueMetaRegistry);
+        return null;
     }
 }
diff --git a/Client/DriverFactoryInterface.php b/Client/DriverFactoryInterface.php
new file mode 100644
index 0000000..ffc96ee
--- /dev/null
+++ b/Client/DriverFactoryInterface.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace Enqueue\Client;
+
+use Interop\Queue\PsrConnectionFactory;
+
+interface DriverFactoryInterface
+{
+    public function create(PsrConnectionFactory $factory, string $dsn, array $config): DriverInterface;
+}
diff --git a/Client/Resources.php b/Client/Resources.php
index 33ee458..7f020b9 100644
--- a/Client/Resources.php
+++ b/Client/Resources.php
@@ -108,20 +108,20 @@ public static function getKnownDrivers(): array
             $map[DbalDriver::class] = [
                 'schemes' => [
                     'db2',
-                    'ibm_db2',
+                    'ibm-db2',
                     'mssql',
-                    'pdo_sqlsrv',
+                    'sqlsrv',
                     'mysql',
                     'mysql2',
-                    'pdo_mysql',
+                    'mysql',
                     'pgsql',
                     'postgres',
-                    'pdo_pgsql',
+                    'pgsql',
                     'sqlite',
                     'sqlite3',
-                    'pdo_sqlite',
+                    'sqlite',
                 ],
-                'requiredSchemeExtensions' => [],
+                'requiredSchemeExtensions' => ['pdo'],
                 'package' => 'enqueue/dbal',
             ];
 
diff --git a/ConnectionFactoryFactory.php b/ConnectionFactoryFactory.php
index 1dcf3ff..9069f43 100644
--- a/ConnectionFactoryFactory.php
+++ b/ConnectionFactoryFactory.php
@@ -11,12 +11,12 @@ public function create(string $dsn): PsrConnectionFactory
     {
         $dsn = new Dsn($dsn);
 
-        if ($factoryClass = $this->findFactory($dsn, Resources::getAvailableConnections())) {
+        if ($factoryClass = $this->findFactoryClass($dsn, Resources::getAvailableConnections())) {
             return new $factoryClass((string) $dsn);
         }
 
         $knownConnections = Resources::getKnownConnections();
-        if ($factoryClass = $this->findFactory($dsn, $knownConnections)) {
+        if ($factoryClass = $this->findFactoryClass($dsn, $knownConnections)) {
             throw new \LogicException(sprintf(
                 'To use given scheme "%s" a package has to be installed. Run "composer req %s" to add it.',
                 $dsn->getScheme(),
@@ -31,7 +31,7 @@ public function create(string $dsn): PsrConnectionFactory
         ));
     }
 
-    private function findFactory(Dsn $dsn, array $factories): ?string
+    private function findFactoryClass(Dsn $dsn, array $factories): ?string
     {
         $protocol = $dsn->getSchemeProtocol();
         foreach ($factories as $connectionClass => $info) {
diff --git a/Tests/Client/DriverFactoryTest.php b/Tests/Client/DriverFactoryTest.php
new file mode 100644
index 0000000..336158c
--- /dev/null
+++ b/Tests/Client/DriverFactoryTest.php
@@ -0,0 +1,179 @@
+<?php
+
+namespace Enqueue\Tests\Client;
+
+use Enqueue\Client\Amqp\AmqpDriver;
+use Enqueue\Client\Amqp\RabbitMqDriver;
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverFactory;
+use Enqueue\Client\DriverFactoryInterface;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Client\Resources;
+use Enqueue\Dbal\Client\DbalDriver;
+use Enqueue\Dbal\DbalConnectionFactory;
+use Enqueue\Dbal\DbalContext;
+use Enqueue\Fs\Client\FsDriver;
+use Enqueue\Fs\FsConnectionFactory;
+use Enqueue\Fs\FsContext;
+use Enqueue\Gearman\GearmanConnectionFactory;
+use Enqueue\Gps\Client\GpsDriver;
+use Enqueue\Gps\GpsConnectionFactory;
+use Enqueue\Gps\GpsContext;
+use Enqueue\Mongodb\Client\MongodbDriver;
+use Enqueue\Mongodb\MongodbConnectionFactory;
+use Enqueue\Mongodb\MongodbContext;
+use Enqueue\Null\Client\NullDriver;
+use Enqueue\Null\NullConnectionFactory;
+use Enqueue\Null\NullContext;
+use Enqueue\RdKafka\Client\RdKafkaDriver;
+use Enqueue\RdKafka\RdKafkaConnectionFactory;
+use Enqueue\RdKafka\RdKafkaContext;
+use Enqueue\Redis\Client\RedisDriver;
+use Enqueue\Redis\RedisConnectionFactory;
+use Enqueue\Redis\RedisContext;
+use Enqueue\Sqs\Client\SqsDriver;
+use Enqueue\Sqs\SqsConnectionFactory;
+use Enqueue\Sqs\SqsContext;
+use Enqueue\Stomp\Client\RabbitMqStompDriver;
+use Enqueue\Stomp\Client\StompDriver;
+use Enqueue\Stomp\StompConnectionFactory;
+use Enqueue\Stomp\StompContext;
+use Interop\Amqp\AmqpConnectionFactory;
+use Interop\Amqp\AmqpContext;
+use Interop\Queue\PsrConnectionFactory;
+use PHPUnit\Framework\TestCase;
+
+class DriverFactoryTest extends TestCase
+{
+    public function testShouldImplementDriverFactoryInterface()
+    {
+        $rc = new \ReflectionClass(DriverFactory::class);
+
+        $this->assertTrue($rc->implementsInterface(DriverFactoryInterface::class));
+    }
+
+    public function testShouldBeFinal()
+    {
+        $rc = new \ReflectionClass(DriverFactory::class);
+
+        $this->assertTrue($rc->isFinal());
+    }
+
+    public function testCouldBeConstructedWithConfigAndQueueMetaAsArguments()
+    {
+        new DriverFactory($this->createConfigMock(), $this->createQueueMetaRegistryMock());
+    }
+
+    public function testThrowIfPackageThatSupportSchemeNotInstalled()
+    {
+        $scheme = 'scheme5b7aa7d7cd213';
+        $class = 'ConnectionClass5b7aa7d7cd213';
+
+        Resources::addDriver($class, [$scheme], [], 'thePackage');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('To use given scheme "scheme5b7aa7d7cd213" a package has to be installed. Run "composer req thePackage" to add it.');
+        $factory = new DriverFactory($this->createConfigMock(), $this->createQueueMetaRegistryMock());
+
+        $factory->create($this->createConnectionFactoryMock(), $scheme.'://foo', []);
+    }
+
+    public function testThrowIfSchemeIsNotKnown()
+    {
+        $scheme = 'scheme5b7aa862e70a5';
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('A given scheme "scheme5b7aa862e70a5" is not supported. Maybe it is a custom driver, make sure you registered it with "Enqueue\Client\Resources::addDriver".');
+
+        $factory = new DriverFactory($this->createConfigMock(), $this->createQueueMetaRegistryMock());
+
+        $factory->create($this->createConnectionFactoryMock(), $scheme.'://foo', []);
+    }
+
+    public function testThrowIfDsnInvalid()
+    {
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The DSN is invalid. It does not have scheme separator ":".');
+
+        $factory = new DriverFactory($this->createConfigMock(), $this->createQueueMetaRegistryMock());
+
+        $factory->create($this->createConnectionFactoryMock(), 'invalidDsn', []);
+    }
+
+    /**
+     * @dataProvider provideDSN
+     */
+    public function testReturnsExpectedFactories(
+        string $dsn,
+        string $connectionFactoryClass,
+        string $contextClass,
+        array $conifg,
+        string $expectedDriverClass
+    ) {
+        $connectionFactoryMock = $this->createMock($connectionFactoryClass);
+        $connectionFactoryMock
+            ->expects($this->once())
+            ->method('createContext')
+            ->willReturn($this->createMock($contextClass))
+        ;
+
+        $driverFactory = new DriverFactory($this->createConfigMock(), $this->createQueueMetaRegistryMock());
+
+        $driver = $driverFactory->create($connectionFactoryMock, $dsn, $conifg);
+
+        $this->assertInstanceOf($expectedDriverClass, $driver);
+    }
+
+    public static function provideDSN()
+    {
+        yield ['null:', NullConnectionFactory::class, NullContext::class, [], NullDriver::class];
+
+        yield ['amqp:', AmqpConnectionFactory::class, AmqpContext::class, [], AmqpDriver::class];
+
+        yield ['amqp+rabbitmq:', AmqpConnectionFactory::class, AmqpContext::class, [], RabbitMqDriver::class];
+
+        yield ['mysql:', DbalConnectionFactory::class, DbalContext::class, [], DbalDriver::class];
+
+        yield ['file:', FsConnectionFactory::class, FsContext::class, [], FsDriver::class];
+
+        // https://github.com/php-enqueue/enqueue-dev/issues/511
+//        yield ['gearman:', GearmanConnectionFactory::class, NullContext::class, [], NullDriver::class];
+
+        yield ['gps:', GpsConnectionFactory::class, GpsContext::class, [], GpsDriver::class];
+
+        yield ['mongodb:', MongodbConnectionFactory::class, MongodbContext::class, [], MongodbDriver::class];
+
+        yield ['kafka:', RdKafkaConnectionFactory::class, RdKafkaContext::class, [], RdKafkaDriver::class];
+
+        yield ['redis:', RedisConnectionFactory::class, RedisContext::class, [], RedisDriver::class];
+
+        yield ['sqs:', SqsConnectionFactory::class, SqsContext::class, [], SqsDriver::class];
+
+        yield ['stomp:', StompConnectionFactory::class, StompContext::class, [], StompDriver::class];
+
+        yield ['stomp+rabbitmq:', StompConnectionFactory::class, StompContext::class, [], RabbitMqStompDriver::class];
+
+        yield ['stomp+rabbitmq:', StompConnectionFactory::class, StompContext::class, [
+            'rabbitmq_management_dsn' => 'http://guest:guest@localhost:15672/mqdev',
+        ], RabbitMqStompDriver::class];
+
+        yield ['stomp+rabbitmq:', StompConnectionFactory::class, StompContext::class, [
+            'management_plugin_port' => 1234,
+        ], RabbitMqStompDriver::class];
+    }
+
+    private function createConnectionFactoryMock(): PsrConnectionFactory
+    {
+        return $this->createMock(PsrConnectionFactory::class);
+    }
+
+    private function createConfigMock(): Config
+    {
+        return $this->createMock(Config::class);
+    }
+
+    private function createQueueMetaRegistryMock()
+    {
+        return $this->createMock(QueueMetaRegistry::class);
+    }
+}
diff --git a/Tests/ConnectionFactoryFactoryTest.php b/Tests/ConnectionFactoryFactoryTest.php
index 588376f..bdfdb04 100644
--- a/Tests/ConnectionFactoryFactoryTest.php
+++ b/Tests/ConnectionFactoryFactoryTest.php
@@ -113,7 +113,8 @@ public static function provideDSN()
 
         yield ['file:', FsConnectionFactory::class];
 
-        yield ['gearman:', GearmanConnectionFactory::class];
+        // https://github.com/php-enqueue/enqueue-dev/issues/511
+//        yield ['gearman:', GearmanConnectionFactory::class];
 
         yield ['gps:', GpsConnectionFactory::class];
 

From 3b92234534268aeaa918abec6d17b477cfa1bb99 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 21 Aug 2018 15:03:01 +0300
Subject: [PATCH 082/337] PHP 7.1+. Queue Interop typed interfaces.

---
 Consumption/FallbackSubscriptionConsumer.php | 31 +++++---------------
 Consumption/QueueConsumer.php                |  9 +++---
 2 files changed, 13 insertions(+), 27 deletions(-)

diff --git a/Consumption/FallbackSubscriptionConsumer.php b/Consumption/FallbackSubscriptionConsumer.php
index f4176a6..e40d8bf 100644
--- a/Consumption/FallbackSubscriptionConsumer.php
+++ b/Consumption/FallbackSubscriptionConsumer.php
@@ -16,7 +16,7 @@ final class FallbackSubscriptionConsumer implements PsrSubscriptionConsumer
     private $subscribers;
 
     /**
-     * @var int|float the time in milliseconds the consumer waits if no message has been received
+     * @var int
      */
     private $idleTime = 0;
 
@@ -25,10 +25,7 @@ public function __construct()
         $this->subscribers = [];
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function consume($timeout = 0)
+    public function consume(int $timeout = 0): void
     {
         if (empty($this->subscribers)) {
             throw new \LogicException('No subscribers');
@@ -65,10 +62,7 @@ public function consume($timeout = 0)
         }
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function subscribe(PsrConsumer $consumer, callable $callback)
+    public function subscribe(PsrConsumer $consumer, callable $callback): void
     {
         $queueName = $consumer->getQueue()->getQueueName();
         if (array_key_exists($queueName, $this->subscribers)) {
@@ -82,10 +76,7 @@ public function subscribe(PsrConsumer $consumer, callable $callback)
         $this->subscribers[$queueName] = [$consumer, $callback];
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function unsubscribe(PsrConsumer $consumer)
+    public function unsubscribe(PsrConsumer $consumer): void
     {
         if (false == array_key_exists($consumer->getQueue()->getQueueName(), $this->subscribers)) {
             return;
@@ -98,26 +89,20 @@ public function unsubscribe(PsrConsumer $consumer)
         unset($this->subscribers[$consumer->getQueue()->getQueueName()]);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function unsubscribeAll()
+    public function unsubscribeAll(): void
     {
         $this->subscribers = [];
     }
 
-    /**
-     * @return float|int
-     */
-    public function getIdleTime()
+    public function getIdleTime(): int
     {
         return $this->idleTime;
     }
 
     /**
-     * @param float|int $idleTime
+     * The time in milliseconds the consumer waits if no message has been received.
      */
-    public function setIdleTime($idleTime)
+    public function setIdleTime(int $idleTime): void
     {
         $this->idleTime = $idleTime;
     }
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 8c21625..5277be5 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -12,7 +12,7 @@
 use Interop\Queue\PsrProcessor;
 use Interop\Queue\PsrQueue;
 use Interop\Queue\PsrSubscriptionConsumer;
-use Interop\Queue\PsrSubscriptionConsumerAwareContext;
+use Interop\Queue\SubscriptionConsumerNotSupportedException;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -188,9 +188,10 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
 
         $this->logger->info('Start consuming');
 
-        $subscriptionConsumer = $this->fallbackSubscriptionConsumer;
-        if ($context instanceof PsrSubscriptionConsumerAwareContext) {
-            $subscriptionConsumer = $context->createSubscriptionConsumer();
+        try {
+            $subscriptionConsumer = $this->psrContext->createSubscriptionConsumer();
+        } catch (SubscriptionConsumerNotSupportedException $e) {
+            $subscriptionConsumer = $this->fallbackSubscriptionConsumer;
         }
 
         $callback = function (PsrMessage $message, PsrConsumer $consumer) use (&$context) {

From f38781a80fbc55457823c17f590bf9a3413d483f Mon Sep 17 00:00:00 2001
From: Arnaud TARROUX <ta.arnaud@gmail.com>
Date: Wed, 22 Aug 2018 14:21:55 +0200
Subject: [PATCH 083/337] Remove bool typehint for php < 7 supports

---
 Consumption/QueueConsumer.php | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index c41028b..80c5917 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -347,8 +347,17 @@ public function consume(ExtensionInterface $runtimeExtension = null)
     /**
      * @param bool $enableSubscriptionConsumer
      */
-    public function enableSubscriptionConsumer(bool $enableSubscriptionConsumer)
+    public function enableSubscriptionConsumer($enableSubscriptionConsumer)
     {
+        if (!is_bool($enableSubscriptionConsumer) {
+            throw new InvalidArgumentException(
+                sprintf(
+                    'The argument must be a boolean but got %s.',
+                    is_object($argument) ? get_class($argument) : gettype($argument)
+                )
+            );
+        }
+            
         $this->enableSubscriptionConsumer = $enableSubscriptionConsumer;
     }
 

From b8944076eb43adb8ebfd03b5c4975b960c4fe949 Mon Sep 17 00:00:00 2001
From: Arnaud TARROUX <ta.arnaud@gmail.com>
Date: Wed, 22 Aug 2018 14:39:02 +0200
Subject: [PATCH 084/337] Missing parenthesis

---
 Consumption/QueueConsumer.php | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 80c5917..58209db 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -346,19 +346,18 @@ public function consume(ExtensionInterface $runtimeExtension = null)
 
     /**
      * @param bool $enableSubscriptionConsumer
+     * @throws \Enqueue\Consumption\Exception\InvalidArgumentException
      */
     public function enableSubscriptionConsumer($enableSubscriptionConsumer)
     {
-        if (!is_bool($enableSubscriptionConsumer) {
+        if (!is_bool($enableSubscriptionConsumer)) {
             throw new InvalidArgumentException(
                 sprintf(
                     'The argument must be a boolean but got %s.',
-                    is_object($argument) ? get_class($argument) : gettype($argument)
+                    is_object($enableSubscriptionConsumer) ? get_class($enableSubscriptionConsumer) : gettype($enableSubscriptionConsumer)
                 )
             );
         }
-            
-        $this->enableSubscriptionConsumer = $enableSubscriptionConsumer;
     }
 
     /**

From 5ceec84e505574a7a05879d747c1e824a9a99ec2 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 23 Aug 2018 12:23:55 +0300
Subject: [PATCH 085/337] fix enqueue tests.

---
 Client/Amqp/AmqpDriver.php                    |   8 +-
 Tests/Client/Amqp/AmqpDriverTest.php          |  17 ---
 Tests/Client/Amqp/RabbitMqDriverTest.php      |  34 -----
 .../Mock/DummySubscriptionConsumer.php        |  10 +-
 Tests/Consumption/QueueConsumerTest.php       | 122 ++++++++++++++++--
 5 files changed, 118 insertions(+), 73 deletions(-)

diff --git a/Client/Amqp/AmqpDriver.php b/Client/Amqp/AmqpDriver.php
index e8b5871..44d9e1b 100644
--- a/Client/Amqp/AmqpDriver.php
+++ b/Client/Amqp/AmqpDriver.php
@@ -145,7 +145,7 @@ public function createTransportMessage(Message $message)
         $transportMessage->setDeliveryMode(AmqpMessage::DELIVERY_MODE_PERSISTENT);
 
         if ($message->getExpire()) {
-            $transportMessage->setExpiration((string) ($message->getExpire() * 1000));
+            $transportMessage->setExpiration($message->getExpire() * 1000);
         }
 
         return $transportMessage;
@@ -166,11 +166,7 @@ public function createClientMessage(PsrMessage $message)
         $clientMessage->setContentType($message->getContentType());
 
         if ($expiration = $message->getExpiration()) {
-            if (false == is_numeric($expiration)) {
-                throw new \LogicException(sprintf('expiration header is not numeric. "%s"', $expiration));
-            }
-
-            $clientMessage->setExpire((int) ((int) $expiration) / 1000);
+            $clientMessage->setExpire((int) ($expiration / 1000));
         }
 
         $clientMessage->setMessageId($message->getMessageId());
diff --git a/Tests/Client/Amqp/AmqpDriverTest.php b/Tests/Client/Amqp/AmqpDriverTest.php
index a82fda0..af69ee2 100644
--- a/Tests/Client/Amqp/AmqpDriverTest.php
+++ b/Tests/Client/Amqp/AmqpDriverTest.php
@@ -127,23 +127,6 @@ public function testShouldConvertTransportMessageToClientMessage()
         $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
     }
 
-    public function testShouldThrowExceptionIfExpirationIsNotNumeric()
-    {
-        $transportMessage = new AmqpMessage();
-        $transportMessage->setHeader('expiration', 'is-not-numeric');
-
-        $driver = new AmqpDriver(
-            $this->createAmqpContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('expiration header is not numeric. "is-not-numeric"');
-
-        $driver->createClientMessage($transportMessage);
-    }
-
     public function testShouldConvertClientMessageToTransportMessage()
     {
         $clientMessage = new Message();
diff --git a/Tests/Client/Amqp/RabbitMqDriverTest.php b/Tests/Client/Amqp/RabbitMqDriverTest.php
index 3b67ef5..dbdd6d4 100644
--- a/Tests/Client/Amqp/RabbitMqDriverTest.php
+++ b/Tests/Client/Amqp/RabbitMqDriverTest.php
@@ -157,40 +157,6 @@ public function testShouldThrowExceptionIfXDelayIsNotNumeric()
         $driver->createClientMessage($transportMessage);
     }
 
-    public function testShouldThrowExceptionIfExpirationIsNotNumeric()
-    {
-        $transportMessage = new AmqpMessage();
-        $transportMessage->setHeader('expiration', 'is-not-numeric');
-
-        $driver = new RabbitMqDriver(
-            $this->createAmqpContextMock(),
-            Config::create(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('expiration header is not numeric. "is-not-numeric"');
-
-        $driver->createClientMessage($transportMessage);
-    }
-
-    public function testShouldThrowExceptionIfCantConvertTransportPriorityToClientPriority()
-    {
-        $transportMessage = new AmqpMessage();
-        $transportMessage->setHeader('priority', 'unknown');
-
-        $driver = new RabbitMqDriver(
-            $this->createAmqpContextMock(),
-            Config::create(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Cant convert transport priority to client: "unknown"');
-
-        $driver->createClientMessage($transportMessage);
-    }
-
     public function testShouldThrowExceptionIfCantConvertClientPriorityToTransportPriority()
     {
         $clientMessage = new Message();
diff --git a/Tests/Consumption/Mock/DummySubscriptionConsumer.php b/Tests/Consumption/Mock/DummySubscriptionConsumer.php
index eb2bece..f832284 100644
--- a/Tests/Consumption/Mock/DummySubscriptionConsumer.php
+++ b/Tests/Consumption/Mock/DummySubscriptionConsumer.php
@@ -15,7 +15,7 @@ class DummySubscriptionConsumer implements PsrSubscriptionConsumer
     /**
      * @param float|int $timeout milliseconds 1000 is 1 second, a zero is consume endlessly
      */
-    public function consume($timeout = 0)
+    public function consume(int $timeout = 0): void
     {
         foreach ($this->messages as list($message, $queueName)) {
             /** @var PsrMessage $message */
@@ -26,22 +26,22 @@ public function consume($timeout = 0)
         }
     }
 
-    public function subscribe(PsrConsumer $consumer, callable $callback)
+    public function subscribe(PsrConsumer $consumer, callable $callback): void
     {
         $this->subscriptions[$consumer->getQueue()->getQueueName()] = [$consumer, $callback];
     }
 
-    public function unsubscribe(PsrConsumer $consumer)
+    public function unsubscribe(PsrConsumer $consumer): void
     {
         unset($this->subscriptions[$consumer->getQueue()->getQueueName()]);
     }
 
-    public function unsubscribeAll()
+    public function unsubscribeAll(): void
     {
         $this->subscriptions = [];
     }
 
-    public function addMessage(PsrMessage $message, string $queueName)
+    public function addMessage(PsrMessage $message, string $queueName): void
     {
         $this->messages[] = [$message, $queueName];
     }
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index b5282d7..9a99606 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -18,6 +18,7 @@
 use Interop\Queue\PsrProcessor;
 use Interop\Queue\PsrQueue;
 use Interop\Queue\PsrSubscriptionConsumer;
+use Interop\Queue\SubscriptionConsumerNotSupportedException;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -127,7 +128,7 @@ public function testShouldAllowBindCallbackToQueueName()
         $queueName = 'theQueueName';
         $queue = new NullQueue($queueName);
 
-        $context = $this->createMock(PsrContext::class);
+        $context = $this->createContextWithoutSubscriptionConsumerMock();
         $context
             ->expects($this->once())
             ->method('createQueue')
@@ -160,6 +161,90 @@ public function testShouldReturnSelfOnBind()
         $this->assertSame($consumer, $consumer->bind(new NullQueue('foo_queue'), $processorMock));
     }
 
+    public function testShouldUseContextSubscriptionConsumerIfSupport()
+    {
+        $expectedQueue = new NullQueue('theQueueName');
+
+        $contextSubscriptionConsumer = $this->createSubscriptionConsumerMock();
+        $contextSubscriptionConsumer
+            ->expects($this->once())
+            ->method('consume')
+            ->willReturn(null)
+        ;
+
+        $fallbackSubscriptionConsumer = $this->createSubscriptionConsumerMock();
+        $fallbackSubscriptionConsumer
+            ->expects($this->never())
+            ->method('consume')
+        ;
+
+        $contextMock = $this->createMock(PsrContext::class);
+        $contextMock
+            ->expects($this->once())
+            ->method('createConsumer')
+            ->with($this->identicalTo($expectedQueue))
+            ->willReturn($this->createConsumerStub())
+        ;
+        $contextMock
+            ->expects($this->once())
+            ->method('createSubscriptionConsumer')
+            ->willReturn($contextSubscriptionConsumer)
+        ;
+
+        $processorMock = $this->createProcessorMock();
+        $processorMock
+            ->expects($this->never())
+            ->method('process')
+        ;
+
+        $queueConsumer = new QueueConsumer($contextMock, new BreakCycleExtension(1));
+        $queueConsumer->setFallbackSubscriptionConsumer($fallbackSubscriptionConsumer);
+        $queueConsumer->bind($expectedQueue, $processorMock);
+        $queueConsumer->consume();
+    }
+
+    public function testShouldUseFallbackSubscriptionConsumerIfNotSupported()
+    {
+        $expectedQueue = new NullQueue('theQueueName');
+
+        $contextSubscriptionConsumer = $this->createSubscriptionConsumerMock();
+        $contextSubscriptionConsumer
+            ->expects($this->never())
+            ->method('consume')
+        ;
+
+        $fallbackSubscriptionConsumer = $this->createSubscriptionConsumerMock();
+        $fallbackSubscriptionConsumer
+            ->expects($this->once())
+            ->method('consume')
+            ->willReturn(null)
+        ;
+
+        $contextMock = $this->createContextWithoutSubscriptionConsumerMock();
+        $contextMock
+            ->expects($this->once())
+            ->method('createConsumer')
+            ->with($this->identicalTo($expectedQueue))
+            ->willReturn($this->createConsumerStub())
+        ;
+        $contextMock
+            ->expects($this->once())
+            ->method('createSubscriptionConsumer')
+            ->willThrowException(SubscriptionConsumerNotSupportedException::providerDoestNotSupportIt())
+        ;
+
+        $processorMock = $this->createProcessorMock();
+        $processorMock
+            ->expects($this->never())
+            ->method('process')
+        ;
+
+        $queueConsumer = new QueueConsumer($contextMock, new BreakCycleExtension(1));
+        $queueConsumer->setFallbackSubscriptionConsumer($fallbackSubscriptionConsumer);
+        $queueConsumer->bind($expectedQueue, $processorMock);
+        $queueConsumer->consume();
+    }
+
     public function testShouldSubscribeToGivenQueueWithExpectedTimeout()
     {
         $expectedQueue = new NullQueue('theQueueName');
@@ -172,7 +257,7 @@ public function testShouldSubscribeToGivenQueueWithExpectedTimeout()
             ->willReturn(null)
         ;
 
-        $contextMock = $this->createMock(PsrContext::class);
+        $contextMock = $this->createContextWithoutSubscriptionConsumerMock();
         $contextMock
             ->expects($this->once())
             ->method('createConsumer')
@@ -203,7 +288,7 @@ public function testShouldSubscribeToGivenQueueAndQuitAfterFifthIdleCycle()
             ->willReturn(null)
         ;
 
-        $contextMock = $this->createMock(PsrContext::class);
+        $contextMock = $this->createContextWithoutSubscriptionConsumerMock();
         $contextMock
             ->expects($this->once())
             ->method('createConsumer')
@@ -1173,7 +1258,7 @@ public function testShouldCallProcessorAsMessageComeAlong()
             'bar_queue' => $barConsumerStub,
         ];
 
-        $contextStub = $this->createMock(PsrContext::class);
+        $contextStub = $this->createContextWithoutSubscriptionConsumerMock();
         $contextStub
             ->expects($this->any())
             ->method('createQueue')
@@ -1225,12 +1310,27 @@ public function testShouldCallProcessorAsMessageComeAlong()
         $this->assertSame($fooConsumerStub, $actualContexts[2]->getPsrConsumer());
     }
 
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createContextWithoutSubscriptionConsumerMock(): PsrContext
+    {
+        $contextMock = $this->createMock(PsrContext::class);
+        $contextMock
+            ->expects($this->any())
+            ->method('createSubscriptionConsumer')
+            ->willThrowException(SubscriptionConsumerNotSupportedException::providerDoestNotSupportIt())
+        ;
+
+        return $contextMock;
+    }
+
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext
      */
-    protected function createPsrContextStub(PsrConsumer $consumer = null)
+    private function createPsrContextStub(PsrConsumer $consumer = null)
     {
-        $context = $this->createMock(PsrContext::class);
+        $context = $this->createContextWithoutSubscriptionConsumerMock();
         $context
             ->expects($this->any())
             ->method('createQueue')
@@ -1257,7 +1357,7 @@ protected function createPsrContextStub(PsrConsumer $consumer = null)
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|PsrProcessor
      */
-    protected function createProcessorMock()
+    private function createProcessorMock()
     {
         return $this->createMock(PsrProcessor::class);
     }
@@ -1265,7 +1365,7 @@ protected function createProcessorMock()
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|PsrProcessor
      */
-    protected function createProcessorStub()
+    private function createProcessorStub()
     {
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -1280,7 +1380,7 @@ protected function createProcessorStub()
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|PsrMessage
      */
-    protected function createMessageMock()
+    private function createMessageMock()
     {
         return $this->createMock(PsrMessage::class);
     }
@@ -1288,7 +1388,7 @@ protected function createMessageMock()
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|ExtensionInterface
      */
-    protected function createExtension()
+    private function createExtension()
     {
         return $this->createMock(ExtensionInterface::class);
     }
@@ -1298,7 +1398,7 @@ protected function createExtension()
      *
      * @return \PHPUnit_Framework_MockObject_MockObject|PsrConsumer
      */
-    protected function createConsumerStub($queue = null)
+    private function createConsumerStub($queue = null)
     {
         if (is_string($queue)) {
             $queue = new NullQueue($queue);

From b93979d4efc1d98cb8716e7480fa9030fb2c84d1 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 23 Aug 2018 20:27:58 +0300
Subject: [PATCH 086/337] use bunny by default

---
 Resources.php | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/Resources.php b/Resources.php
index 897ec7f..99f3ad1 100644
--- a/Resources.php
+++ b/Resources.php
@@ -86,16 +86,16 @@ public static function getKnownConnections(): array
                 'supportedSchemeExtensions' => [],
                 'package' => 'enqueue/fs',
             ];
-            $map[AmqpExtConnectionFactory::class] = [
-                'schemes' => ['amqp', 'amqps'],
-                'supportedSchemeExtensions' => ['ext', 'rabbitmq'],
-                'package' => 'enqueue/amqp-ext',
-            ];
             $map[AmqpBunnyConnectionFactory::class] = [
                 'schemes' => ['amqp'],
                 'supportedSchemeExtensions' => ['bunny', 'rabbitmq'],
                 'package' => 'enqueue/amqp-bunny',
             ];
+            $map[AmqpExtConnectionFactory::class] = [
+                'schemes' => ['amqp', 'amqps'],
+                'supportedSchemeExtensions' => ['ext', 'rabbitmq'],
+                'package' => 'enqueue/amqp-ext',
+            ];
             $map[AmqpLibConnectionFactory::class] = [
                 'schemes' => ['amqp', 'amqps'],
                 'supportedSchemeExtensions' => ['lib', 'rabbitmq'],

From 8e0bf78ca581fdee46c75521333acbb75b63bed0 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 23 Aug 2018 20:37:51 +0300
Subject: [PATCH 087/337] fix tests

---
 Tests/ConnectionFactoryFactoryTest.php     | 2 +-
 Tests/Symfony/AmqpTransportFactoryTest.php | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/Tests/ConnectionFactoryFactoryTest.php b/Tests/ConnectionFactoryFactoryTest.php
index bdfdb04..06d6bde 100644
--- a/Tests/ConnectionFactoryFactoryTest.php
+++ b/Tests/ConnectionFactoryFactoryTest.php
@@ -85,7 +85,7 @@ public static function provideDSN()
     {
         yield ['null:', NullConnectionFactory::class];
 
-        yield ['amqp:', AmqpExtConnectionFactory::class];
+        yield ['amqp:', AmqpBunnyConnectionFactory::class];
 
         yield ['amqp+ext:', AmqpExtConnectionFactory::class];
 
diff --git a/Tests/Symfony/AmqpTransportFactoryTest.php b/Tests/Symfony/AmqpTransportFactoryTest.php
index b27f5b5..7640189 100644
--- a/Tests/Symfony/AmqpTransportFactoryTest.php
+++ b/Tests/Symfony/AmqpTransportFactoryTest.php
@@ -369,7 +369,7 @@ public function testShouldCreateAmqpExtFromConfigWithoutDriverAndDsn()
     {
         $factory = AmqpTransportFactory::createConnectionFactoryFactory(['host' => 'aHost']);
 
-        $this->assertInstanceOf(\Enqueue\AmqpExt\AmqpConnectionFactory::class, $factory);
+        $this->assertInstanceOf(\Enqueue\AmqpBunny\AmqpConnectionFactory::class, $factory);
     }
 
     public function testThrowIfInvalidDriverGiven()
@@ -380,11 +380,11 @@ public function testThrowIfInvalidDriverGiven()
         AmqpTransportFactory::createConnectionFactoryFactory(['driver' => 'invalidDriver']);
     }
 
-    public function testShouldCreateAmqpExtFromDsn()
+    public function testShouldCreateAmqpBunnyFromDsn()
     {
         $factory = AmqpTransportFactory::createConnectionFactoryFactory(['dsn' => 'amqp:']);
 
-        $this->assertInstanceOf(\Enqueue\AmqpExt\AmqpConnectionFactory::class, $factory);
+        $this->assertInstanceOf(\Enqueue\AmqpBunny\AmqpConnectionFactory::class, $factory);
     }
 
     public function testShouldCreateAmqpBunnyFromDsnWithDriver()

From 302ee47f547576ce7ba41d5e0a3ad7afe662bbc9 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 27 Aug 2018 13:28:14 +0300
Subject: [PATCH 088/337] [client] Improve client extension.

---
 Client/ChainExtension.php                     |  28 ++-
 .../ExclusiveCommandExtension.php             |  31 +--
 Client/EmptyExtensionTrait.php                |  22 ++
 Client/Extension/PrepareBodyExtension.php     |  59 +++++
 Client/ExtensionInterface.php                 |  30 +--
 Client/PostSend.php                           |  49 ++++
 Client/PreDriverSend.php                      |  49 ++++
 Client/PreSend.php                            |  69 ++++++
 Client/Producer.php                           | 143 +++++------
 .../Extension/PrepareBodyExtensionTest.php    | 142 +++++++++++
 Tests/Client/ProducerTest.php                 | 227 ------------------
 11 files changed, 491 insertions(+), 358 deletions(-)
 create mode 100644 Client/EmptyExtensionTrait.php
 create mode 100644 Client/Extension/PrepareBodyExtension.php
 create mode 100644 Client/PostSend.php
 create mode 100644 Client/PreDriverSend.php
 create mode 100644 Client/PreSend.php
 create mode 100644 Tests/Client/Extension/PrepareBodyExtensionTest.php

diff --git a/Client/ChainExtension.php b/Client/ChainExtension.php
index c202e98..01bee22 100644
--- a/Client/ChainExtension.php
+++ b/Client/ChainExtension.php
@@ -17,23 +17,31 @@ public function __construct(array $extensions)
         $this->extensions = $extensions;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function onPreSend($topic, Message $message)
+    public function onPreSendEvent(PreSend $event): void
     {
         foreach ($this->extensions as $extension) {
-            $extension->onPreSend($topic, $message);
+            $extension->onPreSendEvent($event);
         }
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function onPostSend($topic, Message $message)
+    public function onPreSendCommand(PreSend $event): void
+    {
+        foreach ($this->extensions as $extension) {
+            $extension->onPreSendCommand($event);
+        }
+    }
+
+    public function onPreDriverSend(PreDriverSend $context): void
+    {
+        foreach ($this->extensions as $extension) {
+            $extension->onPreDriverSend($context);
+        }
+    }
+
+    public function onPostSend(PostSend $event): void
     {
         foreach ($this->extensions as $extension) {
-            $extension->onPostSend($topic, $message);
+            $extension->onPostSend($event);
         }
     }
 }
diff --git a/Client/ConsumptionExtension/ExclusiveCommandExtension.php b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
index a9afde0..add6ef7 100644
--- a/Client/ConsumptionExtension/ExclusiveCommandExtension.php
+++ b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
@@ -3,15 +3,16 @@
 namespace Enqueue\Client\ConsumptionExtension;
 
 use Enqueue\Client\Config;
+use Enqueue\Client\EmptyExtensionTrait as ClientEmptyExtensionTrait;
 use Enqueue\Client\ExtensionInterface as ClientExtensionInterface;
-use Enqueue\Client\Message;
+use Enqueue\Client\PreSend;
 use Enqueue\Consumption\Context;
-use Enqueue\Consumption\EmptyExtensionTrait;
+use Enqueue\Consumption\EmptyExtensionTrait as ConsumptionEmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface as ConsumptionExtensionInterface;
 
 class ExclusiveCommandExtension implements ConsumptionExtensionInterface, ClientExtensionInterface
 {
-    use EmptyExtensionTrait;
+    use ConsumptionEmptyExtensionTrait, ClientEmptyExtensionTrait;
 
     /**
      * @var string[]
@@ -60,26 +61,14 @@ public function onPreReceived(Context $context)
         }
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function onPreSend($topic, Message $message)
+    public function onPreSendCommand(PreSend $context): void
     {
-        if (Config::COMMAND_TOPIC != $topic) {
-            return;
-        }
+        $message = $context->getMessage();
+        $command = $context->getCommandOrTopic();
 
-        $commandName = $message->getProperty(Config::PARAMETER_COMMAND_NAME);
-        if (array_key_exists($commandName, $this->processorNameToQueueNameMap)) {
-            $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $commandName);
-            $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $this->processorNameToQueueNameMap[$commandName]);
+        if (array_key_exists($command, $this->processorNameToQueueNameMap)) {
+            $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $command);
+            $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $this->processorNameToQueueNameMap[$command]);
         }
     }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function onPostSend($topic, Message $message)
-    {
-    }
 }
diff --git a/Client/EmptyExtensionTrait.php b/Client/EmptyExtensionTrait.php
new file mode 100644
index 0000000..baf7f56
--- /dev/null
+++ b/Client/EmptyExtensionTrait.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Enqueue\Client;
+
+trait EmptyExtensionTrait
+{
+    public function onPreSendEvent(PreSend $context): void
+    {
+    }
+
+    public function onPreSendCommand(PreSend $context): void
+    {
+    }
+
+    public function onPreDriverSend(PreDriverSend $context): void
+    {
+    }
+
+    public function onPostSend(PostSend $context): void
+    {
+    }
+}
diff --git a/Client/Extension/PrepareBodyExtension.php b/Client/Extension/PrepareBodyExtension.php
new file mode 100644
index 0000000..47111d2
--- /dev/null
+++ b/Client/Extension/PrepareBodyExtension.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Enqueue\Client\Extension;
+
+use Enqueue\Client\EmptyExtensionTrait;
+use Enqueue\Client\ExtensionInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\PreSend;
+use Enqueue\Util\JSON;
+
+class PrepareBodyExtension implements ExtensionInterface
+{
+    use EmptyExtensionTrait;
+
+    public function onPreSendEvent(PreSend $context): void
+    {
+        $this->prepareBody($context->getMessage());
+    }
+
+    public function onPreSendCommand(PreSend $context): void
+    {
+        $this->prepareBody($context->getMessage());
+    }
+
+    private function prepareBody(Message $message): void
+    {
+        $body = $message->getBody();
+        $contentType = $message->getContentType();
+
+        if (is_scalar($body) || null === $body) {
+            $contentType = $contentType ?: 'text/plain';
+            $body = (string) $body;
+        } elseif (is_array($body)) {
+            // only array of scalars is allowed.
+            array_walk_recursive($body, function ($value) {
+                if (!is_scalar($value) && null !== $value) {
+                    throw new \LogicException(sprintf(
+                        'The message\'s body must be an array of scalars. Found not scalar in the array: %s',
+                        is_object($value) ? get_class($value) : gettype($value)
+                    ));
+                }
+            });
+
+            $contentType = $contentType ?: 'application/json';
+            $body = JSON::encode($body);
+        } elseif ($body instanceof \JsonSerializable) {
+            $contentType = $contentType ?: 'application/json';
+            $body = JSON::encode($body);
+        } else {
+            throw new \InvalidArgumentException(sprintf(
+                'The message\'s body must be either null, scalar, array or object (implements \JsonSerializable). Got: %s',
+                is_object($body) ? get_class($body) : gettype($body)
+            ));
+        }
+
+        $message->setContentType($contentType);
+        $message->setBody($body);
+    }
+}
diff --git a/Client/ExtensionInterface.php b/Client/ExtensionInterface.php
index 4f9fd66..deb7d3f 100644
--- a/Client/ExtensionInterface.php
+++ b/Client/ExtensionInterface.php
@@ -4,19 +4,21 @@
 
 interface ExtensionInterface
 {
-    /**
-     * @param string  $topic
-     * @param Message $message
-     *
-     * @return
-     */
-    public function onPreSend($topic, Message $message);
+    public function onPreSendEvent(PreSend $context): void;
 
-    /**
-     * @param string  $topic
-     * @param Message $message
-     *
-     * @return
-     */
-    public function onPostSend($topic, Message $message);
+    public function onPreSendCommand(PreSend $context): void;
+
+    public function onPreDriverSend(PreDriverSend $context): void;
+
+    public function onPostSend(PostSend $context): void;
+
+//    /**
+//     * @deprecated
+//     */
+//    public function onPreSend($topic, Message $message);
+//
+//    /**
+//     * @deprecated
+//     */
+//    public function onPostSend($topic, Message $message);
 }
diff --git a/Client/PostSend.php b/Client/PostSend.php
new file mode 100644
index 0000000..f99d781
--- /dev/null
+++ b/Client/PostSend.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace Enqueue\Client;
+
+class PostSend
+{
+    private $message;
+
+    private $producer;
+
+    private $driver;
+
+    public function __construct(Message $message, ProducerInterface $producer, DriverInterface $driver)
+    {
+        $this->message = $message;
+        $this->producer = $producer;
+        $this->driver = $driver;
+    }
+
+    public function getMessage(): Message
+    {
+        return $this->message;
+    }
+
+    public function getProducer(): ProducerInterface
+    {
+        return $this->producer;
+    }
+
+    public function getDriver(): DriverInterface
+    {
+        return $this->driver;
+    }
+
+    public function isEvent(): bool
+    {
+        return Config::COMMAND_TOPIC !== $this->message->getProperty(Config::PARAMETER_TOPIC_NAME);
+    }
+
+    public function getCommand(): string
+    {
+        return $this->message->getProperty(Config::PARAMETER_COMMAND_NAME);
+    }
+
+    public function getTopic(): string
+    {
+        return $this->message->getProperty(Config::PARAMETER_TOPIC_NAME);
+    }
+}
diff --git a/Client/PreDriverSend.php b/Client/PreDriverSend.php
new file mode 100644
index 0000000..1ea5804
--- /dev/null
+++ b/Client/PreDriverSend.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace Enqueue\Client;
+
+class PreDriverSend
+{
+    private $message;
+
+    private $producer;
+
+    private $driver;
+
+    public function __construct(Message $message, ProducerInterface $producer, DriverInterface $driver)
+    {
+        $this->message = $message;
+        $this->producer = $producer;
+        $this->driver = $driver;
+    }
+
+    public function getMessage(): Message
+    {
+        return $this->message;
+    }
+
+    public function getProducer(): ProducerInterface
+    {
+        return $this->producer;
+    }
+
+    public function getDriver(): DriverInterface
+    {
+        return $this->driver;
+    }
+
+    public function isEvent(): bool
+    {
+        return Config::COMMAND_TOPIC !== $this->message->getProperty(Config::PARAMETER_TOPIC_NAME);
+    }
+
+    public function getCommand(): string
+    {
+        return $this->message->getProperty(Config::PARAMETER_COMMAND_NAME);
+    }
+
+    public function getTopic(): string
+    {
+        return $this->message->getProperty(Config::PARAMETER_TOPIC_NAME);
+    }
+}
diff --git a/Client/PreSend.php b/Client/PreSend.php
new file mode 100644
index 0000000..b37ce5d
--- /dev/null
+++ b/Client/PreSend.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Enqueue\Client;
+
+class PreSend
+{
+    private $message;
+
+    private $originalMessage;
+
+    private $commandOrTopic;
+
+    private $producer;
+
+    private $driver;
+
+    public function __construct(
+        string $commandOrTopic,
+        Message $message,
+        ProducerInterface $producer,
+        DriverInterface $driver
+    ) {
+        $this->message = $message;
+        $this->commandOrTopic = $commandOrTopic;
+        $this->producer = $producer;
+        $this->driver = $driver;
+
+        $this->originalMessage = clone $message;
+    }
+
+    public function getCommandOrTopic(): string
+    {
+        return $this->commandOrTopic;
+    }
+
+    public function changeCommandOrTopic(string $commandOrTopic): void
+    {
+        $this->commandOrTopic = $commandOrTopic;
+    }
+
+    public function changeBody($body, string $contentType = null): void
+    {
+        $this->message->setBody($body);
+
+        if (null !== $contentType) {
+            $this->message->setContentType($contentType);
+        }
+    }
+
+    public function getMessage(): Message
+    {
+        return $this->message;
+    }
+
+    public function getOriginalMessage(): Message
+    {
+        return $this->originalMessage;
+    }
+
+    public function getProducer(): ProducerInterface
+    {
+        return $this->producer;
+    }
+
+    public function getDriver(): DriverInterface
+    {
+        return $this->driver;
+    }
+}
diff --git a/Client/Producer.php b/Client/Producer.php
index 8fda47f..1595783 100644
--- a/Client/Producer.php
+++ b/Client/Producer.php
@@ -2,8 +2,8 @@
 
 namespace Enqueue\Client;
 
+use Enqueue\Client\Extension\PrepareBodyExtension;
 use Enqueue\Rpc\RpcFactory;
-use Enqueue\Util\JSON;
 use Enqueue\Util\UUID;
 
 class Producer implements ProducerInterface
@@ -37,72 +37,40 @@ public function __construct(
     ) {
         $this->driver = $driver;
         $this->rpcFactory = $rpcFactory;
-        $this->extension = $extension ?: new ChainExtension([]);
+
+        $prepareBodyExtension = new PrepareBodyExtension();
+        $this->extension = new ChainExtension([$extension, $prepareBodyExtension]) ?: new ChainExtension([$prepareBodyExtension]);
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function sendEvent($topic, $message)
     {
         if (false == $message instanceof Message) {
-            $body = $message;
-            $message = new Message();
-            $message->setBody($body);
-        }
-
-        $this->prepareBody($message);
-
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, $topic);
-
-        if (!$message->getMessageId()) {
-            $message->setMessageId(UUID::generate());
+            $message = new Message($message);
         }
 
-        if (!$message->getTimestamp()) {
-            $message->setTimestamp(time());
-        }
+        $preSend = new PreSend($topic, $message, $this, $this->driver);
+        $this->extension->onPreSendEvent($preSend);
 
-        if (!$message->getPriority()) {
-            $message->setPriority(MessagePriority::NORMAL);
-        }
+        $topic = $preSend->getCommandOrTopic();
+        $message = $preSend->getMessage();
 
-        if (Message::SCOPE_MESSAGE_BUS == $message->getScope()) {
-            if ($message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
-                throw new \LogicException(sprintf('The %s property must not be set for messages that are sent to message bus.', Config::PARAMETER_PROCESSOR_QUEUE_NAME));
-            }
-            if ($message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
-                throw new \LogicException(sprintf('The %s property must not be set for messages that are sent to message bus.', Config::PARAMETER_PROCESSOR_NAME));
-            }
-
-            $this->extension->onPreSend($topic, $message);
-            $this->driver->sendToRouter($message);
-            $this->extension->onPostSend($topic, $message);
-        } elseif (Message::SCOPE_APP == $message->getScope()) {
-            if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
-                $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $this->driver->getConfig()->getRouterProcessorName());
-            }
-            if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
-                $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $this->driver->getConfig()->getRouterQueueName());
-            }
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, $topic);
 
-            $this->extension->onPreSend($topic, $message);
-            $this->driver->sendToProcessor($message);
-            $this->extension->onPostSend($topic, $message);
-        } else {
-            throw new \LogicException(sprintf('The message scope "%s" is not supported.', $message->getScope()));
-        }
+        $this->doSend($message);
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function sendCommand($command, $message, $needReply = false)
     {
         if (false == $message instanceof Message) {
             $message = new Message($message);
         }
 
+        $preSend = new PreSend($command, $message, $this, $this->driver);
+        $this->extension->onPreSendEvent($preSend);
+
+        $command = $preSend->getCommandOrTopic();
+        $message = $preSend->getMessage();
+
         $deleteReplyQueue = false;
         $replyTo = $message->getReplyTo();
 
@@ -119,9 +87,10 @@ public function sendCommand($command, $message, $needReply = false)
 
         $message->setProperty(Config::PARAMETER_TOPIC_NAME, Config::COMMAND_TOPIC);
         $message->setProperty(Config::PARAMETER_COMMAND_NAME, $command);
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, Config::COMMAND_TOPIC);
         $message->setScope(Message::SCOPE_APP);
 
-        $this->sendEvent(Config::COMMAND_TOPIC, $message);
+        $this->doSend($message);
 
         if ($needReply) {
             $promise = $this->rpcFactory->createPromise($replyTo, $message->getCorrelationId(), 60000);
@@ -139,49 +108,51 @@ public function send($topic, $message)
         $this->sendEvent($topic, $message);
     }
 
-    /**
-     * @param Message $message
-     */
-    private function prepareBody(Message $message)
+    private function doSend(Message $message)
     {
-        $body = $message->getBody();
-        $contentType = $message->getContentType();
-
-        if (is_scalar($body) || null === $body) {
-            $contentType = $contentType ?: 'text/plain';
-            $body = (string) $body;
-        } elseif (is_array($body)) {
-            if ($contentType && 'application/json' !== $contentType) {
-                throw new \LogicException(sprintf('Content type "application/json" only allowed when body is array'));
+        if (false === is_string($message->getBody())) {
+            throw new \LogicException(sprintf(
+                'The message body must be string at this stage, got "%s". Make sure you passed string as message or there is an extension that converts custom input to string.',
+                is_object($message->getBody()) ? get_class($message->getBody()) : gettype($message->getBody())
+            ));
+        }
+
+        if (!$message->getMessageId()) {
+            $message->setMessageId(UUID::generate());
+        }
+
+        if (!$message->getTimestamp()) {
+            $message->setTimestamp(time());
+        }
+
+        if (!$message->getPriority()) {
+            $message->setPriority(MessagePriority::NORMAL);
+        }
+
+        if (Message::SCOPE_MESSAGE_BUS == $message->getScope()) {
+            if ($message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
+                throw new \LogicException(sprintf('The %s property must not be set for messages that are sent to message bus.', Config::PARAMETER_PROCESSOR_QUEUE_NAME));
+            }
+            if ($message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
+                throw new \LogicException(sprintf('The %s property must not be set for messages that are sent to message bus.', Config::PARAMETER_PROCESSOR_NAME));
             }
 
-            // only array of scalars is allowed.
-            array_walk_recursive($body, function ($value) {
-                if (!is_scalar($value) && null !== $value) {
-                    throw new \LogicException(sprintf(
-                        'The message\'s body must be an array of scalars. Found not scalar in the array: %s',
-                        is_object($value) ? get_class($value) : gettype($value)
-                    ));
-                }
-            });
-
-            $contentType = 'application/json';
-            $body = JSON::encode($body);
-        } elseif ($body instanceof \JsonSerializable) {
-            if ($contentType && 'application/json' !== $contentType) {
-                throw new \LogicException(sprintf('Content type "application/json" only allowed when body is array'));
+            $this->extension->onPreDriverSend(new PreDriverSend($message, $this, $this->driver));
+            $this->driver->sendToRouter($message);
+        } elseif (Message::SCOPE_APP == $message->getScope()) {
+            if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
+                $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $this->driver->getConfig()->getRouterProcessorName());
+            }
+            if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
+                $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $this->driver->getConfig()->getRouterQueueName());
             }
 
-            $contentType = 'application/json';
-            $body = JSON::encode($body);
+            $this->extension->onPreDriverSend(new PreDriverSend($message, $this, $this->driver));
+            $this->driver->sendToRouter($message);
         } else {
-            throw new \InvalidArgumentException(sprintf(
-                'The message\'s body must be either null, scalar, array or object (implements \JsonSerializable). Got: %s',
-                is_object($body) ? get_class($body) : gettype($body)
-            ));
+            throw new \LogicException(sprintf('The message scope "%s" is not supported.', $message->getScope()));
         }
 
-        $message->setContentType($contentType);
-        $message->setBody($body);
+        $this->extension->onPostSend(new PostSend($message, $this, $this->driver));
     }
 }
diff --git a/Tests/Client/Extension/PrepareBodyExtensionTest.php b/Tests/Client/Extension/PrepareBodyExtensionTest.php
new file mode 100644
index 0000000..e99e3e3
--- /dev/null
+++ b/Tests/Client/Extension/PrepareBodyExtensionTest.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace Enqueue\Tests\Client\Extension;
+
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Extension\PrepareBodyExtension;
+use Enqueue\Client\ExtensionInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\PreSend;
+use Enqueue\Client\ProducerInterface;
+use PHPUnit\Framework\TestCase;
+
+class PrepareBodyExtensionTest extends TestCase
+{
+    public function testShouldImplementExtensionInterface()
+    {
+        $rc = new \ReflectionClass(PrepareBodyExtension::class);
+
+        $this->assertTrue($rc->implementsInterface(ExtensionInterface::class));
+    }
+
+    public function testCouldConstructedWithoutAnyArguments()
+    {
+        new PrepareBodyExtension();
+    }
+
+    /**
+     * @dataProvider provideMessages
+     *
+     * @param mixed      $body
+     * @param null|mixed $contentType
+     */
+    public function testShouldSendStringUnchangedAndAddPlainTextContentTypeIfEmpty(
+        $body,
+        $contentType,
+        string $expectedBody,
+        string $expectedContentType
+    ) {
+        $message = new Message($body);
+        $message->setContentType($contentType);
+
+        $context = $this->createDummyPreSendContext('aTopic', $message);
+
+        $extension = new PrepareBodyExtension();
+
+        $extension->onPreSendEvent($context);
+
+        $this->assertSame($expectedBody, $message->getBody());
+        $this->assertSame($expectedContentType, $message->getContentType());
+    }
+
+    public function testThrowIfBodyIsObject()
+    {
+        $message = new Message(new \stdClass());
+
+        $context = $this->createDummyPreSendContext('aTopic', $message);
+
+        $extension = new PrepareBodyExtension();
+
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The message\'s body must be either null, scalar, array or object (implements \JsonSerializable). Got: stdClass');
+
+        $extension->onPreSendEvent($context);
+    }
+
+    public function testThrowIfBodyIsArrayWithObjectsInsideOnSend()
+    {
+        $message = new Message(['foo' => new \stdClass()]);
+
+        $context = $this->createDummyPreSendContext('aTopic', $message);
+
+        $extension = new PrepareBodyExtension();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The message\'s body must be an array of scalars. Found not scalar in the array: stdClass');
+
+        $extension->onPreSendEvent($context);
+    }
+
+    public function testShouldThrowExceptionIfBodyIsArrayWithObjectsInSubArraysInsideOnSend()
+    {
+        $message = new Message(['foo' => ['bar' => new \stdClass()]]);
+
+        $context = $this->createDummyPreSendContext('aTopic', $message);
+
+        $extension = new PrepareBodyExtension();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The message\'s body must be an array of scalars. Found not scalar in the array: stdClass');
+
+        $extension->onPreSendEvent($context);
+    }
+
+    public static function provideMessages()
+    {
+        yield ['theBody', null, 'theBody', 'text/plain'];
+
+        yield ['theBody', 'foo/bar', 'theBody', 'foo/bar'];
+
+        yield [12345, null, '12345', 'text/plain'];
+
+        yield [12345, 'foo/bar', '12345', 'foo/bar'];
+
+        yield [12.345, null, '12.345', 'text/plain'];
+
+        yield [12.345, 'foo/bar', '12.345', 'foo/bar'];
+
+        yield [true, null, '1', 'text/plain'];
+
+        yield [true, 'foo/bar', '1', 'foo/bar'];
+
+        yield [null, null, '', 'text/plain'];
+
+        yield [null, 'foo/bar', '', 'foo/bar'];
+
+        yield [['foo' => 'fooVal'], null, '{"foo":"fooVal"}', 'application/json'];
+
+        yield [['foo' => 'fooVal'], 'foo/bar', '{"foo":"fooVal"}', 'foo/bar'];
+
+        yield [new JsonSerializableObject(), null, '{"foo":"fooVal"}', 'application/json'];
+
+        yield [new JsonSerializableObject(), 'foo/bar', '{"foo":"fooVal"}', 'foo/bar'];
+    }
+
+    private function createDummyPreSendContext($commandOrTopic, $message): PreSend
+    {
+        return new PreSend(
+            $commandOrTopic,
+            $message,
+            $this->createMock(ProducerInterface::class),
+            $this->createMock(DriverInterface::class)
+        );
+    }
+}
+
+class JsonSerializableObject implements \JsonSerializable
+{
+    public function jsonSerialize()
+    {
+        return ['foo' => 'fooVal'];
+    }
+}
diff --git a/Tests/Client/ProducerTest.php b/Tests/Client/ProducerTest.php
index c9a30fc..be8554c 100644
--- a/Tests/Client/ProducerTest.php
+++ b/Tests/Client/ProducerTest.php
@@ -9,7 +9,6 @@
 use Enqueue\Client\MessagePriority;
 use Enqueue\Client\Producer;
 use Enqueue\Client\ProducerInterface;
-use Enqueue\Null\NullQueue;
 use Enqueue\Rpc\RpcFactory;
 use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
@@ -154,207 +153,6 @@ public function testShouldSendMessageWithCustomTimestamp()
         self::assertSame('theCustomTimestamp', $message->getTimestamp());
     }
 
-    public function testShouldSendStringAsPlainText()
-    {
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->once())
-            ->method('sendToRouter')
-            ->willReturnCallback(function (Message $message) {
-                self::assertSame('theStringMessage', $message->getBody());
-                self::assertSame('text/plain', $message->getContentType());
-            })
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent('topic', 'theStringMessage');
-    }
-
-    public function testShouldSendArrayAsJsonString()
-    {
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->once())
-            ->method('sendToRouter')
-            ->willReturnCallback(function (Message $message) {
-                self::assertSame('{"foo":"fooVal"}', $message->getBody());
-                self::assertSame('application/json', $message->getContentType());
-            })
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent('topic', ['foo' => 'fooVal']);
-    }
-
-    public function testShouldConvertMessageArrayBodyJsonString()
-    {
-        $message = new Message();
-        $message->setBody(['foo' => 'fooVal']);
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->once())
-            ->method('sendToRouter')
-            ->willReturnCallback(function (Message $message) {
-                self::assertSame('{"foo":"fooVal"}', $message->getBody());
-                self::assertSame('application/json', $message->getContentType());
-            })
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent('topic', $message);
-    }
-
-    public function testSendShouldForceScalarsToStringAndSetTextContentType()
-    {
-        $queue = new NullQueue('');
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->once())
-            ->method('sendToRouter')
-            ->willReturnCallback(function (Message $message) {
-                self::assertEquals('text/plain', $message->getContentType());
-
-                self::assertInternalType('string', $message->getBody());
-                self::assertEquals('12345', $message->getBody());
-            })
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent($queue, 12345);
-    }
-
-    public function testSendShouldForceMessageScalarsBodyToStringAndSetTextContentType()
-    {
-        $queue = new NullQueue('');
-
-        $message = new Message();
-        $message->setBody(12345);
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->once())
-            ->method('sendToRouter')
-            ->willReturnCallback(function (Message $message) {
-                self::assertEquals('text/plain', $message->getContentType());
-
-                self::assertInternalType('string', $message->getBody());
-                self::assertEquals('12345', $message->getBody());
-            })
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent($queue, $message);
-    }
-
-    public function testSendShouldForceNullToEmptyStringAndSetTextContentType()
-    {
-        $queue = new NullQueue('');
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->once())
-            ->method('sendToRouter')
-            ->willReturnCallback(function (Message $message) {
-                self::assertEquals('text/plain', $message->getContentType());
-
-                self::assertInternalType('string', $message->getBody());
-                self::assertEquals('', $message->getBody());
-            })
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent($queue, null);
-    }
-
-    public function testSendShouldForceNullBodyToEmptyStringAndSetTextContentType()
-    {
-        $queue = new NullQueue('');
-
-        $message = new Message();
-        $message->setBody(null);
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->once())
-            ->method('sendToRouter')
-            ->willReturnCallback(function (Message $message) {
-                self::assertEquals('text/plain', $message->getContentType());
-
-                self::assertInternalType('string', $message->getBody());
-                self::assertEquals('', $message->getBody());
-            })
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent($queue, $message);
-    }
-
-    public function testShouldThrowExceptionIfBodyIsObjectOnSend()
-    {
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->never())
-            ->method('sendToRouter')
-        ;
-        $driver
-            ->expects($this->never())
-            ->method('sendToProcessor')
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-
-        $this->expectException(\InvalidArgumentException::class);
-        $this->expectExceptionMessage('The message\'s body must be either null, scalar, array or object (implements \JsonSerializable). Got: stdClass');
-
-        $producer->sendEvent('topic', new \stdClass());
-    }
-
-    public function testShouldThrowExceptionIfBodyIsArrayWithObjectsInsideOnSend()
-    {
-        $queue = new NullQueue('queue');
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->never())
-            ->method('sendToRouter')
-        ;
-        $driver
-            ->expects($this->never())
-            ->method('sendToProcessor')
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The message\'s body must be an array of scalars. Found not scalar in the array: stdClass');
-
-        $producer->sendEvent($queue, ['foo' => new \stdClass()]);
-    }
-
-    public function testShouldThrowExceptionIfBodyIsArrayWithObjectsInSubArraysInsideOnSend()
-    {
-        $queue = new NullQueue('queue');
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->never())
-            ->method('sendToRouter')
-        ;
-        $driver
-            ->expects($this->never())
-            ->method('sendToProcessor')
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The message\'s body must be an array of scalars. Found not scalar in the array: stdClass');
-
-        $producer->sendEvent($queue, ['foo' => ['bar' => new \stdClass()]]);
-    }
-
     public function testShouldSendJsonSerializableObjectAsJsonStringToMessageBus()
     {
         $object = new JsonSerializableObject();
@@ -444,31 +242,6 @@ public function testThrowIfTryToSendMessageToMessageBusWithProcessorQueueNamePro
         $producer->sendEvent('topic', $message);
     }
 
-    public function testThrowIfNotApplicationJsonContentTypeSetWithJsonSerializableBody()
-    {
-        $object = new JsonSerializableObject();
-
-        $message = new Message();
-        $message->setBody($object);
-        $message->setContentType('foo/bar');
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->never())
-            ->method('sendToRouter')
-        ;
-        $driver
-            ->expects($this->never())
-            ->method('sendToProcessor')
-        ;
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Content type "application/json" only allowed when body is array');
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent('topic', $message);
-    }
-
     public function testShouldSendMessageToApplicationRouter()
     {
         $message = new Message();

From 6d9282f010598d901323aca3e9bfe03ae8f6d662 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 27 Aug 2018 23:19:51 +0300
Subject: [PATCH 089/337] rename onPreDriverSend to onDriverPreSend. split
 getCommandOrTopic into two getters.

---
 Client/ChainExtension.php                        |  4 ++--
 .../ExclusiveCommandExtension.php                |  2 +-
 Client/{PreDriverSend.php => DriverPreSend.php}  |  2 +-
 Client/EmptyExtensionTrait.php                   |  2 +-
 Client/ExtensionInterface.php                    |  2 +-
 Client/PreSend.php                               | 16 +++++++++++++---
 Client/Producer.php                              |  8 ++++----
 7 files changed, 23 insertions(+), 13 deletions(-)
 rename Client/{PreDriverSend.php => DriverPreSend.php} (98%)

diff --git a/Client/ChainExtension.php b/Client/ChainExtension.php
index 01bee22..842f83b 100644
--- a/Client/ChainExtension.php
+++ b/Client/ChainExtension.php
@@ -31,10 +31,10 @@ public function onPreSendCommand(PreSend $event): void
         }
     }
 
-    public function onPreDriverSend(PreDriverSend $context): void
+    public function onDriverPreSend(DriverPreSend $context): void
     {
         foreach ($this->extensions as $extension) {
-            $extension->onPreDriverSend($context);
+            $extension->onDriverPreSend($context);
         }
     }
 
diff --git a/Client/ConsumptionExtension/ExclusiveCommandExtension.php b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
index add6ef7..63fe96c 100644
--- a/Client/ConsumptionExtension/ExclusiveCommandExtension.php
+++ b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
@@ -64,7 +64,7 @@ public function onPreReceived(Context $context)
     public function onPreSendCommand(PreSend $context): void
     {
         $message = $context->getMessage();
-        $command = $context->getCommandOrTopic();
+        $command = $context->getCommand();
 
         if (array_key_exists($command, $this->processorNameToQueueNameMap)) {
             $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $command);
diff --git a/Client/PreDriverSend.php b/Client/DriverPreSend.php
similarity index 98%
rename from Client/PreDriverSend.php
rename to Client/DriverPreSend.php
index 1ea5804..076123d 100644
--- a/Client/PreDriverSend.php
+++ b/Client/DriverPreSend.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Client;
 
-class PreDriverSend
+class DriverPreSend
 {
     private $message;
 
diff --git a/Client/EmptyExtensionTrait.php b/Client/EmptyExtensionTrait.php
index baf7f56..2b4f976 100644
--- a/Client/EmptyExtensionTrait.php
+++ b/Client/EmptyExtensionTrait.php
@@ -12,7 +12,7 @@ public function onPreSendCommand(PreSend $context): void
     {
     }
 
-    public function onPreDriverSend(PreDriverSend $context): void
+    public function onDriverPreSend(DriverPreSend $context): void
     {
     }
 
diff --git a/Client/ExtensionInterface.php b/Client/ExtensionInterface.php
index deb7d3f..1a6b27d 100644
--- a/Client/ExtensionInterface.php
+++ b/Client/ExtensionInterface.php
@@ -8,7 +8,7 @@ public function onPreSendEvent(PreSend $context): void;
 
     public function onPreSendCommand(PreSend $context): void;
 
-    public function onPreDriverSend(PreDriverSend $context): void;
+    public function onDriverPreSend(DriverPreSend $context): void;
 
     public function onPostSend(PostSend $context): void;
 
diff --git a/Client/PreSend.php b/Client/PreSend.php
index b37ce5d..6ebbd75 100644
--- a/Client/PreSend.php
+++ b/Client/PreSend.php
@@ -28,14 +28,24 @@ public function __construct(
         $this->originalMessage = clone $message;
     }
 
-    public function getCommandOrTopic(): string
+    public function getCommand(): string
     {
         return $this->commandOrTopic;
     }
 
-    public function changeCommandOrTopic(string $commandOrTopic): void
+    public function getTopic(): string
     {
-        $this->commandOrTopic = $commandOrTopic;
+        return $this->commandOrTopic;
+    }
+
+    public function changeCommand(string $newCommand): void
+    {
+        $this->commandOrTopic = $newCommand;
+    }
+
+    public function changeTopic(string $newTopic): void
+    {
+        $this->commandOrTopic = $newTopic;
     }
 
     public function changeBody($body, string $contentType = null): void
diff --git a/Client/Producer.php b/Client/Producer.php
index 1595783..a99610a 100644
--- a/Client/Producer.php
+++ b/Client/Producer.php
@@ -51,7 +51,7 @@ public function sendEvent($topic, $message)
         $preSend = new PreSend($topic, $message, $this, $this->driver);
         $this->extension->onPreSendEvent($preSend);
 
-        $topic = $preSend->getCommandOrTopic();
+        $topic = $preSend->getTopic();
         $message = $preSend->getMessage();
 
         $message->setProperty(Config::PARAMETER_TOPIC_NAME, $topic);
@@ -68,7 +68,7 @@ public function sendCommand($command, $message, $needReply = false)
         $preSend = new PreSend($command, $message, $this, $this->driver);
         $this->extension->onPreSendEvent($preSend);
 
-        $command = $preSend->getCommandOrTopic();
+        $command = $preSend->getCommand();
         $message = $preSend->getMessage();
 
         $deleteReplyQueue = false;
@@ -137,7 +137,7 @@ private function doSend(Message $message)
                 throw new \LogicException(sprintf('The %s property must not be set for messages that are sent to message bus.', Config::PARAMETER_PROCESSOR_NAME));
             }
 
-            $this->extension->onPreDriverSend(new PreDriverSend($message, $this, $this->driver));
+            $this->extension->onPreDriverSend(new DriverPreSend($message, $this, $this->driver));
             $this->driver->sendToRouter($message);
         } elseif (Message::SCOPE_APP == $message->getScope()) {
             if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
@@ -147,7 +147,7 @@ private function doSend(Message $message)
                 $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $this->driver->getConfig()->getRouterQueueName());
             }
 
-            $this->extension->onPreDriverSend(new PreDriverSend($message, $this, $this->driver));
+            $this->extension->onPreDriverSend(new DriverPreSend($message, $this, $this->driver));
             $this->driver->sendToRouter($message);
         } else {
             throw new \LogicException(sprintf('The message scope "%s" is not supported.', $message->getScope()));

From b2ca7189eca2c8cead8b57d87aece690940f8462 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 30 Aug 2018 13:05:11 +0300
Subject: [PATCH 090/337] Add tests.

---
 Client/ChainExtension.php                     |   6 +-
 .../ExclusiveCommandExtension.php             |   2 +-
 Client/DriverPreSend.php                      |   2 +-
 Client/PostSend.php                           |   2 +-
 Client/PreSend.php                            |   2 +-
 Client/Producer.php                           |  26 +-
 Tests/Client/ChainExtensionTest.php           | 104 +++-
 .../ExclusiveCommandExtensionTest.php         |  26 +-
 .../Extension/PrepareBodyExtensionTest.php    |   9 +-
 Tests/Client/ProducerSendCommandTest.php      | 508 ++++++++++++++++
 Tests/Client/ProducerSendEventTest.php        | 546 ++++++++++++++++++
 Tests/Client/ProducerTest.php                 | 388 +------------
 .../CustomPrepareBodyClientExtension.php      |  22 +
 Tests/Mocks/JsonSerializableObject.php        |  11 +
 14 files changed, 1235 insertions(+), 419 deletions(-)
 create mode 100644 Tests/Client/ProducerSendCommandTest.php
 create mode 100644 Tests/Client/ProducerSendEventTest.php
 create mode 100644 Tests/Mocks/CustomPrepareBodyClientExtension.php
 create mode 100644 Tests/Mocks/JsonSerializableObject.php

diff --git a/Client/ChainExtension.php b/Client/ChainExtension.php
index 842f83b..603b2b6 100644
--- a/Client/ChainExtension.php
+++ b/Client/ChainExtension.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Client;
 
-class ChainExtension implements ExtensionInterface
+final class ChainExtension implements ExtensionInterface
 {
     /**
      * @var ExtensionInterface[]
@@ -14,7 +14,9 @@ class ChainExtension implements ExtensionInterface
      */
     public function __construct(array $extensions)
     {
-        $this->extensions = $extensions;
+        array_walk($extensions, function (ExtensionInterface $extension) {
+            $this->extensions[] = $extension;
+        });
     }
 
     public function onPreSendEvent(PreSend $event): void
diff --git a/Client/ConsumptionExtension/ExclusiveCommandExtension.php b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
index 63fe96c..824a244 100644
--- a/Client/ConsumptionExtension/ExclusiveCommandExtension.php
+++ b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
@@ -10,7 +10,7 @@
 use Enqueue\Consumption\EmptyExtensionTrait as ConsumptionEmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface as ConsumptionExtensionInterface;
 
-class ExclusiveCommandExtension implements ConsumptionExtensionInterface, ClientExtensionInterface
+final class ExclusiveCommandExtension implements ConsumptionExtensionInterface, ClientExtensionInterface
 {
     use ConsumptionEmptyExtensionTrait, ClientEmptyExtensionTrait;
 
diff --git a/Client/DriverPreSend.php b/Client/DriverPreSend.php
index 076123d..8a298cb 100644
--- a/Client/DriverPreSend.php
+++ b/Client/DriverPreSend.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Client;
 
-class DriverPreSend
+final class DriverPreSend
 {
     private $message;
 
diff --git a/Client/PostSend.php b/Client/PostSend.php
index f99d781..2e0ae62 100644
--- a/Client/PostSend.php
+++ b/Client/PostSend.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Client;
 
-class PostSend
+final class PostSend
 {
     private $message;
 
diff --git a/Client/PreSend.php b/Client/PreSend.php
index 6ebbd75..afd6401 100644
--- a/Client/PreSend.php
+++ b/Client/PreSend.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Client;
 
-class PreSend
+final class PreSend
 {
     private $message;
 
diff --git a/Client/Producer.php b/Client/Producer.php
index a99610a..2e74df0 100644
--- a/Client/Producer.php
+++ b/Client/Producer.php
@@ -6,12 +6,12 @@
 use Enqueue\Rpc\RpcFactory;
 use Enqueue\Util\UUID;
 
-class Producer implements ProducerInterface
+final class Producer implements ProducerInterface
 {
     /**
      * @var DriverInterface
      */
-    protected $driver;
+    private $driver;
 
     /**
      * @var ExtensionInterface
@@ -23,13 +23,6 @@ class Producer implements ProducerInterface
      */
     private $rpcFactory;
 
-    /**
-     * @param DriverInterface         $driver
-     * @param ExtensionInterface|null $extension
-     * @param RpcFactory              $rpcFactory
-     *
-     * @internal param RpcClient $rpcClient
-     */
     public function __construct(
         DriverInterface $driver,
         RpcFactory $rpcFactory,
@@ -38,8 +31,10 @@ public function __construct(
         $this->driver = $driver;
         $this->rpcFactory = $rpcFactory;
 
-        $prepareBodyExtension = new PrepareBodyExtension();
-        $this->extension = new ChainExtension([$extension, $prepareBodyExtension]) ?: new ChainExtension([$prepareBodyExtension]);
+        $this->extension = $extension ?
+            new ChainExtension([$extension, new PrepareBodyExtension()]) :
+            new ChainExtension([new PrepareBodyExtension()])
+        ;
     }
 
     public function sendEvent($topic, $message)
@@ -66,7 +61,7 @@ public function sendCommand($command, $message, $needReply = false)
         }
 
         $preSend = new PreSend($command, $message, $this, $this->driver);
-        $this->extension->onPreSendEvent($preSend);
+        $this->extension->onPreSendCommand($preSend);
 
         $command = $preSend->getCommand();
         $message = $preSend->getMessage();
@@ -87,7 +82,6 @@ public function sendCommand($command, $message, $needReply = false)
 
         $message->setProperty(Config::PARAMETER_TOPIC_NAME, Config::COMMAND_TOPIC);
         $message->setProperty(Config::PARAMETER_COMMAND_NAME, $command);
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, Config::COMMAND_TOPIC);
         $message->setScope(Message::SCOPE_APP);
 
         $this->doSend($message);
@@ -137,7 +131,7 @@ private function doSend(Message $message)
                 throw new \LogicException(sprintf('The %s property must not be set for messages that are sent to message bus.', Config::PARAMETER_PROCESSOR_NAME));
             }
 
-            $this->extension->onPreDriverSend(new DriverPreSend($message, $this, $this->driver));
+            $this->extension->onDriverPreSend(new DriverPreSend($message, $this, $this->driver));
             $this->driver->sendToRouter($message);
         } elseif (Message::SCOPE_APP == $message->getScope()) {
             if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
@@ -147,8 +141,8 @@ private function doSend(Message $message)
                 $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $this->driver->getConfig()->getRouterQueueName());
             }
 
-            $this->extension->onPreDriverSend(new DriverPreSend($message, $this, $this->driver));
-            $this->driver->sendToRouter($message);
+            $this->extension->onDriverPreSend(new DriverPreSend($message, $this, $this->driver));
+            $this->driver->sendToProcessor($message);
         } else {
             throw new \LogicException(sprintf('The message scope "%s" is not supported.', $message->getScope()));
         }
diff --git a/Tests/Client/ChainExtensionTest.php b/Tests/Client/ChainExtensionTest.php
index 3b1d82f..9268c7b 100644
--- a/Tests/Client/ChainExtensionTest.php
+++ b/Tests/Client/ChainExtensionTest.php
@@ -3,8 +3,13 @@
 namespace Enqueue\Tests\Client;
 
 use Enqueue\Client\ChainExtension;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\DriverPreSend;
 use Enqueue\Client\ExtensionInterface;
 use Enqueue\Client\Message;
+use Enqueue\Client\PostSend;
+use Enqueue\Client\PreSend;
+use Enqueue\Client\ProducerInterface;
 use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
 
@@ -17,53 +22,128 @@ public function testShouldImplementExtensionInterface()
         $this->assertClassImplements(ExtensionInterface::class, ChainExtension::class);
     }
 
+    public function testShouldBeFinal()
+    {
+        $this->assertClassFinal(ChainExtension::class);
+    }
+
     public function testCouldBeConstructedWithExtensionsArray()
     {
         new ChainExtension([$this->createExtension(), $this->createExtension()]);
     }
 
-    public function testShouldProxyOnPreSendToAllInternalExtensions()
+    public function testThrowIfArrayContainsNotExtension()
+    {
+        $this->expectException(\TypeError::class);
+        $this->expectExceptionMessage('Argument 1 passed to');
+
+        new ChainExtension([$this->createExtension(), new \stdClass()]);
+    }
+
+    public function testShouldProxyOnPreSendEventToAllInternalExtensions()
+    {
+        $preSend = new PreSend(
+            'aCommandOrTopic',
+            new Message(),
+            $this->createMock(ProducerInterface::class),
+            $this->createMock(DriverInterface::class)
+        );
+
+        $fooExtension = $this->createExtension();
+        $fooExtension
+            ->expects($this->once())
+            ->method('onPreSendEvent')
+            ->with($this->identicalTo($preSend))
+        ;
+        $barExtension = $this->createExtension();
+        $barExtension
+            ->expects($this->once())
+            ->method('onPreSendEvent')
+            ->with($this->identicalTo($preSend))
+        ;
+
+        $extensions = new ChainExtension([$fooExtension, $barExtension]);
+
+        $extensions->onPreSendEvent($preSend);
+    }
+
+    public function testShouldProxyOnPreSendCommandToAllInternalExtensions()
+    {
+        $preSend = new PreSend(
+            'aCommandOrTopic',
+            new Message(),
+            $this->createMock(ProducerInterface::class),
+            $this->createMock(DriverInterface::class)
+        );
+
+        $fooExtension = $this->createExtension();
+        $fooExtension
+            ->expects($this->once())
+            ->method('onPreSendCommand')
+            ->with($this->identicalTo($preSend))
+        ;
+        $barExtension = $this->createExtension();
+        $barExtension
+            ->expects($this->once())
+            ->method('onPreSendCommand')
+            ->with($this->identicalTo($preSend))
+        ;
+
+        $extensions = new ChainExtension([$fooExtension, $barExtension]);
+
+        $extensions->onPreSendCommand($preSend);
+    }
+
+    public function testShouldProxyOnDriverPreSendToAllInternalExtensions()
     {
-        $message = new Message();
+        $driverPreSend = new DriverPreSend(
+            new Message(),
+            $this->createMock(ProducerInterface::class),
+            $this->createMock(DriverInterface::class)
+        );
 
         $fooExtension = $this->createExtension();
         $fooExtension
             ->expects($this->once())
-            ->method('onPreSend')
-            ->with('topic', $this->identicalTo($message))
+            ->method('onDriverPreSend')
+            ->with($this->identicalTo($driverPreSend))
         ;
         $barExtension = $this->createExtension();
         $barExtension
             ->expects($this->once())
-            ->method('onPreSend')
-            ->with('topic', $this->identicalTo($message))
+            ->method('onDriverPreSend')
+            ->with($this->identicalTo($driverPreSend))
         ;
 
         $extensions = new ChainExtension([$fooExtension, $barExtension]);
 
-        $extensions->onPreSend('topic', $message);
+        $extensions->onDriverPreSend($driverPreSend);
     }
 
-    public function testShouldProxyOnPostSendToAllInternalExtensions()
+    public function testShouldProxyOnPostSentToAllInternalExtensions()
     {
-        $message = new Message();
+        $postSend = new PostSend(
+            new Message(),
+            $this->createMock(ProducerInterface::class),
+            $this->createMock(DriverInterface::class)
+        );
 
         $fooExtension = $this->createExtension();
         $fooExtension
             ->expects($this->once())
             ->method('onPostSend')
-            ->with('topic', $this->identicalTo($message))
+            ->with($this->identicalTo($postSend))
         ;
         $barExtension = $this->createExtension();
         $barExtension
             ->expects($this->once())
             ->method('onPostSend')
-            ->with('topic', $this->identicalTo($message))
+            ->with($this->identicalTo($postSend))
         ;
 
         $extensions = new ChainExtension([$fooExtension, $barExtension]);
 
-        $extensions->onPostSend('topic', $message);
+        $extensions->onPostSend($postSend);
     }
 
     /**
diff --git a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
index acbcb41..c5eb0bb 100644
--- a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
@@ -4,8 +4,11 @@
 
 use Enqueue\Client\Config;
 use Enqueue\Client\ConsumptionExtension\ExclusiveCommandExtension;
+use Enqueue\Client\DriverInterface;
 use Enqueue\Client\ExtensionInterface as ClientExtensionInterface;
 use Enqueue\Client\Message;
+use Enqueue\Client\PreSend;
+use Enqueue\Client\ProducerInterface;
 use Enqueue\Consumption\Context;
 use Enqueue\Consumption\ExtensionInterface as ConsumptionExtensionInterface;
 use Enqueue\Null\NullContext;
@@ -24,6 +27,11 @@ public function testShouldImplementConsumptionExtensionInterface()
         $this->assertClassImplements(ConsumptionExtensionInterface::class, ExclusiveCommandExtension::class);
     }
 
+    public function testShouldBeFinal()
+    {
+        $this->assertClassFinal(ExclusiveCommandExtension::class);
+    }
+
     public function testShouldImplementClientExtensionInterface()
     {
         $this->assertClassImplements(ClientExtensionInterface::class, ExclusiveCommandExtension::class);
@@ -145,7 +153,7 @@ public function testShouldSetCommandPropertiesIfCurrentQueueInTheMap()
         ], $message->getProperties());
     }
 
-    public function testShouldDoNothingOnPreSendIfTopicNotCommandOne()
+    public function testShouldDoNothingOnPreSendEvent()
     {
         $message = new Message();
 
@@ -153,7 +161,7 @@ public function testShouldDoNothingOnPreSendIfTopicNotCommandOne()
             'aFooQueueName' => 'aFooProcessorName',
         ]);
 
-        $extension->onPreSend('aTopic', $message);
+        $extension->onPreSendEvent($this->createDummyPreSend('aTopic', $message));
 
         $this->assertEquals([], $message->getProperties());
     }
@@ -167,7 +175,7 @@ public function testShouldDoNothingIfCommandNotExclusive()
             'aFooQueueName' => 'aFooProcessorName',
         ]);
 
-        $extension->onPreSend(Config::COMMAND_TOPIC, $message);
+        $extension->onPreSendCommand($this->createDummyPreSend('theBarProcessorName', $message));
 
         $this->assertEquals([
             'enqueue.command_name' => 'theBarProcessorName',
@@ -183,7 +191,7 @@ public function testShouldForceExclusiveCommandQueue()
             'aFooQueueName' => 'aFooProcessorName',
         ]);
 
-        $extension->onPreSend(Config::COMMAND_TOPIC, $message);
+        $extension->onPreSendCommand($this->createDummyPreSend('aFooProcessorName', $message));
 
         $this->assertEquals([
             'enqueue.command_name' => 'aFooProcessorName',
@@ -191,4 +199,14 @@ public function testShouldForceExclusiveCommandQueue()
             'enqueue.processor_queue_name' => 'aFooQueueName',
         ], $message->getProperties());
     }
+
+    private function createDummyPreSend(string $commandOrTopic, Message $message): PreSend
+    {
+        return new PreSend(
+            $commandOrTopic,
+            $message,
+            $this->createMock(ProducerInterface::class),
+            $this->createMock(DriverInterface::class)
+        );
+    }
 }
diff --git a/Tests/Client/Extension/PrepareBodyExtensionTest.php b/Tests/Client/Extension/PrepareBodyExtensionTest.php
index e99e3e3..b1fd985 100644
--- a/Tests/Client/Extension/PrepareBodyExtensionTest.php
+++ b/Tests/Client/Extension/PrepareBodyExtensionTest.php
@@ -8,6 +8,7 @@
 use Enqueue\Client\Message;
 use Enqueue\Client\PreSend;
 use Enqueue\Client\ProducerInterface;
+use Enqueue\Tests\Mocks\JsonSerializableObject;
 use PHPUnit\Framework\TestCase;
 
 class PrepareBodyExtensionTest extends TestCase
@@ -132,11 +133,3 @@ private function createDummyPreSendContext($commandOrTopic, $message): PreSend
         );
     }
 }
-
-class JsonSerializableObject implements \JsonSerializable
-{
-    public function jsonSerialize()
-    {
-        return ['foo' => 'fooVal'];
-    }
-}
diff --git a/Tests/Client/ProducerSendCommandTest.php b/Tests/Client/ProducerSendCommandTest.php
new file mode 100644
index 0000000..057d227
--- /dev/null
+++ b/Tests/Client/ProducerSendCommandTest.php
@@ -0,0 +1,508 @@
+<?php
+
+namespace Enqueue\Tests\Client;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\DriverPreSend;
+use Enqueue\Client\ExtensionInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\PostSend;
+use Enqueue\Client\PreSend;
+use Enqueue\Client\Producer;
+use Enqueue\Rpc\Promise;
+use Enqueue\Rpc\RpcFactory;
+use Enqueue\Test\ClassExtensionTrait;
+use Enqueue\Tests\Mocks\CustomPrepareBodyClientExtension;
+use PHPUnit\Framework\TestCase;
+
+class ProducerSendCommandTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldSendCommandToProcessor()
+    {
+        $message = new Message();
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+            ->with(self::identicalTo($message))
+        ;
+        $driver
+            ->expects($this->never())
+            ->method('sendToRouter')
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendCommand('command', $message);
+
+        $expectedProperties = [
+            'enqueue.processor_name' => 'a_router_processor_name',
+            'enqueue.topic_name' => '__command__',
+            'enqueue.command_name' => 'command',
+            'enqueue.processor_queue_name' => 'a_router_queue',
+        ];
+
+        self::assertEquals($expectedProperties, $message->getProperties());
+    }
+
+    public function testShouldSendCommandWithReply()
+    {
+        $message = new Message();
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+            ->with(self::identicalTo($message))
+        ;
+        $driver
+            ->expects($this->never())
+            ->method('sendToRouter')
+        ;
+
+        $expectedPromiseMock = $this->createMock(Promise::class);
+
+        $rpcFactoryMock = $this->createRpcFactoryMock();
+        $rpcFactoryMock
+            ->expects($this->once())
+            ->method('createReplyTo')
+            ->willReturn('theReplyQueue')
+        ;
+        $rpcFactoryMock
+            ->expects($this->once())
+            ->method('createPromise')
+            ->with(
+                'theReplyQueue',
+                $this->logicalNot($this->isEmpty()),
+                60000
+            )
+            ->willReturn($expectedPromiseMock)
+        ;
+
+        $producer = new Producer($driver, $rpcFactoryMock);
+        $actualPromise = $producer->sendCommand('command', $message, true);
+
+        $this->assertSame($expectedPromiseMock, $actualPromise);
+
+        self::assertEquals('theReplyQueue', $message->getReplyTo());
+        self::assertNotEmpty($message->getCorrelationId());
+    }
+
+    public function testShouldSendCommandWithReplyAndCustomReplyQueueAndCorrelationId()
+    {
+        $message = new Message();
+        $message->setReplyTo('theCustomReplyQueue');
+        $message->setCorrelationId('theCustomCorrelationId');
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+            ->with(self::identicalTo($message))
+        ;
+        $driver
+            ->expects($this->never())
+            ->method('sendToRouter')
+        ;
+
+        $expectedPromiseMock = $this->createMock(Promise::class);
+
+        $rpcFactoryMock = $this->createRpcFactoryMock();
+        $rpcFactoryMock
+            ->expects($this->never())
+            ->method('createReplyTo')
+        ;
+        $rpcFactoryMock
+            ->expects($this->once())
+            ->method('createPromise')
+            ->with(
+                'theCustomReplyQueue',
+                'theCustomCorrelationId',
+                60000
+            )
+            ->willReturn($expectedPromiseMock)
+        ;
+
+        $producer = new Producer($driver, $rpcFactoryMock);
+        $actualPromise = $producer->sendCommand('command', $message, true);
+
+        $this->assertSame($expectedPromiseMock, $actualPromise);
+
+        self::assertEquals('theCustomReplyQueue', $message->getReplyTo());
+        self::assertSame('theCustomCorrelationId', $message->getCorrelationId());
+    }
+
+    public function testShouldOverwriteExpectedMessageProperties()
+    {
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topicShouldBeOverwritten');
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'topicShouldBeOverwritten');
+        $message->setScope('topicShouldBeOverwritten');
+
+        $driver = $this->createDriverStub();
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendCommand('expectedCommand', $message);
+
+        $expectedProperties = [
+            'enqueue.processor_name' => 'a_router_processor_name',
+            'enqueue.topic_name' => '__command__',
+            'enqueue.command_name' => 'expectedCommand',
+            'enqueue.processor_queue_name' => 'a_router_queue',
+        ];
+
+        self::assertEquals($expectedProperties, $message->getProperties());
+        self::assertSame(Message::SCOPE_APP, $message->getScope());
+    }
+
+    public function testShouldSendCommandWithNormalPriorityByDefault()
+    {
+        $message = new Message();
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+            ->with(self::identicalTo($message))
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendCommand('command', $message);
+
+        self::assertSame(MessagePriority::NORMAL, $message->getPriority());
+    }
+
+    public function testShouldSendCommandWithCustomPriority()
+    {
+        $message = new Message();
+        $message->setPriority(MessagePriority::HIGH);
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+            ->with(self::identicalTo($message))
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendCommand('command', $message);
+
+        self::assertSame(MessagePriority::HIGH, $message->getPriority());
+    }
+
+    public function testShouldSendCommandWithGeneratedMessageId()
+    {
+        $message = new Message();
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+            ->with(self::identicalTo($message))
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendCommand('command', $message);
+
+        self::assertNotEmpty($message->getMessageId());
+    }
+
+    public function testShouldSendCommandWithCustomMessageId()
+    {
+        $message = new Message();
+        $message->setMessageId('theCustomMessageId');
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+            ->with(self::identicalTo($message))
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendCommand('command', $message);
+
+        self::assertSame('theCustomMessageId', $message->getMessageId());
+    }
+
+    public function testShouldSendCommandWithGeneratedTimestamp()
+    {
+        $message = new Message();
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+            ->with(self::identicalTo($message))
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendCommand('command', $message);
+
+        self::assertNotEmpty($message->getTimestamp());
+    }
+
+    public function testShouldSendCommandWithCustomTimestamp()
+    {
+        $message = new Message();
+        $message->setTimestamp('theCustomTimestamp');
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+            ->with(self::identicalTo($message))
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendCommand('command', $message);
+
+        self::assertSame('theCustomTimestamp', $message->getTimestamp());
+    }
+
+    public function testShouldSerializeMessageToJsonByDefault()
+    {
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+            ->willReturnCallback(function (Message $message) {
+                $this->assertSame('{"foo":"fooVal"}', $message->getBody());
+            })
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendCommand('command', ['foo' => 'fooVal']);
+    }
+
+    public function testShouldSerializeMessageByCustomExtension()
+    {
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+            ->willReturnCallback(function (Message $message) {
+                $this->assertSame('theCommandBodySerializedByCustomExtension', $message->getBody());
+            })
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock(), new CustomPrepareBodyClientExtension());
+        $producer->sendCommand('command', ['foo' => 'fooVal']);
+    }
+
+    public function testShouldSendCommandToApplicationRouter()
+    {
+        $message = new Message();
+        $message->setBody('aBody');
+        $message->setScope(Message::SCOPE_APP);
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+            ->willReturnCallback(function (Message $message) {
+                self::assertSame('aBody', $message->getBody());
+                self::assertSame('a_router_processor_name', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
+                self::assertSame('a_router_queue', $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME));
+            })
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendCommand('command', $message);
+    }
+
+    public function testShouldSendCommandWithCustomProcessorAndQueueNamePropertiesSetToApplicationRouter()
+    {
+        $message = new Message();
+        $message->setBody('aBody');
+        $message->setScope(Message::SCOPE_APP);
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aCustomProcessor');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aCustomProcessorQueue');
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+            ->willReturnCallback(function (Message $message) {
+                self::assertSame('aBody', $message->getBody());
+                self::assertSame('aCustomProcessor', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
+                self::assertSame('aCustomProcessorQueue', $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME));
+            })
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendCommand('command', $message);
+    }
+
+    public function testShouldCallPreSendCommandExtensionMethodWhenSendToBus()
+    {
+        $message = new Message();
+        $message->setBody('aBody');
+        $message->setScope(Message::SCOPE_MESSAGE_BUS);
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+        ;
+
+        $extension = $this->createMock(ExtensionInterface::class);
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
+
+        $extension
+            ->expects($this->at(0))
+            ->method('onPreSendCommand')
+            ->willReturnCallback(function (PreSend $context) use ($message, $producer, $driver) {
+                $this->assertSame($message, $context->getMessage());
+                $this->assertSame($producer, $context->getProducer());
+                $this->assertSame($driver, $context->getDriver());
+                $this->assertSame('command', $context->getCommand());
+
+                $this->assertEquals($message, $context->getOriginalMessage());
+                $this->assertNotSame($message, $context->getOriginalMessage());
+            });
+
+        $extension
+            ->expects($this->never())
+            ->method('onPreSendEvent')
+        ;
+
+        $producer->sendCommand('command', $message);
+    }
+
+    public function testShouldCallPreSendCommandExtensionMethodWhenSendToApplicationRouter()
+    {
+        $message = new Message();
+        $message->setBody('aBody');
+        $message->setScope(Message::SCOPE_APP);
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+        ;
+
+        $extension = $this->createMock(ExtensionInterface::class);
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
+
+        $extension
+            ->expects($this->at(0))
+            ->method('onPreSendCommand')
+            ->willReturnCallback(function (PreSend $context) use ($message, $producer, $driver) {
+                $this->assertSame($message, $context->getMessage());
+                $this->assertSame($producer, $context->getProducer());
+                $this->assertSame($driver, $context->getDriver());
+                $this->assertSame('command', $context->getCommand());
+
+                $this->assertEquals($message, $context->getOriginalMessage());
+                $this->assertNotSame($message, $context->getOriginalMessage());
+            });
+
+        $extension
+            ->expects($this->never())
+            ->method('onPreSendEvent')
+        ;
+
+        $producer->sendCommand('command', $message);
+    }
+
+    public function testShouldCallPreDriverSendExtensionMethod()
+    {
+        $message = new Message();
+        $message->setBody('aBody');
+        $message->setScope(Message::SCOPE_APP);
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+        ;
+
+        $extension = $this->createMock(ExtensionInterface::class);
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
+
+        $extension
+            ->expects($this->at(0))
+            ->method('onDriverPreSend')
+            ->willReturnCallback(function (DriverPreSend $context) use ($message, $producer, $driver) {
+                $this->assertSame($message, $context->getMessage());
+                $this->assertSame($producer, $context->getProducer());
+                $this->assertSame($driver, $context->getDriver());
+                $this->assertSame('command', $context->getCommand());
+
+                $this->assertTrue($context->isEvent());
+            });
+
+        $producer->sendCommand('command', $message);
+    }
+
+    public function testShouldCallPostSendExtensionMethod()
+    {
+        $message = new Message();
+        $message->setBody('aBody');
+        $message->setScope(Message::SCOPE_APP);
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+        ;
+
+        $extension = $this->createMock(ExtensionInterface::class);
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
+
+        $extension
+            ->expects($this->at(0))
+            ->method('onDriverPreSend')
+            ->willReturnCallback(function (PostSend $context) use ($message, $producer, $driver) {
+                $this->assertSame($message, $context->getMessage());
+                $this->assertSame($producer, $context->getProducer());
+                $this->assertSame($driver, $context->getDriver());
+                $this->assertSame('command', $context->getCommand());
+
+                $this->assertFalse($context->isEvent());
+            });
+
+        $producer->sendCommand('command', $message);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createRpcFactoryMock(): RpcFactory
+    {
+        return $this->createMock(RpcFactory::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createDriverStub(): DriverInterface
+    {
+        $config = new Config(
+            'a_prefix',
+            'an_app',
+            'a_router_topic',
+            'a_router_queue',
+            'a_default_processor_queue',
+            'a_router_processor_name'
+        );
+
+        $driverMock = $this->createMock(DriverInterface::class);
+        $driverMock
+            ->expects($this->any())
+            ->method('getConfig')
+            ->willReturn($config)
+        ;
+
+        return $driverMock;
+    }
+}
diff --git a/Tests/Client/ProducerSendEventTest.php b/Tests/Client/ProducerSendEventTest.php
new file mode 100644
index 0000000..a5d45d3
--- /dev/null
+++ b/Tests/Client/ProducerSendEventTest.php
@@ -0,0 +1,546 @@
+<?php
+
+namespace Enqueue\Tests\Client;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\DriverPreSend;
+use Enqueue\Client\ExtensionInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\PostSend;
+use Enqueue\Client\PreSend;
+use Enqueue\Client\Producer;
+use Enqueue\Rpc\RpcFactory;
+use Enqueue\Tests\Mocks\CustomPrepareBodyClientExtension;
+use PHPUnit\Framework\TestCase;
+
+class ProducerSendEventTest extends TestCase
+{
+    public function testShouldSendEventToRouter()
+    {
+        $message = new Message();
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToRouter')
+            ->with(self::identicalTo($message))
+        ;
+        $driver
+            ->expects($this->never())
+            ->method('sendToProcessor')
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendEvent('topic', $message);
+
+        $expectedProperties = [
+            'enqueue.topic_name' => 'topic',
+        ];
+
+        self::assertEquals($expectedProperties, $message->getProperties());
+    }
+
+    public function testShouldOverwriteTopicProperty()
+    {
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topicShouldBeOverwritten');
+
+        $driver = $this->createDriverStub();
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendEvent('expectedTopic', $message);
+
+        $expectedProperties = [
+            'enqueue.topic_name' => 'expectedTopic',
+        ];
+
+        self::assertEquals($expectedProperties, $message->getProperties());
+    }
+
+    public function testShouldSendEventWithNormalPriorityByDefault()
+    {
+        $message = new Message();
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToRouter')
+            ->with(self::identicalTo($message))
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendEvent('topic', $message);
+
+        self::assertSame(MessagePriority::NORMAL, $message->getPriority());
+    }
+
+    public function testShouldSendEventWithCustomPriority()
+    {
+        $message = new Message();
+        $message->setPriority(MessagePriority::HIGH);
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToRouter')
+            ->with(self::identicalTo($message))
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendEvent('topic', $message);
+
+        self::assertSame(MessagePriority::HIGH, $message->getPriority());
+    }
+
+    public function testShouldSendEventWithGeneratedMessageId()
+    {
+        $message = new Message();
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToRouter')
+            ->with(self::identicalTo($message))
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendEvent('topic', $message);
+
+        self::assertNotEmpty($message->getMessageId());
+    }
+
+    public function testShouldSendEventWithCustomMessageId()
+    {
+        $message = new Message();
+        $message->setMessageId('theCustomMessageId');
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToRouter')
+            ->with(self::identicalTo($message))
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendEvent('topic', $message);
+
+        self::assertSame('theCustomMessageId', $message->getMessageId());
+    }
+
+    public function testShouldSendEventWithGeneratedTimestamp()
+    {
+        $message = new Message();
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToRouter')
+            ->with(self::identicalTo($message))
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendEvent('topic', $message);
+
+        self::assertNotEmpty($message->getTimestamp());
+    }
+
+    public function testShouldSendEventWithCustomTimestamp()
+    {
+        $message = new Message();
+        $message->setTimestamp('theCustomTimestamp');
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToRouter')
+            ->with(self::identicalTo($message))
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendEvent('topic', $message);
+
+        self::assertSame('theCustomTimestamp', $message->getTimestamp());
+    }
+
+    public function testShouldSerializeMessageToJsonByDefault()
+    {
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToRouter')
+            ->willReturnCallback(function (Message $message) {
+                $this->assertSame('{"foo":"fooVal"}', $message->getBody());
+            })
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendEvent('topic', ['foo' => 'fooVal']);
+    }
+
+    public function testShouldSerializeMessageByCustomExtension()
+    {
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToRouter')
+            ->willReturnCallback(function (Message $message) {
+                $this->assertSame('theEventBodySerializedByCustomExtension', $message->getBody());
+            })
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock(), new CustomPrepareBodyClientExtension());
+        $producer->sendEvent('topic', ['foo' => 'fooVal']);
+    }
+
+    public function testThrowIfSendEventToMessageBusWithProcessorNamePropertySet()
+    {
+        $message = new Message();
+        $message->setBody('');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aProcessor');
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->never())
+            ->method('sendToRouter')
+        ;
+        $driver
+            ->expects($this->never())
+            ->method('sendToProcessor')
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The enqueue.processor_name property must not be set for messages that are sent to message bus.');
+        $producer->sendEvent('topic', $message);
+    }
+
+    public function testThrowIfSendEventToMessageBusWithProcessorQueueNamePropertySet()
+    {
+        $message = new Message();
+        $message->setBody('');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aProcessorQueue');
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->never())
+            ->method('sendToRouter')
+        ;
+        $driver
+            ->expects($this->never())
+            ->method('sendToProcessor')
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The enqueue.processor_queue_name property must not be set for messages that are sent to message bus.');
+        $producer->sendEvent('topic', $message);
+    }
+
+    public function testShouldSendEventToApplicationRouter()
+    {
+        $message = new Message();
+        $message->setBody('aBody');
+        $message->setScope(Message::SCOPE_APP);
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->never())
+            ->method('sendToRouter')
+        ;
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+            ->willReturnCallback(function (Message $message) {
+                self::assertSame('aBody', $message->getBody());
+                self::assertSame('a_router_processor_name', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
+                self::assertSame('a_router_queue', $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME));
+            })
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendEvent('topic', $message);
+    }
+
+    public function testShouldSendEventWithCustomProcessorAndQueueNamePropertiesSetToApplicationRouter()
+    {
+        $message = new Message();
+        $message->setBody('aBody');
+        $message->setScope(Message::SCOPE_APP);
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aCustomProcessor');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aCustomProcessorQueue');
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->never())
+            ->method('sendToRouter')
+        ;
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+            ->willReturnCallback(function (Message $message) {
+                self::assertSame('aBody', $message->getBody());
+                self::assertSame('aCustomProcessor', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
+                self::assertSame('aCustomProcessorQueue', $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME));
+            })
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+        $producer->sendEvent('topic', $message);
+    }
+
+    public function testThrowIfUnSupportedScopeGivenOnSend()
+    {
+        $message = new Message();
+        $message->setScope('iDontKnowScope');
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->never())
+            ->method('sendToRouter')
+        ;
+        $driver
+            ->expects($this->never())
+            ->method('sendToProcessor')
+        ;
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock());
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The message scope "iDontKnowScope" is not supported.');
+        $producer->sendEvent('topic', $message);
+    }
+
+    public function testShouldCallPreSendEventExtensionMethodWhenSendToBus()
+    {
+        $message = new Message();
+        $message->setBody('aBody');
+        $message->setScope(Message::SCOPE_MESSAGE_BUS);
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToRouter')
+        ;
+
+        $extension = $this->createMock(ExtensionInterface::class);
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
+
+        $extension
+            ->expects($this->at(0))
+            ->method('onPreSendEvent')
+            ->willReturnCallback(function (PreSend $context) use ($message, $producer, $driver) {
+                $this->assertSame($message, $context->getMessage());
+                $this->assertSame($producer, $context->getProducer());
+                $this->assertSame($driver, $context->getDriver());
+                $this->assertSame('topic', $context->getTopic());
+
+                $this->assertEquals($message, $context->getOriginalMessage());
+                $this->assertNotSame($message, $context->getOriginalMessage());
+            });
+
+        $extension
+            ->expects($this->never())
+            ->method('onPreSendCommand')
+        ;
+
+        $producer->sendEvent('topic', $message);
+    }
+
+    public function testShouldCallPreSendEventExtensionMethodWhenSendToApplicationRouter()
+    {
+        $message = new Message();
+        $message->setBody('aBody');
+        $message->setScope(Message::SCOPE_APP);
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+        ;
+
+        $extension = $this->createMock(ExtensionInterface::class);
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
+
+        $extension
+            ->expects($this->at(0))
+            ->method('onPreSendEvent')
+            ->willReturnCallback(function (PreSend $context) use ($message, $producer, $driver) {
+                $this->assertSame($message, $context->getMessage());
+                $this->assertSame($producer, $context->getProducer());
+                $this->assertSame($driver, $context->getDriver());
+                $this->assertSame('topic', $context->getTopic());
+
+                $this->assertEquals($message, $context->getOriginalMessage());
+                $this->assertNotSame($message, $context->getOriginalMessage());
+            });
+
+        $extension
+            ->expects($this->never())
+            ->method('onPreSendCommand')
+        ;
+
+        $producer->sendEvent('topic', $message);
+    }
+
+    public function testShouldCallPreDriverSendExtensionMethodWhenSendToMessageBus()
+    {
+        $message = new Message();
+        $message->setBody('aBody');
+        $message->setScope(Message::SCOPE_MESSAGE_BUS);
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToRouter')
+        ;
+
+        $extension = $this->createMock(ExtensionInterface::class);
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
+
+        $extension
+            ->expects($this->at(0))
+            ->method('onDriverPreSend')
+            ->willReturnCallback(function (DriverPreSend $context) use ($message, $producer, $driver) {
+                $this->assertSame($message, $context->getMessage());
+                $this->assertSame($producer, $context->getProducer());
+                $this->assertSame($driver, $context->getDriver());
+                $this->assertSame('topic', $context->getTopic());
+
+                $this->assertTrue($context->isEvent());
+            });
+
+        $producer->sendEvent('topic', $message);
+    }
+
+    public function testShouldCallPreDriverSendExtensionMethodWhenSendToApplicationRouter()
+    {
+        $message = new Message();
+        $message->setBody('aBody');
+        $message->setScope(Message::SCOPE_APP);
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+        ;
+
+        $extension = $this->createMock(ExtensionInterface::class);
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
+
+        $extension
+            ->expects($this->at(0))
+            ->method('onDriverPreSend')
+            ->willReturnCallback(function (DriverPreSend $context) use ($message, $producer, $driver) {
+                $this->assertSame($message, $context->getMessage());
+                $this->assertSame($producer, $context->getProducer());
+                $this->assertSame($driver, $context->getDriver());
+                $this->assertSame('topic', $context->getTopic());
+
+                $this->assertTrue($context->isEvent());
+            });
+
+        $producer->sendEvent('topic', $message);
+    }
+
+    public function testShouldCallPostSendExtensionMethodWhenSendToMessageBus()
+    {
+        $message = new Message();
+        $message->setBody('aBody');
+        $message->setScope(Message::SCOPE_MESSAGE_BUS);
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToRouter')
+        ;
+
+        $extension = $this->createMock(ExtensionInterface::class);
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
+
+        $extension
+            ->expects($this->at(0))
+            ->method('onPostSend')
+            ->willReturnCallback(function (PostSend $context) use ($message, $producer, $driver) {
+                $this->assertSame($message, $context->getMessage());
+                $this->assertSame($producer, $context->getProducer());
+                $this->assertSame($driver, $context->getDriver());
+                $this->assertSame('topic', $context->getTopic());
+
+                $this->assertTrue($context->isEvent());
+            });
+
+        $producer->sendEvent('topic', $message);
+    }
+
+    public function testShouldCallPostSendExtensionMethodWhenSendToApplicationRouter()
+    {
+        $message = new Message();
+        $message->setBody('aBody');
+        $message->setScope(Message::SCOPE_APP);
+
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+        ;
+
+        $extension = $this->createMock(ExtensionInterface::class);
+
+        $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
+
+        $extension
+            ->expects($this->at(0))
+            ->method('onDriverPreSend')
+            ->willReturnCallback(function (PostSend $context) use ($message, $producer, $driver) {
+                $this->assertSame($message, $context->getMessage());
+                $this->assertSame($producer, $context->getProducer());
+                $this->assertSame($driver, $context->getDriver());
+                $this->assertSame('topic', $context->getTopic());
+
+                $this->assertTrue($context->isEvent());
+            });
+
+        $producer->sendEvent('topic', $message);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createRpcFactoryMock(): RpcFactory
+    {
+        return $this->createMock(RpcFactory::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createDriverStub(): DriverInterface
+    {
+        $config = new Config(
+            'a_prefix',
+            'an_app',
+            'a_router_topic',
+            'a_router_queue',
+            'a_default_processor_queue',
+            'a_router_processor_name'
+        );
+
+        $driverMock = $this->createMock(DriverInterface::class);
+        $driverMock
+            ->expects($this->any())
+            ->method('getConfig')
+            ->willReturn($config)
+        ;
+
+        return $driverMock;
+    }
+}
diff --git a/Tests/Client/ProducerTest.php b/Tests/Client/ProducerTest.php
index be8554c..4fbf4ba 100644
--- a/Tests/Client/ProducerTest.php
+++ b/Tests/Client/ProducerTest.php
@@ -2,11 +2,8 @@
 
 namespace Enqueue\Tests\Client;
 
-use Enqueue\Client\Config;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\ExtensionInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\MessagePriority;
 use Enqueue\Client\Producer;
 use Enqueue\Client\ProducerInterface;
 use Enqueue\Rpc\RpcFactory;
@@ -22,393 +19,38 @@ public function testShouldImplementProducerInterface()
         self::assertClassImplements(ProducerInterface::class, Producer::class);
     }
 
-    public function testCouldBeConstructedWithDriverAsFirstArgument()
+    public function testShouldBeFinal()
     {
-        new Producer($this->createDriverStub(), $this->createRpcFactory());
+        self::assertClassFinal(Producer::class);
     }
 
-    public function testShouldSendMessageToRouter()
+    public function testCouldBeConstructedWithRequiredArguments()
     {
-        $message = new Message();
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->once())
-            ->method('sendToRouter')
-            ->with(self::identicalTo($message))
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent('topic', $message);
-
-        $expectedProperties = [
-            'enqueue.topic_name' => 'topic',
-        ];
-
-        self::assertEquals($expectedProperties, $message->getProperties());
-    }
-
-    public function testShouldSendMessageWithNormalPriorityByDefault()
-    {
-        $message = new Message();
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->once())
-            ->method('sendToRouter')
-            ->with(self::identicalTo($message))
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent('topic', $message);
-
-        self::assertSame(MessagePriority::NORMAL, $message->getPriority());
-    }
-
-    public function testShouldSendMessageWithCustomPriority()
-    {
-        $message = new Message();
-        $message->setPriority(MessagePriority::HIGH);
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->once())
-            ->method('sendToRouter')
-            ->with(self::identicalTo($message))
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent('topic', $message);
-
-        self::assertSame(MessagePriority::HIGH, $message->getPriority());
-    }
-
-    public function testShouldSendMessageWithGeneratedMessageId()
-    {
-        $message = new Message();
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->once())
-            ->method('sendToRouter')
-            ->with(self::identicalTo($message))
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent('topic', $message);
-
-        self::assertNotEmpty($message->getMessageId());
-    }
-
-    public function testShouldSendMessageWithCustomMessageId()
-    {
-        $message = new Message();
-        $message->setMessageId('theCustomMessageId');
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->once())
-            ->method('sendToRouter')
-            ->with(self::identicalTo($message))
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent('topic', $message);
-
-        self::assertSame('theCustomMessageId', $message->getMessageId());
+        new Producer($this->createDriverMock(), $this->createRpcFactoryMock());
     }
 
-    public function testShouldSendMessageWithGeneratedTimestamp()
+    public function testCouldBeConstructedWithOptionalArguments()
     {
-        $message = new Message();
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->once())
-            ->method('sendToRouter')
-            ->with(self::identicalTo($message))
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent('topic', $message);
-
-        self::assertNotEmpty($message->getTimestamp());
-    }
-
-    public function testShouldSendMessageWithCustomTimestamp()
-    {
-        $message = new Message();
-        $message->setTimestamp('theCustomTimestamp');
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->once())
-            ->method('sendToRouter')
-            ->with(self::identicalTo($message))
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent('topic', $message);
-
-        self::assertSame('theCustomTimestamp', $message->getTimestamp());
-    }
-
-    public function testShouldSendJsonSerializableObjectAsJsonStringToMessageBus()
-    {
-        $object = new JsonSerializableObject();
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->once())
-            ->method('sendToRouter')
-            ->willReturnCallback(function (Message $message) {
-                self::assertSame('{"foo":"fooVal"}', $message->getBody());
-                self::assertSame('application/json', $message->getContentType());
-            })
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent('topic', $object);
-    }
-
-    public function testShouldSendMessageJsonSerializableBodyAsJsonStringToMessageBus()
-    {
-        $object = new JsonSerializableObject();
-
-        $message = new Message();
-        $message->setBody($object);
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->once())
-            ->method('sendToRouter')
-            ->willReturnCallback(function (Message $message) {
-                self::assertSame('{"foo":"fooVal"}', $message->getBody());
-                self::assertSame('application/json', $message->getContentType());
-            })
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent('topic', $message);
-    }
-
-    public function testThrowIfTryToSendMessageToMessageBusWithProcessorNamePropertySet()
-    {
-        $object = new JsonSerializableObject();
-
-        $message = new Message();
-        $message->setBody($object);
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aProcessor');
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->never())
-            ->method('sendToRouter')
-        ;
-        $driver
-            ->expects($this->never())
-            ->method('sendToProcessor')
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The enqueue.processor_name property must not be set for messages that are sent to message bus.');
-        $producer->sendEvent('topic', $message);
-    }
-
-    public function testThrowIfTryToSendMessageToMessageBusWithProcessorQueueNamePropertySet()
-    {
-        $object = new JsonSerializableObject();
-
-        $message = new Message();
-        $message->setBody($object);
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aProcessorQueue');
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->never())
-            ->method('sendToRouter')
-        ;
-        $driver
-            ->expects($this->never())
-            ->method('sendToProcessor')
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The enqueue.processor_queue_name property must not be set for messages that are sent to message bus.');
-        $producer->sendEvent('topic', $message);
-    }
-
-    public function testShouldSendMessageToApplicationRouter()
-    {
-        $message = new Message();
-        $message->setBody('aBody');
-        $message->setScope(Message::SCOPE_APP);
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->never())
-            ->method('sendToRouter')
-        ;
-        $driver
-            ->expects($this->once())
-            ->method('sendToProcessor')
-            ->willReturnCallback(function (Message $message) {
-                self::assertSame('aBody', $message->getBody());
-                self::assertSame('a_router_processor_name', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
-                self::assertSame('a_router_queue', $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME));
-            })
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent('topic', $message);
-    }
-
-    public function testShouldSendToCustomMessageToApplicationRouter()
-    {
-        $message = new Message();
-        $message->setBody('aBody');
-        $message->setScope(Message::SCOPE_APP);
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aCustomProcessor');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aCustomProcessorQueue');
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->never())
-            ->method('sendToRouter')
-        ;
-        $driver
-            ->expects($this->once())
-            ->method('sendToProcessor')
-            ->willReturnCallback(function (Message $message) {
-                self::assertSame('aBody', $message->getBody());
-                self::assertSame('aCustomProcessor', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
-                self::assertSame('aCustomProcessorQueue', $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME));
-            })
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-        $producer->sendEvent('topic', $message);
-    }
-
-    public function testThrowIfUnSupportedScopeGivenOnSend()
-    {
-        $message = new Message();
-        $message->setScope('iDontKnowScope');
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->never())
-            ->method('sendToRouter')
-        ;
-        $driver
-            ->expects($this->never())
-            ->method('sendToProcessor')
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory());
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The message scope "iDontKnowScope" is not supported.');
-        $producer->sendEvent('topic', $message);
-    }
-
-    public function testShouldCallPreSendPostSendExtensionMethodsWhenSendToRouter()
-    {
-        $message = new Message();
-        $message->setBody('aBody');
-        $message->setScope(Message::SCOPE_MESSAGE_BUS);
-
-        $extension = $this->createMock(ExtensionInterface::class);
-        $extension
-            ->expects($this->at(0))
-            ->method('onPreSend')
-            ->with($this->identicalTo('topic'), $this->identicalTo($message))
-        ;
-        $extension
-            ->expects($this->at(1))
-            ->method('onPostSend')
-            ->with($this->identicalTo('topic'), $this->identicalTo($message))
-        ;
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->once())
-            ->method('sendToRouter')
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory(), $extension);
-        $producer->sendEvent('topic', $message);
-    }
-
-    public function testShouldCallPreSendPostSendExtensionMethodsWhenSendToProcessor()
-    {
-        $message = new Message();
-        $message->setBody('aBody');
-        $message->setScope(Message::SCOPE_APP);
-
-        $extension = $this->createMock(ExtensionInterface::class);
-        $extension
-            ->expects($this->at(0))
-            ->method('onPreSend')
-            ->with($this->identicalTo('topic'), $this->identicalTo($message))
-        ;
-        $extension
-            ->expects($this->at(1))
-            ->method('onPostSend')
-            ->with($this->identicalTo('topic'), $this->identicalTo($message))
-        ;
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->once())
-            ->method('sendToProcessor')
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactory(), $extension);
-        $producer->sendEvent('topic', $message);
+        new Producer(
+            $this->createDriverMock(),
+            $this->createRpcFactoryMock(),
+            $this->createMock(ExtensionInterface::class)
+        );
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|RpcFactory
+     * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    private function createRpcFactory()
+    private function createRpcFactoryMock(): RpcFactory
     {
         return $this->createMock(RpcFactory::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|DriverInterface
+     * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    private function createDriverStub()
-    {
-        $config = new Config(
-            'a_prefix',
-            'an_app',
-            'a_router_topic',
-            'a_router_queue',
-            'a_default_processor_queue',
-            'a_router_processor_name'
-        );
-
-        $driverMock = $this->createMock(DriverInterface::class);
-        $driverMock
-            ->expects($this->any())
-            ->method('getConfig')
-            ->willReturn($config)
-        ;
-
-        return $driverMock;
-    }
-}
-
-class JsonSerializableObject implements \JsonSerializable
-{
-    public function jsonSerialize()
+    private function createDriverMock(): DriverInterface
     {
-        return ['foo' => 'fooVal'];
+        return $this->createMock(DriverInterface::class);
     }
 }
diff --git a/Tests/Mocks/CustomPrepareBodyClientExtension.php b/Tests/Mocks/CustomPrepareBodyClientExtension.php
new file mode 100644
index 0000000..4d68fa1
--- /dev/null
+++ b/Tests/Mocks/CustomPrepareBodyClientExtension.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Enqueue\Tests\Mocks;
+
+use Enqueue\Client\EmptyExtensionTrait;
+use Enqueue\Client\ExtensionInterface;
+use Enqueue\Client\PreSend;
+
+class CustomPrepareBodyClientExtension implements ExtensionInterface
+{
+    use EmptyExtensionTrait;
+
+    public function onPreSendCommand(PreSend $context): void
+    {
+        $context->getMessage()->setBody('theCommandBodySerializedByCustomExtension');
+    }
+
+    public function onPreSendEvent(PreSend $context): void
+    {
+        $context->getMessage()->setBody('theEventBodySerializedByCustomExtension');
+    }
+}
diff --git a/Tests/Mocks/JsonSerializableObject.php b/Tests/Mocks/JsonSerializableObject.php
new file mode 100644
index 0000000..5b74106
--- /dev/null
+++ b/Tests/Mocks/JsonSerializableObject.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace Enqueue\Tests\Mocks;
+
+class JsonSerializableObject implements \JsonSerializable
+{
+    public function jsonSerialize()
+    {
+        return ['foo' => 'fooVal'];
+    }
+}

From d4f9887f24b049593522d454fedf23f9f3aabf55 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 30 Aug 2018 13:14:36 +0300
Subject: [PATCH 091/337] add tests for pre send object.

---
 Tests/Client/PreSendTest.php | 126 +++++++++++++++++++++++++++++++++++
 1 file changed, 126 insertions(+)
 create mode 100644 Tests/Client/PreSendTest.php

diff --git a/Tests/Client/PreSendTest.php b/Tests/Client/PreSendTest.php
new file mode 100644
index 0000000..d1e5595
--- /dev/null
+++ b/Tests/Client/PreSendTest.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace Enqueue\Tests\Client;
+
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\PreSend;
+use Enqueue\Client\ProducerInterface;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+
+class PreSendTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldBeFinal()
+    {
+        self::assertClassFinal(PreSend::class);
+    }
+
+    public function testCouldBeConstructedWithExpectedArguments()
+    {
+        new PreSend(
+            'aCommandOrTopic',
+            new Message(),
+            $this->createProducerMock(),
+            $this->createDriverMock()
+        );
+    }
+
+    public function testShouldAllowGetArgumentSetInConstructor()
+    {
+        $expectedCommandOrTopic = 'theCommandOrTopic';
+        $expectedMessage = new Message();
+        $expectedProducer = $this->createProducerMock();
+        $expectedDriver = $this->createDriverMock();
+
+        $context = new PreSend(
+            $expectedCommandOrTopic,
+            $expectedMessage,
+            $expectedProducer,
+            $expectedDriver
+        );
+
+        $this->assertSame($expectedCommandOrTopic, $context->getTopic());
+        $this->assertSame($expectedCommandOrTopic, $context->getCommand());
+        $this->assertSame($expectedMessage, $context->getMessage());
+        $this->assertSame($expectedProducer, $context->getProducer());
+        $this->assertSame($expectedDriver, $context->getDriver());
+
+        $this->assertEquals($expectedMessage, $context->getOriginalMessage());
+        $this->assertNotSame($expectedMessage, $context->getOriginalMessage());
+    }
+
+    public function testCouldChangeTopic()
+    {
+        $context = new PreSend(
+            'aCommandOrTopic',
+            new Message(),
+            $this->createProducerMock(),
+            $this->createDriverMock()
+        );
+
+        //guard
+        $this->assertSame('aCommandOrTopic', $context->getTopic());
+
+        $context->changeTopic('theChangedTopic');
+
+        $this->assertSame('theChangedTopic', $context->getTopic());
+    }
+
+    public function testCouldChangeCommand()
+    {
+        $context = new PreSend(
+            'aCommandOrTopic',
+            new Message(),
+            $this->createProducerMock(),
+            $this->createDriverMock()
+        );
+
+        //guard
+        $this->assertSame('aCommandOrTopic', $context->getCommand());
+
+        $context->changeCommand('theChangedCommand');
+
+        $this->assertSame('theChangedCommand', $context->getCommand());
+    }
+
+    public function testCouldChangeBody()
+    {
+        $context = new PreSend(
+            'aCommandOrTopic',
+            new Message('aBody'),
+            $this->createProducerMock(),
+            $this->createDriverMock()
+        );
+
+        //guard
+        $this->assertSame('aBody', $context->getMessage()->getBody());
+        $this->assertNull($context->getMessage()->getContentType());
+
+        $context->changeBody('theChangedBody');
+        $this->assertSame('theChangedBody', $context->getMessage()->getBody());
+        $this->assertNull($context->getMessage()->getContentType());
+
+        $context->changeBody('theChangedBodyAgain', 'foo/bar');
+        $this->assertSame('theChangedBodyAgain', $context->getMessage()->getBody());
+        $this->assertSame('foo/bar', $context->getMessage()->getContentType());
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createDriverMock(): DriverInterface
+    {
+        return $this->createMock(DriverInterface::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createProducerMock(): ProducerInterface
+    {
+        return $this->createMock(ProducerInterface::class);
+    }
+}

From b98eeabf17268bfcf70e489153519c9c48f623d6 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 30 Aug 2018 13:58:48 +0300
Subject: [PATCH 092/337] add tests

---
 Tests/Client/DriverPreSendTest.php | 94 ++++++++++++++++++++++++++++++
 Tests/Client/PostSendTest.php      | 94 ++++++++++++++++++++++++++++++
 2 files changed, 188 insertions(+)
 create mode 100644 Tests/Client/DriverPreSendTest.php
 create mode 100644 Tests/Client/PostSendTest.php

diff --git a/Tests/Client/DriverPreSendTest.php b/Tests/Client/DriverPreSendTest.php
new file mode 100644
index 0000000..62d493b
--- /dev/null
+++ b/Tests/Client/DriverPreSendTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace Enqueue\Tests\Client;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\DriverPreSend;
+use Enqueue\Client\Message;
+use Enqueue\Client\ProducerInterface;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+
+class DriverPreSendTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldBeFinal()
+    {
+        self::assertClassFinal(DriverPreSend::class);
+    }
+
+    public function testCouldBeConstructedWithExpectedArguments()
+    {
+        new DriverPreSend(
+            new Message(),
+            $this->createProducerMock(),
+            $this->createDriverMock()
+        );
+    }
+
+    public function testShouldAllowGetArgumentSetInConstructor()
+    {
+        $expectedMessage = new Message();
+        $expectedProducer = $this->createProducerMock();
+        $expectedDriver = $this->createDriverMock();
+
+        $context = new DriverPreSend(
+            $expectedMessage,
+            $expectedProducer,
+            $expectedDriver
+        );
+
+        $this->assertSame($expectedMessage, $context->getMessage());
+        $this->assertSame($expectedProducer, $context->getProducer());
+        $this->assertSame($expectedDriver, $context->getDriver());
+    }
+
+    public function testShouldAllowGetCommand()
+    {
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, Config::COMMAND_TOPIC);
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'theCommand');
+
+        $context = new DriverPreSend(
+            $message,
+            $this->createProducerMock(),
+            $this->createDriverMock()
+        );
+
+        $this->assertFalse($context->isEvent());
+        $this->assertSame('theCommand', $context->getCommand());
+    }
+
+    public function testShouldAllowGetTopic()
+    {
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'theTopic');
+
+        $context = new DriverPreSend(
+            $message,
+            $this->createProducerMock(),
+            $this->createDriverMock()
+        );
+
+        $this->assertTrue($context->isEvent());
+        $this->assertSame('theTopic', $context->getTopic());
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createDriverMock(): DriverInterface
+    {
+        return $this->createMock(DriverInterface::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createProducerMock(): ProducerInterface
+    {
+        return $this->createMock(ProducerInterface::class);
+    }
+}
diff --git a/Tests/Client/PostSendTest.php b/Tests/Client/PostSendTest.php
new file mode 100644
index 0000000..fe78c14
--- /dev/null
+++ b/Tests/Client/PostSendTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace Enqueue\Tests\Client;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\PostSend;
+use Enqueue\Client\ProducerInterface;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+
+class PostSendTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldBeFinal()
+    {
+        self::assertClassFinal(PostSend::class);
+    }
+
+    public function testCouldBeConstructedWithExpectedArguments()
+    {
+        new PostSend(
+            new Message(),
+            $this->createProducerMock(),
+            $this->createDriverMock()
+        );
+    }
+
+    public function testShouldAllowGetArgumentSetInConstructor()
+    {
+        $expectedMessage = new Message();
+        $expectedProducer = $this->createProducerMock();
+        $expectedDriver = $this->createDriverMock();
+
+        $context = new PostSend(
+            $expectedMessage,
+            $expectedProducer,
+            $expectedDriver
+        );
+
+        $this->assertSame($expectedMessage, $context->getMessage());
+        $this->assertSame($expectedProducer, $context->getProducer());
+        $this->assertSame($expectedDriver, $context->getDriver());
+    }
+
+    public function testShouldAllowGetCommand()
+    {
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, Config::COMMAND_TOPIC);
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'theCommand');
+
+        $context = new PostSend(
+            $message,
+            $this->createProducerMock(),
+            $this->createDriverMock()
+        );
+
+        $this->assertFalse($context->isEvent());
+        $this->assertSame('theCommand', $context->getCommand());
+    }
+
+    public function testShouldAllowGetTopic()
+    {
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'theTopic');
+
+        $context = new PostSend(
+            $message,
+            $this->createProducerMock(),
+            $this->createDriverMock()
+        );
+
+        $this->assertTrue($context->isEvent());
+        $this->assertSame('theTopic', $context->getTopic());
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createDriverMock(): DriverInterface
+    {
+        return $this->createMock(DriverInterface::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createProducerMock(): ProducerInterface
+    {
+        return $this->createMock(ProducerInterface::class);
+    }
+}

From 6f0a369257f1374bbce7d6538e427b6eeaa13cbd Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 30 Aug 2018 20:25:25 +0300
Subject: [PATCH 093/337] [client] Add typehints to producer interface, its
 implementations

---
 Client/Producer.php          | 15 ++++-------
 Client/ProducerInterface.php | 14 +++++-----
 Client/SpoolProducer.php     | 25 ++++-------------
 Client/TraceableProducer.php | 52 +++++++-----------------------------
 4 files changed, 27 insertions(+), 79 deletions(-)

diff --git a/Client/Producer.php b/Client/Producer.php
index 2e74df0..7a4d18f 100644
--- a/Client/Producer.php
+++ b/Client/Producer.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Client;
 
 use Enqueue\Client\Extension\PrepareBodyExtension;
+use Enqueue\Rpc\Promise;
 use Enqueue\Rpc\RpcFactory;
 use Enqueue\Util\UUID;
 
@@ -37,7 +38,7 @@ public function __construct(
         ;
     }
 
-    public function sendEvent($topic, $message)
+    public function sendEvent(string $topic, $message): void
     {
         if (false == $message instanceof Message) {
             $message = new Message($message);
@@ -54,7 +55,7 @@ public function sendEvent($topic, $message)
         $this->doSend($message);
     }
 
-    public function sendCommand($command, $message, $needReply = false)
+    public function sendCommand(string $command, $message, bool $needReply = false): ?Promise
     {
         if (false == $message instanceof Message) {
             $message = new Message($message);
@@ -92,17 +93,11 @@ public function sendCommand($command, $message, $needReply = false)
 
             return $promise;
         }
-    }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function send($topic, $message)
-    {
-        $this->sendEvent($topic, $message);
+        return null;
     }
 
-    private function doSend(Message $message)
+    private function doSend(Message $message): void
     {
         if (false === is_string($message->getBody())) {
             throw new \LogicException(sprintf(
diff --git a/Client/ProducerInterface.php b/Client/ProducerInterface.php
index 2fc829e..f4207b3 100644
--- a/Client/ProducerInterface.php
+++ b/Client/ProducerInterface.php
@@ -7,17 +7,17 @@
 interface ProducerInterface
 {
     /**
-     * @param string               $topic
+     * The message could be pretty much everything as long as you have a client extension that transforms a body to string on onPreSendEvent.
+     *
      * @param string|array|Message $message
      */
-    public function sendEvent($topic, $message);
+    public function sendEvent(string $topic, $message): void;
 
     /**
-     * @param string               $command
-     * @param string|array|Message $message
-     * @param bool                 $needReply
+     * The message could be pretty much everything as long as you have a client extension that transforms a body to string on onPreSendCommand.
+     * The promise is returned if needReply argument is true
      *
-     * @return Promise|null the promise is returned if needReply argument is true
+     * @param string|array|Message $message
      */
-    public function sendCommand($command, $message, $needReply = false);
+    public function sendCommand(string $command, $message, bool $needReply = false): ?Promise;
 }
diff --git a/Client/SpoolProducer.php b/Client/SpoolProducer.php
index c538877..1bee97b 100644
--- a/Client/SpoolProducer.php
+++ b/Client/SpoolProducer.php
@@ -2,6 +2,8 @@
 
 namespace Enqueue\Client;
 
+use Enqueue\Rpc\Promise;
+
 class SpoolProducer implements ProducerInterface
 {
     /**
@@ -19,9 +21,6 @@ class SpoolProducer implements ProducerInterface
      */
     private $commands;
 
-    /**
-     * @param ProducerInterface $realProducer
-     */
     public function __construct(ProducerInterface $realProducer)
     {
         $this->realProducer = $realProducer;
@@ -30,10 +29,7 @@ public function __construct(ProducerInterface $realProducer)
         $this->commands = new \SplQueue();
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendCommand($command, $message, $needReply = false)
+    public function sendCommand(string $command, $message, bool $needReply = false): ?Promise
     {
         if ($needReply) {
             return $this->realProducer->sendCommand($command, $message, $needReply);
@@ -42,26 +38,15 @@ public function sendCommand($command, $message, $needReply = false)
         $this->commands->enqueue([$command, $message]);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendEvent($topic, $message)
+    public function sendEvent(string $topic, $message): void
     {
         $this->events->enqueue([$topic, $message]);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function send($topic, $message)
-    {
-        $this->sendEvent($topic, $message);
-    }
-
     /**
      * When it is called it sends all previously queued messages.
      */
-    public function flush()
+    public function flush(): void
     {
         while (false == $this->events->isEmpty()) {
             list($topic, $message) = $this->events->dequeue();
diff --git a/Client/TraceableProducer.php b/Client/TraceableProducer.php
index cd55a91..71af530 100644
--- a/Client/TraceableProducer.php
+++ b/Client/TraceableProducer.php
@@ -2,39 +2,33 @@
 
 namespace Enqueue\Client;
 
+use Enqueue\Rpc\Promise;
+
 class TraceableProducer implements ProducerInterface
 {
     /**
      * @var array
      */
     protected $traces = [];
+
     /**
      * @var ProducerInterface
      */
     private $producer;
 
-    /**
-     * @param ProducerInterface $producer
-     */
     public function __construct(ProducerInterface $producer)
     {
         $this->producer = $producer;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendEvent($topic, $message)
+    public function sendEvent(string $topic, $message): void
     {
         $this->producer->sendEvent($topic, $message);
 
         $this->collectTrace($topic, null, $message);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendCommand($command, $message, $needReply = false)
+    public function sendCommand(string $command, $message, bool $needReply = false): ?Promise
     {
         $result = $this->producer->sendCommand($command, $message, $needReply);
 
@@ -43,20 +37,7 @@ public function sendCommand($command, $message, $needReply = false)
         return $result;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function send($topic, $message)
-    {
-        $this->sendEvent($topic, $message);
-    }
-
-    /**
-     * @param string $topic
-     *
-     * @return array
-     */
-    public function getTopicTraces($topic)
+    public function getTopicTraces(string $topic): array
     {
         $topicTraces = [];
         foreach ($this->traces as $trace) {
@@ -68,12 +49,7 @@ public function getTopicTraces($topic)
         return $topicTraces;
     }
 
-    /**
-     * @param string $command
-     *
-     * @return array
-     */
-    public function getCommandTraces($command)
+    public function getCommandTraces(string $command): array
     {
         $commandTraces = [];
         foreach ($this->traces as $trace) {
@@ -85,25 +61,17 @@ public function getCommandTraces($command)
         return $commandTraces;
     }
 
-    /**
-     * @return array
-     */
-    public function getTraces()
+    public function getTraces(): array
     {
         return $this->traces;
     }
 
-    public function clearTraces()
+    public function clearTraces(): void
     {
         $this->traces = [];
     }
 
-    /**
-     * @param string|null $topic
-     * @param string|null $command
-     * @param mixed       $message
-     */
-    private function collectTrace($topic, $command, $message)
+    private function collectTrace(string $topic = null, string $command = null, $message): void
     {
         $trace = [
             'topic' => $topic,

From fba3f11a996bffbb2dd128cb7cd094c8d8652e6b Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 30 Aug 2018 20:38:38 +0300
Subject: [PATCH 094/337] fix  cs

---
 Client/ProducerInterface.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Client/ProducerInterface.php b/Client/ProducerInterface.php
index f4207b3..1c7b056 100644
--- a/Client/ProducerInterface.php
+++ b/Client/ProducerInterface.php
@@ -15,7 +15,7 @@ public function sendEvent(string $topic, $message): void;
 
     /**
      * The message could be pretty much everything as long as you have a client extension that transforms a body to string on onPreSendCommand.
-     * The promise is returned if needReply argument is true
+     * The promise is returned if needReply argument is true.
      *
      * @param string|array|Message $message
      */

From 19bc14693cfb4686633a5ec351eabdbd7646eb28 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 30 Aug 2018 21:01:10 +0300
Subject: [PATCH 095/337] fix tests

---
 Client/SpoolProducer.php | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Client/SpoolProducer.php b/Client/SpoolProducer.php
index 1bee97b..8ad0940 100644
--- a/Client/SpoolProducer.php
+++ b/Client/SpoolProducer.php
@@ -36,6 +36,8 @@ public function sendCommand(string $command, $message, bool $needReply = false):
         }
 
         $this->commands->enqueue([$command, $message]);
+
+        return null;
     }
 
     public function sendEvent(string $topic, $message): void

From baa3df4016803113b3f4cadb8b3d20e3ae1eacdb Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Sat, 1 Sep 2018 20:48:13 +0300
Subject: [PATCH 096/337] explisitly add queue interop dev package as dep

---
 composer.json | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index e61270c..08c14be 100644
--- a/composer.json
+++ b/composer.json
@@ -7,7 +7,8 @@
     "license": "MIT",
     "require": {
         "php": "^7.1.3",
-        "queue-interop/queue-interop": "^0.6.2@dev",
+        "queue-interop/amqp-interop": "0.8.x-dev",
+        "queue-interop/queue-interop": "0.7.x-dev",
         "enqueue/null": "0.9.x-dev",
         "ramsey/uuid": "^2|^3.5",
         "psr/log": "^1"

From f3e146f7f5fdf4e8414ebed46965ba9d15bdd9f2 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 4 Sep 2018 17:59:00 +0300
Subject: [PATCH 097/337] Simplify Enqueue configuration.

---
 Client/{Amqp => Driver}/AmqpDriver.php        |   2 +-
 Client/Driver/DbalDriver.php                  | 196 +++++
 Client/Driver/FsDriver.php                    | 185 +++++
 Client/Driver/GpsDriver.php                   | 182 +++++
 Client/Driver/MongodbDriver.php               | 186 +++++
 Client/Driver/NullDriver.php                  | 160 ++++
 Client/{Amqp => Driver}/RabbitMqDriver.php    |   2 +-
 Client/Driver/RabbitMqStompDriver.php         | 269 +++++++
 Client/Driver/RdKafkaDriver.php               | 167 +++++
 Client/Driver/RedisDriver.php                 | 152 ++++
 Client/Driver/SqsDriver.php                   | 171 +++++
 Client/Driver/StompDriver.php                 | 191 +++++
 Client/Driver/StompManagementClient.php       |  77 ++
 Client/DriverFactory.php                      |  12 +-
 Client/Resources.php                          |  62 +-
 Client/TraceableProducer.php                  |   4 +-
 ConnectionFactoryFactory.php                  |  18 +-
 ConnectionFactoryFactoryInterface.php         |  14 +-
 Symfony/AmqpTransportFactory.php              |   2 +-
 Symfony/DefaultTransportFactory.php           | 103 +--
 .../DependencyInjection/TransportFactory.php  | 108 +++
 Symfony/RabbitMqAmqpTransportFactory.php      |   2 +-
 .../{Amqp => Driver}/AmqpDriverTest.php       |   4 +-
 Tests/Client/Driver/DbalDriverTest.php        | 357 +++++++++
 Tests/Client/Driver/FsDriverTest.php          | 392 ++++++++++
 Tests/Client/Driver/GpsDriverTest.php         | 385 ++++++++++
 Tests/Client/Driver/MongodbDriverTest.php     | 358 +++++++++
 Tests/Client/Driver/NullDriverTest.php        | 277 +++++++
 .../{Amqp => Driver}/RabbitMqDriverTest.php   |   6 +-
 .../Client/Driver/RabbitMqStompDriverTest.php | 682 ++++++++++++++++++
 Tests/Client/Driver/RdKafkaDriverTest.php     | 368 ++++++++++
 Tests/Client/Driver/RedisDriverTest.php       | 366 ++++++++++
 Tests/Client/Driver/SqsDriverTest.php         | 408 +++++++++++
 Tests/Client/Driver/StompDriverTest.php       | 342 +++++++++
 .../Driver/StompManagementClientTest.php      | 111 +++
 Tests/Client/DriverFactoryTest.php            |  29 +-
 Tests/Client/ResourcesTest.php                |  30 +-
 Tests/ConnectionFactoryFactoryTest.php        |  32 +
 Tests/Symfony/AmqpTransportFactoryTest.php    | 404 -----------
 .../Mock/SetupBrokerExtensionCommand.php      |   2 +-
 .../TransportFactoryTest.php}                 | 155 ++--
 Tests/Symfony/MissingTransportFactoryTest.php |  73 --
 .../RabbitMqAmqpTransportFactoryTest.php      | 129 ----
 43 files changed, 6311 insertions(+), 864 deletions(-)
 rename Client/{Amqp => Driver}/AmqpDriver.php (99%)
 create mode 100644 Client/Driver/DbalDriver.php
 create mode 100644 Client/Driver/FsDriver.php
 create mode 100644 Client/Driver/GpsDriver.php
 create mode 100644 Client/Driver/MongodbDriver.php
 create mode 100644 Client/Driver/NullDriver.php
 rename Client/{Amqp => Driver}/RabbitMqDriver.php (99%)
 create mode 100644 Client/Driver/RabbitMqStompDriver.php
 create mode 100644 Client/Driver/RdKafkaDriver.php
 create mode 100644 Client/Driver/RedisDriver.php
 create mode 100644 Client/Driver/SqsDriver.php
 create mode 100644 Client/Driver/StompDriver.php
 create mode 100644 Client/Driver/StompManagementClient.php
 create mode 100644 Symfony/DependencyInjection/TransportFactory.php
 rename Tests/Client/{Amqp => Driver}/AmqpDriverTest.php (99%)
 create mode 100644 Tests/Client/Driver/DbalDriverTest.php
 create mode 100644 Tests/Client/Driver/FsDriverTest.php
 create mode 100644 Tests/Client/Driver/GpsDriverTest.php
 create mode 100644 Tests/Client/Driver/MongodbDriverTest.php
 create mode 100644 Tests/Client/Driver/NullDriverTest.php
 rename Tests/Client/{Amqp => Driver}/RabbitMqDriverTest.php (99%)
 create mode 100644 Tests/Client/Driver/RabbitMqStompDriverTest.php
 create mode 100644 Tests/Client/Driver/RdKafkaDriverTest.php
 create mode 100644 Tests/Client/Driver/RedisDriverTest.php
 create mode 100644 Tests/Client/Driver/SqsDriverTest.php
 create mode 100644 Tests/Client/Driver/StompDriverTest.php
 create mode 100644 Tests/Client/Driver/StompManagementClientTest.php
 delete mode 100644 Tests/Symfony/AmqpTransportFactoryTest.php
 rename Tests/Symfony/{DefaultTransportFactoryTest.php => DependencyInjection/TransportFactoryTest.php} (56%)
 delete mode 100644 Tests/Symfony/MissingTransportFactoryTest.php
 delete mode 100644 Tests/Symfony/RabbitMqAmqpTransportFactoryTest.php

diff --git a/Client/Amqp/AmqpDriver.php b/Client/Driver/AmqpDriver.php
similarity index 99%
rename from Client/Amqp/AmqpDriver.php
rename to Client/Driver/AmqpDriver.php
index 44d9e1b..c2d274a 100644
--- a/Client/Amqp/AmqpDriver.php
+++ b/Client/Driver/AmqpDriver.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace  Enqueue\Client\Amqp;
+namespace  Enqueue\Client\Driver;
 
 use Enqueue\Client\Config;
 use Enqueue\Client\DriverInterface;
diff --git a/Client/Driver/DbalDriver.php b/Client/Driver/DbalDriver.php
new file mode 100644
index 0000000..9b433db
--- /dev/null
+++ b/Client/Driver/DbalDriver.php
@@ -0,0 +1,196 @@
+<?php
+
+namespace Enqueue\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Dbal\DbalContext;
+use Enqueue\Dbal\DbalMessage;
+use Interop\Queue\PsrMessage;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
+class DbalDriver implements DriverInterface
+{
+    /**
+     * @var DbalContext
+     */
+    private $context;
+
+    /**
+     * @var Config
+     */
+    private $config;
+
+    /**
+     * @var QueueMetaRegistry
+     */
+    private $queueMetaRegistry;
+
+    /**
+     * @var array
+     */
+    private static $priorityMap = [
+        MessagePriority::VERY_LOW => 0,
+        MessagePriority::LOW => 1,
+        MessagePriority::NORMAL => 2,
+        MessagePriority::HIGH => 3,
+        MessagePriority::VERY_HIGH => 4,
+    ];
+
+    /**
+     * @param DbalContext       $context
+     * @param Config            $config
+     * @param QueueMetaRegistry $queueMetaRegistry
+     */
+    public function __construct(DbalContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
+    {
+        $this->context = $context;
+        $this->config = $config;
+        $this->queueMetaRegistry = $queueMetaRegistry;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return DbalMessage
+     */
+    public function createTransportMessage(Message $message)
+    {
+        $properties = $message->getProperties();
+
+        $headers = $message->getHeaders();
+        $headers['content_type'] = $message->getContentType();
+
+        $transportMessage = $this->context->createMessage();
+        $transportMessage->setBody($message->getBody());
+        $transportMessage->setHeaders($headers);
+        $transportMessage->setProperties($properties);
+        $transportMessage->setMessageId($message->getMessageId());
+        $transportMessage->setTimestamp($message->getTimestamp());
+
+        $delay = $message->getDelay();
+        $transportMessage->setDeliveryDelay((null === $delay) ? null : ($delay * 1000));
+
+        $timeToLive = $message->getExpire();
+        $transportMessage->setTimeToLive((null === $timeToLive) ? null : ($timeToLive * 1000));
+        $transportMessage->setReplyTo($message->getReplyTo());
+        $transportMessage->setCorrelationId($message->getCorrelationId());
+        if (array_key_exists($message->getPriority(), self::$priorityMap)) {
+            $transportMessage->setPriority(self::$priorityMap[$message->getPriority()]);
+        }
+
+        return $transportMessage;
+    }
+
+    /**
+     * @param DbalMessage $message
+     *
+     * {@inheritdoc}
+     */
+    public function createClientMessage(PsrMessage $message)
+    {
+        $clientMessage = new Message();
+
+        $clientMessage->setBody($message->getBody());
+        $clientMessage->setHeaders($message->getHeaders());
+        $clientMessage->setProperties($message->getProperties());
+
+        $clientMessage->setContentType($message->getHeader('content_type'));
+        $clientMessage->setMessageId($message->getMessageId());
+        $clientMessage->setTimestamp($message->getTimestamp());
+
+        $timeToLive = $message->getTimeToLive();
+        $clientMessage->setExpire((null === $timeToLive) ? null : (int) ($timeToLive / 1000));
+
+        $delay = $message->getDeliveryDelay();
+        $clientMessage->setDelay((null === $delay) ? null : (int) ($delay / 1000));
+        $clientMessage->setReplyTo($message->getReplyTo());
+        $clientMessage->setCorrelationId($message->getCorrelationId());
+
+        $priorityMap = array_flip(self::$priorityMap);
+        $priority = array_key_exists($message->getPriority(), $priorityMap) ?
+            $priorityMap[$message->getPriority()] :
+            MessagePriority::NORMAL;
+        $clientMessage->setPriority($priority);
+
+        return $clientMessage;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToRouter(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
+            throw new \LogicException('Topic name parameter is required but is not set');
+        }
+
+        $queue = $this->createQueue($this->config->getRouterQueueName());
+        $transportMessage = $this->createTransportMessage($message);
+
+        $this->context->createProducer()->send($queue, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToProcessor(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
+            throw new \LogicException('Processor name parameter is required but is not set');
+        }
+
+        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
+            throw new \LogicException('Queue name parameter is required but is not set');
+        }
+
+        $transportMessage = $this->createTransportMessage($message);
+        $destination = $this->createQueue($queueName);
+
+        $this->context->createProducer()->send($destination, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createQueue($queueName)
+    {
+        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
+
+        return $this->context->createQueue($transportName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setupBroker(LoggerInterface $logger = null)
+    {
+        $logger = $logger ?: new NullLogger();
+        $log = function ($text, ...$args) use ($logger) {
+            $logger->debug(sprintf('[DbalDriver] '.$text, ...$args));
+        };
+
+        $log('Creating database table: "%s"', $this->context->getTableName());
+        $this->context->createDataBaseTable();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getConfig()
+    {
+        return $this->config;
+    }
+
+    /**
+     * @return array
+     */
+    public static function getPriorityMap()
+    {
+        return self::$priorityMap;
+    }
+}
diff --git a/Client/Driver/FsDriver.php b/Client/Driver/FsDriver.php
new file mode 100644
index 0000000..ea91989
--- /dev/null
+++ b/Client/Driver/FsDriver.php
@@ -0,0 +1,185 @@
+<?php
+
+namespace  Enqueue\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Fs\FsContext;
+use Enqueue\Fs\FsDestination;
+use Enqueue\Fs\FsMessage;
+use Interop\Queue\PsrMessage;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
+class FsDriver implements DriverInterface
+{
+    /**
+     * @var FsContext
+     */
+    private $context;
+
+    /**
+     * @var Config
+     */
+    private $config;
+
+    /**
+     * @var QueueMetaRegistry
+     */
+    private $queueMetaRegistry;
+
+    /**
+     * @param FsContext         $context
+     * @param Config            $config
+     * @param QueueMetaRegistry $queueMetaRegistry
+     */
+    public function __construct(FsContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
+    {
+        $this->context = $context;
+        $this->config = $config;
+        $this->queueMetaRegistry = $queueMetaRegistry;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToRouter(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
+            throw new \LogicException('Topic name parameter is required but is not set');
+        }
+
+        $topic = $this->createRouterTopic();
+        $transportMessage = $this->createTransportMessage($message);
+
+        $this->context->createProducer()->send($topic, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToProcessor(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
+            throw new \LogicException('Processor name parameter is required but is not set');
+        }
+
+        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
+            throw new \LogicException('Queue name parameter is required but is not set');
+        }
+
+        $transportMessage = $this->createTransportMessage($message);
+        $destination = $this->createQueue($queueName);
+
+        $this->context->createProducer()->send($destination, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setupBroker(LoggerInterface $logger = null)
+    {
+        $logger = $logger ?: new NullLogger();
+        $log = function ($text, ...$args) use ($logger) {
+            $logger->debug(sprintf('[FsDriver] '.$text, ...$args));
+        };
+
+        // setup router
+        $routerTopic = $this->createRouterTopic();
+        $routerQueue = $this->createQueue($this->config->getRouterQueueName());
+
+        $log('Declare router exchange "%s" file: %s', $routerTopic->getTopicName(), $routerTopic->getFileInfo());
+        $this->context->declareDestination($routerTopic);
+
+        $log('Declare router queue "%s" file: %s', $routerQueue->getQueueName(), $routerTopic->getFileInfo());
+        $this->context->declareDestination($routerQueue);
+
+        // setup queues
+        foreach ($this->queueMetaRegistry->getQueuesMeta() as $meta) {
+            $queue = $this->createQueue($meta->getClientName());
+
+            $log('Declare processor queue "%s" file: %s', $queue->getQueueName(), $queue->getFileInfo());
+            $this->context->declareDestination($queue);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return FsDestination
+     */
+    public function createQueue($queueName)
+    {
+        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
+
+        return $this->context->createQueue($transportName);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return FsMessage
+     */
+    public function createTransportMessage(Message $message)
+    {
+        $properties = $message->getProperties();
+
+        $headers = $message->getHeaders();
+        $headers['content_type'] = $message->getContentType();
+
+        $transportMessage = $this->context->createMessage();
+        $transportMessage->setBody($message->getBody());
+        $transportMessage->setHeaders($headers);
+        $transportMessage->setProperties($properties);
+        $transportMessage->setMessageId($message->getMessageId());
+        $transportMessage->setTimestamp($message->getTimestamp());
+        $transportMessage->setReplyTo($message->getReplyTo());
+        $transportMessage->setCorrelationId($message->getCorrelationId());
+
+        return $transportMessage;
+    }
+
+    /**
+     * @param FsMessage $message
+     *
+     * {@inheritdoc}
+     */
+    public function createClientMessage(PsrMessage $message)
+    {
+        $clientMessage = new Message();
+
+        $clientMessage->setBody($message->getBody());
+        $clientMessage->setHeaders($message->getHeaders());
+        $clientMessage->setProperties($message->getProperties());
+
+        $clientMessage->setContentType($message->getHeader('content_type'));
+        $clientMessage->setMessageId($message->getMessageId());
+        $clientMessage->setTimestamp($message->getTimestamp());
+        $clientMessage->setPriority(MessagePriority::NORMAL);
+        $clientMessage->setReplyTo($message->getReplyTo());
+        $clientMessage->setCorrelationId($message->getCorrelationId());
+
+        return $clientMessage;
+    }
+
+    /**
+     * @return Config
+     */
+    public function getConfig()
+    {
+        return $this->config;
+    }
+
+    /**
+     * @return FsDestination
+     */
+    private function createRouterTopic()
+    {
+        return $this->context->createTopic(
+            $this->config->createTransportQueueName($this->config->getRouterTopicName())
+        );
+    }
+}
diff --git a/Client/Driver/GpsDriver.php b/Client/Driver/GpsDriver.php
new file mode 100644
index 0000000..aebb807
--- /dev/null
+++ b/Client/Driver/GpsDriver.php
@@ -0,0 +1,182 @@
+<?php
+
+namespace Enqueue\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Gps\GpsContext;
+use Enqueue\Gps\GpsMessage;
+use Enqueue\Gps\GpsQueue;
+use Enqueue\Gps\GpsTopic;
+use Interop\Queue\PsrMessage;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
+class GpsDriver implements DriverInterface
+{
+    /**
+     * @var GpsContext
+     */
+    private $context;
+
+    /**
+     * @var Config
+     */
+    private $config;
+
+    /**
+     * @var QueueMetaRegistry
+     */
+    private $queueMetaRegistry;
+
+    /**
+     * @param GpsContext        $context
+     * @param Config            $config
+     * @param QueueMetaRegistry $queueMetaRegistry
+     */
+    public function __construct(GpsContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
+    {
+        $this->context = $context;
+        $this->config = $config;
+        $this->queueMetaRegistry = $queueMetaRegistry;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToRouter(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
+            throw new \LogicException('Topic name parameter is required but is not set');
+        }
+
+        $topic = $this->createRouterTopic();
+        $transportMessage = $this->createTransportMessage($message);
+
+        $this->context->createProducer()->send($topic, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToProcessor(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
+            throw new \LogicException('Processor name parameter is required but is not set');
+        }
+
+        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
+            throw new \LogicException('Queue name parameter is required but is not set');
+        }
+
+        $transportMessage = $this->createTransportMessage($message);
+        $destination = $this->context->createTopic(
+            $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName())
+        ;
+
+        $this->context->createProducer()->send($destination, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setupBroker(LoggerInterface $logger = null)
+    {
+        $logger = $logger ?: new NullLogger();
+        $log = function ($text, ...$args) use ($logger) {
+            $logger->debug(sprintf('[GpsDriver] '.$text, ...$args));
+        };
+
+        // setup router
+        $routerTopic = $this->createRouterTopic();
+        $routerQueue = $this->createQueue($this->config->getRouterQueueName());
+
+        $log('Subscribe router topic to queue: %s -> %s', $routerTopic->getTopicName(), $routerQueue->getQueueName());
+        $this->context->subscribe($routerTopic, $routerQueue);
+
+        // setup queues
+        foreach ($this->queueMetaRegistry->getQueuesMeta() as $meta) {
+            $topic = $this->context->createTopic($meta->getTransportName());
+            $queue = $this->context->createQueue($meta->getTransportName());
+
+            $log('Subscribe processor topic to queue: %s -> %s', $topic->getTopicName(), $queue->getQueueName());
+            $this->context->subscribe($topic, $queue);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return GpsQueue
+     */
+    public function createQueue($queueName)
+    {
+        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
+
+        return $this->context->createQueue($transportName);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return GpsMessage
+     */
+    public function createTransportMessage(Message $message)
+    {
+        $headers = $message->getHeaders();
+        $properties = $message->getProperties();
+
+        $transportMessage = $this->context->createMessage();
+        $transportMessage->setBody($message->getBody());
+        $transportMessage->setHeaders($headers);
+        $transportMessage->setProperties($properties);
+        $transportMessage->setMessageId($message->getMessageId());
+        $transportMessage->setTimestamp($message->getTimestamp());
+        $transportMessage->setReplyTo($message->getReplyTo());
+        $transportMessage->setCorrelationId($message->getCorrelationId());
+
+        return $transportMessage;
+    }
+
+    /**
+     * @param GpsMessage $message
+     *
+     * {@inheritdoc}
+     */
+    public function createClientMessage(PsrMessage $message)
+    {
+        $clientMessage = new Message();
+
+        $clientMessage->setBody($message->getBody());
+        $clientMessage->setHeaders($message->getHeaders());
+        $clientMessage->setProperties($message->getProperties());
+        $clientMessage->setMessageId($message->getMessageId());
+        $clientMessage->setTimestamp($message->getTimestamp());
+        $clientMessage->setReplyTo($message->getReplyTo());
+        $clientMessage->setCorrelationId($message->getCorrelationId());
+
+        return $clientMessage;
+    }
+
+    /**
+     * @return Config
+     */
+    public function getConfig()
+    {
+        return $this->config;
+    }
+
+    /**
+     * @return GpsTopic
+     */
+    private function createRouterTopic()
+    {
+        $topic = $this->context->createTopic(
+            $this->config->createTransportRouterTopicName($this->config->getRouterTopicName())
+        );
+
+        return $topic;
+    }
+}
diff --git a/Client/Driver/MongodbDriver.php b/Client/Driver/MongodbDriver.php
new file mode 100644
index 0000000..b2e3ca3
--- /dev/null
+++ b/Client/Driver/MongodbDriver.php
@@ -0,0 +1,186 @@
+<?php
+
+namespace Enqueue\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Mongodb\MongodbContext;
+use Enqueue\Mongodb\MongodbMessage;
+use Interop\Queue\PsrMessage;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
+class MongodbDriver implements DriverInterface
+{
+    /**
+     * @var MongodbContext
+     */
+    private $context;
+
+    /**
+     * @var Config
+     */
+    private $config;
+
+    /**
+     * @var QueueMetaRegistry
+     */
+    private $queueMetaRegistry;
+
+    /**
+     * @var array
+     */
+    private static $priorityMap = [
+        MessagePriority::VERY_LOW => 0,
+        MessagePriority::LOW => 1,
+        MessagePriority::NORMAL => 2,
+        MessagePriority::HIGH => 3,
+        MessagePriority::VERY_HIGH => 4,
+    ];
+
+    /**
+     * @param MongodbContext    $context
+     * @param Config            $config
+     * @param QueueMetaRegistry $queueMetaRegistry
+     */
+    public function __construct(MongodbContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
+    {
+        $this->context = $context;
+        $this->config = $config;
+        $this->queueMetaRegistry = $queueMetaRegistry;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return MongodbMessage
+     */
+    public function createTransportMessage(Message $message)
+    {
+        $properties = $message->getProperties();
+
+        $headers = $message->getHeaders();
+        $headers['content_type'] = $message->getContentType();
+
+        $transportMessage = $this->context->createMessage();
+        $transportMessage->setBody($message->getBody());
+        $transportMessage->setHeaders($headers);
+        $transportMessage->setProperties($properties);
+        $transportMessage->setMessageId($message->getMessageId());
+        $transportMessage->setTimestamp($message->getTimestamp());
+        $transportMessage->setDeliveryDelay($message->getDelay());
+        $transportMessage->setReplyTo($message->getReplyTo());
+        $transportMessage->setCorrelationId($message->getCorrelationId());
+        if (array_key_exists($message->getPriority(), self::$priorityMap)) {
+            $transportMessage->setPriority(self::$priorityMap[$message->getPriority()]);
+        }
+
+        return $transportMessage;
+    }
+
+    /**
+     * @param MongodbMessage $message
+     *
+     * {@inheritdoc}
+     */
+    public function createClientMessage(PsrMessage $message)
+    {
+        $clientMessage = new Message();
+
+        $clientMessage->setBody($message->getBody());
+        $clientMessage->setHeaders($message->getHeaders());
+        $clientMessage->setProperties($message->getProperties());
+
+        $clientMessage->setContentType($message->getHeader('content_type'));
+        $clientMessage->setMessageId($message->getMessageId());
+        $clientMessage->setTimestamp($message->getTimestamp());
+        $clientMessage->setDelay($message->getDeliveryDelay());
+        $clientMessage->setReplyTo($message->getReplyTo());
+        $clientMessage->setCorrelationId($message->getCorrelationId());
+
+        $priorityMap = array_flip(self::$priorityMap);
+        $priority = array_key_exists($message->getPriority(), $priorityMap) ?
+            $priorityMap[$message->getPriority()] :
+            MessagePriority::NORMAL;
+        $clientMessage->setPriority($priority);
+
+        return $clientMessage;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToRouter(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
+            throw new \LogicException('Topic name parameter is required but is not set');
+        }
+
+        $queue = $this->createQueue($this->config->getRouterQueueName());
+        $transportMessage = $this->createTransportMessage($message);
+
+        $this->context->createProducer()->send($queue, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToProcessor(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
+            throw new \LogicException('Processor name parameter is required but is not set');
+        }
+
+        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
+            throw new \LogicException('Queue name parameter is required but is not set');
+        }
+
+        $transportMessage = $this->createTransportMessage($message);
+        $destination = $this->createQueue($queueName);
+
+        $this->context->createProducer()->send($destination, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createQueue($queueName)
+    {
+        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
+
+        return $this->context->createQueue($transportName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setupBroker(LoggerInterface $logger = null)
+    {
+        $logger = $logger ?: new NullLogger();
+        $log = function ($text, ...$args) use ($logger) {
+            $logger->debug(sprintf('[MongodbDriver] '.$text, ...$args));
+        };
+        $contextConfig = $this->context->getConfig();
+        $log('Creating database and collection: "%s" "%s"', $contextConfig['dbname'], $contextConfig['collection_name']);
+        $this->context->createCollection();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getConfig()
+    {
+        return $this->config;
+    }
+
+    /**
+     * @return array
+     */
+    public static function getPriorityMap()
+    {
+        return self::$priorityMap;
+    }
+}
diff --git a/Client/Driver/NullDriver.php b/Client/Driver/NullDriver.php
new file mode 100644
index 0000000..830df17
--- /dev/null
+++ b/Client/Driver/NullDriver.php
@@ -0,0 +1,160 @@
+<?php
+
+namespace Enqueue\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Null\NullContext;
+use Enqueue\Null\NullMessage;
+use Interop\Queue\PsrMessage;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
+class NullDriver implements DriverInterface
+{
+    /**
+     * @var NullContext
+     */
+    protected $context;
+
+    /**
+     * @var Config
+     */
+    protected $config;
+
+    /**
+     * @var QueueMetaRegistry
+     */
+    private $queueMetaRegistry;
+
+    /**
+     * @param NullContext       $context
+     * @param Config            $config
+     * @param QueueMetaRegistry $queueMetaRegistry
+     */
+    public function __construct(NullContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
+    {
+        $this->context = $context;
+        $this->config = $config;
+        $this->queueMetaRegistry = $queueMetaRegistry;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return NullMessage
+     */
+    public function createTransportMessage(Message $message)
+    {
+        $headers = $message->getHeaders();
+        $headers['content_type'] = $message->getContentType();
+        $headers['expiration'] = $message->getExpire();
+        $headers['delay'] = $message->getDelay();
+        $headers['priority'] = $message->getPriority();
+
+        $transportMessage = $this->context->createMessage();
+        $transportMessage->setBody($message->getBody());
+        $transportMessage->setHeaders($headers);
+        $transportMessage->setProperties($message->getProperties());
+        $transportMessage->setTimestamp($message->getTimestamp());
+        $transportMessage->setMessageId($message->getMessageId());
+        $transportMessage->setReplyTo($message->getReplyTo());
+        $transportMessage->setCorrelationId($message->getCorrelationId());
+
+        return $transportMessage;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @param NullMessage $message
+     */
+    public function createClientMessage(PsrMessage $message)
+    {
+        $clientMessage = new Message();
+        $clientMessage->setBody($message->getBody());
+        $clientMessage->setHeaders($message->getHeaders());
+        $clientMessage->setProperties($message->getProperties());
+        $clientMessage->setTimestamp($message->getTimestamp());
+        $clientMessage->setMessageId($message->getMessageId());
+        $clientMessage->setReplyTo($message->getReplyTo());
+        $clientMessage->setCorrelationId($message->getCorrelationId());
+
+        if ($contentType = $message->getHeader('content_type')) {
+            $clientMessage->setContentType($contentType);
+        }
+
+        if ($expiration = $message->getHeader('expiration')) {
+            $clientMessage->setExpire($expiration);
+        }
+
+        if ($delay = $message->getHeader('delay')) {
+            $clientMessage->setDelay($delay);
+        }
+
+        if ($priority = $message->getHeader('priority')) {
+            $clientMessage->setPriority($priority);
+        }
+
+        return $clientMessage;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createQueue($queueName)
+    {
+        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
+
+        return $this->context->createQueue($transportName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getConfig()
+    {
+        return $this->config;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToRouter(Message $message)
+    {
+        $transportMessage = $this->createTransportMessage($message);
+        $topic = $this->context->createTopic(
+            $this->config->createTransportRouterTopicName(
+                $this->config->getRouterTopicName()
+            )
+        );
+
+        $this->context->createProducer()->send($topic, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToProcessor(Message $message)
+    {
+        $transportMessage = $this->createTransportMessage($message);
+        $queue = $this->context->createQueue(
+            $this->config->createTransportQueueName(
+                $this->config->getRouterQueueName()
+            )
+        );
+
+        $this->context->createProducer()->send($queue, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setupBroker(LoggerInterface $logger = null)
+    {
+        $logger ?: new NullLogger();
+        $logger->debug('[NullDriver] setup broker');
+    }
+}
diff --git a/Client/Amqp/RabbitMqDriver.php b/Client/Driver/RabbitMqDriver.php
similarity index 99%
rename from Client/Amqp/RabbitMqDriver.php
rename to Client/Driver/RabbitMqDriver.php
index 518b900..dec197a 100644
--- a/Client/Amqp/RabbitMqDriver.php
+++ b/Client/Driver/RabbitMqDriver.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace  Enqueue\Client\Amqp;
+namespace  Enqueue\Client\Driver;
 
 use Enqueue\Client\Config;
 use Enqueue\Client\Message;
diff --git a/Client/Driver/RabbitMqStompDriver.php b/Client/Driver/RabbitMqStompDriver.php
new file mode 100644
index 0000000..df005f6
--- /dev/null
+++ b/Client/Driver/RabbitMqStompDriver.php
@@ -0,0 +1,269 @@
+<?php
+
+namespace Enqueue\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\Message;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Stomp\StompContext;
+use Enqueue\Stomp\StompDestination;
+use Enqueue\Stomp\StompMessage;
+use Interop\Queue\PsrMessage;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
+class RabbitMqStompDriver extends StompDriver
+{
+    /**
+     * @var StompContext
+     */
+    private $context;
+
+    /**
+     * @var Config
+     */
+    private $config;
+
+    /**
+     * @var array
+     */
+    private $priorityMap;
+
+    /**
+     * @var StompManagementClient
+     */
+    private $management;
+
+    /**
+     * @var QueueMetaRegistry
+     */
+    private $queueMetaRegistry;
+
+    /**
+     * @param StompContext          $context
+     * @param Config                $config
+     * @param QueueMetaRegistry     $queueMetaRegistry
+     * @param StompManagementClient $management
+     */
+    public function __construct(StompContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry, StompManagementClient $management)
+    {
+        parent::__construct($context, $config, $queueMetaRegistry);
+
+        $this->context = $context;
+        $this->config = $config;
+        $this->queueMetaRegistry = $queueMetaRegistry;
+        $this->management = $management;
+
+        $this->priorityMap = [
+            MessagePriority::VERY_LOW => 0,
+            MessagePriority::LOW => 1,
+            MessagePriority::NORMAL => 2,
+            MessagePriority::HIGH => 3,
+            MessagePriority::VERY_HIGH => 4,
+        ];
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return StompMessage
+     */
+    public function createTransportMessage(Message $message)
+    {
+        $transportMessage = parent::createTransportMessage($message);
+
+        if ($message->getExpire()) {
+            $transportMessage->setHeader('expiration', (string) ($message->getExpire() * 1000));
+        }
+
+        if ($priority = $message->getPriority()) {
+            if (false == array_key_exists($priority, $this->priorityMap)) {
+                throw new \LogicException(sprintf('Cant convert client priority to transport: "%s"', $priority));
+            }
+
+            $transportMessage->setHeader('priority', $this->priorityMap[$priority]);
+        }
+
+        if ($message->getDelay()) {
+            if (false == $this->config->getTransportOption('delay_plugin_installed', false)) {
+                throw new \LogicException('The message delaying is not supported. In order to use delay feature install RabbitMQ delay plugin.');
+            }
+
+            $transportMessage->setHeader('x-delay', (string) ($message->getDelay() * 1000));
+        }
+
+        return $transportMessage;
+    }
+
+    /**
+     * @param StompMessage $message
+     *
+     * {@inheritdoc}
+     */
+    public function createClientMessage(PsrMessage $message)
+    {
+        $clientMessage = parent::createClientMessage($message);
+
+        $headers = $clientMessage->getHeaders();
+        unset(
+            $headers['x-delay'],
+            $headers['expiration'],
+            $headers['priority']
+        );
+        $clientMessage->setHeaders($headers);
+
+        if ($delay = $message->getHeader('x-delay')) {
+            if (false == is_numeric($delay)) {
+                throw new \LogicException(sprintf('x-delay header is not numeric. "%s"', $delay));
+            }
+
+            $clientMessage->setDelay((int) ((int) $delay) / 1000);
+        }
+
+        if ($expiration = $message->getHeader('expiration')) {
+            if (false == is_numeric($expiration)) {
+                throw new \LogicException(sprintf('expiration header is not numeric. "%s"', $expiration));
+            }
+
+            $clientMessage->setExpire((int) ((int) $expiration) / 1000);
+        }
+
+        if ($priority = $message->getHeader('priority')) {
+            if (false === $clientPriority = array_search($priority, $this->priorityMap, true)) {
+                throw new \LogicException(sprintf('Cant convert transport priority to client: "%s"', $priority));
+            }
+
+            $clientMessage->setPriority($clientPriority);
+        }
+
+        return $clientMessage;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToProcessor(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
+            throw new \LogicException('Processor name parameter is required but is not set');
+        }
+
+        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
+            throw new \LogicException('Queue name parameter is required but is not set');
+        }
+
+        $transportMessage = $this->createTransportMessage($message);
+        $destination = $this->createQueue($queueName);
+
+        if ($message->getDelay()) {
+            $destination = $this->createDelayedTopic($destination);
+        }
+
+        $this->context->createProducer()->send($destination, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createQueue($queueName)
+    {
+        $queue = parent::createQueue($queueName);
+        $queue->setHeader('x-max-priority', 4);
+
+        return $queue;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setupBroker(LoggerInterface $logger = null)
+    {
+        $logger = $logger ?: new NullLogger();
+        $log = function ($text, ...$args) use ($logger) {
+            $logger->debug(sprintf('[RabbitMqStompDriver] '.$text, ...$args));
+        };
+
+        if (false == $this->config->getTransportOption('management_plugin_installed', false)) {
+            $log('Could not setup broker. The option `management_plugin_installed` is not enabled. Please enable that option and install rabbit management plugin');
+
+            return;
+        }
+
+        // setup router
+        $routerExchange = $this->config->createTransportRouterTopicName($this->config->getRouterTopicName());
+        $log('Declare router exchange: %s', $routerExchange);
+        $this->management->declareExchange($routerExchange, [
+            'type' => 'fanout',
+            'durable' => true,
+            'auto_delete' => false,
+        ]);
+
+        $routerQueue = $this->config->createTransportQueueName($this->config->getRouterQueueName());
+        $log('Declare router queue: %s', $routerQueue);
+        $this->management->declareQueue($routerQueue, [
+            'auto_delete' => false,
+            'durable' => true,
+            'arguments' => [
+                'x-max-priority' => 4,
+            ],
+        ]);
+
+        $log('Bind router queue to exchange: %s -> %s', $routerQueue, $routerExchange);
+        $this->management->bind($routerExchange, $routerQueue, $routerQueue);
+
+        // setup queues
+        foreach ($this->queueMetaRegistry->getQueuesMeta() as $meta) {
+            $queue = $this->config->createTransportQueueName($meta->getClientName());
+
+            $log('Declare processor queue: %s', $queue);
+            $this->management->declareQueue($queue, [
+                'auto_delete' => false,
+                'durable' => true,
+                'arguments' => [
+                    'x-max-priority' => 4,
+                ],
+            ]);
+        }
+
+        // setup delay exchanges
+        if ($this->config->getTransportOption('delay_plugin_installed', false)) {
+            foreach ($this->queueMetaRegistry->getQueuesMeta() as $meta) {
+                $queue = $this->config->createTransportQueueName($meta->getClientName());
+                $delayExchange = $queue.'.delayed';
+
+                $log('Declare delay exchange: %s', $delayExchange);
+                $this->management->declareExchange($delayExchange, [
+                    'type' => 'x-delayed-message',
+                    'durable' => true,
+                    'auto_delete' => false,
+                    'arguments' => [
+                        'x-delayed-type' => 'direct',
+                    ],
+                ]);
+
+                $log('Bind processor queue to delay exchange: %s -> %s', $queue, $delayExchange);
+                $this->management->bind($delayExchange, $queue, $queue);
+            }
+        } else {
+            $log('Delay exchange and bindings are not setup. if you\'d like to use delays please install delay rabbitmq plugin and set delay_plugin_installed option to true');
+        }
+    }
+
+    /**
+     * @param StompDestination $queue
+     *
+     * @return StompDestination
+     */
+    private function createDelayedTopic(StompDestination $queue)
+    {
+        // in order to use delay feature make sure the rabbitmq_delayed_message_exchange plugin is installed.
+        $destination = $this->context->createTopic($queue->getStompName().'.delayed');
+        $destination->setType(StompDestination::TYPE_EXCHANGE);
+        $destination->setDurable(true);
+        $destination->setAutoDelete(false);
+        $destination->setRoutingKey($queue->getStompName());
+
+        return $destination;
+    }
+}
diff --git a/Client/Driver/RdKafkaDriver.php b/Client/Driver/RdKafkaDriver.php
new file mode 100644
index 0000000..6c606ff
--- /dev/null
+++ b/Client/Driver/RdKafkaDriver.php
@@ -0,0 +1,167 @@
+<?php
+
+namespace Enqueue\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\RdKafka\RdKafkaContext;
+use Interop\Queue\PsrMessage;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
+class RdKafkaDriver implements DriverInterface
+{
+    /**
+     * @var RdKafkaContext
+     */
+    private $context;
+
+    /**
+     * @var Config
+     */
+    private $config;
+
+    /**
+     * @var QueueMetaRegistry
+     */
+    private $queueMetaRegistry;
+
+    /**
+     * @param RdKafkaContext    $context
+     * @param Config            $config
+     * @param QueueMetaRegistry $queueMetaRegistry
+     */
+    public function __construct(RdKafkaContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
+    {
+        $this->context = $context;
+        $this->config = $config;
+        $this->queueMetaRegistry = $queueMetaRegistry;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createTransportMessage(Message $message)
+    {
+        $headers = $message->getHeaders();
+        $headers['content_type'] = $message->getContentType();
+
+        $transportMessage = $this->context->createMessage();
+        $transportMessage->setBody($message->getBody());
+        $transportMessage->setHeaders($headers);
+        $transportMessage->setProperties($message->getProperties());
+        $transportMessage->setMessageId($message->getMessageId());
+        $transportMessage->setTimestamp($message->getTimestamp());
+        $transportMessage->setReplyTo($message->getReplyTo());
+        $transportMessage->setCorrelationId($message->getCorrelationId());
+
+        return $transportMessage;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createClientMessage(PsrMessage $message)
+    {
+        $clientMessage = new Message();
+        $clientMessage->setBody($message->getBody());
+        $clientMessage->setHeaders($message->getHeaders());
+        $clientMessage->setProperties($message->getProperties());
+
+        $clientMessage->setContentType($message->getHeader('content_type'));
+
+        $clientMessage->setTimestamp($message->getTimestamp());
+        $clientMessage->setMessageId($message->getMessageId());
+        $clientMessage->setReplyTo($message->getReplyTo());
+        $clientMessage->setCorrelationId($message->getCorrelationId());
+
+        return $clientMessage;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToRouter(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
+            throw new \LogicException('Topic name parameter is required but is not set');
+        }
+
+        $topic = $this->createRouterTopic();
+        $transportMessage = $this->createTransportMessage($message);
+
+        $this->context->createProducer()->send($topic, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToProcessor(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
+            throw new \LogicException('Processor name parameter is required but is not set');
+        }
+
+        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
+            throw new \LogicException('Queue name parameter is required but is not set');
+        }
+
+        $transportMessage = $this->createTransportMessage($message);
+        $destination = $this->createQueue($queueName);
+
+        $this->context->createProducer()->send($destination, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createQueue($queueName)
+    {
+        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
+
+        return $this->context->createQueue($transportName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setupBroker(LoggerInterface $logger = null)
+    {
+        $logger = $logger ?: new NullLogger();
+        $logger->debug('[RdKafkaDriver] setup broker');
+        $log = function ($text, ...$args) use ($logger) {
+            $logger->debug(sprintf('[RdKafkaDriver] '.$text, ...$args));
+        };
+
+        // setup router
+        $routerQueue = $this->createQueue($this->config->getRouterQueueName());
+        $log('Create router queue: %s', $routerQueue->getQueueName());
+        $this->context->createConsumer($routerQueue);
+
+        // setup queues
+        foreach ($this->queueMetaRegistry->getQueuesMeta() as $meta) {
+            $queue = $this->createQueue($meta->getClientName());
+            $log('Create processor queue: %s', $queue->getQueueName());
+            $this->context->createConsumer($queue);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getConfig()
+    {
+        return $this->config;
+    }
+
+    private function createRouterTopic()
+    {
+        $topic = $this->context->createTopic(
+            $this->config->createTransportRouterTopicName($this->config->getRouterTopicName())
+        );
+
+        return $topic;
+    }
+}
diff --git a/Client/Driver/RedisDriver.php b/Client/Driver/RedisDriver.php
new file mode 100644
index 0000000..62a7383
--- /dev/null
+++ b/Client/Driver/RedisDriver.php
@@ -0,0 +1,152 @@
+<?php
+
+namespace  Enqueue\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Redis\RedisContext;
+use Enqueue\Redis\RedisDestination;
+use Enqueue\Redis\RedisMessage;
+use Interop\Queue\PsrMessage;
+use Psr\Log\LoggerInterface;
+
+class RedisDriver implements DriverInterface
+{
+    /**
+     * @var RedisContext
+     */
+    private $context;
+
+    /**
+     * @var Config
+     */
+    private $config;
+
+    /**
+     * @var QueueMetaRegistry
+     */
+    private $queueMetaRegistry;
+
+    /**
+     * @param RedisContext      $context
+     * @param Config            $config
+     * @param QueueMetaRegistry $queueMetaRegistry
+     */
+    public function __construct(RedisContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
+    {
+        $this->context = $context;
+        $this->config = $config;
+        $this->queueMetaRegistry = $queueMetaRegistry;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToRouter(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
+            throw new \LogicException('Topic name parameter is required but is not set');
+        }
+
+        $queue = $this->createQueue($this->config->getRouterQueueName());
+        $transportMessage = $this->createTransportMessage($message);
+
+        $this->context->createProducer()->send($queue, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToProcessor(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
+            throw new \LogicException('Processor name parameter is required but is not set');
+        }
+
+        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
+            throw new \LogicException('Queue name parameter is required but is not set');
+        }
+
+        $transportMessage = $this->createTransportMessage($message);
+        $destination = $this->createQueue($queueName);
+
+        $this->context->createProducer()->send($destination, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setupBroker(LoggerInterface $logger = null)
+    {
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return RedisDestination
+     */
+    public function createQueue($queueName)
+    {
+        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
+
+        return $this->context->createQueue($transportName);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return RedisMessage
+     */
+    public function createTransportMessage(Message $message)
+    {
+        $properties = $message->getProperties();
+
+        $headers = $message->getHeaders();
+        $headers['content_type'] = $message->getContentType();
+
+        $transportMessage = $this->context->createMessage();
+        $transportMessage->setBody($message->getBody());
+        $transportMessage->setHeaders($headers);
+        $transportMessage->setProperties($properties);
+        $transportMessage->setMessageId($message->getMessageId());
+        $transportMessage->setTimestamp($message->getTimestamp());
+        $transportMessage->setReplyTo($message->getReplyTo());
+        $transportMessage->setCorrelationId($message->getCorrelationId());
+
+        return $transportMessage;
+    }
+
+    /**
+     * @param RedisMessage $message
+     *
+     * {@inheritdoc}
+     */
+    public function createClientMessage(PsrMessage $message)
+    {
+        $clientMessage = new Message();
+
+        $clientMessage->setBody($message->getBody());
+        $clientMessage->setHeaders($message->getHeaders());
+        $clientMessage->setProperties($message->getProperties());
+
+        $clientMessage->setContentType($message->getHeader('content_type'));
+        $clientMessage->setMessageId($message->getMessageId());
+        $clientMessage->setTimestamp($message->getTimestamp());
+        $clientMessage->setPriority(MessagePriority::NORMAL);
+        $clientMessage->setReplyTo($message->getReplyTo());
+        $clientMessage->setCorrelationId($message->getCorrelationId());
+
+        return $clientMessage;
+    }
+
+    /**
+     * @return Config
+     */
+    public function getConfig()
+    {
+        return $this->config;
+    }
+}
diff --git a/Client/Driver/SqsDriver.php b/Client/Driver/SqsDriver.php
new file mode 100644
index 0000000..08a8a08
--- /dev/null
+++ b/Client/Driver/SqsDriver.php
@@ -0,0 +1,171 @@
+<?php
+
+namespace Enqueue\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Sqs\SqsContext;
+use Enqueue\Sqs\SqsDestination;
+use Enqueue\Sqs\SqsMessage;
+use Interop\Queue\PsrMessage;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
+class SqsDriver implements DriverInterface
+{
+    /**
+     * @var SqsContext
+     */
+    private $context;
+
+    /**
+     * @var Config
+     */
+    private $config;
+
+    /**
+     * @var QueueMetaRegistry
+     */
+    private $queueMetaRegistry;
+
+    /**
+     * @param SqsContext        $context
+     * @param Config            $config
+     * @param QueueMetaRegistry $queueMetaRegistry
+     */
+    public function __construct(SqsContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
+    {
+        $this->context = $context;
+        $this->config = $config;
+        $this->queueMetaRegistry = $queueMetaRegistry;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToRouter(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
+            throw new \LogicException('Topic name parameter is required but is not set');
+        }
+
+        $queue = $this->createQueue($this->config->getRouterQueueName());
+        $transportMessage = $this->createTransportMessage($message);
+
+        $this->context->createProducer()->send($queue, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToProcessor(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
+            throw new \LogicException('Processor name parameter is required but is not set');
+        }
+
+        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
+            throw new \LogicException('Queue name parameter is required but is not set');
+        }
+
+        $transportMessage = $this->createTransportMessage($message);
+        $destination = $this->createQueue($queueName);
+
+        $this->context->createProducer()->send($destination, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return SqsDestination
+     */
+    public function createQueue($queueName)
+    {
+        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
+        $transportName = str_replace('.', '_dot_', $transportName);
+
+        return $this->context->createQueue($transportName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setupBroker(LoggerInterface $logger = null)
+    {
+        $logger = $logger ?: new NullLogger();
+        $log = function ($text, ...$args) use ($logger) {
+            $logger->debug(sprintf('[SqsDriver] '.$text, ...$args));
+        };
+
+        // setup router
+        $routerQueue = $this->createQueue($this->config->getRouterQueueName());
+        $log('Declare router queue: %s', $routerQueue->getQueueName());
+        $this->context->declareQueue($routerQueue);
+
+        // setup queues
+        foreach ($this->queueMetaRegistry->getQueuesMeta() as $meta) {
+            $queue = $this->createQueue($meta->getClientName());
+
+            $log('Declare processor queue: %s', $queue->getQueueName());
+            $this->context->declareQueue($queue);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return SqsMessage
+     */
+    public function createTransportMessage(Message $message)
+    {
+        $properties = $message->getProperties();
+
+        $headers = $message->getHeaders();
+        $headers['content_type'] = $message->getContentType();
+
+        $transportMessage = $this->context->createMessage();
+        $transportMessage->setBody($message->getBody());
+        $transportMessage->setHeaders($headers);
+        $transportMessage->setProperties($properties);
+        $transportMessage->setMessageId($message->getMessageId());
+        $transportMessage->setTimestamp($message->getTimestamp());
+        $transportMessage->setReplyTo($message->getReplyTo());
+        $transportMessage->setCorrelationId($message->getCorrelationId());
+
+        return $transportMessage;
+    }
+
+    /**
+     * @param SqsMessage $message
+     *
+     * {@inheritdoc}
+     */
+    public function createClientMessage(PsrMessage $message)
+    {
+        $clientMessage = new Message();
+
+        $clientMessage->setBody($message->getBody());
+        $clientMessage->setHeaders($message->getHeaders());
+        $clientMessage->setProperties($message->getProperties());
+
+        $clientMessage->setContentType($message->getHeader('content_type'));
+        $clientMessage->setMessageId($message->getMessageId());
+        $clientMessage->setTimestamp($message->getTimestamp());
+        $clientMessage->setPriority(MessagePriority::NORMAL);
+        $clientMessage->setReplyTo($message->getReplyTo());
+        $clientMessage->setCorrelationId($message->getCorrelationId());
+
+        return $clientMessage;
+    }
+
+    /**
+     * @return Config
+     */
+    public function getConfig()
+    {
+        return $this->config;
+    }
+}
diff --git a/Client/Driver/StompDriver.php b/Client/Driver/StompDriver.php
new file mode 100644
index 0000000..9d824a6
--- /dev/null
+++ b/Client/Driver/StompDriver.php
@@ -0,0 +1,191 @@
+<?php
+
+namespace Enqueue\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Stomp\StompContext;
+use Enqueue\Stomp\StompDestination;
+use Enqueue\Stomp\StompMessage;
+use Interop\Queue\PsrMessage;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
+class StompDriver implements DriverInterface
+{
+    /**
+     * @var StompContext
+     */
+    private $context;
+
+    /**
+     * @var Config
+     */
+    private $config;
+
+    /**
+     * @var QueueMetaRegistry
+     */
+    private $queueMetaRegistry;
+
+    /**
+     * @param StompContext      $context
+     * @param Config            $config
+     * @param QueueMetaRegistry $queueMetaRegistry
+     */
+    public function __construct(StompContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
+    {
+        $this->context = $context;
+        $this->config = $config;
+        $this->queueMetaRegistry = $queueMetaRegistry;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToRouter(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
+            throw new \LogicException('Topic name parameter is required but is not set');
+        }
+
+        $topic = $this->createRouterTopic();
+        $transportMessage = $this->createTransportMessage($message);
+
+        $this->context->createProducer()->send($topic, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sendToProcessor(Message $message)
+    {
+        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
+            throw new \LogicException('Processor name parameter is required but is not set');
+        }
+
+        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
+            throw new \LogicException('Queue name parameter is required but is not set');
+        }
+
+        $transportMessage = $this->createTransportMessage($message);
+        $destination = $this->createQueue($queueName);
+
+        $this->context->createProducer()->send($destination, $transportMessage);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setupBroker(LoggerInterface $logger = null)
+    {
+        $logger = $logger ?: new NullLogger();
+        $logger->debug('[StompDriver] Stomp protocol does not support broker configuration');
+    }
+
+    /**
+     * @return StompMessage
+     *
+     * {@inheritdoc}
+     */
+    public function createTransportMessage(Message $message)
+    {
+        $headers = $message->getHeaders();
+        $headers['content-type'] = $message->getContentType();
+
+        $transportMessage = $this->context->createMessage();
+        $transportMessage->setHeaders($headers);
+        $transportMessage->setPersistent(true);
+        $transportMessage->setBody($message->getBody());
+        $transportMessage->setProperties($message->getProperties());
+
+        if ($message->getMessageId()) {
+            $transportMessage->setMessageId($message->getMessageId());
+        }
+
+        if ($message->getTimestamp()) {
+            $transportMessage->setTimestamp($message->getTimestamp());
+        }
+
+        if ($message->getReplyTo()) {
+            $transportMessage->setReplyTo($message->getReplyTo());
+        }
+
+        if ($message->getCorrelationId()) {
+            $transportMessage->setCorrelationId($message->getCorrelationId());
+        }
+
+        return $transportMessage;
+    }
+
+    /**
+     * @param StompMessage $message
+     *
+     * {@inheritdoc}
+     */
+    public function createClientMessage(PsrMessage $message)
+    {
+        $clientMessage = new Message();
+
+        $headers = $message->getHeaders();
+        unset(
+            $headers['content-type'],
+            $headers['message_id'],
+            $headers['timestamp'],
+            $headers['reply-to'],
+            $headers['correlation_id']
+        );
+
+        $clientMessage->setHeaders($headers);
+        $clientMessage->setBody($message->getBody());
+        $clientMessage->setProperties($message->getProperties());
+
+        $clientMessage->setContentType($message->getHeader('content-type'));
+
+        $clientMessage->setMessageId($message->getMessageId());
+        $clientMessage->setTimestamp($message->getTimestamp());
+        $clientMessage->setReplyTo($message->getReplyTo());
+        $clientMessage->setCorrelationId($message->getCorrelationId());
+
+        return $clientMessage;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createQueue($queueName)
+    {
+        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
+
+        $queue = $this->context->createQueue($transportName);
+        $queue->setDurable(true);
+        $queue->setAutoDelete(false);
+        $queue->setExclusive(false);
+
+        return $queue;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getConfig()
+    {
+        return $this->config;
+    }
+
+    /**
+     * @return StompDestination
+     */
+    private function createRouterTopic()
+    {
+        $topic = $this->context->createTopic(
+            $this->config->createTransportRouterTopicName($this->config->getRouterTopicName())
+        );
+        $topic->setDurable(true);
+        $topic->setAutoDelete(false);
+
+        return $topic;
+    }
+}
diff --git a/Client/Driver/StompManagementClient.php b/Client/Driver/StompManagementClient.php
new file mode 100644
index 0000000..410728b
--- /dev/null
+++ b/Client/Driver/StompManagementClient.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Enqueue\Client\Driver;
+
+use RabbitMq\ManagementApi\Client;
+
+class StompManagementClient
+{
+    /**
+     * @var Client
+     */
+    private $client;
+
+    /**
+     * @var string
+     */
+    private $vhost;
+
+    /**
+     * @param Client|null $client
+     * @param string      $vhost
+     */
+    public function __construct(Client $client, $vhost = '/')
+    {
+        $this->client = $client;
+        $this->vhost = $vhost;
+    }
+
+    /**
+     * @param string $vhost
+     * @param string $host
+     * @param int    $port
+     * @param string $login
+     * @param string $password
+     *
+     * @return self
+     */
+    public static function create($vhost = '/', $host = 'localhost', $port = 15672, $login = 'guest', $password = 'guest')
+    {
+        return new static(new Client(null, 'http://'.$host.':'.$port, $login, $password), $vhost);
+    }
+
+    /**
+     * @param string $name
+     * @param array  $options
+     *
+     * @return array
+     */
+    public function declareQueue($name, $options)
+    {
+        return $this->client->queues()->create($this->vhost, $name, $options);
+    }
+
+    /**
+     * @param string $name
+     * @param array  $options
+     *
+     * @return array
+     */
+    public function declareExchange($name, $options)
+    {
+        return $this->client->exchanges()->create($this->vhost, $name, $options);
+    }
+
+    /**
+     * @param string $exchange
+     * @param string $queue
+     * @param string $routingKey
+     * @param array  $arguments
+     *
+     * @return array
+     */
+    public function bind($exchange, $queue, $routingKey, $arguments = null)
+    {
+        return $this->client->bindings()->create($this->vhost, $exchange, $queue, $routingKey, $arguments);
+    }
+}
diff --git a/Client/DriverFactory.php b/Client/DriverFactory.php
index a82ec93..65841e6 100644
--- a/Client/DriverFactory.php
+++ b/Client/DriverFactory.php
@@ -2,11 +2,11 @@
 
 namespace Enqueue\Client;
 
-use Enqueue\Client\Amqp\RabbitMqDriver;
+use Enqueue\Client\Driver\RabbitMqDriver;
+use Enqueue\Client\Driver\RabbitMqStompDriver;
+use Enqueue\Client\Driver\StompManagementClient;
 use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\Dsn\Dsn;
-use Enqueue\Stomp\Client\ManagementClient;
-use Enqueue\Stomp\Client\RabbitMqStompDriver;
 use Enqueue\Stomp\StompConnectionFactory;
 use Interop\Amqp\AmqpConnectionFactory;
 use Interop\Queue\PsrConnectionFactory;
@@ -58,7 +58,7 @@ public function create(PsrConnectionFactory $factory, string $dsn, array $config
                 if (isset($config['rabbitmq_management_dsn'])) {
                     $managementDsn = new Dsn($config['rabbitmq_management_dsn']);
 
-                    $managementClient = ManagementClient::create(
+                    $managementClient = StompManagementClient::create(
                         ltrim($managementDsn->getPath(), '/'),
                         $managementDsn->getHost(),
                         $managementDsn->getPort(),
@@ -66,7 +66,7 @@ public function create(PsrConnectionFactory $factory, string $dsn, array $config
                         $managementDsn->getPassword()
                     );
                 } else {
-                    $managementClient = ManagementClient::create(
+                    $managementClient = StompManagementClient::create(
                         ltrim($dsn->getPath(), '/'),
                         $dsn->getHost(),
                         isset($config['management_plugin_port']) ? $config['management_plugin_port'] : 15672,
@@ -86,7 +86,7 @@ public function create(PsrConnectionFactory $factory, string $dsn, array $config
             throw new \LogicException(sprintf(
                 'To use given scheme "%s" a package has to be installed. Run "composer req %s" to add it.',
                 $dsn->getScheme(),
-                $knownDrivers[$driverClass]['package']
+                implode(' ', $knownDrivers[$driverClass]['packages'])
             ));
         }
 
diff --git a/Client/Resources.php b/Client/Resources.php
index 7f020b9..87da9dd 100644
--- a/Client/Resources.php
+++ b/Client/Resources.php
@@ -2,18 +2,18 @@
 
 namespace Enqueue\Client;
 
-use Enqueue\Client\Amqp\AmqpDriver;
-use Enqueue\Client\Amqp\RabbitMqDriver;
-use Enqueue\Dbal\Client\DbalDriver;
-use Enqueue\Fs\Client\FsDriver;
-use Enqueue\Gps\Client\GpsDriver;
-use Enqueue\Mongodb\Client\MongodbDriver;
-use Enqueue\Null\Client\NullDriver;
-use Enqueue\RdKafka\Client\RdKafkaDriver;
-use Enqueue\Redis\Client\RedisDriver;
-use Enqueue\Sqs\Client\SqsDriver;
-use Enqueue\Stomp\Client\RabbitMqStompDriver;
-use Enqueue\Stomp\Client\StompDriver;
+use Enqueue\Client\Driver\AmqpDriver;
+use Enqueue\Client\Driver\DbalDriver;
+use Enqueue\Client\Driver\FsDriver;
+use Enqueue\Client\Driver\GpsDriver;
+use Enqueue\Client\Driver\MongodbDriver;
+use Enqueue\Client\Driver\NullDriver;
+use Enqueue\Client\Driver\RabbitMqDriver;
+use Enqueue\Client\Driver\RabbitMqStompDriver;
+use Enqueue\Client\Driver\RdKafkaDriver;
+use Enqueue\Client\Driver\RedisDriver;
+use Enqueue\Client\Driver\SqsDriver;
+use Enqueue\Client\Driver\StompDriver;
 
 final class Resources
 {
@@ -51,59 +51,59 @@ public static function getKnownDrivers(): array
             $map = [];
 
             $map[AmqpDriver::class] = [
-                'schemes' => ['amqp'],
+                'schemes' => ['amqp', 'amqps'],
                 'requiredSchemeExtensions' => [],
-                'package' => 'enqueue/enqueue',
+                'packages' => ['enqueue/enqueue', 'enqueue/amqp-bunny'],
             ];
             $map[RabbitMqDriver::class] = [
-                'schemes' => ['amqp'],
+                'schemes' => ['amqp', 'amqps'],
                 'requiredSchemeExtensions' => ['rabbitmq'],
-                'package' => 'enqueue/enqueue',
+                'packages' => ['enqueue/enqueue', 'enqueue/amqp-bunny'],
             ];
             $map[FsDriver::class] = [
                 'schemes' => ['file'],
                 'requiredSchemeExtensions' => [],
-                'package' => 'enqueue/fs',
+                'packages' => ['enqueue/enqueue', 'enqueue/fs'],
             ];
             $map[NullDriver::class] = [
                 'schemes' => ['null'],
                 'requiredSchemeExtensions' => [],
-                'package' => 'enqueue/null',
+                'packages' => ['enqueue/enqueue', 'enqueue/null'],
             ];
             $map[GpsDriver::class] = [
                 'schemes' => ['gps'],
                 'requiredSchemeExtensions' => [],
-                'package' => 'enqueue/gps',
+                'packages' => ['enqueue/enqueue', 'enqueue/gps'],
             ];
             $map[RedisDriver::class] = [
                 'schemes' => ['redis'],
                 'requiredSchemeExtensions' => [],
-                'package' => 'enqueue/redis',
+                'packages' => ['enqueue/enqueue', 'enqueue/redis'],
             ];
             $map[SqsDriver::class] = [
                 'schemes' => ['sqs'],
                 'requiredSchemeExtensions' => [],
-                'package' => 'enqueue/sqs',
+                'packages' => ['enqueue/enqueue', 'enqueue/sqs'],
             ];
             $map[StompDriver::class] = [
                 'schemes' => ['stomp'],
                 'requiredSchemeExtensions' => [],
-                'package' => 'enqueue/stomp',
+                'packages' => ['enqueue/enqueue', 'enqueue/stomp'],
             ];
             $map[RabbitMqStompDriver::class] = [
                 'schemes' => ['stomp'],
                 'requiredSchemeExtensions' => ['rabbitmq'],
-                'package' => 'enqueue/stomp',
+                'packages' => ['enqueue/enqueue', 'enqueue/stomp'],
             ];
             $map[RdKafkaDriver::class] = [
                 'schemes' => ['kafka', 'rdkafka'],
                 'requiredSchemeExtensions' => [],
-                'package' => 'enqueue/rdkafka',
+                'packages' => ['enqueue/enqueue', 'enqueue/rdkafka'],
             ];
             $map[MongodbDriver::class] = [
                 'schemes' => ['mongodb'],
                 'requiredSchemeExtensions' => [],
-                'package' => 'enqueue/mongodb',
+                'packages' => ['enqueue/enqueue', 'enqueue/mongodb'],
             ];
             $map[DbalDriver::class] = [
                 'schemes' => [
@@ -121,8 +121,8 @@ public static function getKnownDrivers(): array
                     'sqlite3',
                     'sqlite',
                 ],
-                'requiredSchemeExtensions' => ['pdo'],
-                'package' => 'enqueue/dbal',
+                'requiredSchemeExtensions' => [],
+                'package' => ['enqueue/enqueue', 'enqueue/dbal'],
             ];
 
             self::$knownDrivers = $map;
@@ -131,7 +131,7 @@ public static function getKnownDrivers(): array
         return self::$knownDrivers;
     }
 
-    public static function addDriver(string $driverClass, array $schemes, array $requiredExtensions, string $package): void
+    public static function addDriver(string $driverClass, array $schemes, array $requiredExtensions, array $packages): void
     {
         if (class_exists($driverClass)) {
             if (false == (new \ReflectionClass($driverClass))->implementsInterface(DriverInterface::class)) {
@@ -142,15 +142,15 @@ public static function addDriver(string $driverClass, array $schemes, array $req
         if (empty($schemes)) {
             throw new \InvalidArgumentException('Schemes could not be empty.');
         }
-        if (empty($package)) {
-            throw new \InvalidArgumentException('Package name could not be empty.');
+        if (empty($packages)) {
+            throw new \InvalidArgumentException('Packages could not be empty.');
         }
 
         self::getKnownDrivers();
         self::$knownDrivers[$driverClass] = [
             'schemes' => $schemes,
             'requiredSchemeExtensions' => $requiredExtensions,
-            'package' => $package,
+            'packages' => $packages,
         ];
     }
 }
diff --git a/Client/TraceableProducer.php b/Client/TraceableProducer.php
index 71af530..40ff580 100644
--- a/Client/TraceableProducer.php
+++ b/Client/TraceableProducer.php
@@ -4,12 +4,12 @@
 
 use Enqueue\Rpc\Promise;
 
-class TraceableProducer implements ProducerInterface
+final class TraceableProducer implements ProducerInterface
 {
     /**
      * @var array
      */
-    protected $traces = [];
+    private $traces = [];
 
     /**
      * @var ProducerInterface
diff --git a/ConnectionFactoryFactory.php b/ConnectionFactoryFactory.php
index 9069f43..dce7ec3 100644
--- a/ConnectionFactoryFactory.php
+++ b/ConnectionFactoryFactory.php
@@ -7,12 +7,24 @@
 
 final class ConnectionFactoryFactory implements ConnectionFactoryFactoryInterface
 {
-    public function create(string $dsn): PsrConnectionFactory
+    public function create($config): PsrConnectionFactory
     {
-        $dsn = new Dsn($dsn);
+        if (is_string($config)) {
+            $config = ['dsn' => $config];
+        }
+
+        if (false == is_array($config)) {
+            throw new \InvalidArgumentException('The config must be either array or DSN string.');
+        }
+
+        if (false == array_key_exists('dsn', $config)) {
+            throw new \InvalidArgumentException('The config must have dsn key set.');
+        }
+
+        $dsn = new Dsn($config['dsn']);
 
         if ($factoryClass = $this->findFactoryClass($dsn, Resources::getAvailableConnections())) {
-            return new $factoryClass((string) $dsn);
+            return new $factoryClass($config);
         }
 
         $knownConnections = Resources::getKnownConnections();
diff --git a/ConnectionFactoryFactoryInterface.php b/ConnectionFactoryFactoryInterface.php
index 4c74a5e..22ee4cf 100644
--- a/ConnectionFactoryFactoryInterface.php
+++ b/ConnectionFactoryFactoryInterface.php
@@ -6,5 +6,17 @@
 
 interface ConnectionFactoryFactoryInterface
 {
-    public function create(string $dsn): PsrConnectionFactory;
+    /**
+     * If string is used, it should be a valid DSN.
+     *
+     * If array is used, it must have a dsn key with valid DSN string.
+     * The other array options are treated as default values.
+     * Options from DSN overwrite them.
+     *
+     *
+     * @param string|array $config
+     *
+     * @throws \InvalidArgumentException if invalid config provided
+     */
+    public function create($config): PsrConnectionFactory;
 }
diff --git a/Symfony/AmqpTransportFactory.php b/Symfony/AmqpTransportFactory.php
index cf6f343..1be59c1 100644
--- a/Symfony/AmqpTransportFactory.php
+++ b/Symfony/AmqpTransportFactory.php
@@ -5,7 +5,7 @@
 use Enqueue\AmqpBunny\AmqpConnectionFactory as AmqpBunnyConnectionFactory;
 use Enqueue\AmqpExt\AmqpConnectionFactory as AmqpExtConnectionFactory;
 use Enqueue\AmqpLib\AmqpConnectionFactory as AmqpLibConnectionFactory;
-use Enqueue\Client\Amqp\AmqpDriver;
+use Enqueue\Client\Driver\AmqpDriver;
 use Enqueue\ConnectionFactoryFactory;
 use Interop\Amqp\AmqpConnectionFactory;
 use Interop\Amqp\AmqpContext;
diff --git a/Symfony/DefaultTransportFactory.php b/Symfony/DefaultTransportFactory.php
index a65f2bf..3076697 100644
--- a/Symfony/DefaultTransportFactory.php
+++ b/Symfony/DefaultTransportFactory.php
@@ -5,134 +5,89 @@
 use Enqueue\Client\DriverInterface;
 use Interop\Queue\PsrConnectionFactory;
 use Interop\Queue\PsrContext;
-use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
+use Symfony\Component\Config\Definition\Builder\VariableNodeDefinition;
 use Symfony\Component\DependencyInjection\Alias;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Reference;
 
-class DefaultTransportFactory implements TransportFactoryInterface, DriverFactoryInterface
+class DefaultTransportFactory
 {
-    /**
-     * @var string
-     */
-    private $name;
-
-    /**
-     * @param string $name
-     */
-    public function __construct($name = 'default')
-    {
-        $this->name = $name;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function addConfiguration(ArrayNodeDefinition $builder)
+    public function addConfiguration(VariableNodeDefinition $builder): void
     {
         $builder
             ->beforeNormalization()
                 ->always(function ($v) {
-                    if (is_array($v)) {
-                        if (empty($v['dsn']) && empty($v['alias'])) {
-                            throw new \LogicException('Either dsn or alias option must be set');
-                        }
-
-                        return $v;
-                    }
-
                     if (empty($v)) {
                         return ['dsn' => 'null:'];
                     }
 
+                    if (is_array($v)) {
+                        return $v;
+                    }
+
                     if (is_string($v)) {
-                        return false !== strpos($v, ':') || false !== strpos($v, 'env_') ?
-                            ['dsn' => $v] :
-                            ['alias' => $v]
-                        ;
+                        return ['dsn' => $v];
                     }
 
                     throw new \LogicException(sprintf('The value must be array, null or string. Got "%s"', gettype($v)));
                 })
             ->end()
+            ->pro
             ->children()
-                ->scalarNode('alias')->cannotBeEmpty()->end()
                 ->scalarNode('dsn')->cannotBeEmpty()->end()
+                ->variableNode('config')->end()
             ->end()
         ->end()
         ;
     }
 
-    public function createConnectionFactory(ContainerBuilder $container, array $config)
+    public function createConnectionFactory(ContainerBuilder $container, array $config): string
     {
         $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
-        if (isset($config['alias'])) {
-            $aliasId = sprintf('enqueue.transport.%s.connection_factory', $config['alias']);
-            $container->setAlias($factoryId, new Alias($aliasId, true));
-        } else {
-            $container->register($factoryId, PsrConnectionFactory::class)
-                ->setFactory([new Reference('enqueue.connection_factory_factory'), 'create'])
-                ->addArgument($config['dsn'])
-            ;
-        }
+
+        $container->register($factoryId, PsrConnectionFactory::class)
+            ->setFactory([new Reference('enqueue.connection_factory_factory'), 'create'])
+            ->addArgument($config['dsn'])
+        ;
 
         $container->setAlias('enqueue.transport.connection_factory', new Alias($factoryId, true));
 
         return $factoryId;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function createContext(ContainerBuilder $container, array $config)
+    public function createContext(ContainerBuilder $container, array $config): string
     {
         $contextId = sprintf('enqueue.transport.%s.context', $this->getName());
         $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
 
-        if (isset($config['alias'])) {
-            $aliasId = sprintf('enqueue.transport.%s.context', $config['alias']);
-            $container->setAlias($contextId, new Alias($aliasId, true));
-        } else {
-            $container->register($contextId, PsrContext::class)
-                ->setFactory([new Reference($factoryId), 'createContext'])
-            ;
-        }
+        $container->register($contextId, PsrContext::class)
+            ->setFactory([new Reference($factoryId), 'createContext'])
+        ;
 
         $container->setAlias('enqueue.transport.context', new Alias($contextId, true));
 
         return $contextId;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function createDriver(ContainerBuilder $container, array $config)
+    public function createDriver(ContainerBuilder $container, array $config): string
     {
         $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
         $driverId = sprintf('enqueue.client.%s.driver', $this->getName());
 
-        if (isset($config['alias'])) {
-            $aliasId = sprintf('enqueue.client.%s.driver', $config['alias']);
-            $container->setAlias($driverId, new Alias($aliasId, true));
-        } else {
-            $container->register($driverId, DriverInterface::class)
-                ->setFactory([new Reference('enqueue.client.driver_factory'), 'create'])
-                ->addArgument(new Reference($factoryId))
-                ->addArgument($config['dsn'])
-                ->addArgument($config)
-            ;
-        }
+        $container->register($driverId, DriverInterface::class)
+            ->setFactory([new Reference('enqueue.client.driver_factory'), 'create'])
+            ->addArgument(new Reference($factoryId))
+            ->addArgument($config['dsn'])
+            ->addArgument($config)
+        ;
 
         $container->setAlias('enqueue.client.driver', new Alias($driverId, true));
 
         return $driverId;
     }
 
-    /**
-     * @return string
-     */
-    public function getName()
+    public function getName(): string
     {
-        return $this->name;
+        return 'default';
     }
 }
diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
new file mode 100644
index 0000000..d4dce46
--- /dev/null
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace Enqueue\Symfony\DependencyInjection;
+
+use Enqueue\Client\DriverInterface;
+use Interop\Queue\PsrConnectionFactory;
+use Interop\Queue\PsrContext;
+use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
+use Symfony\Component\DependencyInjection\Alias;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * @internal
+ */
+final class TransportFactory
+{
+    /**
+     * @var string
+     */
+    private $name;
+
+    public function __construct(string $name)
+    {
+        if (empty($name)) {
+            throw new \InvalidArgumentException('The name could not be empty.');
+        }
+
+        $this->name = $name;
+    }
+
+    public function addConfiguration(ArrayNodeDefinition $builder): void
+    {
+        $builder
+            ->beforeNormalization()
+                ->always(function ($v) {
+                    if (empty($v)) {
+                        return ['dsn' => 'null:'];
+                    }
+
+                    if (is_array($v)) {
+                        return $v;
+                    }
+
+                    if (is_string($v)) {
+                        return ['dsn' => $v];
+                    }
+
+                    throw new \LogicException(sprintf('The value must be array, null or string. Got "%s"', gettype($v)));
+                })
+        ->end()
+        ->ignoreExtraKeys(false)
+        ->children()
+            ->scalarNode('dsn')->cannotBeEmpty()->isRequired()->end()
+        ->end()
+        ;
+    }
+
+    public function createConnectionFactory(ContainerBuilder $container, array $config): string
+    {
+        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
+
+        $container->register($factoryId, PsrConnectionFactory::class)
+            ->setFactory([new Reference('enqueue.connection_factory_factory'), 'create'])
+            ->addArgument($config)
+        ;
+
+        $container->setAlias('enqueue.transport.connection_factory', new Alias($factoryId, true));
+
+        return $factoryId;
+    }
+
+    public function createContext(ContainerBuilder $container, array $config): string
+    {
+        $contextId = sprintf('enqueue.transport.%s.context', $this->getName());
+        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
+
+        $container->register($contextId, PsrContext::class)
+            ->setFactory([new Reference($factoryId), 'createContext'])
+        ;
+
+        $container->setAlias('enqueue.transport.context', new Alias($contextId, true));
+
+        return $contextId;
+    }
+
+    public function createDriver(ContainerBuilder $container, array $config): string
+    {
+        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
+        $driverId = sprintf('enqueue.client.%s.driver', $this->getName());
+
+        $container->register($driverId, DriverInterface::class)
+            ->setFactory([new Reference('enqueue.client.driver_factory'), 'create'])
+            ->addArgument(new Reference($factoryId))
+            ->addArgument($config['dsn'])
+            ->addArgument($config)
+        ;
+
+        $container->setAlias('enqueue.client.driver', new Alias($driverId, true));
+
+        return $driverId;
+    }
+
+    public function getName(): string
+    {
+        return $this->name;
+    }
+}
diff --git a/Symfony/RabbitMqAmqpTransportFactory.php b/Symfony/RabbitMqAmqpTransportFactory.php
index 94ee923..f02aa15 100644
--- a/Symfony/RabbitMqAmqpTransportFactory.php
+++ b/Symfony/RabbitMqAmqpTransportFactory.php
@@ -3,7 +3,7 @@
 namespace Enqueue\Symfony;
 
 use Enqueue\AmqpTools\DelayStrategyTransportFactoryTrait;
-use Enqueue\Client\Amqp\RabbitMqDriver;
+use Enqueue\Client\Driver\RabbitMqDriver;
 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Definition;
diff --git a/Tests/Client/Amqp/AmqpDriverTest.php b/Tests/Client/Driver/AmqpDriverTest.php
similarity index 99%
rename from Tests/Client/Amqp/AmqpDriverTest.php
rename to Tests/Client/Driver/AmqpDriverTest.php
index af69ee2..41a6432 100644
--- a/Tests/Client/Amqp/AmqpDriverTest.php
+++ b/Tests/Client/Driver/AmqpDriverTest.php
@@ -1,9 +1,9 @@
 <?php
 
-namespace Enqueue\Tests\Client\Amqp;
+namespace Enqueue\Tests\Client\Driver;
 
-use Enqueue\Client\Amqp\AmqpDriver;
 use Enqueue\Client\Config;
+use Enqueue\Client\Driver\AmqpDriver;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Message;
 use Enqueue\Client\Meta\QueueMetaRegistry;
diff --git a/Tests/Client/Driver/DbalDriverTest.php b/Tests/Client/Driver/DbalDriverTest.php
new file mode 100644
index 0000000..859ed58
--- /dev/null
+++ b/Tests/Client/Driver/DbalDriverTest.php
@@ -0,0 +1,357 @@
+<?php
+
+namespace Enqueue\Tests\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\Driver\DbalDriver;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Dbal\DbalContext;
+use Enqueue\Dbal\DbalDestination;
+use Enqueue\Dbal\DbalMessage;
+use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrProducer;
+
+class DbalDriverTest extends \PHPUnit_Framework_TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementsDriverInterface()
+    {
+        $this->assertClassImplements(DriverInterface::class, DbalDriver::class);
+    }
+
+    public function testCouldBeConstructedWithRequiredArguments()
+    {
+        new DbalDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+    }
+
+    public function testShouldReturnConfigObject()
+    {
+        $config = $this->createDummyConfig();
+
+        $driver = new DbalDriver(
+            $this->createPsrContextMock(),
+            $config,
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->assertSame($config, $driver->getConfig());
+    }
+
+    public function testShouldCreateAndReturnQueueInstance()
+    {
+        $expectedQueue = new DbalDestination('aName');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new DbalDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
+    {
+        $expectedQueue = new DbalDestination('aName');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aBarQueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new DbalDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aBarQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldConvertTransportMessageToClientMessage()
+    {
+        $transportMessage = new DbalMessage();
+        $transportMessage->setBody('body');
+        $transportMessage->setHeaders(['hkey' => 'hval']);
+        $transportMessage->setProperties(['key' => 'val']);
+        $transportMessage->setHeader('content_type', 'ContentType');
+        $transportMessage->setMessageId('MessageId');
+        $transportMessage->setTimestamp(1000);
+        $transportMessage->setPriority(2);
+        $transportMessage->setDeliveryDelay(12345);
+        $transportMessage->setTimeToLive(67890);
+
+        $driver = new DbalDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $clientMessage = $driver->createClientMessage($transportMessage);
+
+        $this->assertInstanceOf(Message::class, $clientMessage);
+        $this->assertSame('body', $clientMessage->getBody());
+        $this->assertSame([
+            'hkey' => 'hval',
+            'content_type' => 'ContentType',
+            'message_id' => 'MessageId',
+            'timestamp' => 1000,
+        ], $clientMessage->getHeaders());
+        $this->assertSame([
+            'key' => 'val',
+        ], $clientMessage->getProperties());
+        $this->assertSame('MessageId', $clientMessage->getMessageId());
+        $this->assertSame('ContentType', $clientMessage->getContentType());
+        $this->assertSame(1000, $clientMessage->getTimestamp());
+        $this->assertSame(12, $clientMessage->getDelay());
+        $this->assertSame(67, $clientMessage->getExpire());
+        $this->assertSame(MessagePriority::NORMAL, $clientMessage->getPriority());
+    }
+
+    public function testShouldConvertClientMessageToTransportMessage()
+    {
+        $clientMessage = new Message();
+        $clientMessage->setBody('body');
+        $clientMessage->setHeaders(['hkey' => 'hval']);
+        $clientMessage->setProperties(['key' => 'val']);
+        $clientMessage->setContentType('ContentType');
+        $clientMessage->setExpire(123);
+        $clientMessage->setPriority(MessagePriority::VERY_HIGH);
+        $clientMessage->setMessageId('MessageId');
+        $clientMessage->setTimestamp(1000);
+        $clientMessage->setDelay(23);
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn(new DbalMessage())
+        ;
+
+        $driver = new DbalDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $transportMessage = $driver->createTransportMessage($clientMessage);
+
+        $this->assertInstanceOf(DbalMessage::class, $transportMessage);
+        $this->assertSame('body', $transportMessage->getBody());
+        $this->assertSame([
+            'hkey' => 'hval',
+            'content_type' => 'ContentType',
+            'message_id' => 'MessageId',
+            'timestamp' => 1000,
+            'reply_to' => null,
+            'correlation_id' => null,
+        ], $transportMessage->getHeaders());
+        $this->assertSame([
+            'key' => 'val',
+        ], $transportMessage->getProperties());
+        $this->assertSame(123000, $transportMessage->getTimeToLive());
+        $this->assertSame('MessageId', $transportMessage->getMessageId());
+        $this->assertSame(1000, $transportMessage->getTimestamp());
+        $this->assertSame(23000, $transportMessage->getDeliveryDelay());
+    }
+
+    public function testShouldSendMessageToRouter()
+    {
+        $topic = new DbalDestination('queue-name');
+        $transportMessage = new DbalMessage();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.default')
+            ->willReturn($topic)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new DbalDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+
+        $driver->sendToRouter($message);
+    }
+
+    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
+    {
+        $driver = new DbalDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Topic name parameter is required but is not set');
+
+        $driver->sendToRouter(new Message());
+    }
+
+    public function testShouldSendMessageToProcessor()
+    {
+        $queue = new DbalDestination('queue-name');
+        $transportMessage = new DbalMessage();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new DbalDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
+    {
+        $driver = new DbalDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Processor name parameter is required but is not set');
+
+        $driver->sendToProcessor(new Message());
+    }
+
+    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
+    {
+        $driver = new DbalDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Queue name parameter is required but is not set');
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldSetupBroker()
+    {
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('getTableName')
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createDataBaseTable')
+        ;
+
+        $driver = new DbalDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $driver->setupBroker();
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|DbalContext
+     */
+    private function createPsrContextMock()
+    {
+        return $this->createMock(DbalContext::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProducer
+     */
+    private function createPsrProducerMock()
+    {
+        return $this->createMock(PsrProducer::class);
+    }
+
+    /**
+     * @return QueueMetaRegistry
+     */
+    private function createDummyQueueMetaRegistry()
+    {
+        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
+        $registry->add('default');
+        $registry->add('aFooQueue');
+        $registry->add('aBarQueue', 'aBarQueue');
+
+        return $registry;
+    }
+
+    /**
+     * @return Config
+     */
+    private function createDummyConfig()
+    {
+        return Config::create('aPrefix');
+    }
+}
diff --git a/Tests/Client/Driver/FsDriverTest.php b/Tests/Client/Driver/FsDriverTest.php
new file mode 100644
index 0000000..6e9cb55
--- /dev/null
+++ b/Tests/Client/Driver/FsDriverTest.php
@@ -0,0 +1,392 @@
+<?php
+
+namespace Enqueue\Tests\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\Driver\FsDriver;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Fs\FsContext;
+use Enqueue\Fs\FsDestination;
+use Enqueue\Fs\FsMessage;
+use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrProducer;
+use Makasim\File\TempFile;
+
+class FsDriverTest extends \PHPUnit\Framework\TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementsDriverInterface()
+    {
+        $this->assertClassImplements(DriverInterface::class, FsDriver::class);
+    }
+
+    public function testCouldBeConstructedWithRequiredArguments()
+    {
+        new FsDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+    }
+
+    public function testShouldReturnConfigObject()
+    {
+        $config = $this->createDummyConfig();
+
+        $driver = new FsDriver($this->createPsrContextMock(), $config, $this->createDummyQueueMetaRegistry());
+
+        $this->assertSame($config, $driver->getConfig());
+    }
+
+    public function testShouldCreateAndReturnQueueInstance()
+    {
+        $expectedQueue = new FsDestination(new TempFile(sys_get_temp_dir().'/queue-name'));
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new FsDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
+    {
+        $expectedQueue = new FsDestination(new TempFile(sys_get_temp_dir().'/queue-name'));
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aBarQueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new FsDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aBarQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldConvertTransportMessageToClientMessage()
+    {
+        $transportMessage = new FsMessage();
+        $transportMessage->setBody('body');
+        $transportMessage->setHeaders(['hkey' => 'hval']);
+        $transportMessage->setProperties(['key' => 'val']);
+        $transportMessage->setHeader('content_type', 'ContentType');
+        $transportMessage->setMessageId('MessageId');
+        $transportMessage->setTimestamp(1000);
+        $transportMessage->setReplyTo('theReplyTo');
+        $transportMessage->setCorrelationId('theCorrelationId');
+
+        $driver = new FsDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $clientMessage = $driver->createClientMessage($transportMessage);
+
+        $this->assertInstanceOf(Message::class, $clientMessage);
+        $this->assertSame('body', $clientMessage->getBody());
+        $this->assertSame([
+            'hkey' => 'hval',
+            'content_type' => 'ContentType',
+            'message_id' => 'MessageId',
+            'timestamp' => 1000,
+            'reply_to' => 'theReplyTo',
+            'correlation_id' => 'theCorrelationId',
+        ], $clientMessage->getHeaders());
+        $this->assertSame([
+            'key' => 'val',
+        ], $clientMessage->getProperties());
+        $this->assertSame('MessageId', $clientMessage->getMessageId());
+        $this->assertSame('ContentType', $clientMessage->getContentType());
+        $this->assertSame(1000, $clientMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
+
+        $this->assertNull($clientMessage->getExpire());
+        $this->assertSame(MessagePriority::NORMAL, $clientMessage->getPriority());
+    }
+
+    public function testShouldConvertClientMessageToTransportMessage()
+    {
+        $clientMessage = new Message();
+        $clientMessage->setBody('body');
+        $clientMessage->setHeaders(['hkey' => 'hval']);
+        $clientMessage->setProperties(['key' => 'val']);
+        $clientMessage->setContentType('ContentType');
+        $clientMessage->setExpire(123);
+        $clientMessage->setPriority(MessagePriority::VERY_HIGH);
+        $clientMessage->setMessageId('MessageId');
+        $clientMessage->setTimestamp(1000);
+        $clientMessage->setReplyTo('theReplyTo');
+        $clientMessage->setCorrelationId('theCorrelationId');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn(new FsMessage())
+        ;
+
+        $driver = new FsDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $transportMessage = $driver->createTransportMessage($clientMessage);
+
+        $this->assertInstanceOf(FsMessage::class, $transportMessage);
+        $this->assertSame('body', $transportMessage->getBody());
+        $this->assertSame([
+            'hkey' => 'hval',
+            'content_type' => 'ContentType',
+            'message_id' => 'MessageId',
+            'timestamp' => 1000,
+            'reply_to' => 'theReplyTo',
+            'correlation_id' => 'theCorrelationId',
+        ], $transportMessage->getHeaders());
+        $this->assertSame([
+            'key' => 'val',
+        ], $transportMessage->getProperties());
+        $this->assertSame('MessageId', $transportMessage->getMessageId());
+        $this->assertSame(1000, $transportMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
+    }
+
+    public function testShouldSendMessageToRouter()
+    {
+        $topic = new FsDestination(TempFile::generate());
+        $transportMessage = new FsMessage();
+        $config = $this->createDummyConfig();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createTopic')
+            ->with('aprefix.router')
+            ->willReturn($topic)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new FsDriver(
+            $context,
+            $config,
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+
+        $driver->sendToRouter($message);
+    }
+
+    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
+    {
+        $driver = new FsDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Topic name parameter is required but is not set');
+
+        $driver->sendToRouter(new Message());
+    }
+
+    public function testShouldSendMessageToProcessor()
+    {
+        $queue = new FsDestination(TempFile::generate());
+        $transportMessage = new FsMessage();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new FsDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
+    {
+        $driver = new FsDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Processor name parameter is required but is not set');
+
+        $driver->sendToProcessor(new Message());
+    }
+
+    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
+    {
+        $driver = new FsDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Queue name parameter is required but is not set');
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldSetupBroker()
+    {
+        $routerTopic = new FsDestination(TempFile::generate());
+        $routerQueue = new FsDestination(TempFile::generate());
+
+        $processorQueue = new FsDestination(TempFile::generate());
+
+        $context = $this->createPsrContextMock();
+        // setup router
+        $context
+            ->expects($this->at(0))
+            ->method('createTopic')
+            ->willReturn($routerTopic)
+        ;
+        $context
+            ->expects($this->at(1))
+            ->method('createQueue')
+            ->willReturn($routerQueue)
+        ;
+        $context
+            ->expects($this->at(2))
+            ->method('declareDestination')
+            ->with($this->identicalTo($routerTopic))
+        ;
+        $context
+            ->expects($this->at(3))
+            ->method('declareDestination')
+            ->with($this->identicalTo($routerQueue))
+        ;
+        // setup processor queue
+        $context
+            ->expects($this->at(4))
+            ->method('createQueue')
+            ->willReturn($processorQueue)
+        ;
+        $context
+            ->expects($this->at(5))
+            ->method('declareDestination')
+            ->with($this->identicalTo($processorQueue))
+        ;
+
+        $meta = new QueueMetaRegistry($this->createDummyConfig(), [
+            'default' => [],
+        ]);
+
+        $driver = new FsDriver(
+            $context,
+            $this->createDummyConfig(),
+            $meta
+        );
+
+        $driver->setupBroker();
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|FsContext
+     */
+    private function createPsrContextMock()
+    {
+        return $this->createMock(FsContext::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProducer
+     */
+    private function createPsrProducerMock()
+    {
+        return $this->createMock(PsrProducer::class);
+    }
+
+    /**
+     * @return QueueMetaRegistry
+     */
+    private function createDummyQueueMetaRegistry()
+    {
+        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
+        $registry->add('default');
+        $registry->add('aFooQueue');
+        $registry->add('aBarQueue', 'aBarQueue');
+
+        return $registry;
+    }
+
+    /**
+     * @return Config
+     */
+    private function createDummyConfig()
+    {
+        return Config::create('aPrefix');
+    }
+}
diff --git a/Tests/Client/Driver/GpsDriverTest.php b/Tests/Client/Driver/GpsDriverTest.php
new file mode 100644
index 0000000..622f622
--- /dev/null
+++ b/Tests/Client/Driver/GpsDriverTest.php
@@ -0,0 +1,385 @@
+<?php
+
+namespace Enqueue\Tests\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\Driver\GpsDriver;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Gps\GpsContext;
+use Enqueue\Gps\GpsMessage;
+use Enqueue\Gps\GpsProducer;
+use Enqueue\Gps\GpsQueue;
+use Enqueue\Gps\GpsTopic;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+
+class GpsDriverTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementsDriverInterface()
+    {
+        $this->assertClassImplements(DriverInterface::class, GpsDriver::class);
+    }
+
+    public function testCouldBeConstructedWithRequiredArguments()
+    {
+        new GpsDriver(
+            $this->createGpsContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+    }
+
+    public function testShouldReturnConfigObject()
+    {
+        $config = $this->createDummyConfig();
+
+        $driver = new GpsDriver($this->createGpsContextMock(), $config, $this->createDummyQueueMetaRegistry());
+
+        $this->assertSame($config, $driver->getConfig());
+    }
+
+    public function testShouldCreateAndReturnQueueInstance()
+    {
+        $expectedQueue = new GpsQueue('aName');
+
+        $context = $this->createGpsContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new GpsDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
+    {
+        $expectedQueue = new GpsQueue('aName');
+
+        $context = $this->createGpsContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aBarQueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new GpsDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aBarQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldConvertTransportMessageToClientMessage()
+    {
+        $transportMessage = new GpsMessage();
+        $transportMessage->setBody('body');
+        $transportMessage->setHeaders(['hkey' => 'hval']);
+        $transportMessage->setProperties(['key' => 'val']);
+        $transportMessage->setMessageId('MessageId');
+        $transportMessage->setTimestamp(1000);
+        $transportMessage->setReplyTo('theReplyTo');
+        $transportMessage->setCorrelationId('theCorrelationId');
+
+        $driver = new GpsDriver(
+            $this->createGpsContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $clientMessage = $driver->createClientMessage($transportMessage);
+
+        $this->assertInstanceOf(Message::class, $clientMessage);
+        $this->assertSame('body', $clientMessage->getBody());
+        $this->assertSame([
+            'hkey' => 'hval',
+            'message_id' => 'MessageId',
+            'timestamp' => 1000,
+            'reply_to' => 'theReplyTo',
+            'correlation_id' => 'theCorrelationId',
+        ], $clientMessage->getHeaders());
+        $this->assertSame([
+            'key' => 'val',
+        ], $clientMessage->getProperties());
+        $this->assertSame('MessageId', $clientMessage->getMessageId());
+        $this->assertNull($clientMessage->getExpire());
+        $this->assertNull($clientMessage->getContentType());
+        $this->assertSame(1000, $clientMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
+    }
+
+    public function testShouldConvertClientMessageToTransportMessage()
+    {
+        $clientMessage = new Message();
+        $clientMessage->setBody('body');
+        $clientMessage->setHeaders(['hkey' => 'hval']);
+        $clientMessage->setProperties(['key' => 'val']);
+        $clientMessage->setContentType('ContentType');
+        $clientMessage->setExpire(123);
+        $clientMessage->setMessageId('MessageId');
+        $clientMessage->setTimestamp(1000);
+        $clientMessage->setReplyTo('theReplyTo');
+        $clientMessage->setCorrelationId('theCorrelationId');
+
+        $context = $this->createGpsContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn(new GpsMessage())
+        ;
+
+        $driver = new GpsDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $transportMessage = $driver->createTransportMessage($clientMessage);
+
+        $this->assertInstanceOf(GpsMessage::class, $transportMessage);
+        $this->assertSame('body', $transportMessage->getBody());
+        $this->assertSame([
+            'hkey' => 'hval',
+            'message_id' => 'MessageId',
+            'timestamp' => 1000,
+            'reply_to' => 'theReplyTo',
+            'correlation_id' => 'theCorrelationId',
+        ], $transportMessage->getHeaders());
+        $this->assertSame([
+            'key' => 'val',
+        ], $transportMessage->getProperties());
+        $this->assertSame('MessageId', $transportMessage->getMessageId());
+        $this->assertSame(1000, $transportMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
+    }
+
+    public function testShouldSendMessageToRouter()
+    {
+        $topic = new GpsTopic('');
+        $transportMessage = new GpsMessage();
+
+        $producer = $this->createGpsProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createGpsContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createTopic')
+            ->willReturn($topic)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new GpsDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+
+        $driver->sendToRouter($message);
+    }
+
+    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
+    {
+        $driver = new GpsDriver(
+            $this->createGpsContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Topic name parameter is required but is not set');
+
+        $driver->sendToRouter(new Message());
+    }
+
+    public function testShouldSendMessageToProcessor()
+    {
+        $topic = new GpsTopic('');
+        $transportMessage = new GpsMessage();
+
+        $producer = $this->createGpsProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createGpsContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createTopic')
+            ->willReturn($topic)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new GpsDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
+    {
+        $driver = new GpsDriver(
+            $this->createGpsContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Processor name parameter is required but is not set');
+
+        $driver->sendToProcessor(new Message());
+    }
+
+    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
+    {
+        $driver = new GpsDriver(
+            $this->createGpsContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Queue name parameter is required but is not set');
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldSetupBroker()
+    {
+        $routerTopic = new GpsTopic('');
+        $routerQueue = new GpsQueue('');
+
+        $processorTopic = new GpsTopic('');
+        $processorQueue = new GpsQueue('');
+
+        $context = $this->createGpsContextMock();
+        // setup router
+        $context
+            ->expects($this->at(0))
+            ->method('createTopic')
+            ->willReturn($routerTopic)
+        ;
+        $context
+            ->expects($this->at(1))
+            ->method('createQueue')
+            ->willReturn($routerQueue)
+        ;
+        $context
+            ->expects($this->at(2))
+            ->method('subscribe')
+            ->with($this->identicalTo($routerTopic), $this->identicalTo($routerQueue))
+        ;
+        // setup processor queue
+        $context
+            ->expects($this->at(3))
+            ->method('createTopic')
+            ->willReturn($processorTopic)
+        ;
+        $context
+            ->expects($this->at(4))
+            ->method('createQueue')
+            ->willReturn($processorQueue)
+        ;
+        $context
+            ->expects($this->at(5))
+            ->method('subscribe')
+            ->with($this->identicalTo($processorTopic), $this->identicalTo($processorQueue))
+        ;
+
+        $meta = new QueueMetaRegistry($this->createDummyConfig(), [
+            'default' => [],
+        ]);
+
+        $driver = new GpsDriver(
+            $context,
+            $this->createDummyConfig(),
+            $meta
+        );
+
+        $driver->setupBroker();
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|GpsContext
+     */
+    private function createGpsContextMock()
+    {
+        return $this->createMock(GpsContext::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|GpsProducer
+     */
+    private function createGpsProducerMock()
+    {
+        return $this->createMock(GpsProducer::class);
+    }
+
+    /**
+     * @return QueueMetaRegistry
+     */
+    private function createDummyQueueMetaRegistry()
+    {
+        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
+        $registry->add('default');
+        $registry->add('aFooQueue');
+        $registry->add('aBarQueue', 'aBarQueue');
+
+        return $registry;
+    }
+
+    /**
+     * @return Config
+     */
+    private function createDummyConfig()
+    {
+        return Config::create('aPrefix');
+    }
+}
diff --git a/Tests/Client/Driver/MongodbDriverTest.php b/Tests/Client/Driver/MongodbDriverTest.php
new file mode 100644
index 0000000..b10a067
--- /dev/null
+++ b/Tests/Client/Driver/MongodbDriverTest.php
@@ -0,0 +1,358 @@
+<?php
+
+namespace Enqueue\Tests\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\Driver\MongodbDriver;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Mongodb\MongodbContext;
+use Enqueue\Mongodb\MongodbDestination;
+use Enqueue\Mongodb\MongodbMessage;
+use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrProducer;
+
+class MongodbDriverTest extends \PHPUnit_Framework_TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementsDriverInterface()
+    {
+        $this->assertClassImplements(DriverInterface::class, MongodbDriver::class);
+    }
+
+    public function testCouldBeConstructedWithRequiredArguments()
+    {
+        new MongodbDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+    }
+
+    public function testShouldReturnConfigObject()
+    {
+        $config = $this->createDummyConfig();
+
+        $driver = new MongodbDriver(
+            $this->createPsrContextMock(),
+            $config,
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->assertSame($config, $driver->getConfig());
+    }
+
+    public function testShouldCreateAndReturnQueueInstance()
+    {
+        $expectedQueue = new MongodbDestination('aName');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new MongodbDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
+    {
+        $expectedQueue = new MongodbDestination('aName');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aBarQueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new MongodbDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aBarQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldConvertTransportMessageToClientMessage()
+    {
+        $transportMessage = new MongodbMessage();
+        $transportMessage->setBody('body');
+        $transportMessage->setHeaders(['hkey' => 'hval']);
+        $transportMessage->setProperties(['key' => 'val']);
+        $transportMessage->setHeader('content_type', 'ContentType');
+        $transportMessage->setMessageId('MessageId');
+        $transportMessage->setTimestamp(1000);
+        $transportMessage->setPriority(2);
+        $transportMessage->setDeliveryDelay(12345);
+
+        $driver = new MongodbDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $clientMessage = $driver->createClientMessage($transportMessage);
+
+        $this->assertInstanceOf(Message::class, $clientMessage);
+        $this->assertSame('body', $clientMessage->getBody());
+        $this->assertSame([
+            'hkey' => 'hval',
+            'content_type' => 'ContentType',
+            'message_id' => 'MessageId',
+            'timestamp' => 1000,
+        ], $clientMessage->getHeaders());
+        $this->assertSame([
+            'key' => 'val',
+        ], $clientMessage->getProperties());
+        $this->assertSame('MessageId', $clientMessage->getMessageId());
+        $this->assertSame('ContentType', $clientMessage->getContentType());
+        $this->assertSame(1000, $clientMessage->getTimestamp());
+        $this->assertSame(12345, $clientMessage->getDelay());
+
+        $this->assertNull($clientMessage->getExpire());
+        $this->assertSame(MessagePriority::NORMAL, $clientMessage->getPriority());
+    }
+
+    public function testShouldConvertClientMessageToTransportMessage()
+    {
+        $clientMessage = new Message();
+        $clientMessage->setBody('body');
+        $clientMessage->setHeaders(['hkey' => 'hval']);
+        $clientMessage->setProperties(['key' => 'val']);
+        $clientMessage->setContentType('ContentType');
+        $clientMessage->setExpire(123);
+        $clientMessage->setPriority(MessagePriority::VERY_HIGH);
+        $clientMessage->setMessageId('MessageId');
+        $clientMessage->setTimestamp(1000);
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn(new MongodbMessage())
+        ;
+
+        $driver = new MongodbDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $transportMessage = $driver->createTransportMessage($clientMessage);
+
+        $this->assertInstanceOf(MongodbMessage::class, $transportMessage);
+        $this->assertSame('body', $transportMessage->getBody());
+        $this->assertSame([
+            'hkey' => 'hval',
+            'content_type' => 'ContentType',
+            'message_id' => 'MessageId',
+            'timestamp' => 1000,
+            'reply_to' => null,
+            'correlation_id' => null,
+        ], $transportMessage->getHeaders());
+        $this->assertSame([
+            'key' => 'val',
+        ], $transportMessage->getProperties());
+        $this->assertSame('MessageId', $transportMessage->getMessageId());
+        $this->assertSame(1000, $transportMessage->getTimestamp());
+    }
+
+    public function testShouldSendMessageToRouter()
+    {
+        $topic = new MongodbDestination('queue-name');
+        $transportMessage = new MongodbMessage();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.default')
+            ->willReturn($topic)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new MongodbDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+
+        $driver->sendToRouter($message);
+    }
+
+    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
+    {
+        $driver = new MongodbDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Topic name parameter is required but is not set');
+
+        $driver->sendToRouter(new Message());
+    }
+
+    public function testShouldSendMessageToProcessor()
+    {
+        $queue = new MongodbDestination('queue-name');
+        $transportMessage = new MongodbMessage();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new MongodbDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
+    {
+        $driver = new MongodbDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Processor name parameter is required but is not set');
+
+        $driver->sendToProcessor(new Message());
+    }
+
+    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
+    {
+        $driver = new MongodbDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Queue name parameter is required but is not set');
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldSetupBroker()
+    {
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createCollection')
+        ;
+        $context
+            ->expects($this->once())
+            ->method('getConfig')
+            ->willReturn([
+                'dbname' => 'aDb',
+                'collection_name' => 'aCol',
+            ])
+        ;
+
+        $driver = new MongodbDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $driver->setupBroker();
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|MongodbContext
+     */
+    private function createPsrContextMock()
+    {
+        return $this->createMock(MongodbContext::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProducer
+     */
+    private function createPsrProducerMock()
+    {
+        return $this->createMock(PsrProducer::class);
+    }
+
+    /**
+     * @return QueueMetaRegistry
+     */
+    private function createDummyQueueMetaRegistry()
+    {
+        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
+        $registry->add('default');
+        $registry->add('aFooQueue');
+        $registry->add('aBarQueue', 'aBarQueue');
+
+        return $registry;
+    }
+
+    /**
+     * @return Config
+     */
+    private function createDummyConfig()
+    {
+        return Config::create('aPrefix');
+    }
+}
diff --git a/Tests/Client/Driver/NullDriverTest.php b/Tests/Client/Driver/NullDriverTest.php
new file mode 100644
index 0000000..233e3b5
--- /dev/null
+++ b/Tests/Client/Driver/NullDriverTest.php
@@ -0,0 +1,277 @@
+<?php
+
+namespace Enqueue\Tests\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\Driver\NullDriver;
+use Enqueue\Client\Message;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Null\NullContext;
+use Enqueue\Null\NullMessage;
+use Enqueue\Null\NullProducer;
+use Enqueue\Null\NullQueue;
+use Enqueue\Null\NullTopic;
+use PHPUnit\Framework\TestCase;
+
+class NullDriverTest extends TestCase
+{
+    public function testCouldBeConstructedWithRequiredArguments()
+    {
+        new NullDriver(new NullContext(), Config::create(), $this->createDummyQueueMetaRegistry());
+    }
+
+    public function testShouldCreateAndReturnQueueInstance()
+    {
+        $expectedQueue = new NullQueue('aName');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new NullDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
+    {
+        $expectedQueue = new NullQueue('aName');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aBarQueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new NullDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aBarQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldSendMessageToRouter()
+    {
+        $config = Config::create();
+        $topic = new NullTopic('topic');
+
+        $transportMessage = new NullMessage();
+
+        $producer = $this->createMessageProducer();
+        $producer
+            ->expects(self::once())
+            ->method('send')
+            ->with(self::identicalTo($topic), self::identicalTo($transportMessage))
+        ;
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createTopic')
+            ->willReturn($topic)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+
+        $driver = new NullDriver($context, $config, $this->createDummyQueueMetaRegistry());
+
+        $driver->sendToRouter(new Message());
+    }
+
+    public function testShouldSendMessageToProcessor()
+    {
+        $config = Config::create();
+        $queue = new NullQueue('');
+
+        $transportMessage = new NullMessage();
+
+        $producer = $this->createMessageProducer();
+        $producer
+            ->expects(self::once())
+            ->method('send')
+            ->with(self::identicalTo($queue), self::identicalTo($transportMessage))
+        ;
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+
+        $driver = new NullDriver($context, $config, $this->createDummyQueueMetaRegistry());
+
+        $driver->sendToProcessor(new Message());
+    }
+
+    public function testShouldConvertClientMessageToTransportMessage()
+    {
+        $config = Config::create();
+
+        $clientMessage = new Message();
+        $clientMessage->setBody('theBody');
+        $clientMessage->setContentType('theContentType');
+        $clientMessage->setMessageId('theMessageId');
+        $clientMessage->setTimestamp(12345);
+        $clientMessage->setDelay(123);
+        $clientMessage->setExpire(345);
+        $clientMessage->setPriority(MessagePriority::LOW);
+        $clientMessage->setHeaders(['theHeaderFoo' => 'theFoo']);
+        $clientMessage->setProperties(['thePropertyBar' => 'theBar']);
+        $clientMessage->setReplyTo('theReplyTo');
+        $clientMessage->setCorrelationId('theCorrelationId');
+
+        $transportMessage = new NullMessage();
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new NullDriver($context, $config, $this->createDummyQueueMetaRegistry());
+
+        $transportMessage = $driver->createTransportMessage($clientMessage);
+
+        self::assertSame('theBody', $transportMessage->getBody());
+        self::assertSame([
+            'theHeaderFoo' => 'theFoo',
+            'content_type' => 'theContentType',
+            'expiration' => 345,
+            'delay' => 123,
+            'priority' => MessagePriority::LOW,
+            'timestamp' => 12345,
+            'message_id' => 'theMessageId',
+            'reply_to' => 'theReplyTo',
+            'correlation_id' => 'theCorrelationId',
+        ], $transportMessage->getHeaders());
+        self::assertSame([
+            'thePropertyBar' => 'theBar',
+        ], $transportMessage->getProperties());
+
+        $this->assertSame('theMessageId', $transportMessage->getMessageId());
+        $this->assertSame(12345, $transportMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
+    }
+
+    public function testShouldConvertTransportMessageToClientMessage()
+    {
+        $config = Config::create();
+
+        $transportMessage = new NullMessage();
+        $transportMessage->setBody('theBody');
+        $transportMessage->setHeaders(['theHeaderFoo' => 'theFoo']);
+        $transportMessage->setTimestamp(12345);
+        $transportMessage->setMessageId('theMessageId');
+        $transportMessage->setHeader('priority', MessagePriority::LOW);
+        $transportMessage->setHeader('content_type', 'theContentType');
+        $transportMessage->setHeader('delay', 123);
+        $transportMessage->setHeader('expiration', 345);
+        $transportMessage->setProperties(['thePropertyBar' => 'theBar']);
+        $transportMessage->setReplyTo('theReplyTo');
+        $transportMessage->setCorrelationId('theCorrelationId');
+
+        $driver = new NullDriver($this->createPsrContextMock(), $config, $this->createDummyQueueMetaRegistry());
+
+        $clientMessage = $driver->createClientMessage($transportMessage);
+
+        self::assertSame('theBody', $clientMessage->getBody());
+        self::assertSame(MessagePriority::LOW, $clientMessage->getPriority());
+        self::assertSame('theContentType', $clientMessage->getContentType());
+        self::assertSame(123, $clientMessage->getDelay());
+        self::assertSame(345, $clientMessage->getExpire());
+        self::assertEquals([
+            'theHeaderFoo' => 'theFoo',
+            'content_type' => 'theContentType',
+            'expiration' => 345,
+            'delay' => 123,
+            'priority' => MessagePriority::LOW,
+            'timestamp' => 12345,
+            'message_id' => 'theMessageId',
+            'reply_to' => 'theReplyTo',
+            'correlation_id' => 'theCorrelationId',
+        ], $clientMessage->getHeaders());
+        self::assertSame([
+            'thePropertyBar' => 'theBar',
+        ], $clientMessage->getProperties());
+
+        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
+    }
+
+    public function testShouldReturnConfigInstance()
+    {
+        $config = Config::create();
+
+        $driver = new NullDriver($this->createPsrContextMock(), $config, $this->createDummyQueueMetaRegistry());
+        $result = $driver->getConfig();
+
+        self::assertSame($config, $result);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|NullContext
+     */
+    private function createPsrContextMock()
+    {
+        return $this->createMock(NullContext::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|NullProducer
+     */
+    private function createMessageProducer()
+    {
+        return $this->createMock(NullProducer::class);
+    }
+
+    /**
+     * @return QueueMetaRegistry
+     */
+    private function createDummyQueueMetaRegistry()
+    {
+        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
+        $registry->add('default');
+        $registry->add('aFooQueue');
+        $registry->add('aBarQueue', 'aBarQueue');
+
+        return $registry;
+    }
+
+    /**
+     * @return Config
+     */
+    private function createDummyConfig()
+    {
+        return Config::create('aPrefix');
+    }
+}
diff --git a/Tests/Client/Amqp/RabbitMqDriverTest.php b/Tests/Client/Driver/RabbitMqDriverTest.php
similarity index 99%
rename from Tests/Client/Amqp/RabbitMqDriverTest.php
rename to Tests/Client/Driver/RabbitMqDriverTest.php
index dbdd6d4..60c92b8 100644
--- a/Tests/Client/Amqp/RabbitMqDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqDriverTest.php
@@ -1,10 +1,10 @@
 <?php
 
-namespace Enqueue\Tests\Client\Amqp;
+namespace Enqueue\Tests\Client\Driver;
 
-use Enqueue\Client\Amqp\AmqpDriver;
-use Enqueue\Client\Amqp\RabbitMqDriver;
 use Enqueue\Client\Config;
+use Enqueue\Client\Driver\AmqpDriver;
+use Enqueue\Client\Driver\RabbitMqDriver;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Message;
 use Enqueue\Client\MessagePriority;
diff --git a/Tests/Client/Driver/RabbitMqStompDriverTest.php b/Tests/Client/Driver/RabbitMqStompDriverTest.php
new file mode 100644
index 0000000..b3d5a8b
--- /dev/null
+++ b/Tests/Client/Driver/RabbitMqStompDriverTest.php
@@ -0,0 +1,682 @@
+<?php
+
+namespace Enqueue\Tests\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\Driver\RabbitMqStompDriver;
+use Enqueue\Client\Driver\StompManagementClient;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Stomp\StompContext;
+use Enqueue\Stomp\StompDestination;
+use Enqueue\Stomp\StompMessage;
+use Enqueue\Stomp\StompProducer;
+use Enqueue\Test\ClassExtensionTrait;
+use Psr\Log\LoggerInterface;
+
+class RabbitMqStompDriverTest extends \PHPUnit\Framework\TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementsDriverInterface()
+    {
+        $this->assertClassImplements(DriverInterface::class, RabbitMqStompDriver::class);
+    }
+
+    public function testCouldBeConstructedWithRequiredArguments()
+    {
+        new RabbitMqStompDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry(),
+            $this->createManagementClientMock()
+        );
+    }
+
+    public function testShouldReturnConfigObject()
+    {
+        $config = $this->createDummyConfig();
+
+        $driver = new RabbitMqStompDriver(
+            $this->createPsrContextMock(),
+            $config,
+            $this->createDummyQueueMetaRegistry(),
+            $this->createManagementClientMock()
+        );
+
+        $this->assertSame($config, $driver->getConfig());
+    }
+
+    public function testShouldCreateAndReturnQueueInstance()
+    {
+        $expectedQueue = new StompDestination();
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new RabbitMqStompDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry(),
+            $this->createManagementClientMock()
+        );
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $expectedHeaders = [
+            'durable' => true,
+            'auto-delete' => false,
+            'exclusive' => false,
+            'x-max-priority' => 4,
+        ];
+
+        $this->assertSame($expectedQueue, $queue);
+        $this->assertTrue($queue->isDurable());
+        $this->assertFalse($queue->isAutoDelete());
+        $this->assertFalse($queue->isExclusive());
+        $this->assertSame($expectedHeaders, $queue->getHeaders());
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
+    {
+        $expectedQueue = new StompDestination();
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aBarQueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new RabbitMqStompDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry(),
+            $this->createManagementClientMock()
+        );
+
+        $queue = $driver->createQueue('aBarQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldConvertTransportMessageToClientMessage()
+    {
+        $transportMessage = new StompMessage();
+        $transportMessage->setBody('body');
+        $transportMessage->setHeaders(['hkey' => 'hval']);
+        $transportMessage->setProperties(['key' => 'val']);
+        $transportMessage->setHeader('content-type', 'ContentType');
+        $transportMessage->setHeader('expiration', '12345000');
+        $transportMessage->setHeader('priority', 3);
+        $transportMessage->setHeader('x-delay', '5678000');
+        $transportMessage->setMessageId('MessageId');
+        $transportMessage->setTimestamp(1000);
+        $transportMessage->setReplyTo('theReplyTo');
+        $transportMessage->setCorrelationId('theCorrelationId');
+
+        $driver = new RabbitMqStompDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry(),
+            $this->createManagementClientMock()
+        );
+
+        $clientMessage = $driver->createClientMessage($transportMessage);
+
+        $this->assertInstanceOf(Message::class, $clientMessage);
+        $this->assertSame('body', $clientMessage->getBody());
+        $this->assertSame(['hkey' => 'hval'], $clientMessage->getHeaders());
+        $this->assertSame(['key' => 'val'], $clientMessage->getProperties());
+        $this->assertSame('MessageId', $clientMessage->getMessageId());
+        $this->assertSame(12345, $clientMessage->getExpire());
+        $this->assertSame(5678, $clientMessage->getDelay());
+        $this->assertSame('ContentType', $clientMessage->getContentType());
+        $this->assertSame(1000, $clientMessage->getTimestamp());
+        $this->assertSame(MessagePriority::HIGH, $clientMessage->getPriority());
+        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
+    }
+
+    public function testShouldThrowExceptionIfXDelayIsNotNumeric()
+    {
+        $transportMessage = new StompMessage();
+        $transportMessage->setHeader('x-delay', 'is-not-numeric');
+
+        $driver = new RabbitMqStompDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry(),
+            $this->createManagementClientMock()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('x-delay header is not numeric. "is-not-numeric"');
+
+        $driver->createClientMessage($transportMessage);
+    }
+
+    public function testShouldThrowExceptionIfExpirationIsNotNumeric()
+    {
+        $transportMessage = new StompMessage();
+        $transportMessage->setHeader('expiration', 'is-not-numeric');
+
+        $driver = new RabbitMqStompDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry(),
+            $this->createManagementClientMock()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('expiration header is not numeric. "is-not-numeric"');
+
+        $driver->createClientMessage($transportMessage);
+    }
+
+    public function testShouldThrowExceptionIfCantConvertTransportPriorityToClientPriority()
+    {
+        $transportMessage = new StompMessage();
+        $transportMessage->setHeader('priority', 'unknown');
+
+        $driver = new RabbitMqStompDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry(),
+            $this->createManagementClientMock()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Cant convert transport priority to client: "unknown"');
+
+        $driver->createClientMessage($transportMessage);
+    }
+
+    public function testShouldThrowExceptionIfCantConvertClientPriorityToTransportPriority()
+    {
+        $clientMessage = new Message();
+        $clientMessage->setPriority('unknown');
+
+        $transportMessage = new StompMessage();
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new RabbitMqStompDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry(),
+            $this->createManagementClientMock()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Cant convert client priority to transport: "unknown"');
+
+        $driver->createTransportMessage($clientMessage);
+    }
+
+    public function testShouldConvertClientMessageToTransportMessage()
+    {
+        $clientMessage = new Message();
+        $clientMessage->setBody('body');
+        $clientMessage->setHeaders(['hkey' => 'hval']);
+        $clientMessage->setProperties(['key' => 'val']);
+        $clientMessage->setContentType('ContentType');
+        $clientMessage->setExpire(123);
+        $clientMessage->setPriority(MessagePriority::VERY_HIGH);
+        $clientMessage->setDelay(432);
+        $clientMessage->setMessageId('MessageId');
+        $clientMessage->setTimestamp(1000);
+        $clientMessage->setReplyTo('theReplyTo');
+        $clientMessage->setCorrelationId('theCorrelationId');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn(new StompMessage())
+        ;
+
+        $driver = new RabbitMqStompDriver(
+            $context,
+            new Config('', '', '', '', '', '', ['delay_plugin_installed' => true]),
+            $this->createDummyQueueMetaRegistry(),
+            $this->createManagementClientMock()
+        );
+
+        $transportMessage = $driver->createTransportMessage($clientMessage);
+
+        $this->assertInstanceOf(StompMessage::class, $transportMessage);
+        $this->assertSame('body', $transportMessage->getBody());
+        $this->assertSame([
+            'hkey' => 'hval',
+            'content-type' => 'ContentType',
+            'persistent' => true,
+            'message_id' => 'MessageId',
+            'timestamp' => 1000,
+            'reply-to' => 'theReplyTo',
+            'correlation_id' => 'theCorrelationId',
+            'expiration' => '123000',
+            'priority' => 4,
+            'x-delay' => '432000',
+        ], $transportMessage->getHeaders());
+        $this->assertSame(['key' => 'val'], $transportMessage->getProperties());
+        $this->assertSame('MessageId', $transportMessage->getMessageId());
+        $this->assertSame(1000, $transportMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
+    }
+
+    public function testShouldThrowExceptionIfDelayIsSetButDelayPluginInstalledOptionIsFalse()
+    {
+        $clientMessage = new Message();
+        $clientMessage->setDelay(123);
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn(new StompMessage())
+        ;
+
+        $driver = new RabbitMqStompDriver(
+            $context,
+            new Config('', '', '', '', '', '', ['delay_plugin_installed' => false]),
+            $this->createDummyQueueMetaRegistry(),
+            $this->createManagementClientMock()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The message delaying is not supported. In order to use delay feature install RabbitMQ delay plugin.');
+
+        $driver->createTransportMessage($clientMessage);
+    }
+
+    public function testShouldSendMessageToRouter()
+    {
+        $topic = new StompDestination();
+        $transportMessage = new StompMessage();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createTopic')
+            ->willReturn($topic)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new RabbitMqStompDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry(),
+            $this->createManagementClientMock()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+
+        $driver->sendToRouter($message);
+    }
+
+    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
+    {
+        $driver = new RabbitMqStompDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry(),
+            $this->createManagementClientMock()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Topic name parameter is required but is not set');
+
+        $driver->sendToRouter(new Message());
+    }
+
+    public function testShouldSendMessageToProcessor()
+    {
+        $queue = new StompDestination();
+        $transportMessage = new StompMessage();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.afooqueue')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new RabbitMqStompDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry(),
+            $this->createManagementClientMock()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldSendMessageToDelayExchangeIfDelaySet()
+    {
+        $queue = new StompDestination();
+        $queue->setStompName('queueName');
+
+        $delayTopic = new StompDestination();
+        $delayTopic->setStompName('delayTopic');
+
+        $transportMessage = new StompMessage();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($delayTopic), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createTopic')
+            ->willReturn($delayTopic)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new RabbitMqStompDriver(
+            $context,
+            new Config('', '', '', '', '', '', ['delay_plugin_installed' => true]),
+            $this->createDummyQueueMetaRegistry(),
+            $this->createManagementClientMock()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
+        $message->setDelay(10);
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
+    {
+        $driver = new RabbitMqStompDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry(),
+            $this->createManagementClientMock()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Processor name parameter is required but is not set');
+
+        $driver->sendToProcessor(new Message());
+    }
+
+    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
+    {
+        $driver = new RabbitMqStompDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry(),
+            $this->createManagementClientMock()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Queue name parameter is required but is not set');
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldNotSetupBrokerIfManagementPluginInstalledOptionIsNotEnabled()
+    {
+        $driver = new RabbitMqStompDriver(
+            $this->createPsrContextMock(),
+            new Config('', '', '', '', '', '', ['management_plugin_installed' => false]),
+            $this->createDummyQueueMetaRegistry(),
+            $this->createManagementClientMock()
+        );
+
+        $logger = $this->createLoggerMock();
+        $logger
+            ->expects($this->once())
+            ->method('debug')
+            ->with('[RabbitMqStompDriver] Could not setup broker. The option `management_plugin_installed` is not enabled. Please enable that option and install rabbit management plugin')
+        ;
+
+        $driver->setupBroker($logger);
+    }
+
+    public function testShouldSetupBroker()
+    {
+        $metaRegistry = $this->createDummyQueueMetaRegistry();
+
+        $managementClient = $this->createManagementClientMock();
+        $managementClient
+            ->expects($this->at(0))
+            ->method('declareExchange')
+            ->with('prefix.routertopic', [
+                'type' => 'fanout',
+                'durable' => true,
+                'auto_delete' => false,
+            ])
+        ;
+        $managementClient
+            ->expects($this->at(1))
+            ->method('declareQueue')
+            ->with('prefix.app.routerqueue', [
+                'durable' => true,
+                'auto_delete' => false,
+                'arguments' => [
+                    'x-max-priority' => 4,
+                ],
+            ])
+        ;
+        $managementClient
+            ->expects($this->at(2))
+            ->method('bind')
+            ->with('prefix.routertopic', 'prefix.app.routerqueue', 'prefix.app.routerqueue')
+        ;
+        $managementClient
+            ->expects($this->at(3))
+            ->method('declareQueue')
+            ->with('prefix.app.default', [
+                'durable' => true,
+                'auto_delete' => false,
+                'arguments' => [
+                    'x-max-priority' => 4,
+                ],
+            ])
+        ;
+
+        $driver = new RabbitMqStompDriver(
+            $this->createPsrContextMock(),
+            new Config('prefix', 'app', 'routerTopic', 'routerQueue', 'processorQueue', '', ['management_plugin_installed' => true]),
+            $metaRegistry,
+            $managementClient
+        );
+
+        $logger = $this->createLoggerMock();
+        $logger
+            ->expects($this->at(0))
+            ->method('debug')
+            ->with('[RabbitMqStompDriver] Declare router exchange: prefix.routertopic')
+        ;
+        $logger
+            ->expects($this->at(1))
+            ->method('debug')
+            ->with('[RabbitMqStompDriver] Declare router queue: prefix.app.routerqueue')
+        ;
+        $logger
+            ->expects($this->at(2))
+            ->method('debug')
+            ->with('[RabbitMqStompDriver] Bind router queue to exchange: prefix.app.routerqueue -> prefix.routertopic')
+        ;
+        $logger
+            ->expects($this->at(3))
+            ->method('debug')
+            ->with('[RabbitMqStompDriver] Declare processor queue: prefix.app.default')
+        ;
+
+        $driver->setupBroker($logger);
+    }
+
+    public function testSetupBrokerShouldCreateDelayExchangeIfEnabled()
+    {
+        $metaRegistry = $this->createDummyQueueMetaRegistry();
+
+        $managementClient = $this->createManagementClientMock();
+        $managementClient
+            ->expects($this->at(6))
+            ->method('declareExchange')
+            ->with('prefix.app.default.delayed', [
+                'type' => 'x-delayed-message',
+                'durable' => true,
+                'auto_delete' => false,
+                'arguments' => [
+                    'x-delayed-type' => 'direct',
+                ],
+            ])
+        ;
+        $managementClient
+            ->expects($this->at(7))
+            ->method('bind')
+            ->with('prefix.app.default.delayed', 'prefix.app.default', 'prefix.app.default')
+        ;
+
+        $driver = new RabbitMqStompDriver(
+            $this->createPsrContextMock(),
+            new Config('prefix', 'app', 'routerTopic', 'routerQueue', 'processorQueue', '', ['management_plugin_installed' => true, 'delay_plugin_installed' => true]),
+            $metaRegistry,
+            $managementClient
+        );
+
+        $logger = $this->createLoggerMock();
+        $logger
+            ->expects($this->at(6))
+            ->method('debug')
+            ->with('[RabbitMqStompDriver] Declare delay exchange: prefix.app.default.delayed')
+        ;
+        $logger
+            ->expects($this->at(7))
+            ->method('debug')
+            ->with('[RabbitMqStompDriver] Bind processor queue to delay exchange: prefix.app.default -> prefix.app.default.delayed')
+        ;
+
+        $driver->setupBroker($logger);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|StompContext
+     */
+    private function createPsrContextMock()
+    {
+        return $this->createMock(StompContext::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|StompProducer
+     */
+    private function createPsrProducerMock()
+    {
+        return $this->createMock(StompProducer::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createManagementClientMock(): StompManagementClient
+    {
+        return $this->createMock(StompManagementClient::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|LoggerInterface
+     */
+    private function createLoggerMock()
+    {
+        return $this->createMock(LoggerInterface::class);
+    }
+
+    /**
+     * @return QueueMetaRegistry
+     */
+    private function createDummyQueueMetaRegistry()
+    {
+        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
+        $registry->add('default');
+        $registry->add('aFooQueue');
+        $registry->add('aBarQueue', 'aBarQueue');
+
+        return $registry;
+    }
+
+    /**
+     * @return Config
+     */
+    private function createDummyConfig()
+    {
+        return Config::create('aPrefix');
+    }
+}
diff --git a/Tests/Client/Driver/RdKafkaDriverTest.php b/Tests/Client/Driver/RdKafkaDriverTest.php
new file mode 100644
index 0000000..f2b7c86
--- /dev/null
+++ b/Tests/Client/Driver/RdKafkaDriverTest.php
@@ -0,0 +1,368 @@
+<?php
+
+namespace Enqueue\Tests\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\Driver\RdKafkaDriver;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\RdKafka\RdKafkaContext;
+use Enqueue\RdKafka\RdKafkaMessage;
+use Enqueue\RdKafka\RdKafkaTopic;
+use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrProducer;
+
+/**
+ * @group rdkafka
+ */
+class RdKafkaDriverTest extends \PHPUnit_Framework_TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementsDriverInterface()
+    {
+        $this->assertClassImplements(DriverInterface::class, RdKafkaDriver::class);
+    }
+
+    public function testCouldBeConstructedWithRequiredArguments()
+    {
+        new RdKafkaDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+    }
+
+    public function testShouldReturnConfigObject()
+    {
+        $config = $this->createDummyConfig();
+
+        $driver = new RdKafkaDriver(
+            $this->createPsrContextMock(),
+            $config,
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->assertSame($config, $driver->getConfig());
+    }
+
+    public function testShouldCreateAndReturnQueueInstance()
+    {
+        $expectedQueue = new RdKafkaTopic('aName');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new RdKafkaDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
+    {
+        $expectedQueue = new RdKafkaTopic('aName');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aBarQueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new RdKafkaDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aBarQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldConvertTransportMessageToClientMessage()
+    {
+        $transportMessage = new RdKafkaMessage();
+        $transportMessage->setBody('body');
+        $transportMessage->setHeaders(['hkey' => 'hval']);
+        $transportMessage->setProperties(['key' => 'val']);
+        $transportMessage->setHeader('content_type', 'ContentType');
+        $transportMessage->setMessageId('MessageId');
+        $transportMessage->setTimestamp(1000);
+
+        $driver = new RdKafkaDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $clientMessage = $driver->createClientMessage($transportMessage);
+
+        $this->assertInstanceOf(Message::class, $clientMessage);
+        $this->assertSame('body', $clientMessage->getBody());
+        $this->assertSame([
+            'hkey' => 'hval',
+            'content_type' => 'ContentType',
+            'message_id' => 'MessageId',
+            'timestamp' => 1000,
+        ], $clientMessage->getHeaders());
+        $this->assertSame([
+            'key' => 'val',
+        ], $clientMessage->getProperties());
+        $this->assertSame('MessageId', $clientMessage->getMessageId());
+        $this->assertSame('ContentType', $clientMessage->getContentType());
+        $this->assertSame(1000, $clientMessage->getTimestamp());
+
+        $this->assertNull($clientMessage->getExpire());
+    }
+
+    public function testShouldConvertClientMessageToTransportMessage()
+    {
+        $clientMessage = new Message();
+        $clientMessage->setBody('body');
+        $clientMessage->setHeaders(['hkey' => 'hval']);
+        $clientMessage->setProperties(['key' => 'val']);
+        $clientMessage->setContentType('ContentType');
+        $clientMessage->setExpire(123);
+        $clientMessage->setMessageId('MessageId');
+        $clientMessage->setTimestamp(1000);
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn(new RdKafkaMessage())
+        ;
+
+        $driver = new RdKafkaDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $transportMessage = $driver->createTransportMessage($clientMessage);
+
+        $this->assertInstanceOf(RdKafkaMessage::class, $transportMessage);
+        $this->assertSame('body', $transportMessage->getBody());
+        $this->assertSame([
+            'hkey' => 'hval',
+            'content_type' => 'ContentType',
+            'message_id' => 'MessageId',
+            'timestamp' => 1000,
+            'reply_to' => null,
+            'correlation_id' => '',
+        ], $transportMessage->getHeaders());
+        $this->assertSame([
+            'key' => 'val',
+        ], $transportMessage->getProperties());
+        $this->assertSame('MessageId', $transportMessage->getMessageId());
+        $this->assertSame(1000, $transportMessage->getTimestamp());
+    }
+
+    public function testShouldSendMessageToRouter()
+    {
+        $topic = new RdKafkaTopic('queue-name');
+        $transportMessage = new RdKafkaMessage();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createTopic')
+            ->with('aprefix.router')
+            ->willReturn($topic)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new RdKafkaDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+
+        $driver->sendToRouter($message);
+    }
+
+    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
+    {
+        $driver = new RdKafkaDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Topic name parameter is required but is not set');
+
+        $driver->sendToRouter(new Message());
+    }
+
+    public function testShouldSendMessageToProcessor()
+    {
+        $queue = new RdKafkaTopic('queue-name');
+        $transportMessage = new RdKafkaMessage();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new RdKafkaDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
+    {
+        $driver = new RdKafkaDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Processor name parameter is required but is not set');
+
+        $driver->sendToProcessor(new Message());
+    }
+
+    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
+    {
+        $driver = new RdKafkaDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Queue name parameter is required but is not set');
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldSetupBroker()
+    {
+        $routerTopic = new RdKafkaTopic('');
+        $routerQueue = new RdKafkaTopic('');
+
+        $processorTopic = new RdKafkaTopic('');
+
+        $context = $this->createPsrContextMock();
+
+        $context
+            ->expects($this->at(0))
+            ->method('createQueue')
+            ->willReturn($routerTopic)
+        ;
+        $context
+            ->expects($this->at(1))
+            ->method('createQueue')
+            ->willReturn($routerQueue)
+        ;
+        $context
+            ->expects($this->at(2))
+            ->method('createQueue')
+            ->willReturn($processorTopic)
+        ;
+
+        $meta = new QueueMetaRegistry($this->createDummyConfig(), [
+            'default' => [],
+        ]);
+
+        $driver = new RdKafkaDriver(
+            $context,
+            $this->createDummyConfig(),
+            $meta
+        );
+
+        $driver->setupBroker();
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|RdKafkaContext
+     */
+    private function createPsrContextMock()
+    {
+        return $this->createMock(RdKafkaContext::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProducer
+     */
+    private function createPsrProducerMock()
+    {
+        return $this->createMock(PsrProducer::class);
+    }
+
+    /**
+     * @return QueueMetaRegistry
+     */
+    private function createDummyQueueMetaRegistry()
+    {
+        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
+        $registry->add('default');
+        $registry->add('aFooQueue');
+        $registry->add('aBarQueue', 'aBarQueue');
+
+        return $registry;
+    }
+
+    /**
+     * @return Config
+     */
+    private function createDummyConfig()
+    {
+        return Config::create('aPrefix');
+    }
+}
diff --git a/Tests/Client/Driver/RedisDriverTest.php b/Tests/Client/Driver/RedisDriverTest.php
new file mode 100644
index 0000000..1dd5950
--- /dev/null
+++ b/Tests/Client/Driver/RedisDriverTest.php
@@ -0,0 +1,366 @@
+<?php
+
+namespace Enqueue\Tests\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\Driver\RedisDriver;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Redis\RedisContext;
+use Enqueue\Redis\RedisDestination;
+use Enqueue\Redis\RedisMessage;
+use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrProducer;
+use PHPUnit\Framework\TestCase;
+
+class RedisDriverTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementsDriverInterface()
+    {
+        $this->assertClassImplements(DriverInterface::class, RedisDriver::class);
+    }
+
+    public function testCouldBeConstructedWithRequiredArguments()
+    {
+        new RedisDriver(
+            $this->createPsrContextMock(),
+            Config::create(),
+            $this->createDummyQueueMetaRegistry()
+        );
+    }
+
+    public function testShouldReturnConfigObject()
+    {
+        $config = Config::create();
+
+        $driver = new RedisDriver($this->createPsrContextMock(), $config, $this->createDummyQueueMetaRegistry());
+
+        $this->assertSame($config, $driver->getConfig());
+    }
+
+    public function testShouldCreateAndReturnQueueInstance()
+    {
+        $expectedQueue = new RedisDestination('aName');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new RedisDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
+    {
+        $expectedQueue = new RedisDestination('aName');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aBarQueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new RedisDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aBarQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldConvertTransportMessageToClientMessage()
+    {
+        $transportMessage = new RedisMessage();
+        $transportMessage->setBody('body');
+        $transportMessage->setHeaders(['hkey' => 'hval']);
+        $transportMessage->setProperties(['key' => 'val']);
+        $transportMessage->setHeader('content_type', 'ContentType');
+        $transportMessage->setMessageId('MessageId');
+        $transportMessage->setTimestamp(1000);
+        $transportMessage->setReplyTo('theReplyTo');
+        $transportMessage->setCorrelationId('theCorrelationId');
+        $transportMessage->setReplyTo('theReplyTo');
+        $transportMessage->setCorrelationId('theCorrelationId');
+
+        $driver = new RedisDriver(
+            $this->createPsrContextMock(),
+            Config::create(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $clientMessage = $driver->createClientMessage($transportMessage);
+
+        $this->assertInstanceOf(Message::class, $clientMessage);
+        $this->assertSame('body', $clientMessage->getBody());
+        $this->assertSame([
+            'hkey' => 'hval',
+            'content_type' => 'ContentType',
+            'message_id' => 'MessageId',
+            'timestamp' => 1000,
+            'reply_to' => 'theReplyTo',
+            'correlation_id' => 'theCorrelationId',
+        ], $clientMessage->getHeaders());
+        $this->assertSame([
+            'key' => 'val',
+        ], $clientMessage->getProperties());
+        $this->assertSame('MessageId', $clientMessage->getMessageId());
+        $this->assertSame('ContentType', $clientMessage->getContentType());
+        $this->assertSame(1000, $clientMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
+
+        $this->assertNull($clientMessage->getExpire());
+        $this->assertSame(MessagePriority::NORMAL, $clientMessage->getPriority());
+    }
+
+    public function testShouldConvertClientMessageToTransportMessage()
+    {
+        $clientMessage = new Message();
+        $clientMessage->setBody('body');
+        $clientMessage->setHeaders(['hkey' => 'hval']);
+        $clientMessage->setProperties(['key' => 'val']);
+        $clientMessage->setContentType('ContentType');
+        $clientMessage->setExpire(123);
+        $clientMessage->setPriority(MessagePriority::VERY_HIGH);
+        $clientMessage->setMessageId('MessageId');
+        $clientMessage->setTimestamp(1000);
+        $clientMessage->setReplyTo('theReplyTo');
+        $clientMessage->setCorrelationId('theCorrelationId');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn(new RedisMessage())
+        ;
+
+        $driver = new RedisDriver(
+            $context,
+            Config::create(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $transportMessage = $driver->createTransportMessage($clientMessage);
+
+        $this->assertInstanceOf(RedisMessage::class, $transportMessage);
+        $this->assertSame('body', $transportMessage->getBody());
+        $this->assertSame([
+            'hkey' => 'hval',
+            'content_type' => 'ContentType',
+            'message_id' => 'MessageId',
+            'timestamp' => 1000,
+            'reply_to' => 'theReplyTo',
+            'correlation_id' => 'theCorrelationId',
+        ], $transportMessage->getHeaders());
+        $this->assertSame([
+            'key' => 'val',
+        ], $transportMessage->getProperties());
+        $this->assertSame('MessageId', $transportMessage->getMessageId());
+        $this->assertSame(1000, $transportMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
+    }
+
+    public function testShouldSendMessageToRouterQueue()
+    {
+        $topic = new RedisDestination('aDestinationName');
+        $transportMessage = new RedisMessage();
+        $config = $this->createDummyConfig();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.default')
+            ->willReturn($topic)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new RedisDriver(
+            $context,
+            $config,
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+
+        $driver->sendToRouter($message);
+    }
+
+    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
+    {
+        $driver = new RedisDriver(
+            $this->createPsrContextMock(),
+            Config::create(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Topic name parameter is required but is not set');
+
+        $driver->sendToRouter(new Message());
+    }
+
+    public function testShouldSendMessageToProcessor()
+    {
+        $queue = new RedisDestination('aDestinationName');
+        $transportMessage = new RedisMessage();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new RedisDriver(
+            $context,
+            Config::create(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
+    {
+        $driver = new RedisDriver(
+            $this->createPsrContextMock(),
+            Config::create(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Processor name parameter is required but is not set');
+
+        $driver->sendToProcessor(new Message());
+    }
+
+    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
+    {
+        $driver = new RedisDriver(
+            $this->createPsrContextMock(),
+            Config::create(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Queue name parameter is required but is not set');
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldDoNothingOnSetupBroker()
+    {
+        $context = $this->createPsrContextMock();
+        // setup router
+        $context
+            ->expects($this->never())
+            ->method('createTopic')
+        ;
+        $context
+            ->expects($this->never())
+            ->method('createQueue')
+        ;
+
+        $meta = new QueueMetaRegistry(Config::create(), [
+            'default' => [],
+        ]);
+
+        $driver = new RedisDriver(
+            $context,
+            Config::create(),
+            $meta
+        );
+
+        $driver->setupBroker();
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|RedisContext
+     */
+    private function createPsrContextMock()
+    {
+        return $this->createMock(RedisContext::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProducer
+     */
+    private function createPsrProducerMock()
+    {
+        return $this->createMock(PsrProducer::class);
+    }
+
+    /**
+     * @return QueueMetaRegistry
+     */
+    private function createDummyQueueMetaRegistry()
+    {
+        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
+        $registry->add('default');
+        $registry->add('aFooQueue');
+        $registry->add('aBarQueue', 'aBarQueue');
+
+        return $registry;
+    }
+
+    /**
+     * @return Config
+     */
+    private function createDummyConfig()
+    {
+        return Config::create('aPrefix');
+    }
+}
diff --git a/Tests/Client/Driver/SqsDriverTest.php b/Tests/Client/Driver/SqsDriverTest.php
new file mode 100644
index 0000000..92bbaaf
--- /dev/null
+++ b/Tests/Client/Driver/SqsDriverTest.php
@@ -0,0 +1,408 @@
+<?php
+
+namespace Enqueue\Tests\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\Driver\SqsDriver;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\Meta\QueueMeta;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Sqs\SqsContext;
+use Enqueue\Sqs\SqsDestination;
+use Enqueue\Sqs\SqsMessage;
+use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrProducer;
+use PHPUnit\Framework\TestCase;
+
+class SqsDriverTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementsDriverInterface()
+    {
+        $this->assertClassImplements(DriverInterface::class, SqsDriver::class);
+    }
+
+    public function testCouldBeConstructedWithRequiredArguments()
+    {
+        new SqsDriver(
+            $this->createPsrContextMock(),
+            Config::create(),
+            $this->createQueueMetaRegistryMock()
+        );
+    }
+
+    public function testShouldReturnConfigObject()
+    {
+        $config = Config::create();
+
+        $driver = new SqsDriver($this->createPsrContextMock(), $config, $this->createQueueMetaRegistryMock());
+
+        $this->assertSame($config, $driver->getConfig());
+    }
+
+    public function testShouldCreateAndReturnQueueInstance()
+    {
+        $expectedQueue = new SqsDestination('aQueueName');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix_dot_afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new SqsDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+        $this->assertSame('aQueueName', $queue->getQueueName());
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
+    {
+        $expectedQueue = new SqsDestination('aQueueName');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aBarQueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new SqsDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aBarQueue');
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldConvertTransportMessageToClientMessage()
+    {
+        $transportMessage = new SqsMessage();
+        $transportMessage->setBody('body');
+        $transportMessage->setHeaders(['hkey' => 'hval']);
+        $transportMessage->setProperties(['key' => 'val']);
+        $transportMessage->setHeader('content_type', 'ContentType');
+        $transportMessage->setMessageId('MessageId');
+        $transportMessage->setTimestamp(1000);
+        $transportMessage->setReplyTo('theReplyTo');
+        $transportMessage->setCorrelationId('theCorrelationId');
+        $transportMessage->setReplyTo('theReplyTo');
+        $transportMessage->setCorrelationId('theCorrelationId');
+
+        $driver = new SqsDriver(
+            $this->createPsrContextMock(),
+            Config::create(),
+            $this->createQueueMetaRegistryMock()
+        );
+
+        $clientMessage = $driver->createClientMessage($transportMessage);
+
+        $this->assertInstanceOf(Message::class, $clientMessage);
+        $this->assertSame('body', $clientMessage->getBody());
+        $this->assertSame([
+            'hkey' => 'hval',
+            'content_type' => 'ContentType',
+            'message_id' => 'MessageId',
+            'timestamp' => 1000,
+            'reply_to' => 'theReplyTo',
+            'correlation_id' => 'theCorrelationId',
+        ], $clientMessage->getHeaders());
+        $this->assertSame([
+            'key' => 'val',
+        ], $clientMessage->getProperties());
+        $this->assertSame('MessageId', $clientMessage->getMessageId());
+        $this->assertSame('ContentType', $clientMessage->getContentType());
+        $this->assertSame(1000, $clientMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
+
+        $this->assertNull($clientMessage->getExpire());
+        $this->assertSame(MessagePriority::NORMAL, $clientMessage->getPriority());
+    }
+
+    public function testShouldConvertClientMessageToTransportMessage()
+    {
+        $clientMessage = new Message();
+        $clientMessage->setBody('body');
+        $clientMessage->setHeaders(['hkey' => 'hval']);
+        $clientMessage->setProperties(['key' => 'val']);
+        $clientMessage->setContentType('ContentType');
+        $clientMessage->setExpire(123);
+        $clientMessage->setPriority(MessagePriority::VERY_HIGH);
+        $clientMessage->setMessageId('MessageId');
+        $clientMessage->setTimestamp(1000);
+        $clientMessage->setReplyTo('theReplyTo');
+        $clientMessage->setCorrelationId('theCorrelationId');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn(new SqsMessage())
+        ;
+
+        $driver = new SqsDriver(
+            $context,
+            Config::create(),
+            $this->createQueueMetaRegistryMock()
+        );
+
+        $transportMessage = $driver->createTransportMessage($clientMessage);
+
+        $this->assertInstanceOf(SqsMessage::class, $transportMessage);
+        $this->assertSame('body', $transportMessage->getBody());
+        $this->assertSame([
+            'hkey' => 'hval',
+            'content_type' => 'ContentType',
+            'message_id' => 'MessageId',
+            'timestamp' => 1000,
+            'reply_to' => 'theReplyTo',
+            'correlation_id' => 'theCorrelationId',
+        ], $transportMessage->getHeaders());
+        $this->assertSame([
+            'key' => 'val',
+        ], $transportMessage->getProperties());
+        $this->assertSame('MessageId', $transportMessage->getMessageId());
+        $this->assertSame(1000, $transportMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
+    }
+
+    public function testShouldSendMessageToRouterQueue()
+    {
+        $topic = new SqsDestination('aDestinationName');
+        $transportMessage = new SqsMessage();
+        $config = $this->createConfigMock();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('theTransportName')
+            ->willReturn($topic)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $meta = $this->createQueueMetaRegistryMock();
+        $meta
+            ->expects($this->once())
+            ->method('getQueueMeta')
+            ->willReturn(new QueueMeta('theClientName', 'theTransportName'))
+        ;
+
+        $driver = new SqsDriver($context, $config, $meta);
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+
+        $driver->sendToRouter($message);
+    }
+
+    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
+    {
+        $driver = new SqsDriver(
+            $this->createPsrContextMock(),
+            Config::create(),
+            $this->createQueueMetaRegistryMock()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Topic name parameter is required but is not set');
+
+        $driver->sendToRouter(new Message());
+    }
+
+    public function testShouldSendMessageToProcessor()
+    {
+        $queue = new SqsDestination('aDestinationName');
+        $transportMessage = new SqsMessage();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $meta = $this->createQueueMetaRegistryMock();
+        $meta
+            ->expects($this->once())
+            ->method('getQueueMeta')
+            ->willReturn(new QueueMeta('theClientName', 'theTransportName'))
+        ;
+
+        $driver = new SqsDriver($context, Config::create(), $meta);
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'queue');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
+    {
+        $driver = new SqsDriver(
+            $this->createPsrContextMock(),
+            Config::create(),
+            $this->createQueueMetaRegistryMock()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Processor name parameter is required but is not set');
+
+        $driver->sendToProcessor(new Message());
+    }
+
+    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
+    {
+        $driver = new SqsDriver(
+            $this->createPsrContextMock(),
+            Config::create(),
+            $this->createQueueMetaRegistryMock()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Queue name parameter is required but is not set');
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldSetupBroker()
+    {
+        $routerQueue = new SqsDestination('');
+        $processorQueue = new SqsDestination('');
+
+        $context = $this->createPsrContextMock();
+        // setup router
+        $context
+            ->expects($this->at(0))
+            ->method('createQueue')
+            ->willReturn($routerQueue)
+        ;
+        $context
+            ->expects($this->at(1))
+            ->method('declareQueue')
+            ->with($this->identicalTo($routerQueue))
+        ;
+        // setup processor queue
+        $context
+            ->expects($this->at(2))
+            ->method('createQueue')
+            ->willReturn($processorQueue)
+        ;
+        $context
+            ->expects($this->at(3))
+            ->method('declareQueue')
+            ->with($this->identicalTo($processorQueue))
+        ;
+
+        $metaRegistry = $this->createQueueMetaRegistryMock();
+        $metaRegistry
+            ->expects($this->once())
+            ->method('getQueuesMeta')
+            ->willReturn([new QueueMeta('theClientName', 'theTransportName')])
+        ;
+        $metaRegistry
+            ->expects($this->exactly(2))
+            ->method('getQueueMeta')
+            ->willReturn(new QueueMeta('theClientName', 'theTransportName'))
+        ;
+
+        $driver = new SqsDriver($context, $this->createConfigMock(), $metaRegistry);
+
+        $driver->setupBroker();
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|SqsContext
+     */
+    private function createPsrContextMock()
+    {
+        return $this->createMock(SqsContext::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProducer
+     */
+    private function createPsrProducerMock()
+    {
+        return $this->createMock(PsrProducer::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|QueueMetaRegistry
+     */
+    private function createQueueMetaRegistryMock()
+    {
+        return $this->createMock(QueueMetaRegistry::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|Config
+     */
+    private function createConfigMock()
+    {
+        return $this->createMock(Config::class);
+    }
+
+    /**
+     * @return Config
+     */
+    private function createDummyConfig()
+    {
+        return Config::create('aPrefix');
+    }
+
+    /**
+     * @return QueueMetaRegistry
+     */
+    private function createDummyQueueMetaRegistry()
+    {
+        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
+        $registry->add('default');
+        $registry->add('aFooQueue');
+        $registry->add('aBarQueue', 'aBarQueue');
+
+        return $registry;
+    }
+}
diff --git a/Tests/Client/Driver/StompDriverTest.php b/Tests/Client/Driver/StompDriverTest.php
new file mode 100644
index 0000000..a8e9dab
--- /dev/null
+++ b/Tests/Client/Driver/StompDriverTest.php
@@ -0,0 +1,342 @@
+<?php
+
+namespace Enqueue\Tests\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\Driver\StompDriver;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Stomp\StompContext;
+use Enqueue\Stomp\StompDestination;
+use Enqueue\Stomp\StompMessage;
+use Enqueue\Stomp\StompProducer;
+use Enqueue\Test\ClassExtensionTrait;
+use Psr\Log\LoggerInterface;
+
+class StompDriverTest extends \PHPUnit\Framework\TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementsDriverInterface()
+    {
+        $this->assertClassImplements(DriverInterface::class, StompDriver::class);
+    }
+
+    public function testCouldBeConstructedWithRequiredArguments()
+    {
+        new StompDriver($this->createPsrContextMock(), $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+    }
+
+    public function testShouldReturnConfigObject()
+    {
+        $config = $this->createDummyConfig();
+
+        $driver = new StompDriver($this->createPsrContextMock(), $config, $this->createDummyQueueMetaRegistry());
+
+        $this->assertSame($config, $driver->getConfig());
+    }
+
+    public function testShouldCreateAndReturnQueueInstance()
+    {
+        $expectedQueue = new StompDestination();
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new StompDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+        $this->assertTrue($queue->isDurable());
+        $this->assertFalse($queue->isAutoDelete());
+        $this->assertFalse($queue->isExclusive());
+        $this->assertSame([
+            'durable' => true,
+            'auto-delete' => false,
+            'exclusive' => false,
+        ], $queue->getHeaders());
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
+    {
+        $expectedQueue = new StompDestination();
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aBarQueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new StompDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $queue = $driver->createQueue('aBarQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldConvertTransportMessageToClientMessage()
+    {
+        $transportMessage = new StompMessage();
+        $transportMessage->setBody('body');
+        $transportMessage->setHeaders(['hkey' => 'hval']);
+        $transportMessage->setProperties(['key' => 'val']);
+        $transportMessage->setHeader('content-type', 'ContentType');
+        $transportMessage->setMessageId('MessageId');
+        $transportMessage->setTimestamp(1000);
+        $transportMessage->setReplyTo('theReplyTo');
+        $transportMessage->setCorrelationId('theCorrelationId');
+
+        $driver = new StompDriver($this->createPsrContextMock(), $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $clientMessage = $driver->createClientMessage($transportMessage);
+
+        $this->assertInstanceOf(Message::class, $clientMessage);
+        $this->assertSame('body', $clientMessage->getBody());
+        $this->assertSame(['hkey' => 'hval'], $clientMessage->getHeaders());
+        $this->assertSame(['key' => 'val'], $clientMessage->getProperties());
+        $this->assertSame('MessageId', $clientMessage->getMessageId());
+        $this->assertSame('ContentType', $clientMessage->getContentType());
+        $this->assertSame(1000, $clientMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
+    }
+
+    public function testShouldConvertClientMessageToTransportMessage()
+    {
+        $clientMessage = new Message();
+        $clientMessage->setBody('body');
+        $clientMessage->setHeaders(['hkey' => 'hval']);
+        $clientMessage->setProperties(['key' => 'val']);
+        $clientMessage->setContentType('ContentType');
+        $clientMessage->setMessageId('MessageId');
+        $clientMessage->setTimestamp(1000);
+        $clientMessage->setReplyTo('theReplyTo');
+        $clientMessage->setCorrelationId('theCorrelationId');
+
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn(new StompMessage())
+        ;
+
+        $driver = new StompDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+
+        $transportMessage = $driver->createTransportMessage($clientMessage);
+
+        $this->assertInstanceOf(StompMessage::class, $transportMessage);
+        $this->assertSame('body', $transportMessage->getBody());
+        $this->assertSame([
+            'hkey' => 'hval',
+            'content-type' => 'ContentType',
+            'persistent' => true,
+            'message_id' => 'MessageId',
+            'timestamp' => 1000,
+            'reply-to' => 'theReplyTo',
+            'correlation_id' => 'theCorrelationId',
+        ], $transportMessage->getHeaders());
+        $this->assertSame(['key' => 'val'], $transportMessage->getProperties());
+        $this->assertSame('MessageId', $transportMessage->getMessageId());
+        $this->assertSame(1000, $transportMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
+    }
+
+    public function testShouldSendMessageToRouter()
+    {
+        $topic = new StompDestination();
+        $transportMessage = new StompMessage();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createTopic')
+            ->willReturn($topic)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new StompDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+
+        $driver->sendToRouter($message);
+    }
+
+    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
+    {
+        $driver = new StompDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Topic name parameter is required but is not set');
+
+        $driver->sendToRouter(new Message());
+    }
+
+    public function testShouldSendMessageToProcessor()
+    {
+        $queue = new StompDestination();
+        $transportMessage = new StompMessage();
+
+        $producer = $this->createPsrProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createPsrContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new StompDriver(
+            $context,
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
+    {
+        $driver = new StompDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Processor name parameter is required but is not set');
+
+        $driver->sendToProcessor(new Message());
+    }
+
+    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
+    {
+        $driver = new StompDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Queue name parameter is required but is not set');
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testSetupBrokerShouldOnlyLogMessageThatStompDoesNotSupportBrokerSetup()
+    {
+        $driver = new StompDriver(
+            $this->createPsrContextMock(),
+            $this->createDummyConfig(),
+            $this->createDummyQueueMetaRegistry()
+        );
+
+        $logger = $this->createLoggerMock();
+        $logger
+            ->expects($this->once())
+            ->method('debug')
+            ->with('[StompDriver] Stomp protocol does not support broker configuration')
+        ;
+
+        $driver->setupBroker($logger);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|StompContext
+     */
+    private function createPsrContextMock()
+    {
+        return $this->createMock(StompContext::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|StompProducer
+     */
+    private function createPsrProducerMock()
+    {
+        return $this->createMock(StompProducer::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|LoggerInterface
+     */
+    private function createLoggerMock()
+    {
+        return $this->createMock(LoggerInterface::class);
+    }
+
+    /**
+     * @return QueueMetaRegistry
+     */
+    private function createDummyQueueMetaRegistry()
+    {
+        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
+        $registry->add('default');
+        $registry->add('aFooQueue');
+        $registry->add('aBarQueue', 'aBarQueue');
+
+        return $registry;
+    }
+
+    /**
+     * @return Config
+     */
+    private function createDummyConfig()
+    {
+        return Config::create('aPrefix');
+    }
+}
diff --git a/Tests/Client/Driver/StompManagementClientTest.php b/Tests/Client/Driver/StompManagementClientTest.php
new file mode 100644
index 0000000..033c070
--- /dev/null
+++ b/Tests/Client/Driver/StompManagementClientTest.php
@@ -0,0 +1,111 @@
+<?php
+
+namespace Enqueue\Tests\Client\Driver;
+
+use Enqueue\Client\Driver\StompManagementClient;
+use RabbitMq\ManagementApi\Api\Binding;
+use RabbitMq\ManagementApi\Api\Exchange;
+use RabbitMq\ManagementApi\Api\Queue;
+use RabbitMq\ManagementApi\Client;
+
+class StompManagementClientTest extends \PHPUnit\Framework\TestCase
+{
+    public function testCouldDeclareExchange()
+    {
+        $exchange = $this->createExchangeMock();
+        $exchange
+            ->expects($this->once())
+            ->method('create')
+            ->with('vhost', 'name', ['options'])
+        ;
+
+        $client = $this->createClientMock();
+        $client
+            ->expects($this->once())
+            ->method('exchanges')
+            ->willReturn($exchange)
+        ;
+
+        $management = new StompManagementClient($client, 'vhost');
+        $management->declareExchange('name', ['options']);
+    }
+
+    public function testCouldDeclareQueue()
+    {
+        $queue = $this->createQueueMock();
+        $queue
+            ->expects($this->once())
+            ->method('create')
+            ->with('vhost', 'name', ['options'])
+        ;
+
+        $client = $this->createClientMock();
+        $client
+            ->expects($this->once())
+            ->method('queues')
+            ->willReturn($queue)
+        ;
+
+        $management = new StompManagementClient($client, 'vhost');
+        $management->declareQueue('name', ['options']);
+    }
+
+    public function testCouldBind()
+    {
+        $binding = $this->createBindingMock();
+        $binding
+            ->expects($this->once())
+            ->method('create')
+            ->with('vhost', 'exchange', 'queue', 'routing-key', ['arguments'])
+        ;
+
+        $client = $this->createClientMock();
+        $client
+            ->expects($this->once())
+            ->method('bindings')
+            ->willReturn($binding)
+        ;
+
+        $management = new StompManagementClient($client, 'vhost');
+        $management->bind('exchange', 'queue', 'routing-key', ['arguments']);
+    }
+
+    public function testCouldCreateNewInstanceUsingFactory()
+    {
+        $instance = StompManagementClient::create('', '');
+
+        $this->assertInstanceOf(StompManagementClient::class, $instance);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|Client
+     */
+    private function createClientMock()
+    {
+        return $this->createMock(Client::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|Exchange
+     */
+    private function createExchangeMock()
+    {
+        return $this->createMock(Exchange::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|Queue
+     */
+    private function createQueueMock()
+    {
+        return $this->createMock(Queue::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|Binding
+     */
+    private function createBindingMock()
+    {
+        return $this->createMock(Binding::class);
+    }
+}
diff --git a/Tests/Client/DriverFactoryTest.php b/Tests/Client/DriverFactoryTest.php
index 336158c..a2654e4 100644
--- a/Tests/Client/DriverFactoryTest.php
+++ b/Tests/Client/DriverFactoryTest.php
@@ -2,40 +2,39 @@
 
 namespace Enqueue\Tests\Client;
 
-use Enqueue\Client\Amqp\AmqpDriver;
-use Enqueue\Client\Amqp\RabbitMqDriver;
 use Enqueue\Client\Config;
+use Enqueue\Client\Driver\AmqpDriver;
+use Enqueue\Client\Driver\DbalDriver;
+use Enqueue\Client\Driver\FsDriver;
+use Enqueue\Client\Driver\GpsDriver;
+use Enqueue\Client\Driver\MongodbDriver;
+use Enqueue\Client\Driver\NullDriver;
+use Enqueue\Client\Driver\RabbitMqDriver;
+use Enqueue\Client\Driver\RabbitMqStompDriver;
+use Enqueue\Client\Driver\RdKafkaDriver;
+use Enqueue\Client\Driver\RedisDriver;
+use Enqueue\Client\Driver\SqsDriver;
+use Enqueue\Client\Driver\StompDriver;
 use Enqueue\Client\DriverFactory;
 use Enqueue\Client\DriverFactoryInterface;
 use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\Client\Resources;
-use Enqueue\Dbal\Client\DbalDriver;
 use Enqueue\Dbal\DbalConnectionFactory;
 use Enqueue\Dbal\DbalContext;
-use Enqueue\Fs\Client\FsDriver;
 use Enqueue\Fs\FsConnectionFactory;
 use Enqueue\Fs\FsContext;
-use Enqueue\Gearman\GearmanConnectionFactory;
-use Enqueue\Gps\Client\GpsDriver;
 use Enqueue\Gps\GpsConnectionFactory;
 use Enqueue\Gps\GpsContext;
-use Enqueue\Mongodb\Client\MongodbDriver;
 use Enqueue\Mongodb\MongodbConnectionFactory;
 use Enqueue\Mongodb\MongodbContext;
-use Enqueue\Null\Client\NullDriver;
 use Enqueue\Null\NullConnectionFactory;
 use Enqueue\Null\NullContext;
-use Enqueue\RdKafka\Client\RdKafkaDriver;
 use Enqueue\RdKafka\RdKafkaConnectionFactory;
 use Enqueue\RdKafka\RdKafkaContext;
-use Enqueue\Redis\Client\RedisDriver;
 use Enqueue\Redis\RedisConnectionFactory;
 use Enqueue\Redis\RedisContext;
-use Enqueue\Sqs\Client\SqsDriver;
 use Enqueue\Sqs\SqsConnectionFactory;
 use Enqueue\Sqs\SqsContext;
-use Enqueue\Stomp\Client\RabbitMqStompDriver;
-use Enqueue\Stomp\Client\StompDriver;
 use Enqueue\Stomp\StompConnectionFactory;
 use Enqueue\Stomp\StompContext;
 use Interop\Amqp\AmqpConnectionFactory;
@@ -69,10 +68,10 @@ public function testThrowIfPackageThatSupportSchemeNotInstalled()
         $scheme = 'scheme5b7aa7d7cd213';
         $class = 'ConnectionClass5b7aa7d7cd213';
 
-        Resources::addDriver($class, [$scheme], [], 'thePackage');
+        Resources::addDriver($class, [$scheme], [], ['thePackage', 'theOtherPackage']);
 
         $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('To use given scheme "scheme5b7aa7d7cd213" a package has to be installed. Run "composer req thePackage" to add it.');
+        $this->expectExceptionMessage('To use given scheme "scheme5b7aa7d7cd213" a package has to be installed. Run "composer req thePackage theOtherPackage" to add it.');
         $factory = new DriverFactory($this->createConfigMock(), $this->createQueueMetaRegistryMock());
 
         $factory->create($this->createConnectionFactoryMock(), $scheme.'://foo', []);
diff --git a/Tests/Client/ResourcesTest.php b/Tests/Client/ResourcesTest.php
index 55ff951..08feabf 100644
--- a/Tests/Client/ResourcesTest.php
+++ b/Tests/Client/ResourcesTest.php
@@ -2,10 +2,10 @@
 
 namespace Enqueue\Tests\Client;
 
-use Enqueue\Client\Amqp\RabbitMqDriver;
+use Enqueue\Client\Driver\RabbitMqDriver;
+use Enqueue\Client\Driver\RedisDriver;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Resources;
-use Enqueue\Redis\Client\RedisDriver;
 use PHPUnit\Framework\TestCase;
 
 class ResourcesTest extends TestCase
@@ -38,8 +38,8 @@ public function testShouldGetAvailableDriverInExpectedFormat()
         $this->assertArrayHasKey('requiredSchemeExtensions', $driverInfo);
         $this->assertSame([], $driverInfo['requiredSchemeExtensions']);
 
-        $this->assertArrayHasKey('package', $driverInfo);
-        $this->assertSame('enqueue/redis', $driverInfo['package']);
+        $this->assertArrayHasKey('packages', $driverInfo);
+        $this->assertSame(['enqueue/enqueue', 'enqueue/redis'], $driverInfo['packages']);
     }
 
     public function testShouldGetAvailableDriverWithRequiredExtensionInExpectedFormat()
@@ -51,13 +51,13 @@ public function testShouldGetAvailableDriverWithRequiredExtensionInExpectedForma
 
         $driverInfo = $availableDrivers[RabbitMqDriver::class];
         $this->assertArrayHasKey('schemes', $driverInfo);
-        $this->assertSame(['amqp'], $driverInfo['schemes']);
+        $this->assertSame(['amqp', 'amqps'], $driverInfo['schemes']);
 
         $this->assertArrayHasKey('requiredSchemeExtensions', $driverInfo);
         $this->assertSame(['rabbitmq'], $driverInfo['requiredSchemeExtensions']);
 
-        $this->assertArrayHasKey('package', $driverInfo);
-        $this->assertSame('enqueue/enqueue', $driverInfo['package']);
+        $this->assertArrayHasKey('packages', $driverInfo);
+        $this->assertSame(['enqueue/enqueue', 'enqueue/amqp-bunny'], $driverInfo['packages']);
     }
 
     public function testShouldGetKnownDriversInExpectedFormat()
@@ -80,7 +80,7 @@ public function testThrowsIfDriverClassNotImplementDriverFactoryInterfaceOnAddDr
         $this->expectException(\InvalidArgumentException::class);
         $this->expectExceptionMessage('The driver class "stdClass" must implement "Enqueue\Client\DriverInterface" interface.');
 
-        Resources::addDriver(\stdClass::class, [], [], 'foo');
+        Resources::addDriver(\stdClass::class, [], [], ['foo']);
     }
 
     public function testThrowsIfNoSchemesProvidedOnAddDriver()
@@ -90,7 +90,7 @@ public function testThrowsIfNoSchemesProvidedOnAddDriver()
         $this->expectException(\InvalidArgumentException::class);
         $this->expectExceptionMessage('Schemes could not be empty.');
 
-        Resources::addDriver($driverClass, [], [], 'foo');
+        Resources::addDriver($driverClass, [], [], ['foo']);
     }
 
     public function testThrowsIfNoPackageProvidedOnAddDriver()
@@ -98,14 +98,14 @@ public function testThrowsIfNoPackageProvidedOnAddDriver()
         $driverClass = $this->getMockClass(DriverInterface::class);
 
         $this->expectException(\InvalidArgumentException::class);
-        $this->expectExceptionMessage('Package name could not be empty.');
+        $this->expectExceptionMessage('Packages could not be empty.');
 
-        Resources::addDriver($driverClass, ['foo'], [], '');
+        Resources::addDriver($driverClass, ['foo'], [], []);
     }
 
     public function testShouldAllowRegisterDriverThatIsNotInstalled()
     {
-        Resources::addDriver('theDriverClass', ['foo'], ['barExtension'], 'foo');
+        Resources::addDriver('theDriverClass', ['foo'], ['barExtension'], ['foo']);
 
         $knownDrivers = Resources::getKnownDrivers();
         $this->assertInternalType('array', $knownDrivers);
@@ -125,7 +125,7 @@ public function testShouldAllowGetPreviouslyRegisteredDriver()
             $driverClass,
             ['fooscheme', 'barscheme'],
             ['fooextension', 'barextension'],
-            'foo/bar'
+            ['foo/bar']
         );
 
         $availableDrivers = Resources::getAvailableDrivers();
@@ -140,7 +140,7 @@ public function testShouldAllowGetPreviouslyRegisteredDriver()
         $this->assertArrayHasKey('requiredSchemeExtensions', $driverInfo);
         $this->assertSame(['fooextension', 'barextension'], $driverInfo['requiredSchemeExtensions']);
 
-        $this->assertArrayHasKey('package', $driverInfo);
-        $this->assertSame('foo/bar', $driverInfo['package']);
+        $this->assertArrayHasKey('packages', $driverInfo);
+        $this->assertSame(['foo/bar'], $driverInfo['packages']);
     }
 }
diff --git a/Tests/ConnectionFactoryFactoryTest.php b/Tests/ConnectionFactoryFactoryTest.php
index 06d6bde..644c891 100644
--- a/Tests/ConnectionFactoryFactoryTest.php
+++ b/Tests/ConnectionFactoryFactoryTest.php
@@ -42,6 +42,38 @@ public function testCouldBeConstructedWithoutAnyArguments()
         new ConnectionFactoryFactory();
     }
 
+    public function testShouldAcceptStringDSN()
+    {
+        $factory = new ConnectionFactoryFactory();
+
+        $factory->create('null:');
+    }
+
+    public function testShouldAcceptArrayWithDsnKey()
+    {
+        $factory = new ConnectionFactoryFactory();
+
+        $factory->create(['dsn' => 'null:']);
+    }
+
+    public function testThrowIfInvalidConfigGiven()
+    {
+        $factory = new ConnectionFactoryFactory();
+
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The config must be either array or DSN string.');
+        $factory->create(new \stdClass());
+    }
+
+    public function testThrowIfArrayConfigMissDsnKeyInvalidConfigGiven()
+    {
+        $factory = new ConnectionFactoryFactory();
+
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The config must be either array or DSN string.');
+        $factory->create(new \stdClass());
+    }
+
     public function testThrowIfPackageThatSupportSchemeNotInstalled()
     {
         $scheme = 'scheme5b7aa7d7cd213';
diff --git a/Tests/Symfony/AmqpTransportFactoryTest.php b/Tests/Symfony/AmqpTransportFactoryTest.php
deleted file mode 100644
index 7640189..0000000
--- a/Tests/Symfony/AmqpTransportFactoryTest.php
+++ /dev/null
@@ -1,404 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Symfony;
-
-use Enqueue\Client\Amqp\AmqpDriver;
-use Enqueue\Symfony\AmqpTransportFactory;
-use Enqueue\Symfony\TransportFactoryInterface;
-use Enqueue\Test\ClassExtensionTrait;
-use Interop\Amqp\AmqpConnectionFactory;
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\Config\Definition\Builder\TreeBuilder;
-use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
-use Symfony\Component\Config\Definition\Processor;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\Reference;
-
-class AmqpTransportFactoryTest extends TestCase
-{
-    use ClassExtensionTrait;
-
-    public function testShouldImplementTransportFactoryInterface()
-    {
-        $this->assertClassImplements(TransportFactoryInterface::class, AmqpTransportFactory::class);
-    }
-
-    public function testCouldBeConstructedWithDefaultName()
-    {
-        $transport = new AmqpTransportFactory();
-
-        $this->assertEquals('amqp', $transport->getName());
-    }
-
-    public function testCouldBeConstructedWithCustomName()
-    {
-        $transport = new AmqpTransportFactory('theCustomName');
-
-        $this->assertEquals('theCustomName', $transport->getName());
-    }
-
-    public function testThrowIfCouldBeConstructedWithCustomName()
-    {
-        $transport = new AmqpTransportFactory('theCustomName');
-
-        $this->assertEquals('theCustomName', $transport->getName());
-    }
-
-    public function testShouldAllowAddConfiguration()
-    {
-        $transport = new AmqpTransportFactory();
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
-
-        $transport->addConfiguration($rootNode);
-        $processor = new Processor();
-        $config = $processor->process($tb->buildTree(), [[
-            'host' => 'localhost',
-            'port' => 5672,
-            'user' => 'guest',
-            'pass' => 'guest',
-            'vhost' => '/',
-            'read_timeout' => 3.,
-            'write_timeout' => 3.,
-            'connection_timeout' => 3.,
-            'heartbeat' => 0,
-            'persisted' => false,
-            'lazy' => true,
-            'qos_global' => false,
-            'qos_prefetch_size' => 0,
-            'qos_prefetch_count' => 1,
-            'receive_method' => 'basic_get',
-        ]]);
-
-        $this->assertEquals([
-            'host' => 'localhost',
-            'port' => 5672,
-            'user' => 'guest',
-            'pass' => 'guest',
-            'vhost' => '/',
-            'read_timeout' => 3.,
-            'write_timeout' => 3.,
-            'connection_timeout' => 3.,
-            'heartbeat' => 0,
-            'persisted' => false,
-            'lazy' => true,
-            'qos_global' => false,
-            'qos_prefetch_size' => 0,
-            'qos_prefetch_count' => 1,
-            'receive_method' => 'basic_get',
-        ], $config);
-    }
-
-    public function testShouldAllowAddConfigurationWithDriverOptions()
-    {
-        $transport = new AmqpTransportFactory();
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
-
-        $transport->addConfiguration($rootNode);
-        $processor = new Processor();
-        $config = $processor->process($tb->buildTree(), [[
-            'host' => 'localhost',
-            'driver_options' => [
-                'foo' => 'fooVal',
-            ],
-        ]]);
-
-        $this->assertEquals([
-            'host' => 'localhost',
-            'driver_options' => [
-                'foo' => 'fooVal',
-            ],
-        ], $config);
-    }
-
-    public function testShouldAllowAddSslOptions()
-    {
-        $transport = new AmqpTransportFactory();
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
-
-        $transport->addConfiguration($rootNode);
-        $processor = new Processor();
-        $config = $processor->process($tb->buildTree(), [[
-            'ssl_on' => true,
-            'ssl_verify' => false,
-            'ssl_cacert' => '/path/to/cacert.pem',
-            'ssl_cert' => '/path/to/cert.pem',
-            'ssl_key' => '/path/to/key.pem',
-        ]]);
-
-        $this->assertEquals([
-            'ssl_on' => true,
-            'ssl_verify' => false,
-            'ssl_cacert' => '/path/to/cacert.pem',
-            'ssl_cert' => '/path/to/cert.pem',
-            'ssl_key' => '/path/to/key.pem',
-        ], $config);
-    }
-
-    public function testThrowIfNotSupportedDriverSet()
-    {
-        $transport = new AmqpTransportFactory();
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
-
-        $transport->addConfiguration($rootNode);
-        $processor = new Processor();
-
-        $this->expectException(InvalidConfigurationException::class);
-        $this->expectExceptionMessage('Invalid configuration for path "foo.driver": Unexpected driver given "invalidDriver"');
-        $processor->process($tb->buildTree(), [[
-            'driver' => 'invalidDriver',
-        ]]);
-    }
-
-    public function testShouldAllowSetDriver()
-    {
-        $transport = new AmqpTransportFactory();
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
-
-        $transport->addConfiguration($rootNode);
-        $processor = new Processor();
-        $config = $processor->process($tb->buildTree(), [[
-            'driver' => 'ext',
-        ]]);
-
-        $this->assertEquals([
-            'driver' => 'ext',
-        ], $config);
-    }
-
-    public function testShouldAllowAddConfigurationAsString()
-    {
-        $transport = new AmqpTransportFactory();
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
-
-        $transport->addConfiguration($rootNode);
-        $processor = new Processor();
-        $config = $processor->process($tb->buildTree(), ['amqpDSN']);
-
-        $this->assertEquals([
-            'dsn' => 'amqpDSN',
-        ], $config);
-    }
-
-    public function testThrowIfInvalidReceiveMethodIsSet()
-    {
-        $transport = new AmqpTransportFactory();
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
-
-        $transport->addConfiguration($rootNode);
-        $processor = new Processor();
-
-        $this->expectException(InvalidConfigurationException::class);
-        $this->expectExceptionMessage('The value "anInvalidMethod" is not allowed for path "foo.receive_method". Permissible values: "basic_get", "basic_consume"');
-        $processor->process($tb->buildTree(), [[
-            'receive_method' => 'anInvalidMethod',
-        ]]);
-    }
-
-    public function testShouldAllowChangeReceiveMethod()
-    {
-        $transport = new AmqpTransportFactory();
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
-
-        $transport->addConfiguration($rootNode);
-        $processor = new Processor();
-        $config = $processor->process($tb->buildTree(), [[
-            'receive_method' => 'basic_consume',
-        ]]);
-
-        $this->assertEquals([
-            'receive_method' => 'basic_consume',
-        ], $config);
-    }
-
-    public function testShouldCreateConnectionFactoryForEmptyConfig()
-    {
-        $container = new ContainerBuilder();
-
-        $transport = new AmqpTransportFactory();
-
-        $serviceId = $transport->createConnectionFactory($container, []);
-
-        $this->assertTrue($container->hasDefinition($serviceId));
-        $factory = $container->getDefinition($serviceId);
-        $this->assertEquals(AmqpConnectionFactory::class, $factory->getClass());
-
-        $this->assertSame([[]], $factory->getArguments());
-    }
-
-    public function testShouldCreateConnectionFactoryFromDsnString()
-    {
-        $container = new ContainerBuilder();
-
-        $transport = new AmqpTransportFactory();
-
-        $serviceId = $transport->createConnectionFactory($container, [
-            'dsn' => 'theConnectionDSN:',
-        ]);
-
-        $this->assertTrue($container->hasDefinition($serviceId));
-        $factory = $container->getDefinition($serviceId);
-        $this->assertEquals(AmqpConnectionFactory::class, $factory->getClass());
-        $this->assertSame([['dsn' => 'theConnectionDSN:']], $factory->getArguments());
-    }
-
-    public function testShouldCreateConnectionFactoryAndMergeDriverOptionsIfSet()
-    {
-        $container = new ContainerBuilder();
-
-        $transport = new AmqpTransportFactory();
-
-        $serviceId = $transport->createConnectionFactory($container, [
-            'host' => 'aHost',
-            'driver_options' => [
-                'foo' => 'fooVal',
-            ],
-        ]);
-
-        $this->assertTrue($container->hasDefinition($serviceId));
-        $factory = $container->getDefinition($serviceId);
-        $this->assertEquals(AmqpConnectionFactory::class, $factory->getClass());
-        $this->assertSame([['foo' => 'fooVal', 'host' => 'aHost']], $factory->getArguments());
-    }
-
-    public function testShouldCreateConnectionFactoryFromDsnStringPlushArrayOptions()
-    {
-        $container = new ContainerBuilder();
-
-        $transport = new AmqpTransportFactory();
-
-        $serviceId = $transport->createConnectionFactory($container, [
-            'host' => 'localhost',
-            'port' => 5672,
-            'user' => 'guest',
-            'pass' => 'guest',
-            'vhost' => '/',
-            'persisted' => false,
-        ]);
-
-        $this->assertTrue($container->hasDefinition($serviceId));
-        $factory = $container->getDefinition($serviceId);
-        $this->assertEquals(AmqpConnectionFactory::class, $factory->getClass());
-        $this->assertSame([[
-            'host' => 'localhost',
-            'port' => 5672,
-            'user' => 'guest',
-            'pass' => 'guest',
-            'vhost' => '/',
-            'persisted' => false,
-        ]], $factory->getArguments());
-    }
-
-    public function testShouldCreateContext()
-    {
-        $container = new ContainerBuilder();
-
-        $transport = new AmqpTransportFactory();
-
-        $serviceId = $transport->createContext($container, [
-            'host' => 'localhost',
-            'port' => 5672,
-            'user' => 'guest',
-            'pass' => 'guest',
-            'vhost' => '/',
-            'persisted' => false,
-        ]);
-
-        $this->assertEquals('enqueue.transport.amqp.context', $serviceId);
-        $this->assertTrue($container->hasDefinition($serviceId));
-
-        $context = $container->getDefinition('enqueue.transport.amqp.context');
-        $this->assertInstanceOf(Reference::class, $context->getFactory()[0]);
-        $this->assertEquals('enqueue.transport.amqp.connection_factory', (string) $context->getFactory()[0]);
-        $this->assertEquals('createContext', $context->getFactory()[1]);
-    }
-
-    public function testShouldCreateDriver()
-    {
-        $container = new ContainerBuilder();
-
-        $transport = new AmqpTransportFactory();
-
-        $serviceId = $transport->createDriver($container, []);
-
-        $this->assertEquals('enqueue.client.amqp.driver', $serviceId);
-        $this->assertTrue($container->hasDefinition($serviceId));
-
-        $driver = $container->getDefinition($serviceId);
-        $this->assertSame(AmqpDriver::class, $driver->getClass());
-
-        $this->assertInstanceOf(Reference::class, $driver->getArgument(0));
-        $this->assertEquals('enqueue.transport.amqp.context', (string) $driver->getArgument(0));
-
-        $this->assertInstanceOf(Reference::class, $driver->getArgument(1));
-        $this->assertEquals('enqueue.client.config', (string) $driver->getArgument(1));
-
-        $this->assertInstanceOf(Reference::class, $driver->getArgument(2));
-        $this->assertEquals('enqueue.client.meta.queue_meta_registry', (string) $driver->getArgument(2));
-    }
-
-    public function testShouldCreateAmqpExtConnectionFactoryBySetDriver()
-    {
-        $factory = AmqpTransportFactory::createConnectionFactoryFactory(['driver' => 'ext']);
-
-        $this->assertInstanceOf(\Enqueue\AmqpExt\AmqpConnectionFactory::class, $factory);
-    }
-
-    public function testShouldCreateAmqpLibConnectionFactoryBySetDriver()
-    {
-        $factory = AmqpTransportFactory::createConnectionFactoryFactory(['driver' => 'lib']);
-
-        $this->assertInstanceOf(\Enqueue\AmqpLib\AmqpConnectionFactory::class, $factory);
-    }
-
-    public function testShouldCreateAmqpBunnyConnectionFactoryBySetDriver()
-    {
-        $factory = AmqpTransportFactory::createConnectionFactoryFactory(['driver' => 'bunny']);
-
-        $this->assertInstanceOf(\Enqueue\AmqpBunny\AmqpConnectionFactory::class, $factory);
-    }
-
-    public function testShouldCreateAmqpExtFromConfigWithoutDriverAndDsn()
-    {
-        $factory = AmqpTransportFactory::createConnectionFactoryFactory(['host' => 'aHost']);
-
-        $this->assertInstanceOf(\Enqueue\AmqpBunny\AmqpConnectionFactory::class, $factory);
-    }
-
-    public function testThrowIfInvalidDriverGiven()
-    {
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Unexpected driver given "invalidDriver"');
-
-        AmqpTransportFactory::createConnectionFactoryFactory(['driver' => 'invalidDriver']);
-    }
-
-    public function testShouldCreateAmqpBunnyFromDsn()
-    {
-        $factory = AmqpTransportFactory::createConnectionFactoryFactory(['dsn' => 'amqp:']);
-
-        $this->assertInstanceOf(\Enqueue\AmqpBunny\AmqpConnectionFactory::class, $factory);
-    }
-
-    public function testShouldCreateAmqpBunnyFromDsnWithDriver()
-    {
-        $factory = AmqpTransportFactory::createConnectionFactoryFactory(['dsn' => 'amqp+bunny:']);
-
-        $this->assertInstanceOf(\Enqueue\AmqpBunny\AmqpConnectionFactory::class, $factory);
-    }
-
-    public function testThrowIfNotAmqpDsnProvided()
-    {
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Factory must be instance of "Interop\Amqp\AmqpConnectionFactory" but got "Enqueue\Sqs\SqsConnectionFactory"');
-
-        AmqpTransportFactory::createConnectionFactoryFactory(['dsn' => 'sqs:']);
-    }
-}
diff --git a/Tests/Symfony/Client/Mock/SetupBrokerExtensionCommand.php b/Tests/Symfony/Client/Mock/SetupBrokerExtensionCommand.php
index fe7a352..c4b20cf 100644
--- a/Tests/Symfony/Client/Mock/SetupBrokerExtensionCommand.php
+++ b/Tests/Symfony/Client/Mock/SetupBrokerExtensionCommand.php
@@ -3,8 +3,8 @@
 namespace Enqueue\Tests\Symfony\Client\Mock;
 
 use Enqueue\Client\Config;
+use Enqueue\Client\Driver\NullDriver;
 use Enqueue\Client\Meta\QueueMetaRegistry;
-use Enqueue\Null\Client\NullDriver;
 use Enqueue\Null\NullContext;
 use Enqueue\Symfony\Client\SetupBrokerExtensionCommandTrait;
 use Symfony\Component\Console\Command\Command;
diff --git a/Tests/Symfony/DefaultTransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
similarity index 56%
rename from Tests/Symfony/DefaultTransportFactoryTest.php
rename to Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index 5b480d8..c8893e6 100644
--- a/Tests/Symfony/DefaultTransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -1,68 +1,43 @@
 <?php
 
-namespace Enqueue\Tests\Symfony;
+namespace Enqueue\Tests\Symfony\DependencyInjection;
 
-use Enqueue\Symfony\DefaultTransportFactory;
-use Enqueue\Symfony\TransportFactoryInterface;
+use Enqueue\Symfony\DependencyInjection\TransportFactory;
 use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\Config\Definition\Builder\TreeBuilder;
+use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
 use Symfony\Component\Config\Definition\Processor;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Reference;
 
-class DefaultTransportFactoryTest extends TestCase
+class TransportFactoryTest extends TestCase
 {
     use ClassExtensionTrait;
 
-    public function testShouldImplementTransportFactoryInterface()
+    public function testShouldBeFinal()
     {
-        $this->assertClassImplements(TransportFactoryInterface::class, DefaultTransportFactory::class);
+        $this->assertClassFinal(TransportFactory::class);
     }
 
-    public function testCouldBeConstructedWithDefaultName()
+    public function testShouldAllowGetNameSetInConstructor()
     {
-        $transport = new DefaultTransportFactory();
+        $transport = new TransportFactory('aName');
 
-        $this->assertEquals('default', $transport->getName());
+        $this->assertEquals('aName', $transport->getName());
     }
 
-    public function testCouldBeConstructedWithCustomName()
+    public function testThrowIfEmptyNameGivenOnConstruction()
     {
-        $transport = new DefaultTransportFactory('theCustomName');
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The name could not be empty.');
 
-        $this->assertEquals('theCustomName', $transport->getName());
+        new TransportFactory('');
     }
 
-    public function testShouldAllowAddConfigurationAsAliasAsString()
+    public function testShouldAllowAddConfigurationAsStringDsn()
     {
-        $transport = new DefaultTransportFactory();
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
-
-        $transport->addConfiguration($rootNode);
-        $processor = new Processor();
-        $config = $processor->process($tb->buildTree(), ['the_alias']);
-
-        $this->assertEquals(['alias' => 'the_alias'], $config);
-    }
-
-    public function testShouldAllowAddConfigurationAsAliasAsOption()
-    {
-        $transport = new DefaultTransportFactory();
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
-
-        $transport->addConfiguration($rootNode);
-        $processor = new Processor();
-        $config = $processor->process($tb->buildTree(), [['alias' => 'the_alias']]);
-
-        $this->assertEquals(['alias' => 'the_alias'], $config);
-    }
-
-    public function testShouldAllowAddConfigurationAsDsn()
-    {
-        $transport = new DefaultTransportFactory();
+        $transport = new TransportFactory('default');
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
@@ -80,7 +55,7 @@ public function testShouldAllowAddConfigurationAsDsn()
      */
     public function testShouldAllowAddConfigurationAsDsnWithoutSlashes()
     {
-        $transport = new DefaultTransportFactory();
+        $transport = new TransportFactory('default');
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
@@ -91,9 +66,9 @@ public function testShouldAllowAddConfigurationAsDsnWithoutSlashes()
         $this->assertEquals(['dsn' => 'dsn:'], $config);
     }
 
-    public function testShouldSetNullTransportByDefault()
+    public function testShouldSetNullTransportIfNullGiven()
     {
-        $transport = new DefaultTransportFactory();
+        $transport = new TransportFactory('default');
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
@@ -102,91 +77,69 @@ public function testShouldSetNullTransportByDefault()
 
         $config = $processor->process($tb->buildTree(), [null]);
         $this->assertEquals(['dsn' => 'null:'], $config);
-
-        $config = $processor->process($tb->buildTree(), ['']);
-        $this->assertEquals(['dsn' => 'null:'], $config);
     }
 
-    public function testThrowIfNeitherDsnNorAliasConfigured()
+    public function testShouldSetNullTransportIfEmptyStringGiven()
     {
-        $transport = new DefaultTransportFactory();
+        $transport = new TransportFactory('default');
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
         $transport->addConfiguration($rootNode);
         $processor = new Processor();
 
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Either dsn or alias option must be set');
-        $processor->process($tb->buildTree(), [[]]);
+        $config = $processor->process($tb->buildTree(), ['']);
+        $this->assertEquals(['dsn' => 'null:'], $config);
     }
 
-    public function testShouldCreateConnectionFactoryFromAlias()
+    public function testShouldSetNullTransportIfEmptyArrayGiven()
     {
-        $container = new ContainerBuilder();
-
-        $transport = new DefaultTransportFactory();
-
-        $serviceId = $transport->createConnectionFactory($container, ['alias' => 'foo']);
-
-        $this->assertEquals('enqueue.transport.default.connection_factory', $serviceId);
+        $transport = new TransportFactory('default');
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
 
-        $this->assertTrue($container->hasAlias('enqueue.transport.default.connection_factory'));
-        $this->assertEquals(
-            'enqueue.transport.foo.connection_factory',
-            (string) $container->getAlias('enqueue.transport.default.connection_factory')
-        );
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
 
-        $this->assertTrue($container->hasAlias('enqueue.transport.connection_factory'));
-        $this->assertEquals(
-            'enqueue.transport.default.connection_factory',
-            (string) $container->getAlias('enqueue.transport.connection_factory')
-        );
+        $config = $processor->process($tb->buildTree(), [[]]);
+        $this->assertEquals(['dsn' => 'null:'], $config);
     }
 
-    public function testShouldCreateContextFromAlias()
+    public function testThrowIfEmptyDsnGiven()
     {
-        $container = new ContainerBuilder();
-
-        $transport = new DefaultTransportFactory();
-
-        $serviceId = $transport->createContext($container, ['alias' => 'the_alias']);
-
-        $this->assertEquals('enqueue.transport.default.context', $serviceId);
+        $transport = new TransportFactory('default');
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
 
-        $this->assertTrue($container->hasAlias($serviceId));
-        $context = $container->getAlias($serviceId);
-        $this->assertEquals('enqueue.transport.the_alias.context', (string) $context);
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
 
-        $this->assertTrue($container->hasAlias('enqueue.transport.context'));
-        $context = $container->getAlias('enqueue.transport.context');
-        $this->assertEquals($serviceId, (string) $context);
+        $this->expectException(InvalidConfigurationException::class);
+        $this->expectExceptionMessage('The path "foo.dsn" cannot contain an empty value, but got "".');
+        $processor->process($tb->buildTree(), [['dsn' => '']]);
     }
 
-    public function testShouldCreateDriverFromAlias()
+    public function testThrowIfExtraOptionGiven()
     {
-        $container = new ContainerBuilder();
-
-        $transport = new DefaultTransportFactory();
-
-        $driverId = $transport->createDriver($container, ['alias' => 'the_alias']);
-
-        $this->assertEquals('enqueue.client.default.driver', $driverId);
+        $transport = new TransportFactory('default');
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
 
-        $this->assertTrue($container->hasAlias($driverId));
-        $context = $container->getAlias($driverId);
-        $this->assertEquals('enqueue.client.the_alias.driver', (string) $context);
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
 
-        $this->assertTrue($container->hasAlias('enqueue.client.driver'));
-        $context = $container->getAlias('enqueue.client.driver');
-        $this->assertEquals($driverId, (string) $context);
+        $config = $processor->process($tb->buildTree(), [['dsn' => 'foo:', 'extraOption' => 'aVal']]);
+        $this->assertEquals(
+            ['dsn' => 'foo:', 'extraOption' => 'aVal'],
+            $config
+        );
     }
 
     public function testShouldCreateConnectionFactoryFromDSN()
     {
         $container = new ContainerBuilder();
 
-        $transport = new DefaultTransportFactory();
+        $transport = new TransportFactory('default');
 
         $serviceId = $transport->createConnectionFactory($container, ['dsn' => 'foo://bar/baz']);
 
@@ -200,7 +153,7 @@ public function testShouldCreateConnectionFactoryFromDSN()
             $container->getDefinition('enqueue.transport.default.connection_factory')->getFactory())
         ;
         $this->assertSame(
-            ['foo://bar/baz'],
+            [['dsn' => 'foo://bar/baz']],
             $container->getDefinition('enqueue.transport.default.connection_factory')->getArguments())
         ;
 
@@ -215,7 +168,7 @@ public function testShouldCreateContextFromDsn()
     {
         $container = new ContainerBuilder();
 
-        $transport = new DefaultTransportFactory();
+        $transport = new TransportFactory('default');
 
         $serviceId = $transport->createContext($container, ['dsn' => 'foo://bar/baz']);
 
@@ -242,7 +195,7 @@ public function testShouldCreateDriverFromDsn()
     {
         $container = new ContainerBuilder();
 
-        $transport = new DefaultTransportFactory();
+        $transport = new TransportFactory('default');
 
         $serviceId = $transport->createDriver($container, ['dsn' => 'foo://bar/baz', 'foo' => 'fooVal']);
 
diff --git a/Tests/Symfony/MissingTransportFactoryTest.php b/Tests/Symfony/MissingTransportFactoryTest.php
deleted file mode 100644
index b466a5b..0000000
--- a/Tests/Symfony/MissingTransportFactoryTest.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Symfony;
-
-use Enqueue\Symfony\MissingTransportFactory;
-use Enqueue\Symfony\TransportFactoryInterface;
-use Enqueue\Test\ClassExtensionTrait;
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\Config\Definition\Builder\TreeBuilder;
-use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
-use Symfony\Component\Config\Definition\Processor;
-
-class MissingTransportFactoryTest extends TestCase
-{
-    use ClassExtensionTrait;
-
-    public function testShouldImplementTransportFactoryInterface()
-    {
-        $this->assertClassImplements(TransportFactoryInterface::class, MissingTransportFactory::class);
-    }
-
-    public function testCouldBeConstructedWithNameAndPackages()
-    {
-        $transport = new MissingTransportFactory('aMissingTransportName', ['aPackage', 'anotherPackage']);
-
-        $this->assertEquals('aMissingTransportName', $transport->getName());
-    }
-
-    public function testThrowOnProcessForOnePackageToInstall()
-    {
-        $transport = new MissingTransportFactory('aMissingTransportName', ['aFooPackage']);
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
-
-        $transport->addConfiguration($rootNode);
-        $processor = new Processor();
-
-        $this->expectException(InvalidConfigurationException::class);
-        $this->expectExceptionMessage('Invalid configuration for path "foo": In order to use the transport "aMissingTransportName" install a package "aFooPackage"');
-        $processor->process($tb->buildTree(), [[]]);
-    }
-
-    public function testThrowOnProcessForSeveralPackagesToInstall()
-    {
-        $transport = new MissingTransportFactory('aMissingTransportName', ['aFooPackage', 'aBarPackage']);
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
-
-        $transport->addConfiguration($rootNode);
-        $processor = new Processor();
-
-        $this->expectException(InvalidConfigurationException::class);
-        $this->expectExceptionMessage('Invalid configuration for path "foo": In order to use the transport "aMissingTransportName" install one of the packages "aFooPackage", "aBarPackage"');
-        $processor->process($tb->buildTree(), [[]]);
-    }
-
-    public function testThrowEvenIfThereAreSomeOptionsPassed()
-    {
-        $transport = new MissingTransportFactory('aMissingTransportName', ['aFooPackage', 'aBarPackage']);
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
-
-        $transport->addConfiguration($rootNode);
-        $processor = new Processor();
-
-        $this->expectException(InvalidConfigurationException::class);
-        $this->expectExceptionMessage('In order to use the transport "aMissingTransportName"');
-        $processor->process($tb->buildTree(), [[
-            'foo' => 'fooVal',
-            'bar' => 'barVal',
-        ]]);
-    }
-}
diff --git a/Tests/Symfony/RabbitMqAmqpTransportFactoryTest.php b/Tests/Symfony/RabbitMqAmqpTransportFactoryTest.php
deleted file mode 100644
index cfe77e6..0000000
--- a/Tests/Symfony/RabbitMqAmqpTransportFactoryTest.php
+++ /dev/null
@@ -1,129 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Symfony;
-
-use Enqueue\Client\Amqp\RabbitMqDriver;
-use Enqueue\Symfony\AmqpTransportFactory;
-use Enqueue\Symfony\RabbitMqAmqpTransportFactory;
-use Enqueue\Symfony\TransportFactoryInterface;
-use Enqueue\Test\ClassExtensionTrait;
-use Interop\Amqp\AmqpConnectionFactory;
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\Config\Definition\Builder\TreeBuilder;
-use Symfony\Component\Config\Definition\Processor;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\Reference;
-
-class RabbitMqAmqpTransportFactoryTest extends TestCase
-{
-    use ClassExtensionTrait;
-
-    public function testShouldImplementTransportFactoryInterface()
-    {
-        $this->assertClassImplements(TransportFactoryInterface::class, RabbitMqAmqpTransportFactory::class);
-    }
-
-    public function testShouldExtendAmqpTransportFactoryClass()
-    {
-        $this->assertClassExtends(AmqpTransportFactory::class, RabbitMqAmqpTransportFactory::class);
-    }
-
-    public function testCouldBeConstructedWithDefaultName()
-    {
-        $transport = new RabbitMqAmqpTransportFactory();
-
-        $this->assertEquals('rabbitmq_amqp', $transport->getName());
-    }
-
-    public function testCouldBeConstructedWithCustomName()
-    {
-        $transport = new RabbitMqAmqpTransportFactory('theCustomName');
-
-        $this->assertEquals('theCustomName', $transport->getName());
-    }
-
-    public function testShouldAllowAddConfiguration()
-    {
-        $transport = new RabbitMqAmqpTransportFactory();
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
-
-        $transport->addConfiguration($rootNode);
-        $processor = new Processor();
-        $config = $processor->process($tb->buildTree(), []);
-
-        $this->assertEquals([
-            'delay_strategy' => 'dlx',
-        ], $config);
-    }
-
-    public function testShouldCreateConnectionFactory()
-    {
-        $container = new ContainerBuilder();
-
-        $transport = new RabbitMqAmqpTransportFactory();
-
-        $serviceId = $transport->createConnectionFactory($container, [
-            'host' => 'localhost',
-            'port' => 5672,
-            'user' => 'guest',
-            'pass' => 'guest',
-            'vhost' => '/',
-            'persisted' => false,
-            'delay_strategy' => null,
-        ]);
-
-        $this->assertTrue($container->hasDefinition($serviceId));
-        $factory = $container->getDefinition($serviceId);
-        $this->assertEquals(AmqpConnectionFactory::class, $factory->getClass());
-        $this->assertSame([[
-            'host' => 'localhost',
-            'port' => 5672,
-            'user' => 'guest',
-            'pass' => 'guest',
-            'vhost' => '/',
-            'persisted' => false,
-            'delay_strategy' => null,
-        ]], $factory->getArguments());
-    }
-
-    public function testShouldCreateContext()
-    {
-        $container = new ContainerBuilder();
-
-        $transport = new RabbitMqAmqpTransportFactory();
-
-        $serviceId = $transport->createContext($container, [
-            'host' => 'localhost',
-            'port' => 5672,
-            'user' => 'guest',
-            'pass' => 'guest',
-            'vhost' => '/',
-            'persisted' => false,
-            'delay_strategy' => null,
-        ]);
-
-        $this->assertEquals('enqueue.transport.rabbitmq_amqp.context', $serviceId);
-        $this->assertTrue($container->hasDefinition($serviceId));
-
-        $context = $container->getDefinition('enqueue.transport.rabbitmq_amqp.context');
-        $this->assertInstanceOf(Reference::class, $context->getFactory()[0]);
-        $this->assertEquals('enqueue.transport.rabbitmq_amqp.connection_factory', (string) $context->getFactory()[0]);
-        $this->assertEquals('createContext', $context->getFactory()[1]);
-    }
-
-    public function testShouldCreateDriver()
-    {
-        $container = new ContainerBuilder();
-
-        $transport = new RabbitMqAmqpTransportFactory();
-
-        $serviceId = $transport->createDriver($container, []);
-
-        $this->assertEquals('enqueue.client.rabbitmq_amqp.driver', $serviceId);
-        $this->assertTrue($container->hasDefinition($serviceId));
-
-        $driver = $container->getDefinition($serviceId);
-        $this->assertSame(RabbitMqDriver::class, $driver->getClass());
-    }
-}

From 6843d044c3eaa95b5186945447c8b7bf04588fad Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 7 Sep 2018 19:03:47 +0300
Subject: [PATCH 098/337] fixes

---
 Client/Driver/RabbitMqStompDriver.php | 6 ------
 Client/DriverFactory.php              | 4 ++++
 Client/Resources.php                  | 2 +-
 3 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/Client/Driver/RabbitMqStompDriver.php b/Client/Driver/RabbitMqStompDriver.php
index df005f6..5d4ae1b 100644
--- a/Client/Driver/RabbitMqStompDriver.php
+++ b/Client/Driver/RabbitMqStompDriver.php
@@ -40,12 +40,6 @@ class RabbitMqStompDriver extends StompDriver
      */
     private $queueMetaRegistry;
 
-    /**
-     * @param StompContext          $context
-     * @param Config                $config
-     * @param QueueMetaRegistry     $queueMetaRegistry
-     * @param StompManagementClient $management
-     */
     public function __construct(StompContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry, StompManagementClient $management)
     {
         parent::__construct($context, $config, $queueMetaRegistry);
diff --git a/Client/DriverFactory.php b/Client/DriverFactory.php
index 65841e6..d60b56c 100644
--- a/Client/DriverFactory.php
+++ b/Client/DriverFactory.php
@@ -55,6 +55,10 @@ public function create(PsrConnectionFactory $factory, string $dsn, array $config
                     ));
                 }
 
+                if (false == isset($config['management_plugin_installed'])) {
+                    throw new \LogicException(sprintf('Scheme %s requires the management plugin is to be installed', $dsn->getScheme()));
+                }
+
                 if (isset($config['rabbitmq_management_dsn'])) {
                     $managementDsn = new Dsn($config['rabbitmq_management_dsn']);
 
diff --git a/Client/Resources.php b/Client/Resources.php
index 87da9dd..46896df 100644
--- a/Client/Resources.php
+++ b/Client/Resources.php
@@ -121,7 +121,7 @@ public static function getKnownDrivers(): array
                     'sqlite3',
                     'sqlite',
                 ],
-                'requiredSchemeExtensions' => [],
+                'requiredSchemeExtensions' => ['pdo'],
                 'package' => ['enqueue/enqueue', 'enqueue/dbal'],
             ];
 

From 0737e41857bf6a1d15f4eae2b4f4745b86675aa1 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Sat, 8 Sep 2018 13:34:46 +0300
Subject: [PATCH 099/337] [client] fixes

---
 Client/DriverFactory.php     | 6 +++---
 Client/Resources.php         | 2 +-
 ConnectionFactoryFactory.php | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/Client/DriverFactory.php b/Client/DriverFactory.php
index d60b56c..bcde086 100644
--- a/Client/DriverFactory.php
+++ b/Client/DriverFactory.php
@@ -109,12 +109,12 @@ private function findDriverClass(Dsn $dsn, array $factories): ?string
                 continue;
             }
 
-            if (false == $dsn->getSchemeExtensions()) {
+            if (empty($info['requiredSchemeExtensions'])) {
                 return $driverClass;
             }
 
-            if (empty($info['requiredSchemeExtensions'])) {
-                continue;
+            if (false == $dsn->getSchemeExtensions()) {
+                return null;
             }
 
             $diff = array_diff($dsn->getSchemeExtensions(), $info['requiredSchemeExtensions']);
diff --git a/Client/Resources.php b/Client/Resources.php
index 46896df..87da9dd 100644
--- a/Client/Resources.php
+++ b/Client/Resources.php
@@ -121,7 +121,7 @@ public static function getKnownDrivers(): array
                     'sqlite3',
                     'sqlite',
                 ],
-                'requiredSchemeExtensions' => ['pdo'],
+                'requiredSchemeExtensions' => [],
                 'package' => ['enqueue/enqueue', 'enqueue/dbal'],
             ];
 
diff --git a/ConnectionFactoryFactory.php b/ConnectionFactoryFactory.php
index dce7ec3..5ab4256 100644
--- a/ConnectionFactoryFactory.php
+++ b/ConnectionFactoryFactory.php
@@ -24,7 +24,7 @@ public function create($config): PsrConnectionFactory
         $dsn = new Dsn($config['dsn']);
 
         if ($factoryClass = $this->findFactoryClass($dsn, Resources::getAvailableConnections())) {
-            return new $factoryClass($config);
+            return new $factoryClass(1 === count($config) ? $config['dsn'] : $config);
         }
 
         $knownConnections = Resources::getKnownConnections();

From 7d62f2aef42268f80c89c23ccd3745a92d35d3a4 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 10 Sep 2018 14:56:39 +0300
Subject: [PATCH 100/337] [symfony] Add ability to customize connection factory
 factory.

---
 .../DependencyInjection/TransportFactory.php  |  21 +++-
 .../TransportFactoryTest.php                  | 118 ++++++++++++++++++
 2 files changed, 138 insertions(+), 1 deletion(-)

diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index d4dce46..85444f9 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -39,6 +39,10 @@ public function addConfiguration(ArrayNodeDefinition $builder): void
                     }
 
                     if (is_array($v)) {
+                        if (isset($v['factory_class']) && isset($v['factory_service'])) {
+                            throw new \LogicException('Both options factory_class and factory_service are set. Please choose one.');
+                        }
+
                         return $v;
                     }
 
@@ -52,16 +56,31 @@ public function addConfiguration(ArrayNodeDefinition $builder): void
         ->ignoreExtraKeys(false)
         ->children()
             ->scalarNode('dsn')->cannotBeEmpty()->isRequired()->end()
+            ->scalarNode('factory_service')->end()
+            ->scalarNode('factory_class')->end()
         ->end()
         ;
     }
 
     public function createConnectionFactory(ContainerBuilder $container, array $config): string
     {
+        $factoryFactoryId = 'enqueue.connection_factory_factory';
         $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
 
+        if (array_key_exists('factory_class', $config)) {
+            $factoryFactoryId = sprintf('enqueue.transport.%s.connection_factory_factory', $this->getName());
+
+            $container->register($factoryFactoryId, $config['factory_class']);
+        }
+
+        $factoryFactoryService = new Reference(
+            array_key_exists('factory_service', $config) ? $config['factory_service'] : $factoryFactoryId
+        );
+
+        unset($config['factory_service'], $config['factory_class']);
+
         $container->register($factoryId, PsrConnectionFactory::class)
-            ->setFactory([new Reference('enqueue.connection_factory_factory'), 'create'])
+            ->setFactory([$factoryFactoryService, 'create'])
             ->addArgument($config)
         ;
 
diff --git a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index c8893e6..e4e5a40 100644
--- a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -119,6 +119,60 @@ public function testThrowIfEmptyDsnGiven()
         $processor->process($tb->buildTree(), [['dsn' => '']]);
     }
 
+    public function testThrowIfFactoryClassAndFactoryServiceSetAtTheSameTime()
+    {
+        $transport = new TransportFactory('default');
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Both options factory_class and factory_service are set. Please choose one.');
+        $processor->process($tb->buildTree(), [[
+            'dsn' => 'foo:',
+            'factory_class' => 'aFactoryClass',
+            'factory_service' => 'aFactoryService',
+        ]]);
+    }
+
+    public function testShouldAllowSetFactoryClass()
+    {
+        $transport = new TransportFactory('default');
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+
+        $config = $processor->process($tb->buildTree(), [[
+            'dsn' => 'foo:',
+            'factory_class' => 'theFactoryClass',
+        ]]);
+
+        $this->assertArrayHasKey('factory_class', $config);
+        $this->assertSame('theFactoryClass', $config['factory_class']);
+    }
+
+    public function testShouldAllowSetFactoryService()
+    {
+        $transport = new TransportFactory('default');
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+
+        $config = $processor->process($tb->buildTree(), [[
+            'dsn' => 'foo:',
+            'factory_service' => 'theFactoryService',
+        ]]);
+
+        $this->assertArrayHasKey('factory_service', $config);
+        $this->assertSame('theFactoryService', $config['factory_service']);
+    }
+
     public function testThrowIfExtraOptionGiven()
     {
         $transport = new TransportFactory('default');
@@ -164,6 +218,70 @@ public function testShouldCreateConnectionFactoryFromDSN()
         );
     }
 
+    public function testShouldCreateConnectionFactoryUsingCustomFactortyClass()
+    {
+        $container = new ContainerBuilder();
+
+        $transport = new TransportFactory('default');
+
+        $serviceId = $transport->createConnectionFactory($container, ['dsn' => 'foo:', 'factory_class' => 'theFactoryClass']);
+
+        $this->assertEquals('enqueue.transport.default.connection_factory', $serviceId);
+
+        $this->assertTrue($container->hasDefinition('enqueue.transport.default.connection_factory_factory'));
+        $this->assertSame(
+            'theFactoryClass',
+            $container->getDefinition('enqueue.transport.default.connection_factory_factory')->getClass()
+        );
+
+        $this->assertTrue($container->hasDefinition('enqueue.transport.default.connection_factory'));
+
+        $this->assertNotEmpty($container->getDefinition('enqueue.transport.default.connection_factory')->getFactory());
+        $this->assertEquals(
+            [new Reference('enqueue.transport.default.connection_factory_factory'), 'create'],
+            $container->getDefinition('enqueue.transport.default.connection_factory')->getFactory())
+        ;
+        $this->assertSame(
+            [['dsn' => 'foo:']],
+            $container->getDefinition('enqueue.transport.default.connection_factory')->getArguments())
+        ;
+
+        $this->assertTrue($container->hasAlias('enqueue.transport.connection_factory'));
+        $this->assertEquals(
+            'enqueue.transport.default.connection_factory',
+            (string) $container->getAlias('enqueue.transport.connection_factory')
+        );
+    }
+
+    public function testShouldCreateConnectionFactoryUsingCustomFactortyService()
+    {
+        $container = new ContainerBuilder();
+
+        $transport = new TransportFactory('default');
+
+        $serviceId = $transport->createConnectionFactory($container, ['dsn' => 'foo:', 'factory_service' => 'theFactoryService']);
+
+        $this->assertEquals('enqueue.transport.default.connection_factory', $serviceId);
+
+        $this->assertTrue($container->hasDefinition('enqueue.transport.default.connection_factory'));
+
+        $this->assertNotEmpty($container->getDefinition('enqueue.transport.default.connection_factory')->getFactory());
+        $this->assertEquals(
+            [new Reference('theFactoryService'), 'create'],
+            $container->getDefinition('enqueue.transport.default.connection_factory')->getFactory())
+        ;
+        $this->assertSame(
+            [['dsn' => 'foo:']],
+            $container->getDefinition('enqueue.transport.default.connection_factory')->getArguments())
+        ;
+
+        $this->assertTrue($container->hasAlias('enqueue.transport.connection_factory'));
+        $this->assertEquals(
+            'enqueue.transport.default.connection_factory',
+            (string) $container->getAlias('enqueue.transport.connection_factory')
+        );
+    }
+
     public function testShouldCreateContextFromDsn()
     {
         $container = new ContainerBuilder();

From 1ade11754d170bef5ac5890845131fe9092b365d Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 10 Sep 2018 20:15:05 +0300
Subject: [PATCH 101/337] fix tests.

---
 Client/DriverFactory.php           | 63 ++++++++++++++----------------
 Tests/Client/DriverFactoryTest.php | 10 ++---
 2 files changed, 32 insertions(+), 41 deletions(-)

diff --git a/Client/DriverFactory.php b/Client/DriverFactory.php
index bcde086..1e49294 100644
--- a/Client/DriverFactory.php
+++ b/Client/DriverFactory.php
@@ -55,29 +55,13 @@ public function create(PsrConnectionFactory $factory, string $dsn, array $config
                     ));
                 }
 
-                if (false == isset($config['management_plugin_installed'])) {
-                    throw new \LogicException(sprintf('Scheme %s requires the management plugin is to be installed', $dsn->getScheme()));
-                }
-
-                if (isset($config['rabbitmq_management_dsn'])) {
-                    $managementDsn = new Dsn($config['rabbitmq_management_dsn']);
-
-                    $managementClient = StompManagementClient::create(
-                        ltrim($managementDsn->getPath(), '/'),
-                        $managementDsn->getHost(),
-                        $managementDsn->getPort(),
-                        $managementDsn->getUser(),
-                        $managementDsn->getPassword()
-                    );
-                } else {
-                    $managementClient = StompManagementClient::create(
-                        ltrim($dsn->getPath(), '/'),
-                        $dsn->getHost(),
-                        isset($config['management_plugin_port']) ? $config['management_plugin_port'] : 15672,
-                        $dsn->getUser(),
-                        $dsn->getPassword()
-                    );
-                }
+                $managementClient = StompManagementClient::create(
+                    ltrim($dsn->getPath(), '/'),
+                    $dsn->getHost(),
+                    isset($config['management_plugin_port']) ? $config['management_plugin_port'] : 15672,
+                    $dsn->getUser(),
+                    $dsn->getPassword()
+                );
 
                 return new RabbitMqStompDriver($factory->createContext(), $this->config, $this->queueMetaRegistry, $managementClient);
             }
@@ -104,23 +88,34 @@ public function create(PsrConnectionFactory $factory, string $dsn, array $config
     private function findDriverClass(Dsn $dsn, array $factories): ?string
     {
         $protocol = $dsn->getSchemeProtocol();
-        foreach ($factories as $driverClass => $info) {
-            if (false == in_array($protocol, $info['schemes'], true)) {
-                continue;
-            }
 
-            if (empty($info['requiredSchemeExtensions'])) {
-                return $driverClass;
+        if ($dsn->getSchemeExtensions()) {
+            foreach ($factories as $driverClass => $info) {
+                if (empty($info['requiredSchemeExtensions'])) {
+                    continue;
+                }
+
+                if (false == in_array($protocol, $info['schemes'], true)) {
+                    continue;
+                }
+
+                $diff = array_diff($dsn->getSchemeExtensions(), $info['requiredSchemeExtensions']);
+                if (empty($diff)) {
+                    return $driverClass;
+                }
             }
+        }
 
-            if (false == $dsn->getSchemeExtensions()) {
-                return null;
+        foreach ($factories as $driverClass => $info) {
+            if (false == empty($info['requiredSchemeExtensions'])) {
+                continue;
             }
 
-            $diff = array_diff($dsn->getSchemeExtensions(), $info['requiredSchemeExtensions']);
-            if (empty($diff)) {
-                return $driverClass;
+            if (false == in_array($protocol, $info['schemes'], true)) {
+                continue;
             }
+
+            return $driverClass;
         }
 
         return null;
diff --git a/Tests/Client/DriverFactoryTest.php b/Tests/Client/DriverFactoryTest.php
index a2654e4..6d3e6ce 100644
--- a/Tests/Client/DriverFactoryTest.php
+++ b/Tests/Client/DriverFactoryTest.php
@@ -146,19 +146,15 @@ public static function provideDSN()
 
         yield ['redis:', RedisConnectionFactory::class, RedisContext::class, [], RedisDriver::class];
 
+        yield ['redis+predis:', RedisConnectionFactory::class, RedisContext::class, [], RedisDriver::class];
+
         yield ['sqs:', SqsConnectionFactory::class, SqsContext::class, [], SqsDriver::class];
 
         yield ['stomp:', StompConnectionFactory::class, StompContext::class, [], StompDriver::class];
 
         yield ['stomp+rabbitmq:', StompConnectionFactory::class, StompContext::class, [], RabbitMqStompDriver::class];
 
-        yield ['stomp+rabbitmq:', StompConnectionFactory::class, StompContext::class, [
-            'rabbitmq_management_dsn' => 'http://guest:guest@localhost:15672/mqdev',
-        ], RabbitMqStompDriver::class];
-
-        yield ['stomp+rabbitmq:', StompConnectionFactory::class, StompContext::class, [
-            'management_plugin_port' => 1234,
-        ], RabbitMqStompDriver::class];
+        yield ['stomp+foo+bar:', StompConnectionFactory::class, StompContext::class, [], StompDriver::class];
     }
 
     private function createConnectionFactoryMock(): PsrConnectionFactory

From 6edafe5ff09651edabedf0c73386e743d20b9c6e Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 11 Sep 2018 10:44:35 +0300
Subject: [PATCH 102/337] [symfony] add ability to define connection factory
 class.

---
 .../DependencyInjection/TransportFactory.php  | 22 ++++-
 .../TransportFactoryTest.php                  | 84 ++++++++++++++++++-
 2 files changed, 100 insertions(+), 6 deletions(-)

diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index 85444f9..694aefa 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -43,6 +43,10 @@ public function addConfiguration(ArrayNodeDefinition $builder): void
                             throw new \LogicException('Both options factory_class and factory_service are set. Please choose one.');
                         }
 
+                        if (isset($v['connection_factory_class']) && (isset($v['factory_class']) || isset($v['factory_service']))) {
+                            throw new \LogicException('The option connection_factory_class must not be used with factory_class or factory_service at the same time. Please choose one.');
+                        }
+
                         return $v;
                     }
 
@@ -56,6 +60,7 @@ public function addConfiguration(ArrayNodeDefinition $builder): void
         ->ignoreExtraKeys(false)
         ->children()
             ->scalarNode('dsn')->cannotBeEmpty()->isRequired()->end()
+            ->scalarNode('connection_factory_class')->end()
             ->scalarNode('factory_service')->end()
             ->scalarNode('factory_class')->end()
         ->end()
@@ -79,10 +84,19 @@ public function createConnectionFactory(ContainerBuilder $container, array $conf
 
         unset($config['factory_service'], $config['factory_class']);
 
-        $container->register($factoryId, PsrConnectionFactory::class)
-            ->setFactory([$factoryFactoryService, 'create'])
-            ->addArgument($config)
-        ;
+        if (array_key_exists('connection_factory_class', $config)) {
+            $connectionFactoryClass = $config['connection_factory_class'];
+            unset($config['connection_factory_class']);
+
+            $container->register($factoryId, $connectionFactoryClass)
+                ->addArgument($config)
+            ;
+        } else {
+            $container->register($factoryId, PsrConnectionFactory::class)
+                ->setFactory([$factoryFactoryService, 'create'])
+                ->addArgument($config)
+            ;
+        }
 
         $container->setAlias('enqueue.transport.connection_factory', new Alias($factoryId, true));
 
diff --git a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index e4e5a40..f537751 100644
--- a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -137,6 +137,42 @@ public function testThrowIfFactoryClassAndFactoryServiceSetAtTheSameTime()
         ]]);
     }
 
+    public function testThrowIfConnectionFactoryClassUsedWithFactoryClassAtTheSameTime()
+    {
+        $transport = new TransportFactory('default');
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The option connection_factory_class must not be used with factory_class or factory_service at the same time. Please choose one.');
+        $processor->process($tb->buildTree(), [[
+            'dsn' => 'foo:',
+            'connection_factory_class' => 'aFactoryClass',
+            'factory_service' => 'aFactoryService',
+        ]]);
+    }
+
+    public function testThrowIfConnectionFactoryClassUsedWithFactoryServiceAtTheSameTime()
+    {
+        $transport = new TransportFactory('default');
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The option connection_factory_class must not be used with factory_class or factory_service at the same time. Please choose one.');
+        $processor->process($tb->buildTree(), [[
+            'dsn' => 'foo:',
+            'connection_factory_class' => 'aFactoryClass',
+            'factory_service' => 'aFactoryService',
+        ]]);
+    }
+
     public function testShouldAllowSetFactoryClass()
     {
         $transport = new TransportFactory('default');
@@ -173,6 +209,24 @@ public function testShouldAllowSetFactoryService()
         $this->assertSame('theFactoryService', $config['factory_service']);
     }
 
+    public function testShouldAllowSetConnectionFactoryClass()
+    {
+        $transport = new TransportFactory('default');
+        $tb = new TreeBuilder();
+        $rootNode = $tb->root('foo');
+
+        $transport->addConfiguration($rootNode);
+        $processor = new Processor();
+
+        $config = $processor->process($tb->buildTree(), [[
+            'dsn' => 'foo:',
+            'connection_factory_class' => 'theFactoryClass',
+        ]]);
+
+        $this->assertArrayHasKey('connection_factory_class', $config);
+        $this->assertSame('theFactoryClass', $config['connection_factory_class']);
+    }
+
     public function testThrowIfExtraOptionGiven()
     {
         $transport = new TransportFactory('default');
@@ -218,7 +272,7 @@ public function testShouldCreateConnectionFactoryFromDSN()
         );
     }
 
-    public function testShouldCreateConnectionFactoryUsingCustomFactortyClass()
+    public function testShouldCreateConnectionFactoryUsingCustomFactoryClass()
     {
         $container = new ContainerBuilder();
 
@@ -253,7 +307,7 @@ public function testShouldCreateConnectionFactoryUsingCustomFactortyClass()
         );
     }
 
-    public function testShouldCreateConnectionFactoryUsingCustomFactortyService()
+    public function testShouldCreateConnectionFactoryUsingCustomFactoryService()
     {
         $container = new ContainerBuilder();
 
@@ -282,6 +336,32 @@ public function testShouldCreateConnectionFactoryUsingCustomFactortyService()
         );
     }
 
+    public function testShouldCreateConnectionFactoryUsingConnectionFactoryClassWithoutFactory()
+    {
+        $container = new ContainerBuilder();
+
+        $transport = new TransportFactory('default');
+
+        $serviceId = $transport->createConnectionFactory($container, ['dsn' => 'foo:', 'connection_factory_class' => 'theFactoryClass']);
+
+        $this->assertEquals('enqueue.transport.default.connection_factory', $serviceId);
+
+        $this->assertTrue($container->hasDefinition('enqueue.transport.default.connection_factory'));
+
+        $this->assertEmpty($container->getDefinition('enqueue.transport.default.connection_factory')->getFactory());
+        $this->assertSame('theFactoryClass', $container->getDefinition('enqueue.transport.default.connection_factory')->getClass());
+        $this->assertSame(
+            [['dsn' => 'foo:']],
+            $container->getDefinition('enqueue.transport.default.connection_factory')->getArguments())
+        ;
+
+        $this->assertTrue($container->hasAlias('enqueue.transport.connection_factory'));
+        $this->assertEquals(
+            'enqueue.transport.default.connection_factory',
+            (string) $container->getAlias('enqueue.transport.connection_factory')
+        );
+    }
+
     public function testShouldCreateContextFromDsn()
     {
         $container = new ContainerBuilder();

From fd1e30e9e8e1a8f89d20ecdaa79d448ef312761c Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 11 Sep 2018 10:58:21 +0300
Subject: [PATCH 103/337] [symfony] add info that describes config options.

---
 .../DependencyInjection/TransportFactory.php  | 28 ++++++++++++++++---
 1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index 694aefa..4f1eab7 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -3,6 +3,8 @@
 namespace Enqueue\Symfony\DependencyInjection;
 
 use Enqueue\Client\DriverInterface;
+use Enqueue\ConnectionFactoryFactoryInterface;
+use Enqueue\Resources;
 use Interop\Queue\PsrConnectionFactory;
 use Interop\Queue\PsrContext;
 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
@@ -31,7 +33,11 @@ public function __construct(string $name)
 
     public function addConfiguration(ArrayNodeDefinition $builder): void
     {
+        $knownSchemes = array_keys(Resources::getKnownSchemes());
+        $availableSchemes = array_keys(Resources::getAvailableSchemes());
+
         $builder
+            ->info('The transport option could accept a string DSN, an array with DSN key, or null. It accept extra options. To find out what option you can set, look at connection factory constructor docblock.')
             ->beforeNormalization()
                 ->always(function ($v) {
                     if (empty($v)) {
@@ -59,10 +65,24 @@ public function addConfiguration(ArrayNodeDefinition $builder): void
         ->end()
         ->ignoreExtraKeys(false)
         ->children()
-            ->scalarNode('dsn')->cannotBeEmpty()->isRequired()->end()
-            ->scalarNode('connection_factory_class')->end()
-            ->scalarNode('factory_service')->end()
-            ->scalarNode('factory_class')->end()
+            ->scalarNode('dsn')
+                ->cannotBeEmpty()
+                ->isRequired()
+                ->info(sprintf(
+                    'The MQ broker DSN. These schemes are supported: "%s", to use these "%s" you have to install a package.',
+                    implode('", "', $knownSchemes),
+                    implode('", "', $availableSchemes)
+                ))
+            ->end()
+            ->scalarNode('connection_factory_class')
+                ->info(sprintf('The connection factory class should implement "%s" interface', PsrConnectionFactory::class))
+            ->end()
+            ->scalarNode('factory_service')
+                ->info(sprintf('The factory class should implement "%s" interface', ConnectionFactoryFactoryInterface::class))
+            ->end()
+            ->scalarNode('factory_class')
+                ->info(sprintf('The factory service should be a class that implements "%s" interface', ConnectionFactoryFactoryInterface::class))
+            ->end()
         ->end()
         ;
     }

From fe9de9b250c27cc5c5c3aceb12f3a5beb497885d Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 13 Sep 2018 16:41:57 +0300
Subject: [PATCH 104/337] [client] Introduce route and route collection. Rework
 tags

---
 Client/Route.php                              | 114 ++++++
 Client/RouteCollection.php                    | 102 +++++
 Client/RouterProcessor.php                    | 113 ++----
 Client/TopicSubscriberInterface.php           |  17 +-
 .../BuildCommandSubscriberRoutesPass.php      | 103 +++++
 .../BuildProcessorRegistryPass.php            |  54 +++
 .../BuildProcessorRoutesPass.php              |  80 ++++
 .../BuildTopicSubscriberRoutesPass.php        | 103 +++++
 Tests/Client/RouterProcessorTest.php          | 120 +++---
 .../BuildCommandSubscriberRoutesPassTest.php  | 353 ++++++++++++++++++
 .../BuildProcessorRegistryPassTest.php        | 146 ++++++++
 .../BuildProcessorRoutesPassTest.php          | 282 ++++++++++++++
 .../BuildTopicSubscriberRoutesPassTest.php    | 353 ++++++++++++++++++
 13 files changed, 1780 insertions(+), 160 deletions(-)
 create mode 100644 Client/Route.php
 create mode 100644 Client/RouteCollection.php
 create mode 100644 Symfony/DependencyInjection/BuildCommandSubscriberRoutesPass.php
 create mode 100644 Symfony/DependencyInjection/BuildProcessorRegistryPass.php
 create mode 100644 Symfony/DependencyInjection/BuildProcessorRoutesPass.php
 create mode 100644 Symfony/DependencyInjection/BuildTopicSubscriberRoutesPass.php
 create mode 100644 Tests/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
 create mode 100644 Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
 create mode 100644 Tests/Symfony/DependencyInjection/BuildProcessorRoutesPassTest.php
 create mode 100644 Tests/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php

diff --git a/Client/Route.php b/Client/Route.php
new file mode 100644
index 0000000..e11fa19
--- /dev/null
+++ b/Client/Route.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace Enqueue\Client;
+
+final class Route
+{
+    const TOPIC = 'enqueue.client.topic_route';
+
+    const COMMAND = 'enqueue.client.command_route';
+
+    /**
+     * @var string
+     */
+    private $source;
+
+    /**
+     * @var string
+     */
+    private $sourceType;
+
+    /**
+     * @var string
+     */
+    private $processor;
+
+    /**
+     * @var array
+     */
+    private $options;
+
+    public function __construct(
+        string $source,
+        string $sourceType,
+        string $processor,
+        array $options = []
+    ) {
+        $this->source = $source;
+        $this->sourceType = $sourceType;
+        $this->processor = $processor;
+        $this->options = $options;
+    }
+
+    public function getSource(): string
+    {
+        return $this->source;
+    }
+
+    public function isCommand(): bool
+    {
+        return self::COMMAND === $this->sourceType;
+    }
+
+    public function isTopic(): bool
+    {
+        return self::TOPIC === $this->sourceType;
+    }
+
+    public function getProcessor(): string
+    {
+        return $this->processor;
+    }
+
+    public function isProcessorExclusive(): bool
+    {
+        return (bool) $this->getOption('exclusive', false);
+    }
+
+    public function isProcessorExternal(): bool
+    {
+        return (bool) $this->getOption('external', false);
+    }
+
+    public function getQueue(): ?string
+    {
+        return $this->getOption('queue');
+    }
+
+    public function isPrefixQueue(): bool
+    {
+        return (bool) $this->getOption('prefix_queue', false);
+    }
+
+    public function getOptions(): array
+    {
+        return $this->options;
+    }
+
+    public function getOption(string $name, $default = null)
+    {
+        return array_key_exists($name, $this->options) ? $this->options[$name] : $default;
+    }
+
+    public function toArray(): array
+    {
+        return array_replace($this->options, [
+            'source' => $this->source,
+            'source_type' => $this->sourceType,
+            'processor' => $this->processor,
+        ]);
+    }
+
+    public static function fromArray(array $route): self
+    {
+        list(
+            'source' => $source,
+            'source_type' => $sourceType,
+            'processor' => $processor) = $route;
+
+        unset($route['source'], $route['source_type'], $route['processor']);
+        $options = $route;
+
+        return new self($source, $sourceType, $processor, $options);
+    }
+}
diff --git a/Client/RouteCollection.php b/Client/RouteCollection.php
new file mode 100644
index 0000000..d2a0307
--- /dev/null
+++ b/Client/RouteCollection.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace Enqueue\Client;
+
+class RouteCollection
+{
+    /**
+     * @var Route[]
+     */
+    private $routes;
+
+    /**
+     * @var Route[]
+     */
+    private $commandRoutes;
+
+    /**
+     * @var Route[]
+     */
+    private $topicRoutes;
+
+    /**
+     * @param Route[] $routes
+     */
+    public function __construct(array $routes)
+    {
+        $this->routes = $routes;
+    }
+
+    public function add(Route $route): void
+    {
+        $this->routes[] = $route;
+        $this->topicRoutes = null;
+        $this->commandRoutes = null;
+    }
+
+    /**
+     * @return Route[]
+     */
+    public function allRoutes(): array
+    {
+        return $this->routes;
+    }
+
+    /**
+     * @return Route[]
+     */
+    public function commandRoute(string $command): ?Route
+    {
+        if (null === $this->commandRoutes) {
+            $commandRoutes = [];
+            foreach ($this->routes as $route) {
+                if ($route->isCommand()) {
+                    $commandRoutes[$route->getSource()] = $route;
+                }
+            }
+
+            $this->commandRoutes = $commandRoutes;
+        }
+
+        return array_key_exists($command, $this->commandRoutes) ? $this->commandRoutes[$command] : null;
+    }
+
+    /**
+     * @return Route[]
+     */
+    public function topicRoutes(string $topic): array
+    {
+        if (null === $this->topicRoutes) {
+            $topicRoutes = [];
+            foreach ($this->routes as $route) {
+                if ($route->isTopic()) {
+                    $topicRoutes[$route->getSource()][$route->getProcessor()] = $route;
+                }
+            }
+
+            $this->topicRoutes = $topicRoutes;
+        }
+
+        return array_key_exists($topic, $this->topicRoutes) ? $this->topicRoutes[$topic] : [];
+    }
+
+    public function toArray(): array
+    {
+        $rawRoutes = [];
+        foreach ($this->routes as $route) {
+            $rawRoutes[] = $route->toArray();
+        }
+
+        return $rawRoutes;
+    }
+
+    public static function fromArray(array $rawRoutes): self
+    {
+        $routes = [];
+        foreach ($rawRoutes as $rawRoute) {
+            $routes[] = Route::fromArray($rawRoute);
+        }
+
+        return new self($routes);
+    }
+}
diff --git a/Client/RouterProcessor.php b/Client/RouterProcessor.php
index 35efe1b..2bf9779 100644
--- a/Client/RouterProcessor.php
+++ b/Client/RouterProcessor.php
@@ -7,7 +7,7 @@
 use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrProcessor;
 
-class RouterProcessor implements PsrProcessor
+final class RouterProcessor implements PsrProcessor
 {
     /**
      * @var DriverInterface
@@ -15,107 +15,64 @@ class RouterProcessor implements PsrProcessor
     private $driver;
 
     /**
-     * @var array
+     * @var RouteCollection
      */
-    private $eventRoutes;
+    private $routeCollection;
 
-    /**
-     * @var array
-     */
-    private $commandRoutes;
-
-    /**
-     * @param DriverInterface $driver
-     * @param array           $eventRoutes
-     * @param array           $commandRoutes
-     */
-    public function __construct(DriverInterface $driver, array $eventRoutes = [], array $commandRoutes = [])
+    public function __construct(DriverInterface $driver, RouteCollection $routeCollection)
     {
         $this->driver = $driver;
-
-        $this->eventRoutes = $eventRoutes;
-        $this->commandRoutes = $commandRoutes;
+        $this->routeCollection = $routeCollection;
     }
 
-    /**
-     * @param string $topicName
-     * @param string $queueName
-     * @param string $processorName
-     */
-    public function add($topicName, $queueName, $processorName)
+    public function process(PsrMessage $message, PsrContext $context): Result
     {
-        if (Config::COMMAND_TOPIC === $topicName) {
-            $this->commandRoutes[$processorName] = $queueName;
-        } else {
-            $this->eventRoutes[$topicName][] = [$processorName, $queueName];
+        $topic = $message->getProperty(Config::PARAMETER_TOPIC_NAME);
+        if ($topic) {
+            return $this->routeEvent($topic, $message);
         }
-    }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function process(PsrMessage $message, PsrContext $context)
-    {
-        $topicName = $message->getProperty(Config::PARAMETER_TOPIC_NAME);
-        if (false == $topicName) {
-            return Result::reject(sprintf(
-                'Got message without required parameter: "%s"',
-                Config::PARAMETER_TOPIC_NAME
-            ));
+        $command = $message->getProperty(Config::PARAMETER_COMMAND_NAME);
+        if ($command) {
+            return $this->routeCommand($command, $message);
         }
 
-        if (Config::COMMAND_TOPIC === $topicName) {
-            return $this->routeCommand($message);
-        }
-
-        return $this->routeEvent($message);
+        return Result::reject(sprintf(
+            'Got message without required parameters. Either "%s" or "%s" property should be set',
+            Config::PARAMETER_TOPIC_NAME,
+            Config::PARAMETER_COMMAND_NAME
+        ));
     }
 
-    /**
-     * @param PsrMessage $message
-     *
-     * @return string|Result
-     */
-    private function routeEvent(PsrMessage $message)
+    private function routeEvent(string $topic, PsrMessage $message): Result
     {
-        $topicName = $message->getProperty(Config::PARAMETER_TOPIC_NAME);
+        $count = 0;
+        foreach ($this->routeCollection->topicRoutes($topic) as $route) {
+            $processorMessage = clone $message;
+            $processorMessage->setProperty(Config::PARAMETER_PROCESSOR_NAME, $route->getProcessor());
+            $processorMessage->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $route->getQueue());
 
-        if (array_key_exists($topicName, $this->eventRoutes)) {
-            foreach ($this->eventRoutes[$topicName] as $route) {
-                $processorMessage = clone $message;
-                $processorMessage->setProperty(Config::PARAMETER_PROCESSOR_NAME, $route[0]);
-                $processorMessage->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $route[1]);
+            $this->driver->sendToProcessor($this->driver->createClientMessage($processorMessage));
 
-                $this->driver->sendToProcessor($this->driver->createClientMessage($processorMessage));
-            }
+            ++$count;
         }
 
-        return self::ACK;
+        return Result::ack(sprintf('Routed to "%d" event subscribers', $count));
     }
 
-    /**
-     * @param PsrMessage $message
-     *
-     * @return string|Result
-     */
-    private function routeCommand(PsrMessage $message)
+    private function routeCommand(string $command, PsrMessage $message): Result
     {
-        $commandName = $message->getProperty(Config::PARAMETER_COMMAND_NAME);
-        if (false == $commandName) {
-            return Result::reject(sprintf(
-                'Got message without required parameter: "%s"',
-                Config::PARAMETER_COMMAND_NAME
-            ));
+        $route = $this->routeCollection->commandRoute($command);
+        if (false == $route) {
+            throw new \LogicException(sprintf('The command "%s" processor not found', $command));
         }
 
-        if (isset($this->commandRoutes[$commandName])) {
-            $processorMessage = clone $message;
-            $processorMessage->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $this->commandRoutes[$commandName]);
-            $processorMessage->setProperty(Config::PARAMETER_PROCESSOR_NAME, $commandName);
+        $processorMessage = clone $message;
+        $processorMessage->setProperty(Config::PARAMETER_PROCESSOR_NAME, $route->getProcessor());
+        $processorMessage->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $route->getQueue());
 
-            $this->driver->sendToProcessor($this->driver->createClientMessage($processorMessage));
-        }
+        $this->driver->sendToProcessor($this->driver->createClientMessage($processorMessage));
 
-        return self::ACK;
+        return Result::ack('Routed to the command processor');
     }
 }
diff --git a/Client/TopicSubscriberInterface.php b/Client/TopicSubscriberInterface.php
index bdaa43f..4723c67 100644
--- a/Client/TopicSubscriberInterface.php
+++ b/Client/TopicSubscriberInterface.php
@@ -7,21 +7,22 @@ interface TopicSubscriberInterface
     /**
      * The result maybe either:.
      *
-     * ['aTopicName']
+     * 'aTopicName'
      *
      * or
      *
-     * ['aTopicName' => [
-     *     'processorName' => 'processor',
-     *     'queueName' => 'a_client_queue_name',
-     *     'queueNameHardcoded' => true,
-     *   ]]
+     * ['aTopicName', 'anotherTopicName']
+     *
+     * or
      *
-     * processorName, queueName and queueNameHardcoded are optional.
+     * ['aTopicName' => [
+     *     'processor' => 'processor',
+     *     'queue' => 'a_client_queue_name',
+     * ]]
      *
      * Note: If you set queueNameHardcoded to true then the queueName is used as is and therefor the driver is not used to create a transport queue name.
      *
-     * @return array
+     * @return string|array
      */
     public static function getSubscribedTopics();
 }
diff --git a/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPass.php b/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPass.php
new file mode 100644
index 0000000..e9e16a3
--- /dev/null
+++ b/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPass.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace Enqueue\Symfony\DependencyInjection;
+
+use Enqueue\Client\CommandSubscriberInterface;
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+final class BuildCommandSubscriberRoutesPass implements CompilerPassInterface
+{
+    /**
+     * @var string
+     */
+    private $name;
+
+    public function __construct(string $clientName)
+    {
+        if (empty($clientName)) {
+            throw new \InvalidArgumentException('The name could not be empty.');
+        }
+
+        $this->name = $clientName;
+    }
+
+    public function process(ContainerBuilder $container): void
+    {
+        $routeCollectionId = sprintf('enqueue.client.%s.route_collection', $this->name);
+        if (false == $container->hasDefinition($routeCollectionId)) {
+            return;
+        }
+
+        $tag = 'enqueue.command_subscriber';
+        $routeCollection = new RouteCollection([]);
+        foreach ($container->findTaggedServiceIds($tag) as $serviceId => $tagAttributes) {
+            $processorDefinition = $container->getDefinition($serviceId);
+            if ($processorDefinition->getFactory()) {
+                throw new \LogicException('The command subscriber tag could not be applied to a service created by factory.');
+            }
+
+            $processorClass = $processorDefinition->getClass();
+            if (false == class_exists($processorClass)) {
+                throw new \LogicException(sprintf('The processor class "%s" could not be found.', $processorClass));
+            }
+
+            if (false == is_subclass_of($processorClass, CommandSubscriberInterface::class)) {
+                throw new \LogicException(sprintf('The processor must implement "%s" interface to be used with the tag "%s"', CommandSubscriberInterface::class, $tag));
+            }
+
+            foreach ($tagAttributes as $tagAttribute) {
+                $client = $tagAttribute['client'] ?? 'default';
+
+                if ($client !== $this->name && 'all' !== $client) {
+                    continue;
+                }
+
+                /** @var CommandSubscriberInterface $processorClass */
+                $commands = $processorClass::getSubscribedCommand();
+
+                if (empty($commands)) {
+                    throw new \LogicException('Command subscriber must return something.');
+                }
+
+                if (is_string($commands)) {
+                    $commands = [$commands];
+                }
+
+                if (!is_array($commands)) {
+                    throw new \LogicException('Command subscriber configuration is invalid. Should be an array or string.');
+                }
+
+                foreach ($commands as $key => $params) {
+                    if (is_string($params)) {
+                        $routeCollection->add(new Route($params, Route::COMMAND, $serviceId, ['processor_service_id' => $serviceId]));
+                    } elseif (is_array($params)) {
+                        $source = $params['command'] ?? null;
+                        $processor = $params['processor'] ?? $serviceId;
+                        unset($params['command'], $params['source'], $params['source_type'], $params['processor'], $params['options']);
+                        $options = $params;
+                        $options['processor_service_id'] = $serviceId;
+
+                        $routeCollection->add(new Route($source, Route::COMMAND, $processor, $options));
+                    } else {
+                        throw new \LogicException(sprintf(
+                            'Command subscriber configuration is invalid for "%s::getSubscribedCommand()". "%s"',
+                            $processorClass,
+                            json_encode($processorClass::getSubscribedCommand())
+                        ));
+                    }
+                }
+            }
+        }
+
+        $rawRoutes = $routeCollection->toArray();
+
+        $routeCollectionService = $container->getDefinition($routeCollectionId);
+        $routeCollectionService->replaceArgument(0, array_merge(
+            $routeCollectionService->getArgument(0),
+            $rawRoutes
+        ));
+    }
+}
diff --git a/Symfony/DependencyInjection/BuildProcessorRegistryPass.php b/Symfony/DependencyInjection/BuildProcessorRegistryPass.php
new file mode 100644
index 0000000..7cdc209
--- /dev/null
+++ b/Symfony/DependencyInjection/BuildProcessorRegistryPass.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Enqueue\Symfony\DependencyInjection;
+
+use Enqueue\Client\RouteCollection;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+final class BuildProcessorRegistryPass implements CompilerPassInterface
+{
+    /**
+     * @var string
+     */
+    private $name;
+
+    public function __construct(string $clientName)
+    {
+        if (empty($clientName)) {
+            throw new \InvalidArgumentException('The name could not be empty.');
+        }
+
+        $this->name = $clientName;
+    }
+
+    public function process(ContainerBuilder $container): void
+    {
+        $processorRegistryId = sprintf('enqueue.client.%s.processor_registry', $this->name);
+        if (false == $container->hasDefinition($processorRegistryId)) {
+            return;
+        }
+
+        $routeCollectionId = sprintf('enqueue.client.%s.route_collection', $this->name);
+        if (false == $container->hasDefinition($routeCollectionId)) {
+            throw new \LogicException(sprintf('The required route collection "%s" is not registered. Make sure the client with name "%s" was loaded.', $routeCollectionId, $this->name));
+        }
+
+        $routeCollection = RouteCollection::fromArray($container->getDefinition($routeCollectionId)->getArgument(0));
+
+        $map = [];
+        foreach ($routeCollection->allRoutes() as $route) {
+            if (false == $processorServiceId = $route->getOption('processor_service_id')) {
+                throw new \LogicException('The route option "processor_service_id" is required');
+            }
+
+            $map[$route->getProcessor()] = $processorServiceId;
+        }
+
+        $registry = $container->getDefinition($processorRegistryId);
+        $registry->replaceArgument(0, array_replace(
+            $registry->getArgument(0),
+            $map
+        ));
+    }
+}
diff --git a/Symfony/DependencyInjection/BuildProcessorRoutesPass.php b/Symfony/DependencyInjection/BuildProcessorRoutesPass.php
new file mode 100644
index 0000000..7a6853b
--- /dev/null
+++ b/Symfony/DependencyInjection/BuildProcessorRoutesPass.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Enqueue\Symfony\DependencyInjection;
+
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+final class BuildProcessorRoutesPass implements CompilerPassInterface
+{
+    /**
+     * @var string
+     */
+    private $name;
+
+    public function __construct(string $clientName)
+    {
+        if (empty($clientName)) {
+            throw new \InvalidArgumentException('The name could not be empty.');
+        }
+
+        $this->name = $clientName;
+    }
+
+    public function process(ContainerBuilder $container): void
+    {
+        $routeCollectionId = sprintf('enqueue.client.%s.route_collection', $this->name);
+        if (false == $container->hasDefinition($routeCollectionId)) {
+            return;
+        }
+
+        $tag = 'enqueue.processor';
+        $routeCollection = new RouteCollection([]);
+        foreach ($container->findTaggedServiceIds($tag) as $serviceId => $tagAttributes) {
+            foreach ($tagAttributes as $tagAttribute) {
+                $client = $tagAttribute['client'] ?? 'default';
+
+                if ($client !== $this->name && 'all' !== $client) {
+                    continue;
+                }
+
+                $topic = $tagAttribute['topic'] ?? null;
+                $command = $tagAttribute['command'] ?? null;
+
+                if (false == $topic && false == $command) {
+                    throw new \LogicException('Either "topic" or "command" tag attribute must be set. None is set.');
+                }
+                if ($topic && $command) {
+                    throw new \LogicException('Either "topic" or "command" tag attribute must be set. Both are set.');
+                }
+
+                $source = $command ?: $topic;
+                $sourceType = $command ? Route::COMMAND : Route::TOPIC;
+                $processor = $tagAttribute['processor'] ?? $serviceId;
+
+                unset(
+                    $tagAttribute['topic'],
+                    $tagAttribute['command'],
+                    $tagAttribute['source'],
+                    $tagAttribute['source_type'],
+                    $tagAttribute['processor'],
+                    $tagAttribute['options']
+                );
+                $options = $tagAttribute;
+                $options['processor_service_id'] = $serviceId;
+
+                $routeCollection->add(new Route($source, $sourceType, $processor, $options));
+            }
+        }
+
+        $rawRoutes = $routeCollection->toArray();
+
+        $routeCollectionService = $container->getDefinition($routeCollectionId);
+        $routeCollectionService->replaceArgument(0, array_merge(
+            $routeCollectionService->getArgument(0),
+            $rawRoutes
+        ));
+    }
+}
diff --git a/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPass.php b/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPass.php
new file mode 100644
index 0000000..869a9f0
--- /dev/null
+++ b/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPass.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace Enqueue\Symfony\DependencyInjection;
+
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
+use Enqueue\Client\TopicSubscriberInterface;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+final class BuildTopicSubscriberRoutesPass implements CompilerPassInterface
+{
+    /**
+     * @var string
+     */
+    private $name;
+
+    public function __construct(string $clientName)
+    {
+        if (empty($clientName)) {
+            throw new \InvalidArgumentException('The name could not be empty.');
+        }
+
+        $this->name = $clientName;
+    }
+
+    public function process(ContainerBuilder $container): void
+    {
+        $routeCollectionId = sprintf('enqueue.client.%s.route_collection', $this->name);
+        if (false == $container->hasDefinition($routeCollectionId)) {
+            return;
+        }
+
+        $tag = 'enqueue.topic_subscriber';
+        $routeCollection = new RouteCollection([]);
+        foreach ($container->findTaggedServiceIds($tag) as $serviceId => $tagAttributes) {
+            $processorDefinition = $container->getDefinition($serviceId);
+            if ($processorDefinition->getFactory()) {
+                throw new \LogicException('The topic subscriber tag could not be applied to a service created by factory.');
+            }
+
+            $processorClass = $processorDefinition->getClass();
+            if (false == class_exists($processorClass)) {
+                throw new \LogicException(sprintf('The processor class "%s" could not be found.', $processorClass));
+            }
+
+            if (false == is_subclass_of($processorClass, TopicSubscriberInterface::class)) {
+                throw new \LogicException(sprintf('The processor must implement "%s" interface to be used with the tag "%s"', TopicSubscriberInterface::class, $tag));
+            }
+
+            foreach ($tagAttributes as $tagAttribute) {
+                $client = $tagAttribute['client'] ?? 'default';
+
+                if ($client !== $this->name && 'all' !== $client) {
+                    continue;
+                }
+
+                /** @var TopicSubscriberInterface $processorClass */
+                $topics = $processorClass::getSubscribedTopics();
+
+                if (empty($topics)) {
+                    throw new \LogicException('Topic subscriber must return something.');
+                }
+
+                if (is_string($topics)) {
+                    $topics = [$topics];
+                }
+
+                if (!is_array($topics)) {
+                    throw new \LogicException('Topic subscriber configuration is invalid. Should be an array or string.');
+                }
+
+                foreach ($topics as $key => $params) {
+                    if (is_string($params)) {
+                        $routeCollection->add(new Route($params, Route::TOPIC, $serviceId, ['processor_service_id' => $serviceId]));
+                    } elseif (is_array($params)) {
+                        $source = $params['topic'] ?? null;
+                        $processor = $params['processor'] ?? $serviceId;
+                        unset($params['topic'], $params['source'], $params['source_type'], $params['processor'], $params['options']);
+                        $options = $params;
+                        $options['processor_service_id'] = $serviceId;
+
+                        $routeCollection->add(new Route($source, Route::TOPIC, $processor, $options));
+                    } else {
+                        throw new \LogicException(sprintf(
+                            'Topic subscriber configuration is invalid for "%s::getSubscribedTopics()". Got "%s"',
+                            $processorClass,
+                            json_encode($processorClass::getSubscribedTopics())
+                        ));
+                    }
+                }
+            }
+        }
+
+        $rawRoutes = $routeCollection->toArray();
+
+        $routeCollectionService = $container->getDefinition($routeCollectionId);
+        $routeCollectionService->replaceArgument(0, array_merge(
+            $routeCollectionService->getArgument(0),
+            $rawRoutes
+        ));
+    }
+}
diff --git a/Tests/Client/RouterProcessorTest.php b/Tests/Client/RouterProcessorTest.php
index e362c1e..da803b1 100644
--- a/Tests/Client/RouterProcessorTest.php
+++ b/Tests/Client/RouterProcessorTest.php
@@ -5,40 +5,49 @@
 use Enqueue\Client\Config;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Message;
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
 use Enqueue\Client\RouterProcessor;
 use Enqueue\Consumption\Result;
 use Enqueue\Null\NullContext;
 use Enqueue\Null\NullMessage;
+use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrProcessor;
 use PHPUnit\Framework\TestCase;
 
 class RouterProcessorTest extends TestCase
 {
-    public function testCouldBeConstructedWithDriverAsFirstArgument()
+    use ClassExtensionTrait;
+
+    public function testShouldImplementProcessorInterface()
+    {
+        $this->assertClassImplements(PsrProcessor::class, RouterProcessor::class);
+    }
+
+    public function testShouldBeFinal()
     {
-        new RouterProcessor($this->createDriverMock());
+        $this->assertClassFinal(RouterProcessor::class);
     }
 
-    public function testCouldBeConstructedWithSessionAndRoutes()
+    public function testCouldBeConstructedWithDriverAndRouteCollection()
     {
-        $routes = [
-            'aTopicName' => [['aProcessorName', 'aQueueName']],
-            'anotherTopicName' => [['aProcessorName', 'aQueueName']],
-        ];
+        $driver = $this->createDriverMock();
+        $routeCollection = new RouteCollection([]);
 
-        $router = new RouterProcessor($this->createDriverMock(), $routes);
+        $processor = new RouterProcessor($driver, $routeCollection);
 
-        $this->assertAttributeEquals($routes, 'eventRoutes', $router);
+        $this->assertAttributeSame($driver, 'driver', $processor);
+        $this->assertAttributeSame($routeCollection, 'routeCollection', $processor);
     }
 
-    public function testShouldRejectIfTopicNameParameterIsNotSet()
+    public function testShouldRejectIfNeitherTopicNorCommandParameterIsSet()
     {
-        $router = new RouterProcessor($this->createDriverMock());
+        $router = new RouterProcessor($this->createDriverMock(), new RouteCollection([]));
 
         $result = $router->process(new NullMessage(), new NullContext());
 
-        $this->assertInstanceOf(Result::class, $result);
         $this->assertEquals(Result::REJECT, $result->getStatus());
-        $this->assertEquals('Got message without required parameter: "enqueue.topic_name"', $result->getReason());
+        $this->assertEquals('Got message without required parameters. Either "enqueue.topic_name" or "enqueue.command_name" property should be set', $result->getReason());
     }
 
     public function testShouldRouteOriginalMessageToEventRecipient()
@@ -68,15 +77,15 @@ public function testShouldRouteOriginalMessageToEventRecipient()
             })
         ;
 
-        $routes = [
-            'theTopicName' => [['aFooProcessor', 'aQueueName']],
-        ];
+        $processor = new RouterProcessor($driver, new RouteCollection([
+            new Route('theTopicName', Route::TOPIC, 'aFooProcessor', ['queue' => 'aQueueName']),
+        ]));
 
-        $router = new RouterProcessor($driver, $routes);
+        $result = $processor->process($message, new NullContext());
 
-        $result = $router->process($message, new NullContext());
+        $this->assertEquals(Result::ACK, $result->getStatus());
+        $this->assertEquals('Routed to "1" event subscribers', $result->getReason());
 
-        $this->assertEquals(Result::ACK, $result);
         $this->assertEquals([
             'aProp' => 'aPropVal',
             'enqueue.topic_name' => 'theTopicName',
@@ -92,8 +101,7 @@ public function testShouldRouteOriginalMessageToCommandRecipient()
         $message->setHeaders(['aHeader' => 'aHeaderVal']);
         $message->setProperties([
             'aProp' => 'aPropVal',
-            Config::PARAMETER_TOPIC_NAME => Config::COMMAND_TOPIC,
-            Config::PARAMETER_COMMAND_NAME => 'theCommandName',
+            'enqueue.command_name' => 'theCommandName',
         ]);
 
         $clientMessage = new Message();
@@ -116,84 +124,48 @@ public function testShouldRouteOriginalMessageToCommandRecipient()
             })
         ;
 
-        $routes = [
-            'theCommandName' => 'aQueueName',
-        ];
+        $processor = new RouterProcessor($driver, new RouteCollection([
+            new Route('theCommandName', Route::COMMAND, 'aFooProcessor', ['queue' => 'aQueueName']),
+        ]));
 
-        $router = new RouterProcessor($driver, [], $routes);
+        $result = $processor->process($message, new NullContext());
 
-        $result = $router->process($message, new NullContext());
+        $this->assertEquals(Result::ACK, $result->getStatus());
+        $this->assertEquals('Routed to the command processor', $result->getReason());
 
-        $this->assertEquals(Result::ACK, $result);
         $this->assertEquals([
             'aProp' => 'aPropVal',
-            'enqueue.topic_name' => Config::COMMAND_TOPIC,
-            'enqueue.processor_name' => 'theCommandName',
+            'enqueue.processor_name' => 'aFooProcessor',
             'enqueue.command_name' => 'theCommandName',
             'enqueue.processor_queue_name' => 'aQueueName',
         ], $routedMessage->getProperties());
     }
 
-    public function testShouldRejectCommandMessageIfCommandNamePropertyMissing()
+    public function testThrowIfNoRouteForGivenCommand()
     {
         $message = new NullMessage();
         $message->setBody('theBody');
         $message->setHeaders(['aHeader' => 'aHeaderVal']);
         $message->setProperties([
             'aProp' => 'aPropVal',
-            Config::PARAMETER_TOPIC_NAME => Config::COMMAND_TOPIC,
+            'enqueue.command_name' => 'theCommandName',
         ]);
 
+        $clientMessage = new Message();
+
+        $routedMessage = null;
+
         $driver = $this->createDriverMock();
         $driver
             ->expects($this->never())
             ->method('sendToProcessor')
         ;
-        $driver
-            ->expects($this->never())
-            ->method('createClientMessage')
-        ;
-
-        $routes = [
-            'theCommandName' => 'aQueueName',
-        ];
-
-        $router = new RouterProcessor($driver, [], $routes);
-
-        $result = $router->process($message, new NullContext());
-
-        $this->assertInstanceOf(Result::class, $result);
-        $this->assertEquals(Result::REJECT, $result->getStatus());
-        $this->assertEquals('Got message without required parameter: "enqueue.command_name"', $result->getReason());
-    }
-
-    public function testShouldAddEventRoute()
-    {
-        $router = new RouterProcessor($this->createDriverMock(), []);
-
-        $this->assertAttributeSame([], 'eventRoutes', $router);
-
-        $router->add('theTopicName', 'theQueueName', 'aProcessorName');
-
-        $this->assertAttributeSame([
-            'theTopicName' => [
-                ['aProcessorName', 'theQueueName'],
-            ],
-        ], 'eventRoutes', $router);
-
-        $this->assertAttributeSame([], 'commandRoutes', $router);
-    }
-
-    public function testShouldAddCommandRoute()
-    {
-        $router = new RouterProcessor($this->createDriverMock(), []);
-
-        $this->assertAttributeSame([], 'eventRoutes', $router);
 
-        $router->add(Config::COMMAND_TOPIC, 'theQueueName', 'aProcessorName');
+        $processor = new RouterProcessor($driver, new RouteCollection([]));
 
-        $this->assertAttributeSame(['aProcessorName' => 'theQueueName'], 'commandRoutes', $router);
-        $this->assertAttributeSame([], 'eventRoutes', $router);
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The command "theCommandName" processor not found');
+        $processor->process($message, new NullContext());
     }
 
     /**
diff --git a/Tests/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php b/Tests/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
new file mode 100644
index 0000000..3e4d69a
--- /dev/null
+++ b/Tests/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
@@ -0,0 +1,353 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\DependencyInjection;
+
+use Enqueue\Client\CommandSubscriberInterface;
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
+use Enqueue\Symfony\DependencyInjection\BuildCommandSubscriberRoutesPass;
+use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrContext;
+use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrProcessor;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+
+class BuildCommandSubscriberRoutesPassTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementCompilerPassInterface()
+    {
+        $this->assertClassImplements(CompilerPassInterface::class, BuildCommandSubscriberRoutesPass::class);
+    }
+
+    public function testShouldBeFinal()
+    {
+        $this->assertClassFinal(BuildCommandSubscriberRoutesPass::class);
+    }
+
+    public function testCouldBeConstructedWithName()
+    {
+        $pass = new BuildCommandSubscriberRoutesPass('aName');
+
+        $this->assertAttributeSame('aName', 'name', $pass);
+    }
+
+    public function testThrowIfNameEmptyOnConstruct()
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The name could not be empty.');
+        new BuildCommandSubscriberRoutesPass('');
+    }
+
+    public function testShouldDoNothingIfRouteCollectionServiceIsNotRegistered()
+    {
+        $pass = new BuildCommandSubscriberRoutesPass('aName');
+        $pass->process(new ContainerBuilder());
+    }
+
+    public function testThrowIfTaggedProcessorIsBuiltByFactory()
+    {
+        $container = new ContainerBuilder();
+        $container->register('enqueue.client.aName.route_collection', RouteCollection::class)
+            ->addArgument([])
+        ;
+        $container->register('aProcessor', PsrProcessor::class)
+            ->setFactory('foo')
+            ->addTag('enqueue.command_subscriber')
+        ;
+
+        $pass = new BuildCommandSubscriberRoutesPass('aName');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The command subscriber tag could not be applied to a service created by factory.');
+        $pass->process($container);
+    }
+
+    public function testShouldRegisterProcessorWithMatchedName()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($this->createCommandSubscriberProcessor()))
+            ->addTag('enqueue.command_subscriber', ['client' => 'foo'])
+        ;
+        $container->register('aProcessor', get_class($this->createCommandSubscriberProcessor()))
+            ->addTag('enqueue.command_subscriber', ['client' => 'bar'])
+        ;
+
+        $pass = new BuildCommandSubscriberRoutesPass('foo');
+
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(1, $routeCollection->getArgument(0));
+    }
+
+    public function testShouldRegisterProcessorWithoutNameToDefaultClient()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($this->createCommandSubscriberProcessor()))
+            ->addTag('enqueue.command_subscriber')
+        ;
+        $container->register('aProcessor', get_class($this->createCommandSubscriberProcessor()))
+            ->addTag('enqueue.command_subscriber', ['client' => 'bar'])
+        ;
+
+        $pass = new BuildCommandSubscriberRoutesPass('default');
+
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(1, $routeCollection->getArgument(0));
+    }
+
+    public function testShouldRegisterProcessorIfClientNameEqualsAll()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($this->createCommandSubscriberProcessor()))
+            ->addTag('enqueue.command_subscriber', ['client' => 'all'])
+        ;
+        $container->register('aProcessor', get_class($this->createCommandSubscriberProcessor()))
+            ->addTag('enqueue.command_subscriber', ['client' => 'bar'])
+        ;
+
+        $pass = new BuildCommandSubscriberRoutesPass('default');
+
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(1, $routeCollection->getArgument(0));
+    }
+
+    public function testShouldRegisterProcessorIfCommandsIsString()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $processor = $this->createCommandSubscriberProcessor('fooCommand');
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($processor))
+            ->addTag('enqueue.command_subscriber')
+        ;
+
+        $pass = new BuildCommandSubscriberRoutesPass('default');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(1, $routeCollection->getArgument(0));
+
+        $this->assertEquals(
+            [
+                [
+                    'source' => 'fooCommand',
+                    'source_type' => 'enqueue.client.command_route',
+                    'processor' => 'aFooProcessor',
+                    'processor_service_id' => 'aFooProcessor',
+                ],
+            ],
+            $routeCollection->getArgument(0)
+        );
+    }
+
+    public function testThrowIfCommandSubscriberReturnsNothing()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $processor = $this->createCommandSubscriberProcessor(null);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($processor))
+            ->addTag('enqueue.command_subscriber')
+        ;
+
+        $pass = new BuildCommandSubscriberRoutesPass('default');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Command subscriber must return something.');
+        $pass->process($container);
+    }
+
+    public function testShouldRegisterProcessorIfCommandsAreStrings()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $processor = $this->createCommandSubscriberProcessor(['fooCommand', 'barCommand']);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($processor))
+            ->addTag('enqueue.command_subscriber')
+        ;
+
+        $pass = new BuildCommandSubscriberRoutesPass('default');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(2, $routeCollection->getArgument(0));
+
+        $this->assertEquals(
+            [
+                [
+                    'source' => 'fooCommand',
+                    'source_type' => 'enqueue.client.command_route',
+                    'processor' => 'aFooProcessor',
+                    'processor_service_id' => 'aFooProcessor',
+                ],
+                [
+                    'source' => 'barCommand',
+                    'source_type' => 'enqueue.client.command_route',
+                    'processor' => 'aFooProcessor',
+                    'processor_service_id' => 'aFooProcessor',
+                ],
+            ],
+            $routeCollection->getArgument(0)
+        );
+    }
+
+    public function testShouldRegisterProcessorIfCommandsAreParamArrays()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $processor = $this->createCommandSubscriberProcessor([
+            ['command' => 'fooCommand', 'processor' => 'aCustomFooProcessorName', 'anOption' => 'aFooVal'],
+            ['command' => 'barCommand', 'processor' => 'aCustomBarProcessorName', 'anOption' => 'aBarVal'],
+        ]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($processor))
+            ->addTag('enqueue.command_subscriber')
+        ;
+
+        $pass = new BuildCommandSubscriberRoutesPass('default');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(2, $routeCollection->getArgument(0));
+
+        $this->assertEquals(
+            [
+                [
+                    'source' => 'fooCommand',
+                    'source_type' => 'enqueue.client.command_route',
+                    'processor' => 'aCustomFooProcessorName',
+                    'processor_service_id' => 'aFooProcessor',
+                    'anOption' => 'aFooVal',
+                ],
+                [
+                    'source' => 'barCommand',
+                    'source_type' => 'enqueue.client.command_route',
+                    'processor' => 'aCustomBarProcessorName',
+                    'processor_service_id' => 'aFooProcessor',
+                    'anOption' => 'aBarVal',
+                ],
+            ],
+            $routeCollection->getArgument(0)
+        );
+    }
+
+    public function testThrowIfCommandSubscriberParamsInvalid()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $processor = $this->createCommandSubscriberProcessor(['fooBar', true]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($processor))
+            ->addTag('enqueue.command_subscriber')
+        ;
+
+        $pass = new BuildCommandSubscriberRoutesPass('default');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Command subscriber configuration is invalid');
+        $pass->process($container);
+    }
+
+    public function testShouldMergeExtractedRoutesWithAlreadySetInCollection()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([
+            (new Route('aCommand', Route::COMMAND, 'aProcessor'))->toArray(),
+            (new Route('aCommand', Route::COMMAND, 'aProcessor'))->toArray(),
+        ]);
+
+        $processor = $this->createCommandSubscriberProcessor(['fooCommand']);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($processor))
+            ->addTag('enqueue.command_subscriber')
+        ;
+
+        $pass = new BuildCommandSubscriberRoutesPass('default');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(3, $routeCollection->getArgument(0));
+
+        $this->assertEquals(
+            [
+                [
+                    'source' => 'aCommand',
+                    'source_type' => 'enqueue.client.command_route',
+                    'processor' => 'aProcessor',
+                ],
+                [
+                    'source' => 'aCommand',
+                    'source_type' => 'enqueue.client.command_route',
+                    'processor' => 'aProcessor',
+                ],
+                [
+                    'source' => 'fooCommand',
+                    'source_type' => 'enqueue.client.command_route',
+                    'processor' => 'aFooProcessor',
+                    'processor_service_id' => 'aFooProcessor',
+                ],
+            ],
+            $routeCollection->getArgument(0)
+        );
+    }
+
+    private function createCommandSubscriberProcessor($commandSubscriberReturns = ['aCommand'])
+    {
+        $processor = new class() implements PsrProcessor, CommandSubscriberInterface {
+            public static $return;
+
+            public function process(PsrMessage $message, PsrContext $context)
+            {
+            }
+
+            public static function getSubscribedCommand()
+            {
+                return static::$return;
+            }
+        };
+
+        $processor::$return = $commandSubscriberReturns;
+
+        return $processor;
+    }
+}
diff --git a/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php b/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
new file mode 100644
index 0000000..70ad5df
--- /dev/null
+++ b/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\DependencyInjection;
+
+use Enqueue\Client\Route;
+use Enqueue\Symfony\DependencyInjection\BuildProcessorRegistryPass;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+
+class BuildProcessorRegistryPassTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementCompilerPassInterface()
+    {
+        $this->assertClassImplements(CompilerPassInterface::class, BuildProcessorRegistryPass::class);
+    }
+
+    public function testShouldBeFinal()
+    {
+        $this->assertClassFinal(BuildProcessorRegistryPass::class);
+    }
+
+    public function testCouldBeConstructedWithName()
+    {
+        $pass = new BuildProcessorRegistryPass('aName');
+
+        $this->assertAttributeSame('aName', 'name', $pass);
+    }
+
+    public function testThrowIfNameEmptyOnConstruct()
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The name could not be empty.');
+        new BuildProcessorRegistryPass('');
+    }
+
+    public function testShouldDoNothingIfRouteCollectionServiceIsNotRegistered()
+    {
+        $container = new ContainerBuilder();
+
+        //guard
+        $this->assertFalse($container->hasDefinition('enqueue.client.aName.route_collection'));
+
+        $pass = new BuildProcessorRegistryPass('aName');
+        $pass->process($container);
+    }
+
+    public function testShouldDoNothingIfProcessorRegistryCollectionServiceIsNotRegistered()
+    {
+        $container = new ContainerBuilder();
+        $container->register('enqueue.client.aName.route_collection');
+
+        //guard
+        $this->assertFalse($container->hasDefinition('enqueue.client.aName.processor_registry'));
+
+        $pass = new BuildProcessorRegistryPass('aName');
+        $pass->process($container);
+    }
+
+    public function testThrowIfProcessorServiceIdOptionNotSet()
+    {
+        $container = new ContainerBuilder();
+        $container->register('enqueue.client.aName.route_collection')->addArgument([
+            (new Route('aCommand', Route::COMMAND, 'aProcessor'))->toArray(),
+        ]);
+        $container->register('enqueue.client.aName.processor_registry')->addArgument([]);
+
+        $pass = new BuildProcessorRegistryPass('aName');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The route option "processor_service_id" is required');
+        $pass->process($container);
+    }
+
+    public function testShouldSetProcessorsMapToRegistryAsFirstArgument()
+    {
+        $registry = new Definition();
+        $registry->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->register('enqueue.client.aName.route_collection')->addArgument([
+            (new Route(
+                'aCommand',
+                Route::COMMAND,
+                'aBarProcessor',
+                ['processor_service_id' => 'aBarServiceId']
+            ))->toArray(),
+            (new Route(
+                'aTopic',
+                Route::TOPIC,
+                'aFooProcessor',
+                ['processor_service_id' => 'aFooServiceId']
+            ))->toArray(),
+        ]);
+        $container->setDefinition('enqueue.client.aName.processor_registry', $registry);
+
+        $pass = new BuildProcessorRegistryPass('aName');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $registry->getArgument(0));
+        $this->assertEquals([
+            'aBarProcessor' => 'aBarServiceId',
+            'aFooProcessor' => 'aFooServiceId',
+        ], $registry->getArgument(0));
+    }
+
+    public function testShouldMergeWithAddedPreviously()
+    {
+        $registry = new Definition();
+        $registry->addArgument([
+            'aBarProcessor' => 'aBarServiceIdAddedPreviously',
+            'aOloloProcessor' => 'aOloloServiceIdAddedPreviously',
+        ]);
+
+        $container = new ContainerBuilder();
+        $container->register('enqueue.client.aName.route_collection')->addArgument([
+            (new Route(
+                'aCommand',
+                Route::COMMAND,
+                'aBarProcessor',
+                ['processor_service_id' => 'aBarServiceId']
+            ))->toArray(),
+            (new Route(
+                'aTopic',
+                Route::TOPIC,
+                'aFooProcessor',
+                ['processor_service_id' => 'aFooServiceId']
+            ))->toArray(),
+        ]);
+        $container->setDefinition('enqueue.client.aName.processor_registry', $registry);
+
+        $pass = new BuildProcessorRegistryPass('aName');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $registry->getArgument(0));
+        $this->assertEquals([
+            'aOloloProcessor' => 'aOloloServiceIdAddedPreviously',
+            'aBarProcessor' => 'aBarServiceId',
+            'aFooProcessor' => 'aFooServiceId',
+        ], $registry->getArgument(0));
+    }
+}
diff --git a/Tests/Symfony/DependencyInjection/BuildProcessorRoutesPassTest.php b/Tests/Symfony/DependencyInjection/BuildProcessorRoutesPassTest.php
new file mode 100644
index 0000000..517f98b
--- /dev/null
+++ b/Tests/Symfony/DependencyInjection/BuildProcessorRoutesPassTest.php
@@ -0,0 +1,282 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\DependencyInjection;
+
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
+use Enqueue\Symfony\DependencyInjection\BuildProcessorRoutesPass;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+
+class BuildProcessorRoutesPassTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementCompilerPassInterface()
+    {
+        $this->assertClassImplements(CompilerPassInterface::class, BuildProcessorRoutesPass::class);
+    }
+
+    public function testShouldBeFinal()
+    {
+        $this->assertClassFinal(BuildProcessorRoutesPass::class);
+    }
+
+    public function testCouldBeConstructedWithName()
+    {
+        $pass = new BuildProcessorRoutesPass('aName');
+
+        $this->assertAttributeSame('aName', 'name', $pass);
+    }
+
+    public function testThrowIfNameEmptyOnConstruct()
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The name could not be empty.');
+        new BuildProcessorRoutesPass('');
+    }
+
+    public function testShouldDoNothingIfRouteCollectionServiceIsNotRegistered()
+    {
+        $pass = new BuildProcessorRoutesPass('aName');
+        $pass->process(new ContainerBuilder());
+    }
+
+    public function testThrowIfBothTopicAndCommandAttributesAreSet()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', 'aProcessorClass')
+            ->addTag('enqueue.processor', ['topic' => 'foo', 'command' => 'bar'])
+        ;
+
+        $pass = new BuildProcessorRoutesPass('default');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Either "topic" or "command" tag attribute must be set. Both are set.');
+        $pass->process($container);
+    }
+
+    public function testThrowIfNeitherTopicNorCommandAttributesAreSet()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', 'aProcessorClass')
+            ->addTag('enqueue.processor', [])
+        ;
+
+        $pass = new BuildProcessorRoutesPass('default');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Either "topic" or "command" tag attribute must be set. None is set.');
+        $pass->process($container);
+    }
+
+    public function testShouldRegisterProcessorWithMatchedName()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
+        $container->register('aFooProcessor', 'aProcessorClass')
+            ->addTag('enqueue.processor', ['client' => 'foo', 'topic' => 'foo'])
+        ;
+        $container->register('aProcessor', 'aProcessorClass')
+            ->addTag('enqueue.processor', ['client' => 'bar', 'command' => 'foo'])
+        ;
+
+        $pass = new BuildProcessorRoutesPass('foo');
+
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(1, $routeCollection->getArgument(0));
+    }
+
+    public function testShouldRegisterProcessorWithoutNameToDefaultClient()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', 'aProcessorClass')
+            ->addTag('enqueue.processor', ['topic' => 'foo'])
+        ;
+        $container->register('aProcessor', 'aProcessorClass')
+            ->addTag('enqueue.processor', ['client' => 'bar', 'command' => 'foo'])
+        ;
+
+        $pass = new BuildProcessorRoutesPass('default');
+
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(1, $routeCollection->getArgument(0));
+    }
+
+    public function testShouldRegisterProcessorIfClientNameEqualsAll()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', 'aProcessorClass')
+            ->addTag('enqueue.processor', ['client' => 'all', 'topic' => 'foo'])
+        ;
+        $container->register('aProcessor', 'aProcessorClass')
+            ->addTag('enqueue.processor', ['client' => 'bar', 'command' => 'foo'])
+        ;
+
+        $pass = new BuildProcessorRoutesPass('default');
+
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(1, $routeCollection->getArgument(0));
+    }
+
+    public function testShouldRegisterAsTopicProcessor()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', 'aProcessorClass')
+            ->addTag('enqueue.processor', ['topic' => 'aTopic'])
+        ;
+
+        $pass = new BuildProcessorRoutesPass('default');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(1, $routeCollection->getArgument(0));
+
+        $this->assertEquals(
+            [
+                [
+                    'source' => 'aTopic',
+                    'source_type' => 'enqueue.client.topic_route',
+                    'processor' => 'aFooProcessor',
+                    'processor_service_id' => 'aFooProcessor',
+                ],
+            ],
+            $routeCollection->getArgument(0)
+        );
+    }
+
+    public function testShouldRegisterAsCommandProcessor()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', 'aProcessorClass')
+            ->addTag('enqueue.processor', ['command' => 'aCommand'])
+        ;
+
+        $pass = new BuildProcessorRoutesPass('default');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(1, $routeCollection->getArgument(0));
+
+        $this->assertEquals(
+            [
+                [
+                    'source' => 'aCommand',
+                    'source_type' => 'enqueue.client.command_route',
+                    'processor' => 'aFooProcessor',
+                    'processor_service_id' => 'aFooProcessor',
+                ],
+            ],
+            $routeCollection->getArgument(0)
+        );
+    }
+
+    public function testShouldRegisterWithCustomProcessorName()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', 'aProcessorClass')
+            ->addTag('enqueue.processor', ['command' => 'aCommand', 'processor' => 'customProcessorName'])
+        ;
+
+        $pass = new BuildProcessorRoutesPass('default');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(1, $routeCollection->getArgument(0));
+
+        $this->assertEquals(
+            [
+                [
+                    'source' => 'aCommand',
+                    'source_type' => 'enqueue.client.command_route',
+                    'processor' => 'customProcessorName',
+                    'processor_service_id' => 'aFooProcessor',
+                ],
+            ],
+            $routeCollection->getArgument(0)
+        );
+    }
+
+    public function testShouldMergeExtractedRoutesWithAlreadySetInCollection()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([
+            (new Route('aTopic', Route::TOPIC, 'aProcessor'))->toArray(),
+            (new Route('aCommand', Route::COMMAND, 'aProcessor'))->toArray(),
+        ]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', 'aProcessorClass')
+            ->addTag('enqueue.processor', ['command' => 'fooCommand'])
+        ;
+
+        $pass = new BuildProcessorRoutesPass('default');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(3, $routeCollection->getArgument(0));
+
+        $this->assertEquals(
+            [
+                [
+                    'source' => 'aTopic',
+                    'source_type' => 'enqueue.client.topic_route',
+                    'processor' => 'aProcessor',
+                ],
+                [
+                    'source' => 'aCommand',
+                    'source_type' => 'enqueue.client.command_route',
+                    'processor' => 'aProcessor',
+                ],
+                [
+                    'source' => 'fooCommand',
+                    'source_type' => 'enqueue.client.command_route',
+                    'processor' => 'aFooProcessor',
+                    'processor_service_id' => 'aFooProcessor',
+                ],
+            ],
+            $routeCollection->getArgument(0)
+        );
+    }
+}
diff --git a/Tests/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php b/Tests/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
new file mode 100644
index 0000000..af3b0b1
--- /dev/null
+++ b/Tests/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
@@ -0,0 +1,353 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\DependencyInjection;
+
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
+use Enqueue\Client\TopicSubscriberInterface;
+use Enqueue\Symfony\DependencyInjection\BuildTopicSubscriberRoutesPass;
+use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrContext;
+use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrProcessor;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+
+class BuildTopicSubscriberRoutesPassTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementCompilerPassInterface()
+    {
+        $this->assertClassImplements(CompilerPassInterface::class, BuildTopicSubscriberRoutesPass::class);
+    }
+
+    public function testShouldBeFinal()
+    {
+        $this->assertClassFinal(BuildTopicSubscriberRoutesPass::class);
+    }
+
+    public function testCouldBeConstructedWithName()
+    {
+        $pass = new BuildTopicSubscriberRoutesPass('aName');
+
+        $this->assertAttributeSame('aName', 'name', $pass);
+    }
+
+    public function testThrowIfNameEmptyOnConstruct()
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The name could not be empty.');
+        new BuildTopicSubscriberRoutesPass('');
+    }
+
+    public function testShouldDoNothingIfRouteCollectionServiceIsNotRegistered()
+    {
+        $pass = new BuildTopicSubscriberRoutesPass('aName');
+        $pass->process(new ContainerBuilder());
+    }
+
+    public function testThrowIfTaggedProcessorIsBuiltByFactory()
+    {
+        $container = new ContainerBuilder();
+        $container->register('enqueue.client.aName.route_collection', RouteCollection::class)
+            ->addArgument([])
+        ;
+        $container->register('aProcessor', PsrProcessor::class)
+            ->setFactory('foo')
+            ->addTag('enqueue.topic_subscriber')
+        ;
+
+        $pass = new BuildTopicSubscriberRoutesPass('aName');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The topic subscriber tag could not be applied to a service created by factory.');
+        $pass->process($container);
+    }
+
+    public function testShouldRegisterProcessorWithMatchedName()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($this->createTopicSubscriberProcessor()))
+            ->addTag('enqueue.topic_subscriber', ['client' => 'foo'])
+        ;
+        $container->register('aProcessor', get_class($this->createTopicSubscriberProcessor()))
+            ->addTag('enqueue.topic_subscriber', ['client' => 'bar'])
+        ;
+
+        $pass = new BuildTopicSubscriberRoutesPass('foo');
+
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(1, $routeCollection->getArgument(0));
+    }
+
+    public function testShouldRegisterProcessorWithoutNameToDefaultClient()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($this->createTopicSubscriberProcessor()))
+            ->addTag('enqueue.topic_subscriber')
+        ;
+        $container->register('aProcessor', get_class($this->createTopicSubscriberProcessor()))
+            ->addTag('enqueue.topic_subscriber', ['client' => 'bar'])
+        ;
+
+        $pass = new BuildTopicSubscriberRoutesPass('default');
+
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(1, $routeCollection->getArgument(0));
+    }
+
+    public function testShouldRegisterProcessorIfClientNameEqualsAll()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($this->createTopicSubscriberProcessor()))
+            ->addTag('enqueue.topic_subscriber', ['client' => 'all'])
+        ;
+        $container->register('aProcessor', get_class($this->createTopicSubscriberProcessor()))
+            ->addTag('enqueue.topic_subscriber', ['client' => 'bar'])
+        ;
+
+        $pass = new BuildTopicSubscriberRoutesPass('default');
+
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(1, $routeCollection->getArgument(0));
+    }
+
+    public function testShouldRegisterProcessorIfTopicsIsString()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $processor = $this->createTopicSubscriberProcessor('fooTopic');
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($processor))
+            ->addTag('enqueue.topic_subscriber')
+        ;
+
+        $pass = new BuildTopicSubscriberRoutesPass('default');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(1, $routeCollection->getArgument(0));
+
+        $this->assertEquals(
+            [
+                [
+                    'source' => 'fooTopic',
+                    'source_type' => 'enqueue.client.topic_route',
+                    'processor' => 'aFooProcessor',
+                    'processor_service_id' => 'aFooProcessor',
+                ],
+            ],
+            $routeCollection->getArgument(0)
+        );
+    }
+
+    public function testThrowIfTopicSubscriberReturnsNothing()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $processor = $this->createTopicSubscriberProcessor(null);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($processor))
+            ->addTag('enqueue.topic_subscriber')
+        ;
+
+        $pass = new BuildTopicSubscriberRoutesPass('default');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Topic subscriber must return something.');
+        $pass->process($container);
+    }
+
+    public function testShouldRegisterProcessorIfTopicsAreStrings()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $processor = $this->createTopicSubscriberProcessor(['fooTopic', 'barTopic']);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($processor))
+            ->addTag('enqueue.topic_subscriber')
+        ;
+
+        $pass = new BuildTopicSubscriberRoutesPass('default');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(2, $routeCollection->getArgument(0));
+
+        $this->assertEquals(
+            [
+                [
+                    'source' => 'fooTopic',
+                    'source_type' => 'enqueue.client.topic_route',
+                    'processor' => 'aFooProcessor',
+                    'processor_service_id' => 'aFooProcessor',
+                ],
+                [
+                    'source' => 'barTopic',
+                    'source_type' => 'enqueue.client.topic_route',
+                    'processor' => 'aFooProcessor',
+                    'processor_service_id' => 'aFooProcessor',
+                ],
+            ],
+            $routeCollection->getArgument(0)
+        );
+    }
+
+    public function testShouldRegisterProcessorIfTopicsAreParamArrays()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $processor = $this->createTopicSubscriberProcessor([
+            ['topic' => 'fooTopic', 'processor' => 'aCustomFooProcessorName', 'anOption' => 'aFooVal'],
+            ['topic' => 'barTopic', 'processor' => 'aCustomBarProcessorName', 'anOption' => 'aBarVal'],
+        ]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($processor))
+            ->addTag('enqueue.topic_subscriber')
+        ;
+
+        $pass = new BuildTopicSubscriberRoutesPass('default');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(2, $routeCollection->getArgument(0));
+
+        $this->assertEquals(
+            [
+                [
+                    'source' => 'fooTopic',
+                    'source_type' => 'enqueue.client.topic_route',
+                    'processor' => 'aCustomFooProcessorName',
+                    'processor_service_id' => 'aFooProcessor',
+                    'anOption' => 'aFooVal',
+                ],
+                [
+                    'source' => 'barTopic',
+                    'source_type' => 'enqueue.client.topic_route',
+                    'processor' => 'aCustomBarProcessorName',
+                    'processor_service_id' => 'aFooProcessor',
+                    'anOption' => 'aBarVal',
+                ],
+            ],
+            $routeCollection->getArgument(0)
+        );
+    }
+
+    public function testThrowIfTopicSubscriberParamsInvalid()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $processor = $this->createTopicSubscriberProcessor(['fooBar', true]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($processor))
+            ->addTag('enqueue.topic_subscriber')
+        ;
+
+        $pass = new BuildTopicSubscriberRoutesPass('default');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Topic subscriber configuration is invalid');
+        $pass->process($container);
+    }
+
+    public function testShouldMergeExtractedRoutesWithAlreadySetInCollection()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([
+            (new Route('aTopic', Route::TOPIC, 'aProcessor'))->toArray(),
+            (new Route('aCommand', Route::COMMAND, 'aProcessor'))->toArray(),
+        ]);
+
+        $processor = $this->createTopicSubscriberProcessor(['fooTopic']);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($processor))
+            ->addTag('enqueue.topic_subscriber')
+        ;
+
+        $pass = new BuildTopicSubscriberRoutesPass('default');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(3, $routeCollection->getArgument(0));
+
+        $this->assertEquals(
+            [
+                [
+                    'source' => 'aTopic',
+                    'source_type' => 'enqueue.client.topic_route',
+                    'processor' => 'aProcessor',
+                ],
+                [
+                    'source' => 'aCommand',
+                    'source_type' => 'enqueue.client.command_route',
+                    'processor' => 'aProcessor',
+                ],
+                [
+                    'source' => 'fooTopic',
+                    'source_type' => 'enqueue.client.topic_route',
+                    'processor' => 'aFooProcessor',
+                    'processor_service_id' => 'aFooProcessor',
+                ],
+            ],
+            $routeCollection->getArgument(0)
+        );
+    }
+
+    private function createTopicSubscriberProcessor($topicSubscriberReturns = ['aTopic'])
+    {
+        $processor = new class() implements PsrProcessor, TopicSubscriberInterface {
+            public static $return;
+
+            public function process(PsrMessage $message, PsrContext $context)
+            {
+            }
+
+            public static function getSubscribedTopics()
+            {
+                return static::$return;
+            }
+        };
+
+        $processor::$return = $topicSubscriberReturns;
+
+        return $processor;
+    }
+}

From 9f6f072a1530758ff26341703b071ee61e94f6fb Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 14 Sep 2018 10:43:45 +0300
Subject: [PATCH 105/337] [client] Add typehints.

---
 Client/Driver/AmqpDriver.php                  | 43 ++++--------------
 Client/Driver/DbalDriver.php                  | 44 +++++-------------
 Client/Driver/FsDriver.php                    | 43 ++++--------------
 Client/Driver/GpsDriver.php                   | 43 ++++--------------
 Client/Driver/MongodbDriver.php               | 44 +++++-------------
 Client/Driver/NullDriver.php                  | 39 +++++-----------
 Client/Driver/RabbitMqDriver.php              | 23 +++-------
 Client/Driver/RabbitMqStompDriver.php         | 30 ++++---------
 Client/Driver/RdKafkaDriver.php               | 43 ++++++------------
 Client/Driver/RedisDriver.php                 | 38 ++++------------
 Client/Driver/SqsDriver.php                   | 38 ++++------------
 Client/Driver/StompDriver.php                 | 43 +++++-------------
 Client/Driver/StompManagementClient.php       | 43 +++---------------
 Client/DriverInterface.php                    | 45 +++++--------------
 Tests/Client/Driver/SqsDriverTest.php         | 24 ++++------
 .../Driver/StompManagementClientTest.php      |  3 ++
 16 files changed, 139 insertions(+), 447 deletions(-)

diff --git a/Client/Driver/AmqpDriver.php b/Client/Driver/AmqpDriver.php
index c2d274a..714aab6 100644
--- a/Client/Driver/AmqpDriver.php
+++ b/Client/Driver/AmqpDriver.php
@@ -12,6 +12,7 @@
 use Interop\Amqp\AmqpTopic;
 use Interop\Amqp\Impl\AmqpBind;
 use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -32,11 +33,6 @@ class AmqpDriver implements DriverInterface
      */
     private $queueMetaRegistry;
 
-    /**
-     * @param AmqpContext       $context
-     * @param Config            $config
-     * @param QueueMetaRegistry $queueMetaRegistry
-     */
     public function __construct(AmqpContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
     {
         $this->context = $context;
@@ -44,10 +40,7 @@ public function __construct(AmqpContext $context, Config $config, QueueMetaRegis
         $this->queueMetaRegistry = $queueMetaRegistry;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToRouter(Message $message)
+    public function sendToRouter(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
             throw new \LogicException('Topic name parameter is required but is not set');
@@ -59,10 +52,7 @@ public function sendToRouter(Message $message)
         $this->context->createProducer()->send($topic, $transportMessage);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToProcessor(Message $message)
+    public function sendToProcessor(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
             throw new \LogicException('Processor name parameter is required but is not set');
@@ -78,10 +68,7 @@ public function sendToProcessor(Message $message)
         $this->context->createProducer()->send($destination, $transportMessage);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function setupBroker(LoggerInterface $logger = null)
+    public function setupBroker(LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $log = function ($text, ...$args) use ($logger) {
@@ -109,11 +96,9 @@ public function setupBroker(LoggerInterface $logger = null)
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return AmqpQueue
      */
-    public function createQueue($queueName)
+    public function createQueue(string $queueName): PsrQueue
     {
         $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
 
@@ -124,11 +109,9 @@ public function createQueue($queueName)
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return AmqpMessage
      */
-    public function createTransportMessage(Message $message)
+    public function createTransportMessage(Message $message): PsrMessage
     {
         $headers = $message->getHeaders();
         $properties = $message->getProperties();
@@ -153,10 +136,8 @@ public function createTransportMessage(Message $message)
 
     /**
      * @param AmqpMessage $message
-     *
-     * {@inheritdoc}
      */
-    public function createClientMessage(PsrMessage $message)
+    public function createClientMessage(PsrMessage $message): Message
     {
         $clientMessage = new Message();
 
@@ -177,18 +158,12 @@ public function createClientMessage(PsrMessage $message)
         return $clientMessage;
     }
 
-    /**
-     * @return Config
-     */
-    public function getConfig()
+    public function getConfig(): Config
     {
         return $this->config;
     }
 
-    /**
-     * @return AmqpTopic
-     */
-    private function createRouterTopic()
+    private function createRouterTopic(): AmqpTopic
     {
         $topic = $this->context->createTopic(
             $this->config->createTransportRouterTopicName($this->config->getRouterTopicName())
diff --git a/Client/Driver/DbalDriver.php b/Client/Driver/DbalDriver.php
index 9b433db..d031bdb 100644
--- a/Client/Driver/DbalDriver.php
+++ b/Client/Driver/DbalDriver.php
@@ -8,8 +8,10 @@
 use Enqueue\Client\MessagePriority;
 use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\Dbal\DbalContext;
+use Enqueue\Dbal\DbalDestination;
 use Enqueue\Dbal\DbalMessage;
 use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -41,11 +43,6 @@ class DbalDriver implements DriverInterface
         MessagePriority::VERY_HIGH => 4,
     ];
 
-    /**
-     * @param DbalContext       $context
-     * @param Config            $config
-     * @param QueueMetaRegistry $queueMetaRegistry
-     */
     public function __construct(DbalContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
     {
         $this->context = $context;
@@ -54,11 +51,9 @@ public function __construct(DbalContext $context, Config $config, QueueMetaRegis
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return DbalMessage
      */
-    public function createTransportMessage(Message $message)
+    public function createTransportMessage(Message $message): PsrMessage
     {
         $properties = $message->getProperties();
 
@@ -88,10 +83,8 @@ public function createTransportMessage(Message $message)
 
     /**
      * @param DbalMessage $message
-     *
-     * {@inheritdoc}
      */
-    public function createClientMessage(PsrMessage $message)
+    public function createClientMessage(PsrMessage $message): Message
     {
         $clientMessage = new Message();
 
@@ -120,10 +113,7 @@ public function createClientMessage(PsrMessage $message)
         return $clientMessage;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToRouter(Message $message)
+    public function sendToRouter(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
             throw new \LogicException('Topic name parameter is required but is not set');
@@ -135,10 +125,7 @@ public function sendToRouter(Message $message)
         $this->context->createProducer()->send($queue, $transportMessage);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToProcessor(Message $message)
+    public function sendToProcessor(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
             throw new \LogicException('Processor name parameter is required but is not set');
@@ -155,19 +142,16 @@ public function sendToProcessor(Message $message)
     }
 
     /**
-     * {@inheritdoc}
+     * @return DbalDestination
      */
-    public function createQueue($queueName)
+    public function createQueue(string $queueName): PsrQueue
     {
         $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
 
         return $this->context->createQueue($transportName);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function setupBroker(LoggerInterface $logger = null)
+    public function setupBroker(LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $log = function ($text, ...$args) use ($logger) {
@@ -178,18 +162,12 @@ public function setupBroker(LoggerInterface $logger = null)
         $this->context->createDataBaseTable();
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getConfig()
+    public function getConfig(): Config
     {
         return $this->config;
     }
 
-    /**
-     * @return array
-     */
-    public static function getPriorityMap()
+    public static function getPriorityMap(): array
     {
         return self::$priorityMap;
     }
diff --git a/Client/Driver/FsDriver.php b/Client/Driver/FsDriver.php
index ea91989..76c22b1 100644
--- a/Client/Driver/FsDriver.php
+++ b/Client/Driver/FsDriver.php
@@ -11,6 +11,7 @@
 use Enqueue\Fs\FsDestination;
 use Enqueue\Fs\FsMessage;
 use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -31,11 +32,6 @@ class FsDriver implements DriverInterface
      */
     private $queueMetaRegistry;
 
-    /**
-     * @param FsContext         $context
-     * @param Config            $config
-     * @param QueueMetaRegistry $queueMetaRegistry
-     */
     public function __construct(FsContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
     {
         $this->context = $context;
@@ -43,10 +39,7 @@ public function __construct(FsContext $context, Config $config, QueueMetaRegistr
         $this->queueMetaRegistry = $queueMetaRegistry;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToRouter(Message $message)
+    public function sendToRouter(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
             throw new \LogicException('Topic name parameter is required but is not set');
@@ -58,10 +51,7 @@ public function sendToRouter(Message $message)
         $this->context->createProducer()->send($topic, $transportMessage);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToProcessor(Message $message)
+    public function sendToProcessor(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
             throw new \LogicException('Processor name parameter is required but is not set');
@@ -77,10 +67,7 @@ public function sendToProcessor(Message $message)
         $this->context->createProducer()->send($destination, $transportMessage);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function setupBroker(LoggerInterface $logger = null)
+    public function setupBroker(LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $log = function ($text, ...$args) use ($logger) {
@@ -107,11 +94,9 @@ public function setupBroker(LoggerInterface $logger = null)
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return FsDestination
      */
-    public function createQueue($queueName)
+    public function createQueue(string $queueName): PsrQueue
     {
         $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
 
@@ -119,11 +104,9 @@ public function createQueue($queueName)
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return FsMessage
      */
-    public function createTransportMessage(Message $message)
+    public function createTransportMessage(Message $message): PsrMessage
     {
         $properties = $message->getProperties();
 
@@ -144,10 +127,8 @@ public function createTransportMessage(Message $message)
 
     /**
      * @param FsMessage $message
-     *
-     * {@inheritdoc}
      */
-    public function createClientMessage(PsrMessage $message)
+    public function createClientMessage(PsrMessage $message): Message
     {
         $clientMessage = new Message();
 
@@ -165,18 +146,12 @@ public function createClientMessage(PsrMessage $message)
         return $clientMessage;
     }
 
-    /**
-     * @return Config
-     */
-    public function getConfig()
+    public function getConfig(): Config
     {
         return $this->config;
     }
 
-    /**
-     * @return FsDestination
-     */
-    private function createRouterTopic()
+    private function createRouterTopic(): FsDestination
     {
         return $this->context->createTopic(
             $this->config->createTransportQueueName($this->config->getRouterTopicName())
diff --git a/Client/Driver/GpsDriver.php b/Client/Driver/GpsDriver.php
index aebb807..f45313e 100644
--- a/Client/Driver/GpsDriver.php
+++ b/Client/Driver/GpsDriver.php
@@ -11,6 +11,7 @@
 use Enqueue\Gps\GpsQueue;
 use Enqueue\Gps\GpsTopic;
 use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -31,11 +32,6 @@ class GpsDriver implements DriverInterface
      */
     private $queueMetaRegistry;
 
-    /**
-     * @param GpsContext        $context
-     * @param Config            $config
-     * @param QueueMetaRegistry $queueMetaRegistry
-     */
     public function __construct(GpsContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
     {
         $this->context = $context;
@@ -43,10 +39,7 @@ public function __construct(GpsContext $context, Config $config, QueueMetaRegist
         $this->queueMetaRegistry = $queueMetaRegistry;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToRouter(Message $message)
+    public function sendToRouter(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
             throw new \LogicException('Topic name parameter is required but is not set');
@@ -58,10 +51,7 @@ public function sendToRouter(Message $message)
         $this->context->createProducer()->send($topic, $transportMessage);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToProcessor(Message $message)
+    public function sendToProcessor(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
             throw new \LogicException('Processor name parameter is required but is not set');
@@ -79,10 +69,7 @@ public function sendToProcessor(Message $message)
         $this->context->createProducer()->send($destination, $transportMessage);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function setupBroker(LoggerInterface $logger = null)
+    public function setupBroker(LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $log = function ($text, ...$args) use ($logger) {
@@ -107,11 +94,9 @@ public function setupBroker(LoggerInterface $logger = null)
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return GpsQueue
      */
-    public function createQueue($queueName)
+    public function createQueue(string $queueName): PsrQueue
     {
         $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
 
@@ -119,11 +104,9 @@ public function createQueue($queueName)
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return GpsMessage
      */
-    public function createTransportMessage(Message $message)
+    public function createTransportMessage(Message $message): PsrMessage
     {
         $headers = $message->getHeaders();
         $properties = $message->getProperties();
@@ -142,10 +125,8 @@ public function createTransportMessage(Message $message)
 
     /**
      * @param GpsMessage $message
-     *
-     * {@inheritdoc}
      */
-    public function createClientMessage(PsrMessage $message)
+    public function createClientMessage(PsrMessage $message): Message
     {
         $clientMessage = new Message();
 
@@ -160,18 +141,12 @@ public function createClientMessage(PsrMessage $message)
         return $clientMessage;
     }
 
-    /**
-     * @return Config
-     */
-    public function getConfig()
+    public function getConfig(): Config
     {
         return $this->config;
     }
 
-    /**
-     * @return GpsTopic
-     */
-    private function createRouterTopic()
+    private function createRouterTopic(): GpsTopic
     {
         $topic = $this->context->createTopic(
             $this->config->createTransportRouterTopicName($this->config->getRouterTopicName())
diff --git a/Client/Driver/MongodbDriver.php b/Client/Driver/MongodbDriver.php
index b2e3ca3..9e53946 100644
--- a/Client/Driver/MongodbDriver.php
+++ b/Client/Driver/MongodbDriver.php
@@ -8,8 +8,10 @@
 use Enqueue\Client\MessagePriority;
 use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\Mongodb\MongodbContext;
+use Enqueue\Mongodb\MongodbDestination;
 use Enqueue\Mongodb\MongodbMessage;
 use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -41,11 +43,6 @@ class MongodbDriver implements DriverInterface
         MessagePriority::VERY_HIGH => 4,
     ];
 
-    /**
-     * @param MongodbContext    $context
-     * @param Config            $config
-     * @param QueueMetaRegistry $queueMetaRegistry
-     */
     public function __construct(MongodbContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
     {
         $this->context = $context;
@@ -54,11 +51,9 @@ public function __construct(MongodbContext $context, Config $config, QueueMetaRe
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return MongodbMessage
      */
-    public function createTransportMessage(Message $message)
+    public function createTransportMessage(Message $message): PsrMessage
     {
         $properties = $message->getProperties();
 
@@ -83,10 +78,8 @@ public function createTransportMessage(Message $message)
 
     /**
      * @param MongodbMessage $message
-     *
-     * {@inheritdoc}
      */
-    public function createClientMessage(PsrMessage $message)
+    public function createClientMessage(PsrMessage $message): Message
     {
         $clientMessage = new Message();
 
@@ -110,10 +103,7 @@ public function createClientMessage(PsrMessage $message)
         return $clientMessage;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToRouter(Message $message)
+    public function sendToRouter(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
             throw new \LogicException('Topic name parameter is required but is not set');
@@ -125,10 +115,7 @@ public function sendToRouter(Message $message)
         $this->context->createProducer()->send($queue, $transportMessage);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToProcessor(Message $message)
+    public function sendToProcessor(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
             throw new \LogicException('Processor name parameter is required but is not set');
@@ -145,19 +132,16 @@ public function sendToProcessor(Message $message)
     }
 
     /**
-     * {@inheritdoc}
+     * @return MongodbDestination
      */
-    public function createQueue($queueName)
+    public function createQueue(string $queueName): PsrQueue
     {
         $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
 
         return $this->context->createQueue($transportName);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function setupBroker(LoggerInterface $logger = null)
+    public function setupBroker(LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $log = function ($text, ...$args) use ($logger) {
@@ -168,18 +152,12 @@ public function setupBroker(LoggerInterface $logger = null)
         $this->context->createCollection();
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getConfig()
+    public function getConfig(): Config
     {
         return $this->config;
     }
 
-    /**
-     * @return array
-     */
-    public static function getPriorityMap()
+    public static function getPriorityMap(): array
     {
         return self::$priorityMap;
     }
diff --git a/Client/Driver/NullDriver.php b/Client/Driver/NullDriver.php
index 830df17..41a9366 100644
--- a/Client/Driver/NullDriver.php
+++ b/Client/Driver/NullDriver.php
@@ -8,7 +8,9 @@
 use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\Null\NullContext;
 use Enqueue\Null\NullMessage;
+use Enqueue\Null\NullQueue;
 use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -29,11 +31,6 @@ class NullDriver implements DriverInterface
      */
     private $queueMetaRegistry;
 
-    /**
-     * @param NullContext       $context
-     * @param Config            $config
-     * @param QueueMetaRegistry $queueMetaRegistry
-     */
     public function __construct(NullContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
     {
         $this->context = $context;
@@ -42,11 +39,9 @@ public function __construct(NullContext $context, Config $config, QueueMetaRegis
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return NullMessage
      */
-    public function createTransportMessage(Message $message)
+    public function createTransportMessage(Message $message): PsrMessage
     {
         $headers = $message->getHeaders();
         $headers['content_type'] = $message->getContentType();
@@ -67,11 +62,9 @@ public function createTransportMessage(Message $message)
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @param NullMessage $message
      */
-    public function createClientMessage(PsrMessage $message)
+    public function createClientMessage(PsrMessage $message): Message
     {
         $clientMessage = new Message();
         $clientMessage->setBody($message->getBody());
@@ -102,27 +95,21 @@ public function createClientMessage(PsrMessage $message)
     }
 
     /**
-     * {@inheritdoc}
+     * @return NullQueue
      */
-    public function createQueue($queueName)
+    public function createQueue(string $queueName): PsrQueue
     {
         $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
 
         return $this->context->createQueue($transportName);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getConfig()
+    public function getConfig(): Config
     {
         return $this->config;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToRouter(Message $message)
+    public function sendToRouter(Message $message): void
     {
         $transportMessage = $this->createTransportMessage($message);
         $topic = $this->context->createTopic(
@@ -134,10 +121,7 @@ public function sendToRouter(Message $message)
         $this->context->createProducer()->send($topic, $transportMessage);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToProcessor(Message $message)
+    public function sendToProcessor(Message $message): void
     {
         $transportMessage = $this->createTransportMessage($message);
         $queue = $this->context->createQueue(
@@ -149,10 +133,7 @@ public function sendToProcessor(Message $message)
         $this->context->createProducer()->send($queue, $transportMessage);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function setupBroker(LoggerInterface $logger = null)
+    public function setupBroker(LoggerInterface $logger = null): void
     {
         $logger ?: new NullLogger();
         $logger->debug('[NullDriver] setup broker');
diff --git a/Client/Driver/RabbitMqDriver.php b/Client/Driver/RabbitMqDriver.php
index dec197a..ac9eb14 100644
--- a/Client/Driver/RabbitMqDriver.php
+++ b/Client/Driver/RabbitMqDriver.php
@@ -11,6 +11,7 @@
 use Interop\Amqp\AmqpMessage;
 use Interop\Amqp\AmqpQueue;
 use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrQueue;
 
 class RabbitMqDriver extends AmqpDriver
 {
@@ -34,11 +35,6 @@ class RabbitMqDriver extends AmqpDriver
      */
     private $priorityMap;
 
-    /**
-     * @param AmqpContext       $context
-     * @param Config            $config
-     * @param QueueMetaRegistry $queueMetaRegistry
-     */
     public function __construct(AmqpContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
     {
         parent::__construct($context, $config, $queueMetaRegistry);
@@ -56,10 +52,7 @@ public function __construct(AmqpContext $context, Config $config, QueueMetaRegis
         ];
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToProcessor(Message $message)
+    public function sendToProcessor(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
             throw new \LogicException('Processor name parameter is required but is not set');
@@ -81,11 +74,9 @@ public function sendToProcessor(Message $message)
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return AmqpQueue
      */
-    public function createQueue($queueName)
+    public function createQueue(string $queueName): PsrQueue
     {
         $queue = parent::createQueue($queueName);
         $queue->setArguments(['x-max-priority' => 4]);
@@ -94,11 +85,9 @@ public function createQueue($queueName)
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return AmqpMessage
      */
-    public function createTransportMessage(Message $message)
+    public function createTransportMessage(Message $message): PsrMessage
     {
         $transportMessage = parent::createTransportMessage($message);
 
@@ -126,10 +115,8 @@ public function createTransportMessage(Message $message)
 
     /**
      * @param AmqpMessage $message
-     *
-     * {@inheritdoc}
      */
-    public function createClientMessage(PsrMessage $message)
+    public function createClientMessage(PsrMessage $message): Message
     {
         $clientMessage = parent::createClientMessage($message);
 
diff --git a/Client/Driver/RabbitMqStompDriver.php b/Client/Driver/RabbitMqStompDriver.php
index 5d4ae1b..a265f66 100644
--- a/Client/Driver/RabbitMqStompDriver.php
+++ b/Client/Driver/RabbitMqStompDriver.php
@@ -10,6 +10,7 @@
 use Enqueue\Stomp\StompDestination;
 use Enqueue\Stomp\StompMessage;
 use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -59,11 +60,9 @@ public function __construct(StompContext $context, Config $config, QueueMetaRegi
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return StompMessage
      */
-    public function createTransportMessage(Message $message)
+    public function createTransportMessage(Message $message): PsrMessage
     {
         $transportMessage = parent::createTransportMessage($message);
 
@@ -92,10 +91,8 @@ public function createTransportMessage(Message $message)
 
     /**
      * @param StompMessage $message
-     *
-     * {@inheritdoc}
      */
-    public function createClientMessage(PsrMessage $message)
+    public function createClientMessage(PsrMessage $message): Message
     {
         $clientMessage = parent::createClientMessage($message);
 
@@ -134,10 +131,7 @@ public function createClientMessage(PsrMessage $message)
         return $clientMessage;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToProcessor(Message $message)
+    public function sendToProcessor(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
             throw new \LogicException('Processor name parameter is required but is not set');
@@ -158,9 +152,9 @@ public function sendToProcessor(Message $message)
     }
 
     /**
-     * {@inheritdoc}
+     * @return StompDestination
      */
-    public function createQueue($queueName)
+    public function createQueue(string $queueName): PsrQueue
     {
         $queue = parent::createQueue($queueName);
         $queue->setHeader('x-max-priority', 4);
@@ -168,10 +162,7 @@ public function createQueue($queueName)
         return $queue;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function setupBroker(LoggerInterface $logger = null)
+    public function setupBroker(LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $log = function ($text, ...$args) use ($logger) {
@@ -244,12 +235,7 @@ public function setupBroker(LoggerInterface $logger = null)
         }
     }
 
-    /**
-     * @param StompDestination $queue
-     *
-     * @return StompDestination
-     */
-    private function createDelayedTopic(StompDestination $queue)
+    private function createDelayedTopic(StompDestination $queue): StompDestination
     {
         // in order to use delay feature make sure the rabbitmq_delayed_message_exchange plugin is installed.
         $destination = $this->context->createTopic($queue->getStompName().'.delayed');
diff --git a/Client/Driver/RdKafkaDriver.php b/Client/Driver/RdKafkaDriver.php
index 6c606ff..7f536a7 100644
--- a/Client/Driver/RdKafkaDriver.php
+++ b/Client/Driver/RdKafkaDriver.php
@@ -7,7 +7,10 @@
 use Enqueue\Client\Message;
 use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\RdKafka\RdKafkaContext;
+use Enqueue\RdKafka\RdKafkaMessage;
+use Enqueue\RdKafka\RdKafkaTopic;
 use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -28,11 +31,6 @@ class RdKafkaDriver implements DriverInterface
      */
     private $queueMetaRegistry;
 
-    /**
-     * @param RdKafkaContext    $context
-     * @param Config            $config
-     * @param QueueMetaRegistry $queueMetaRegistry
-     */
     public function __construct(RdKafkaContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
     {
         $this->context = $context;
@@ -41,9 +39,9 @@ public function __construct(RdKafkaContext $context, Config $config, QueueMetaRe
     }
 
     /**
-     * {@inheritdoc}
+     * @return RdKafkaMessage
      */
-    public function createTransportMessage(Message $message)
+    public function createTransportMessage(Message $message): PsrMessage
     {
         $headers = $message->getHeaders();
         $headers['content_type'] = $message->getContentType();
@@ -60,10 +58,7 @@ public function createTransportMessage(Message $message)
         return $transportMessage;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function createClientMessage(PsrMessage $message)
+    public function createClientMessage(PsrMessage $message): Message
     {
         $clientMessage = new Message();
         $clientMessage->setBody($message->getBody());
@@ -80,10 +75,7 @@ public function createClientMessage(PsrMessage $message)
         return $clientMessage;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToRouter(Message $message)
+    public function sendToRouter(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
             throw new \LogicException('Topic name parameter is required but is not set');
@@ -95,10 +87,7 @@ public function sendToRouter(Message $message)
         $this->context->createProducer()->send($topic, $transportMessage);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToProcessor(Message $message)
+    public function sendToProcessor(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
             throw new \LogicException('Processor name parameter is required but is not set');
@@ -115,19 +104,16 @@ public function sendToProcessor(Message $message)
     }
 
     /**
-     * {@inheritdoc}
+     * @return RdKafkaTopic
      */
-    public function createQueue($queueName)
+    public function createQueue(string $queueName): PsrQueue
     {
         $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
 
         return $this->context->createQueue($transportName);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function setupBroker(LoggerInterface $logger = null)
+    public function setupBroker(LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $logger->debug('[RdKafkaDriver] setup broker');
@@ -148,15 +134,12 @@ public function setupBroker(LoggerInterface $logger = null)
         }
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getConfig()
+    public function getConfig(): Config
     {
         return $this->config;
     }
 
-    private function createRouterTopic()
+    private function createRouterTopic(): RdKafkaTopic
     {
         $topic = $this->context->createTopic(
             $this->config->createTransportRouterTopicName($this->config->getRouterTopicName())
diff --git a/Client/Driver/RedisDriver.php b/Client/Driver/RedisDriver.php
index 62a7383..10b9cb9 100644
--- a/Client/Driver/RedisDriver.php
+++ b/Client/Driver/RedisDriver.php
@@ -11,6 +11,7 @@
 use Enqueue\Redis\RedisDestination;
 use Enqueue\Redis\RedisMessage;
 use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 
 class RedisDriver implements DriverInterface
@@ -30,11 +31,6 @@ class RedisDriver implements DriverInterface
      */
     private $queueMetaRegistry;
 
-    /**
-     * @param RedisContext      $context
-     * @param Config            $config
-     * @param QueueMetaRegistry $queueMetaRegistry
-     */
     public function __construct(RedisContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
     {
         $this->context = $context;
@@ -42,10 +38,7 @@ public function __construct(RedisContext $context, Config $config, QueueMetaRegi
         $this->queueMetaRegistry = $queueMetaRegistry;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToRouter(Message $message)
+    public function sendToRouter(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
             throw new \LogicException('Topic name parameter is required but is not set');
@@ -57,10 +50,7 @@ public function sendToRouter(Message $message)
         $this->context->createProducer()->send($queue, $transportMessage);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToProcessor(Message $message)
+    public function sendToProcessor(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
             throw new \LogicException('Processor name parameter is required but is not set');
@@ -76,19 +66,14 @@ public function sendToProcessor(Message $message)
         $this->context->createProducer()->send($destination, $transportMessage);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function setupBroker(LoggerInterface $logger = null)
+    public function setupBroker(LoggerInterface $logger = null): void
     {
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return RedisDestination
      */
-    public function createQueue($queueName)
+    public function createQueue(string $queueName): PsrQueue
     {
         $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
 
@@ -96,11 +81,9 @@ public function createQueue($queueName)
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return RedisMessage
      */
-    public function createTransportMessage(Message $message)
+    public function createTransportMessage(Message $message): PsrMessage
     {
         $properties = $message->getProperties();
 
@@ -121,10 +104,8 @@ public function createTransportMessage(Message $message)
 
     /**
      * @param RedisMessage $message
-     *
-     * {@inheritdoc}
      */
-    public function createClientMessage(PsrMessage $message)
+    public function createClientMessage(PsrMessage $message): Message
     {
         $clientMessage = new Message();
 
@@ -142,10 +123,7 @@ public function createClientMessage(PsrMessage $message)
         return $clientMessage;
     }
 
-    /**
-     * @return Config
-     */
-    public function getConfig()
+    public function getConfig(): Config
     {
         return $this->config;
     }
diff --git a/Client/Driver/SqsDriver.php b/Client/Driver/SqsDriver.php
index 08a8a08..8aba2e1 100644
--- a/Client/Driver/SqsDriver.php
+++ b/Client/Driver/SqsDriver.php
@@ -11,6 +11,7 @@
 use Enqueue\Sqs\SqsDestination;
 use Enqueue\Sqs\SqsMessage;
 use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -31,11 +32,6 @@ class SqsDriver implements DriverInterface
      */
     private $queueMetaRegistry;
 
-    /**
-     * @param SqsContext        $context
-     * @param Config            $config
-     * @param QueueMetaRegistry $queueMetaRegistry
-     */
     public function __construct(SqsContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
     {
         $this->context = $context;
@@ -43,10 +39,7 @@ public function __construct(SqsContext $context, Config $config, QueueMetaRegist
         $this->queueMetaRegistry = $queueMetaRegistry;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToRouter(Message $message)
+    public function sendToRouter(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
             throw new \LogicException('Topic name parameter is required but is not set');
@@ -58,10 +51,7 @@ public function sendToRouter(Message $message)
         $this->context->createProducer()->send($queue, $transportMessage);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToProcessor(Message $message)
+    public function sendToProcessor(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
             throw new \LogicException('Processor name parameter is required but is not set');
@@ -78,11 +68,9 @@ public function sendToProcessor(Message $message)
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return SqsDestination
      */
-    public function createQueue($queueName)
+    public function createQueue(string $queueName): PsrQueue
     {
         $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
         $transportName = str_replace('.', '_dot_', $transportName);
@@ -90,10 +78,7 @@ public function createQueue($queueName)
         return $this->context->createQueue($transportName);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function setupBroker(LoggerInterface $logger = null)
+    public function setupBroker(LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $log = function ($text, ...$args) use ($logger) {
@@ -115,11 +100,9 @@ public function setupBroker(LoggerInterface $logger = null)
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return SqsMessage
      */
-    public function createTransportMessage(Message $message)
+    public function createTransportMessage(Message $message): PsrMessage
     {
         $properties = $message->getProperties();
 
@@ -140,10 +123,8 @@ public function createTransportMessage(Message $message)
 
     /**
      * @param SqsMessage $message
-     *
-     * {@inheritdoc}
      */
-    public function createClientMessage(PsrMessage $message)
+    public function createClientMessage(PsrMessage $message): Message
     {
         $clientMessage = new Message();
 
@@ -161,10 +142,7 @@ public function createClientMessage(PsrMessage $message)
         return $clientMessage;
     }
 
-    /**
-     * @return Config
-     */
-    public function getConfig()
+    public function getConfig(): Config
     {
         return $this->config;
     }
diff --git a/Client/Driver/StompDriver.php b/Client/Driver/StompDriver.php
index 9d824a6..d360847 100644
--- a/Client/Driver/StompDriver.php
+++ b/Client/Driver/StompDriver.php
@@ -10,6 +10,7 @@
 use Enqueue\Stomp\StompDestination;
 use Enqueue\Stomp\StompMessage;
 use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -30,11 +31,6 @@ class StompDriver implements DriverInterface
      */
     private $queueMetaRegistry;
 
-    /**
-     * @param StompContext      $context
-     * @param Config            $config
-     * @param QueueMetaRegistry $queueMetaRegistry
-     */
     public function __construct(StompContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
     {
         $this->context = $context;
@@ -42,10 +38,7 @@ public function __construct(StompContext $context, Config $config, QueueMetaRegi
         $this->queueMetaRegistry = $queueMetaRegistry;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToRouter(Message $message)
+    public function sendToRouter(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
             throw new \LogicException('Topic name parameter is required but is not set');
@@ -57,10 +50,7 @@ public function sendToRouter(Message $message)
         $this->context->createProducer()->send($topic, $transportMessage);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function sendToProcessor(Message $message)
+    public function sendToProcessor(Message $message): void
     {
         if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
             throw new \LogicException('Processor name parameter is required but is not set');
@@ -76,10 +66,7 @@ public function sendToProcessor(Message $message)
         $this->context->createProducer()->send($destination, $transportMessage);
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function setupBroker(LoggerInterface $logger = null)
+    public function setupBroker(LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $logger->debug('[StompDriver] Stomp protocol does not support broker configuration');
@@ -87,10 +74,8 @@ public function setupBroker(LoggerInterface $logger = null)
 
     /**
      * @return StompMessage
-     *
-     * {@inheritdoc}
      */
-    public function createTransportMessage(Message $message)
+    public function createTransportMessage(Message $message): PsrMessage
     {
         $headers = $message->getHeaders();
         $headers['content-type'] = $message->getContentType();
@@ -122,10 +107,8 @@ public function createTransportMessage(Message $message)
 
     /**
      * @param StompMessage $message
-     *
-     * {@inheritdoc}
      */
-    public function createClientMessage(PsrMessage $message)
+    public function createClientMessage(PsrMessage $message): Message
     {
         $clientMessage = new Message();
 
@@ -153,9 +136,9 @@ public function createClientMessage(PsrMessage $message)
     }
 
     /**
-     * {@inheritdoc}
+     * @return StompDestination
      */
-    public function createQueue($queueName)
+    public function createQueue(string $queueName): PsrQueue
     {
         $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
 
@@ -167,18 +150,12 @@ public function createQueue($queueName)
         return $queue;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getConfig()
+    public function getConfig(): Config
     {
         return $this->config;
     }
 
-    /**
-     * @return StompDestination
-     */
-    private function createRouterTopic()
+    private function createRouterTopic(): StompDestination
     {
         $topic = $this->context->createTopic(
             $this->config->createTransportRouterTopicName($this->config->getRouterTopicName())
diff --git a/Client/Driver/StompManagementClient.php b/Client/Driver/StompManagementClient.php
index 410728b..f97caef 100644
--- a/Client/Driver/StompManagementClient.php
+++ b/Client/Driver/StompManagementClient.php
@@ -16,61 +16,28 @@ class StompManagementClient
      */
     private $vhost;
 
-    /**
-     * @param Client|null $client
-     * @param string      $vhost
-     */
-    public function __construct(Client $client, $vhost = '/')
+    public function __construct(Client $client, string $vhost = '/')
     {
         $this->client = $client;
         $this->vhost = $vhost;
     }
 
-    /**
-     * @param string $vhost
-     * @param string $host
-     * @param int    $port
-     * @param string $login
-     * @param string $password
-     *
-     * @return self
-     */
-    public static function create($vhost = '/', $host = 'localhost', $port = 15672, $login = 'guest', $password = 'guest')
+    public static function create(string $vhost = '/', string $host = 'localhost', int $port = 15672, string $login = 'guest', string $password = 'guest'): self
     {
         return new static(new Client(null, 'http://'.$host.':'.$port, $login, $password), $vhost);
     }
 
-    /**
-     * @param string $name
-     * @param array  $options
-     *
-     * @return array
-     */
-    public function declareQueue($name, $options)
+    public function declareQueue(string $name, array $options): array
     {
         return $this->client->queues()->create($this->vhost, $name, $options);
     }
 
-    /**
-     * @param string $name
-     * @param array  $options
-     *
-     * @return array
-     */
-    public function declareExchange($name, $options)
+    public function declareExchange(string $name, array $options): array
     {
         return $this->client->exchanges()->create($this->vhost, $name, $options);
     }
 
-    /**
-     * @param string $exchange
-     * @param string $queue
-     * @param string $routingKey
-     * @param array  $arguments
-     *
-     * @return array
-     */
-    public function bind($exchange, $queue, $routingKey, $arguments = null)
+    public function bind(string $exchange, string $queue, string $routingKey = null, $arguments = null): array
     {
         return $this->client->bindings()->create($this->vhost, $exchange, $queue, $routingKey, $arguments);
     }
diff --git a/Client/DriverInterface.php b/Client/DriverInterface.php
index 2bb0cdc..11f70f6 100644
--- a/Client/DriverInterface.php
+++ b/Client/DriverInterface.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace Enqueue\Client;
 
 use Interop\Queue\PsrMessage;
@@ -8,46 +10,21 @@
 
 interface DriverInterface
 {
-    /**
-     * @param Message $message
-     *
-     * @return PsrMessage
-     */
-    public function createTransportMessage(Message $message);
+    public function createTransportMessage(Message $message): PsrMessage;
 
-    /**
-     * @param PsrMessage $message
-     *
-     * @return Message
-     */
-    public function createClientMessage(PsrMessage $message);
+    public function createClientMessage(PsrMessage $message): Message;
 
-    /**
-     * @param Message $message
-     */
-    public function sendToRouter(Message $message);
+    public function sendToRouter(Message $message): void;
 
-    /**
-     * @param Message $message
-     */
-    public function sendToProcessor(Message $message);
+    public function sendToProcessor(Message $message): void;
 
-    /**
-     * @param string $queueName
-     *
-     * @return PsrQueue
-     */
-    public function createQueue($queueName);
+    public function createQueue(string $queueName): PsrQueue;
 
     /**
-     * Creates all required queues, exchanges, topics, bindings on broker side.
-     *
-     * @param LoggerInterface $logger
+     * Prepare broker for work.
+     * Creates all required queues, exchanges, topics, bindings etc.
      */
-    public function setupBroker(LoggerInterface $logger = null);
+    public function setupBroker(LoggerInterface $logger = null): void;
 
-    /**
-     * @return Config
-     */
-    public function getConfig();
+    public function getConfig(): Config;
 }
diff --git a/Tests/Client/Driver/SqsDriverTest.php b/Tests/Client/Driver/SqsDriverTest.php
index 92bbaaf..d64df83 100644
--- a/Tests/Client/Driver/SqsDriverTest.php
+++ b/Tests/Client/Driver/SqsDriverTest.php
@@ -177,7 +177,7 @@ public function testShouldSendMessageToRouterQueue()
     {
         $topic = new SqsDestination('aDestinationName');
         $transportMessage = new SqsMessage();
-        $config = $this->createConfigMock();
+        $config = $this->createDummyConfig();
 
         $producer = $this->createPsrProducerMock();
         $producer
@@ -348,7 +348,7 @@ public function testShouldSetupBroker()
             ->willReturn(new QueueMeta('theClientName', 'theTransportName'))
         ;
 
-        $driver = new SqsDriver($context, $this->createConfigMock(), $metaRegistry);
+        $driver = new SqsDriver($context, $this->createDummyConfig(), $metaRegistry);
 
         $driver->setupBroker();
     }
@@ -377,20 +377,14 @@ private function createQueueMetaRegistryMock()
         return $this->createMock(QueueMetaRegistry::class);
     }
 
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Config
-     */
-    private function createConfigMock()
-    {
-        return $this->createMock(Config::class);
-    }
-
-    /**
-     * @return Config
-     */
-    private function createDummyConfig()
+    private function createDummyConfig(): Config
     {
-        return Config::create('aPrefix');
+        return Config::create(
+            'aPrefix',
+            '',
+            'aRouterTopic',
+            'aRouterQueue',
+            'aDefaultQueue');
     }
 
     /**
diff --git a/Tests/Client/Driver/StompManagementClientTest.php b/Tests/Client/Driver/StompManagementClientTest.php
index 033c070..325fe2c 100644
--- a/Tests/Client/Driver/StompManagementClientTest.php
+++ b/Tests/Client/Driver/StompManagementClientTest.php
@@ -17,6 +17,7 @@ public function testCouldDeclareExchange()
             ->expects($this->once())
             ->method('create')
             ->with('vhost', 'name', ['options'])
+            ->willReturn([])
         ;
 
         $client = $this->createClientMock();
@@ -37,6 +38,7 @@ public function testCouldDeclareQueue()
             ->expects($this->once())
             ->method('create')
             ->with('vhost', 'name', ['options'])
+            ->willReturn([])
         ;
 
         $client = $this->createClientMock();
@@ -57,6 +59,7 @@ public function testCouldBind()
             ->expects($this->once())
             ->method('create')
             ->with('vhost', 'exchange', 'queue', 'routing-key', ['arguments'])
+            ->willReturn([])
         ;
 
         $client = $this->createClientMock();

From 137f4003fb04e1e00911b6b17af813fa7a8021ef Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 14 Sep 2018 11:05:51 +0300
Subject: [PATCH 106/337] fix test

---
 Client/DriverFactory.php | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Client/DriverFactory.php b/Client/DriverFactory.php
index 1e49294..8c47297 100644
--- a/Client/DriverFactory.php
+++ b/Client/DriverFactory.php
@@ -57,10 +57,10 @@ public function create(PsrConnectionFactory $factory, string $dsn, array $config
 
                 $managementClient = StompManagementClient::create(
                     ltrim($dsn->getPath(), '/'),
-                    $dsn->getHost(),
-                    isset($config['management_plugin_port']) ? $config['management_plugin_port'] : 15672,
-                    $dsn->getUser(),
-                    $dsn->getPassword()
+                    $dsn->getHost() ?: 'localhost',
+                    $config['management_plugin_port'] ?? 15672,
+                    (string) $dsn->getUser(),
+                    (string) $dsn->getPassword()
                 );
 
                 return new RabbitMqStompDriver($factory->createContext(), $this->config, $this->queueMetaRegistry, $managementClient);

From fd8a23c06205313cb191fc02b4ec4f646fbc333b Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 14 Sep 2018 11:28:28 +0300
Subject: [PATCH 107/337] fix tests

---
 Client/Driver/StompManagementClient.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Client/Driver/StompManagementClient.php b/Client/Driver/StompManagementClient.php
index f97caef..dfc284b 100644
--- a/Client/Driver/StompManagementClient.php
+++ b/Client/Driver/StompManagementClient.php
@@ -27,12 +27,12 @@ public static function create(string $vhost = '/', string $host = 'localhost', i
         return new static(new Client(null, 'http://'.$host.':'.$port, $login, $password), $vhost);
     }
 
-    public function declareQueue(string $name, array $options): array
+    public function declareQueue(string $name, array $options)
     {
         return $this->client->queues()->create($this->vhost, $name, $options);
     }
 
-    public function declareExchange(string $name, array $options): array
+    public function declareExchange(string $name, array $options)
     {
         return $this->client->exchanges()->create($this->vhost, $name, $options);
     }

From 28ed8ff58c1639a37a73faffd64d6abd554286db Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 14 Sep 2018 11:41:00 +0300
Subject: [PATCH 108/337] fix test.

---
 Client/Driver/StompManagementClient.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Client/Driver/StompManagementClient.php b/Client/Driver/StompManagementClient.php
index dfc284b..c068a57 100644
--- a/Client/Driver/StompManagementClient.php
+++ b/Client/Driver/StompManagementClient.php
@@ -37,7 +37,7 @@ public function declareExchange(string $name, array $options)
         return $this->client->exchanges()->create($this->vhost, $name, $options);
     }
 
-    public function bind(string $exchange, string $queue, string $routingKey = null, $arguments = null): array
+    public function bind(string $exchange, string $queue, string $routingKey = null, $arguments = null)
     {
         return $this->client->bindings()->create($this->vhost, $exchange, $queue, $routingKey, $arguments);
     }

From bc46afb7f9d041c8ac1eed18228a53da5942996d Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 14 Sep 2018 16:18:32 +0300
Subject: [PATCH 109/337] Reowrk router processor, remove client routing
 compiler pass.

---
 Client/Config.php                             |  88 +++------
 Client/RouteCollection.php                    |  18 +-
 Client/RouterProcessor.php                    |  56 ++----
 .../BuildProcessorRegistryPass.php            |   4 +-
 Tests/Client/RouterProcessorTest.php          | 179 +++++++++++-------
 5 files changed, 161 insertions(+), 184 deletions(-)

diff --git a/Client/Config.php b/Client/Config.php
index d5d8b07..46c0230 100644
--- a/Client/Config.php
+++ b/Client/Config.php
@@ -46,16 +46,7 @@ class Config
      */
     private $transportConfig;
 
-    /**
-     * @param string $prefix
-     * @param string $appName
-     * @param string $routerTopicName
-     * @param string $routerQueueName
-     * @param string $defaultProcessorQueueName
-     * @param string $routerProcessorName
-     * @param array  $transportConfig
-     */
-    public function __construct($prefix, $appName, $routerTopicName, $routerQueueName, $defaultProcessorQueueName, $routerProcessorName, array $transportConfig = [])
+    public function __construct(string $prefix, string $appName, string $routerTopicName, string $routerQueueName, string $defaultProcessorQueueName, string $routerProcessorName, array $transportConfig = [])
     {
         $this->prefix = $prefix;
         $this->appName = $appName;
@@ -66,89 +57,60 @@ public function __construct($prefix, $appName, $routerTopicName, $routerQueueNam
         $this->transportConfig = $transportConfig;
     }
 
-    /**
-     * @return string
-     */
-    public function getRouterTopicName()
+    public function getPrefix(): string
+    {
+        return $this->prefix;
+    }
+
+    public function getAppName(): string
+    {
+        return $this->appName;
+    }
+
+    public function getRouterTopicName(): string
     {
         return $this->routerTopicName;
     }
 
-    /**
-     * @return string
-     */
-    public function getRouterQueueName()
+    public function getRouterQueueName(): string
     {
         return $this->routerQueueName;
     }
 
-    /**
-     * @return string
-     */
-    public function getDefaultProcessorQueueName()
+    public function getDefaultProcessorQueueName(): string
     {
         return $this->defaultProcessorQueueName;
     }
 
-    /**
-     * @return string
-     */
-    public function getRouterProcessorName()
+    public function getRouterProcessorName(): string
     {
         return $this->routerProcessorName;
     }
 
-    /**
-     * @param string $name
-     *
-     * @return string
-     */
-    public function createTransportRouterTopicName($name)
+    public function createTransportRouterTopicName(string $name): string
     {
         return strtolower(implode('.', array_filter([trim($this->prefix), trim($name)])));
     }
 
-    /**
-     * @param string $name
-     *
-     * @return string
-     */
-    public function createTransportQueueName($name)
+    public function createTransportQueueName(string $name): string
     {
         return strtolower(implode('.', array_filter([trim($this->prefix), trim($this->appName), trim($name)])));
     }
 
-    /**
-     * @param string     $name
-     * @param mixed|null $default
-     *
-     * @return array
-     */
-    public function getTransportOption($name, $default = null)
+    public function getTransportOption(string $name, $default = null)
     {
         return array_key_exists($name, $this->transportConfig) ? $this->transportConfig[$name] : $default;
     }
 
-    /**
-     * @param string|null $prefix
-     * @param string|null $appName
-     * @param string|null $routerTopicName
-     * @param string|null $routerQueueName
-     * @param string|null $defaultProcessorQueueName
-     * @param string|null $routerProcessorName
-     * @param array       $transportConfig
-     *
-     * @return static
-     */
     public static function create(
-        $prefix = null,
-        $appName = null,
-        $routerTopicName = null,
-        $routerQueueName = null,
-        $defaultProcessorQueueName = null,
-        $routerProcessorName = null,
+        string $prefix = null,
+        string $appName = null,
+        string $routerTopicName = null,
+        string $routerQueueName = null,
+        string $defaultProcessorQueueName = null,
+        string $routerProcessorName = null,
         array $transportConfig = []
-    ) {
+    ): self {
         return new static(
             $prefix ?: '',
             $appName ?: '',
diff --git a/Client/RouteCollection.php b/Client/RouteCollection.php
index d2a0307..76bcbe4 100644
--- a/Client/RouteCollection.php
+++ b/Client/RouteCollection.php
@@ -37,7 +37,7 @@ public function add(Route $route): void
     /**
      * @return Route[]
      */
-    public function allRoutes(): array
+    public function all(): array
     {
         return $this->routes;
     }
@@ -45,7 +45,7 @@ public function allRoutes(): array
     /**
      * @return Route[]
      */
-    public function commandRoute(string $command): ?Route
+    public function command(string $command): ?Route
     {
         if (null === $this->commandRoutes) {
             $commandRoutes = [];
@@ -64,7 +64,7 @@ public function commandRoute(string $command): ?Route
     /**
      * @return Route[]
      */
-    public function topicRoutes(string $topic): array
+    public function topic(string $topic): array
     {
         if (null === $this->topicRoutes) {
             $topicRoutes = [];
@@ -80,6 +80,18 @@ public function topicRoutes(string $topic): array
         return array_key_exists($topic, $this->topicRoutes) ? $this->topicRoutes[$topic] : [];
     }
 
+    public function topicAndProcessor(string $topic, string $processor): ?Route
+    {
+        $routes = $this->topic($topic);
+        foreach ($routes as $route) {
+            if ($route->getProcessor() === $processor) {
+                return $route;
+            }
+        }
+
+        return null;
+    }
+
     public function toArray(): array
     {
         $rawRoutes = [];
diff --git a/Client/RouterProcessor.php b/Client/RouterProcessor.php
index 2bf9779..7918316 100644
--- a/Client/RouterProcessor.php
+++ b/Client/RouterProcessor.php
@@ -14,65 +14,35 @@ final class RouterProcessor implements PsrProcessor
      */
     private $driver;
 
-    /**
-     * @var RouteCollection
-     */
-    private $routeCollection;
-
-    public function __construct(DriverInterface $driver, RouteCollection $routeCollection)
+    public function __construct(DriverInterface $driver)
     {
         $this->driver = $driver;
-        $this->routeCollection = $routeCollection;
     }
 
     public function process(PsrMessage $message, PsrContext $context): Result
     {
-        $topic = $message->getProperty(Config::PARAMETER_TOPIC_NAME);
-        if ($topic) {
-            return $this->routeEvent($topic, $message);
+        if ($message->getProperty(Config::PARAMETER_COMMAND_NAME)) {
+            return Result::reject(sprintf(
+                'Unexpected command "%s" got. Command must not go to the router.',
+                $message->getProperty(Config::PARAMETER_COMMAND_NAME)
+            ));
         }
 
-        $command = $message->getProperty(Config::PARAMETER_COMMAND_NAME);
-        if ($command) {
-            return $this->routeCommand($command, $message);
+        $topic = $message->getProperty(Config::PARAMETER_TOPIC_NAME);
+        if (false == $topic) {
+            return Result::reject(sprintf('Topic property "%s" is required but not set or empty.', Config::PARAMETER_TOPIC_NAME));
         }
 
-        return Result::reject(sprintf(
-            'Got message without required parameters. Either "%s" or "%s" property should be set',
-            Config::PARAMETER_TOPIC_NAME,
-            Config::PARAMETER_COMMAND_NAME
-        ));
-    }
-
-    private function routeEvent(string $topic, PsrMessage $message): Result
-    {
         $count = 0;
-        foreach ($this->routeCollection->topicRoutes($topic) as $route) {
-            $processorMessage = clone $message;
-            $processorMessage->setProperty(Config::PARAMETER_PROCESSOR_NAME, $route->getProcessor());
-            $processorMessage->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $route->getQueue());
+        foreach ($this->driver->getRouteCollection()->topic($topic) as $route) {
+            $clientMessage = $this->driver->createClientMessage($message);
+            $clientMessage->setProperty(Config::PARAMETER_PROCESSOR_NAME, $route->getProcessor());
 
-            $this->driver->sendToProcessor($this->driver->createClientMessage($processorMessage));
+            $this->driver->sendToProcessor($clientMessage);
 
             ++$count;
         }
 
         return Result::ack(sprintf('Routed to "%d" event subscribers', $count));
     }
-
-    private function routeCommand(string $command, PsrMessage $message): Result
-    {
-        $route = $this->routeCollection->commandRoute($command);
-        if (false == $route) {
-            throw new \LogicException(sprintf('The command "%s" processor not found', $command));
-        }
-
-        $processorMessage = clone $message;
-        $processorMessage->setProperty(Config::PARAMETER_PROCESSOR_NAME, $route->getProcessor());
-        $processorMessage->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $route->getQueue());
-
-        $this->driver->sendToProcessor($this->driver->createClientMessage($processorMessage));
-
-        return Result::ack('Routed to the command processor');
-    }
 }
diff --git a/Symfony/DependencyInjection/BuildProcessorRegistryPass.php b/Symfony/DependencyInjection/BuildProcessorRegistryPass.php
index 7cdc209..4620cef 100644
--- a/Symfony/DependencyInjection/BuildProcessorRegistryPass.php
+++ b/Symfony/DependencyInjection/BuildProcessorRegistryPass.php
@@ -31,13 +31,13 @@ public function process(ContainerBuilder $container): void
 
         $routeCollectionId = sprintf('enqueue.client.%s.route_collection', $this->name);
         if (false == $container->hasDefinition($routeCollectionId)) {
-            throw new \LogicException(sprintf('The required route collection "%s" is not registered. Make sure the client with name "%s" was loaded.', $routeCollectionId, $this->name));
+            return;
         }
 
         $routeCollection = RouteCollection::fromArray($container->getDefinition($routeCollectionId)->getArgument(0));
 
         $map = [];
-        foreach ($routeCollection->allRoutes() as $route) {
+        foreach ($routeCollection->all() as $route) {
             if (false == $processorServiceId = $route->getOption('processor_service_id')) {
                 throw new \LogicException('The route option "processor_service_id" is required');
             }
diff --git a/Tests/Client/RouterProcessorTest.php b/Tests/Client/RouterProcessorTest.php
index da803b1..f69bb07 100644
--- a/Tests/Client/RouterProcessorTest.php
+++ b/Tests/Client/RouterProcessorTest.php
@@ -29,150 +29,183 @@ public function testShouldBeFinal()
         $this->assertClassFinal(RouterProcessor::class);
     }
 
-    public function testCouldBeConstructedWithDriverAndRouteCollection()
+    public function testCouldBeConstructedWithDriver()
     {
-        $driver = $this->createDriverMock();
-        $routeCollection = new RouteCollection([]);
+        $driver = $this->createDriverStub();
 
-        $processor = new RouterProcessor($driver, $routeCollection);
+        $processor = new RouterProcessor($driver);
 
         $this->assertAttributeSame($driver, 'driver', $processor);
-        $this->assertAttributeSame($routeCollection, 'routeCollection', $processor);
     }
 
-    public function testShouldRejectIfNeitherTopicNorCommandParameterIsSet()
+    public function testShouldRejectIfTopicNotSet()
     {
-        $router = new RouterProcessor($this->createDriverMock(), new RouteCollection([]));
+        $router = new RouterProcessor($this->createDriverStub());
 
         $result = $router->process(new NullMessage(), new NullContext());
 
         $this->assertEquals(Result::REJECT, $result->getStatus());
-        $this->assertEquals('Got message without required parameters. Either "enqueue.topic_name" or "enqueue.command_name" property should be set', $result->getReason());
+        $this->assertEquals('Topic property "enqueue.topic_name" is required but not set or empty.', $result->getReason());
+    }
+
+    public function testShouldRejectIfCommandSet()
+    {
+        $router = new RouterProcessor($this->createDriverStub());
+
+        $message = new NullMessage();
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'aCommand');
+
+        $result = $router->process($message, new NullContext());
+
+        $this->assertEquals(Result::REJECT, $result->getStatus());
+        $this->assertEquals('Unexpected command "aCommand" got. Command must not go to the router.', $result->getReason());
     }
 
-    public function testShouldRouteOriginalMessageToEventRecipient()
+    public function testShouldRouteOriginalMessageToAllRecipients()
     {
         $message = new NullMessage();
         $message->setBody('theBody');
         $message->setHeaders(['aHeader' => 'aHeaderVal']);
         $message->setProperties(['aProp' => 'aPropVal', Config::PARAMETER_TOPIC_NAME => 'theTopicName']);
 
-        $clientMessage = new Message();
+        /** @var Message[] $routedMessages */
+        $routedMessages = new \ArrayObject();
 
-        $routedMessage = null;
+        $routeCollection = new RouteCollection([
+            new Route('theTopicName', Route::TOPIC, 'aFooProcessor'),
+            new Route('theTopicName', Route::TOPIC, 'aBarProcessor'),
+            new Route('theTopicName', Route::TOPIC, 'aBazProcessor'),
+        ]);
 
-        $driver = $this->createDriverMock();
+        $driver = $this->createDriverStub($routeCollection);
         $driver
-            ->expects($this->once())
+            ->expects($this->exactly(3))
             ->method('sendToProcessor')
-            ->with($this->identicalTo($clientMessage))
+            ->willReturnCallback(function (Message $message) use ($routedMessages) {
+                $routedMessages->append($message);
+            })
         ;
         $driver
-            ->expects($this->once())
+            ->expects($this->exactly(3))
             ->method('createClientMessage')
-            ->willReturnCallback(function (NullMessage $message) use (&$routedMessage, $clientMessage) {
-                $routedMessage = $message;
-
-                return $clientMessage;
+            ->willReturnCallback(function (NullMessage $message) {
+                return new Message($message->getBody(), $message->getProperties(), $message->getHeaders());
             })
         ;
 
-        $processor = new RouterProcessor($driver, new RouteCollection([
-            new Route('theTopicName', Route::TOPIC, 'aFooProcessor', ['queue' => 'aQueueName']),
-        ]));
+        $processor = new RouterProcessor($driver);
 
         $result = $processor->process($message, new NullContext());
 
         $this->assertEquals(Result::ACK, $result->getStatus());
-        $this->assertEquals('Routed to "1" event subscribers', $result->getReason());
-
-        $this->assertEquals([
-            'aProp' => 'aPropVal',
-            'enqueue.topic_name' => 'theTopicName',
-            'enqueue.processor_name' => 'aFooProcessor',
-            'enqueue.processor_queue_name' => 'aQueueName',
-        ], $routedMessage->getProperties());
+        $this->assertEquals('Routed to "3" event subscribers', $result->getReason());
+
+        $this->assertContainsOnly(Message::class, $routedMessages);
+        $this->assertCount(3, $routedMessages);
+
+        $this->assertSame('aFooProcessor', $routedMessages[0]->getProperty(Config::PARAMETER_PROCESSOR_NAME));
+        $this->assertSame('aBarProcessor', $routedMessages[1]->getProperty(Config::PARAMETER_PROCESSOR_NAME));
+        $this->assertSame('aBazProcessor', $routedMessages[2]->getProperty(Config::PARAMETER_PROCESSOR_NAME));
+
+//        $this->assertEquals([
+//            'aProp' => 'aPropVal',
+//            'enqueue.topic_name' => 'theTopicName',
+//            'enqueue.processor_name' => 'aFooProcessor',
+//            'enqueue.processor_queue_name' => 'aQueueName',
+//        ], $routedMessages[0]->getProperties());
     }
 
-    public function testShouldRouteOriginalMessageToCommandRecipient()
+    public function testShouldDoNothingIfNoRoutes()
     {
         $message = new NullMessage();
         $message->setBody('theBody');
         $message->setHeaders(['aHeader' => 'aHeaderVal']);
-        $message->setProperties([
-            'aProp' => 'aPropVal',
-            'enqueue.command_name' => 'theCommandName',
-        ]);
+        $message->setProperties(['aProp' => 'aPropVal', Config::PARAMETER_TOPIC_NAME => 'theTopicName']);
 
-        $clientMessage = new Message();
+        /** @var Message[] $routedMessages */
+        $routedMessages = new \ArrayObject();
 
-        $routedMessage = null;
+        $routeCollection = new RouteCollection([]);
 
-        $driver = $this->createDriverMock();
+        $driver = $this->createDriverStub($routeCollection);
         $driver
-            ->expects($this->once())
+            ->expects($this->never())
             ->method('sendToProcessor')
-            ->with($this->identicalTo($clientMessage))
+            ->willReturnCallback(function (Message $message) use ($routedMessages) {
+                $routedMessages->append($message);
+            })
         ;
         $driver
-            ->expects($this->once())
+            ->expects($this->never())
             ->method('createClientMessage')
-            ->willReturnCallback(function (NullMessage $message) use (&$routedMessage, $clientMessage) {
-                $routedMessage = $message;
-
-                return $clientMessage;
+            ->willReturnCallback(function (NullMessage $message) {
+                return new Message($message->getBody(), $message->getProperties(), $message->getHeaders());
             })
         ;
 
-        $processor = new RouterProcessor($driver, new RouteCollection([
-            new Route('theCommandName', Route::COMMAND, 'aFooProcessor', ['queue' => 'aQueueName']),
-        ]));
+        $processor = new RouterProcessor($driver);
 
         $result = $processor->process($message, new NullContext());
 
         $this->assertEquals(Result::ACK, $result->getStatus());
-        $this->assertEquals('Routed to the command processor', $result->getReason());
-
-        $this->assertEquals([
-            'aProp' => 'aPropVal',
-            'enqueue.processor_name' => 'aFooProcessor',
-            'enqueue.command_name' => 'theCommandName',
-            'enqueue.processor_queue_name' => 'aQueueName',
-        ], $routedMessage->getProperties());
+        $this->assertEquals('Routed to "0" event subscribers', $result->getReason());
+
+        $this->assertCount(0, $routedMessages);
     }
 
-    public function testThrowIfNoRouteForGivenCommand()
+    public function testShouldDoNotModifyOriginalMessage()
     {
         $message = new NullMessage();
         $message->setBody('theBody');
         $message->setHeaders(['aHeader' => 'aHeaderVal']);
-        $message->setProperties([
-            'aProp' => 'aPropVal',
-            'enqueue.command_name' => 'theCommandName',
-        ]);
+        $message->setProperties(['aProp' => 'aPropVal', Config::PARAMETER_TOPIC_NAME => 'theTopicName']);
 
-        $clientMessage = new Message();
+        /** @var Message[] $routedMessages */
+        $routedMessages = new \ArrayObject();
 
-        $routedMessage = null;
+        $routeCollection = new RouteCollection([
+            new Route('theTopicName', Route::TOPIC, 'aFooProcessor'),
+            new Route('theTopicName', Route::TOPIC, 'aBarProcessor'),
+        ]);
 
-        $driver = $this->createDriverMock();
+        $driver = $this->createDriverStub($routeCollection);
         $driver
-            ->expects($this->never())
+            ->expects($this->atLeastOnce())
             ->method('sendToProcessor')
-        ;
+            ->willReturnCallback(function (Message $message) use ($routedMessages) {
+                $routedMessages->append($message);
+            });
+        $driver
+            ->expects($this->atLeastOnce())
+            ->method('createClientMessage')
+            ->willReturnCallback(function (NullMessage $message) {
+                return new Message($message->getBody(), $message->getProperties(), $message->getHeaders());
+            });
 
-        $processor = new RouterProcessor($driver, new RouteCollection([]));
+        $processor = new RouterProcessor($driver);
 
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The command "theCommandName" processor not found');
-        $processor->process($message, new NullContext());
+        $result = $processor->process($message, new NullContext());
+
+        //guard
+        $this->assertEquals(Result::ACK, $result->getStatus());
+
+        $this->assertSame('theBody', $message->getBody());
+        $this->assertSame(['aProp' => 'aPropVal', Config::PARAMETER_TOPIC_NAME => 'theTopicName'], $message->getProperties());
+        $this->assertSame(['aHeader' => 'aHeaderVal'], $message->getHeaders());
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|DriverInterface
+     * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    protected function createDriverMock()
+    private function createDriverStub(RouteCollection $routeCollection = null): DriverInterface
     {
-        return $this->createMock(DriverInterface::class);
+        $driver = $this->createMock(DriverInterface::class);
+        $driver
+            ->expects($this->any())
+            ->method('getRouteCollection')
+            ->willReturn($routeCollection)
+        ;
+
+        return $driver;
     }
 }

From 6dff636e445f66975274e91bd726cd622b00008d Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 14 Sep 2018 16:19:10 +0300
Subject: [PATCH 110/337] add analyze route collection pass

---
 .../AnalyzeRouteCollectionPass.php            |  90 +++++++++++
 .../AnalyzeRouteCollectionPassTest.php        | 140 ++++++++++++++++++
 2 files changed, 230 insertions(+)
 create mode 100644 Symfony/DependencyInjection/AnalyzeRouteCollectionPass.php
 create mode 100644 Tests/Symfony/DependencyInjection/AnalyzeRouteCollectionPassTest.php

diff --git a/Symfony/DependencyInjection/AnalyzeRouteCollectionPass.php b/Symfony/DependencyInjection/AnalyzeRouteCollectionPass.php
new file mode 100644
index 0000000..b5bd11f
--- /dev/null
+++ b/Symfony/DependencyInjection/AnalyzeRouteCollectionPass.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace Enqueue\Symfony\DependencyInjection;
+
+use Enqueue\Client\RouteCollection;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+final class AnalyzeRouteCollectionPass implements CompilerPassInterface
+{
+    /**
+     * @var string
+     */
+    private $name;
+
+    public function __construct(string $clientName)
+    {
+        if (empty($clientName)) {
+            throw new \InvalidArgumentException('The name could not be empty.');
+        }
+
+        $this->name = $clientName;
+    }
+
+    public function process(ContainerBuilder $container): void
+    {
+        $routeCollectionId = sprintf('enqueue.client.%s.route_collection', $this->name);
+        if (false == $container->hasDefinition($routeCollectionId)) {
+            return;
+        }
+
+        $collection = RouteCollection::fromArray($container->getDefinition($routeCollectionId)->getArgument(0));
+
+        $this->exclusiveCommandsCouldNotBeRunOnDefaultQueue($collection);
+        $this->exclusiveCommandProcessorMustBeSingleOnGivenQueue($collection);
+    }
+
+    private function exclusiveCommandsCouldNotBeRunOnDefaultQueue(RouteCollection $collection)
+    {
+        foreach ($collection->all() as $route) {
+            if ($route->isCommand() && $route->isProcessorExclusive() && false == $route->getQueue()) {
+                throw new \LogicException(sprintf(
+                    'The command "%s" processor "%s" is exclusive but queue is not specified. Exclusive processors could not be run on a default queue.',
+                    $route->getSource(),
+                    $route->getProcessor()
+                ));
+            }
+        }
+    }
+
+    private function exclusiveCommandProcessorMustBeSingleOnGivenQueue(RouteCollection $collection)
+    {
+        $prefixedQueues = [];
+        $queues = [];
+        foreach ($collection->all() as $route) {
+            if (false == $route->isCommand()) {
+                continue;
+            }
+            if (false == $route->isProcessorExclusive()) {
+                continue;
+            }
+
+            if ($route->isPrefixQueue()) {
+                if (array_key_exists($route->getQueue(), $prefixedQueues)) {
+                    throw new \LogicException(sprintf(
+                        'The command "%s" processor "%s" is exclusive. The queue "%s" already has another exclusive command processor "%s" bound to it.',
+                        $route->getSource(),
+                        $route->getProcessor(),
+                        $route->getQueue(),
+                        $prefixedQueues[$route->getQueue()]
+                    ));
+                }
+
+                $prefixedQueues[$route->getQueue()] = $route->getProcessor();
+            } else {
+                if (array_key_exists($route->getQueue(), $queues)) {
+                    throw new \LogicException(sprintf(
+                        'The command "%s" processor "%s" is exclusive. The queue "%s" already has another exclusive command processor "%s" bound to it.',
+                        $route->getSource(),
+                        $route->getProcessor(),
+                        $route->getQueue(),
+                        $queues[$route->getQueue()]
+                    ));
+                }
+
+                $queues[$route->getQueue()] = $route->getProcessor();
+            }
+        }
+    }
+}
diff --git a/Tests/Symfony/DependencyInjection/AnalyzeRouteCollectionPassTest.php b/Tests/Symfony/DependencyInjection/AnalyzeRouteCollectionPassTest.php
new file mode 100644
index 0000000..be844d8
--- /dev/null
+++ b/Tests/Symfony/DependencyInjection/AnalyzeRouteCollectionPassTest.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\DependencyInjection;
+
+use Enqueue\Client\Route;
+use Enqueue\Symfony\DependencyInjection\AnalyzeRouteCollectionPass;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+class AnalyzeRouteCollectionPassTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementCompilerPass()
+    {
+        $this->assertClassImplements(CompilerPassInterface::class, AnalyzeRouteCollectionPass::class);
+    }
+
+    public function testShouldBeFinal()
+    {
+        $this->assertClassFinal(AnalyzeRouteCollectionPass::class);
+    }
+
+    public function testCouldBeConstructedWithName()
+    {
+        $pass = new AnalyzeRouteCollectionPass('aName');
+
+        $this->assertAttributeSame('aName', 'name', $pass);
+    }
+
+    public function testThrowIfNameEmptyOnConstruct()
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The name could not be empty.');
+        new AnalyzeRouteCollectionPass('');
+    }
+
+    public function testShouldDoNothingIfRouteCollectionServiceIsNotRegistered()
+    {
+        $pass = new AnalyzeRouteCollectionPass('aName');
+        $pass->process(new ContainerBuilder());
+    }
+
+    public function testThrowIfExclusiveCommandProcessorOnDefaultQueue()
+    {
+        $container = new ContainerBuilder();
+        $container->register('enqueue.client.aName.route_collection')->addArgument([
+            (new Route(
+                'aCommand',
+                Route::COMMAND,
+                'aBarProcessor',
+                ['exclusive' => true]
+            ))->toArray(),
+        ]);
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The command "aCommand" processor "aBarProcessor" is exclusive but queue is not specified. Exclusive processors could not be run on a default queue.');
+        $pass = new AnalyzeRouteCollectionPass('aName');
+
+        $pass->process($container);
+    }
+
+    public function testThrowIfTwoExclusiveCommandProcessorsWorkOnSamePrefixedQueue()
+    {
+        $container = new ContainerBuilder();
+        $container->register('enqueue.client.aName.route_collection')->addArgument([
+            (new Route(
+                'aFooCommand',
+                Route::COMMAND,
+                'aFooProcessor',
+                ['exclusive' => true, 'queue' => 'aQueue', 'prefix_queue' => true]
+            ))->toArray(),
+
+            (new Route(
+                'aBarCommand',
+                Route::COMMAND,
+                'aBarProcessor',
+                ['exclusive' => true, 'queue' => 'aQueue', 'prefix_queue' => true]
+            ))->toArray(),
+        ]);
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The command "aBarCommand" processor "aBarProcessor" is exclusive. The queue "aQueue" already has another exclusive command processor "aFooProcessor" bound to it.');
+        $pass = new AnalyzeRouteCollectionPass('aName');
+
+        $pass->process($container);
+    }
+
+    public function testThrowIfTwoExclusiveCommandProcessorsWorkOnSameQueue()
+    {
+        $container = new ContainerBuilder();
+        $container->register('enqueue.client.aName.route_collection')->addArgument([
+            (new Route(
+                'aFooCommand',
+                Route::COMMAND,
+                'aFooProcessor',
+                ['exclusive' => true, 'queue' => 'aQueue', 'prefix_queue' => false]
+            ))->toArray(),
+
+            (new Route(
+                'aBarCommand',
+                Route::COMMAND,
+                'aBarProcessor',
+                ['exclusive' => true, 'queue' => 'aQueue', 'prefix_queue' => false]
+            ))->toArray(),
+        ]);
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The command "aBarCommand" processor "aBarProcessor" is exclusive. The queue "aQueue" already has another exclusive command processor "aFooProcessor" bound to it.');
+        $pass = new AnalyzeRouteCollectionPass('aName');
+
+        $pass->process($container);
+    }
+
+    public function testShouldNotThrowIfTwoExclusiveCommandProcessorsWorkOnQueueWithSameNameButOnePrefixed()
+    {
+        $container = new ContainerBuilder();
+        $container->register('enqueue.client.aName.route_collection')->addArgument([
+            (new Route(
+                'aFooCommand',
+                Route::COMMAND,
+                'aFooProcessor',
+                ['exclusive' => true, 'queue' => 'aQueue', 'prefix_queue' => false]
+            ))->toArray(),
+
+            (new Route(
+                'aBarCommand',
+                Route::COMMAND,
+                'aBarProcessor',
+                ['exclusive' => true, 'queue' => 'aQueue', 'prefix_queue' => true]
+            ))->toArray(),
+        ]);
+
+        $pass = new AnalyzeRouteCollectionPass('aName');
+
+        $pass->process($container);
+    }
+}

From 9304ba967e09415ea01d15d11ecdc927a8c7735b Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 14 Sep 2018 16:34:29 +0300
Subject: [PATCH 111/337] Rework exclusive command extension.

---
 .../ExclusiveCommandExtension.php             |  65 ++++---
 .../ExclusiveCommandExtensionTest.php         | 180 ++++++++++--------
 2 files changed, 132 insertions(+), 113 deletions(-)

diff --git a/Client/ConsumptionExtension/ExclusiveCommandExtension.php b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
index 824a244..261ac02 100644
--- a/Client/ConsumptionExtension/ExclusiveCommandExtension.php
+++ b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
@@ -3,34 +3,29 @@
 namespace Enqueue\Client\ConsumptionExtension;
 
 use Enqueue\Client\Config;
-use Enqueue\Client\EmptyExtensionTrait as ClientEmptyExtensionTrait;
-use Enqueue\Client\ExtensionInterface as ClientExtensionInterface;
-use Enqueue\Client\PreSend;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Route;
 use Enqueue\Consumption\Context;
 use Enqueue\Consumption\EmptyExtensionTrait as ConsumptionEmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface as ConsumptionExtensionInterface;
 
-final class ExclusiveCommandExtension implements ConsumptionExtensionInterface, ClientExtensionInterface
+final class ExclusiveCommandExtension implements ConsumptionExtensionInterface
 {
-    use ConsumptionEmptyExtensionTrait, ClientEmptyExtensionTrait;
+    use ConsumptionEmptyExtensionTrait;
 
     /**
-     * @var string[]
+     * @var DriverInterface
      */
-    private $queueNameToProcessorNameMap;
+    private $driver;
 
     /**
-     * @var string[]
+     * @var Route[]
      */
-    private $processorNameToQueueNameMap;
+    private $queueToRouteMap;
 
-    /**
-     * @param string[] $queueNameToProcessorNameMap
-     */
-    public function __construct(array $queueNameToProcessorNameMap)
+    public function __construct(DriverInterface $driver)
     {
-        $this->queueNameToProcessorNameMap = $queueNameToProcessorNameMap;
-        $this->processorNameToQueueNameMap = array_flip($queueNameToProcessorNameMap);
+        $this->driver = $driver;
     }
 
     public function onPreReceived(Context $context)
@@ -41,34 +36,46 @@ public function onPreReceived(Context $context)
         if ($message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
             return;
         }
-        if ($message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
+        if ($message->getProperty(Config::PARAMETER_COMMAND_NAME)) {
             return;
         }
         if ($message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
             return;
         }
-        if ($message->getProperty(Config::PARAMETER_COMMAND_NAME)) {
-            return;
+
+        if (null === $this->queueToRouteMap) {
+            $this->queueToRouteMap = $this->buildMap();
         }
 
-        if (array_key_exists($queue->getQueueName(), $this->queueNameToProcessorNameMap)) {
+        if (array_key_exists($queue->getQueueName(), $this->queueToRouteMap)) {
             $context->getLogger()->debug('[ExclusiveCommandExtension] This is a exclusive command queue and client\'s properties are not set. Setting them');
 
-            $message->setProperty(Config::PARAMETER_TOPIC_NAME, Config::COMMAND_TOPIC);
-            $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $queue->getQueueName());
-            $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $this->queueNameToProcessorNameMap[$queue->getQueueName()]);
-            $message->setProperty(Config::PARAMETER_COMMAND_NAME, $this->queueNameToProcessorNameMap[$queue->getQueueName()]);
+            $route = $this->queueToRouteMap[$queue->getQueueName()];
+            $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $route->getProcessor());
+            $message->setProperty(Config::PARAMETER_COMMAND_NAME, $route->getSource());
         }
     }
 
-    public function onPreSendCommand(PreSend $context): void
+    private function buildMap(): array
     {
-        $message = $context->getMessage();
-        $command = $context->getCommand();
+        $map = [];
+        foreach ($this->driver->getRouteCollection()->all() as $route) {
+            if (false == $route->isCommand()) {
+                continue;
+            }
+
+            if (false == $route->isProcessorExclusive()) {
+                continue;
+            }
 
-        if (array_key_exists($command, $this->processorNameToQueueNameMap)) {
-            $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $command);
-            $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $this->processorNameToQueueNameMap[$command]);
+            $queueName = $this->driver->createQueue($route->getQueue())->getQueueName();
+            if (array_key_exists($queueName, $map)) {
+                throw new \LogicException('The queue name has been already bound by another exclusive command processor');
+            }
+
+            $map[$queueName] = $route;
         }
+
+        return $map;
     }
 }
diff --git a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
index c5eb0bb..d858c91 100644
--- a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
@@ -5,10 +5,8 @@
 use Enqueue\Client\Config;
 use Enqueue\Client\ConsumptionExtension\ExclusiveCommandExtension;
 use Enqueue\Client\DriverInterface;
-use Enqueue\Client\ExtensionInterface as ClientExtensionInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\PreSend;
-use Enqueue\Client\ProducerInterface;
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
 use Enqueue\Consumption\Context;
 use Enqueue\Consumption\ExtensionInterface as ConsumptionExtensionInterface;
 use Enqueue\Null\NullContext;
@@ -32,16 +30,9 @@ public function testShouldBeFinal()
         $this->assertClassFinal(ExclusiveCommandExtension::class);
     }
 
-    public function testShouldImplementClientExtensionInterface()
+    public function testCouldBeConstructedWithDriverAsFirstArgument()
     {
-        $this->assertClassImplements(ClientExtensionInterface::class, ExclusiveCommandExtension::class);
-    }
-
-    public function testCouldBeConstructedWithQueueNameToProcessorNameMap()
-    {
-        new ExclusiveCommandExtension([]);
-
-        new ExclusiveCommandExtension(['fooQueueName' => 'fooProcessorName']);
+        new ExclusiveCommandExtension($this->createDriverStub());
     }
 
     public function testShouldDoNothingIfMessageHasTopicPropertySetOnPreReceive()
@@ -52,9 +43,13 @@ public function testShouldDoNothingIfMessageHasTopicPropertySetOnPreReceive()
         $context = new Context(new NullContext());
         $context->setPsrMessage($message);
 
-        $extension = new ExclusiveCommandExtension([
-            'aFooQueueName' => 'aFooProcessorName',
-        ]);
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->never())
+            ->method('createQueue')
+        ;
+
+        $extension = new ExclusiveCommandExtension($driver);
 
         $extension->onPreReceived($context);
 
@@ -65,49 +60,57 @@ public function testShouldDoNothingIfMessageHasTopicPropertySetOnPreReceive()
         ], $message->getProperties());
     }
 
-    public function testShouldDoNothingIfMessageHasProcessorNamePropertySetOnPreReceive()
+    public function testShouldDoNothingIfMessageHasCommandPropertySetOnPreReceive()
     {
         $message = new NullMessage();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aProcessor');
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'aCommand');
 
         $context = new Context(new NullContext());
         $context->setPsrMessage($message);
 
-        $extension = new ExclusiveCommandExtension([
-            'aFooQueueName' => 'aFooProcessorName',
-        ]);
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->never())
+            ->method('createQueue')
+        ;
+
+        $extension = new ExclusiveCommandExtension($driver);
 
         $extension->onPreReceived($context);
 
         self::assertNull($context->getResult());
 
         $this->assertEquals([
-            'enqueue.processor_name' => 'aProcessor',
+            'enqueue.command_name' => 'aCommand',
         ], $message->getProperties());
     }
 
-    public function testShouldDoNothingIfMessageHasProcessorQueueNamePropertySetOnPreReceive()
+    public function testShouldDoNothingIfMessageHasProcessorPropertySetOnPreReceive()
     {
         $message = new NullMessage();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aProcessorQueueName');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aProcessor');
 
         $context = new Context(new NullContext());
         $context->setPsrMessage($message);
 
-        $extension = new ExclusiveCommandExtension([
-            'aFooQueueName' => 'aFooProcessorName',
-        ]);
+        $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->never())
+            ->method('createQueue')
+        ;
+
+        $extension = new ExclusiveCommandExtension($driver);
 
         $extension->onPreReceived($context);
 
         self::assertNull($context->getResult());
 
         $this->assertEquals([
-            'enqueue.processor_queue_name' => 'aProcessorQueueName',
+            'enqueue.processor_name' => 'aProcessor',
         ], $message->getProperties());
     }
 
-    public function testShouldDoNothingIfCurrentQueueIsNotInTheMap()
+    public function testShouldDoNothingIfCurrentQueueHasNoExclusiveProcessor()
     {
         $message = new NullMessage();
         $queue = new NullQueue('aBarQueueName');
@@ -116,9 +119,7 @@ public function testShouldDoNothingIfCurrentQueueIsNotInTheMap()
         $context->setPsrMessage($message);
         $context->setPsrQueue($queue);
 
-        $extension = new ExclusiveCommandExtension([
-            'aFooQueueName' => 'aFooProcessorName',
-        ]);
+        $extension = new ExclusiveCommandExtension($this->createDriverStub(new RouteCollection([])));
 
         $extension->onPreReceived($context);
 
@@ -127,86 +128,97 @@ public function testShouldDoNothingIfCurrentQueueIsNotInTheMap()
         $this->assertEquals([], $message->getProperties());
     }
 
-    public function testShouldSetCommandPropertiesIfCurrentQueueInTheMap()
+    public function testShouldSetCommandPropertiesIfCurrentQueueHasExclusiveCommandProcessor()
     {
         $message = new NullMessage();
-        $queue = new NullQueue('aFooQueueName');
+        $queue = new NullQueue('fooQueue');
 
         $context = new Context(new NullContext());
         $context->setPsrMessage($message);
         $context->setPsrQueue($queue);
         $context->setLogger(new NullLogger());
 
-        $extension = new ExclusiveCommandExtension([
-            'aFooQueueName' => 'aFooProcessorName',
+        $routeCollection = new RouteCollection([
+            new Route('fooCommand', Route::COMMAND, 'theFooProcessor', [
+                'exclusive' => true,
+                'queue' => 'fooQueue',
+            ]),
+            new Route('barCommand', Route::COMMAND, 'theFooProcessor', [
+                'exclusive' => true,
+                'queue' => 'barQueue',
+            ]),
         ]);
 
+        $driver = $this->createDriverStub($routeCollection);
+        $driver
+            ->expects($this->any())
+            ->method('createQueue')
+            ->willReturnCallback(function (string $queueName) {
+                return new NullQueue($queueName);
+            })
+        ;
+
+        $extension = new ExclusiveCommandExtension($driver);
         $extension->onPreReceived($context);
 
         self::assertNull($context->getResult());
 
         $this->assertEquals([
-            'enqueue.topic_name' => '__command__',
-            'enqueue.processor_queue_name' => 'aFooQueueName',
-            'enqueue.processor_name' => 'aFooProcessorName',
-            'enqueue.command_name' => 'aFooProcessorName',
+            'enqueue.processor_name' => 'theFooProcessor',
+            'enqueue.command_name' => 'fooCommand',
         ], $message->getProperties());
     }
 
-    public function testShouldDoNothingOnPreSendEvent()
+    public function testShouldDoNothingIfAnotherQueue()
     {
-        $message = new Message();
-
-        $extension = new ExclusiveCommandExtension([
-            'aFooQueueName' => 'aFooProcessorName',
-        ]);
-
-        $extension->onPreSendEvent($this->createDummyPreSend('aTopic', $message));
-
-        $this->assertEquals([], $message->getProperties());
-    }
+        $message = new NullMessage();
+        $queue = new NullQueue('barQueue');
 
-    public function testShouldDoNothingIfCommandNotExclusive()
-    {
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'theBarProcessorName');
+        $context = new Context(new NullContext());
+        $context->setPsrMessage($message);
+        $context->setPsrQueue($queue);
+        $context->setLogger(new NullLogger());
 
-        $extension = new ExclusiveCommandExtension([
-            'aFooQueueName' => 'aFooProcessorName',
+        $routeCollection = new RouteCollection([
+            new Route('fooCommand', Route::COMMAND, 'theFooProcessor', [
+                'exclusive' => true,
+                'queue' => 'fooQueue',
+            ]),
+            new Route('barCommand', Route::COMMAND, 'theFooProcessor', [
+                'exclusive' => false,
+                'queue' => 'barQueue',
+            ]),
         ]);
 
-        $extension->onPreSendCommand($this->createDummyPreSend('theBarProcessorName', $message));
-
-        $this->assertEquals([
-            'enqueue.command_name' => 'theBarProcessorName',
-        ], $message->getProperties());
-    }
-
-    public function testShouldForceExclusiveCommandQueue()
-    {
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'aFooProcessorName');
+        $driver = $this->createDriverStub($routeCollection);
+        $driver
+            ->expects($this->any())
+            ->method('createQueue')
+            ->willReturnCallback(function (string $queueName) {
+                return new NullQueue($queueName);
+            })
+        ;
 
-        $extension = new ExclusiveCommandExtension([
-            'aFooQueueName' => 'aFooProcessorName',
-        ]);
+        $extension = new ExclusiveCommandExtension($driver);
+        $extension->onPreReceived($context);
 
-        $extension->onPreSendCommand($this->createDummyPreSend('aFooProcessorName', $message));
+        self::assertNull($context->getResult());
 
-        $this->assertEquals([
-            'enqueue.command_name' => 'aFooProcessorName',
-            'enqueue.processor_name' => 'aFooProcessorName',
-            'enqueue.processor_queue_name' => 'aFooQueueName',
-        ], $message->getProperties());
+        $this->assertEquals([], $message->getProperties());
     }
 
-    private function createDummyPreSend(string $commandOrTopic, Message $message): PreSend
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createDriverStub(RouteCollection $routeCollection = null): DriverInterface
     {
-        return new PreSend(
-            $commandOrTopic,
-            $message,
-            $this->createMock(ProducerInterface::class),
-            $this->createMock(DriverInterface::class)
-        );
+        $driver = $this->createMock(DriverInterface::class);
+        $driver
+            ->expects($this->any())
+            ->method('getRouteCollection')
+            ->willReturn($routeCollection)
+        ;
+
+        return $driver;
     }
 }

From 230e7cfc2854ce29cd8c576bf736cfa513723cc2 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 14 Sep 2018 18:50:58 +0300
Subject: [PATCH 112/337] [client] udpdate config.

---
 Client/Config.php           |  57 +++++++++++++--
 Tests/Client/ConfigTest.php | 141 ++++++++++++++++++++++++++++++++++++
 2 files changed, 192 insertions(+), 6 deletions(-)

diff --git a/Client/Config.php b/Client/Config.php
index 46c0230..4e7dd39 100644
--- a/Client/Config.php
+++ b/Client/Config.php
@@ -7,8 +7,20 @@ class Config
     const PARAMETER_TOPIC_NAME = 'enqueue.topic_name';
     const PARAMETER_COMMAND_NAME = 'enqueue.command_name';
     const PARAMETER_PROCESSOR_NAME = 'enqueue.processor_name';
+
+    /**
+     * @deprecated
+     */
     const PARAMETER_PROCESSOR_QUEUE_NAME = 'enqueue.processor_queue_name';
+
+    /**
+     * @deprecated
+     */
     const DEFAULT_PROCESSOR_QUEUE_NAME = 'default';
+
+    /**
+     * @deprecated
+     */
     const COMMAND_TOPIC = '__command__';
 
     /**
@@ -48,12 +60,29 @@ class Config
 
     public function __construct(string $prefix, string $appName, string $routerTopicName, string $routerQueueName, string $defaultProcessorQueueName, string $routerProcessorName, array $transportConfig = [])
     {
-        $this->prefix = $prefix;
-        $this->appName = $appName;
-        $this->routerTopicName = $routerTopicName;
-        $this->routerQueueName = $routerQueueName;
-        $this->defaultProcessorQueueName = $defaultProcessorQueueName;
-        $this->routerProcessorName = $routerProcessorName;
+        $this->prefix = trim($prefix);
+        $this->appName = trim($appName);
+
+        $this->routerTopicName = trim($routerTopicName);
+        if (empty($this->routerTopicName)) {
+            throw new \InvalidArgumentException('Router topic is empty.');
+        }
+
+        $this->routerQueueName = trim($routerQueueName);
+        if (empty($this->routerQueueName)) {
+            throw new \InvalidArgumentException('Router queue is empty.');
+        }
+
+        $this->defaultProcessorQueueName = trim($defaultProcessorQueueName);
+        if (empty($this->defaultProcessorQueueName)) {
+            throw new \InvalidArgumentException('Default processor queue name is empty.');
+        }
+
+        $this->routerProcessorName = trim($routerProcessorName);
+        if (empty($this->routerProcessorName)) {
+            throw new \InvalidArgumentException('Router processor name is empty.');
+        }
+
         $this->transportConfig = $transportConfig;
     }
 
@@ -62,6 +91,11 @@ public function getPrefix(): string
         return $this->prefix;
     }
 
+    public function getSeparator(): string
+    {
+        return '.';
+    }
+
     public function getAppName(): string
     {
         return $this->appName;
@@ -87,16 +121,27 @@ public function getRouterProcessorName(): string
         return $this->routerProcessorName;
     }
 
+    /**
+     * @deprecated
+     */
     public function createTransportRouterTopicName(string $name): string
     {
         return strtolower(implode('.', array_filter([trim($this->prefix), trim($name)])));
     }
 
+    /**
+     * @deprecated
+     */
     public function createTransportQueueName(string $name): string
     {
         return strtolower(implode('.', array_filter([trim($this->prefix), trim($this->appName), trim($name)])));
     }
 
+    /**
+     * @deprecated
+     *
+     * @param null|mixed $default
+     */
     public function getTransportOption(string $name, $default = null)
     {
         return array_key_exists($name, $this->transportConfig) ? $this->transportConfig[$name] : $default;
diff --git a/Tests/Client/ConfigTest.php b/Tests/Client/ConfigTest.php
index 1ecb0f9..d616b56 100644
--- a/Tests/Client/ConfigTest.php
+++ b/Tests/Client/ConfigTest.php
@@ -7,6 +7,68 @@
 
 class ConfigTest extends TestCase
 {
+    public function testShouldReturnPrefixSetInConstructor()
+    {
+        $config = new Config(
+            'thePrefix',
+            'aApp',
+            'aRouterTopicName',
+            'aRouterQueueName',
+            'aDefaultQueueName',
+            'aRouterProcessorName'
+        );
+
+        $this->assertEquals('thePrefix', $config->getPrefix());
+    }
+
+    /**
+     * @dataProvider provideEmptyStrings
+     */
+    public function testShouldTrimReturnPrefixSetInConstructor(string $empty)
+    {
+        $config = new Config(
+            $empty,
+            'aApp',
+            'aRouterTopicName',
+            'aRouterQueueName',
+            'aDefaultQueueName',
+            'aRouterProcessorName'
+        );
+
+        $this->assertSame('', $config->getPrefix());
+    }
+
+    public function testShouldReturnAppNameSetInConstructor()
+    {
+        $config = new Config(
+            'aPrefix',
+            'theApp',
+            'aRouterTopicName',
+            'aRouterQueueName',
+            'aDefaultQueueName',
+            'aRouterProcessorName'
+        );
+
+        $this->assertEquals('theApp', $config->getAppName());
+    }
+
+    /**
+     * @dataProvider provideEmptyStrings
+     */
+    public function testShouldTrimReturnAppNameSetInConstructor(string $empty)
+    {
+        $config = new Config(
+            'aPrefix',
+            $empty,
+            'aRouterTopicName',
+            'aRouterQueueName',
+            'aDefaultQueueName',
+            'aRouterProcessorName'
+        );
+
+        $this->assertSame('', $config->getAppName());
+    }
+
     public function testShouldReturnRouterProcessorNameSetInConstructor()
     {
         $config = new Config(
@@ -142,4 +204,83 @@ public function testShouldCreateDefaultConfig()
         $this->assertSame('default', $config->getRouterQueueName());
         $this->assertSame('router', $config->getRouterTopicName());
     }
+
+    /**
+     * @dataProvider provideEmptyStrings
+     */
+    public function testThrowIfRouterTopicNameIsEmpty(string $empty)
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Router topic is empty.');
+        new Config(
+            '',
+            '',
+            $empty,
+            'aRouterQueueName',
+            'aDefaultQueueName',
+            'aRouterProcessorName'
+        );
+    }
+
+    /**
+     * @dataProvider provideEmptyStrings
+     */
+    public function testThrowIfRouterQueueNameIsEmpty(string $empty)
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Router queue is empty.');
+        new Config(
+            '',
+            '',
+            'aRouterTopicName',
+            $empty,
+            'aDefaultQueueName',
+            'aRouterProcessorName'
+        );
+    }
+
+    /**
+     * @dataProvider provideEmptyStrings
+     */
+    public function testThrowIfDefaultQueueNameIsEmpty(string $empty)
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Default processor queue name is empty.');
+        new Config(
+            '',
+            '',
+            'aRouterTopicName',
+            'aRouterQueueName',
+            $empty,
+            'aRouterProcessorName'
+        );
+    }
+
+    /**
+     * @dataProvider provideEmptyStrings
+     */
+    public function testThrowIfRouterProcessorNameIsEmpty(string $empty)
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Router processor name is empty.');
+        new Config(
+            '',
+            '',
+            'aRouterTopicName',
+            'aRouterQueueName',
+            'aDefaultQueueName',
+            $empty
+        );
+    }
+
+    public function provideEmptyStrings()
+    {
+        yield [''];
+
+        yield [' '];
+
+        yield ['  '];
+
+        yield ["\t"];
+    }
 }

From f0c2dd4245c5bfae19013b1209ea65f10df2953e Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 14 Sep 2018 19:15:42 +0300
Subject: [PATCH 113/337] [client] Introduce GenericDriver.

---
 Client/Driver/GenericDriver.php           | 230 ++++++++
 Tests/Client/Driver/GenericDriverTest.php | 683 ++++++++++++++++++++++
 2 files changed, 913 insertions(+)
 create mode 100644 Client/Driver/GenericDriver.php
 create mode 100644 Tests/Client/Driver/GenericDriverTest.php

diff --git a/Client/Driver/GenericDriver.php b/Client/Driver/GenericDriver.php
new file mode 100644
index 0000000..5538802
--- /dev/null
+++ b/Client/Driver/GenericDriver.php
@@ -0,0 +1,230 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Enqueue\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
+use Interop\Queue\PsrContext;
+use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrQueue;
+use Interop\Queue\PsrTopic;
+use Psr\Log\LoggerInterface;
+
+class GenericDriver implements DriverInterface
+{
+    /**
+     * @var PsrContext
+     */
+    private $context;
+
+    /**
+     * @var Config
+     */
+    private $config;
+
+    /**
+     * @var RouteCollection
+     */
+    private $routeCollection;
+
+    public function __construct(
+        PsrContext $context,
+        Config $config,
+        RouteCollection $routeCollection
+    ) {
+        $this->context = $context;
+        $this->config = $config;
+        $this->routeCollection = $routeCollection;
+    }
+
+    public function sendToRouter(Message $message): void
+    {
+        if ($message->getProperty(Config::PARAMETER_COMMAND_NAME)) {
+            throw new \LogicException('Command must not be send to router but go directly to its processor.');
+        }
+        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
+            throw new \LogicException('Topic name parameter is required but is not set');
+        }
+
+        $topic = $this->createRouterTopic();
+        $transportMessage = $this->createTransportMessage($message);
+
+        $this->doSendToRouter($topic, $transportMessage);
+    }
+
+    public function sendToProcessor(Message $message): void
+    {
+        $processor = $message->getProperty(Config::PARAMETER_PROCESSOR_NAME);
+        if (false == $processor) {
+            throw new \LogicException('Processor name parameter is required but is not set');
+        }
+
+        $topic = $message->getProperty(Config::PARAMETER_TOPIC_NAME);
+        $command = $message->getProperty(Config::PARAMETER_COMMAND_NAME);
+
+        /** @var Route $route */
+        $route = null;
+        if ($topic) {
+            $route = $this->routeCollection->topicAndProcessor($topic, $processor);
+            if (false == $route) {
+                throw new \LogicException(sprintf('There is no route for topic "%s" and processor "%s"', $topic, $processor));
+            }
+        } elseif ($command) {
+            $route = $this->routeCollection->command($command);
+            if (false == $route) {
+                throw new \LogicException(sprintf('There is no route for command "%s" and processor "%s"', $command, $processor));
+            }
+
+            if ($processor !== $route->getProcessor()) {
+                throw new \LogicException(sprintf('The command "%s" route was found but processors do not match. Given "%s", route "%s"', $command, $processor, $route->getProcessor()));
+            }
+        } else {
+            throw new \LogicException('Either topic or command parameter must be set.');
+        }
+
+        $transportMessage = $this->createTransportMessage($message);
+        $queue = $this->createRouteQueue($route);
+
+        $this->doSendToProcessor($queue, $transportMessage);
+    }
+
+    public function setupBroker(LoggerInterface $logger = null): void
+    {
+    }
+
+    public function createQueue(string $clientQueueName): PsrQueue
+    {
+        $transportName = $this->createTransportQueueName($clientQueueName, true);
+
+        return $this->context->createQueue($transportName);
+    }
+
+    public function createTransportMessage(Message $clientMessage): PsrMessage
+    {
+        $headers = $clientMessage->getHeaders();
+        $properties = $clientMessage->getProperties();
+
+        $transportMessage = $this->context->createMessage();
+        $transportMessage->setBody($clientMessage->getBody());
+        $transportMessage->setHeaders($headers);
+        $transportMessage->setProperties($properties);
+        $transportMessage->setMessageId($clientMessage->getMessageId());
+        $transportMessage->setTimestamp($clientMessage->getTimestamp());
+        $transportMessage->setReplyTo($clientMessage->getReplyTo());
+        $transportMessage->setCorrelationId($clientMessage->getCorrelationId());
+
+        if ($contentType = $clientMessage->getContentType()) {
+            $transportMessage->setProperty('X-Enqueue-Content-Type', $contentType);
+        }
+
+        if ($priority = $clientMessage->getPriority()) {
+            $transportMessage->setProperty('X-Enqueue-Priority', $priority);
+        }
+
+        if ($expire = $clientMessage->getExpire()) {
+            $transportMessage->setProperty('X-Enqueue-Expire', $expire);
+        }
+
+        if ($delay = $clientMessage->getDelay()) {
+            $transportMessage->setProperty('X-Enqueue-Delay', $delay);
+        }
+
+        return $transportMessage;
+    }
+
+    public function createClientMessage(PsrMessage $transportMessage): Message
+    {
+        $clientMessage = new Message();
+
+        $clientMessage->setBody($transportMessage->getBody());
+        $clientMessage->setHeaders($transportMessage->getHeaders());
+        $clientMessage->setProperties($transportMessage->getProperties());
+
+        $clientMessage->setMessageId($transportMessage->getMessageId());
+        $clientMessage->setTimestamp($transportMessage->getTimestamp());
+        $clientMessage->setPriority(MessagePriority::NORMAL);
+        $clientMessage->setReplyTo($transportMessage->getReplyTo());
+        $clientMessage->setCorrelationId($transportMessage->getCorrelationId());
+
+        if ($contentType = $transportMessage->getProperty('X-Enqueue-Content-Type')) {
+            $clientMessage->setContentType($contentType);
+        }
+
+        if ($priority = $transportMessage->getProperty('X-Enqueue-Priority')) {
+            $clientMessage->setPriority($priority);
+        }
+
+        if ($delay = $transportMessage->getProperty('X-Enqueue-Delay')) {
+            $clientMessage->setDelay((int) $delay);
+        }
+
+        if ($expire = $transportMessage->getProperty('X-Enqueue-Expire')) {
+            $clientMessage->setExpire((int) $expire);
+        }
+
+        return $clientMessage;
+    }
+
+    public function getConfig(): Config
+    {
+        return $this->config;
+    }
+
+    public function getContext(): PsrContext
+    {
+        return $this->context;
+    }
+
+    public function getRouteCollection(): RouteCollection
+    {
+        return $this->routeCollection;
+    }
+
+    protected function doSendToRouter(PsrTopic $topic, PsrMessage $transportMessage): void
+    {
+        $this->context->createProducer()->send($topic, $transportMessage);
+    }
+
+    protected function doSendToProcessor(PsrQueue $queue, PsrMessage $transportMessage): void
+    {
+        $this->context->createProducer()->send($queue, $transportMessage);
+    }
+
+    protected function createRouterTopic(): PsrTopic
+    {
+        return $this->context->createTopic(
+            $this->createTransportRouterTopicName($this->config->getRouterTopicName(), true)
+        );
+    }
+
+    protected function createRouteQueue(Route $route): PsrQueue
+    {
+        $transportName = $this->createTransportQueueName(
+            $route->getQueue() ?: $this->config->getDefaultProcessorQueueName(),
+            $route->isPrefixQueue()
+        );
+
+        return $this->context->createQueue($transportName);
+    }
+
+    protected function createTransportRouterTopicName(string $name, bool $prefix): string
+    {
+        $clientPrefix = $prefix ? $this->config->getPrefix() : '';
+
+        return strtolower(implode($this->config->getSeparator(), array_filter([$clientPrefix, $name])));
+    }
+
+    protected function createTransportQueueName(string $name, bool $prefix): string
+    {
+        $clientPrefix = $prefix ? $this->config->getPrefix() : '';
+        $clientAppName = $prefix ? $this->config->getAppName() : '';
+
+        return strtolower(implode($this->config->getSeparator(), array_filter([$clientPrefix, $clientAppName, $name])));
+    }
+}
diff --git a/Tests/Client/Driver/GenericDriverTest.php b/Tests/Client/Driver/GenericDriverTest.php
new file mode 100644
index 0000000..ddf85e9
--- /dev/null
+++ b/Tests/Client/Driver/GenericDriverTest.php
@@ -0,0 +1,683 @@
+<?php
+
+namespace Enqueue\Tests\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\Driver\GenericDriver;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
+use Enqueue\Null\NullMessage;
+use Enqueue\Null\NullQueue;
+use Enqueue\Null\NullTopic;
+use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrContext;
+use Interop\Queue\PsrProducer;
+use PHPUnit\Framework\TestCase;
+
+class GenericDriverTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementsDriverInterface()
+    {
+        $this->assertClassImplements(DriverInterface::class, GenericDriver::class);
+    }
+
+    public function testCouldBeConstructedWithRequiredArguments()
+    {
+        new GenericDriver(
+            $this->createContextMock(),
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+    }
+
+    public function testShouldReturnPsrContextSetInConstructor()
+    {
+        $context = $this->createContextMock();
+
+        $driver = new GenericDriver($context, $this->createDummyConfig(), new RouteCollection([]));
+
+        $this->assertSame($context, $driver->getContext());
+    }
+
+    public function testShouldReturnConfigObjectSetInConstructor()
+    {
+        $config = $this->createDummyConfig();
+
+        $driver = new GenericDriver($this->createContextMock(), $config, new RouteCollection([]));
+
+        $this->assertSame($config, $driver->getConfig());
+    }
+
+    public function testShouldReturnRouteCollectionSetInConstructor()
+    {
+        $routeCollection = new RouteCollection([]);
+
+        $driver = new GenericDriver($this->createContextMock(), $this->createDummyConfig(), $routeCollection);
+
+        $this->assertSame($routeCollection, $driver->getRouteCollection());
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithPrefixAndAppName()
+    {
+        $expectedQueue = new NullQueue('aName');
+
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.anappname.afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $config = new Config(
+            'aPrefix',
+            'anAppName',
+            'aRouterTopicName',
+            'aRouterQueueName',
+            'aDefaultQueue',
+            'aRouterProcessor',
+            []
+        );
+
+        $driver = new GenericDriver($context, $config, new RouteCollection([]));
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithPrefixWithoutAppName()
+    {
+        $expectedQueue = new NullQueue('aName');
+
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $config = new Config(
+            'aPrefix',
+            '',
+            'aRouterTopicName',
+            'aRouterQueueName',
+            'aDefaultQueue',
+            'aRouterProcessor',
+            []
+        );
+
+        $driver = new GenericDriver($context, $config, new RouteCollection([]));
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithAppNameAndWithoutPrefix()
+    {
+        $expectedQueue = new NullQueue('aName');
+
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('anappname.afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $config = new Config(
+            '',
+            'anAppName',
+            'aRouterTopicName',
+            'aRouterQueueName',
+            'aDefaultQueue',
+            'aRouterProcessor',
+            []
+        );
+
+        $driver = new GenericDriver($context, $config, new RouteCollection([]));
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithoutPrefixAndAppName()
+    {
+        $expectedQueue = new NullQueue('aName');
+
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $config = new Config(
+            '',
+            '',
+            'aRouterTopicName',
+            'aRouterQueueName',
+            'aDefaultQueue',
+            'aRouterProcessor',
+            []
+        );
+
+        $driver = new GenericDriver($context, $config, new RouteCollection([]));
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldCreateAndReturnQueueInstance()
+    {
+        $expectedQueue = new NullQueue('aName');
+
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = new GenericDriver($context, $this->createDummyConfig(), new RouteCollection([]));
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldConvertTransportMessageToClientMessage()
+    {
+        $transportMessage = new NullMessage();
+        $transportMessage->setBody('body');
+        $transportMessage->setHeaders(['hkey' => 'hval']);
+        $transportMessage->setProperty('pkey', 'pval');
+        $transportMessage->setProperty('X-Enqueue-Content-Type', 'theContentType');
+        $transportMessage->setProperty('X-Enqueue-Expire', '22');
+        $transportMessage->setProperty('X-Enqueue-Priority', 'thePriority');
+        $transportMessage->setProperty('X-Enqueue-Delay', '44');
+        $transportMessage->setMessageId('theMessageId');
+        $transportMessage->setTimestamp(1000);
+        $transportMessage->setReplyTo('theReplyTo');
+        $transportMessage->setCorrelationId('theCorrelationId');
+
+        $driver = new GenericDriver(
+            $this->createContextMock(),
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $clientMessage = $driver->createClientMessage($transportMessage);
+
+        $this->assertInstanceOf(Message::class, $clientMessage);
+        $this->assertSame('body', $clientMessage->getBody());
+        $this->assertEquals([
+            'hkey' => 'hval',
+            'message_id' => 'theMessageId',
+            'timestamp' => 1000,
+            'reply_to' => 'theReplyTo',
+            'correlation_id' => 'theCorrelationId',
+        ], $clientMessage->getHeaders());
+        $this->assertEquals([
+            'pkey' => 'pval',
+            'X-Enqueue-Content-Type' => 'theContentType',
+            'X-Enqueue-Expire' => '22',
+            'X-Enqueue-Priority' => 'thePriority',
+            'X-Enqueue-Delay' => '44',
+        ], $clientMessage->getProperties());
+        $this->assertSame('theMessageId', $clientMessage->getMessageId());
+        $this->assertSame(22, $clientMessage->getExpire());
+        $this->assertSame(44, $clientMessage->getDelay());
+        $this->assertSame('thePriority', $clientMessage->getPriority());
+        $this->assertSame('theContentType', $clientMessage->getContentType());
+        $this->assertSame(1000, $clientMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
+    }
+
+    public function testShouldConvertClientMessageToTransportMessage()
+    {
+        $clientMessage = new Message();
+        $clientMessage->setBody('body');
+        $clientMessage->setHeaders(['hkey' => 'hval']);
+        $clientMessage->setProperties(['pkey' => 'pval']);
+        $clientMessage->setContentType('ContentType');
+        $clientMessage->setExpire(123);
+        $clientMessage->setDelay(345);
+        $clientMessage->setPriority('thePriority');
+        $clientMessage->setMessageId('theMessageId');
+        $clientMessage->setTimestamp(1000);
+        $clientMessage->setReplyTo('theReplyTo');
+        $clientMessage->setCorrelationId('theCorrelationId');
+
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn(new NullMessage())
+        ;
+
+        $driver = new GenericDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $transportMessage = $driver->createTransportMessage($clientMessage);
+
+        $this->assertInstanceOf(NullMessage::class, $transportMessage);
+        $this->assertSame('body', $transportMessage->getBody());
+        $this->assertEquals([
+            'hkey' => 'hval',
+            'message_id' => 'theMessageId',
+            'timestamp' => 1000,
+            'reply_to' => 'theReplyTo',
+            'correlation_id' => 'theCorrelationId',
+        ], $transportMessage->getHeaders());
+        $this->assertEquals([
+            'pkey' => 'pval',
+            'X-Enqueue-Content-Type' => 'ContentType',
+            'X-Enqueue-Priority' => 'thePriority',
+            'X-Enqueue-Expire' => 123,
+            'X-Enqueue-Delay' => 345,
+        ], $transportMessage->getProperties());
+        $this->assertSame('theMessageId', $transportMessage->getMessageId());
+        $this->assertSame(1000, $transportMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
+    }
+
+    public function testShouldSendMessageToRouter()
+    {
+        $topic = new NullTopic('');
+        $transportMessage = new NullMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createTopic')
+            ->willReturn($topic)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new GenericDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+
+        $driver->sendToRouter($message);
+    }
+
+    public function testThrowIfTopicIsNotSetOnSendToRouter()
+    {
+        $driver = new GenericDriver(
+            $this->createContextMock(),
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Topic name parameter is required but is not set');
+
+        $driver->sendToRouter(new Message());
+    }
+
+    public function testThrowIfCommandSetOnSendToRouter()
+    {
+        $driver = new GenericDriver(
+            $this->createContextMock(),
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'aCommand');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Command must not be send to router but go directly to its processor.');
+
+        $driver->sendToRouter($message);
+    }
+
+    public function testShouldSendTopicMessageToProcessorToDefaultQueue()
+    {
+        $queue = new NullQueue('');
+        $transportMessage = new NullMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('default')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new GenericDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('topic', Route::TOPIC, 'processor'),
+            ])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldSendTopicMessageToProcessorToCustomQueue()
+    {
+        $queue = new NullQueue('');
+        $transportMessage = new NullMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('custom')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new GenericDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('topic', Route::TOPIC, 'processor', ['queue' => 'custom']),
+            ])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testThrowIfNoRouteFoundForTopicMessageOnSendToProcessor()
+    {
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->never())
+            ->method('createProducer')
+        ;
+        $context
+            ->expects($this->never())
+            ->method('createMessage')
+        ;
+
+        $driver = new GenericDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('There is no route for topic "topic" and processor "processor"');
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldSendCommandMessageToProcessorToDefaultQueue()
+    {
+        $queue = new NullQueue('');
+        $transportMessage = new NullMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('default')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new GenericDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('command', Route::COMMAND, 'processor'),
+            ])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldSendCommandMessageToProcessorToCustomQueue()
+    {
+        $queue = new NullQueue('');
+        $transportMessage = new NullMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('custom')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = new GenericDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('command', Route::COMMAND, 'processor', ['queue' => 'custom']),
+            ])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testThrowIfNoRouteFoundForCommandMessageOnSendToProcessor()
+    {
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->never())
+            ->method('createProducer')
+        ;
+        $context
+            ->expects($this->never())
+            ->method('createMessage')
+        ;
+
+        $driver = new GenericDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('There is no route for command "command" and processor "processor"');
+        $driver->sendToProcessor($message);
+    }
+
+    public function testThrowIfRouteProcessorDoesNotMatchMessageOneOnSendToProcessor()
+    {
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->never())
+            ->method('createProducer')
+        ;
+        $context
+            ->expects($this->never())
+            ->method('createMessage')
+        ;
+
+        $driver = new GenericDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('command', Route::COMMAND, 'anotherProcessor', ['queue' => 'custom']),
+            ])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The command "command" route was found but processors do not match. Given "processor", route "anotherProcessor"');
+        $driver->sendToProcessor($message);
+    }
+
+    public function testThrowIfProcessorIsNotSetOnSendToProcessor()
+    {
+        $driver = new GenericDriver(
+            $this->createContextMock(),
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Processor name parameter is required but is not set');
+
+        $driver->sendToProcessor(new Message());
+    }
+
+    public function testThrowIfNeitherTopicNorCommandAreSentOnSendToProcessor()
+    {
+        $driver = new GenericDriver(
+            $this->createContextMock(),
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Queue name parameter is required but is not set');
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Either topic or command parameter must be set.');
+        $driver->sendToProcessor($message);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createContextMock(): PsrContext
+    {
+        return $this->createMock(PsrContext::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createProducerMock(): PsrProducer
+    {
+        return $this->createMock(PsrProducer::class);
+    }
+
+    private function createDummyConfig(): Config
+    {
+        return Config::create('aPrefix');
+    }
+}

From 1e7c955df034655d3b421c9edf877e926ff87c9f Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 17 Sep 2018 13:13:15 +0300
Subject: [PATCH 114/337] [client] Add generic driver tests.

---
 Tests/Client/Driver/GenericDriverTest.php     | 642 +----------------
 .../Client/Driver/GenericDriverTestsTrait.php | 682 ++++++++++++++++++
 2 files changed, 702 insertions(+), 622 deletions(-)
 create mode 100644 Tests/Client/Driver/GenericDriverTestsTrait.php

diff --git a/Tests/Client/Driver/GenericDriverTest.php b/Tests/Client/Driver/GenericDriverTest.php
index ddf85e9..31f92a5 100644
--- a/Tests/Client/Driver/GenericDriverTest.php
+++ b/Tests/Client/Driver/GenericDriverTest.php
@@ -2,282 +2,64 @@
 
 namespace Enqueue\Tests\Client\Driver;
 
-use Enqueue\Client\Config;
 use Enqueue\Client\Driver\GenericDriver;
 use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\Route;
-use Enqueue\Client\RouteCollection;
+use Enqueue\Client\MessagePriority;
 use Enqueue\Null\NullMessage;
 use Enqueue\Null\NullQueue;
 use Enqueue\Null\NullTopic;
 use Enqueue\Test\ClassExtensionTrait;
 use Interop\Queue\PsrContext;
+use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrProducer;
+use Interop\Queue\PsrQueue;
+use Interop\Queue\PsrTopic;
 use PHPUnit\Framework\TestCase;
 
 class GenericDriverTest extends TestCase
 {
     use ClassExtensionTrait;
+    use GenericDriverTestsTrait;
 
     public function testShouldImplementsDriverInterface()
     {
         $this->assertClassImplements(DriverInterface::class, GenericDriver::class);
     }
 
-    public function testCouldBeConstructedWithRequiredArguments()
+    protected function createDriver(...$args): DriverInterface
     {
-        new GenericDriver(
-            $this->createContextMock(),
-            $this->createDummyConfig(),
-            new RouteCollection([])
-        );
+        return new GenericDriver(...$args);
     }
 
-    public function testShouldReturnPsrContextSetInConstructor()
+    protected function createContextMock(): PsrContext
     {
-        $context = $this->createContextMock();
-
-        $driver = new GenericDriver($context, $this->createDummyConfig(), new RouteCollection([]));
-
-        $this->assertSame($context, $driver->getContext());
+        return $this->createMock(PsrContext::class);
     }
 
-    public function testShouldReturnConfigObjectSetInConstructor()
+    protected function createProducerMock(): PsrProducer
     {
-        $config = $this->createDummyConfig();
-
-        $driver = new GenericDriver($this->createContextMock(), $config, new RouteCollection([]));
-
-        $this->assertSame($config, $driver->getConfig());
+        return $this->createMock(PsrProducer::class);
     }
 
-    public function testShouldReturnRouteCollectionSetInConstructor()
+    protected function createQueue(string $name): PsrQueue
     {
-        $routeCollection = new RouteCollection([]);
-
-        $driver = new GenericDriver($this->createContextMock(), $this->createDummyConfig(), $routeCollection);
-
-        $this->assertSame($routeCollection, $driver->getRouteCollection());
+        return new NullQueue($name);
     }
 
-    public function testShouldCreateAndReturnQueueInstanceWithPrefixAndAppName()
+    protected function createTopic(string $name): PsrTopic
     {
-        $expectedQueue = new NullQueue('aName');
-
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix.anappname.afooqueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $config = new Config(
-            'aPrefix',
-            'anAppName',
-            'aRouterTopicName',
-            'aRouterQueueName',
-            'aDefaultQueue',
-            'aRouterProcessor',
-            []
-        );
-
-        $driver = new GenericDriver($context, $config, new RouteCollection([]));
-
-        $queue = $driver->createQueue('aFooQueue');
-
-        $this->assertSame($expectedQueue, $queue);
+        return new NullTopic($name);
     }
 
-    public function testShouldCreateAndReturnQueueInstanceWithPrefixWithoutAppName()
+    protected function createMessage(): PsrMessage
     {
-        $expectedQueue = new NullQueue('aName');
-
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix.afooqueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $config = new Config(
-            'aPrefix',
-            '',
-            'aRouterTopicName',
-            'aRouterQueueName',
-            'aDefaultQueue',
-            'aRouterProcessor',
-            []
-        );
-
-        $driver = new GenericDriver($context, $config, new RouteCollection([]));
-
-        $queue = $driver->createQueue('aFooQueue');
-
-        $this->assertSame($expectedQueue, $queue);
+        return new NullMessage();
     }
 
-    public function testShouldCreateAndReturnQueueInstanceWithAppNameAndWithoutPrefix()
+    protected function assertTransportMessage(PsrMessage $transportMessage): void
     {
-        $expectedQueue = new NullQueue('aName');
-
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('anappname.afooqueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $config = new Config(
-            '',
-            'anAppName',
-            'aRouterTopicName',
-            'aRouterQueueName',
-            'aDefaultQueue',
-            'aRouterProcessor',
-            []
-        );
-
-        $driver = new GenericDriver($context, $config, new RouteCollection([]));
-
-        $queue = $driver->createQueue('aFooQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldCreateAndReturnQueueInstanceWithoutPrefixAndAppName()
-    {
-        $expectedQueue = new NullQueue('aName');
-
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('afooqueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $config = new Config(
-            '',
-            '',
-            'aRouterTopicName',
-            'aRouterQueueName',
-            'aDefaultQueue',
-            'aRouterProcessor',
-            []
-        );
-
-        $driver = new GenericDriver($context, $config, new RouteCollection([]));
-
-        $queue = $driver->createQueue('aFooQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldCreateAndReturnQueueInstance()
-    {
-        $expectedQueue = new NullQueue('aName');
-
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix.afooqueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new GenericDriver($context, $this->createDummyConfig(), new RouteCollection([]));
-
-        $queue = $driver->createQueue('aFooQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldConvertTransportMessageToClientMessage()
-    {
-        $transportMessage = new NullMessage();
-        $transportMessage->setBody('body');
-        $transportMessage->setHeaders(['hkey' => 'hval']);
-        $transportMessage->setProperty('pkey', 'pval');
-        $transportMessage->setProperty('X-Enqueue-Content-Type', 'theContentType');
-        $transportMessage->setProperty('X-Enqueue-Expire', '22');
-        $transportMessage->setProperty('X-Enqueue-Priority', 'thePriority');
-        $transportMessage->setProperty('X-Enqueue-Delay', '44');
-        $transportMessage->setMessageId('theMessageId');
-        $transportMessage->setTimestamp(1000);
-        $transportMessage->setReplyTo('theReplyTo');
-        $transportMessage->setCorrelationId('theCorrelationId');
-
-        $driver = new GenericDriver(
-            $this->createContextMock(),
-            $this->createDummyConfig(),
-            new RouteCollection([])
-        );
-
-        $clientMessage = $driver->createClientMessage($transportMessage);
-
-        $this->assertInstanceOf(Message::class, $clientMessage);
-        $this->assertSame('body', $clientMessage->getBody());
-        $this->assertEquals([
-            'hkey' => 'hval',
-            'message_id' => 'theMessageId',
-            'timestamp' => 1000,
-            'reply_to' => 'theReplyTo',
-            'correlation_id' => 'theCorrelationId',
-        ], $clientMessage->getHeaders());
-        $this->assertEquals([
-            'pkey' => 'pval',
-            'X-Enqueue-Content-Type' => 'theContentType',
-            'X-Enqueue-Expire' => '22',
-            'X-Enqueue-Priority' => 'thePriority',
-            'X-Enqueue-Delay' => '44',
-        ], $clientMessage->getProperties());
-        $this->assertSame('theMessageId', $clientMessage->getMessageId());
-        $this->assertSame(22, $clientMessage->getExpire());
-        $this->assertSame(44, $clientMessage->getDelay());
-        $this->assertSame('thePriority', $clientMessage->getPriority());
-        $this->assertSame('theContentType', $clientMessage->getContentType());
-        $this->assertSame(1000, $clientMessage->getTimestamp());
-        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
-    }
-
-    public function testShouldConvertClientMessageToTransportMessage()
-    {
-        $clientMessage = new Message();
-        $clientMessage->setBody('body');
-        $clientMessage->setHeaders(['hkey' => 'hval']);
-        $clientMessage->setProperties(['pkey' => 'pval']);
-        $clientMessage->setContentType('ContentType');
-        $clientMessage->setExpire(123);
-        $clientMessage->setDelay(345);
-        $clientMessage->setPriority('thePriority');
-        $clientMessage->setMessageId('theMessageId');
-        $clientMessage->setTimestamp(1000);
-        $clientMessage->setReplyTo('theReplyTo');
-        $clientMessage->setCorrelationId('theCorrelationId');
-
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn(new NullMessage())
-        ;
-
-        $driver = new GenericDriver(
-            $context,
-            $this->createDummyConfig(),
-            new RouteCollection([])
-        );
-
-        $transportMessage = $driver->createTransportMessage($clientMessage);
-
-        $this->assertInstanceOf(NullMessage::class, $transportMessage);
         $this->assertSame('body', $transportMessage->getBody());
-        $this->assertEquals([
+        $this->assertArraySubset([
             'hkey' => 'hval',
             'message_id' => 'theMessageId',
             'timestamp' => 1000,
@@ -287,7 +69,7 @@ public function testShouldConvertClientMessageToTransportMessage()
         $this->assertEquals([
             'pkey' => 'pval',
             'X-Enqueue-Content-Type' => 'ContentType',
-            'X-Enqueue-Priority' => 'thePriority',
+            'X-Enqueue-Priority' => MessagePriority::HIGH,
             'X-Enqueue-Expire' => 123,
             'X-Enqueue-Delay' => 345,
         ], $transportMessage->getProperties());
@@ -296,388 +78,4 @@ public function testShouldConvertClientMessageToTransportMessage()
         $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
         $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
     }
-
-    public function testShouldSendMessageToRouter()
-    {
-        $topic = new NullTopic('');
-        $transportMessage = new NullMessage();
-
-        $producer = $this->createProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createTopic')
-            ->willReturn($topic)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new GenericDriver(
-            $context,
-            $this->createDummyConfig(),
-            new RouteCollection([])
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-
-        $driver->sendToRouter($message);
-    }
-
-    public function testThrowIfTopicIsNotSetOnSendToRouter()
-    {
-        $driver = new GenericDriver(
-            $this->createContextMock(),
-            $this->createDummyConfig(),
-            new RouteCollection([])
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Topic name parameter is required but is not set');
-
-        $driver->sendToRouter(new Message());
-    }
-
-    public function testThrowIfCommandSetOnSendToRouter()
-    {
-        $driver = new GenericDriver(
-            $this->createContextMock(),
-            $this->createDummyConfig(),
-            new RouteCollection([])
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'aCommand');
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Command must not be send to router but go directly to its processor.');
-
-        $driver->sendToRouter($message);
-    }
-
-    public function testShouldSendTopicMessageToProcessorToDefaultQueue()
-    {
-        $queue = new NullQueue('');
-        $transportMessage = new NullMessage();
-
-        $producer = $this->createProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('default')
-            ->willReturn($queue)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new GenericDriver(
-            $context,
-            $this->createDummyConfig(),
-            new RouteCollection([
-                new Route('topic', Route::TOPIC, 'processor'),
-            ])
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-
-        $driver->sendToProcessor($message);
-    }
-
-    public function testShouldSendTopicMessageToProcessorToCustomQueue()
-    {
-        $queue = new NullQueue('');
-        $transportMessage = new NullMessage();
-
-        $producer = $this->createProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('custom')
-            ->willReturn($queue)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new GenericDriver(
-            $context,
-            $this->createDummyConfig(),
-            new RouteCollection([
-                new Route('topic', Route::TOPIC, 'processor', ['queue' => 'custom']),
-            ])
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-
-        $driver->sendToProcessor($message);
-    }
-
-    public function testThrowIfNoRouteFoundForTopicMessageOnSendToProcessor()
-    {
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->never())
-            ->method('createProducer')
-        ;
-        $context
-            ->expects($this->never())
-            ->method('createMessage')
-        ;
-
-        $driver = new GenericDriver(
-            $context,
-            $this->createDummyConfig(),
-            new RouteCollection([])
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('There is no route for topic "topic" and processor "processor"');
-        $driver->sendToProcessor($message);
-    }
-
-    public function testShouldSendCommandMessageToProcessorToDefaultQueue()
-    {
-        $queue = new NullQueue('');
-        $transportMessage = new NullMessage();
-
-        $producer = $this->createProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('default')
-            ->willReturn($queue)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new GenericDriver(
-            $context,
-            $this->createDummyConfig(),
-            new RouteCollection([
-                new Route('command', Route::COMMAND, 'processor'),
-            ])
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-
-        $driver->sendToProcessor($message);
-    }
-
-    public function testShouldSendCommandMessageToProcessorToCustomQueue()
-    {
-        $queue = new NullQueue('');
-        $transportMessage = new NullMessage();
-
-        $producer = $this->createProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('custom')
-            ->willReturn($queue)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new GenericDriver(
-            $context,
-            $this->createDummyConfig(),
-            new RouteCollection([
-                new Route('command', Route::COMMAND, 'processor', ['queue' => 'custom']),
-            ])
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-
-        $driver->sendToProcessor($message);
-    }
-
-    public function testThrowIfNoRouteFoundForCommandMessageOnSendToProcessor()
-    {
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->never())
-            ->method('createProducer')
-        ;
-        $context
-            ->expects($this->never())
-            ->method('createMessage')
-        ;
-
-        $driver = new GenericDriver(
-            $context,
-            $this->createDummyConfig(),
-            new RouteCollection([])
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('There is no route for command "command" and processor "processor"');
-        $driver->sendToProcessor($message);
-    }
-
-    public function testThrowIfRouteProcessorDoesNotMatchMessageOneOnSendToProcessor()
-    {
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->never())
-            ->method('createProducer')
-        ;
-        $context
-            ->expects($this->never())
-            ->method('createMessage')
-        ;
-
-        $driver = new GenericDriver(
-            $context,
-            $this->createDummyConfig(),
-            new RouteCollection([
-                new Route('command', Route::COMMAND, 'anotherProcessor', ['queue' => 'custom']),
-            ])
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The command "command" route was found but processors do not match. Given "processor", route "anotherProcessor"');
-        $driver->sendToProcessor($message);
-    }
-
-    public function testThrowIfProcessorIsNotSetOnSendToProcessor()
-    {
-        $driver = new GenericDriver(
-            $this->createContextMock(),
-            $this->createDummyConfig(),
-            new RouteCollection([])
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Processor name parameter is required but is not set');
-
-        $driver->sendToProcessor(new Message());
-    }
-
-    public function testThrowIfNeitherTopicNorCommandAreSentOnSendToProcessor()
-    {
-        $driver = new GenericDriver(
-            $this->createContextMock(),
-            $this->createDummyConfig(),
-            new RouteCollection([])
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Queue name parameter is required but is not set');
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Either topic or command parameter must be set.');
-        $driver->sendToProcessor($message);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
-     */
-    private function createContextMock(): PsrContext
-    {
-        return $this->createMock(PsrContext::class);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
-     */
-    private function createProducerMock(): PsrProducer
-    {
-        return $this->createMock(PsrProducer::class);
-    }
-
-    private function createDummyConfig(): Config
-    {
-        return Config::create('aPrefix');
-    }
 }
diff --git a/Tests/Client/Driver/GenericDriverTestsTrait.php b/Tests/Client/Driver/GenericDriverTestsTrait.php
new file mode 100644
index 0000000..a297a7c
--- /dev/null
+++ b/Tests/Client/Driver/GenericDriverTestsTrait.php
@@ -0,0 +1,682 @@
+<?php
+
+namespace Enqueue\Tests\Client\Driver;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Message;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
+use Interop\Queue\PsrContext;
+use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrProducer;
+use Interop\Queue\PsrQueue;
+use Interop\Queue\PsrTopic;
+
+trait GenericDriverTestsTrait
+{
+    public function testCouldBeConstructedWithRequiredArguments()
+    {
+        $driver = $this->createDriver(
+            $this->createContextMock(),
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $this->assertInstanceOf(DriverInterface::class, $driver);
+    }
+
+    public function testShouldReturnPsrContextSetInConstructor()
+    {
+        $context = $this->createContextMock();
+
+        $driver = $this->createDriver($context, $this->createDummyConfig(), new RouteCollection([]));
+
+        $this->assertSame($context, $driver->getContext());
+    }
+
+    public function testShouldReturnConfigObjectSetInConstructor()
+    {
+        $config = $this->createDummyConfig();
+
+        $driver = $this->createDriver($this->createContextMock(), $config, new RouteCollection([]));
+
+        $this->assertSame($config, $driver->getConfig());
+    }
+
+    public function testShouldReturnRouteCollectionSetInConstructor()
+    {
+        $routeCollection = new RouteCollection([]);
+
+        $driver = $this->createDriver($this->createContextMock(), $this->createDummyConfig(), $routeCollection);
+
+        $this->assertSame($routeCollection, $driver->getRouteCollection());
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithPrefixAndAppName()
+    {
+        $expectedQueue = $this->createQueue('aName');
+
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.anappname.afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $config = new Config(
+            'aPrefix',
+            'anAppName',
+            'aRouterTopicName',
+            'aRouterQueueName',
+            'aDefaultQueue',
+            'aRouterProcessor',
+            []
+        );
+
+        $driver = $this->createDriver($context, $config, new RouteCollection([]));
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithPrefixWithoutAppName()
+    {
+        $expectedQueue = $this->createQueue('aName');
+
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $config = new Config(
+            'aPrefix',
+            '',
+            'aRouterTopicName',
+            'aRouterQueueName',
+            'aDefaultQueue',
+            'aRouterProcessor',
+            []
+        );
+
+        $driver = $this->createDriver($context, $config, new RouteCollection([]));
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithAppNameAndWithoutPrefix()
+    {
+        $expectedQueue = $this->createQueue('aName');
+
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('anappname.afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $config = new Config(
+            '',
+            'anAppName',
+            'aRouterTopicName',
+            'aRouterQueueName',
+            'aDefaultQueue',
+            'aRouterProcessor',
+            []
+        );
+
+        $driver = $this->createDriver($context, $config, new RouteCollection([]));
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldCreateAndReturnQueueInstanceWithoutPrefixAndAppName()
+    {
+        $expectedQueue = $this->createQueue('aName');
+
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $config = new Config(
+            '',
+            '',
+            'aRouterTopicName',
+            'aRouterQueueName',
+            'aDefaultQueue',
+            'aRouterProcessor',
+            []
+        );
+
+        $driver = $this->createDriver($context, $config, new RouteCollection([]));
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldCreateAndReturnQueueInstance()
+    {
+        $expectedQueue = $this->createQueue('aName');
+
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('aprefix.afooqueue')
+            ->willReturn($expectedQueue)
+        ;
+
+        $driver = $this->createDriver($context, $this->createDummyConfig(), new RouteCollection([]));
+
+        $queue = $driver->createQueue('aFooQueue');
+
+        $this->assertSame($expectedQueue, $queue);
+    }
+
+    public function testShouldCreateClientMessageFromTransportOne()
+    {
+        $transportMessage = $this->createMessage();
+        $transportMessage->setBody('body');
+        $transportMessage->setHeaders(['hkey' => 'hval']);
+        $transportMessage->setProperty('pkey', 'pval');
+        $transportMessage->setProperty('X-Enqueue-Content-Type', 'theContentType');
+        $transportMessage->setProperty('X-Enqueue-Expire', '22');
+        $transportMessage->setProperty('X-Enqueue-Priority', MessagePriority::HIGH);
+        $transportMessage->setProperty('X-Enqueue-Delay', '44');
+        $transportMessage->setMessageId('theMessageId');
+        $transportMessage->setTimestamp(1000);
+        $transportMessage->setReplyTo('theReplyTo');
+        $transportMessage->setCorrelationId('theCorrelationId');
+
+        $driver = $this->createDriver(
+            $this->createContextMock(),
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $clientMessage = $driver->createClientMessage($transportMessage);
+
+        $this->assertClientMessage($clientMessage);
+    }
+
+    public function testShouldCreateTransportMessageFromClientOne()
+    {
+        $clientMessage = new Message();
+        $clientMessage->setBody('body');
+        $clientMessage->setHeaders(['hkey' => 'hval']);
+        $clientMessage->setProperties(['pkey' => 'pval']);
+        $clientMessage->setContentType('ContentType');
+        $clientMessage->setExpire(123);
+        $clientMessage->setDelay(345);
+        $clientMessage->setPriority(MessagePriority::HIGH);
+        $clientMessage->setMessageId('theMessageId');
+        $clientMessage->setTimestamp(1000);
+        $clientMessage->setReplyTo('theReplyTo');
+        $clientMessage->setCorrelationId('theCorrelationId');
+
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($this->createMessage())
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $transportMessage = $driver->createTransportMessage($clientMessage);
+
+        $this->assertTransportMessage($transportMessage);
+    }
+
+    public function testShouldSendMessageToRouter()
+    {
+        $topic = $this->createTopic('');
+        $transportMessage = $this->createMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createTopic')
+            ->willReturn($topic)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+
+        $driver->sendToRouter($message);
+    }
+
+    public function testThrowIfTopicIsNotSetOnSendToRouter()
+    {
+        $driver = $this->createDriver(
+            $this->createContextMock(),
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Topic name parameter is required but is not set');
+
+        $driver->sendToRouter(new Message());
+    }
+
+    public function testThrowIfCommandSetOnSendToRouter()
+    {
+        $driver = $this->createDriver(
+            $this->createContextMock(),
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'aCommand');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Command must not be send to router but go directly to its processor.');
+
+        $driver->sendToRouter($message);
+    }
+
+    public function testShouldSendTopicMessageToProcessorToDefaultQueue()
+    {
+        $queue = $this->createQueue('');
+        $transportMessage = $this->createMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('default')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('topic', Route::TOPIC, 'processor'),
+            ])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldSendTopicMessageToProcessorToCustomQueue()
+    {
+        $queue = $this->createQueue('');
+        $transportMessage = $this->createMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('custom')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('topic', Route::TOPIC, 'processor', ['queue' => 'custom']),
+            ])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testThrowIfNoRouteFoundForTopicMessageOnSendToProcessor()
+    {
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->never())
+            ->method('createProducer')
+        ;
+        $context
+            ->expects($this->never())
+            ->method('createMessage')
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('There is no route for topic "topic" and processor "processor"');
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldSendCommandMessageToProcessorToDefaultQueue()
+    {
+        $queue = $this->createQueue('');
+        $transportMessage = $this->createMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('default')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('command', Route::COMMAND, 'processor'),
+            ])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldSendCommandMessageToProcessorToCustomQueue()
+    {
+        $queue = $this->createQueue('');
+        $transportMessage = $this->createMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('custom')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('command', Route::COMMAND, 'processor', ['queue' => 'custom']),
+            ])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testThrowIfNoRouteFoundForCommandMessageOnSendToProcessor()
+    {
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->never())
+            ->method('createProducer')
+        ;
+        $context
+            ->expects($this->never())
+            ->method('createMessage')
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('There is no route for command "command" and processor "processor"');
+        $driver->sendToProcessor($message);
+    }
+
+    public function testThrowIfRouteProcessorDoesNotMatchMessageOneOnSendToProcessor()
+    {
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->never())
+            ->method('createProducer')
+        ;
+        $context
+            ->expects($this->never())
+            ->method('createMessage')
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('command', Route::COMMAND, 'anotherProcessor', ['queue' => 'custom']),
+            ])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The command "command" route was found but processors do not match. Given "processor", route "anotherProcessor"');
+        $driver->sendToProcessor($message);
+    }
+
+    public function testThrowIfProcessorIsNotSetOnSendToProcessor()
+    {
+        $driver = $this->createDriver(
+            $this->createContextMock(),
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Processor name parameter is required but is not set');
+
+        $driver->sendToProcessor(new Message());
+    }
+
+    public function testThrowIfNeitherTopicNorCommandAreSentOnSendToProcessor()
+    {
+        $driver = $this->createDriver(
+            $this->createContextMock(),
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Queue name parameter is required but is not set');
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Either topic or command parameter must be set.');
+        $driver->sendToProcessor($message);
+    }
+
+    abstract protected function createDriver(...$args): DriverInterface;
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    abstract protected function createContextMock(): PsrContext;
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    abstract protected function createProducerMock(): PsrProducer;
+
+    abstract protected function createQueue(string $name): PsrQueue;
+
+    abstract protected function createTopic(string $name): PsrTopic;
+
+    abstract protected function createMessage(): PsrMessage;
+
+    protected function assertTransportMessage(PsrMessage $transportMessage): void
+    {
+        $this->assertSame('body', $transportMessage->getBody());
+        $this->assertEquals([
+            'hkey' => 'hval',
+            'message_id' => 'theMessageId',
+            'timestamp' => 1000,
+            'reply_to' => 'theReplyTo',
+            'correlation_id' => 'theCorrelationId',
+        ], $transportMessage->getHeaders());
+        $this->assertEquals([
+            'pkey' => 'pval',
+            'X-Enqueue-Content-Type' => 'ContentType',
+            'X-Enqueue-Priority' => MessagePriority::HIGH,
+            'X-Enqueue-Expire' => 123,
+            'X-Enqueue-Delay' => 345,
+        ], $transportMessage->getProperties());
+        $this->assertSame('theMessageId', $transportMessage->getMessageId());
+        $this->assertSame(1000, $transportMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
+    }
+
+    protected function assertClientMessage(Message $clientMessage): void
+    {
+        $this->assertSame('body', $clientMessage->getBody());
+        $this->assertArraySubset([
+            'hkey' => 'hval',
+        ], $clientMessage->getHeaders());
+        $this->assertArraySubset([
+            'pkey' => 'pval',
+            'X-Enqueue-Content-Type' => 'theContentType',
+            'X-Enqueue-Expire' => '22',
+            'X-Enqueue-Priority' => MessagePriority::HIGH,
+            'X-Enqueue-Delay' => '44',
+        ], $clientMessage->getProperties());
+        $this->assertSame('theMessageId', $clientMessage->getMessageId());
+        $this->assertSame(22, $clientMessage->getExpire());
+        $this->assertSame(44, $clientMessage->getDelay());
+        $this->assertSame(MessagePriority::HIGH, $clientMessage->getPriority());
+        $this->assertSame('theContentType', $clientMessage->getContentType());
+        $this->assertSame(1000, $clientMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
+    }
+
+    protected function createDummyConfig(): Config
+    {
+        return Config::create('aPrefix');
+    }
+}

From 5c11db96f233f269313e0d0b14851aae221bea3b Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 17 Sep 2018 13:14:05 +0300
Subject: [PATCH 115/337] [client] Rework AmqpDriver

fixes https://github.com/php-enqueue/enqueue-dev/issues/523 (for amqp)
---
 Client/Driver/AmqpDriver.php           | 162 +++++-----
 Tests/Client/Driver/AmqpDriverTest.php | 418 +++++++++++--------------
 2 files changed, 262 insertions(+), 318 deletions(-)

diff --git a/Client/Driver/AmqpDriver.php b/Client/Driver/AmqpDriver.php
index 714aab6..147d40b 100644
--- a/Client/Driver/AmqpDriver.php
+++ b/Client/Driver/AmqpDriver.php
@@ -1,11 +1,13 @@
 <?php
 
+declare(strict_types=1);
+
 namespace  Enqueue\Client\Driver;
 
 use Enqueue\Client\Config;
-use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Message;
-use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\RouteCollection;
 use Interop\Amqp\AmqpContext;
 use Interop\Amqp\AmqpMessage;
 use Interop\Amqp\AmqpQueue;
@@ -13,10 +15,11 @@
 use Interop\Amqp\Impl\AmqpBind;
 use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrQueue;
+use Interop\Queue\PsrTopic;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
-class AmqpDriver implements DriverInterface
+class AmqpDriver extends GenericDriver
 {
     /**
      * @var AmqpContext
@@ -29,43 +32,59 @@ class AmqpDriver implements DriverInterface
     private $config;
 
     /**
-     * @var QueueMetaRegistry
+     * @var array
+     */
+    private $priorityMap;
+
+    /**
+     * @var RouteCollection
      */
-    private $queueMetaRegistry;
+    private $routeCollection;
 
-    public function __construct(AmqpContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
+    public function __construct(AmqpContext $context, Config $config, RouteCollection $routeCollection)
     {
         $this->context = $context;
         $this->config = $config;
-        $this->queueMetaRegistry = $queueMetaRegistry;
-    }
+        $this->routeCollection = $routeCollection;
 
-    public function sendToRouter(Message $message): void
-    {
-        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
-            throw new \LogicException('Topic name parameter is required but is not set');
-        }
-
-        $topic = $this->createRouterTopic();
-        $transportMessage = $this->createTransportMessage($message);
+        $this->priorityMap = [
+            MessagePriority::VERY_LOW => 0,
+            MessagePriority::LOW => 1,
+            MessagePriority::NORMAL => 2,
+            MessagePriority::HIGH => 3,
+            MessagePriority::VERY_HIGH => 4,
+        ];
 
-        $this->context->createProducer()->send($topic, $transportMessage);
+        parent::__construct($context, $config, $routeCollection);
     }
 
-    public function sendToProcessor(Message $message): void
+    /**
+     * @return AmqpMessage
+     */
+    public function createTransportMessage(Message $clientMessage): PsrMessage
     {
-        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
-            throw new \LogicException('Processor name parameter is required but is not set');
-        }
+        /** @var AmqpMessage $transportMessage */
+        $transportMessage = parent::createTransportMessage($clientMessage);
+        $transportMessage->setDeliveryMode(AmqpMessage::DELIVERY_MODE_PERSISTENT);
+        $transportMessage->setContentType($clientMessage->getContentType());
 
-        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
-            throw new \LogicException('Queue name parameter is required but is not set');
+        if ($clientMessage->getExpire()) {
+            $transportMessage->setExpiration($clientMessage->getExpire() * 1000);
         }
 
-        $transportMessage = $this->createTransportMessage($message);
-        $destination = $this->createQueue($queueName);
+        if ($priority = $clientMessage->getPriority()) {
+            if (false == array_key_exists($priority, $this->getPriorityMap())) {
+                throw new \InvalidArgumentException(sprintf(
+                    'Cant convert client priority "%s" to transport one. Could be one of "%s"',
+                    $priority,
+                    implode('", "', array_keys($this->getPriorityMap()))
+                ));
+            }
 
-        $this->context->createProducer()->send($destination, $transportMessage);
+            $transportMessage->setPriority($this->priorityMap[$priority]);
+        }
+
+        return $transportMessage;
     }
 
     public function setupBroker(LoggerInterface $logger = null): void
@@ -77,100 +96,73 @@ public function setupBroker(LoggerInterface $logger = null): void
 
         // setup router
         $routerTopic = $this->createRouterTopic();
-        $routerQueue = $this->createQueue($this->config->getRouterQueueName());
-
         $log('Declare router exchange: %s', $routerTopic->getTopicName());
         $this->context->declareTopic($routerTopic);
+
+        $routerQueue = $this->createQueue($this->config->getRouterQueueName());
         $log('Declare router queue: %s', $routerQueue->getQueueName());
         $this->context->declareQueue($routerQueue);
+
         $log('Bind router queue to exchange: %s -> %s', $routerQueue->getQueueName(), $routerTopic->getTopicName());
         $this->context->bind(new AmqpBind($routerTopic, $routerQueue, $routerQueue->getQueueName()));
 
         // setup queues
-        foreach ($this->queueMetaRegistry->getQueuesMeta() as $meta) {
-            $queue = $this->createQueue($meta->getClientName());
+        $declaredQueues = [];
+        foreach ($this->routeCollection->all() as $route) {
+            /** @var AmqpQueue $queue */
+            $queue = $this->createRouteQueue($route);
+            if (array_key_exists($queue->getQueueName(), $declaredQueues)) {
+                continue;
+            }
 
             $log('Declare processor queue: %s', $queue->getQueueName());
             $this->context->declareQueue($queue);
+
+            $declaredQueues[$queue->getQueueName()] = true;
         }
     }
 
     /**
      * @return AmqpQueue
      */
-    public function createQueue(string $queueName): PsrQueue
+    public function createQueue(string $clientQueuName): PsrQueue
     {
-        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
-
-        $queue = $this->context->createQueue($transportName);
+        /** @var AmqpQueue $queue */
+        $queue = parent::createQueue($clientQueuName);
         $queue->addFlag(AmqpQueue::FLAG_DURABLE);
 
         return $queue;
     }
 
     /**
-     * @return AmqpMessage
+     * @param AmqpTopic   $topic
+     * @param AmqpMessage $transportMessage
      */
-    public function createTransportMessage(Message $message): PsrMessage
+    protected function doSendToRouter(PsrTopic $topic, PsrMessage $transportMessage): void
     {
-        $headers = $message->getHeaders();
-        $properties = $message->getProperties();
-
-        $transportMessage = $this->context->createMessage();
-        $transportMessage->setBody($message->getBody());
-        $transportMessage->setHeaders($headers);
-        $transportMessage->setProperties($properties);
-        $transportMessage->setMessageId($message->getMessageId());
-        $transportMessage->setTimestamp($message->getTimestamp());
-        $transportMessage->setReplyTo($message->getReplyTo());
-        $transportMessage->setCorrelationId($message->getCorrelationId());
-        $transportMessage->setContentType($message->getContentType());
-        $transportMessage->setDeliveryMode(AmqpMessage::DELIVERY_MODE_PERSISTENT);
-
-        if ($message->getExpire()) {
-            $transportMessage->setExpiration($message->getExpire() * 1000);
-        }
+        // We should not handle priority, expiration, and delay at this stage.
+        // The router will take care of it while re-sending the message to the final destinations.
+        $transportMessage->setPriority(null);
+        $transportMessage->setExpiration(null);
 
-        return $transportMessage;
+        $this->context->createProducer()->send($topic, $transportMessage);
     }
 
     /**
-     * @param AmqpMessage $message
+     * @return AmqpTopic
      */
-    public function createClientMessage(PsrMessage $message): Message
-    {
-        $clientMessage = new Message();
-
-        $clientMessage->setBody($message->getBody());
-        $clientMessage->setHeaders($message->getHeaders());
-        $clientMessage->setProperties($message->getProperties());
-        $clientMessage->setContentType($message->getContentType());
-
-        if ($expiration = $message->getExpiration()) {
-            $clientMessage->setExpire((int) ($expiration / 1000));
-        }
-
-        $clientMessage->setMessageId($message->getMessageId());
-        $clientMessage->setTimestamp($message->getTimestamp());
-        $clientMessage->setReplyTo($message->getReplyTo());
-        $clientMessage->setCorrelationId($message->getCorrelationId());
-
-        return $clientMessage;
-    }
-
-    public function getConfig(): Config
+    protected function createRouterTopic(): PsrTopic
     {
-        return $this->config;
-    }
-
-    private function createRouterTopic(): AmqpTopic
-    {
-        $topic = $this->context->createTopic(
-            $this->config->createTransportRouterTopicName($this->config->getRouterTopicName())
-        );
+        /** @var AmqpTopic $topic */
+        $topic = parent::createRouterTopic();
         $topic->setType(AmqpTopic::TYPE_FANOUT);
         $topic->addFlag(AmqpTopic::FLAG_DURABLE);
 
         return $topic;
     }
+
+    protected function getPriorityMap(): array
+    {
+        return $this->priorityMap;
+    }
 }
diff --git a/Tests/Client/Driver/AmqpDriverTest.php b/Tests/Client/Driver/AmqpDriverTest.php
index 41a6432..13d399c 100644
--- a/Tests/Client/Driver/AmqpDriverTest.php
+++ b/Tests/Client/Driver/AmqpDriverTest.php
@@ -4,9 +4,12 @@
 
 use Enqueue\Client\Config;
 use Enqueue\Client\Driver\AmqpDriver;
+use Enqueue\Client\Driver\GenericDriver;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Message;
-use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
 use Enqueue\Test\ClassExtensionTrait;
 use Interop\Amqp\AmqpContext;
 use Interop\Amqp\AmqpProducer;
@@ -14,238 +17,135 @@
 use Interop\Amqp\Impl\AmqpMessage;
 use Interop\Amqp\Impl\AmqpQueue;
 use Interop\Amqp\Impl\AmqpTopic;
+use Interop\Queue\PsrContext;
+use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrProducer;
+use Interop\Queue\PsrQueue;
 use PHPUnit\Framework\TestCase;
 
 class AmqpDriverTest extends TestCase
 {
     use ClassExtensionTrait;
+    use GenericDriverTestsTrait;
 
     public function testShouldImplementsDriverInterface()
     {
-        $this->assertClassImplements(DriverInterface::class, AmqpDriver::class);
+        $this->assertClassImplements(DriverInterface::class, GenericDriver::class);
     }
 
-    public function testCouldBeConstructedWithRequiredArguments()
+    public function testThrowIfPriorityIsNotSupportedOnCreateTransportMessage()
     {
-        new AmqpDriver(
-            $this->createAmqpContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-    }
-
-    public function testShouldReturnConfigObject()
-    {
-        $config = $this->createDummyConfig();
-
-        $driver = new AmqpDriver($this->createAmqpContextMock(), $config, $this->createDummyQueueMetaRegistry());
-
-        $this->assertSame($config, $driver->getConfig());
-    }
-
-    public function testShouldCreateAndReturnQueueInstance()
-    {
-        $expectedQueue = new AmqpQueue('aName');
+        $clientMessage = new Message();
+        $clientMessage->setPriority('invalidPriority');
 
-        $context = $this->createAmqpContextMock();
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix.afooqueue')
-            ->willReturn($expectedQueue)
+            ->method('createMessage')
+            ->willReturn($this->createMessage())
         ;
 
-        $driver = new AmqpDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aFooQueue');
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
 
-        $this->assertSame($expectedQueue, $queue);
-        $this->assertSame([], $queue->getArguments());
-        $this->assertSame(2, $queue->getFlags());
-        $this->assertNull($queue->getConsumerTag());
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Cant convert client priority "invalidPriority" to transport one. Could be one of "enqueue.message_queue.client.very_low_message_priority", "enqueue.message_queue.client.low_message_priority", "enqueue.message_queue.client.normal_message_priority');
+        $driver->createTransportMessage($clientMessage);
     }
 
-    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
+    public function testShouldSetExpirationHeaderFromClientMessageExpireInMillisecondsOnCreateTransportMessage()
     {
-        $expectedQueue = new AmqpQueue('aName');
+        $clientMessage = new Message();
+        $clientMessage->setExpire(333);
 
-        $context = $this->createAmqpContextMock();
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
-            ->method('createQueue')
-            ->with('aBarQueue')
-            ->willReturn($expectedQueue)
+            ->method('createMessage')
+            ->willReturn($this->createMessage())
         ;
 
-        $driver = new AmqpDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aBarQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldConvertTransportMessageToClientMessage()
-    {
-        $transportMessage = new AmqpMessage();
-        $transportMessage->setBody('body');
-        $transportMessage->setHeaders(['hkey' => 'hval']);
-        $transportMessage->setProperties(['key' => 'val']);
-        $transportMessage->setHeader('content_type', 'ContentType');
-        $transportMessage->setHeader('expiration', '12345000');
-        $transportMessage->setMessageId('MessageId');
-        $transportMessage->setTimestamp(1000);
-        $transportMessage->setReplyTo('theReplyTo');
-        $transportMessage->setCorrelationId('theCorrelationId');
-
-        $driver = new AmqpDriver(
-            $this->createAmqpContextMock(),
+        $driver = $this->createDriver(
+            $context,
             $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
+            new RouteCollection([])
         );
 
-        $clientMessage = $driver->createClientMessage($transportMessage);
+        /** @var AmqpMessage $transportMessage */
+        $transportMessage = $driver->createTransportMessage($clientMessage);
 
-        $this->assertInstanceOf(Message::class, $clientMessage);
-        $this->assertSame('body', $clientMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'content_type' => 'ContentType',
-            'expiration' => '12345000',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-            'reply_to' => 'theReplyTo',
-            'correlation_id' => 'theCorrelationId',
-        ], $clientMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-        ], $clientMessage->getProperties());
-        $this->assertSame('MessageId', $clientMessage->getMessageId());
-        $this->assertSame(12345, $clientMessage->getExpire());
-        $this->assertSame('ContentType', $clientMessage->getContentType());
-        $this->assertSame(1000, $clientMessage->getTimestamp());
-        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
+        $this->assertSame(333000, $transportMessage->getExpiration());
+        $this->assertSame('333000', $transportMessage->getHeader('expiration'));
     }
 
-    public function testShouldConvertClientMessageToTransportMessage()
+    public function testShouldSetPersistedDeliveryModeOnCreateTransportMessage()
     {
         $clientMessage = new Message();
-        $clientMessage->setBody('body');
-        $clientMessage->setHeaders(['hkey' => 'hval']);
-        $clientMessage->setProperties(['key' => 'val']);
-        $clientMessage->setContentType('ContentType');
-        $clientMessage->setExpire(123);
-        $clientMessage->setMessageId('MessageId');
-        $clientMessage->setTimestamp(1000);
-        $clientMessage->setReplyTo('theReplyTo');
-        $clientMessage->setCorrelationId('theCorrelationId');
-
-        $context = $this->createAmqpContextMock();
+
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
             ->method('createMessage')
-            ->willReturn(new AmqpMessage())
+            ->willReturn($this->createMessage())
         ;
 
-        $driver = new AmqpDriver(
+        $driver = $this->createDriver(
             $context,
             $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
+            new RouteCollection([])
         );
 
+        /** @var AmqpMessage $transportMessage */
         $transportMessage = $driver->createTransportMessage($clientMessage);
 
-        $this->assertInstanceOf(AmqpMessage::class, $transportMessage);
-        $this->assertSame('body', $transportMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-            'reply_to' => 'theReplyTo',
-            'correlation_id' => 'theCorrelationId',
-            'content_type' => 'ContentType',
-            'delivery_mode' => 2,
-            'expiration' => '123000',
-        ], $transportMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-        ], $transportMessage->getProperties());
-        $this->assertSame('MessageId', $transportMessage->getMessageId());
-        $this->assertSame(1000, $transportMessage->getTimestamp());
-        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
+        $this->assertSame(AmqpMessage::DELIVERY_MODE_PERSISTENT, $transportMessage->getDeliveryMode());
     }
 
-    public function testShouldSendMessageToRouter()
+    public function testShouldCreateDurableQueue()
     {
-        $topic = new AmqpTopic('');
-        $transportMessage = new AmqpMessage();
-
-        $producer = $this->createAmqpProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createAmqpContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createTopic')
-            ->willReturn($topic)
-        ;
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
+            ->method('createQueue')
+            ->willReturn($this->createQueue('aName'))
         ;
 
-        $driver = new AmqpDriver(
+        $driver = $this->createDriver(
             $context,
             $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-
-        $driver->sendToRouter($message);
-    }
-
-    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
-    {
-        $driver = new AmqpDriver(
-            $this->createAmqpContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
+            new RouteCollection([])
         );
 
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Topic name parameter is required but is not set');
+        /** @var AmqpQueue $queue */
+        $queue = $driver->createQueue('aName');
 
-        $driver->sendToRouter(new Message());
+        $this->assertSame(AmqpQueue::FLAG_DURABLE, $queue->getFlags());
     }
 
-    public function testShouldSendMessageToProcessor()
+    public function testShouldResetPriorityAndExpirationAndNeverCallProducerDeliveryDelayOnSendMessageToRouter()
     {
-        $queue = new AmqpQueue('');
-        $transportMessage = new AmqpMessage();
+        $topic = $this->createTopic('');
+        $transportMessage = $this->createMessage();
 
-        $producer = $this->createAmqpProducerMock();
+        $producer = $this->createProducerMock();
         $producer
             ->expects($this->once())
             ->method('send')
-            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
         ;
-        $context = $this->createAmqpContextMock();
+        $producer
+            ->expects($this->never())
+            ->method('setDeliveryDelay')
+        ;
+
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
-            ->method('createQueue')
-            ->willReturn($queue)
+            ->method('createTopic')
+            ->willReturn($topic)
         ;
         $context
             ->expects($this->once())
@@ -258,58 +158,30 @@ public function testShouldSendMessageToProcessor()
             ->willReturn($transportMessage)
         ;
 
-        $driver = new AmqpDriver(
+        $driver = $this->createDriver(
             $context,
             $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
+            new RouteCollection([])
         );
 
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
-
-        $driver->sendToProcessor($message);
-    }
-
-    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
-    {
-        $driver = new AmqpDriver(
-            $this->createAmqpContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Processor name parameter is required but is not set');
-
-        $driver->sendToProcessor(new Message());
-    }
-
-    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
-    {
-        $driver = new AmqpDriver(
-            $this->createAmqpContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Queue name parameter is required but is not set');
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+        $message->setExpire(123);
+        $message->setPriority(MessagePriority::HIGH);
 
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $driver->sendToRouter($message);
 
-        $driver->sendToProcessor($message);
+        $this->assertNull($transportMessage->getExpiration());
+        $this->assertNull($transportMessage->getPriority());
     }
 
     public function testShouldSetupBroker()
     {
-        $routerTopic = new AmqpTopic('');
-        $routerQueue = new AmqpQueue('');
-
-        $processorQueue = new AmqpQueue('');
-
-        $context = $this->createAmqpContextMock();
+        $routerTopic = $this->createTopic('');
+        $routerQueue = $this->createQueue('');
+        $processorWithDefaultQueue = $this->createQueue('default');
+        $processorWithCustomQueue = $this->createQueue('custom');
+        $context = $this->createContextMock();
         // setup router
         $context
             ->expects($this->at(0))
@@ -318,83 +190,163 @@ public function testShouldSetupBroker()
         ;
         $context
             ->expects($this->at(1))
-            ->method('createQueue')
-            ->willReturn($routerQueue)
+            ->method('declareTopic')
+            ->with($this->identicalTo($routerTopic))
         ;
+
         $context
             ->expects($this->at(2))
-            ->method('declareTopic')
-            ->with($this->identicalTo($routerTopic))
+            ->method('createQueue')
+            ->willReturn($routerQueue)
         ;
         $context
             ->expects($this->at(3))
             ->method('declareQueue')
             ->with($this->identicalTo($routerQueue))
         ;
+
         $context
             ->expects($this->at(4))
             ->method('bind')
             ->with($this->isInstanceOf(AmqpBind::class))
         ;
-        // setup processor queue
+
+        // setup processor with default queue
         $context
             ->expects($this->at(5))
             ->method('createQueue')
-            ->willReturn($processorQueue)
+            ->with('default')
+            ->willReturn($processorWithDefaultQueue)
         ;
         $context
             ->expects($this->at(6))
             ->method('declareQueue')
-            ->with($this->identicalTo($processorQueue))
+            ->with($this->identicalTo($processorWithDefaultQueue))
         ;
 
-        $meta = new QueueMetaRegistry($this->createDummyConfig(), [
-            'default' => [],
-        ]);
+        $context
+            ->expects($this->at(7))
+            ->method('createQueue')
+            ->with('custom')
+            ->willReturn($processorWithCustomQueue)
+        ;
+        $context
+            ->expects($this->at(8))
+            ->method('declareQueue')
+            ->with($this->identicalTo($processorWithCustomQueue))
+        ;
 
         $driver = new AmqpDriver(
             $context,
             $this->createDummyConfig(),
-            $meta
+            new RouteCollection([
+                new Route('aTopic', Route::TOPIC, 'aProcessor'),
+                new Route('aCommand', Route::COMMAND, 'aProcessor', ['queue' => 'custom']),
+            ])
         );
+        $driver->setupBroker();
+    }
 
+    public function testShouldNotDeclareSameQueues()
+    {
+        $context = $this->createContextMock();
+
+        // setup processor with default queue
+        $context
+            ->expects($this->any())
+            ->method('createTopic')
+            ->willReturn($this->createTopic(''))
+        ;
+        $context
+            ->expects($this->any())
+            ->method('createQueue')
+            ->willReturn($this->createQueue('custom'))
+        ;
+        $context
+            ->expects($this->exactly(2))
+            ->method('declareQueue')
+        ;
+
+        $driver = new AmqpDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('aTopic', Route::TOPIC, 'aProcessor', ['queue' => 'custom']),
+                new Route('aCommand', Route::COMMAND, 'aProcessor', ['queue' => 'custom']),
+            ])
+        );
         $driver->setupBroker();
     }
 
+    protected function createDriver(...$args): DriverInterface
+    {
+        return new AmqpDriver(...$args);
+    }
+
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|AmqpContext
+     * @return AmqpContext
      */
-    private function createAmqpContextMock()
+    protected function createContextMock(): PsrContext
     {
         return $this->createMock(AmqpContext::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|AmqpProducer
+     * @return AmqpProducer
      */
-    private function createAmqpProducerMock()
+    protected function createProducerMock(): PsrProducer
     {
         return $this->createMock(AmqpProducer::class);
     }
 
     /**
-     * @return QueueMetaRegistry
+     * @return AmqpQueue
      */
-    private function createDummyQueueMetaRegistry()
+    protected function createQueue(string $name): PsrQueue
     {
-        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
-        $registry->add('default');
-        $registry->add('aFooQueue');
-        $registry->add('aBarQueue', 'aBarQueue');
+        return new AmqpQueue($name);
+    }
 
-        return $registry;
+    /**
+     * @return AmqpTopic
+     */
+    protected function createTopic(string $name): AmqpTopic
+    {
+        return new AmqpTopic($name);
     }
 
     /**
-     * @return Config
+     * @return AmqpMessage
      */
-    private function createDummyConfig()
+    protected function createMessage(): PsrMessage
     {
-        return Config::create('aPrefix');
+        return new AmqpMessage();
+    }
+
+    protected function assertTransportMessage(PsrMessage $transportMessage): void
+    {
+        $this->assertSame('body', $transportMessage->getBody());
+        $this->assertArraySubset([
+            'hkey' => 'hval',
+            'delivery_mode' => AmqpMessage::DELIVERY_MODE_PERSISTENT,
+            'content_type' => 'ContentType',
+            'expiration' => '123000',
+            'priority' => 3,
+            'message_id' => 'theMessageId',
+            'timestamp' => 1000,
+            'reply_to' => 'theReplyTo',
+            'correlation_id' => 'theCorrelationId',
+        ], $transportMessage->getHeaders());
+        $this->assertEquals([
+            'pkey' => 'pval',
+            'X-Enqueue-Content-Type' => 'ContentType',
+            'X-Enqueue-Priority' => MessagePriority::HIGH,
+            'X-Enqueue-Expire' => 123,
+            'X-Enqueue-Delay' => 345,
+        ], $transportMessage->getProperties());
+        $this->assertSame('theMessageId', $transportMessage->getMessageId());
+        $this->assertSame(1000, $transportMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
     }
 }

From 2d93df3ec8b11c63e13ec095516a56d22fc945dd Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 17 Sep 2018 16:05:50 +0300
Subject: [PATCH 116/337] [amqp] Set delay strategy if rabbitmq scheme
 extension present.

---
 ConnectionFactoryFactory.php           | 29 +++++++++++++++---------
 Resources.php                          |  6 ++---
 Tests/ConnectionFactoryFactoryTest.php | 31 ++++++++++++++++++++------
 3 files changed, 45 insertions(+), 21 deletions(-)

diff --git a/ConnectionFactoryFactory.php b/ConnectionFactoryFactory.php
index 5ab4256..3aabfce 100644
--- a/ConnectionFactoryFactory.php
+++ b/ConnectionFactoryFactory.php
@@ -46,23 +46,30 @@ public function create($config): PsrConnectionFactory
     private function findFactoryClass(Dsn $dsn, array $factories): ?string
     {
         $protocol = $dsn->getSchemeProtocol();
-        foreach ($factories as $connectionClass => $info) {
-            if (false == in_array($protocol, $info['schemes'], true)) {
-                continue;
-            }
 
-            if (false == $dsn->getSchemeExtensions()) {
-                return $connectionClass;
+        if ($dsn->getSchemeExtensions()) {
+            foreach ($factories as $connectionClass => $info) {
+                if (empty($info['supportedSchemeExtensions'])) {
+                    continue;
+                }
+
+                if (false == in_array($protocol, $info['schemes'], true)) {
+                    continue;
+                }
+
+                $diff = array_diff($info['supportedSchemeExtensions'], $dsn->getSchemeExtensions());
+                if (empty($diff)) {
+                    return $connectionClass;
+                }
             }
+        }
 
-            if (empty($info['supportedSchemeExtensions'])) {
+        foreach ($factories as $driverClass => $info) {
+            if (false == in_array($protocol, $info['schemes'], true)) {
                 continue;
             }
 
-            $diff = array_diff($dsn->getSchemeExtensions(), $info['supportedSchemeExtensions']);
-            if (empty($diff)) {
-                return $connectionClass;
-            }
+            return $driverClass;
         }
 
         return null;
diff --git a/Resources.php b/Resources.php
index 99f3ad1..d58ba92 100644
--- a/Resources.php
+++ b/Resources.php
@@ -88,17 +88,17 @@ public static function getKnownConnections(): array
             ];
             $map[AmqpBunnyConnectionFactory::class] = [
                 'schemes' => ['amqp'],
-                'supportedSchemeExtensions' => ['bunny', 'rabbitmq'],
+                'supportedSchemeExtensions' => ['bunny'],
                 'package' => 'enqueue/amqp-bunny',
             ];
             $map[AmqpExtConnectionFactory::class] = [
                 'schemes' => ['amqp', 'amqps'],
-                'supportedSchemeExtensions' => ['ext', 'rabbitmq'],
+                'supportedSchemeExtensions' => ['ext'],
                 'package' => 'enqueue/amqp-ext',
             ];
             $map[AmqpLibConnectionFactory::class] = [
                 'schemes' => ['amqp', 'amqps'],
-                'supportedSchemeExtensions' => ['lib', 'rabbitmq'],
+                'supportedSchemeExtensions' => ['lib'],
                 'package' => 'enqueue/amqp-lib',
             ];
 
diff --git a/Tests/ConnectionFactoryFactoryTest.php b/Tests/ConnectionFactoryFactoryTest.php
index 644c891..3fb4fb5 100644
--- a/Tests/ConnectionFactoryFactoryTest.php
+++ b/Tests/ConnectionFactoryFactoryTest.php
@@ -119,23 +119,34 @@ public static function provideDSN()
 
         yield ['amqp:', AmqpBunnyConnectionFactory::class];
 
+        yield ['amqp+bunny:', AmqpBunnyConnectionFactory::class];
+
+        yield ['amqp+lib:', AmqpLibConnectionFactory::class];
+
         yield ['amqp+ext:', AmqpExtConnectionFactory::class];
 
-//        yield ['amqp+rabbitmq:', AmqpExtConnectionFactory::class];
+        yield ['amqp+rabbitmq:', AmqpBunnyConnectionFactory::class];
+
+        yield ['amqp+rabbitmq+bunny:', AmqpBunnyConnectionFactory::class];
+
+        yield ['amqp+foo+bar+lib:', AmqpLibConnectionFactory::class];
+
+        yield ['amqp+rabbitmq+ext:', AmqpExtConnectionFactory::class];
 
-//        yield ['amqp+rabbitmq+ext:', AmqpExtConnectionFactory::class];
+        yield ['amqp+rabbitmq+lib:', AmqpLibConnectionFactory::class];
 
+        // bunny does not support amqps, so it is skipped
         yield ['amqps:', AmqpExtConnectionFactory::class];
 
+        // bunny does not support amqps, so it is skipped
         yield ['amqps+ext:', AmqpExtConnectionFactory::class];
 
-//        yield ['amqps+rabbitmq:', AmqpExtConnectionFactory::class];
+        // bunny does not support amqps, so it is skipped
+        yield ['amqps+rabbitmq:', AmqpExtConnectionFactory::class];
 
-//        yield ['amqps+ext+rabbitmq:', AmqpExtConnectionFactory::class];
+        yield ['amqps+ext+rabbitmq:', AmqpExtConnectionFactory::class];
 
-        yield ['amqp+bunny:', AmqpBunnyConnectionFactory::class];
-
-        yield ['amqp+lib:', AmqpLibConnectionFactory::class];
+        yield ['amqps+lib+rabbitmq:', AmqpLibConnectionFactory::class];
 
         yield ['mssql:', DbalConnectionFactory::class];
 
@@ -158,6 +169,12 @@ public static function provideDSN()
 
         yield ['redis:', RedisConnectionFactory::class];
 
+        yield ['redis+predis:', RedisConnectionFactory::class];
+
+        yield ['redis+foo+bar+phpredis:', RedisConnectionFactory::class];
+
+        yield ['redis+phpredis:', RedisConnectionFactory::class];
+
         yield ['sqs:', SqsConnectionFactory::class];
 
         yield ['stomp:', StompConnectionFactory::class];

From 670ef6398a6855790d2b14274441dd8b7a073c41 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 17 Sep 2018 18:28:39 +0300
Subject: [PATCH 117/337] [client] GenericDriver should init delivery delay,
 prioirt, time to live.

---
 Client/Driver/GenericDriver.php               |  46 ++-
 .../Client/Driver/GenericDriverTestsTrait.php | 288 ++++++++++++++++++
 2 files changed, 328 insertions(+), 6 deletions(-)

diff --git a/Client/Driver/GenericDriver.php b/Client/Driver/GenericDriver.php
index 5538802..64088d6 100644
--- a/Client/Driver/GenericDriver.php
+++ b/Client/Driver/GenericDriver.php
@@ -12,6 +12,7 @@
 use Enqueue\Client\RouteCollection;
 use Interop\Queue\PsrContext;
 use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrProducer;
 use Interop\Queue\PsrQueue;
 use Interop\Queue\PsrTopic;
 use Psr\Log\LoggerInterface;
@@ -54,8 +55,9 @@ public function sendToRouter(Message $message): void
 
         $topic = $this->createRouterTopic();
         $transportMessage = $this->createTransportMessage($message);
+        $producer = $this->getContext()->createProducer();
 
-        $this->doSendToRouter($topic, $transportMessage);
+        $this->doSendToRouter($producer, $topic, $transportMessage);
     }
 
     public function sendToProcessor(Message $message): void
@@ -91,7 +93,23 @@ public function sendToProcessor(Message $message): void
         $transportMessage = $this->createTransportMessage($message);
         $queue = $this->createRouteQueue($route);
 
-        $this->doSendToProcessor($queue, $transportMessage);
+        $producer = $this->context->createProducer();
+
+        if ($delay = $transportMessage->getProperty('X-Enqueue-Delay')) {
+            $producer->setDeliveryDelay($delay * 1000);
+        }
+
+        if ($expire = $transportMessage->getProperty('X-Enqueue-Expire')) {
+            $producer->setTimeToLive($expire * 1000);
+        }
+
+        if ($priority = $transportMessage->getProperty('X-Enqueue-Priority')) {
+            $priorityMap = $this->getPriorityMap();
+
+            $producer->setPriority($priorityMap[$priority]);
+        }
+
+        $this->doSendToProcessor($producer, $queue, $transportMessage);
     }
 
     public function setupBroker(LoggerInterface $logger = null): void
@@ -186,14 +204,14 @@ public function getRouteCollection(): RouteCollection
         return $this->routeCollection;
     }
 
-    protected function doSendToRouter(PsrTopic $topic, PsrMessage $transportMessage): void
+    protected function doSendToRouter(PsrProducer $producer, PsrTopic $topic, PsrMessage $transportMessage): void
     {
-        $this->context->createProducer()->send($topic, $transportMessage);
+        $producer->send($topic, $transportMessage);
     }
 
-    protected function doSendToProcessor(PsrQueue $queue, PsrMessage $transportMessage): void
+    protected function doSendToProcessor(PsrProducer $producer, PsrQueue $queue, PsrMessage $transportMessage): void
     {
-        $this->context->createProducer()->send($queue, $transportMessage);
+        $producer->send($queue, $transportMessage);
     }
 
     protected function createRouterTopic(): PsrTopic
@@ -227,4 +245,20 @@ protected function createTransportQueueName(string $name, bool $prefix): string
 
         return strtolower(implode($this->config->getSeparator(), array_filter([$clientPrefix, $clientAppName, $name])));
     }
+
+    /**
+     * [client message priority => transport message priority].
+     *
+     * @return int[]
+     */
+    protected function getPriorityMap(): array
+    {
+        return [
+            MessagePriority::VERY_LOW => 0,
+            MessagePriority::LOW => 1,
+            MessagePriority::NORMAL => 2,
+            MessagePriority::HIGH => 3,
+            MessagePriority::VERY_HIGH => 4,
+        ];
+    }
 }
diff --git a/Tests/Client/Driver/GenericDriverTestsTrait.php b/Tests/Client/Driver/GenericDriverTestsTrait.php
index a297a7c..b7f5f2b 100644
--- a/Tests/Client/Driver/GenericDriverTestsTrait.php
+++ b/Tests/Client/Driver/GenericDriverTestsTrait.php
@@ -288,6 +288,144 @@ public function testShouldSendMessageToRouter()
         $driver->sendToRouter($message);
     }
 
+    public function testShouldNotInitDeliveryDelayOnSendMessageToRouter()
+    {
+        $topic = $this->createTopic('');
+        $transportMessage = $this->createMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
+        ;
+        $producer
+            ->expects($this->never())
+            ->method('setDeliveryDelay')
+        ;
+
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createTopic')
+            ->willReturn($topic)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $message = new Message();
+        $message->setDelay(456);
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+
+        $driver->sendToRouter($message);
+    }
+
+    public function testShouldNotInitTimeToLiveOnSendMessageToRouter()
+    {
+        $topic = $this->createTopic('');
+        $transportMessage = $this->createMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
+        ;
+        $producer
+            ->expects($this->never())
+            ->method('setTimeToLive')
+        ;
+
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createTopic')
+            ->willReturn($topic)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $message = new Message();
+        $message->setExpire(456);
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+
+        $driver->sendToRouter($message);
+    }
+
+    public function testShouldNotInitPriorityOnSendMessageToRouter()
+    {
+        $topic = $this->createTopic('');
+        $transportMessage = $this->createMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
+        ;
+        $producer
+            ->expects($this->never())
+            ->method('setPriority')
+        ;
+
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createTopic')
+            ->willReturn($topic)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
+
+        $message = new Message();
+        $message->setPriority(MessagePriority::HIGH);
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+
+        $driver->sendToRouter($message);
+    }
+
     public function testThrowIfTopicIsNotSetOnSendToRouter()
     {
         $driver = $this->createDriver(
@@ -407,6 +545,156 @@ public function testShouldSendTopicMessageToProcessorToCustomQueue()
         $driver->sendToProcessor($message);
     }
 
+    public function testShouldInitDeliveryDelayIfDelayPropertyOnSendToProcessor()
+    {
+        $queue = $this->createQueue('');
+        $transportMessage = $this->createMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('setDeliveryDelay')
+            ->with(456000)
+        ;
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('default')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('topic', Route::TOPIC, 'processor'),
+            ])
+        );
+
+        $message = new Message();
+        $message->setDelay(456);
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldSetInitTimeToLiveIfExpirePropertyOnSendToProcessor()
+    {
+        $queue = $this->createQueue('');
+        $transportMessage = $this->createMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('setTimeToLive')
+            ->with(678000)
+        ;
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('default')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('topic', Route::TOPIC, 'processor'),
+            ])
+        );
+
+        $message = new Message();
+        $message->setExpire(678);
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldSetInitPriorityIfPriorityPropertyOnSendToProcessor()
+    {
+        $queue = $this->createQueue('');
+        $transportMessage = $this->createMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('setPriority')
+            ->with(3)
+        ;
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('default')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('topic', Route::TOPIC, 'processor'),
+            ])
+        );
+
+        $message = new Message();
+        $message->setPriority(MessagePriority::HIGH);
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+
+        $driver->sendToProcessor($message);
+    }
+
     public function testThrowIfNoRouteFoundForTopicMessageOnSendToProcessor()
     {
         $context = $this->createContextMock();

From d791f8f75ed5c0df1756def73514fc9356835ddc Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 17 Sep 2018 18:29:09 +0300
Subject: [PATCH 118/337] [client] fix tests, update rabbitmq driver.

---
 Client/Driver/AmqpDriver.php               |  37 +-
 Client/Driver/RabbitMqDriver.php           | 123 +----
 Tests/Client/Driver/AmqpDriverTest.php     |   7 +-
 Tests/Client/Driver/RabbitMqDriverTest.php | 564 +++------------------
 4 files changed, 85 insertions(+), 646 deletions(-)

diff --git a/Client/Driver/AmqpDriver.php b/Client/Driver/AmqpDriver.php
index 147d40b..0b2a976 100644
--- a/Client/Driver/AmqpDriver.php
+++ b/Client/Driver/AmqpDriver.php
@@ -4,9 +4,9 @@
 
 namespace  Enqueue\Client\Driver;
 
+use Enqueue\AmqpExt\AmqpProducer;
 use Enqueue\Client\Config;
 use Enqueue\Client\Message;
-use Enqueue\Client\MessagePriority;
 use Enqueue\Client\RouteCollection;
 use Interop\Amqp\AmqpContext;
 use Interop\Amqp\AmqpMessage;
@@ -14,6 +14,7 @@
 use Interop\Amqp\AmqpTopic;
 use Interop\Amqp\Impl\AmqpBind;
 use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrProducer;
 use Interop\Queue\PsrQueue;
 use Interop\Queue\PsrTopic;
 use Psr\Log\LoggerInterface;
@@ -31,11 +32,6 @@ class AmqpDriver extends GenericDriver
      */
     private $config;
 
-    /**
-     * @var array
-     */
-    private $priorityMap;
-
     /**
      * @var RouteCollection
      */
@@ -47,14 +43,6 @@ public function __construct(AmqpContext $context, Config $config, RouteCollectio
         $this->config = $config;
         $this->routeCollection = $routeCollection;
 
-        $this->priorityMap = [
-            MessagePriority::VERY_LOW => 0,
-            MessagePriority::LOW => 1,
-            MessagePriority::NORMAL => 2,
-            MessagePriority::HIGH => 3,
-            MessagePriority::VERY_HIGH => 4,
-        ];
-
         parent::__construct($context, $config, $routeCollection);
     }
 
@@ -72,16 +60,17 @@ public function createTransportMessage(Message $clientMessage): PsrMessage
             $transportMessage->setExpiration($clientMessage->getExpire() * 1000);
         }
 
+        $priorityMap = $this->getPriorityMap();
         if ($priority = $clientMessage->getPriority()) {
-            if (false == array_key_exists($priority, $this->getPriorityMap())) {
+            if (false == array_key_exists($priority, $priorityMap)) {
                 throw new \InvalidArgumentException(sprintf(
                     'Cant convert client priority "%s" to transport one. Could be one of "%s"',
                     $priority,
-                    implode('", "', array_keys($this->getPriorityMap()))
+                    implode('", "', array_keys($priorityMap))
                 ));
             }
 
-            $transportMessage->setPriority($this->priorityMap[$priority]);
+            $transportMessage->setPriority($priorityMap[$priority]);
         }
 
         return $transportMessage;
@@ -135,17 +124,18 @@ public function createQueue(string $clientQueuName): PsrQueue
     }
 
     /**
-     * @param AmqpTopic   $topic
-     * @param AmqpMessage $transportMessage
+     * @param AmqpProducer $producer
+     * @param AmqpTopic    $topic
+     * @param AmqpMessage  $transportMessage
      */
-    protected function doSendToRouter(PsrTopic $topic, PsrMessage $transportMessage): void
+    protected function doSendToRouter(PsrProducer $producer, PsrTopic $topic, PsrMessage $transportMessage): void
     {
         // We should not handle priority, expiration, and delay at this stage.
         // The router will take care of it while re-sending the message to the final destinations.
         $transportMessage->setPriority(null);
         $transportMessage->setExpiration(null);
 
-        $this->context->createProducer()->send($topic, $transportMessage);
+        $producer->send($topic, $transportMessage);
     }
 
     /**
@@ -160,9 +150,4 @@ protected function createRouterTopic(): PsrTopic
 
         return $topic;
     }
-
-    protected function getPriorityMap(): array
-    {
-        return $this->priorityMap;
-    }
 }
diff --git a/Client/Driver/RabbitMqDriver.php b/Client/Driver/RabbitMqDriver.php
index ac9eb14..fd1f019 100644
--- a/Client/Driver/RabbitMqDriver.php
+++ b/Client/Driver/RabbitMqDriver.php
@@ -2,77 +2,11 @@
 
 namespace  Enqueue\Client\Driver;
 
-use Enqueue\Client\Config;
-use Enqueue\Client\Message;
-use Enqueue\Client\MessagePriority;
-use Enqueue\Client\Meta\QueueMetaRegistry;
-use Enqueue\Consumption\Exception\LogicException;
-use Interop\Amqp\AmqpContext;
-use Interop\Amqp\AmqpMessage;
 use Interop\Amqp\AmqpQueue;
-use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrQueue;
 
-class RabbitMqDriver extends AmqpDriver
+final class RabbitMqDriver extends AmqpDriver
 {
-    /**
-     * @var AmqpContext
-     */
-    private $context;
-
-    /**
-     * @var Config
-     */
-    private $config;
-
-    /**
-     * @var QueueMetaRegistry
-     */
-    private $queueMetaRegistry;
-
-    /**
-     * @var array
-     */
-    private $priorityMap;
-
-    public function __construct(AmqpContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
-    {
-        parent::__construct($context, $config, $queueMetaRegistry);
-
-        $this->config = $config;
-        $this->context = $context;
-        $this->queueMetaRegistry = $queueMetaRegistry;
-
-        $this->priorityMap = [
-            MessagePriority::VERY_LOW => 0,
-            MessagePriority::LOW => 1,
-            MessagePriority::NORMAL => 2,
-            MessagePriority::HIGH => 3,
-            MessagePriority::VERY_HIGH => 4,
-        ];
-    }
-
-    public function sendToProcessor(Message $message): void
-    {
-        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
-            throw new \LogicException('Processor name parameter is required but is not set');
-        }
-
-        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
-            throw new \LogicException('Queue name parameter is required but is not set');
-        }
-
-        $transportMessage = $this->createTransportMessage($message);
-        $destination = $this->createQueue($queueName);
-        $producer = $this->context->createProducer();
-
-        if ($message->getDelay()) {
-            $producer->setDeliveryDelay($message->getDelay() * 1000);
-        }
-
-        $producer->send($destination, $transportMessage);
-    }
-
     /**
      * @return AmqpQueue
      */
@@ -83,59 +17,4 @@ public function createQueue(string $queueName): PsrQueue
 
         return $queue;
     }
-
-    /**
-     * @return AmqpMessage
-     */
-    public function createTransportMessage(Message $message): PsrMessage
-    {
-        $transportMessage = parent::createTransportMessage($message);
-
-        if ($priority = $message->getPriority()) {
-            if (false == array_key_exists($priority, $this->priorityMap)) {
-                throw new \InvalidArgumentException(sprintf(
-                    'Given priority could not be converted to client\'s one. Got: %s',
-                    $priority
-                ));
-            }
-
-            $transportMessage->setPriority($this->priorityMap[$priority]);
-        }
-
-        if ($message->getDelay()) {
-            if (false == $this->config->getTransportOption('delay_strategy', false)) {
-                throw new LogicException('The message delaying is not supported. In order to use delay feature install RabbitMQ delay strategy.');
-            }
-
-            $transportMessage->setProperty('enqueue-delay', $message->getDelay() * 1000);
-        }
-
-        return $transportMessage;
-    }
-
-    /**
-     * @param AmqpMessage $message
-     */
-    public function createClientMessage(PsrMessage $message): Message
-    {
-        $clientMessage = parent::createClientMessage($message);
-
-        if ($priority = $message->getPriority()) {
-            if (false === $clientPriority = array_search($priority, $this->priorityMap, true)) {
-                throw new \LogicException(sprintf('Cant convert transport priority to client: "%s"', $priority));
-            }
-
-            $clientMessage->setPriority($clientPriority);
-        }
-
-        if ($delay = $message->getProperty('enqueue-delay')) {
-            if (false == is_numeric($delay)) {
-                throw new \LogicException(sprintf('"enqueue-delay" header is not numeric. "%s"', $delay));
-            }
-
-            $clientMessage->setDelay((int) ((int) $delay) / 1000);
-        }
-
-        return $clientMessage;
-    }
 }
diff --git a/Tests/Client/Driver/AmqpDriverTest.php b/Tests/Client/Driver/AmqpDriverTest.php
index 13d399c..9e8ce1b 100644
--- a/Tests/Client/Driver/AmqpDriverTest.php
+++ b/Tests/Client/Driver/AmqpDriverTest.php
@@ -30,7 +30,12 @@ class AmqpDriverTest extends TestCase
 
     public function testShouldImplementsDriverInterface()
     {
-        $this->assertClassImplements(DriverInterface::class, GenericDriver::class);
+        $this->assertClassImplements(DriverInterface::class, AmqpDriver::class);
+    }
+
+    public function testShouldBeSubClassOfGenericDriver()
+    {
+        $this->assertClassExtends(GenericDriver::class, AmqpDriver::class);
     }
 
     public function testThrowIfPriorityIsNotSupportedOnCreateTransportMessage()
diff --git a/Tests/Client/Driver/RabbitMqDriverTest.php b/Tests/Client/Driver/RabbitMqDriverTest.php
index 60c92b8..8f27232 100644
--- a/Tests/Client/Driver/RabbitMqDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqDriverTest.php
@@ -2,564 +2,134 @@
 
 namespace Enqueue\Tests\Client\Driver;
 
-use Enqueue\Client\Config;
 use Enqueue\Client\Driver\AmqpDriver;
+use Enqueue\Client\Driver\GenericDriver;
 use Enqueue\Client\Driver\RabbitMqDriver;
 use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
 use Enqueue\Client\MessagePriority;
-use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Client\RouteCollection;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Amqp\AmqpBind;
 use Interop\Amqp\AmqpContext;
 use Interop\Amqp\AmqpProducer;
 use Interop\Amqp\Impl\AmqpMessage;
 use Interop\Amqp\Impl\AmqpQueue;
 use Interop\Amqp\Impl\AmqpTopic;
+use Interop\Queue\PsrContext;
+use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrProducer;
+use Interop\Queue\PsrQueue;
 use PHPUnit\Framework\TestCase;
 
 class RabbitMqDriverTest extends TestCase
 {
     use ClassExtensionTrait;
+    use GenericDriverTestsTrait;
 
     public function testShouldImplementsDriverInterface()
     {
         $this->assertClassImplements(DriverInterface::class, RabbitMqDriver::class);
     }
 
-    public function testShouldExtendsAmqpDriverClass()
+    public function testShouldBeSubClassOfGenericDriver()
     {
-        $this->assertClassExtends(AmqpDriver::class, RabbitMqDriver::class);
-    }
-
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new RabbitMqDriver(
-            $this->createAmqpContextMock(),
-            Config::create(),
-            $this->createDummyQueueMetaRegistry()
-        );
-    }
-
-    public function testShouldReturnConfigObject()
-    {
-        $config = Config::create();
-
-        $driver = new RabbitMqDriver($this->createAmqpContextMock(), $config, $this->createDummyQueueMetaRegistry());
-
-        $this->assertSame($config, $driver->getConfig());
-    }
-
-    public function testShouldCreateAndReturnQueueInstance()
-    {
-        $expectedQueue = new AmqpQueue('aName');
-
-        $context = $this->createAmqpContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix.afooqueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new AmqpDriver($context, Config::create(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aFooQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-        $this->assertSame([], $queue->getArguments());
-        $this->assertSame(2, $queue->getFlags());
-        $this->assertNull($queue->getConsumerTag());
-    }
-
-    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
-    {
-        $expectedQueue = new AmqpQueue('aName');
-
-        $context = $this->createAmqpContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aBarQueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new AmqpDriver($context, Config::create(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aBarQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldConvertTransportMessageToClientMessage()
-    {
-        $transportMessage = new AmqpMessage();
-        $transportMessage->setBody('body');
-        $transportMessage->setHeaders(['hkey' => 'hval']);
-        $transportMessage->setProperties(['key' => 'val']);
-        $transportMessage->setProperty('enqueue-delay', '5678000');
-        $transportMessage->setHeader('content_type', 'ContentType');
-        $transportMessage->setHeader('expiration', '12345000');
-        $transportMessage->setHeader('priority', 3);
-        $transportMessage->setMessageId('MessageId');
-        $transportMessage->setTimestamp(1000);
-        $transportMessage->setReplyTo('theReplyTo');
-        $transportMessage->setCorrelationId('theCorrelationId');
-
-        $driver = new RabbitMqDriver(
-            $this->createAmqpContextMock(),
-            new Config('', '', '', '', '', '', ['delay_strategy' => 'dlx']),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $clientMessage = $driver->createClientMessage($transportMessage);
-
-        $this->assertInstanceOf(Message::class, $clientMessage);
-        $this->assertSame('body', $clientMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'content_type' => 'ContentType',
-            'expiration' => '12345000',
-            'priority' => 3,
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-            'reply_to' => 'theReplyTo',
-            'correlation_id' => 'theCorrelationId',
-        ], $clientMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-            'enqueue-delay' => '5678000',
-        ], $clientMessage->getProperties());
-        $this->assertSame('MessageId', $clientMessage->getMessageId());
-        $this->assertSame(12345, $clientMessage->getExpire());
-        $this->assertSame(5678, $clientMessage->getDelay());
-        $this->assertSame('ContentType', $clientMessage->getContentType());
-        $this->assertSame(1000, $clientMessage->getTimestamp());
-        $this->assertSame(MessagePriority::HIGH, $clientMessage->getPriority());
-        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
-    }
-
-    public function testShouldThrowExceptionIfXDelayIsNotNumeric()
-    {
-        $transportMessage = new AmqpMessage();
-        $transportMessage->setProperty('enqueue-delay', 'is-not-numeric');
-
-        $driver = new RabbitMqDriver(
-            $this->createAmqpContextMock(),
-            Config::create(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('"enqueue-delay" header is not numeric. "is-not-numeric"');
-
-        $driver->createClientMessage($transportMessage);
-    }
-
-    public function testShouldThrowExceptionIfCantConvertClientPriorityToTransportPriority()
-    {
-        $clientMessage = new Message();
-        $clientMessage->setPriority('unknown');
-
-        $context = $this->createAmqpContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn(new AmqpMessage())
-        ;
-
-        $driver = new RabbitMqDriver(
-            $context,
-            Config::create(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Given priority could not be converted to client\'s one. Got: unknown');
-
-        $driver->createTransportMessage($clientMessage);
-    }
-
-    public function testShouldConvertClientMessageToTransportMessage()
-    {
-        $clientMessage = new Message();
-        $clientMessage->setBody('body');
-        $clientMessage->setHeaders(['hkey' => 'hval']);
-        $clientMessage->setProperties(['key' => 'val']);
-        $clientMessage->setContentType('ContentType');
-        $clientMessage->setExpire(123);
-        $clientMessage->setPriority(MessagePriority::VERY_HIGH);
-        $clientMessage->setDelay(432);
-        $clientMessage->setMessageId('MessageId');
-        $clientMessage->setTimestamp(1000);
-        $clientMessage->setReplyTo('theReplyTo');
-        $clientMessage->setCorrelationId('theCorrelationId');
-
-        $context = $this->createAmqpContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn(new AmqpMessage())
-        ;
-
-        $driver = new RabbitMqDriver(
-            $context,
-            new Config('', '', '', '', '', '', ['delay_strategy' => 'dlx']),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $transportMessage = $driver->createTransportMessage($clientMessage);
-
-        $this->assertInstanceOf(AmqpMessage::class, $transportMessage);
-        $this->assertSame('body', $transportMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-            'reply_to' => 'theReplyTo',
-            'correlation_id' => 'theCorrelationId',
-            'content_type' => 'ContentType',
-            'delivery_mode' => 2,
-            'expiration' => '123000',
-            'priority' => 4,
-        ], $transportMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-            'enqueue-delay' => 432000,
-        ], $transportMessage->getProperties());
-        $this->assertSame('MessageId', $transportMessage->getMessageId());
-        $this->assertSame(1000, $transportMessage->getTimestamp());
-        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
-    }
-
-    public function testThrowIfDelayNotSupportedOnConvertClientMessageToTransportMessage()
-    {
-        $clientMessage = new Message();
-        $clientMessage->setDelay(432);
-
-        $context = $this->createAmqpContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn(new AmqpMessage())
-        ;
-
-        $driver = new RabbitMqDriver(
-            $context,
-            new Config('', '', '', '', '', '', ['delay_strategy' => null]),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The message delaying is not supported. In order to use delay feature install RabbitMQ delay strategy.');
-        $driver->createTransportMessage($clientMessage);
-    }
-
-    public function testShouldSendMessageToRouter()
-    {
-        $topic = new AmqpTopic('');
-        $transportMessage = new AmqpMessage();
-
-        $producer = $this->createAmqpProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createAmqpContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createTopic')
-            ->willReturn($topic)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new RabbitMqDriver(
-            $context,
-            Config::create(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-
-        $driver->sendToRouter($message);
+        $this->assertClassExtends(GenericDriver::class, RabbitMqDriver::class);
     }
 
-    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
+    public function testShouldBeSubClassOfAmqpDriver()
     {
-        $driver = new RabbitMqDriver(
-            $this->createAmqpContextMock(),
-            Config::create(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Topic name parameter is required but is not set');
-
-        $driver->sendToRouter(new Message());
-    }
-
-    public function testShouldSendMessageToProcessor()
-    {
-        $queue = new AmqpQueue('');
-        $transportMessage = new AmqpMessage();
-
-        $producer = $this->createAmqpProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createAmqpContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->willReturn($queue)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new RabbitMqDriver(
-            $context,
-            Config::create(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
-
-        $driver->sendToProcessor($message);
+        $this->assertClassExtends(AmqpDriver::class, RabbitMqDriver::class);
     }
 
-    public function testShouldSendMessageToProcessorWithDeliveryDelay()
+    public function testShouldCreateQueueWithMaxPriorityArgument()
     {
-        $queue = new AmqpQueue('');
-        $transportMessage = new AmqpMessage();
-
-        $producer = $this->createAmqpProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
-        ;
-        $producer
-            ->expects($this->once())
-            ->method('setDeliveryDelay')
-            ->with($this->identicalTo(10000))
-        ;
-        $context = $this->createAmqpContextMock();
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
             ->method('createQueue')
-            ->willReturn($queue)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
+            ->willReturn($this->createQueue('aName'))
         ;
 
-        $driver = new RabbitMqDriver(
+        $driver = $this->createDriver(
             $context,
-            new Config('', '', '', '', '', '', ['delay_strategy' => 'dlx']),
-            $this->createDummyQueueMetaRegistry()
+            $this->createDummyConfig(),
+            new RouteCollection([])
         );
 
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
-        $message->setDelay(10);
+        /** @var AmqpQueue $queue */
+        $queue = $driver->createQueue('aName');
 
-        $driver->sendToProcessor($message);
+        $this->assertSame(['x-max-priority' => 4], $queue->getArguments());
     }
 
-    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
+    protected function createDriver(...$args): DriverInterface
     {
-        $driver = new RabbitMqDriver(
-            $this->createAmqpContextMock(),
-            Config::create(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Processor name parameter is required but is not set');
-
-        $driver->sendToProcessor(new Message());
+        return new RabbitMqDriver(...$args);
     }
 
-    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
-    {
-        $driver = new RabbitMqDriver(
-            $this->createAmqpContextMock(),
-            Config::create(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Queue name parameter is required but is not set');
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-
-        $driver->sendToProcessor($message);
-    }
-
-    public function testShouldSetupBrokerWhenDelayPluginNotInstalled()
+    /**
+     * @return AmqpContext
+     */
+    protected function createContextMock(): PsrContext
     {
-        $routerTopic = new AmqpTopic('');
-        $routerQueue = new AmqpQueue('');
-
-        $processorQueue = new AmqpQueue('');
-
-        $context = $this->createAmqpContextMock();
-        // setup router
-        $context
-            ->expects($this->at(0))
-            ->method('createTopic')
-            ->willReturn($routerTopic)
-        ;
-        $context
-            ->expects($this->at(1))
-            ->method('createQueue')
-            ->willReturn($routerQueue)
-        ;
-        $context
-            ->expects($this->at(2))
-            ->method('declareTopic')
-            ->with($this->identicalTo($routerTopic))
-        ;
-        $context
-            ->expects($this->at(3))
-            ->method('declareQueue')
-            ->with($this->identicalTo($routerQueue))
-        ;
-        $context
-            ->expects($this->at(4))
-            ->method('bind')
-            ->with($this->isInstanceOf(AmqpBind::class))
-        ;
-        // setup processor queue
-        $context
-            ->expects($this->at(5))
-            ->method('createQueue')
-            ->willReturn($processorQueue)
-        ;
-
-        $config = Config::create('', '', '', '', '', '', ['delay_strategy' => null]);
-
-        $meta = new QueueMetaRegistry($config, ['default' => []]);
-
-        $driver = new RabbitMqDriver($context, $config, $meta);
-
-        $driver->setupBroker();
+        return $this->createMock(AmqpContext::class);
     }
 
-    public function testShouldSetupBroker()
+    /**
+     * @return AmqpProducer
+     */
+    protected function createProducerMock(): PsrProducer
     {
-        $routerTopic = new AmqpTopic('');
-        $routerQueue = new AmqpQueue('');
-
-        $processorQueue = new AmqpQueue('');
-
-        $context = $this->createAmqpContextMock();
-        // setup router
-        $context
-            ->expects($this->at(0))
-            ->method('createTopic')
-            ->willReturn($routerTopic)
-        ;
-        $context
-            ->expects($this->at(1))
-            ->method('createQueue')
-            ->willReturn($routerQueue)
-        ;
-        $context
-            ->expects($this->at(2))
-            ->method('declareTopic')
-            ->with($this->identicalTo($routerTopic))
-        ;
-        $context
-            ->expects($this->at(3))
-            ->method('declareQueue')
-            ->with($this->identicalTo($routerQueue))
-        ;
-        $context
-            ->expects($this->at(4))
-            ->method('bind')
-            ->with($this->isInstanceOf(AmqpBind::class))
-        ;
-        // setup processor queue
-        $context
-            ->expects($this->at(5))
-            ->method('createQueue')
-            ->willReturn($processorQueue)
-        ;
-        $context
-            ->expects($this->at(6))
-            ->method('declareQueue')
-            ->with($this->identicalTo($processorQueue))
-        ;
-
-        $config = Config::create('', '', '', '', '', '', ['delay_strategy' => 'dlx']);
-
-        $meta = new QueueMetaRegistry($config, ['default' => []]);
-
-        $driver = new RabbitMqDriver($context, $config, $meta);
-
-        $driver->setupBroker();
+        return $this->createMock(AmqpProducer::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|AmqpContext
+     * @return AmqpQueue
      */
-    private function createAmqpContextMock()
+    protected function createQueue(string $name): PsrQueue
     {
-        return $this->createMock(AmqpContext::class);
+        return new AmqpQueue($name);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|AmqpProducer
+     * @return AmqpTopic
      */
-    private function createAmqpProducerMock()
+    protected function createTopic(string $name): AmqpTopic
     {
-        return $this->createMock(AmqpProducer::class);
+        return new AmqpTopic($name);
     }
 
     /**
-     * @return QueueMetaRegistry
+     * @return AmqpMessage
      */
-    private function createDummyQueueMetaRegistry()
+    protected function createMessage(): PsrMessage
     {
-        $registry = new QueueMetaRegistry(Config::create('aPrefix'), []);
-        $registry->add('aFooQueue');
-        $registry->add('aBarQueue', 'aBarQueue');
+        return new AmqpMessage();
+    }
 
-        return $registry;
+    protected function assertTransportMessage(PsrMessage $transportMessage): void
+    {
+        $this->assertSame('body', $transportMessage->getBody());
+        $this->assertArraySubset([
+            'hkey' => 'hval',
+            'delivery_mode' => AmqpMessage::DELIVERY_MODE_PERSISTENT,
+            'content_type' => 'ContentType',
+            'expiration' => '123000',
+            'priority' => 3,
+            'message_id' => 'theMessageId',
+            'timestamp' => 1000,
+            'reply_to' => 'theReplyTo',
+            'correlation_id' => 'theCorrelationId',
+        ], $transportMessage->getHeaders());
+        $this->assertEquals([
+            'pkey' => 'pval',
+            'X-Enqueue-Content-Type' => 'ContentType',
+            'X-Enqueue-Priority' => MessagePriority::HIGH,
+            'X-Enqueue-Expire' => 123,
+            'X-Enqueue-Delay' => 345,
+        ], $transportMessage->getProperties());
+        $this->assertSame('theMessageId', $transportMessage->getMessageId());
+        $this->assertSame(1000, $transportMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
     }
 }

From ea3eaf945ac60431ec0dbf2011d60c0d711ab994 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 17 Sep 2018 19:47:27 +0300
Subject: [PATCH 119/337] [client] Extend FsDriver from GenericDriver.

---
 Client/Driver/AmqpDriver.php         |  40 +---
 Client/Driver/FsDriver.php           | 148 ++----------
 Client/Driver/RabbitMqDriver.php     |   2 +-
 Tests/Client/Driver/FsDriverTest.php | 336 ++++-----------------------
 4 files changed, 73 insertions(+), 453 deletions(-)

diff --git a/Client/Driver/AmqpDriver.php b/Client/Driver/AmqpDriver.php
index 0b2a976..9b62ddc 100644
--- a/Client/Driver/AmqpDriver.php
+++ b/Client/Driver/AmqpDriver.php
@@ -5,9 +5,7 @@
 namespace  Enqueue\Client\Driver;
 
 use Enqueue\AmqpExt\AmqpProducer;
-use Enqueue\Client\Config;
 use Enqueue\Client\Message;
-use Enqueue\Client\RouteCollection;
 use Interop\Amqp\AmqpContext;
 use Interop\Amqp\AmqpMessage;
 use Interop\Amqp\AmqpQueue;
@@ -20,30 +18,14 @@
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
+/**
+ * @method AmqpContext getContext
+ */
 class AmqpDriver extends GenericDriver
 {
-    /**
-     * @var AmqpContext
-     */
-    private $context;
-
-    /**
-     * @var Config
-     */
-    private $config;
-
-    /**
-     * @var RouteCollection
-     */
-    private $routeCollection;
-
-    public function __construct(AmqpContext $context, Config $config, RouteCollection $routeCollection)
+    public function __construct(AmqpContext $context, ...$args)
     {
-        $this->context = $context;
-        $this->config = $config;
-        $this->routeCollection = $routeCollection;
-
-        parent::__construct($context, $config, $routeCollection);
+        parent::__construct($context, ...$args);
     }
 
     /**
@@ -86,18 +68,18 @@ public function setupBroker(LoggerInterface $logger = null): void
         // setup router
         $routerTopic = $this->createRouterTopic();
         $log('Declare router exchange: %s', $routerTopic->getTopicName());
-        $this->context->declareTopic($routerTopic);
+        $this->getContext()->declareTopic($routerTopic);
 
-        $routerQueue = $this->createQueue($this->config->getRouterQueueName());
+        $routerQueue = $this->createQueue($this->getConfig()->getRouterQueueName());
         $log('Declare router queue: %s', $routerQueue->getQueueName());
-        $this->context->declareQueue($routerQueue);
+        $this->getContext()->declareQueue($routerQueue);
 
         $log('Bind router queue to exchange: %s -> %s', $routerQueue->getQueueName(), $routerTopic->getTopicName());
-        $this->context->bind(new AmqpBind($routerTopic, $routerQueue, $routerQueue->getQueueName()));
+        $this->getContext()->bind(new AmqpBind($routerTopic, $routerQueue, $routerQueue->getQueueName()));
 
         // setup queues
         $declaredQueues = [];
-        foreach ($this->routeCollection->all() as $route) {
+        foreach ($this->getRouteCollection()->all() as $route) {
             /** @var AmqpQueue $queue */
             $queue = $this->createRouteQueue($route);
             if (array_key_exists($queue->getQueueName(), $declaredQueues)) {
@@ -105,7 +87,7 @@ public function setupBroker(LoggerInterface $logger = null): void
             }
 
             $log('Declare processor queue: %s', $queue->getQueueName());
-            $this->context->declareQueue($queue);
+            $this->getContext()->declareQueue($queue);
 
             $declaredQueues[$queue->getQueueName()] = true;
         }
diff --git a/Client/Driver/FsDriver.php b/Client/Driver/FsDriver.php
index 76c22b1..376e419 100644
--- a/Client/Driver/FsDriver.php
+++ b/Client/Driver/FsDriver.php
@@ -2,69 +2,21 @@
 
 namespace  Enqueue\Client\Driver;
 
-use Enqueue\Client\Config;
-use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\MessagePriority;
-use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\Fs\FsContext;
 use Enqueue\Fs\FsDestination;
-use Enqueue\Fs\FsMessage;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
-class FsDriver implements DriverInterface
+/**
+ * @method FsContext getContext
+ * @method FsDestination createQueue(string $name)
+ * @method FsDestination createRouterTopic
+ */
+class FsDriver extends GenericDriver
 {
-    /**
-     * @var FsContext
-     */
-    private $context;
-
-    /**
-     * @var Config
-     */
-    private $config;
-
-    /**
-     * @var QueueMetaRegistry
-     */
-    private $queueMetaRegistry;
-
-    public function __construct(FsContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
-    {
-        $this->context = $context;
-        $this->config = $config;
-        $this->queueMetaRegistry = $queueMetaRegistry;
-    }
-
-    public function sendToRouter(Message $message): void
-    {
-        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
-            throw new \LogicException('Topic name parameter is required but is not set');
-        }
-
-        $topic = $this->createRouterTopic();
-        $transportMessage = $this->createTransportMessage($message);
-
-        $this->context->createProducer()->send($topic, $transportMessage);
-    }
-
-    public function sendToProcessor(Message $message): void
+    public function __construct(FsContext $context, ...$args)
     {
-        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
-            throw new \LogicException('Processor name parameter is required but is not set');
-        }
-
-        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
-            throw new \LogicException('Queue name parameter is required but is not set');
-        }
-
-        $transportMessage = $this->createTransportMessage($message);
-        $destination = $this->createQueue($queueName);
-
-        $this->context->createProducer()->send($destination, $transportMessage);
+        parent::__construct($context, ...$args);
     }
 
     public function setupBroker(LoggerInterface $logger = null): void
@@ -76,85 +28,27 @@ public function setupBroker(LoggerInterface $logger = null): void
 
         // setup router
         $routerTopic = $this->createRouterTopic();
-        $routerQueue = $this->createQueue($this->config->getRouterQueueName());
+        $routerQueue = $this->createQueue($this->getConfig()->getRouterQueueName());
 
         $log('Declare router exchange "%s" file: %s', $routerTopic->getTopicName(), $routerTopic->getFileInfo());
-        $this->context->declareDestination($routerTopic);
+        $this->getContext()->declareDestination($routerTopic);
 
         $log('Declare router queue "%s" file: %s', $routerQueue->getQueueName(), $routerTopic->getFileInfo());
-        $this->context->declareDestination($routerQueue);
+        $this->getContext()->declareDestination($routerQueue);
 
         // setup queues
-        foreach ($this->queueMetaRegistry->getQueuesMeta() as $meta) {
-            $queue = $this->createQueue($meta->getClientName());
+        $declaredQueues = [];
+        foreach ($this->getRouteCollection()->all() as $route) {
+            /** @var FsDestination $queue */
+            $queue = $this->createRouteQueue($route);
+            if (array_key_exists($queue->getQueueName(), $declaredQueues)) {
+                continue;
+            }
 
             $log('Declare processor queue "%s" file: %s', $queue->getQueueName(), $queue->getFileInfo());
-            $this->context->declareDestination($queue);
-        }
-    }
-
-    /**
-     * @return FsDestination
-     */
-    public function createQueue(string $queueName): PsrQueue
-    {
-        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
-
-        return $this->context->createQueue($transportName);
-    }
-
-    /**
-     * @return FsMessage
-     */
-    public function createTransportMessage(Message $message): PsrMessage
-    {
-        $properties = $message->getProperties();
-
-        $headers = $message->getHeaders();
-        $headers['content_type'] = $message->getContentType();
-
-        $transportMessage = $this->context->createMessage();
-        $transportMessage->setBody($message->getBody());
-        $transportMessage->setHeaders($headers);
-        $transportMessage->setProperties($properties);
-        $transportMessage->setMessageId($message->getMessageId());
-        $transportMessage->setTimestamp($message->getTimestamp());
-        $transportMessage->setReplyTo($message->getReplyTo());
-        $transportMessage->setCorrelationId($message->getCorrelationId());
-
-        return $transportMessage;
-    }
-
-    /**
-     * @param FsMessage $message
-     */
-    public function createClientMessage(PsrMessage $message): Message
-    {
-        $clientMessage = new Message();
-
-        $clientMessage->setBody($message->getBody());
-        $clientMessage->setHeaders($message->getHeaders());
-        $clientMessage->setProperties($message->getProperties());
-
-        $clientMessage->setContentType($message->getHeader('content_type'));
-        $clientMessage->setMessageId($message->getMessageId());
-        $clientMessage->setTimestamp($message->getTimestamp());
-        $clientMessage->setPriority(MessagePriority::NORMAL);
-        $clientMessage->setReplyTo($message->getReplyTo());
-        $clientMessage->setCorrelationId($message->getCorrelationId());
-
-        return $clientMessage;
-    }
+            $this->getContext()->declareDestination($queue);
 
-    public function getConfig(): Config
-    {
-        return $this->config;
-    }
-
-    private function createRouterTopic(): FsDestination
-    {
-        return $this->context->createTopic(
-            $this->config->createTransportQueueName($this->config->getRouterTopicName())
-        );
+            $declaredQueues[$queue->getQueueName()] = true;
+        }
     }
 }
diff --git a/Client/Driver/RabbitMqDriver.php b/Client/Driver/RabbitMqDriver.php
index fd1f019..2d5611b 100644
--- a/Client/Driver/RabbitMqDriver.php
+++ b/Client/Driver/RabbitMqDriver.php
@@ -5,7 +5,7 @@
 use Interop\Amqp\AmqpQueue;
 use Interop\Queue\PsrQueue;
 
-final class RabbitMqDriver extends AmqpDriver
+class RabbitMqDriver extends AmqpDriver
 {
     /**
      * @return AmqpQueue
diff --git a/Tests/Client/Driver/FsDriverTest.php b/Tests/Client/Driver/FsDriverTest.php
index 6e9cb55..f65cab0 100644
--- a/Tests/Client/Driver/FsDriverTest.php
+++ b/Tests/Client/Driver/FsDriverTest.php
@@ -2,301 +2,37 @@
 
 namespace Enqueue\Tests\Client\Driver;
 
-use Enqueue\Client\Config;
 use Enqueue\Client\Driver\FsDriver;
+use Enqueue\Client\Driver\GenericDriver;
 use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\MessagePriority;
-use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
 use Enqueue\Fs\FsContext;
 use Enqueue\Fs\FsDestination;
 use Enqueue\Fs\FsMessage;
+use Enqueue\Fs\FsProducer;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrContext;
+use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrProducer;
+use Interop\Queue\PsrQueue;
+use Interop\Queue\PsrTopic;
 use Makasim\File\TempFile;
+use PHPUnit\Framework\TestCase;
 
-class FsDriverTest extends \PHPUnit\Framework\TestCase
+class FsDriverTest extends TestCase
 {
     use ClassExtensionTrait;
+    use GenericDriverTestsTrait;
 
     public function testShouldImplementsDriverInterface()
     {
         $this->assertClassImplements(DriverInterface::class, FsDriver::class);
     }
 
-    public function testCouldBeConstructedWithRequiredArguments()
+    public function testShouldBeSubClassOfGenericDriver()
     {
-        new FsDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-    }
-
-    public function testShouldReturnConfigObject()
-    {
-        $config = $this->createDummyConfig();
-
-        $driver = new FsDriver($this->createPsrContextMock(), $config, $this->createDummyQueueMetaRegistry());
-
-        $this->assertSame($config, $driver->getConfig());
-    }
-
-    public function testShouldCreateAndReturnQueueInstance()
-    {
-        $expectedQueue = new FsDestination(new TempFile(sys_get_temp_dir().'/queue-name'));
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix.afooqueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new FsDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aFooQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
-    {
-        $expectedQueue = new FsDestination(new TempFile(sys_get_temp_dir().'/queue-name'));
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aBarQueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new FsDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aBarQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldConvertTransportMessageToClientMessage()
-    {
-        $transportMessage = new FsMessage();
-        $transportMessage->setBody('body');
-        $transportMessage->setHeaders(['hkey' => 'hval']);
-        $transportMessage->setProperties(['key' => 'val']);
-        $transportMessage->setHeader('content_type', 'ContentType');
-        $transportMessage->setMessageId('MessageId');
-        $transportMessage->setTimestamp(1000);
-        $transportMessage->setReplyTo('theReplyTo');
-        $transportMessage->setCorrelationId('theCorrelationId');
-
-        $driver = new FsDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $clientMessage = $driver->createClientMessage($transportMessage);
-
-        $this->assertInstanceOf(Message::class, $clientMessage);
-        $this->assertSame('body', $clientMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'content_type' => 'ContentType',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-            'reply_to' => 'theReplyTo',
-            'correlation_id' => 'theCorrelationId',
-        ], $clientMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-        ], $clientMessage->getProperties());
-        $this->assertSame('MessageId', $clientMessage->getMessageId());
-        $this->assertSame('ContentType', $clientMessage->getContentType());
-        $this->assertSame(1000, $clientMessage->getTimestamp());
-        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
-
-        $this->assertNull($clientMessage->getExpire());
-        $this->assertSame(MessagePriority::NORMAL, $clientMessage->getPriority());
-    }
-
-    public function testShouldConvertClientMessageToTransportMessage()
-    {
-        $clientMessage = new Message();
-        $clientMessage->setBody('body');
-        $clientMessage->setHeaders(['hkey' => 'hval']);
-        $clientMessage->setProperties(['key' => 'val']);
-        $clientMessage->setContentType('ContentType');
-        $clientMessage->setExpire(123);
-        $clientMessage->setPriority(MessagePriority::VERY_HIGH);
-        $clientMessage->setMessageId('MessageId');
-        $clientMessage->setTimestamp(1000);
-        $clientMessage->setReplyTo('theReplyTo');
-        $clientMessage->setCorrelationId('theCorrelationId');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn(new FsMessage())
-        ;
-
-        $driver = new FsDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $transportMessage = $driver->createTransportMessage($clientMessage);
-
-        $this->assertInstanceOf(FsMessage::class, $transportMessage);
-        $this->assertSame('body', $transportMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'content_type' => 'ContentType',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-            'reply_to' => 'theReplyTo',
-            'correlation_id' => 'theCorrelationId',
-        ], $transportMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-        ], $transportMessage->getProperties());
-        $this->assertSame('MessageId', $transportMessage->getMessageId());
-        $this->assertSame(1000, $transportMessage->getTimestamp());
-        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
-    }
-
-    public function testShouldSendMessageToRouter()
-    {
-        $topic = new FsDestination(TempFile::generate());
-        $transportMessage = new FsMessage();
-        $config = $this->createDummyConfig();
-
-        $producer = $this->createPsrProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createTopic')
-            ->with('aprefix.router')
-            ->willReturn($topic)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new FsDriver(
-            $context,
-            $config,
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-
-        $driver->sendToRouter($message);
-    }
-
-    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
-    {
-        $driver = new FsDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Topic name parameter is required but is not set');
-
-        $driver->sendToRouter(new Message());
-    }
-
-    public function testShouldSendMessageToProcessor()
-    {
-        $queue = new FsDestination(TempFile::generate());
-        $transportMessage = new FsMessage();
-
-        $producer = $this->createPsrProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->willReturn($queue)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new FsDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
-
-        $driver->sendToProcessor($message);
-    }
-
-    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
-    {
-        $driver = new FsDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Processor name parameter is required but is not set');
-
-        $driver->sendToProcessor(new Message());
-    }
-
-    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
-    {
-        $driver = new FsDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Queue name parameter is required but is not set');
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-
-        $driver->sendToProcessor($message);
+        $this->assertClassExtends(GenericDriver::class, FsDriver::class);
     }
 
     public function testShouldSetupBroker()
@@ -306,7 +42,7 @@ public function testShouldSetupBroker()
 
         $processorQueue = new FsDestination(TempFile::generate());
 
-        $context = $this->createPsrContextMock();
+        $context = $this->createContextMock();
         // setup router
         $context
             ->expects($this->at(0))
@@ -340,53 +76,61 @@ public function testShouldSetupBroker()
             ->with($this->identicalTo($processorQueue))
         ;
 
-        $meta = new QueueMetaRegistry($this->createDummyConfig(), [
-            'default' => [],
+        $routeCollection = new RouteCollection([
+            new Route('aTopic', Route::TOPIC, 'aProcessor'),
         ]);
 
         $driver = new FsDriver(
             $context,
             $this->createDummyConfig(),
-            $meta
+            $routeCollection
         );
 
         $driver->setupBroker();
     }
 
+    protected function createDriver(...$args): DriverInterface
+    {
+        return new FsDriver(...$args);
+    }
+
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|FsContext
+     * @return FsContext
      */
-    private function createPsrContextMock()
+    protected function createContextMock(): PsrContext
     {
         return $this->createMock(FsContext::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProducer
+     * @return FsProducer
      */
-    private function createPsrProducerMock()
+    protected function createProducerMock(): PsrProducer
     {
-        return $this->createMock(PsrProducer::class);
+        return $this->createMock(FsProducer::class);
     }
 
     /**
-     * @return QueueMetaRegistry
+     * @return FsDestination
      */
-    private function createDummyQueueMetaRegistry()
+    protected function createQueue(string $name): PsrQueue
     {
-        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
-        $registry->add('default');
-        $registry->add('aFooQueue');
-        $registry->add('aBarQueue', 'aBarQueue');
+        return new FsDestination(new \SplFileInfo($name));
+    }
 
-        return $registry;
+    /**
+     * @return FsDestination
+     */
+    protected function createTopic(string $name): PsrTopic
+    {
+        return new FsDestination(new \SplFileInfo($name));
     }
 
     /**
-     * @return Config
+     * @return FsMessage
      */
-    private function createDummyConfig()
+    protected function createMessage(): PsrMessage
     {
-        return Config::create('aPrefix');
+        return new FsMessage();
     }
 }

From ba6c9dcf59cb9687d940f4e03a1e1f36b4acaa73 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 18 Sep 2018 08:00:27 +0300
Subject: [PATCH 120/337] [client] Extend DbalDriver from GenericDriver.

---
 Client/Driver/DbalDriver.php           | 162 +-----------
 Tests/Client/Driver/DbalDriverTest.php | 327 +++----------------------
 2 files changed, 41 insertions(+), 448 deletions(-)

diff --git a/Client/Driver/DbalDriver.php b/Client/Driver/DbalDriver.php
index d031bdb..ce48885 100644
--- a/Client/Driver/DbalDriver.php
+++ b/Client/Driver/DbalDriver.php
@@ -2,155 +2,15 @@
 
 namespace Enqueue\Client\Driver;
 
-use Enqueue\Client\Config;
-use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\MessagePriority;
-use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\Dbal\DbalContext;
-use Enqueue\Dbal\DbalDestination;
-use Enqueue\Dbal\DbalMessage;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
-class DbalDriver implements DriverInterface
+/**
+ * @method DbalContext getContext
+ */
+class DbalDriver extends GenericDriver
 {
-    /**
-     * @var DbalContext
-     */
-    private $context;
-
-    /**
-     * @var Config
-     */
-    private $config;
-
-    /**
-     * @var QueueMetaRegistry
-     */
-    private $queueMetaRegistry;
-
-    /**
-     * @var array
-     */
-    private static $priorityMap = [
-        MessagePriority::VERY_LOW => 0,
-        MessagePriority::LOW => 1,
-        MessagePriority::NORMAL => 2,
-        MessagePriority::HIGH => 3,
-        MessagePriority::VERY_HIGH => 4,
-    ];
-
-    public function __construct(DbalContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
-    {
-        $this->context = $context;
-        $this->config = $config;
-        $this->queueMetaRegistry = $queueMetaRegistry;
-    }
-
-    /**
-     * @return DbalMessage
-     */
-    public function createTransportMessage(Message $message): PsrMessage
-    {
-        $properties = $message->getProperties();
-
-        $headers = $message->getHeaders();
-        $headers['content_type'] = $message->getContentType();
-
-        $transportMessage = $this->context->createMessage();
-        $transportMessage->setBody($message->getBody());
-        $transportMessage->setHeaders($headers);
-        $transportMessage->setProperties($properties);
-        $transportMessage->setMessageId($message->getMessageId());
-        $transportMessage->setTimestamp($message->getTimestamp());
-
-        $delay = $message->getDelay();
-        $transportMessage->setDeliveryDelay((null === $delay) ? null : ($delay * 1000));
-
-        $timeToLive = $message->getExpire();
-        $transportMessage->setTimeToLive((null === $timeToLive) ? null : ($timeToLive * 1000));
-        $transportMessage->setReplyTo($message->getReplyTo());
-        $transportMessage->setCorrelationId($message->getCorrelationId());
-        if (array_key_exists($message->getPriority(), self::$priorityMap)) {
-            $transportMessage->setPriority(self::$priorityMap[$message->getPriority()]);
-        }
-
-        return $transportMessage;
-    }
-
-    /**
-     * @param DbalMessage $message
-     */
-    public function createClientMessage(PsrMessage $message): Message
-    {
-        $clientMessage = new Message();
-
-        $clientMessage->setBody($message->getBody());
-        $clientMessage->setHeaders($message->getHeaders());
-        $clientMessage->setProperties($message->getProperties());
-
-        $clientMessage->setContentType($message->getHeader('content_type'));
-        $clientMessage->setMessageId($message->getMessageId());
-        $clientMessage->setTimestamp($message->getTimestamp());
-
-        $timeToLive = $message->getTimeToLive();
-        $clientMessage->setExpire((null === $timeToLive) ? null : (int) ($timeToLive / 1000));
-
-        $delay = $message->getDeliveryDelay();
-        $clientMessage->setDelay((null === $delay) ? null : (int) ($delay / 1000));
-        $clientMessage->setReplyTo($message->getReplyTo());
-        $clientMessage->setCorrelationId($message->getCorrelationId());
-
-        $priorityMap = array_flip(self::$priorityMap);
-        $priority = array_key_exists($message->getPriority(), $priorityMap) ?
-            $priorityMap[$message->getPriority()] :
-            MessagePriority::NORMAL;
-        $clientMessage->setPriority($priority);
-
-        return $clientMessage;
-    }
-
-    public function sendToRouter(Message $message): void
-    {
-        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
-            throw new \LogicException('Topic name parameter is required but is not set');
-        }
-
-        $queue = $this->createQueue($this->config->getRouterQueueName());
-        $transportMessage = $this->createTransportMessage($message);
-
-        $this->context->createProducer()->send($queue, $transportMessage);
-    }
-
-    public function sendToProcessor(Message $message): void
-    {
-        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
-            throw new \LogicException('Processor name parameter is required but is not set');
-        }
-
-        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
-            throw new \LogicException('Queue name parameter is required but is not set');
-        }
-
-        $transportMessage = $this->createTransportMessage($message);
-        $destination = $this->createQueue($queueName);
-
-        $this->context->createProducer()->send($destination, $transportMessage);
-    }
-
-    /**
-     * @return DbalDestination
-     */
-    public function createQueue(string $queueName): PsrQueue
-    {
-        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
-
-        return $this->context->createQueue($transportName);
-    }
-
     public function setupBroker(LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
@@ -158,17 +18,7 @@ public function setupBroker(LoggerInterface $logger = null): void
             $logger->debug(sprintf('[DbalDriver] '.$text, ...$args));
         };
 
-        $log('Creating database table: "%s"', $this->context->getTableName());
-        $this->context->createDataBaseTable();
-    }
-
-    public function getConfig(): Config
-    {
-        return $this->config;
-    }
-
-    public static function getPriorityMap(): array
-    {
-        return self::$priorityMap;
+        $log('Creating database table: "%s"', $this->getContext()->getTableName());
+        $this->getContext()->createDataBaseTable();
     }
 }
diff --git a/Tests/Client/Driver/DbalDriverTest.php b/Tests/Client/Driver/DbalDriverTest.php
index 859ed58..ed1064c 100644
--- a/Tests/Client/Driver/DbalDriverTest.php
+++ b/Tests/Client/Driver/DbalDriverTest.php
@@ -2,304 +2,39 @@
 
 namespace Enqueue\Tests\Client\Driver;
 
-use Enqueue\Client\Config;
 use Enqueue\Client\Driver\DbalDriver;
+use Enqueue\Client\Driver\GenericDriver;
 use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\MessagePriority;
-use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Client\RouteCollection;
 use Enqueue\Dbal\DbalContext;
 use Enqueue\Dbal\DbalDestination;
 use Enqueue\Dbal\DbalMessage;
+use Enqueue\Dbal\DbalProducer;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrContext;
+use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrProducer;
+use Interop\Queue\PsrQueue;
+use Interop\Queue\PsrTopic;
 
 class DbalDriverTest extends \PHPUnit_Framework_TestCase
 {
     use ClassExtensionTrait;
+    use GenericDriverTestsTrait;
 
     public function testShouldImplementsDriverInterface()
     {
         $this->assertClassImplements(DriverInterface::class, DbalDriver::class);
     }
 
-    public function testCouldBeConstructedWithRequiredArguments()
+    public function testShouldBeSubClassOfGenericDriver()
     {
-        new DbalDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-    }
-
-    public function testShouldReturnConfigObject()
-    {
-        $config = $this->createDummyConfig();
-
-        $driver = new DbalDriver(
-            $this->createPsrContextMock(),
-            $config,
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->assertSame($config, $driver->getConfig());
-    }
-
-    public function testShouldCreateAndReturnQueueInstance()
-    {
-        $expectedQueue = new DbalDestination('aName');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix.afooqueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new DbalDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aFooQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
-    {
-        $expectedQueue = new DbalDestination('aName');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aBarQueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new DbalDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aBarQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldConvertTransportMessageToClientMessage()
-    {
-        $transportMessage = new DbalMessage();
-        $transportMessage->setBody('body');
-        $transportMessage->setHeaders(['hkey' => 'hval']);
-        $transportMessage->setProperties(['key' => 'val']);
-        $transportMessage->setHeader('content_type', 'ContentType');
-        $transportMessage->setMessageId('MessageId');
-        $transportMessage->setTimestamp(1000);
-        $transportMessage->setPriority(2);
-        $transportMessage->setDeliveryDelay(12345);
-        $transportMessage->setTimeToLive(67890);
-
-        $driver = new DbalDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $clientMessage = $driver->createClientMessage($transportMessage);
-
-        $this->assertInstanceOf(Message::class, $clientMessage);
-        $this->assertSame('body', $clientMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'content_type' => 'ContentType',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-        ], $clientMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-        ], $clientMessage->getProperties());
-        $this->assertSame('MessageId', $clientMessage->getMessageId());
-        $this->assertSame('ContentType', $clientMessage->getContentType());
-        $this->assertSame(1000, $clientMessage->getTimestamp());
-        $this->assertSame(12, $clientMessage->getDelay());
-        $this->assertSame(67, $clientMessage->getExpire());
-        $this->assertSame(MessagePriority::NORMAL, $clientMessage->getPriority());
-    }
-
-    public function testShouldConvertClientMessageToTransportMessage()
-    {
-        $clientMessage = new Message();
-        $clientMessage->setBody('body');
-        $clientMessage->setHeaders(['hkey' => 'hval']);
-        $clientMessage->setProperties(['key' => 'val']);
-        $clientMessage->setContentType('ContentType');
-        $clientMessage->setExpire(123);
-        $clientMessage->setPriority(MessagePriority::VERY_HIGH);
-        $clientMessage->setMessageId('MessageId');
-        $clientMessage->setTimestamp(1000);
-        $clientMessage->setDelay(23);
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn(new DbalMessage())
-        ;
-
-        $driver = new DbalDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $transportMessage = $driver->createTransportMessage($clientMessage);
-
-        $this->assertInstanceOf(DbalMessage::class, $transportMessage);
-        $this->assertSame('body', $transportMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'content_type' => 'ContentType',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-            'reply_to' => null,
-            'correlation_id' => null,
-        ], $transportMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-        ], $transportMessage->getProperties());
-        $this->assertSame(123000, $transportMessage->getTimeToLive());
-        $this->assertSame('MessageId', $transportMessage->getMessageId());
-        $this->assertSame(1000, $transportMessage->getTimestamp());
-        $this->assertSame(23000, $transportMessage->getDeliveryDelay());
-    }
-
-    public function testShouldSendMessageToRouter()
-    {
-        $topic = new DbalDestination('queue-name');
-        $transportMessage = new DbalMessage();
-
-        $producer = $this->createPsrProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix.default')
-            ->willReturn($topic)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new DbalDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-
-        $driver->sendToRouter($message);
-    }
-
-    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
-    {
-        $driver = new DbalDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Topic name parameter is required but is not set');
-
-        $driver->sendToRouter(new Message());
-    }
-
-    public function testShouldSendMessageToProcessor()
-    {
-        $queue = new DbalDestination('queue-name');
-        $transportMessage = new DbalMessage();
-
-        $producer = $this->createPsrProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->willReturn($queue)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new DbalDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
-
-        $driver->sendToProcessor($message);
-    }
-
-    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
-    {
-        $driver = new DbalDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Processor name parameter is required but is not set');
-
-        $driver->sendToProcessor(new Message());
-    }
-
-    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
-    {
-        $driver = new DbalDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Queue name parameter is required but is not set');
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-
-        $driver->sendToProcessor($message);
+        $this->assertClassExtends(GenericDriver::class, DbalDriver::class);
     }
 
     public function testShouldSetupBroker()
     {
-        $context = $this->createPsrContextMock();
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
             ->method('getTableName')
@@ -312,46 +47,54 @@ public function testShouldSetupBroker()
         $driver = new DbalDriver(
             $context,
             $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
+            new RouteCollection([])
         );
 
         $driver->setupBroker();
     }
 
+    protected function createDriver(...$args): DriverInterface
+    {
+        return new DbalDriver(...$args);
+    }
+
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|DbalContext
+     * @return DbalContext
      */
-    private function createPsrContextMock()
+    protected function createContextMock(): PsrContext
     {
         return $this->createMock(DbalContext::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProducer
+     * @return DbalProducer
      */
-    private function createPsrProducerMock()
+    protected function createProducerMock(): PsrProducer
     {
-        return $this->createMock(PsrProducer::class);
+        return $this->createMock(DbalProducer::class);
     }
 
     /**
-     * @return QueueMetaRegistry
+     * @return DbalDestination
      */
-    private function createDummyQueueMetaRegistry()
+    protected function createQueue(string $name): PsrQueue
     {
-        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
-        $registry->add('default');
-        $registry->add('aFooQueue');
-        $registry->add('aBarQueue', 'aBarQueue');
+        return new DbalDestination(new \SplFileInfo($name));
+    }
 
-        return $registry;
+    /**
+     * @return DbalDestination
+     */
+    protected function createTopic(string $name): PsrTopic
+    {
+        return new DbalDestination(new \SplFileInfo($name));
     }
 
     /**
-     * @return Config
+     * @return DbalMessage
      */
-    private function createDummyConfig()
+    protected function createMessage(): PsrMessage
     {
-        return Config::create('aPrefix');
+        return new DbalMessage();
     }
 }

From 8645ce59b4eceee7424f3942e62b0a09ac0ec88e Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 18 Sep 2018 10:27:30 +0300
Subject: [PATCH 121/337] [client] Extend GpsDriver from GenericDriver.

---
 Client/Driver/GpsDriver.php           | 148 ++---------
 Tests/Client/Driver/GpsDriverTest.php | 340 ++++----------------------
 2 files changed, 66 insertions(+), 422 deletions(-)

diff --git a/Client/Driver/GpsDriver.php b/Client/Driver/GpsDriver.php
index f45313e..84b574f 100644
--- a/Client/Driver/GpsDriver.php
+++ b/Client/Driver/GpsDriver.php
@@ -2,73 +2,19 @@
 
 namespace Enqueue\Client\Driver;
 
-use Enqueue\Client\Config;
-use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\Gps\GpsContext;
-use Enqueue\Gps\GpsMessage;
 use Enqueue\Gps\GpsQueue;
 use Enqueue\Gps\GpsTopic;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
-class GpsDriver implements DriverInterface
+/**
+ * @method GpsContext getContext
+ * @method GpsQueue createQueue(string $name)
+ * @method GpsTopic createRouterTopic
+ */
+class GpsDriver extends GenericDriver
 {
-    /**
-     * @var GpsContext
-     */
-    private $context;
-
-    /**
-     * @var Config
-     */
-    private $config;
-
-    /**
-     * @var QueueMetaRegistry
-     */
-    private $queueMetaRegistry;
-
-    public function __construct(GpsContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
-    {
-        $this->context = $context;
-        $this->config = $config;
-        $this->queueMetaRegistry = $queueMetaRegistry;
-    }
-
-    public function sendToRouter(Message $message): void
-    {
-        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
-            throw new \LogicException('Topic name parameter is required but is not set');
-        }
-
-        $topic = $this->createRouterTopic();
-        $transportMessage = $this->createTransportMessage($message);
-
-        $this->context->createProducer()->send($topic, $transportMessage);
-    }
-
-    public function sendToProcessor(Message $message): void
-    {
-        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
-            throw new \LogicException('Processor name parameter is required but is not set');
-        }
-
-        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
-            throw new \LogicException('Queue name parameter is required but is not set');
-        }
-
-        $transportMessage = $this->createTransportMessage($message);
-        $destination = $this->context->createTopic(
-            $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName())
-        ;
-
-        $this->context->createProducer()->send($destination, $transportMessage);
-    }
-
     public function setupBroker(LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
@@ -78,80 +24,26 @@ public function setupBroker(LoggerInterface $logger = null): void
 
         // setup router
         $routerTopic = $this->createRouterTopic();
-        $routerQueue = $this->createQueue($this->config->getRouterQueueName());
+        $routerQueue = $this->createQueue($this->getConfig()->getRouterQueueName());
 
         $log('Subscribe router topic to queue: %s -> %s', $routerTopic->getTopicName(), $routerQueue->getQueueName());
-        $this->context->subscribe($routerTopic, $routerQueue);
+        $this->getContext()->subscribe($routerTopic, $routerQueue);
 
         // setup queues
-        foreach ($this->queueMetaRegistry->getQueuesMeta() as $meta) {
-            $topic = $this->context->createTopic($meta->getTransportName());
-            $queue = $this->context->createQueue($meta->getTransportName());
+        $declaredQueues = [];
+        foreach ($this->getRouteCollection()->all() as $route) {
+            /** @var GpsQueue $queue */
+            $queue = $this->createRouteQueue($route);
+            if (array_key_exists($queue->getQueueName(), $declaredQueues)) {
+                continue;
+            }
 
-            $log('Subscribe processor topic to queue: %s -> %s', $topic->getTopicName(), $queue->getQueueName());
-            $this->context->subscribe($topic, $queue);
-        }
-    }
-
-    /**
-     * @return GpsQueue
-     */
-    public function createQueue(string $queueName): PsrQueue
-    {
-        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
+            $topic = $this->getContext()->createTopic($queue->getQueueName());
 
-        return $this->context->createQueue($transportName);
-    }
-
-    /**
-     * @return GpsMessage
-     */
-    public function createTransportMessage(Message $message): PsrMessage
-    {
-        $headers = $message->getHeaders();
-        $properties = $message->getProperties();
-
-        $transportMessage = $this->context->createMessage();
-        $transportMessage->setBody($message->getBody());
-        $transportMessage->setHeaders($headers);
-        $transportMessage->setProperties($properties);
-        $transportMessage->setMessageId($message->getMessageId());
-        $transportMessage->setTimestamp($message->getTimestamp());
-        $transportMessage->setReplyTo($message->getReplyTo());
-        $transportMessage->setCorrelationId($message->getCorrelationId());
-
-        return $transportMessage;
-    }
-
-    /**
-     * @param GpsMessage $message
-     */
-    public function createClientMessage(PsrMessage $message): Message
-    {
-        $clientMessage = new Message();
-
-        $clientMessage->setBody($message->getBody());
-        $clientMessage->setHeaders($message->getHeaders());
-        $clientMessage->setProperties($message->getProperties());
-        $clientMessage->setMessageId($message->getMessageId());
-        $clientMessage->setTimestamp($message->getTimestamp());
-        $clientMessage->setReplyTo($message->getReplyTo());
-        $clientMessage->setCorrelationId($message->getCorrelationId());
-
-        return $clientMessage;
-    }
-
-    public function getConfig(): Config
-    {
-        return $this->config;
-    }
-
-    private function createRouterTopic(): GpsTopic
-    {
-        $topic = $this->context->createTopic(
-            $this->config->createTransportRouterTopicName($this->config->getRouterTopicName())
-        );
+            $log('Subscribe processor topic to queue: %s -> %s', $topic->getTopicName(), $queue->getQueueName());
+            $this->getContext()->subscribe($topic, $queue);
 
-        return $topic;
+            $declaredQueues[$queue->getQueueName()] = true;
+        }
     }
 }
diff --git a/Tests/Client/Driver/GpsDriverTest.php b/Tests/Client/Driver/GpsDriverTest.php
index 622f622..dd10e47 100644
--- a/Tests/Client/Driver/GpsDriverTest.php
+++ b/Tests/Client/Driver/GpsDriverTest.php
@@ -2,293 +2,37 @@
 
 namespace Enqueue\Tests\Client\Driver;
 
-use Enqueue\Client\Config;
+use Enqueue\Client\Driver\GenericDriver;
 use Enqueue\Client\Driver\GpsDriver;
 use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
 use Enqueue\Gps\GpsContext;
 use Enqueue\Gps\GpsMessage;
 use Enqueue\Gps\GpsProducer;
 use Enqueue\Gps\GpsQueue;
 use Enqueue\Gps\GpsTopic;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrContext;
+use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrProducer;
+use Interop\Queue\PsrQueue;
+use Interop\Queue\PsrTopic;
 use PHPUnit\Framework\TestCase;
 
 class GpsDriverTest extends TestCase
 {
     use ClassExtensionTrait;
+    use GenericDriverTestsTrait;
 
     public function testShouldImplementsDriverInterface()
     {
         $this->assertClassImplements(DriverInterface::class, GpsDriver::class);
     }
 
-    public function testCouldBeConstructedWithRequiredArguments()
+    public function testShouldBeSubClassOfGenericDriver()
     {
-        new GpsDriver(
-            $this->createGpsContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-    }
-
-    public function testShouldReturnConfigObject()
-    {
-        $config = $this->createDummyConfig();
-
-        $driver = new GpsDriver($this->createGpsContextMock(), $config, $this->createDummyQueueMetaRegistry());
-
-        $this->assertSame($config, $driver->getConfig());
-    }
-
-    public function testShouldCreateAndReturnQueueInstance()
-    {
-        $expectedQueue = new GpsQueue('aName');
-
-        $context = $this->createGpsContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix.afooqueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new GpsDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aFooQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
-    {
-        $expectedQueue = new GpsQueue('aName');
-
-        $context = $this->createGpsContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aBarQueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new GpsDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aBarQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldConvertTransportMessageToClientMessage()
-    {
-        $transportMessage = new GpsMessage();
-        $transportMessage->setBody('body');
-        $transportMessage->setHeaders(['hkey' => 'hval']);
-        $transportMessage->setProperties(['key' => 'val']);
-        $transportMessage->setMessageId('MessageId');
-        $transportMessage->setTimestamp(1000);
-        $transportMessage->setReplyTo('theReplyTo');
-        $transportMessage->setCorrelationId('theCorrelationId');
-
-        $driver = new GpsDriver(
-            $this->createGpsContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $clientMessage = $driver->createClientMessage($transportMessage);
-
-        $this->assertInstanceOf(Message::class, $clientMessage);
-        $this->assertSame('body', $clientMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-            'reply_to' => 'theReplyTo',
-            'correlation_id' => 'theCorrelationId',
-        ], $clientMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-        ], $clientMessage->getProperties());
-        $this->assertSame('MessageId', $clientMessage->getMessageId());
-        $this->assertNull($clientMessage->getExpire());
-        $this->assertNull($clientMessage->getContentType());
-        $this->assertSame(1000, $clientMessage->getTimestamp());
-        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
-    }
-
-    public function testShouldConvertClientMessageToTransportMessage()
-    {
-        $clientMessage = new Message();
-        $clientMessage->setBody('body');
-        $clientMessage->setHeaders(['hkey' => 'hval']);
-        $clientMessage->setProperties(['key' => 'val']);
-        $clientMessage->setContentType('ContentType');
-        $clientMessage->setExpire(123);
-        $clientMessage->setMessageId('MessageId');
-        $clientMessage->setTimestamp(1000);
-        $clientMessage->setReplyTo('theReplyTo');
-        $clientMessage->setCorrelationId('theCorrelationId');
-
-        $context = $this->createGpsContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn(new GpsMessage())
-        ;
-
-        $driver = new GpsDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $transportMessage = $driver->createTransportMessage($clientMessage);
-
-        $this->assertInstanceOf(GpsMessage::class, $transportMessage);
-        $this->assertSame('body', $transportMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-            'reply_to' => 'theReplyTo',
-            'correlation_id' => 'theCorrelationId',
-        ], $transportMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-        ], $transportMessage->getProperties());
-        $this->assertSame('MessageId', $transportMessage->getMessageId());
-        $this->assertSame(1000, $transportMessage->getTimestamp());
-        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
-    }
-
-    public function testShouldSendMessageToRouter()
-    {
-        $topic = new GpsTopic('');
-        $transportMessage = new GpsMessage();
-
-        $producer = $this->createGpsProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createGpsContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createTopic')
-            ->willReturn($topic)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new GpsDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-
-        $driver->sendToRouter($message);
-    }
-
-    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
-    {
-        $driver = new GpsDriver(
-            $this->createGpsContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Topic name parameter is required but is not set');
-
-        $driver->sendToRouter(new Message());
-    }
-
-    public function testShouldSendMessageToProcessor()
-    {
-        $topic = new GpsTopic('');
-        $transportMessage = new GpsMessage();
-
-        $producer = $this->createGpsProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createGpsContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createTopic')
-            ->willReturn($topic)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new GpsDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
-
-        $driver->sendToProcessor($message);
-    }
-
-    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
-    {
-        $driver = new GpsDriver(
-            $this->createGpsContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Processor name parameter is required but is not set');
-
-        $driver->sendToProcessor(new Message());
-    }
-
-    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
-    {
-        $driver = new GpsDriver(
-            $this->createGpsContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Queue name parameter is required but is not set');
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-
-        $driver->sendToProcessor($message);
+        $this->assertClassExtends(GenericDriver::class, GpsDriver::class);
     }
 
     public function testShouldSetupBroker()
@@ -296,10 +40,10 @@ public function testShouldSetupBroker()
         $routerTopic = new GpsTopic('');
         $routerQueue = new GpsQueue('');
 
-        $processorTopic = new GpsTopic('');
-        $processorQueue = new GpsQueue('');
+        $processorTopic = new GpsTopic('default');
+        $processorQueue = new GpsQueue('default');
 
-        $context = $this->createGpsContextMock();
+        $context = $this->createContextMock();
         // setup router
         $context
             ->expects($this->at(0))
@@ -316,16 +60,18 @@ public function testShouldSetupBroker()
             ->method('subscribe')
             ->with($this->identicalTo($routerTopic), $this->identicalTo($routerQueue))
         ;
-        // setup processor queue
         $context
             ->expects($this->at(3))
-            ->method('createTopic')
-            ->willReturn($processorTopic)
+            ->method('createQueue')
+            ->with('default')
+            ->willReturn($processorQueue)
         ;
+        // setup processor queue
         $context
             ->expects($this->at(4))
-            ->method('createQueue')
-            ->willReturn($processorQueue)
+            ->method('createTopic')
+            ->with('default')
+            ->willReturn($processorTopic)
         ;
         $context
             ->expects($this->at(5))
@@ -333,53 +79,59 @@ public function testShouldSetupBroker()
             ->with($this->identicalTo($processorTopic), $this->identicalTo($processorQueue))
         ;
 
-        $meta = new QueueMetaRegistry($this->createDummyConfig(), [
-            'default' => [],
-        ]);
-
         $driver = new GpsDriver(
             $context,
             $this->createDummyConfig(),
-            $meta
+            new RouteCollection([
+                new Route('aTopic', Route::TOPIC, 'aProcessor'),
+            ])
         );
 
         $driver->setupBroker();
     }
 
+    protected function createDriver(...$args): DriverInterface
+    {
+        return new GpsDriver(...$args);
+    }
+
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|GpsContext
+     * @return GpsContext
      */
-    private function createGpsContextMock()
+    protected function createContextMock(): PsrContext
     {
         return $this->createMock(GpsContext::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|GpsProducer
+     * @return GpsProducer
      */
-    private function createGpsProducerMock()
+    protected function createProducerMock(): PsrProducer
     {
         return $this->createMock(GpsProducer::class);
     }
 
     /**
-     * @return QueueMetaRegistry
+     * @return GpsQueue
      */
-    private function createDummyQueueMetaRegistry()
+    protected function createQueue(string $name): PsrQueue
     {
-        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
-        $registry->add('default');
-        $registry->add('aFooQueue');
-        $registry->add('aBarQueue', 'aBarQueue');
+        return new GpsQueue($name);
+    }
 
-        return $registry;
+    /**
+     * @return GpsTopic
+     */
+    protected function createTopic(string $name): PsrTopic
+    {
+        return new GpsTopic($name);
     }
 
     /**
-     * @return Config
+     * @return GpsMessage
      */
-    private function createDummyConfig()
+    protected function createMessage(): PsrMessage
     {
-        return Config::create('aPrefix');
+        return new GpsMessage();
     }
 }

From f7d8c2df7e1a1c06b05c906a7647a441f2f9e35f Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 18 Sep 2018 10:48:22 +0300
Subject: [PATCH 122/337] [client] Extend MongodbDriver from GenericDriver.

---
 Client/Driver/DbalDriver.php              |   5 +
 Client/Driver/GpsDriver.php               |   5 +
 Client/Driver/MongodbDriver.php           | 152 +---------
 Tests/Client/Driver/MongodbDriverTest.php | 324 +++-------------------
 4 files changed, 54 insertions(+), 432 deletions(-)

diff --git a/Client/Driver/DbalDriver.php b/Client/Driver/DbalDriver.php
index ce48885..8b1f326 100644
--- a/Client/Driver/DbalDriver.php
+++ b/Client/Driver/DbalDriver.php
@@ -11,6 +11,11 @@
  */
 class DbalDriver extends GenericDriver
 {
+    public function __construct(DbalContext $context, ...$args)
+    {
+        parent::__construct($context, ...$args);
+    }
+
     public function setupBroker(LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
diff --git a/Client/Driver/GpsDriver.php b/Client/Driver/GpsDriver.php
index 84b574f..01dc943 100644
--- a/Client/Driver/GpsDriver.php
+++ b/Client/Driver/GpsDriver.php
@@ -15,6 +15,11 @@
  */
 class GpsDriver extends GenericDriver
 {
+    public function __construct(GpsContext $context, ...$args)
+    {
+        parent::__construct($context, ...$args);
+    }
+
     public function setupBroker(LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
diff --git a/Client/Driver/MongodbDriver.php b/Client/Driver/MongodbDriver.php
index 9e53946..19f2c57 100644
--- a/Client/Driver/MongodbDriver.php
+++ b/Client/Driver/MongodbDriver.php
@@ -2,143 +2,18 @@
 
 namespace Enqueue\Client\Driver;
 
-use Enqueue\Client\Config;
-use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\MessagePriority;
-use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\Mongodb\MongodbContext;
-use Enqueue\Mongodb\MongodbDestination;
-use Enqueue\Mongodb\MongodbMessage;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
-class MongodbDriver implements DriverInterface
+/**
+ * @method MongodbContext getContext
+ */
+class MongodbDriver extends GenericDriver
 {
-    /**
-     * @var MongodbContext
-     */
-    private $context;
-
-    /**
-     * @var Config
-     */
-    private $config;
-
-    /**
-     * @var QueueMetaRegistry
-     */
-    private $queueMetaRegistry;
-
-    /**
-     * @var array
-     */
-    private static $priorityMap = [
-        MessagePriority::VERY_LOW => 0,
-        MessagePriority::LOW => 1,
-        MessagePriority::NORMAL => 2,
-        MessagePriority::HIGH => 3,
-        MessagePriority::VERY_HIGH => 4,
-    ];
-
-    public function __construct(MongodbContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
-    {
-        $this->context = $context;
-        $this->config = $config;
-        $this->queueMetaRegistry = $queueMetaRegistry;
-    }
-
-    /**
-     * @return MongodbMessage
-     */
-    public function createTransportMessage(Message $message): PsrMessage
-    {
-        $properties = $message->getProperties();
-
-        $headers = $message->getHeaders();
-        $headers['content_type'] = $message->getContentType();
-
-        $transportMessage = $this->context->createMessage();
-        $transportMessage->setBody($message->getBody());
-        $transportMessage->setHeaders($headers);
-        $transportMessage->setProperties($properties);
-        $transportMessage->setMessageId($message->getMessageId());
-        $transportMessage->setTimestamp($message->getTimestamp());
-        $transportMessage->setDeliveryDelay($message->getDelay());
-        $transportMessage->setReplyTo($message->getReplyTo());
-        $transportMessage->setCorrelationId($message->getCorrelationId());
-        if (array_key_exists($message->getPriority(), self::$priorityMap)) {
-            $transportMessage->setPriority(self::$priorityMap[$message->getPriority()]);
-        }
-
-        return $transportMessage;
-    }
-
-    /**
-     * @param MongodbMessage $message
-     */
-    public function createClientMessage(PsrMessage $message): Message
+    public function __construct(MongodbContext $context, ...$args)
     {
-        $clientMessage = new Message();
-
-        $clientMessage->setBody($message->getBody());
-        $clientMessage->setHeaders($message->getHeaders());
-        $clientMessage->setProperties($message->getProperties());
-
-        $clientMessage->setContentType($message->getHeader('content_type'));
-        $clientMessage->setMessageId($message->getMessageId());
-        $clientMessage->setTimestamp($message->getTimestamp());
-        $clientMessage->setDelay($message->getDeliveryDelay());
-        $clientMessage->setReplyTo($message->getReplyTo());
-        $clientMessage->setCorrelationId($message->getCorrelationId());
-
-        $priorityMap = array_flip(self::$priorityMap);
-        $priority = array_key_exists($message->getPriority(), $priorityMap) ?
-            $priorityMap[$message->getPriority()] :
-            MessagePriority::NORMAL;
-        $clientMessage->setPriority($priority);
-
-        return $clientMessage;
-    }
-
-    public function sendToRouter(Message $message): void
-    {
-        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
-            throw new \LogicException('Topic name parameter is required but is not set');
-        }
-
-        $queue = $this->createQueue($this->config->getRouterQueueName());
-        $transportMessage = $this->createTransportMessage($message);
-
-        $this->context->createProducer()->send($queue, $transportMessage);
-    }
-
-    public function sendToProcessor(Message $message): void
-    {
-        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
-            throw new \LogicException('Processor name parameter is required but is not set');
-        }
-
-        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
-            throw new \LogicException('Queue name parameter is required but is not set');
-        }
-
-        $transportMessage = $this->createTransportMessage($message);
-        $destination = $this->createQueue($queueName);
-
-        $this->context->createProducer()->send($destination, $transportMessage);
-    }
-
-    /**
-     * @return MongodbDestination
-     */
-    public function createQueue(string $queueName): PsrQueue
-    {
-        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
-
-        return $this->context->createQueue($transportName);
+        parent::__construct($context, ...$args);
     }
 
     public function setupBroker(LoggerInterface $logger = null): void
@@ -147,18 +22,9 @@ public function setupBroker(LoggerInterface $logger = null): void
         $log = function ($text, ...$args) use ($logger) {
             $logger->debug(sprintf('[MongodbDriver] '.$text, ...$args));
         };
-        $contextConfig = $this->context->getConfig();
-        $log('Creating database and collection: "%s" "%s"', $contextConfig['dbname'], $contextConfig['collection_name']);
-        $this->context->createCollection();
-    }
 
-    public function getConfig(): Config
-    {
-        return $this->config;
-    }
-
-    public static function getPriorityMap(): array
-    {
-        return self::$priorityMap;
+        $contextConfig = $this->getContext()->getConfig();
+        $log('Creating database and collection: "%s" "%s"', $contextConfig['dbname'], $contextConfig['collection_name']);
+        $this->getContext()->createCollection();
     }
 }
diff --git a/Tests/Client/Driver/MongodbDriverTest.php b/Tests/Client/Driver/MongodbDriverTest.php
index b10a067..6c9b55a 100644
--- a/Tests/Client/Driver/MongodbDriverTest.php
+++ b/Tests/Client/Driver/MongodbDriverTest.php
@@ -2,301 +2,39 @@
 
 namespace Enqueue\Tests\Client\Driver;
 
-use Enqueue\Client\Config;
+use Enqueue\Client\Driver\GenericDriver;
 use Enqueue\Client\Driver\MongodbDriver;
 use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\MessagePriority;
-use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Client\RouteCollection;
 use Enqueue\Mongodb\MongodbContext;
 use Enqueue\Mongodb\MongodbDestination;
 use Enqueue\Mongodb\MongodbMessage;
+use Enqueue\Mongodb\MongodbProducer;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrContext;
+use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrProducer;
+use Interop\Queue\PsrQueue;
+use Interop\Queue\PsrTopic;
 
 class MongodbDriverTest extends \PHPUnit_Framework_TestCase
 {
     use ClassExtensionTrait;
+    use GenericDriverTestsTrait;
 
     public function testShouldImplementsDriverInterface()
     {
         $this->assertClassImplements(DriverInterface::class, MongodbDriver::class);
     }
 
-    public function testCouldBeConstructedWithRequiredArguments()
+    public function testShouldBeSubClassOfGenericDriver()
     {
-        new MongodbDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-    }
-
-    public function testShouldReturnConfigObject()
-    {
-        $config = $this->createDummyConfig();
-
-        $driver = new MongodbDriver(
-            $this->createPsrContextMock(),
-            $config,
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->assertSame($config, $driver->getConfig());
-    }
-
-    public function testShouldCreateAndReturnQueueInstance()
-    {
-        $expectedQueue = new MongodbDestination('aName');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix.afooqueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new MongodbDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aFooQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
-    {
-        $expectedQueue = new MongodbDestination('aName');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aBarQueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new MongodbDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aBarQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldConvertTransportMessageToClientMessage()
-    {
-        $transportMessage = new MongodbMessage();
-        $transportMessage->setBody('body');
-        $transportMessage->setHeaders(['hkey' => 'hval']);
-        $transportMessage->setProperties(['key' => 'val']);
-        $transportMessage->setHeader('content_type', 'ContentType');
-        $transportMessage->setMessageId('MessageId');
-        $transportMessage->setTimestamp(1000);
-        $transportMessage->setPriority(2);
-        $transportMessage->setDeliveryDelay(12345);
-
-        $driver = new MongodbDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $clientMessage = $driver->createClientMessage($transportMessage);
-
-        $this->assertInstanceOf(Message::class, $clientMessage);
-        $this->assertSame('body', $clientMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'content_type' => 'ContentType',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-        ], $clientMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-        ], $clientMessage->getProperties());
-        $this->assertSame('MessageId', $clientMessage->getMessageId());
-        $this->assertSame('ContentType', $clientMessage->getContentType());
-        $this->assertSame(1000, $clientMessage->getTimestamp());
-        $this->assertSame(12345, $clientMessage->getDelay());
-
-        $this->assertNull($clientMessage->getExpire());
-        $this->assertSame(MessagePriority::NORMAL, $clientMessage->getPriority());
-    }
-
-    public function testShouldConvertClientMessageToTransportMessage()
-    {
-        $clientMessage = new Message();
-        $clientMessage->setBody('body');
-        $clientMessage->setHeaders(['hkey' => 'hval']);
-        $clientMessage->setProperties(['key' => 'val']);
-        $clientMessage->setContentType('ContentType');
-        $clientMessage->setExpire(123);
-        $clientMessage->setPriority(MessagePriority::VERY_HIGH);
-        $clientMessage->setMessageId('MessageId');
-        $clientMessage->setTimestamp(1000);
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn(new MongodbMessage())
-        ;
-
-        $driver = new MongodbDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $transportMessage = $driver->createTransportMessage($clientMessage);
-
-        $this->assertInstanceOf(MongodbMessage::class, $transportMessage);
-        $this->assertSame('body', $transportMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'content_type' => 'ContentType',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-            'reply_to' => null,
-            'correlation_id' => null,
-        ], $transportMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-        ], $transportMessage->getProperties());
-        $this->assertSame('MessageId', $transportMessage->getMessageId());
-        $this->assertSame(1000, $transportMessage->getTimestamp());
-    }
-
-    public function testShouldSendMessageToRouter()
-    {
-        $topic = new MongodbDestination('queue-name');
-        $transportMessage = new MongodbMessage();
-
-        $producer = $this->createPsrProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix.default')
-            ->willReturn($topic)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new MongodbDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-
-        $driver->sendToRouter($message);
-    }
-
-    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
-    {
-        $driver = new MongodbDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Topic name parameter is required but is not set');
-
-        $driver->sendToRouter(new Message());
-    }
-
-    public function testShouldSendMessageToProcessor()
-    {
-        $queue = new MongodbDestination('queue-name');
-        $transportMessage = new MongodbMessage();
-
-        $producer = $this->createPsrProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->willReturn($queue)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new MongodbDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
-
-        $driver->sendToProcessor($message);
-    }
-
-    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
-    {
-        $driver = new MongodbDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Processor name parameter is required but is not set');
-
-        $driver->sendToProcessor(new Message());
-    }
-
-    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
-    {
-        $driver = new MongodbDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Queue name parameter is required but is not set');
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-
-        $driver->sendToProcessor($message);
+        $this->assertClassExtends(GenericDriver::class, MongodbDriver::class);
     }
 
     public function testShouldSetupBroker()
     {
-        $context = $this->createPsrContextMock();
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
             ->method('createCollection')
@@ -313,46 +51,54 @@ public function testShouldSetupBroker()
         $driver = new MongodbDriver(
             $context,
             $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
+            new RouteCollection([])
         );
 
         $driver->setupBroker();
     }
 
+    protected function createDriver(...$args): DriverInterface
+    {
+        return new MongodbDriver(...$args);
+    }
+
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|MongodbContext
+     * @return MongodbContext
      */
-    private function createPsrContextMock()
+    protected function createContextMock(): PsrContext
     {
         return $this->createMock(MongodbContext::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProducer
+     * @return MongodbProducer
      */
-    private function createPsrProducerMock()
+    protected function createProducerMock(): PsrProducer
     {
-        return $this->createMock(PsrProducer::class);
+        return $this->createMock(MongodbProducer::class);
     }
 
     /**
-     * @return QueueMetaRegistry
+     * @return MongodbDestination
      */
-    private function createDummyQueueMetaRegistry()
+    protected function createQueue(string $name): PsrQueue
     {
-        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
-        $registry->add('default');
-        $registry->add('aFooQueue');
-        $registry->add('aBarQueue', 'aBarQueue');
+        return new MongodbDestination(new \SplFileInfo($name));
+    }
 
-        return $registry;
+    /**
+     * @return MongodbDestination
+     */
+    protected function createTopic(string $name): PsrTopic
+    {
+        return new MongodbDestination(new \SplFileInfo($name));
     }
 
     /**
-     * @return Config
+     * @return MongodbMessage
      */
-    private function createDummyConfig()
+    protected function createMessage(): PsrMessage
     {
-        return Config::create('aPrefix');
+        return new MongodbMessage();
     }
 }

From e3a0f8e7414b0850b64c17ac2e53ee4f676a0b9c Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 18 Sep 2018 11:11:20 +0300
Subject: [PATCH 123/337] [client] Remove NullDriver. Use generic instead.

---
 Client/Driver/NullDriver.php                  | 141 ---------
 Client/Resources.php                          |   4 +-
 Tests/Client/Driver/NullDriverTest.php        | 277 ------------------
 Tests/Client/DriverFactoryTest.php            |   4 +-
 .../Mock/SetupBrokerExtensionCommand.php      |   8 +-
 5 files changed, 8 insertions(+), 426 deletions(-)
 delete mode 100644 Client/Driver/NullDriver.php
 delete mode 100644 Tests/Client/Driver/NullDriverTest.php

diff --git a/Client/Driver/NullDriver.php b/Client/Driver/NullDriver.php
deleted file mode 100644
index 41a9366..0000000
--- a/Client/Driver/NullDriver.php
+++ /dev/null
@@ -1,141 +0,0 @@
-<?php
-
-namespace Enqueue\Client\Driver;
-
-use Enqueue\Client\Config;
-use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\Meta\QueueMetaRegistry;
-use Enqueue\Null\NullContext;
-use Enqueue\Null\NullMessage;
-use Enqueue\Null\NullQueue;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrQueue;
-use Psr\Log\LoggerInterface;
-use Psr\Log\NullLogger;
-
-class NullDriver implements DriverInterface
-{
-    /**
-     * @var NullContext
-     */
-    protected $context;
-
-    /**
-     * @var Config
-     */
-    protected $config;
-
-    /**
-     * @var QueueMetaRegistry
-     */
-    private $queueMetaRegistry;
-
-    public function __construct(NullContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
-    {
-        $this->context = $context;
-        $this->config = $config;
-        $this->queueMetaRegistry = $queueMetaRegistry;
-    }
-
-    /**
-     * @return NullMessage
-     */
-    public function createTransportMessage(Message $message): PsrMessage
-    {
-        $headers = $message->getHeaders();
-        $headers['content_type'] = $message->getContentType();
-        $headers['expiration'] = $message->getExpire();
-        $headers['delay'] = $message->getDelay();
-        $headers['priority'] = $message->getPriority();
-
-        $transportMessage = $this->context->createMessage();
-        $transportMessage->setBody($message->getBody());
-        $transportMessage->setHeaders($headers);
-        $transportMessage->setProperties($message->getProperties());
-        $transportMessage->setTimestamp($message->getTimestamp());
-        $transportMessage->setMessageId($message->getMessageId());
-        $transportMessage->setReplyTo($message->getReplyTo());
-        $transportMessage->setCorrelationId($message->getCorrelationId());
-
-        return $transportMessage;
-    }
-
-    /**
-     * @param NullMessage $message
-     */
-    public function createClientMessage(PsrMessage $message): Message
-    {
-        $clientMessage = new Message();
-        $clientMessage->setBody($message->getBody());
-        $clientMessage->setHeaders($message->getHeaders());
-        $clientMessage->setProperties($message->getProperties());
-        $clientMessage->setTimestamp($message->getTimestamp());
-        $clientMessage->setMessageId($message->getMessageId());
-        $clientMessage->setReplyTo($message->getReplyTo());
-        $clientMessage->setCorrelationId($message->getCorrelationId());
-
-        if ($contentType = $message->getHeader('content_type')) {
-            $clientMessage->setContentType($contentType);
-        }
-
-        if ($expiration = $message->getHeader('expiration')) {
-            $clientMessage->setExpire($expiration);
-        }
-
-        if ($delay = $message->getHeader('delay')) {
-            $clientMessage->setDelay($delay);
-        }
-
-        if ($priority = $message->getHeader('priority')) {
-            $clientMessage->setPriority($priority);
-        }
-
-        return $clientMessage;
-    }
-
-    /**
-     * @return NullQueue
-     */
-    public function createQueue(string $queueName): PsrQueue
-    {
-        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
-
-        return $this->context->createQueue($transportName);
-    }
-
-    public function getConfig(): Config
-    {
-        return $this->config;
-    }
-
-    public function sendToRouter(Message $message): void
-    {
-        $transportMessage = $this->createTransportMessage($message);
-        $topic = $this->context->createTopic(
-            $this->config->createTransportRouterTopicName(
-                $this->config->getRouterTopicName()
-            )
-        );
-
-        $this->context->createProducer()->send($topic, $transportMessage);
-    }
-
-    public function sendToProcessor(Message $message): void
-    {
-        $transportMessage = $this->createTransportMessage($message);
-        $queue = $this->context->createQueue(
-            $this->config->createTransportQueueName(
-                $this->config->getRouterQueueName()
-            )
-        );
-
-        $this->context->createProducer()->send($queue, $transportMessage);
-    }
-
-    public function setupBroker(LoggerInterface $logger = null): void
-    {
-        $logger ?: new NullLogger();
-        $logger->debug('[NullDriver] setup broker');
-    }
-}
diff --git a/Client/Resources.php b/Client/Resources.php
index 87da9dd..bef313b 100644
--- a/Client/Resources.php
+++ b/Client/Resources.php
@@ -5,9 +5,9 @@
 use Enqueue\Client\Driver\AmqpDriver;
 use Enqueue\Client\Driver\DbalDriver;
 use Enqueue\Client\Driver\FsDriver;
+use Enqueue\Client\Driver\GenericDriver;
 use Enqueue\Client\Driver\GpsDriver;
 use Enqueue\Client\Driver\MongodbDriver;
-use Enqueue\Client\Driver\NullDriver;
 use Enqueue\Client\Driver\RabbitMqDriver;
 use Enqueue\Client\Driver\RabbitMqStompDriver;
 use Enqueue\Client\Driver\RdKafkaDriver;
@@ -65,7 +65,7 @@ public static function getKnownDrivers(): array
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/fs'],
             ];
-            $map[NullDriver::class] = [
+            $map[GenericDriver::class] = [
                 'schemes' => ['null'],
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/null'],
diff --git a/Tests/Client/Driver/NullDriverTest.php b/Tests/Client/Driver/NullDriverTest.php
deleted file mode 100644
index 233e3b5..0000000
--- a/Tests/Client/Driver/NullDriverTest.php
+++ /dev/null
@@ -1,277 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Client\Driver;
-
-use Enqueue\Client\Config;
-use Enqueue\Client\Driver\NullDriver;
-use Enqueue\Client\Message;
-use Enqueue\Client\MessagePriority;
-use Enqueue\Client\Meta\QueueMetaRegistry;
-use Enqueue\Null\NullContext;
-use Enqueue\Null\NullMessage;
-use Enqueue\Null\NullProducer;
-use Enqueue\Null\NullQueue;
-use Enqueue\Null\NullTopic;
-use PHPUnit\Framework\TestCase;
-
-class NullDriverTest extends TestCase
-{
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new NullDriver(new NullContext(), Config::create(), $this->createDummyQueueMetaRegistry());
-    }
-
-    public function testShouldCreateAndReturnQueueInstance()
-    {
-        $expectedQueue = new NullQueue('aName');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix.afooqueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new NullDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aFooQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
-    {
-        $expectedQueue = new NullQueue('aName');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aBarQueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new NullDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aBarQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldSendMessageToRouter()
-    {
-        $config = Config::create();
-        $topic = new NullTopic('topic');
-
-        $transportMessage = new NullMessage();
-
-        $producer = $this->createMessageProducer();
-        $producer
-            ->expects(self::once())
-            ->method('send')
-            ->with(self::identicalTo($topic), self::identicalTo($transportMessage))
-        ;
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createTopic')
-            ->willReturn($topic)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-
-        $driver = new NullDriver($context, $config, $this->createDummyQueueMetaRegistry());
-
-        $driver->sendToRouter(new Message());
-    }
-
-    public function testShouldSendMessageToProcessor()
-    {
-        $config = Config::create();
-        $queue = new NullQueue('');
-
-        $transportMessage = new NullMessage();
-
-        $producer = $this->createMessageProducer();
-        $producer
-            ->expects(self::once())
-            ->method('send')
-            ->with(self::identicalTo($queue), self::identicalTo($transportMessage))
-        ;
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->willReturn($queue)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-
-        $driver = new NullDriver($context, $config, $this->createDummyQueueMetaRegistry());
-
-        $driver->sendToProcessor(new Message());
-    }
-
-    public function testShouldConvertClientMessageToTransportMessage()
-    {
-        $config = Config::create();
-
-        $clientMessage = new Message();
-        $clientMessage->setBody('theBody');
-        $clientMessage->setContentType('theContentType');
-        $clientMessage->setMessageId('theMessageId');
-        $clientMessage->setTimestamp(12345);
-        $clientMessage->setDelay(123);
-        $clientMessage->setExpire(345);
-        $clientMessage->setPriority(MessagePriority::LOW);
-        $clientMessage->setHeaders(['theHeaderFoo' => 'theFoo']);
-        $clientMessage->setProperties(['thePropertyBar' => 'theBar']);
-        $clientMessage->setReplyTo('theReplyTo');
-        $clientMessage->setCorrelationId('theCorrelationId');
-
-        $transportMessage = new NullMessage();
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new NullDriver($context, $config, $this->createDummyQueueMetaRegistry());
-
-        $transportMessage = $driver->createTransportMessage($clientMessage);
-
-        self::assertSame('theBody', $transportMessage->getBody());
-        self::assertSame([
-            'theHeaderFoo' => 'theFoo',
-            'content_type' => 'theContentType',
-            'expiration' => 345,
-            'delay' => 123,
-            'priority' => MessagePriority::LOW,
-            'timestamp' => 12345,
-            'message_id' => 'theMessageId',
-            'reply_to' => 'theReplyTo',
-            'correlation_id' => 'theCorrelationId',
-        ], $transportMessage->getHeaders());
-        self::assertSame([
-            'thePropertyBar' => 'theBar',
-        ], $transportMessage->getProperties());
-
-        $this->assertSame('theMessageId', $transportMessage->getMessageId());
-        $this->assertSame(12345, $transportMessage->getTimestamp());
-        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
-    }
-
-    public function testShouldConvertTransportMessageToClientMessage()
-    {
-        $config = Config::create();
-
-        $transportMessage = new NullMessage();
-        $transportMessage->setBody('theBody');
-        $transportMessage->setHeaders(['theHeaderFoo' => 'theFoo']);
-        $transportMessage->setTimestamp(12345);
-        $transportMessage->setMessageId('theMessageId');
-        $transportMessage->setHeader('priority', MessagePriority::LOW);
-        $transportMessage->setHeader('content_type', 'theContentType');
-        $transportMessage->setHeader('delay', 123);
-        $transportMessage->setHeader('expiration', 345);
-        $transportMessage->setProperties(['thePropertyBar' => 'theBar']);
-        $transportMessage->setReplyTo('theReplyTo');
-        $transportMessage->setCorrelationId('theCorrelationId');
-
-        $driver = new NullDriver($this->createPsrContextMock(), $config, $this->createDummyQueueMetaRegistry());
-
-        $clientMessage = $driver->createClientMessage($transportMessage);
-
-        self::assertSame('theBody', $clientMessage->getBody());
-        self::assertSame(MessagePriority::LOW, $clientMessage->getPriority());
-        self::assertSame('theContentType', $clientMessage->getContentType());
-        self::assertSame(123, $clientMessage->getDelay());
-        self::assertSame(345, $clientMessage->getExpire());
-        self::assertEquals([
-            'theHeaderFoo' => 'theFoo',
-            'content_type' => 'theContentType',
-            'expiration' => 345,
-            'delay' => 123,
-            'priority' => MessagePriority::LOW,
-            'timestamp' => 12345,
-            'message_id' => 'theMessageId',
-            'reply_to' => 'theReplyTo',
-            'correlation_id' => 'theCorrelationId',
-        ], $clientMessage->getHeaders());
-        self::assertSame([
-            'thePropertyBar' => 'theBar',
-        ], $clientMessage->getProperties());
-
-        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
-    }
-
-    public function testShouldReturnConfigInstance()
-    {
-        $config = Config::create();
-
-        $driver = new NullDriver($this->createPsrContextMock(), $config, $this->createDummyQueueMetaRegistry());
-        $result = $driver->getConfig();
-
-        self::assertSame($config, $result);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|NullContext
-     */
-    private function createPsrContextMock()
-    {
-        return $this->createMock(NullContext::class);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|NullProducer
-     */
-    private function createMessageProducer()
-    {
-        return $this->createMock(NullProducer::class);
-    }
-
-    /**
-     * @return QueueMetaRegistry
-     */
-    private function createDummyQueueMetaRegistry()
-    {
-        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
-        $registry->add('default');
-        $registry->add('aFooQueue');
-        $registry->add('aBarQueue', 'aBarQueue');
-
-        return $registry;
-    }
-
-    /**
-     * @return Config
-     */
-    private function createDummyConfig()
-    {
-        return Config::create('aPrefix');
-    }
-}
diff --git a/Tests/Client/DriverFactoryTest.php b/Tests/Client/DriverFactoryTest.php
index 6d3e6ce..e1b9679 100644
--- a/Tests/Client/DriverFactoryTest.php
+++ b/Tests/Client/DriverFactoryTest.php
@@ -6,9 +6,9 @@
 use Enqueue\Client\Driver\AmqpDriver;
 use Enqueue\Client\Driver\DbalDriver;
 use Enqueue\Client\Driver\FsDriver;
+use Enqueue\Client\Driver\GenericDriver;
 use Enqueue\Client\Driver\GpsDriver;
 use Enqueue\Client\Driver\MongodbDriver;
-use Enqueue\Client\Driver\NullDriver;
 use Enqueue\Client\Driver\RabbitMqDriver;
 use Enqueue\Client\Driver\RabbitMqStompDriver;
 use Enqueue\Client\Driver\RdKafkaDriver;
@@ -125,7 +125,7 @@ public function testReturnsExpectedFactories(
 
     public static function provideDSN()
     {
-        yield ['null:', NullConnectionFactory::class, NullContext::class, [], NullDriver::class];
+        yield ['null:', NullConnectionFactory::class, NullContext::class, [], GenericDriver::class];
 
         yield ['amqp:', AmqpConnectionFactory::class, AmqpContext::class, [], AmqpDriver::class];
 
diff --git a/Tests/Symfony/Client/Mock/SetupBrokerExtensionCommand.php b/Tests/Symfony/Client/Mock/SetupBrokerExtensionCommand.php
index c4b20cf..aa0c812 100644
--- a/Tests/Symfony/Client/Mock/SetupBrokerExtensionCommand.php
+++ b/Tests/Symfony/Client/Mock/SetupBrokerExtensionCommand.php
@@ -3,8 +3,8 @@
 namespace Enqueue\Tests\Symfony\Client\Mock;
 
 use Enqueue\Client\Config;
-use Enqueue\Client\Driver\NullDriver;
-use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Client\Driver\GenericDriver;
+use Enqueue\Client\RouteCollection;
 use Enqueue\Null\NullContext;
 use Enqueue\Symfony\Client\SetupBrokerExtensionCommandTrait;
 use Symfony\Component\Console\Command\Command;
@@ -31,10 +31,10 @@ protected function configure()
 
     protected function execute(InputInterface $input, OutputInterface $output)
     {
-        $this->extension = $this->getSetupBrokerExtension($input, new NullDriver(
+        $this->extension = $this->getSetupBrokerExtension($input, new GenericDriver(
             new NullContext(),
             Config::create(),
-            new QueueMetaRegistry(Config::create(), [])
+            new RouteCollection([])
         ));
     }
 }

From 979311f00e2fcdc2f4abe7878518cb198ff08409 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 18 Sep 2018 11:17:12 +0300
Subject: [PATCH 124/337] [client] Extend StompDriver from GenericDriver.

---
 Client/Driver/StompDriver.php           | 133 ++-------
 Tests/Client/Driver/StompDriverTest.php | 340 ++++++------------------
 2 files changed, 104 insertions(+), 369 deletions(-)

diff --git a/Client/Driver/StompDriver.php b/Client/Driver/StompDriver.php
index d360847..aedd95b 100644
--- a/Client/Driver/StompDriver.php
+++ b/Client/Driver/StompDriver.php
@@ -2,68 +2,24 @@
 
 namespace Enqueue\Client\Driver;
 
-use Enqueue\Client\Config;
-use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Message;
-use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\Stomp\StompContext;
 use Enqueue\Stomp\StompDestination;
 use Enqueue\Stomp\StompMessage;
 use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrQueue;
+use Interop\Queue\PsrTopic;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
-class StompDriver implements DriverInterface
+/**
+ * @method StompContext getContext
+ */
+class StompDriver extends GenericDriver
 {
-    /**
-     * @var StompContext
-     */
-    private $context;
-
-    /**
-     * @var Config
-     */
-    private $config;
-
-    /**
-     * @var QueueMetaRegistry
-     */
-    private $queueMetaRegistry;
-
-    public function __construct(StompContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
+    public function __construct(StompContext $context, ...$args)
     {
-        $this->context = $context;
-        $this->config = $config;
-        $this->queueMetaRegistry = $queueMetaRegistry;
-    }
-
-    public function sendToRouter(Message $message): void
-    {
-        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
-            throw new \LogicException('Topic name parameter is required but is not set');
-        }
-
-        $topic = $this->createRouterTopic();
-        $transportMessage = $this->createTransportMessage($message);
-
-        $this->context->createProducer()->send($topic, $transportMessage);
-    }
-
-    public function sendToProcessor(Message $message): void
-    {
-        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
-            throw new \LogicException('Processor name parameter is required but is not set');
-        }
-
-        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
-            throw new \LogicException('Queue name parameter is required but is not set');
-        }
-
-        $transportMessage = $this->createTransportMessage($message);
-        $destination = $this->createQueue($queueName);
-
-        $this->context->createProducer()->send($destination, $transportMessage);
+        parent::__construct($context, ...$args);
     }
 
     public function setupBroker(LoggerInterface $logger = null): void
@@ -77,72 +33,20 @@ public function setupBroker(LoggerInterface $logger = null): void
      */
     public function createTransportMessage(Message $message): PsrMessage
     {
-        $headers = $message->getHeaders();
-        $headers['content-type'] = $message->getContentType();
-
-        $transportMessage = $this->context->createMessage();
-        $transportMessage->setHeaders($headers);
+        /** @var StompMessage $transportMessage */
+        $transportMessage = parent::createTransportMessage($message);
         $transportMessage->setPersistent(true);
-        $transportMessage->setBody($message->getBody());
-        $transportMessage->setProperties($message->getProperties());
-
-        if ($message->getMessageId()) {
-            $transportMessage->setMessageId($message->getMessageId());
-        }
-
-        if ($message->getTimestamp()) {
-            $transportMessage->setTimestamp($message->getTimestamp());
-        }
-
-        if ($message->getReplyTo()) {
-            $transportMessage->setReplyTo($message->getReplyTo());
-        }
-
-        if ($message->getCorrelationId()) {
-            $transportMessage->setCorrelationId($message->getCorrelationId());
-        }
 
         return $transportMessage;
     }
 
-    /**
-     * @param StompMessage $message
-     */
-    public function createClientMessage(PsrMessage $message): Message
-    {
-        $clientMessage = new Message();
-
-        $headers = $message->getHeaders();
-        unset(
-            $headers['content-type'],
-            $headers['message_id'],
-            $headers['timestamp'],
-            $headers['reply-to'],
-            $headers['correlation_id']
-        );
-
-        $clientMessage->setHeaders($headers);
-        $clientMessage->setBody($message->getBody());
-        $clientMessage->setProperties($message->getProperties());
-
-        $clientMessage->setContentType($message->getHeader('content-type'));
-
-        $clientMessage->setMessageId($message->getMessageId());
-        $clientMessage->setTimestamp($message->getTimestamp());
-        $clientMessage->setReplyTo($message->getReplyTo());
-        $clientMessage->setCorrelationId($message->getCorrelationId());
-
-        return $clientMessage;
-    }
-
     /**
      * @return StompDestination
      */
     public function createQueue(string $queueName): PsrQueue
     {
-        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
-
-        $queue = $this->context->createQueue($transportName);
+        /** @var StompDestination $queue */
+        $queue = parent::createQueue($queueName);
         $queue->setDurable(true);
         $queue->setAutoDelete(false);
         $queue->setExclusive(false);
@@ -150,16 +54,13 @@ public function createQueue(string $queueName): PsrQueue
         return $queue;
     }
 
-    public function getConfig(): Config
-    {
-        return $this->config;
-    }
-
-    private function createRouterTopic(): StompDestination
+    /**
+     * @return StompDestination
+     */
+    protected function createRouterTopic(): PsrTopic
     {
-        $topic = $this->context->createTopic(
-            $this->config->createTransportRouterTopicName($this->config->getRouterTopicName())
-        );
+        /** @var StompDestination $topic */
+        $topic = parent::createRouterTopic();
         $topic->setDurable(true);
         $topic->setAutoDelete(false);
 
diff --git a/Tests/Client/Driver/StompDriverTest.php b/Tests/Client/Driver/StompDriverTest.php
index a8e9dab..556d593 100644
--- a/Tests/Client/Driver/StompDriverTest.php
+++ b/Tests/Client/Driver/StompDriverTest.php
@@ -2,341 +2,175 @@
 
 namespace Enqueue\Tests\Client\Driver;
 
-use Enqueue\Client\Config;
+use Enqueue\Client\Driver\GenericDriver;
 use Enqueue\Client\Driver\StompDriver;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Message;
-use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Client\MessagePriority;
+use Enqueue\Client\RouteCollection;
 use Enqueue\Stomp\StompContext;
 use Enqueue\Stomp\StompDestination;
 use Enqueue\Stomp\StompMessage;
 use Enqueue\Stomp\StompProducer;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrContext;
+use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrProducer;
+use Interop\Queue\PsrQueue;
+use Interop\Queue\PsrTopic;
+use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 
-class StompDriverTest extends \PHPUnit\Framework\TestCase
+class StompDriverTest extends TestCase
 {
     use ClassExtensionTrait;
+    use GenericDriverTestsTrait;
 
     public function testShouldImplementsDriverInterface()
     {
         $this->assertClassImplements(DriverInterface::class, StompDriver::class);
     }
 
-    public function testCouldBeConstructedWithRequiredArguments()
+    public function testShouldBeSubClassOfGenericDriver()
     {
-        new StompDriver($this->createPsrContextMock(), $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
+        $this->assertClassExtends(GenericDriver::class, StompDriver::class);
     }
 
-    public function testShouldReturnConfigObject()
-    {
-        $config = $this->createDummyConfig();
-
-        $driver = new StompDriver($this->createPsrContextMock(), $config, $this->createDummyQueueMetaRegistry());
-
-        $this->assertSame($config, $driver->getConfig());
-    }
-
-    public function testShouldCreateAndReturnQueueInstance()
+    public function testSetupBrokerShouldOnlyLogMessageThatStompDoesNotSupportBrokerSetup()
     {
-        $expectedQueue = new StompDestination();
+        $driver = new StompDriver(
+            $this->createContextMock(),
+            $this->createDummyConfig(),
+            new RouteCollection([])
+        );
 
-        $context = $this->createPsrContextMock();
-        $context
+        $logger = $this->createLoggerMock();
+        $logger
             ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix.afooqueue')
-            ->willReturn($expectedQueue)
+            ->method('debug')
+            ->with('[StompDriver] Stomp protocol does not support broker configuration')
         ;
 
-        $driver = new StompDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aFooQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-        $this->assertTrue($queue->isDurable());
-        $this->assertFalse($queue->isAutoDelete());
-        $this->assertFalse($queue->isExclusive());
-        $this->assertSame([
-            'durable' => true,
-            'auto-delete' => false,
-            'exclusive' => false,
-        ], $queue->getHeaders());
+        $driver->setupBroker($logger);
     }
 
-    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
+    public function testShouldCreateDurableQueue()
     {
-        $expectedQueue = new StompDestination();
-
-        $context = $this->createPsrContextMock();
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
             ->method('createQueue')
-            ->with('aBarQueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new StompDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aBarQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldConvertTransportMessageToClientMessage()
-    {
-        $transportMessage = new StompMessage();
-        $transportMessage->setBody('body');
-        $transportMessage->setHeaders(['hkey' => 'hval']);
-        $transportMessage->setProperties(['key' => 'val']);
-        $transportMessage->setHeader('content-type', 'ContentType');
-        $transportMessage->setMessageId('MessageId');
-        $transportMessage->setTimestamp(1000);
-        $transportMessage->setReplyTo('theReplyTo');
-        $transportMessage->setCorrelationId('theCorrelationId');
-
-        $driver = new StompDriver($this->createPsrContextMock(), $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $clientMessage = $driver->createClientMessage($transportMessage);
-
-        $this->assertInstanceOf(Message::class, $clientMessage);
-        $this->assertSame('body', $clientMessage->getBody());
-        $this->assertSame(['hkey' => 'hval'], $clientMessage->getHeaders());
-        $this->assertSame(['key' => 'val'], $clientMessage->getProperties());
-        $this->assertSame('MessageId', $clientMessage->getMessageId());
-        $this->assertSame('ContentType', $clientMessage->getContentType());
-        $this->assertSame(1000, $clientMessage->getTimestamp());
-        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
-    }
-
-    public function testShouldConvertClientMessageToTransportMessage()
-    {
-        $clientMessage = new Message();
-        $clientMessage->setBody('body');
-        $clientMessage->setHeaders(['hkey' => 'hval']);
-        $clientMessage->setProperties(['key' => 'val']);
-        $clientMessage->setContentType('ContentType');
-        $clientMessage->setMessageId('MessageId');
-        $clientMessage->setTimestamp(1000);
-        $clientMessage->setReplyTo('theReplyTo');
-        $clientMessage->setCorrelationId('theCorrelationId');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn(new StompMessage())
-        ;
-
-        $driver = new StompDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $transportMessage = $driver->createTransportMessage($clientMessage);
-
-        $this->assertInstanceOf(StompMessage::class, $transportMessage);
-        $this->assertSame('body', $transportMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'content-type' => 'ContentType',
-            'persistent' => true,
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-            'reply-to' => 'theReplyTo',
-            'correlation_id' => 'theCorrelationId',
-        ], $transportMessage->getHeaders());
-        $this->assertSame(['key' => 'val'], $transportMessage->getProperties());
-        $this->assertSame('MessageId', $transportMessage->getMessageId());
-        $this->assertSame(1000, $transportMessage->getTimestamp());
-        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
-    }
-
-    public function testShouldSendMessageToRouter()
-    {
-        $topic = new StompDestination();
-        $transportMessage = new StompMessage();
-
-        $producer = $this->createPsrProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createTopic')
-            ->willReturn($topic)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
+            ->willReturn($this->createQueue('aName'))
         ;
 
-        $driver = new StompDriver(
+        $driver = $this->createDriver(
             $context,
             $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
+            new RouteCollection([])
         );
 
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+        /** @var StompDestination $queue */
+        $queue = $driver->createQueue('aName');
 
-        $driver->sendToRouter($message);
-    }
-
-    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
-    {
-        $driver = new StompDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Topic name parameter is required but is not set');
-
-        $driver->sendToRouter(new Message());
+        $this->assertTrue($queue->isDurable());
+        $this->assertFalse($queue->isAutoDelete());
+        $this->assertFalse($queue->isExclusive());
     }
 
-    public function testShouldSendMessageToProcessor()
+    public function testShouldSetPersistedTrueOnCreateTransportMessage()
     {
-        $queue = new StompDestination();
-        $transportMessage = new StompMessage();
+        $clientMessage = new Message();
 
-        $producer = $this->createPsrProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->willReturn($queue)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
             ->method('createMessage')
-            ->willReturn($transportMessage)
+            ->willReturn($this->createMessage())
         ;
 
-        $driver = new StompDriver(
+        $driver = $this->createDriver(
             $context,
             $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
-
-        $driver->sendToProcessor($message);
-    }
-
-    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
-    {
-        $driver = new StompDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
+            new RouteCollection([])
         );
 
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Processor name parameter is required but is not set');
-
-        $driver->sendToProcessor(new Message());
-    }
-
-    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
-    {
-        $driver = new StompDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Queue name parameter is required but is not set');
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        /** @var StompMessage $transportMessage */
+        $transportMessage = $driver->createTransportMessage($clientMessage);
 
-        $driver->sendToProcessor($message);
+        $this->assertTrue($transportMessage->isPersistent());
     }
 
-    public function testSetupBrokerShouldOnlyLogMessageThatStompDoesNotSupportBrokerSetup()
+    protected function createDriver(...$args): DriverInterface
     {
-        $driver = new StompDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $logger = $this->createLoggerMock();
-        $logger
-            ->expects($this->once())
-            ->method('debug')
-            ->with('[StompDriver] Stomp protocol does not support broker configuration')
-        ;
-
-        $driver->setupBroker($logger);
+        return new StompDriver(...$args);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|StompContext
+     * @return StompContext
      */
-    private function createPsrContextMock()
+    protected function createContextMock(): PsrContext
     {
         return $this->createMock(StompContext::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|StompProducer
+     * @return StompProducer
      */
-    private function createPsrProducerMock()
+    protected function createProducerMock(): PsrProducer
     {
         return $this->createMock(StompProducer::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|LoggerInterface
+     * @return StompDestination
      */
-    private function createLoggerMock()
+    protected function createQueue(string $name): PsrQueue
     {
-        return $this->createMock(LoggerInterface::class);
+        return new StompDestination();
     }
 
     /**
-     * @return QueueMetaRegistry
+     * @return StompDestination
      */
-    private function createDummyQueueMetaRegistry()
+    protected function createTopic(string $name): PsrTopic
     {
-        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
-        $registry->add('default');
-        $registry->add('aFooQueue');
-        $registry->add('aBarQueue', 'aBarQueue');
-
-        return $registry;
+        return new StompDestination();
     }
 
     /**
-     * @return Config
+     * @return StompMessage
      */
-    private function createDummyConfig()
+    protected function createMessage(): PsrMessage
     {
-        return Config::create('aPrefix');
+        return new StompMessage();
+    }
+
+    protected function assertTransportMessage(PsrMessage $transportMessage): void
+    {
+        $this->assertSame('body', $transportMessage->getBody());
+        $this->assertEquals([
+            'hkey' => 'hval',
+            'message_id' => 'theMessageId',
+            'timestamp' => 1000,
+            'reply-to' => 'theReplyTo',
+            'persistent' => true,
+            'correlation_id' => 'theCorrelationId',
+        ], $transportMessage->getHeaders());
+        $this->assertEquals([
+            'pkey' => 'pval',
+            'X-Enqueue-Content-Type' => 'ContentType',
+            'X-Enqueue-Priority' => MessagePriority::HIGH,
+            'X-Enqueue-Expire' => 123,
+            'X-Enqueue-Delay' => 345,
+        ], $transportMessage->getProperties());
+        $this->assertSame('theMessageId', $transportMessage->getMessageId());
+        $this->assertSame(1000, $transportMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
+    }
+
+    protected function createLoggerMock(): LoggerInterface
+    {
+        return $this->createMock(LoggerInterface::class);
     }
 }

From 3963c9240fbcf0fb9bbcadd7698f7fb9f7342637 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 18 Sep 2018 12:18:39 +0300
Subject: [PATCH 125/337] [client] Extend RabbitMqStompDriver from
 GenericDriver.

---
 Client/Driver/RabbitMqStompDriver.php         | 170 ++---
 Tests/Client/Driver/DbalDriverTest.php        |   4 +-
 Tests/Client/Driver/MongodbDriverTest.php     |   4 +-
 .../Client/Driver/RabbitMqStompDriverTest.php | 634 ++++++++----------
 4 files changed, 323 insertions(+), 489 deletions(-)

diff --git a/Client/Driver/RabbitMqStompDriver.php b/Client/Driver/RabbitMqStompDriver.php
index a265f66..4a4d697 100644
--- a/Client/Driver/RabbitMqStompDriver.php
+++ b/Client/Driver/RabbitMqStompDriver.php
@@ -4,59 +4,30 @@
 
 use Enqueue\Client\Config;
 use Enqueue\Client\Message;
-use Enqueue\Client\MessagePriority;
-use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Client\RouteCollection;
 use Enqueue\Stomp\StompContext;
 use Enqueue\Stomp\StompDestination;
 use Enqueue\Stomp\StompMessage;
+use Enqueue\Stomp\StompProducer;
 use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrProducer;
 use Interop\Queue\PsrQueue;
+use Interop\Queue\PsrTopic;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
 class RabbitMqStompDriver extends StompDriver
 {
-    /**
-     * @var StompContext
-     */
-    private $context;
-
-    /**
-     * @var Config
-     */
-    private $config;
-
-    /**
-     * @var array
-     */
-    private $priorityMap;
-
     /**
      * @var StompManagementClient
      */
     private $management;
 
-    /**
-     * @var QueueMetaRegistry
-     */
-    private $queueMetaRegistry;
-
-    public function __construct(StompContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry, StompManagementClient $management)
+    public function __construct(StompContext $context, Config $config, RouteCollection $routeCollection, StompManagementClient $management)
     {
-        parent::__construct($context, $config, $queueMetaRegistry);
+        parent::__construct($context, $config, $routeCollection);
 
-        $this->context = $context;
-        $this->config = $config;
-        $this->queueMetaRegistry = $queueMetaRegistry;
         $this->management = $management;
-
-        $this->priorityMap = [
-            MessagePriority::VERY_LOW => 0,
-            MessagePriority::LOW => 1,
-            MessagePriority::NORMAL => 2,
-            MessagePriority::HIGH => 3,
-            MessagePriority::VERY_HIGH => 4,
-        ];
     }
 
     /**
@@ -71,15 +42,17 @@ public function createTransportMessage(Message $message): PsrMessage
         }
 
         if ($priority = $message->getPriority()) {
-            if (false == array_key_exists($priority, $this->priorityMap)) {
+            $priorityMap = $this->getPriorityMap();
+
+            if (false == array_key_exists($priority, $priorityMap)) {
                 throw new \LogicException(sprintf('Cant convert client priority to transport: "%s"', $priority));
             }
 
-            $transportMessage->setHeader('priority', $this->priorityMap[$priority]);
+            $transportMessage->setHeader('priority', $priorityMap[$priority]);
         }
 
         if ($message->getDelay()) {
-            if (false == $this->config->getTransportOption('delay_plugin_installed', false)) {
+            if (false == $this->getConfig()->getTransportOption('delay_plugin_installed', false)) {
                 throw new \LogicException('The message delaying is not supported. In order to use delay feature install RabbitMQ delay plugin.');
             }
 
@@ -89,68 +62,6 @@ public function createTransportMessage(Message $message): PsrMessage
         return $transportMessage;
     }
 
-    /**
-     * @param StompMessage $message
-     */
-    public function createClientMessage(PsrMessage $message): Message
-    {
-        $clientMessage = parent::createClientMessage($message);
-
-        $headers = $clientMessage->getHeaders();
-        unset(
-            $headers['x-delay'],
-            $headers['expiration'],
-            $headers['priority']
-        );
-        $clientMessage->setHeaders($headers);
-
-        if ($delay = $message->getHeader('x-delay')) {
-            if (false == is_numeric($delay)) {
-                throw new \LogicException(sprintf('x-delay header is not numeric. "%s"', $delay));
-            }
-
-            $clientMessage->setDelay((int) ((int) $delay) / 1000);
-        }
-
-        if ($expiration = $message->getHeader('expiration')) {
-            if (false == is_numeric($expiration)) {
-                throw new \LogicException(sprintf('expiration header is not numeric. "%s"', $expiration));
-            }
-
-            $clientMessage->setExpire((int) ((int) $expiration) / 1000);
-        }
-
-        if ($priority = $message->getHeader('priority')) {
-            if (false === $clientPriority = array_search($priority, $this->priorityMap, true)) {
-                throw new \LogicException(sprintf('Cant convert transport priority to client: "%s"', $priority));
-            }
-
-            $clientMessage->setPriority($clientPriority);
-        }
-
-        return $clientMessage;
-    }
-
-    public function sendToProcessor(Message $message): void
-    {
-        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
-            throw new \LogicException('Processor name parameter is required but is not set');
-        }
-
-        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
-            throw new \LogicException('Queue name parameter is required but is not set');
-        }
-
-        $transportMessage = $this->createTransportMessage($message);
-        $destination = $this->createQueue($queueName);
-
-        if ($message->getDelay()) {
-            $destination = $this->createDelayedTopic($destination);
-        }
-
-        $this->context->createProducer()->send($destination, $transportMessage);
-    }
-
     /**
      * @return StompDestination
      */
@@ -169,14 +80,14 @@ public function setupBroker(LoggerInterface $logger = null): void
             $logger->debug(sprintf('[RabbitMqStompDriver] '.$text, ...$args));
         };
 
-        if (false == $this->config->getTransportOption('management_plugin_installed', false)) {
+        if (false == $this->getConfig()->getTransportOption('management_plugin_installed', false)) {
             $log('Could not setup broker. The option `management_plugin_installed` is not enabled. Please enable that option and install rabbit management plugin');
 
             return;
         }
 
         // setup router
-        $routerExchange = $this->config->createTransportRouterTopicName($this->config->getRouterTopicName());
+        $routerExchange = $this->getConfig()->createTransportRouterTopicName($this->getConfig()->getRouterTopicName());
         $log('Declare router exchange: %s', $routerExchange);
         $this->management->declareExchange($routerExchange, [
             'type' => 'fanout',
@@ -184,7 +95,7 @@ public function setupBroker(LoggerInterface $logger = null): void
             'auto_delete' => false,
         ]);
 
-        $routerQueue = $this->config->createTransportQueueName($this->config->getRouterQueueName());
+        $routerQueue = $this->getConfig()->createTransportQueueName($this->getConfig()->getRouterQueueName());
         $log('Declare router queue: %s', $routerQueue);
         $this->management->declareQueue($routerQueue, [
             'auto_delete' => false,
@@ -198,11 +109,11 @@ public function setupBroker(LoggerInterface $logger = null): void
         $this->management->bind($routerExchange, $routerQueue, $routerQueue);
 
         // setup queues
-        foreach ($this->queueMetaRegistry->getQueuesMeta() as $meta) {
-            $queue = $this->config->createTransportQueueName($meta->getClientName());
+        foreach ($this->getRouteCollection()->all() as $route) {
+            $queue = $this->createRouteQueue($route);
 
-            $log('Declare processor queue: %s', $queue);
-            $this->management->declareQueue($queue, [
+            $log('Declare processor queue: %s', $queue->getStompName());
+            $this->management->declareQueue($queue->getStompName(), [
                 'auto_delete' => false,
                 'durable' => true,
                 'arguments' => [
@@ -212,10 +123,10 @@ public function setupBroker(LoggerInterface $logger = null): void
         }
 
         // setup delay exchanges
-        if ($this->config->getTransportOption('delay_plugin_installed', false)) {
-            foreach ($this->queueMetaRegistry->getQueuesMeta() as $meta) {
-                $queue = $this->config->createTransportQueueName($meta->getClientName());
-                $delayExchange = $queue.'.delayed';
+        if ($this->getConfig()->getTransportOption('delay_plugin_installed', false)) {
+            foreach ($this->getRouteCollection()->all() as $route) {
+                $queue = $this->createRouteQueue($route);
+                $delayExchange = $queue->getStompName().'.delayed';
 
                 $log('Declare delay exchange: %s', $delayExchange);
                 $this->management->declareExchange($delayExchange, [
@@ -227,18 +138,49 @@ public function setupBroker(LoggerInterface $logger = null): void
                     ],
                 ]);
 
-                $log('Bind processor queue to delay exchange: %s -> %s', $queue, $delayExchange);
-                $this->management->bind($delayExchange, $queue, $queue);
+                $log('Bind processor queue to delay exchange: %s -> %s', $queue->getStompName(), $delayExchange);
+                $this->management->bind($delayExchange, $queue->getStompName(), $queue->getStompName());
             }
         } else {
             $log('Delay exchange and bindings are not setup. if you\'d like to use delays please install delay rabbitmq plugin and set delay_plugin_installed option to true');
         }
     }
 
+    /**
+     * @param StompProducer    $producer
+     * @param StompDestination $topic
+     * @param StompMessage     $transportMessage
+     */
+    protected function doSendToRouter(PsrProducer $producer, PsrTopic $topic, PsrMessage $transportMessage): void
+    {
+        // We should not handle priority, expiration, and delay at this stage.
+        // The router will take care of it while re-sending the message to the final destinations.
+        $transportMessage->setHeader('expiration', null);
+        $transportMessage->setHeader('priority', null);
+        $transportMessage->setHeader('x-delay', null);
+
+        $producer->send($topic, $transportMessage);
+    }
+
+    /**
+     * @param StompProducer    $producer
+     * @param StompDestination $destination
+     * @param StompMessage     $transportMessage
+     */
+    protected function doSendToProcessor(PsrProducer $producer, PsrQueue $destination, PsrMessage $transportMessage): void
+    {
+        if ($delay = $transportMessage->getProperty('X-Enqueue-Delay')) {
+            $producer->setDeliveryDelay(null);
+            $destination = $this->createDelayedTopic($destination);
+        }
+
+        $producer->send($destination, $transportMessage);
+    }
+
     private function createDelayedTopic(StompDestination $queue): StompDestination
     {
         // in order to use delay feature make sure the rabbitmq_delayed_message_exchange plugin is installed.
-        $destination = $this->context->createTopic($queue->getStompName().'.delayed');
+        $destination = $this->getContext()->createTopic($queue->getStompName().'.delayed');
         $destination->setType(StompDestination::TYPE_EXCHANGE);
         $destination->setDurable(true);
         $destination->setAutoDelete(false);
diff --git a/Tests/Client/Driver/DbalDriverTest.php b/Tests/Client/Driver/DbalDriverTest.php
index ed1064c..c6fd10d 100644
--- a/Tests/Client/Driver/DbalDriverTest.php
+++ b/Tests/Client/Driver/DbalDriverTest.php
@@ -79,7 +79,7 @@ protected function createProducerMock(): PsrProducer
      */
     protected function createQueue(string $name): PsrQueue
     {
-        return new DbalDestination(new \SplFileInfo($name));
+        return new DbalDestination($name);
     }
 
     /**
@@ -87,7 +87,7 @@ protected function createQueue(string $name): PsrQueue
      */
     protected function createTopic(string $name): PsrTopic
     {
-        return new DbalDestination(new \SplFileInfo($name));
+        return new DbalDestination($name);
     }
 
     /**
diff --git a/Tests/Client/Driver/MongodbDriverTest.php b/Tests/Client/Driver/MongodbDriverTest.php
index 6c9b55a..da9c7e6 100644
--- a/Tests/Client/Driver/MongodbDriverTest.php
+++ b/Tests/Client/Driver/MongodbDriverTest.php
@@ -83,7 +83,7 @@ protected function createProducerMock(): PsrProducer
      */
     protected function createQueue(string $name): PsrQueue
     {
-        return new MongodbDestination(new \SplFileInfo($name));
+        return new MongodbDestination($name);
     }
 
     /**
@@ -91,7 +91,7 @@ protected function createQueue(string $name): PsrQueue
      */
     protected function createTopic(string $name): PsrTopic
     {
-        return new MongodbDestination(new \SplFileInfo($name));
+        return new MongodbDestination($name);
     }
 
     /**
diff --git a/Tests/Client/Driver/RabbitMqStompDriverTest.php b/Tests/Client/Driver/RabbitMqStompDriverTest.php
index b3d5a8b..a153e46 100644
--- a/Tests/Client/Driver/RabbitMqStompDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqStompDriverTest.php
@@ -3,57 +3,53 @@
 namespace Enqueue\Tests\Client\Driver;
 
 use Enqueue\Client\Config;
+use Enqueue\Client\Driver\GenericDriver;
 use Enqueue\Client\Driver\RabbitMqStompDriver;
+use Enqueue\Client\Driver\StompDriver;
 use Enqueue\Client\Driver\StompManagementClient;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Message;
 use Enqueue\Client\MessagePriority;
-use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
 use Enqueue\Stomp\StompContext;
 use Enqueue\Stomp\StompDestination;
 use Enqueue\Stomp\StompMessage;
 use Enqueue\Stomp\StompProducer;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrContext;
+use Interop\Queue\PsrMessage;
+use Interop\Queue\PsrProducer;
+use Interop\Queue\PsrQueue;
+use Interop\Queue\PsrTopic;
+use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 
-class RabbitMqStompDriverTest extends \PHPUnit\Framework\TestCase
+class RabbitMqStompDriverTest extends TestCase
 {
     use ClassExtensionTrait;
+    use GenericDriverTestsTrait;
 
     public function testShouldImplementsDriverInterface()
     {
         $this->assertClassImplements(DriverInterface::class, RabbitMqStompDriver::class);
     }
 
-    public function testCouldBeConstructedWithRequiredArguments()
+    public function testShouldBeSubClassOfGenericDriver()
     {
-        new RabbitMqStompDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry(),
-            $this->createManagementClientMock()
-        );
+        $this->assertClassExtends(GenericDriver::class, RabbitMqStompDriver::class);
     }
 
-    public function testShouldReturnConfigObject()
+    public function testShouldBeSubClassOfStompDriver()
     {
-        $config = $this->createDummyConfig();
-
-        $driver = new RabbitMqStompDriver(
-            $this->createPsrContextMock(),
-            $config,
-            $this->createDummyQueueMetaRegistry(),
-            $this->createManagementClientMock()
-        );
-
-        $this->assertSame($config, $driver->getConfig());
+        $this->assertClassExtends(StompDriver::class, RabbitMqStompDriver::class);
     }
 
-    public function testShouldCreateAndReturnQueueInstance()
+    public function testShouldCreateAndReturnStompQueueInstance()
     {
         $expectedQueue = new StompDestination();
 
-        $context = $this->createPsrContextMock();
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
             ->method('createQueue')
@@ -61,10 +57,10 @@ public function testShouldCreateAndReturnQueueInstance()
             ->willReturn($expectedQueue)
         ;
 
-        $driver = new RabbitMqStompDriver(
+        $driver = $this->createDriver(
             $context,
             $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry(),
+            new RouteCollection([]),
             $this->createManagementClientMock()
         );
 
@@ -84,140 +80,24 @@ public function testShouldCreateAndReturnQueueInstance()
         $this->assertSame($expectedHeaders, $queue->getHeaders());
     }
 
-    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
-    {
-        $expectedQueue = new StompDestination();
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aBarQueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new RabbitMqStompDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry(),
-            $this->createManagementClientMock()
-        );
-
-        $queue = $driver->createQueue('aBarQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldConvertTransportMessageToClientMessage()
-    {
-        $transportMessage = new StompMessage();
-        $transportMessage->setBody('body');
-        $transportMessage->setHeaders(['hkey' => 'hval']);
-        $transportMessage->setProperties(['key' => 'val']);
-        $transportMessage->setHeader('content-type', 'ContentType');
-        $transportMessage->setHeader('expiration', '12345000');
-        $transportMessage->setHeader('priority', 3);
-        $transportMessage->setHeader('x-delay', '5678000');
-        $transportMessage->setMessageId('MessageId');
-        $transportMessage->setTimestamp(1000);
-        $transportMessage->setReplyTo('theReplyTo');
-        $transportMessage->setCorrelationId('theCorrelationId');
-
-        $driver = new RabbitMqStompDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry(),
-            $this->createManagementClientMock()
-        );
-
-        $clientMessage = $driver->createClientMessage($transportMessage);
-
-        $this->assertInstanceOf(Message::class, $clientMessage);
-        $this->assertSame('body', $clientMessage->getBody());
-        $this->assertSame(['hkey' => 'hval'], $clientMessage->getHeaders());
-        $this->assertSame(['key' => 'val'], $clientMessage->getProperties());
-        $this->assertSame('MessageId', $clientMessage->getMessageId());
-        $this->assertSame(12345, $clientMessage->getExpire());
-        $this->assertSame(5678, $clientMessage->getDelay());
-        $this->assertSame('ContentType', $clientMessage->getContentType());
-        $this->assertSame(1000, $clientMessage->getTimestamp());
-        $this->assertSame(MessagePriority::HIGH, $clientMessage->getPriority());
-        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
-    }
-
-    public function testShouldThrowExceptionIfXDelayIsNotNumeric()
-    {
-        $transportMessage = new StompMessage();
-        $transportMessage->setHeader('x-delay', 'is-not-numeric');
-
-        $driver = new RabbitMqStompDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry(),
-            $this->createManagementClientMock()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('x-delay header is not numeric. "is-not-numeric"');
-
-        $driver->createClientMessage($transportMessage);
-    }
-
-    public function testShouldThrowExceptionIfExpirationIsNotNumeric()
-    {
-        $transportMessage = new StompMessage();
-        $transportMessage->setHeader('expiration', 'is-not-numeric');
-
-        $driver = new RabbitMqStompDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry(),
-            $this->createManagementClientMock()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('expiration header is not numeric. "is-not-numeric"');
-
-        $driver->createClientMessage($transportMessage);
-    }
-
-    public function testShouldThrowExceptionIfCantConvertTransportPriorityToClientPriority()
-    {
-        $transportMessage = new StompMessage();
-        $transportMessage->setHeader('priority', 'unknown');
-
-        $driver = new RabbitMqStompDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry(),
-            $this->createManagementClientMock()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Cant convert transport priority to client: "unknown"');
-
-        $driver->createClientMessage($transportMessage);
-    }
-
-    public function testShouldThrowExceptionIfCantConvertClientPriorityToTransportPriority()
+    public function testThrowIfClientPriorityInvalidOnCreateTransportMessage()
     {
         $clientMessage = new Message();
         $clientMessage->setPriority('unknown');
 
         $transportMessage = new StompMessage();
 
-        $context = $this->createPsrContextMock();
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
             ->method('createMessage')
             ->willReturn($transportMessage)
         ;
 
-        $driver = new RabbitMqStompDriver(
+        $driver = $this->createDriver(
             $context,
             $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry(),
+            new RouteCollection([]),
             $this->createManagementClientMock()
         );
 
@@ -227,74 +107,32 @@ public function testShouldThrowExceptionIfCantConvertClientPriorityToTransportPr
         $driver->createTransportMessage($clientMessage);
     }
 
-    public function testShouldConvertClientMessageToTransportMessage()
-    {
-        $clientMessage = new Message();
-        $clientMessage->setBody('body');
-        $clientMessage->setHeaders(['hkey' => 'hval']);
-        $clientMessage->setProperties(['key' => 'val']);
-        $clientMessage->setContentType('ContentType');
-        $clientMessage->setExpire(123);
-        $clientMessage->setPriority(MessagePriority::VERY_HIGH);
-        $clientMessage->setDelay(432);
-        $clientMessage->setMessageId('MessageId');
-        $clientMessage->setTimestamp(1000);
-        $clientMessage->setReplyTo('theReplyTo');
-        $clientMessage->setCorrelationId('theCorrelationId');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn(new StompMessage())
-        ;
-
-        $driver = new RabbitMqStompDriver(
-            $context,
-            new Config('', '', '', '', '', '', ['delay_plugin_installed' => true]),
-            $this->createDummyQueueMetaRegistry(),
-            $this->createManagementClientMock()
-        );
-
-        $transportMessage = $driver->createTransportMessage($clientMessage);
-
-        $this->assertInstanceOf(StompMessage::class, $transportMessage);
-        $this->assertSame('body', $transportMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'content-type' => 'ContentType',
-            'persistent' => true,
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-            'reply-to' => 'theReplyTo',
-            'correlation_id' => 'theCorrelationId',
-            'expiration' => '123000',
-            'priority' => 4,
-            'x-delay' => '432000',
-        ], $transportMessage->getHeaders());
-        $this->assertSame(['key' => 'val'], $transportMessage->getProperties());
-        $this->assertSame('MessageId', $transportMessage->getMessageId());
-        $this->assertSame(1000, $transportMessage->getTimestamp());
-        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
-    }
-
-    public function testShouldThrowExceptionIfDelayIsSetButDelayPluginInstalledOptionIsFalse()
+    public function testThrowIfDelayIsSetButDelayPluginInstalledOptionIsFalse()
     {
         $clientMessage = new Message();
         $clientMessage->setDelay(123);
 
-        $context = $this->createPsrContextMock();
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
             ->method('createMessage')
             ->willReturn(new StompMessage())
         ;
 
-        $driver = new RabbitMqStompDriver(
+        $config = Config::create(
+            'aPrefix',
+            '',
+            null,
+            null,
+            null,
+            null,
+            ['delay_plugin_installed' => false]
+        );
+
+        $driver = $this->createDriver(
             $context,
-            new Config('', '', '', '', '', '', ['delay_plugin_installed' => false]),
-            $this->createDummyQueueMetaRegistry(),
+            $config,
+            new RouteCollection([]),
             $this->createManagementClientMock()
         );
 
@@ -304,106 +142,46 @@ public function testShouldThrowExceptionIfDelayIsSetButDelayPluginInstalledOptio
         $driver->createTransportMessage($clientMessage);
     }
 
-    public function testShouldSendMessageToRouter()
+    public function testShouldSetXDelayHeaderIfDelayPluginInstalledOptionIsTrue()
     {
-        $topic = new StompDestination();
-        $transportMessage = new StompMessage();
+        $clientMessage = new Message();
+        $clientMessage->setDelay(123);
 
-        $producer = $this->createPsrProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createTopic')
-            ->willReturn($topic)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
             ->method('createMessage')
-            ->willReturn($transportMessage)
+            ->willReturn(new StompMessage())
         ;
 
-        $driver = new RabbitMqStompDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry(),
-            $this->createManagementClientMock()
+        $config = Config::create(
+            'aPrefix',
+            '',
+            null,
+            null,
+            null,
+            null,
+            ['delay_plugin_installed' => true]
         );
 
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-
-        $driver->sendToRouter($message);
-    }
-
-    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
-    {
-        $driver = new RabbitMqStompDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry(),
+        $driver = $this->createDriver(
+            $context,
+            $config,
+            new RouteCollection([]),
             $this->createManagementClientMock()
         );
 
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Topic name parameter is required but is not set');
+        $transportMessage = $driver->createTransportMessage($clientMessage);
 
-        $driver->sendToRouter(new Message());
+        $this->assertSame('123000', $transportMessage->getHeader('x-delay'));
     }
 
-    public function testShouldSendMessageToProcessor()
+    public function testShouldInitDeliveryDelayIfDelayPropertyOnSendToProcessor()
     {
-        $queue = new StompDestination();
-        $transportMessage = new StompMessage();
-
-        $producer = $this->createPsrProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix.afooqueue')
-            ->willReturn($queue)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new RabbitMqStompDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry(),
-            $this->createManagementClientMock()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
-
-        $driver->sendToProcessor($message);
+        $this->shouldSendMessageToDelayExchangeIfDelaySet();
     }
 
-    public function testShouldSendMessageToDelayExchangeIfDelaySet()
+    public function shouldSendMessageToDelayExchangeIfDelaySet()
     {
         $queue = new StompDestination();
         $queue->setStompName('queueName');
@@ -413,13 +191,24 @@ public function testShouldSendMessageToDelayExchangeIfDelaySet()
 
         $transportMessage = new StompMessage();
 
-        $producer = $this->createPsrProducerMock();
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->at(0))
+            ->method('setDeliveryDelay')
+            ->with(10000)
+        ;
+        $producer
+            ->expects($this->at(1))
+            ->method('setDeliveryDelay')
+            ->with(null)
+        ;
         $producer
             ->expects($this->once())
             ->method('send')
             ->with($this->identicalTo($delayTopic), $this->identicalTo($transportMessage))
         ;
-        $context = $this->createPsrContextMock();
+
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
             ->method('createQueue')
@@ -441,60 +230,49 @@ public function testShouldSendMessageToDelayExchangeIfDelaySet()
             ->willReturn($transportMessage)
         ;
 
-        $driver = new RabbitMqStompDriver(
+        $config = Config::create(
+            'aPrefix',
+            '',
+            null,
+            null,
+            null,
+            null,
+            ['delay_plugin_installed' => true]
+        );
+
+        $driver = $this->createDriver(
             $context,
-            new Config('', '', '', '', '', '', ['delay_plugin_installed' => true]),
-            $this->createDummyQueueMetaRegistry(),
+            $config,
+            new RouteCollection([
+                new Route('topic', Route::TOPIC, 'processor'),
+            ]),
             $this->createManagementClientMock()
         );
 
         $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
         $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
         $message->setDelay(10);
 
         $driver->sendToProcessor($message);
     }
 
-    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
-    {
-        $driver = new RabbitMqStompDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry(),
-            $this->createManagementClientMock()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Processor name parameter is required but is not set');
-
-        $driver->sendToProcessor(new Message());
-    }
-
-    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
+    public function testShouldNotSetupBrokerIfManagementPluginInstalledOptionIsNotEnabled()
     {
-        $driver = new RabbitMqStompDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry(),
-            $this->createManagementClientMock()
+        $config = Config::create(
+            'aPrefix',
+            '',
+            null,
+            null,
+            null,
+            null,
+            ['management_plugin_installed' => false]
         );
 
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Queue name parameter is required but is not set');
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-
-        $driver->sendToProcessor($message);
-    }
-
-    public function testShouldNotSetupBrokerIfManagementPluginInstalledOptionIsNotEnabled()
-    {
-        $driver = new RabbitMqStompDriver(
-            $this->createPsrContextMock(),
-            new Config('', '', '', '', '', '', ['management_plugin_installed' => false]),
-            $this->createDummyQueueMetaRegistry(),
+        $driver = $this->createDriver(
+            $this->createContextMock(),
+            $config,
+            new RouteCollection([]),
             $this->createManagementClientMock()
         );
 
@@ -510,13 +288,15 @@ public function testShouldNotSetupBrokerIfManagementPluginInstalledOptionIsNotEn
 
     public function testShouldSetupBroker()
     {
-        $metaRegistry = $this->createDummyQueueMetaRegistry();
+        $routeCollection = new RouteCollection([
+            new Route('topic', Route::TOPIC, 'processor'),
+        ]);
 
         $managementClient = $this->createManagementClientMock();
         $managementClient
             ->expects($this->at(0))
             ->method('declareExchange')
-            ->with('prefix.routertopic', [
+            ->with('aprefix.router', [
                 'type' => 'fanout',
                 'durable' => true,
                 'auto_delete' => false,
@@ -525,7 +305,7 @@ public function testShouldSetupBroker()
         $managementClient
             ->expects($this->at(1))
             ->method('declareQueue')
-            ->with('prefix.app.routerqueue', [
+            ->with('aprefix.default', [
                 'durable' => true,
                 'auto_delete' => false,
                 'arguments' => [
@@ -536,12 +316,12 @@ public function testShouldSetupBroker()
         $managementClient
             ->expects($this->at(2))
             ->method('bind')
-            ->with('prefix.routertopic', 'prefix.app.routerqueue', 'prefix.app.routerqueue')
+            ->with('aprefix.router', 'aprefix.default', 'aprefix.default')
         ;
         $managementClient
             ->expects($this->at(3))
             ->method('declareQueue')
-            ->with('prefix.app.default', [
+            ->with('default', [
                 'durable' => true,
                 'auto_delete' => false,
                 'arguments' => [
@@ -550,10 +330,33 @@ public function testShouldSetupBroker()
             ])
         ;
 
-        $driver = new RabbitMqStompDriver(
-            $this->createPsrContextMock(),
-            new Config('prefix', 'app', 'routerTopic', 'routerQueue', 'processorQueue', '', ['management_plugin_installed' => true]),
-            $metaRegistry,
+        $contextMock = $this->createContextMock();
+        $contextMock
+            ->expects($this->any())
+            ->method('createQueue')
+            ->willReturnCallback(function (string $name) {
+                $destination = new StompDestination();
+                $destination->setType(StompDestination::TYPE_QUEUE);
+                $destination->setStompName($name);
+
+                return $destination;
+            })
+        ;
+
+        $config = Config::create(
+            'aPrefix',
+            '',
+            null,
+            null,
+            null,
+            null,
+            ['delay_plugin_installed' => false, 'management_plugin_installed' => true]
+        );
+
+        $driver = $this->createDriver(
+            $contextMock,
+            $config,
+            $routeCollection,
             $managementClient
         );
 
@@ -561,22 +364,22 @@ public function testShouldSetupBroker()
         $logger
             ->expects($this->at(0))
             ->method('debug')
-            ->with('[RabbitMqStompDriver] Declare router exchange: prefix.routertopic')
+            ->with('[RabbitMqStompDriver] Declare router exchange: aprefix.router')
         ;
         $logger
             ->expects($this->at(1))
             ->method('debug')
-            ->with('[RabbitMqStompDriver] Declare router queue: prefix.app.routerqueue')
+            ->with('[RabbitMqStompDriver] Declare router queue: aprefix.default')
         ;
         $logger
             ->expects($this->at(2))
             ->method('debug')
-            ->with('[RabbitMqStompDriver] Bind router queue to exchange: prefix.app.routerqueue -> prefix.routertopic')
+            ->with('[RabbitMqStompDriver] Bind router queue to exchange: aprefix.default -> aprefix.router')
         ;
         $logger
             ->expects($this->at(3))
             ->method('debug')
-            ->with('[RabbitMqStompDriver] Declare processor queue: prefix.app.default')
+            ->with('[RabbitMqStompDriver] Declare processor queue: default')
         ;
 
         $driver->setupBroker($logger);
@@ -584,13 +387,15 @@ public function testShouldSetupBroker()
 
     public function testSetupBrokerShouldCreateDelayExchangeIfEnabled()
     {
-        $metaRegistry = $this->createDummyQueueMetaRegistry();
+        $routeCollection = new RouteCollection([
+            new Route('topic', Route::TOPIC, 'processor'),
+        ]);
 
         $managementClient = $this->createManagementClientMock();
         $managementClient
-            ->expects($this->at(6))
+            ->expects($this->at(4))
             ->method('declareExchange')
-            ->with('prefix.app.default.delayed', [
+            ->with('default.delayed', [
                 'type' => 'x-delayed-message',
                 'durable' => true,
                 'auto_delete' => false,
@@ -600,83 +405,170 @@ public function testSetupBrokerShouldCreateDelayExchangeIfEnabled()
             ])
         ;
         $managementClient
-            ->expects($this->at(7))
+            ->expects($this->at(5))
             ->method('bind')
-            ->with('prefix.app.default.delayed', 'prefix.app.default', 'prefix.app.default')
+            ->with('default.delayed', 'default', 'default')
         ;
 
-        $driver = new RabbitMqStompDriver(
-            $this->createPsrContextMock(),
-            new Config('prefix', 'app', 'routerTopic', 'routerQueue', 'processorQueue', '', ['management_plugin_installed' => true, 'delay_plugin_installed' => true]),
-            $metaRegistry,
+        $config = Config::create(
+            'aPrefix',
+            '',
+            null,
+            null,
+            null,
+            null,
+            ['delay_plugin_installed' => true, 'management_plugin_installed' => true]
+        );
+
+        $contextMock = $this->createContextMock();
+        $contextMock
+            ->expects($this->any())
+            ->method('createQueue')
+            ->willReturnCallback(function (string $name) {
+                $destination = new StompDestination();
+                $destination->setType(StompDestination::TYPE_QUEUE);
+                $destination->setStompName($name);
+
+                return $destination;
+            })
+        ;
+        $contextMock
+            ->expects($this->any())
+            ->method('createTopic')
+            ->willReturnCallback(function (string $name) {
+                $destination = new StompDestination();
+                $destination->setType(StompDestination::TYPE_TOPIC);
+                $destination->setStompName($name);
+
+                return $destination;
+            })
+        ;
+
+        $driver = $this->createDriver(
+            $contextMock,
+            $config,
+            $routeCollection,
             $managementClient
         );
 
         $logger = $this->createLoggerMock();
         $logger
-            ->expects($this->at(6))
+            ->expects($this->at(4))
             ->method('debug')
-            ->with('[RabbitMqStompDriver] Declare delay exchange: prefix.app.default.delayed')
+            ->with('[RabbitMqStompDriver] Declare delay exchange: default.delayed')
         ;
         $logger
-            ->expects($this->at(7))
+            ->expects($this->at(5))
             ->method('debug')
-            ->with('[RabbitMqStompDriver] Bind processor queue to delay exchange: prefix.app.default -> prefix.app.default.delayed')
+            ->with('[RabbitMqStompDriver] Bind processor queue to delay exchange: default -> default.delayed')
         ;
 
         $driver->setupBroker($logger);
     }
 
+    protected function createDriver(...$args): DriverInterface
+    {
+        return new RabbitMqStompDriver(
+            $args[0],
+            $args[1],
+            $args[2],
+            isset($args[3]) ? $args[3] : $this->createManagementClientMock()
+        );
+    }
+
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|StompContext
+     * @return StompContext
      */
-    private function createPsrContextMock()
+    protected function createContextMock(): PsrContext
     {
         return $this->createMock(StompContext::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|StompProducer
+     * @return StompProducer
      */
-    private function createPsrProducerMock()
+    protected function createProducerMock(): PsrProducer
     {
         return $this->createMock(StompProducer::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return StompDestination
      */
-    private function createManagementClientMock(): StompManagementClient
+    protected function createQueue(string $name): PsrQueue
     {
-        return $this->createMock(StompManagementClient::class);
+        return new StompDestination();
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|LoggerInterface
+     * @return StompDestination
      */
-    private function createLoggerMock()
+    protected function createTopic(string $name): PsrTopic
     {
-        return $this->createMock(LoggerInterface::class);
+        return new StompDestination();
     }
 
     /**
-     * @return QueueMetaRegistry
+     * @return StompMessage
      */
-    private function createDummyQueueMetaRegistry()
+    protected function createMessage(): PsrMessage
     {
-        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
-        $registry->add('default');
-        $registry->add('aFooQueue');
-        $registry->add('aBarQueue', 'aBarQueue');
+        return new StompMessage();
+    }
 
-        return $registry;
+    protected function assertTransportMessage(PsrMessage $transportMessage): void
+    {
+        $this->assertSame('body', $transportMessage->getBody());
+        $this->assertEquals([
+            'hkey' => 'hval',
+            'message_id' => 'theMessageId',
+            'timestamp' => 1000,
+            'reply-to' => 'theReplyTo',
+            'persistent' => true,
+            'correlation_id' => 'theCorrelationId',
+            'expiration' => '123000',
+            'priority' => 3,
+            'x-delay' => '345000',
+        ], $transportMessage->getHeaders());
+        $this->assertEquals([
+            'pkey' => 'pval',
+            'X-Enqueue-Content-Type' => 'ContentType',
+            'X-Enqueue-Priority' => MessagePriority::HIGH,
+            'X-Enqueue-Expire' => 123,
+            'X-Enqueue-Delay' => 345,
+        ], $transportMessage->getProperties());
+        $this->assertSame('theMessageId', $transportMessage->getMessageId());
+        $this->assertSame(1000, $transportMessage->getTimestamp());
+        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
+        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
+    }
+
+    protected function createDummyConfig(): Config
+    {
+        return Config::create(
+            'aPrefix',
+            '',
+            null,
+            null,
+            null,
+            null,
+            ['delay_plugin_installed' => true, 'management_plugin_installed' => true]
+        );
     }
 
     /**
-     * @return Config
+     * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    private function createDummyConfig()
+    private function createManagementClientMock(): StompManagementClient
     {
-        return Config::create('aPrefix');
+        return $this->createMock(StompManagementClient::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|LoggerInterface
+     */
+    private function createLoggerMock()
+    {
+        return $this->createMock(LoggerInterface::class);
     }
 }

From 7d36b820727a42a79c54a593a9d3e9f6ffeecdf6 Mon Sep 17 00:00:00 2001
From: Arnaud T <a.tarroux@leshabitues.fr>
Date: Tue, 18 Sep 2018 15:30:59 +0200
Subject: [PATCH 126/337] enableSubscriptionConsumer setter

---
 Consumption/QueueConsumer.php | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 58209db..6f4a26e 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -358,6 +358,8 @@ public function enableSubscriptionConsumer($enableSubscriptionConsumer)
                 )
             );
         }
+
+        $this->enableSubscriptionConsumer = $enableSubscriptionConsumer;
     }
 
     /**

From c80b040fe7342e54957da7c1c4bbc1b86117addf Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Wed, 19 Sep 2018 14:53:52 +0300
Subject: [PATCH 127/337] [client] Extend rest of drivers from generic one.
 Rework driver factory.

---
 Client/Driver/RdKafkaDriver.php               | 138 +------
 Client/Driver/RedisDriver.php                 | 130 ------
 Client/Driver/SqsDriver.php                   | 137 ++-----
 Client/DriverFactory.php                      |  31 +-
 Client/DriverInterface.php                    |   5 +
 Client/Resources.php                          |  46 ++-
 Client/Route.php                              |   2 +-
 Tests/Client/Driver/AmqpDriverTest.php        |   4 +-
 .../Client/Driver/GenericDriverTestsTrait.php |  48 ++-
 Tests/Client/Driver/GpsDriverTest.php         |   8 +-
 .../Client/Driver/RabbitMqStompDriverTest.php |  12 +-
 Tests/Client/Driver/RdKafkaDriverTest.php     | 328 ++-------------
 Tests/Client/Driver/RedisDriverTest.php       | 366 -----------------
 Tests/Client/Driver/SqsDriverTest.php         | 386 +++---------------
 Tests/Client/DriverFactoryTest.php            |  24 +-
 Tests/Client/ResourcesTest.php                |  53 ++-
 16 files changed, 294 insertions(+), 1424 deletions(-)
 delete mode 100644 Client/Driver/RedisDriver.php
 delete mode 100644 Tests/Client/Driver/RedisDriverTest.php

diff --git a/Client/Driver/RdKafkaDriver.php b/Client/Driver/RdKafkaDriver.php
index 7f536a7..01b9e27 100644
--- a/Client/Driver/RdKafkaDriver.php
+++ b/Client/Driver/RdKafkaDriver.php
@@ -2,115 +2,19 @@
 
 namespace Enqueue\Client\Driver;
 
-use Enqueue\Client\Config;
-use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\RdKafka\RdKafkaContext;
-use Enqueue\RdKafka\RdKafkaMessage;
 use Enqueue\RdKafka\RdKafkaTopic;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
-class RdKafkaDriver implements DriverInterface
+/**
+ * @method RdKafkaContext getContext()
+ */
+class RdKafkaDriver extends GenericDriver
 {
-    /**
-     * @var RdKafkaContext
-     */
-    private $context;
-
-    /**
-     * @var Config
-     */
-    private $config;
-
-    /**
-     * @var QueueMetaRegistry
-     */
-    private $queueMetaRegistry;
-
-    public function __construct(RdKafkaContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
-    {
-        $this->context = $context;
-        $this->config = $config;
-        $this->queueMetaRegistry = $queueMetaRegistry;
-    }
-
-    /**
-     * @return RdKafkaMessage
-     */
-    public function createTransportMessage(Message $message): PsrMessage
-    {
-        $headers = $message->getHeaders();
-        $headers['content_type'] = $message->getContentType();
-
-        $transportMessage = $this->context->createMessage();
-        $transportMessage->setBody($message->getBody());
-        $transportMessage->setHeaders($headers);
-        $transportMessage->setProperties($message->getProperties());
-        $transportMessage->setMessageId($message->getMessageId());
-        $transportMessage->setTimestamp($message->getTimestamp());
-        $transportMessage->setReplyTo($message->getReplyTo());
-        $transportMessage->setCorrelationId($message->getCorrelationId());
-
-        return $transportMessage;
-    }
-
-    public function createClientMessage(PsrMessage $message): Message
-    {
-        $clientMessage = new Message();
-        $clientMessage->setBody($message->getBody());
-        $clientMessage->setHeaders($message->getHeaders());
-        $clientMessage->setProperties($message->getProperties());
-
-        $clientMessage->setContentType($message->getHeader('content_type'));
-
-        $clientMessage->setTimestamp($message->getTimestamp());
-        $clientMessage->setMessageId($message->getMessageId());
-        $clientMessage->setReplyTo($message->getReplyTo());
-        $clientMessage->setCorrelationId($message->getCorrelationId());
-
-        return $clientMessage;
-    }
-
-    public function sendToRouter(Message $message): void
-    {
-        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
-            throw new \LogicException('Topic name parameter is required but is not set');
-        }
-
-        $topic = $this->createRouterTopic();
-        $transportMessage = $this->createTransportMessage($message);
-
-        $this->context->createProducer()->send($topic, $transportMessage);
-    }
-
-    public function sendToProcessor(Message $message): void
-    {
-        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
-            throw new \LogicException('Processor name parameter is required but is not set');
-        }
-
-        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
-            throw new \LogicException('Queue name parameter is required but is not set');
-        }
-
-        $transportMessage = $this->createTransportMessage($message);
-        $destination = $this->createQueue($queueName);
-
-        $this->context->createProducer()->send($destination, $transportMessage);
-    }
-
-    /**
-     * @return RdKafkaTopic
-     */
-    public function createQueue(string $queueName): PsrQueue
+    public function __construct(RdKafkaContext $context, ...$args)
     {
-        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
-
-        return $this->context->createQueue($transportName);
+        parent::__construct($context, ...$args);
     }
 
     public function setupBroker(LoggerInterface $logger = null): void
@@ -122,29 +26,21 @@ public function setupBroker(LoggerInterface $logger = null): void
         };
 
         // setup router
-        $routerQueue = $this->createQueue($this->config->getRouterQueueName());
+        $routerQueue = $this->createQueue($this->getConfig()->getRouterQueueName());
         $log('Create router queue: %s', $routerQueue->getQueueName());
-        $this->context->createConsumer($routerQueue);
+        $this->getContext()->createConsumer($routerQueue);
 
         // setup queues
-        foreach ($this->queueMetaRegistry->getQueuesMeta() as $meta) {
-            $queue = $this->createQueue($meta->getClientName());
+        $declaredQueues = [];
+        foreach ($this->getRouteCollection()->all() as $route) {
+            /** @var RdKafkaTopic $queue */
+            $queue = $this->createRouteQueue($route);
+            if (array_key_exists($queue->getQueueName(), $declaredQueues)) {
+                continue;
+            }
+
             $log('Create processor queue: %s', $queue->getQueueName());
-            $this->context->createConsumer($queue);
+            $this->getContext()->createConsumer($queue);
         }
     }
-
-    public function getConfig(): Config
-    {
-        return $this->config;
-    }
-
-    private function createRouterTopic(): RdKafkaTopic
-    {
-        $topic = $this->context->createTopic(
-            $this->config->createTransportRouterTopicName($this->config->getRouterTopicName())
-        );
-
-        return $topic;
-    }
 }
diff --git a/Client/Driver/RedisDriver.php b/Client/Driver/RedisDriver.php
deleted file mode 100644
index 10b9cb9..0000000
--- a/Client/Driver/RedisDriver.php
+++ /dev/null
@@ -1,130 +0,0 @@
-<?php
-
-namespace  Enqueue\Client\Driver;
-
-use Enqueue\Client\Config;
-use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\MessagePriority;
-use Enqueue\Client\Meta\QueueMetaRegistry;
-use Enqueue\Redis\RedisContext;
-use Enqueue\Redis\RedisDestination;
-use Enqueue\Redis\RedisMessage;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrQueue;
-use Psr\Log\LoggerInterface;
-
-class RedisDriver implements DriverInterface
-{
-    /**
-     * @var RedisContext
-     */
-    private $context;
-
-    /**
-     * @var Config
-     */
-    private $config;
-
-    /**
-     * @var QueueMetaRegistry
-     */
-    private $queueMetaRegistry;
-
-    public function __construct(RedisContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
-    {
-        $this->context = $context;
-        $this->config = $config;
-        $this->queueMetaRegistry = $queueMetaRegistry;
-    }
-
-    public function sendToRouter(Message $message): void
-    {
-        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
-            throw new \LogicException('Topic name parameter is required but is not set');
-        }
-
-        $queue = $this->createQueue($this->config->getRouterQueueName());
-        $transportMessage = $this->createTransportMessage($message);
-
-        $this->context->createProducer()->send($queue, $transportMessage);
-    }
-
-    public function sendToProcessor(Message $message): void
-    {
-        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
-            throw new \LogicException('Processor name parameter is required but is not set');
-        }
-
-        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
-            throw new \LogicException('Queue name parameter is required but is not set');
-        }
-
-        $transportMessage = $this->createTransportMessage($message);
-        $destination = $this->createQueue($queueName);
-
-        $this->context->createProducer()->send($destination, $transportMessage);
-    }
-
-    public function setupBroker(LoggerInterface $logger = null): void
-    {
-    }
-
-    /**
-     * @return RedisDestination
-     */
-    public function createQueue(string $queueName): PsrQueue
-    {
-        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
-
-        return $this->context->createQueue($transportName);
-    }
-
-    /**
-     * @return RedisMessage
-     */
-    public function createTransportMessage(Message $message): PsrMessage
-    {
-        $properties = $message->getProperties();
-
-        $headers = $message->getHeaders();
-        $headers['content_type'] = $message->getContentType();
-
-        $transportMessage = $this->context->createMessage();
-        $transportMessage->setBody($message->getBody());
-        $transportMessage->setHeaders($headers);
-        $transportMessage->setProperties($properties);
-        $transportMessage->setMessageId($message->getMessageId());
-        $transportMessage->setTimestamp($message->getTimestamp());
-        $transportMessage->setReplyTo($message->getReplyTo());
-        $transportMessage->setCorrelationId($message->getCorrelationId());
-
-        return $transportMessage;
-    }
-
-    /**
-     * @param RedisMessage $message
-     */
-    public function createClientMessage(PsrMessage $message): Message
-    {
-        $clientMessage = new Message();
-
-        $clientMessage->setBody($message->getBody());
-        $clientMessage->setHeaders($message->getHeaders());
-        $clientMessage->setProperties($message->getProperties());
-
-        $clientMessage->setContentType($message->getHeader('content_type'));
-        $clientMessage->setMessageId($message->getMessageId());
-        $clientMessage->setTimestamp($message->getTimestamp());
-        $clientMessage->setPriority(MessagePriority::NORMAL);
-        $clientMessage->setReplyTo($message->getReplyTo());
-        $clientMessage->setCorrelationId($message->getCorrelationId());
-
-        return $clientMessage;
-    }
-
-    public function getConfig(): Config
-    {
-        return $this->config;
-    }
-}
diff --git a/Client/Driver/SqsDriver.php b/Client/Driver/SqsDriver.php
index 8aba2e1..622c6f7 100644
--- a/Client/Driver/SqsDriver.php
+++ b/Client/Driver/SqsDriver.php
@@ -2,80 +2,20 @@
 
 namespace Enqueue\Client\Driver;
 
-use Enqueue\Client\Config;
-use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\MessagePriority;
-use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\Sqs\SqsContext;
 use Enqueue\Sqs\SqsDestination;
-use Enqueue\Sqs\SqsMessage;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
-class SqsDriver implements DriverInterface
+/**
+ * @method SqsContext getContext
+ * @method SqsDestination createQueue(string $clientQueueName): PsrQueue
+ */
+class SqsDriver extends GenericDriver
 {
-    /**
-     * @var SqsContext
-     */
-    private $context;
-
-    /**
-     * @var Config
-     */
-    private $config;
-
-    /**
-     * @var QueueMetaRegistry
-     */
-    private $queueMetaRegistry;
-
-    public function __construct(SqsContext $context, Config $config, QueueMetaRegistry $queueMetaRegistry)
+    public function __construct(SqsContext $context, ...$args)
     {
-        $this->context = $context;
-        $this->config = $config;
-        $this->queueMetaRegistry = $queueMetaRegistry;
-    }
-
-    public function sendToRouter(Message $message): void
-    {
-        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
-            throw new \LogicException('Topic name parameter is required but is not set');
-        }
-
-        $queue = $this->createQueue($this->config->getRouterQueueName());
-        $transportMessage = $this->createTransportMessage($message);
-
-        $this->context->createProducer()->send($queue, $transportMessage);
-    }
-
-    public function sendToProcessor(Message $message): void
-    {
-        if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
-            throw new \LogicException('Processor name parameter is required but is not set');
-        }
-
-        if (false == $queueName = $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
-            throw new \LogicException('Queue name parameter is required but is not set');
-        }
-
-        $transportMessage = $this->createTransportMessage($message);
-        $destination = $this->createQueue($queueName);
-
-        $this->context->createProducer()->send($destination, $transportMessage);
-    }
-
-    /**
-     * @return SqsDestination
-     */
-    public function createQueue(string $queueName): PsrQueue
-    {
-        $transportName = $this->queueMetaRegistry->getQueueMeta($queueName)->getTransportName();
-        $transportName = str_replace('.', '_dot_', $transportName);
-
-        return $this->context->createQueue($transportName);
+        parent::__construct($context, ...$args);
     }
 
     public function setupBroker(LoggerInterface $logger = null): void
@@ -86,64 +26,37 @@ public function setupBroker(LoggerInterface $logger = null): void
         };
 
         // setup router
-        $routerQueue = $this->createQueue($this->config->getRouterQueueName());
+        $routerQueue = $this->createQueue($this->getConfig()->getRouterQueueName());
         $log('Declare router queue: %s', $routerQueue->getQueueName());
-        $this->context->declareQueue($routerQueue);
+        $this->getContext()->declareQueue($routerQueue);
 
         // setup queues
-        foreach ($this->queueMetaRegistry->getQueuesMeta() as $meta) {
-            $queue = $this->createQueue($meta->getClientName());
+        $declaredQueues = [];
+        foreach ($this->getRouteCollection()->all() as $route) {
+            /** @var SqsDestination $queue */
+            $queue = $this->createRouteQueue($route);
+            if (array_key_exists($queue->getQueueName(), $declaredQueues)) {
+                continue;
+            }
 
             $log('Declare processor queue: %s', $queue->getQueueName());
-            $this->context->declareQueue($queue);
+            $this->getContext()->declareQueue($queue);
+
+            $declaredQueues[$queue->getQueueName()] = true;
         }
     }
 
-    /**
-     * @return SqsMessage
-     */
-    public function createTransportMessage(Message $message): PsrMessage
+    protected function createTransportRouterTopicName(string $name, bool $prefix): string
     {
-        $properties = $message->getProperties();
+        $name = parent::createTransportRouterTopicName($name, $prefix);
 
-        $headers = $message->getHeaders();
-        $headers['content_type'] = $message->getContentType();
-
-        $transportMessage = $this->context->createMessage();
-        $transportMessage->setBody($message->getBody());
-        $transportMessage->setHeaders($headers);
-        $transportMessage->setProperties($properties);
-        $transportMessage->setMessageId($message->getMessageId());
-        $transportMessage->setTimestamp($message->getTimestamp());
-        $transportMessage->setReplyTo($message->getReplyTo());
-        $transportMessage->setCorrelationId($message->getCorrelationId());
-
-        return $transportMessage;
+        return str_replace('.', '_dot_', $name);
     }
 
-    /**
-     * @param SqsMessage $message
-     */
-    public function createClientMessage(PsrMessage $message): Message
+    protected function createTransportQueueName(string $name, bool $prefix): string
     {
-        $clientMessage = new Message();
-
-        $clientMessage->setBody($message->getBody());
-        $clientMessage->setHeaders($message->getHeaders());
-        $clientMessage->setProperties($message->getProperties());
-
-        $clientMessage->setContentType($message->getHeader('content_type'));
-        $clientMessage->setMessageId($message->getMessageId());
-        $clientMessage->setTimestamp($message->getTimestamp());
-        $clientMessage->setPriority(MessagePriority::NORMAL);
-        $clientMessage->setReplyTo($message->getReplyTo());
-        $clientMessage->setCorrelationId($message->getCorrelationId());
+        $name = parent::createTransportQueueName($name, $prefix);
 
-        return $clientMessage;
-    }
-
-    public function getConfig(): Config
-    {
-        return $this->config;
+        return str_replace('.', '_dot_', $name);
     }
 }
diff --git a/Client/DriverFactory.php b/Client/DriverFactory.php
index 8c47297..67d3607 100644
--- a/Client/DriverFactory.php
+++ b/Client/DriverFactory.php
@@ -5,7 +5,6 @@
 use Enqueue\Client\Driver\RabbitMqDriver;
 use Enqueue\Client\Driver\RabbitMqStompDriver;
 use Enqueue\Client\Driver\StompManagementClient;
-use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\Dsn\Dsn;
 use Enqueue\Stomp\StompConnectionFactory;
 use Interop\Amqp\AmqpConnectionFactory;
@@ -19,21 +18,23 @@ final class DriverFactory implements DriverFactoryInterface
     private $config;
 
     /**
-     * @var QueueMetaRegistry
+     * @var RouteCollection
      */
-    private $queueMetaRegistry;
+    private $routeCollection;
 
-    public function __construct(Config $config, QueueMetaRegistry $queueMetaRegistry)
+    public function __construct(Config $config, RouteCollection $routeCollection)
     {
         $this->config = $config;
-        $this->queueMetaRegistry = $queueMetaRegistry;
+        $this->routeCollection = $routeCollection;
     }
 
     public function create(PsrConnectionFactory $factory, string $dsn, array $config): DriverInterface
     {
         $dsn = new Dsn($dsn);
 
-        if ($driverClass = $this->findDriverClass($dsn, Resources::getAvailableDrivers())) {
+        if ($driverInfo = $this->findDriverInfo($dsn, Resources::getAvailableDrivers())) {
+            $driverClass = $driverInfo['factoryClass'];
+
             if (RabbitMqDriver::class === $driverClass) {
                 if (false == $factory instanceof AmqpConnectionFactory) {
                     throw new \LogicException(sprintf(
@@ -43,7 +44,7 @@ public function create(PsrConnectionFactory $factory, string $dsn, array $config
                     ));
                 }
 
-                return new RabbitMqDriver($factory->createContext(), $this->config, $this->queueMetaRegistry);
+                return new RabbitMqDriver($factory->createContext(), $this->config, $this->routeCollection);
             }
 
             if (RabbitMqStompDriver::class === $driverClass) {
@@ -63,18 +64,18 @@ public function create(PsrConnectionFactory $factory, string $dsn, array $config
                     (string) $dsn->getPassword()
                 );
 
-                return new RabbitMqStompDriver($factory->createContext(), $this->config, $this->queueMetaRegistry, $managementClient);
+                return new RabbitMqStompDriver($factory->createContext(), $this->config, $this->routeCollection, $managementClient);
             }
 
-            return new $driverClass($factory->createContext(), $this->config, $this->queueMetaRegistry);
+            return new $driverClass($factory->createContext(), $this->config, $this->routeCollection);
         }
 
         $knownDrivers = Resources::getKnownDrivers();
-        if ($driverClass = $this->findDriverClass($dsn, $knownDrivers)) {
+        if ($driverInfo = $this->findDriverInfo($dsn, $knownDrivers)) {
             throw new \LogicException(sprintf(
                 'To use given scheme "%s" a package has to be installed. Run "composer req %s" to add it.',
                 $dsn->getScheme(),
-                implode(' ', $knownDrivers[$driverClass]['packages'])
+                implode(' ', $driverInfo['packages'])
             ));
         }
 
@@ -85,12 +86,12 @@ public function create(PsrConnectionFactory $factory, string $dsn, array $config
         ));
     }
 
-    private function findDriverClass(Dsn $dsn, array $factories): ?string
+    private function findDriverInfo(Dsn $dsn, array $factories): ?array
     {
         $protocol = $dsn->getSchemeProtocol();
 
         if ($dsn->getSchemeExtensions()) {
-            foreach ($factories as $driverClass => $info) {
+            foreach ($factories as $info) {
                 if (empty($info['requiredSchemeExtensions'])) {
                     continue;
                 }
@@ -101,7 +102,7 @@ private function findDriverClass(Dsn $dsn, array $factories): ?string
 
                 $diff = array_diff($dsn->getSchemeExtensions(), $info['requiredSchemeExtensions']);
                 if (empty($diff)) {
-                    return $driverClass;
+                    return $info;
                 }
             }
         }
@@ -115,7 +116,7 @@ private function findDriverClass(Dsn $dsn, array $factories): ?string
                 continue;
             }
 
-            return $driverClass;
+            return $info;
         }
 
         return null;
diff --git a/Client/DriverInterface.php b/Client/DriverInterface.php
index 11f70f6..e671556 100644
--- a/Client/DriverInterface.php
+++ b/Client/DriverInterface.php
@@ -4,6 +4,7 @@
 
 namespace Enqueue\Client;
 
+use Interop\Queue\PsrContext;
 use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
@@ -27,4 +28,8 @@ public function createQueue(string $queueName): PsrQueue;
     public function setupBroker(LoggerInterface $logger = null): void;
 
     public function getConfig(): Config;
+
+    public function getContext(): PsrContext;
+
+    public function getRouteCollection(): RouteCollection;
 }
diff --git a/Client/Resources.php b/Client/Resources.php
index bef313b..09ee430 100644
--- a/Client/Resources.php
+++ b/Client/Resources.php
@@ -11,7 +11,6 @@
 use Enqueue\Client\Driver\RabbitMqDriver;
 use Enqueue\Client\Driver\RabbitMqStompDriver;
 use Enqueue\Client\Driver\RdKafkaDriver;
-use Enqueue\Client\Driver\RedisDriver;
 use Enqueue\Client\Driver\SqsDriver;
 use Enqueue\Client\Driver\StompDriver;
 
@@ -36,9 +35,9 @@ public static function getAvailableDrivers(): array
         $map = self::getKnownDrivers();
 
         $availableMap = [];
-        foreach ($map as $driverClass => $item) {
-            if (class_exists($driverClass)) {
-                $availableMap[$driverClass] = $item;
+        foreach ($map as $item) {
+            if (class_exists($item['factoryClass'])) {
+                $availableMap[] = $item;
             }
         }
 
@@ -50,62 +49,73 @@ public static function getKnownDrivers(): array
         if (null === self::$knownDrivers) {
             $map = [];
 
-            $map[AmqpDriver::class] = [
+            $map[] = [
                 'schemes' => ['amqp', 'amqps'],
+                'factoryClass' => AmqpDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/amqp-bunny'],
             ];
-            $map[RabbitMqDriver::class] = [
+            $map[] = [
                 'schemes' => ['amqp', 'amqps'],
+                'factoryClass' => RabbitMqDriver::class,
                 'requiredSchemeExtensions' => ['rabbitmq'],
                 'packages' => ['enqueue/enqueue', 'enqueue/amqp-bunny'],
             ];
-            $map[FsDriver::class] = [
+            $map[] = [
                 'schemes' => ['file'],
+                'factoryClass' => FsDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/fs'],
             ];
-            $map[GenericDriver::class] = [
+            $map[] = [
                 'schemes' => ['null'],
+                'factoryClass' => GenericDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/null'],
             ];
-            $map[GpsDriver::class] = [
+            $map[] = [
                 'schemes' => ['gps'],
+                'factoryClass' => GpsDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/gps'],
             ];
-            $map[RedisDriver::class] = [
+            $map[] = [
                 'schemes' => ['redis'],
+                'factoryClass' => GenericDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/redis'],
             ];
-            $map[SqsDriver::class] = [
+            $map[] = [
                 'schemes' => ['sqs'],
+                'factoryClass' => SqsDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/sqs'],
             ];
-            $map[StompDriver::class] = [
+            $map[] = [
                 'schemes' => ['stomp'],
+                'factoryClass' => StompDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/stomp'],
             ];
-            $map[RabbitMqStompDriver::class] = [
+            $map[] = [
                 'schemes' => ['stomp'],
+                'factoryClass' => RabbitMqStompDriver::class,
                 'requiredSchemeExtensions' => ['rabbitmq'],
                 'packages' => ['enqueue/enqueue', 'enqueue/stomp'],
             ];
-            $map[RdKafkaDriver::class] = [
+            $map[] = [
                 'schemes' => ['kafka', 'rdkafka'],
+                'factoryClass' => RdKafkaDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/rdkafka'],
             ];
-            $map[MongodbDriver::class] = [
+            $map[] = [
                 'schemes' => ['mongodb'],
+                'factoryClass' => MongodbDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/mongodb'],
             ];
-            $map[DbalDriver::class] = [
+            $map[] = [
                 'schemes' => [
                     'db2',
                     'ibm-db2',
@@ -121,6 +131,7 @@ public static function getKnownDrivers(): array
                     'sqlite3',
                     'sqlite',
                 ],
+                'factoryClass' => DbalDriver::class,
                 'requiredSchemeExtensions' => [],
                 'package' => ['enqueue/enqueue', 'enqueue/dbal'],
             ];
@@ -147,8 +158,9 @@ public static function addDriver(string $driverClass, array $schemes, array $req
         }
 
         self::getKnownDrivers();
-        self::$knownDrivers[$driverClass] = [
+        self::$knownDrivers[] = [
             'schemes' => $schemes,
+            'factoryClass' => $driverClass,
             'requiredSchemeExtensions' => $requiredExtensions,
             'packages' => $packages,
         ];
diff --git a/Client/Route.php b/Client/Route.php
index e11fa19..5c98fa6 100644
--- a/Client/Route.php
+++ b/Client/Route.php
@@ -77,7 +77,7 @@ public function getQueue(): ?string
 
     public function isPrefixQueue(): bool
     {
-        return (bool) $this->getOption('prefix_queue', false);
+        return (bool) $this->getOption('prefix_queue', true);
     }
 
     public function getOptions(): array
diff --git a/Tests/Client/Driver/AmqpDriverTest.php b/Tests/Client/Driver/AmqpDriverTest.php
index 9e8ce1b..91a7f52 100644
--- a/Tests/Client/Driver/AmqpDriverTest.php
+++ b/Tests/Client/Driver/AmqpDriverTest.php
@@ -220,7 +220,7 @@ public function testShouldSetupBroker()
         $context
             ->expects($this->at(5))
             ->method('createQueue')
-            ->with('default')
+            ->with($this->getDefaultQueueTransportName())
             ->willReturn($processorWithDefaultQueue)
         ;
         $context
@@ -232,7 +232,7 @@ public function testShouldSetupBroker()
         $context
             ->expects($this->at(7))
             ->method('createQueue')
-            ->with('custom')
+            ->with($this->getCustomQueueTransportName())
             ->willReturn($processorWithCustomQueue)
         ;
         $context
diff --git a/Tests/Client/Driver/GenericDriverTestsTrait.php b/Tests/Client/Driver/GenericDriverTestsTrait.php
index b7f5f2b..4c03a14 100644
--- a/Tests/Client/Driver/GenericDriverTestsTrait.php
+++ b/Tests/Client/Driver/GenericDriverTestsTrait.php
@@ -49,6 +49,7 @@ public function testShouldReturnRouteCollectionSetInConstructor()
     {
         $routeCollection = new RouteCollection([]);
 
+        /** @var DriverInterface $driver */
         $driver = $this->createDriver($this->createContextMock(), $this->createDummyConfig(), $routeCollection);
 
         $this->assertSame($routeCollection, $driver->getRouteCollection());
@@ -62,7 +63,7 @@ public function testShouldCreateAndReturnQueueInstanceWithPrefixAndAppName()
         $context
             ->expects($this->once())
             ->method('createQueue')
-            ->with('aprefix.anappname.afooqueue')
+            ->with($this->getPrefixAppFooQueueTransportName())
             ->willReturn($expectedQueue)
         ;
 
@@ -91,7 +92,7 @@ public function testShouldCreateAndReturnQueueInstanceWithPrefixWithoutAppName()
         $context
             ->expects($this->once())
             ->method('createQueue')
-            ->with('aprefix.afooqueue')
+            ->with($this->getPrefixFooQueueTransportName())
             ->willReturn($expectedQueue)
         ;
 
@@ -120,7 +121,7 @@ public function testShouldCreateAndReturnQueueInstanceWithAppNameAndWithoutPrefi
         $context
             ->expects($this->once())
             ->method('createQueue')
-            ->with('anappname.afooqueue')
+            ->with($this->getAppFooQueueTransportName())
             ->willReturn($expectedQueue)
         ;
 
@@ -178,7 +179,7 @@ public function testShouldCreateAndReturnQueueInstance()
         $context
             ->expects($this->once())
             ->method('createQueue')
-            ->with('aprefix.afooqueue')
+            ->with($this->getPrefixFooQueueTransportName())
             ->willReturn($expectedQueue)
         ;
 
@@ -472,7 +473,7 @@ public function testShouldSendTopicMessageToProcessorToDefaultQueue()
         $context
             ->expects($this->once())
             ->method('createQueue')
-            ->with('default')
+            ->with($this->getDefaultQueueTransportName())
             ->willReturn($queue)
         ;
         $context
@@ -516,7 +517,7 @@ public function testShouldSendTopicMessageToProcessorToCustomQueue()
         $context
             ->expects($this->once())
             ->method('createQueue')
-            ->with('custom')
+            ->with($this->getCustomQueueTransportName())
             ->willReturn($queue)
         ;
         $context
@@ -565,7 +566,7 @@ public function testShouldInitDeliveryDelayIfDelayPropertyOnSendToProcessor()
         $context
             ->expects($this->once())
             ->method('createQueue')
-            ->with('default')
+            ->with($this->getDefaultQueueTransportName())
             ->willReturn($queue)
         ;
         $context
@@ -615,7 +616,7 @@ public function testShouldSetInitTimeToLiveIfExpirePropertyOnSendToProcessor()
         $context
             ->expects($this->once())
             ->method('createQueue')
-            ->with('default')
+            ->with($this->getDefaultQueueTransportName())
             ->willReturn($queue)
         ;
         $context
@@ -665,7 +666,7 @@ public function testShouldSetInitPriorityIfPriorityPropertyOnSendToProcessor()
         $context
             ->expects($this->once())
             ->method('createQueue')
-            ->with('default')
+            ->with($this->getDefaultQueueTransportName())
             ->willReturn($queue)
         ;
         $context
@@ -737,7 +738,7 @@ public function testShouldSendCommandMessageToProcessorToDefaultQueue()
         $context
             ->expects($this->once())
             ->method('createQueue')
-            ->with('default')
+            ->with($this->getDefaultQueueTransportName())
             ->willReturn($queue)
         ;
         $context
@@ -781,7 +782,7 @@ public function testShouldSendCommandMessageToProcessorToCustomQueue()
         $context
             ->expects($this->once())
             ->method('createQueue')
-            ->with('custom')
+            ->with($this->getCustomQueueTransportName())
             ->willReturn($queue)
         ;
         $context
@@ -967,4 +968,29 @@ protected function createDummyConfig(): Config
     {
         return Config::create('aPrefix');
     }
+
+    protected function getDefaultQueueTransportName(): string
+    {
+        return 'aprefix.default';
+    }
+
+    protected function getCustomQueueTransportName(): string
+    {
+        return 'aprefix.custom';
+    }
+
+    protected function getPrefixAppFooQueueTransportName(): string
+    {
+        return 'aprefix.anappname.afooqueue';
+    }
+
+    protected function getPrefixFooQueueTransportName(): string
+    {
+        return 'aprefix.afooqueue';
+    }
+
+    protected function getAppFooQueueTransportName(): string
+    {
+        return 'anappname.afooqueue';
+    }
 }
diff --git a/Tests/Client/Driver/GpsDriverTest.php b/Tests/Client/Driver/GpsDriverTest.php
index dd10e47..15b8e63 100644
--- a/Tests/Client/Driver/GpsDriverTest.php
+++ b/Tests/Client/Driver/GpsDriverTest.php
@@ -40,8 +40,8 @@ public function testShouldSetupBroker()
         $routerTopic = new GpsTopic('');
         $routerQueue = new GpsQueue('');
 
-        $processorTopic = new GpsTopic('default');
-        $processorQueue = new GpsQueue('default');
+        $processorTopic = new GpsTopic($this->getDefaultQueueTransportName());
+        $processorQueue = new GpsQueue($this->getDefaultQueueTransportName());
 
         $context = $this->createContextMock();
         // setup router
@@ -63,14 +63,14 @@ public function testShouldSetupBroker()
         $context
             ->expects($this->at(3))
             ->method('createQueue')
-            ->with('default')
+            ->with($this->getDefaultQueueTransportName())
             ->willReturn($processorQueue)
         ;
         // setup processor queue
         $context
             ->expects($this->at(4))
             ->method('createTopic')
-            ->with('default')
+            ->with($this->getDefaultQueueTransportName())
             ->willReturn($processorTopic)
         ;
         $context
diff --git a/Tests/Client/Driver/RabbitMqStompDriverTest.php b/Tests/Client/Driver/RabbitMqStompDriverTest.php
index a153e46..8a866a8 100644
--- a/Tests/Client/Driver/RabbitMqStompDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqStompDriverTest.php
@@ -321,7 +321,7 @@ public function testShouldSetupBroker()
         $managementClient
             ->expects($this->at(3))
             ->method('declareQueue')
-            ->with('default', [
+            ->with('aprefix.default', [
                 'durable' => true,
                 'auto_delete' => false,
                 'arguments' => [
@@ -379,7 +379,7 @@ public function testShouldSetupBroker()
         $logger
             ->expects($this->at(3))
             ->method('debug')
-            ->with('[RabbitMqStompDriver] Declare processor queue: default')
+            ->with('[RabbitMqStompDriver] Declare processor queue: aprefix.default')
         ;
 
         $driver->setupBroker($logger);
@@ -395,7 +395,7 @@ public function testSetupBrokerShouldCreateDelayExchangeIfEnabled()
         $managementClient
             ->expects($this->at(4))
             ->method('declareExchange')
-            ->with('default.delayed', [
+            ->with('aprefix.default.delayed', [
                 'type' => 'x-delayed-message',
                 'durable' => true,
                 'auto_delete' => false,
@@ -407,7 +407,7 @@ public function testSetupBrokerShouldCreateDelayExchangeIfEnabled()
         $managementClient
             ->expects($this->at(5))
             ->method('bind')
-            ->with('default.delayed', 'default', 'default')
+            ->with('aprefix.default.delayed', 'aprefix.default', 'aprefix.default')
         ;
 
         $config = Config::create(
@@ -455,12 +455,12 @@ public function testSetupBrokerShouldCreateDelayExchangeIfEnabled()
         $logger
             ->expects($this->at(4))
             ->method('debug')
-            ->with('[RabbitMqStompDriver] Declare delay exchange: default.delayed')
+            ->with('[RabbitMqStompDriver] Declare delay exchange: aprefix.default.delayed')
         ;
         $logger
             ->expects($this->at(5))
             ->method('debug')
-            ->with('[RabbitMqStompDriver] Bind processor queue to delay exchange: default -> default.delayed')
+            ->with('[RabbitMqStompDriver] Bind processor queue to delay exchange: aprefix.default -> aprefix.default.delayed')
         ;
 
         $driver->setupBroker($logger);
diff --git a/Tests/Client/Driver/RdKafkaDriverTest.php b/Tests/Client/Driver/RdKafkaDriverTest.php
index f2b7c86..3c89fe1 100644
--- a/Tests/Client/Driver/RdKafkaDriverTest.php
+++ b/Tests/Client/Driver/RdKafkaDriverTest.php
@@ -3,292 +3,38 @@
 namespace Enqueue\Tests\Client\Driver;
 
 use Enqueue\Client\Config;
+use Enqueue\Client\Driver\GenericDriver;
 use Enqueue\Client\Driver\RdKafkaDriver;
 use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
 use Enqueue\RdKafka\RdKafkaContext;
 use Enqueue\RdKafka\RdKafkaMessage;
+use Enqueue\RdKafka\RdKafkaProducer;
 use Enqueue\RdKafka\RdKafkaTopic;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrContext;
+use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrProducer;
+use Interop\Queue\PsrQueue;
+use PHPUnit\Framework\TestCase;
 
 /**
  * @group rdkafka
  */
-class RdKafkaDriverTest extends \PHPUnit_Framework_TestCase
+class RdKafkaDriverTest extends TestCase
 {
     use ClassExtensionTrait;
+    use GenericDriverTestsTrait;
 
     public function testShouldImplementsDriverInterface()
     {
         $this->assertClassImplements(DriverInterface::class, RdKafkaDriver::class);
     }
 
-    public function testCouldBeConstructedWithRequiredArguments()
+    public function testShouldBeSubClassOfGenericDriver()
     {
-        new RdKafkaDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-    }
-
-    public function testShouldReturnConfigObject()
-    {
-        $config = $this->createDummyConfig();
-
-        $driver = new RdKafkaDriver(
-            $this->createPsrContextMock(),
-            $config,
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->assertSame($config, $driver->getConfig());
-    }
-
-    public function testShouldCreateAndReturnQueueInstance()
-    {
-        $expectedQueue = new RdKafkaTopic('aName');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix.afooqueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new RdKafkaDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aFooQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
-    {
-        $expectedQueue = new RdKafkaTopic('aName');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aBarQueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new RdKafkaDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aBarQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldConvertTransportMessageToClientMessage()
-    {
-        $transportMessage = new RdKafkaMessage();
-        $transportMessage->setBody('body');
-        $transportMessage->setHeaders(['hkey' => 'hval']);
-        $transportMessage->setProperties(['key' => 'val']);
-        $transportMessage->setHeader('content_type', 'ContentType');
-        $transportMessage->setMessageId('MessageId');
-        $transportMessage->setTimestamp(1000);
-
-        $driver = new RdKafkaDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $clientMessage = $driver->createClientMessage($transportMessage);
-
-        $this->assertInstanceOf(Message::class, $clientMessage);
-        $this->assertSame('body', $clientMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'content_type' => 'ContentType',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-        ], $clientMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-        ], $clientMessage->getProperties());
-        $this->assertSame('MessageId', $clientMessage->getMessageId());
-        $this->assertSame('ContentType', $clientMessage->getContentType());
-        $this->assertSame(1000, $clientMessage->getTimestamp());
-
-        $this->assertNull($clientMessage->getExpire());
-    }
-
-    public function testShouldConvertClientMessageToTransportMessage()
-    {
-        $clientMessage = new Message();
-        $clientMessage->setBody('body');
-        $clientMessage->setHeaders(['hkey' => 'hval']);
-        $clientMessage->setProperties(['key' => 'val']);
-        $clientMessage->setContentType('ContentType');
-        $clientMessage->setExpire(123);
-        $clientMessage->setMessageId('MessageId');
-        $clientMessage->setTimestamp(1000);
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn(new RdKafkaMessage())
-        ;
-
-        $driver = new RdKafkaDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $transportMessage = $driver->createTransportMessage($clientMessage);
-
-        $this->assertInstanceOf(RdKafkaMessage::class, $transportMessage);
-        $this->assertSame('body', $transportMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'content_type' => 'ContentType',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-            'reply_to' => null,
-            'correlation_id' => '',
-        ], $transportMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-        ], $transportMessage->getProperties());
-        $this->assertSame('MessageId', $transportMessage->getMessageId());
-        $this->assertSame(1000, $transportMessage->getTimestamp());
-    }
-
-    public function testShouldSendMessageToRouter()
-    {
-        $topic = new RdKafkaTopic('queue-name');
-        $transportMessage = new RdKafkaMessage();
-
-        $producer = $this->createPsrProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createTopic')
-            ->with('aprefix.router')
-            ->willReturn($topic)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new RdKafkaDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-
-        $driver->sendToRouter($message);
-    }
-
-    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
-    {
-        $driver = new RdKafkaDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Topic name parameter is required but is not set');
-
-        $driver->sendToRouter(new Message());
-    }
-
-    public function testShouldSendMessageToProcessor()
-    {
-        $queue = new RdKafkaTopic('queue-name');
-        $transportMessage = new RdKafkaMessage();
-
-        $producer = $this->createPsrProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->willReturn($queue)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new RdKafkaDriver(
-            $context,
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
-
-        $driver->sendToProcessor($message);
-    }
-
-    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
-    {
-        $driver = new RdKafkaDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Processor name parameter is required but is not set');
-
-        $driver->sendToProcessor(new Message());
-    }
-
-    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
-    {
-        $driver = new RdKafkaDriver(
-            $this->createPsrContextMock(),
-            $this->createDummyConfig(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Queue name parameter is required but is not set');
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-
-        $driver->sendToProcessor($message);
+        $this->assertClassExtends(GenericDriver::class, RdKafkaDriver::class);
     }
 
     public function testShouldSetupBroker()
@@ -298,7 +44,7 @@ public function testShouldSetupBroker()
 
         $processorTopic = new RdKafkaTopic('');
 
-        $context = $this->createPsrContextMock();
+        $context = $this->createContextMock();
 
         $context
             ->expects($this->at(0))
@@ -316,46 +62,60 @@ public function testShouldSetupBroker()
             ->willReturn($processorTopic)
         ;
 
-        $meta = new QueueMetaRegistry($this->createDummyConfig(), [
-            'default' => [],
-        ]);
-
         $driver = new RdKafkaDriver(
             $context,
             $this->createDummyConfig(),
-            $meta
+            new RouteCollection([
+                new Route('topic', Route::TOPIC, 'processor'),
+            ])
         );
 
         $driver->setupBroker();
     }
 
+    protected function createDriver(...$args): DriverInterface
+    {
+        return new RdKafkaDriver(...$args);
+    }
+
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|RdKafkaContext
+     * @return RdKafkaContext
      */
-    private function createPsrContextMock()
+    protected function createContextMock(): PsrContext
     {
         return $this->createMock(RdKafkaContext::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProducer
+     * @return RdKafkaProducer
      */
-    private function createPsrProducerMock()
+    protected function createProducerMock(): PsrProducer
     {
-        return $this->createMock(PsrProducer::class);
+        return $this->createMock(RdKafkaProducer::class);
     }
 
     /**
-     * @return QueueMetaRegistry
+     * @return RdKafkaTopic
      */
-    private function createDummyQueueMetaRegistry()
+    protected function createQueue(string $name): PsrQueue
     {
-        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
-        $registry->add('default');
-        $registry->add('aFooQueue');
-        $registry->add('aBarQueue', 'aBarQueue');
+        return new RdKafkaTopic($name);
+    }
 
-        return $registry;
+    /**
+     * @return RdKafkaTopic
+     */
+    protected function createTopic(string $name): RdKafkaTopic
+    {
+        return new RdKafkaTopic($name);
+    }
+
+    /**
+     * @return RdKafkaMessage
+     */
+    protected function createMessage(): PsrMessage
+    {
+        return new RdKafkaMessage();
     }
 
     /**
diff --git a/Tests/Client/Driver/RedisDriverTest.php b/Tests/Client/Driver/RedisDriverTest.php
deleted file mode 100644
index 1dd5950..0000000
--- a/Tests/Client/Driver/RedisDriverTest.php
+++ /dev/null
@@ -1,366 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Client\Driver;
-
-use Enqueue\Client\Config;
-use Enqueue\Client\Driver\RedisDriver;
-use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\MessagePriority;
-use Enqueue\Client\Meta\QueueMetaRegistry;
-use Enqueue\Redis\RedisContext;
-use Enqueue\Redis\RedisDestination;
-use Enqueue\Redis\RedisMessage;
-use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrProducer;
-use PHPUnit\Framework\TestCase;
-
-class RedisDriverTest extends TestCase
-{
-    use ClassExtensionTrait;
-
-    public function testShouldImplementsDriverInterface()
-    {
-        $this->assertClassImplements(DriverInterface::class, RedisDriver::class);
-    }
-
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new RedisDriver(
-            $this->createPsrContextMock(),
-            Config::create(),
-            $this->createDummyQueueMetaRegistry()
-        );
-    }
-
-    public function testShouldReturnConfigObject()
-    {
-        $config = Config::create();
-
-        $driver = new RedisDriver($this->createPsrContextMock(), $config, $this->createDummyQueueMetaRegistry());
-
-        $this->assertSame($config, $driver->getConfig());
-    }
-
-    public function testShouldCreateAndReturnQueueInstance()
-    {
-        $expectedQueue = new RedisDestination('aName');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix.afooqueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new RedisDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aFooQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
-    {
-        $expectedQueue = new RedisDestination('aName');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aBarQueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new RedisDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aBarQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldConvertTransportMessageToClientMessage()
-    {
-        $transportMessage = new RedisMessage();
-        $transportMessage->setBody('body');
-        $transportMessage->setHeaders(['hkey' => 'hval']);
-        $transportMessage->setProperties(['key' => 'val']);
-        $transportMessage->setHeader('content_type', 'ContentType');
-        $transportMessage->setMessageId('MessageId');
-        $transportMessage->setTimestamp(1000);
-        $transportMessage->setReplyTo('theReplyTo');
-        $transportMessage->setCorrelationId('theCorrelationId');
-        $transportMessage->setReplyTo('theReplyTo');
-        $transportMessage->setCorrelationId('theCorrelationId');
-
-        $driver = new RedisDriver(
-            $this->createPsrContextMock(),
-            Config::create(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $clientMessage = $driver->createClientMessage($transportMessage);
-
-        $this->assertInstanceOf(Message::class, $clientMessage);
-        $this->assertSame('body', $clientMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'content_type' => 'ContentType',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-            'reply_to' => 'theReplyTo',
-            'correlation_id' => 'theCorrelationId',
-        ], $clientMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-        ], $clientMessage->getProperties());
-        $this->assertSame('MessageId', $clientMessage->getMessageId());
-        $this->assertSame('ContentType', $clientMessage->getContentType());
-        $this->assertSame(1000, $clientMessage->getTimestamp());
-        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
-
-        $this->assertNull($clientMessage->getExpire());
-        $this->assertSame(MessagePriority::NORMAL, $clientMessage->getPriority());
-    }
-
-    public function testShouldConvertClientMessageToTransportMessage()
-    {
-        $clientMessage = new Message();
-        $clientMessage->setBody('body');
-        $clientMessage->setHeaders(['hkey' => 'hval']);
-        $clientMessage->setProperties(['key' => 'val']);
-        $clientMessage->setContentType('ContentType');
-        $clientMessage->setExpire(123);
-        $clientMessage->setPriority(MessagePriority::VERY_HIGH);
-        $clientMessage->setMessageId('MessageId');
-        $clientMessage->setTimestamp(1000);
-        $clientMessage->setReplyTo('theReplyTo');
-        $clientMessage->setCorrelationId('theCorrelationId');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn(new RedisMessage())
-        ;
-
-        $driver = new RedisDriver(
-            $context,
-            Config::create(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $transportMessage = $driver->createTransportMessage($clientMessage);
-
-        $this->assertInstanceOf(RedisMessage::class, $transportMessage);
-        $this->assertSame('body', $transportMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'content_type' => 'ContentType',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-            'reply_to' => 'theReplyTo',
-            'correlation_id' => 'theCorrelationId',
-        ], $transportMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-        ], $transportMessage->getProperties());
-        $this->assertSame('MessageId', $transportMessage->getMessageId());
-        $this->assertSame(1000, $transportMessage->getTimestamp());
-        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
-    }
-
-    public function testShouldSendMessageToRouterQueue()
-    {
-        $topic = new RedisDestination('aDestinationName');
-        $transportMessage = new RedisMessage();
-        $config = $this->createDummyConfig();
-
-        $producer = $this->createPsrProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix.default')
-            ->willReturn($topic)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new RedisDriver(
-            $context,
-            $config,
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-
-        $driver->sendToRouter($message);
-    }
-
-    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
-    {
-        $driver = new RedisDriver(
-            $this->createPsrContextMock(),
-            Config::create(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Topic name parameter is required but is not set');
-
-        $driver->sendToRouter(new Message());
-    }
-
-    public function testShouldSendMessageToProcessor()
-    {
-        $queue = new RedisDestination('aDestinationName');
-        $transportMessage = new RedisMessage();
-
-        $producer = $this->createPsrProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->willReturn($queue)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $driver = new RedisDriver(
-            $context,
-            Config::create(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aFooQueue');
-
-        $driver->sendToProcessor($message);
-    }
-
-    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
-    {
-        $driver = new RedisDriver(
-            $this->createPsrContextMock(),
-            Config::create(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Processor name parameter is required but is not set');
-
-        $driver->sendToProcessor(new Message());
-    }
-
-    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
-    {
-        $driver = new RedisDriver(
-            $this->createPsrContextMock(),
-            Config::create(),
-            $this->createDummyQueueMetaRegistry()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Queue name parameter is required but is not set');
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-
-        $driver->sendToProcessor($message);
-    }
-
-    public function testShouldDoNothingOnSetupBroker()
-    {
-        $context = $this->createPsrContextMock();
-        // setup router
-        $context
-            ->expects($this->never())
-            ->method('createTopic')
-        ;
-        $context
-            ->expects($this->never())
-            ->method('createQueue')
-        ;
-
-        $meta = new QueueMetaRegistry(Config::create(), [
-            'default' => [],
-        ]);
-
-        $driver = new RedisDriver(
-            $context,
-            Config::create(),
-            $meta
-        );
-
-        $driver->setupBroker();
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|RedisContext
-     */
-    private function createPsrContextMock()
-    {
-        return $this->createMock(RedisContext::class);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProducer
-     */
-    private function createPsrProducerMock()
-    {
-        return $this->createMock(PsrProducer::class);
-    }
-
-    /**
-     * @return QueueMetaRegistry
-     */
-    private function createDummyQueueMetaRegistry()
-    {
-        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
-        $registry->add('default');
-        $registry->add('aFooQueue');
-        $registry->add('aBarQueue', 'aBarQueue');
-
-        return $registry;
-    }
-
-    /**
-     * @return Config
-     */
-    private function createDummyConfig()
-    {
-        return Config::create('aPrefix');
-    }
-}
diff --git a/Tests/Client/Driver/SqsDriverTest.php b/Tests/Client/Driver/SqsDriverTest.php
index d64df83..3888326 100644
--- a/Tests/Client/Driver/SqsDriverTest.php
+++ b/Tests/Client/Driver/SqsDriverTest.php
@@ -2,309 +2,36 @@
 
 namespace Enqueue\Tests\Client\Driver;
 
-use Enqueue\Client\Config;
+use Enqueue\Client\Driver\GenericDriver;
 use Enqueue\Client\Driver\SqsDriver;
 use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Message;
-use Enqueue\Client\MessagePriority;
-use Enqueue\Client\Meta\QueueMeta;
-use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
 use Enqueue\Sqs\SqsContext;
 use Enqueue\Sqs\SqsDestination;
 use Enqueue\Sqs\SqsMessage;
+use Enqueue\Sqs\SqsProducer;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\PsrContext;
+use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrProducer;
+use Interop\Queue\PsrQueue;
+use Interop\Queue\PsrTopic;
 use PHPUnit\Framework\TestCase;
 
 class SqsDriverTest extends TestCase
 {
     use ClassExtensionTrait;
+    use GenericDriverTestsTrait;
 
     public function testShouldImplementsDriverInterface()
     {
         $this->assertClassImplements(DriverInterface::class, SqsDriver::class);
     }
 
-    public function testCouldBeConstructedWithRequiredArguments()
+    public function testShouldBeSubClassOfGenericDriver()
     {
-        new SqsDriver(
-            $this->createPsrContextMock(),
-            Config::create(),
-            $this->createQueueMetaRegistryMock()
-        );
-    }
-
-    public function testShouldReturnConfigObject()
-    {
-        $config = Config::create();
-
-        $driver = new SqsDriver($this->createPsrContextMock(), $config, $this->createQueueMetaRegistryMock());
-
-        $this->assertSame($config, $driver->getConfig());
-    }
-
-    public function testShouldCreateAndReturnQueueInstance()
-    {
-        $expectedQueue = new SqsDestination('aQueueName');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aprefix_dot_afooqueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new SqsDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-        $queue = $driver->createQueue('aFooQueue');
-
-        $this->assertSame($expectedQueue, $queue);
-        $this->assertSame('aQueueName', $queue->getQueueName());
-    }
-
-    public function testShouldCreateAndReturnQueueInstanceWithHardcodedTransportName()
-    {
-        $expectedQueue = new SqsDestination('aQueueName');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('aBarQueue')
-            ->willReturn($expectedQueue)
-        ;
-
-        $driver = new SqsDriver($context, $this->createDummyConfig(), $this->createDummyQueueMetaRegistry());
-
-        $queue = $driver->createQueue('aBarQueue');
-        $this->assertSame($expectedQueue, $queue);
-    }
-
-    public function testShouldConvertTransportMessageToClientMessage()
-    {
-        $transportMessage = new SqsMessage();
-        $transportMessage->setBody('body');
-        $transportMessage->setHeaders(['hkey' => 'hval']);
-        $transportMessage->setProperties(['key' => 'val']);
-        $transportMessage->setHeader('content_type', 'ContentType');
-        $transportMessage->setMessageId('MessageId');
-        $transportMessage->setTimestamp(1000);
-        $transportMessage->setReplyTo('theReplyTo');
-        $transportMessage->setCorrelationId('theCorrelationId');
-        $transportMessage->setReplyTo('theReplyTo');
-        $transportMessage->setCorrelationId('theCorrelationId');
-
-        $driver = new SqsDriver(
-            $this->createPsrContextMock(),
-            Config::create(),
-            $this->createQueueMetaRegistryMock()
-        );
-
-        $clientMessage = $driver->createClientMessage($transportMessage);
-
-        $this->assertInstanceOf(Message::class, $clientMessage);
-        $this->assertSame('body', $clientMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'content_type' => 'ContentType',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-            'reply_to' => 'theReplyTo',
-            'correlation_id' => 'theCorrelationId',
-        ], $clientMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-        ], $clientMessage->getProperties());
-        $this->assertSame('MessageId', $clientMessage->getMessageId());
-        $this->assertSame('ContentType', $clientMessage->getContentType());
-        $this->assertSame(1000, $clientMessage->getTimestamp());
-        $this->assertSame('theReplyTo', $clientMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $clientMessage->getCorrelationId());
-
-        $this->assertNull($clientMessage->getExpire());
-        $this->assertSame(MessagePriority::NORMAL, $clientMessage->getPriority());
-    }
-
-    public function testShouldConvertClientMessageToTransportMessage()
-    {
-        $clientMessage = new Message();
-        $clientMessage->setBody('body');
-        $clientMessage->setHeaders(['hkey' => 'hval']);
-        $clientMessage->setProperties(['key' => 'val']);
-        $clientMessage->setContentType('ContentType');
-        $clientMessage->setExpire(123);
-        $clientMessage->setPriority(MessagePriority::VERY_HIGH);
-        $clientMessage->setMessageId('MessageId');
-        $clientMessage->setTimestamp(1000);
-        $clientMessage->setReplyTo('theReplyTo');
-        $clientMessage->setCorrelationId('theCorrelationId');
-
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn(new SqsMessage())
-        ;
-
-        $driver = new SqsDriver(
-            $context,
-            Config::create(),
-            $this->createQueueMetaRegistryMock()
-        );
-
-        $transportMessage = $driver->createTransportMessage($clientMessage);
-
-        $this->assertInstanceOf(SqsMessage::class, $transportMessage);
-        $this->assertSame('body', $transportMessage->getBody());
-        $this->assertSame([
-            'hkey' => 'hval',
-            'content_type' => 'ContentType',
-            'message_id' => 'MessageId',
-            'timestamp' => 1000,
-            'reply_to' => 'theReplyTo',
-            'correlation_id' => 'theCorrelationId',
-        ], $transportMessage->getHeaders());
-        $this->assertSame([
-            'key' => 'val',
-        ], $transportMessage->getProperties());
-        $this->assertSame('MessageId', $transportMessage->getMessageId());
-        $this->assertSame(1000, $transportMessage->getTimestamp());
-        $this->assertSame('theReplyTo', $transportMessage->getReplyTo());
-        $this->assertSame('theCorrelationId', $transportMessage->getCorrelationId());
-    }
-
-    public function testShouldSendMessageToRouterQueue()
-    {
-        $topic = new SqsDestination('aDestinationName');
-        $transportMessage = new SqsMessage();
-        $config = $this->createDummyConfig();
-
-        $producer = $this->createPsrProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->with('theTransportName')
-            ->willReturn($topic)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $meta = $this->createQueueMetaRegistryMock();
-        $meta
-            ->expects($this->once())
-            ->method('getQueueMeta')
-            ->willReturn(new QueueMeta('theClientName', 'theTransportName'))
-        ;
-
-        $driver = new SqsDriver($context, $config, $meta);
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-
-        $driver->sendToRouter($message);
-    }
-
-    public function testShouldThrowExceptionIfTopicParameterIsNotSet()
-    {
-        $driver = new SqsDriver(
-            $this->createPsrContextMock(),
-            Config::create(),
-            $this->createQueueMetaRegistryMock()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Topic name parameter is required but is not set');
-
-        $driver->sendToRouter(new Message());
-    }
-
-    public function testShouldSendMessageToProcessor()
-    {
-        $queue = new SqsDestination('aDestinationName');
-        $transportMessage = new SqsMessage();
-
-        $producer = $this->createPsrProducerMock();
-        $producer
-            ->expects($this->once())
-            ->method('send')
-            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createPsrContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createQueue')
-            ->willReturn($queue)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createProducer')
-            ->willReturn($producer)
-        ;
-        $context
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($transportMessage)
-        ;
-
-        $meta = $this->createQueueMetaRegistryMock();
-        $meta
-            ->expects($this->once())
-            ->method('getQueueMeta')
-            ->willReturn(new QueueMeta('theClientName', 'theTransportName'))
-        ;
-
-        $driver = new SqsDriver($context, Config::create(), $meta);
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'queue');
-
-        $driver->sendToProcessor($message);
-    }
-
-    public function testShouldThrowExceptionIfProcessorNameParameterIsNotSet()
-    {
-        $driver = new SqsDriver(
-            $this->createPsrContextMock(),
-            Config::create(),
-            $this->createQueueMetaRegistryMock()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Processor name parameter is required but is not set');
-
-        $driver->sendToProcessor(new Message());
-    }
-
-    public function testShouldThrowExceptionIfProcessorQueueNameParameterIsNotSet()
-    {
-        $driver = new SqsDriver(
-            $this->createPsrContextMock(),
-            Config::create(),
-            $this->createQueueMetaRegistryMock()
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Queue name parameter is required but is not set');
-
-        $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
-
-        $driver->sendToProcessor($message);
+        $this->assertClassExtends(GenericDriver::class, SqsDriver::class);
     }
 
     public function testShouldSetupBroker()
@@ -312,11 +39,12 @@ public function testShouldSetupBroker()
         $routerQueue = new SqsDestination('');
         $processorQueue = new SqsDestination('');
 
-        $context = $this->createPsrContextMock();
+        $context = $this->createContextMock();
         // setup router
         $context
             ->expects($this->at(0))
             ->method('createQueue')
+            ->with('aprefix_dot_default')
             ->willReturn($routerQueue)
         ;
         $context
@@ -328,6 +56,7 @@ public function testShouldSetupBroker()
         $context
             ->expects($this->at(2))
             ->method('createQueue')
+            ->with('aprefix_dot_default')
             ->willReturn($processorQueue)
         ;
         $context
@@ -336,67 +65,84 @@ public function testShouldSetupBroker()
             ->with($this->identicalTo($processorQueue))
         ;
 
-        $metaRegistry = $this->createQueueMetaRegistryMock();
-        $metaRegistry
-            ->expects($this->once())
-            ->method('getQueuesMeta')
-            ->willReturn([new QueueMeta('theClientName', 'theTransportName')])
-        ;
-        $metaRegistry
-            ->expects($this->exactly(2))
-            ->method('getQueueMeta')
-            ->willReturn(new QueueMeta('theClientName', 'theTransportName'))
-        ;
-
-        $driver = new SqsDriver($context, $this->createDummyConfig(), $metaRegistry);
+        $driver = new SqsDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('topic', Route::TOPIC, 'processor'),
+            ])
+        );
 
         $driver->setupBroker();
     }
 
+    protected function createDriver(...$args): DriverInterface
+    {
+        return new SqsDriver(...$args);
+    }
+
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|SqsContext
+     * @return SqsContext
      */
-    private function createPsrContextMock()
+    protected function createContextMock(): PsrContext
     {
         return $this->createMock(SqsContext::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProducer
+     * @return SqsProducer
      */
-    private function createPsrProducerMock()
+    protected function createProducerMock(): PsrProducer
     {
-        return $this->createMock(PsrProducer::class);
+        return $this->createMock(SqsProducer::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|QueueMetaRegistry
+     * @return SqsDestination
      */
-    private function createQueueMetaRegistryMock()
+    protected function createQueue(string $name): PsrQueue
     {
-        return $this->createMock(QueueMetaRegistry::class);
+        return new SqsDestination($name);
     }
 
-    private function createDummyConfig(): Config
+    /**
+     * @return SqsDestination
+     */
+    protected function createTopic(string $name): PsrTopic
     {
-        return Config::create(
-            'aPrefix',
-            '',
-            'aRouterTopic',
-            'aRouterQueue',
-            'aDefaultQueue');
+        return new SqsDestination($name);
     }
 
     /**
-     * @return QueueMetaRegistry
+     * @return SqsMessage
      */
-    private function createDummyQueueMetaRegistry()
+    protected function createMessage(): PsrMessage
+    {
+        return new SqsMessage();
+    }
+
+    protected function getPrefixAppFooQueueTransportName(): string
     {
-        $registry = new QueueMetaRegistry($this->createDummyConfig(), []);
-        $registry->add('default');
-        $registry->add('aFooQueue');
-        $registry->add('aBarQueue', 'aBarQueue');
+        return 'aprefix_dot_anappname_dot_afooqueue';
+    }
+
+    protected function getPrefixFooQueueTransportName(): string
+    {
+        return 'aprefix_dot_afooqueue';
+    }
 
-        return $registry;
+    protected function getAppFooQueueTransportName(): string
+    {
+        return 'anappname_dot_afooqueue';
+    }
+
+    protected function getDefaultQueueTransportName(): string
+    {
+        return 'aprefix_dot_default';
+    }
+
+    protected function getCustomQueueTransportName(): string
+    {
+        return 'aprefix_dot_custom';
     }
 }
diff --git a/Tests/Client/DriverFactoryTest.php b/Tests/Client/DriverFactoryTest.php
index e1b9679..a51adb3 100644
--- a/Tests/Client/DriverFactoryTest.php
+++ b/Tests/Client/DriverFactoryTest.php
@@ -12,13 +12,12 @@
 use Enqueue\Client\Driver\RabbitMqDriver;
 use Enqueue\Client\Driver\RabbitMqStompDriver;
 use Enqueue\Client\Driver\RdKafkaDriver;
-use Enqueue\Client\Driver\RedisDriver;
 use Enqueue\Client\Driver\SqsDriver;
 use Enqueue\Client\Driver\StompDriver;
 use Enqueue\Client\DriverFactory;
 use Enqueue\Client\DriverFactoryInterface;
-use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\Client\Resources;
+use Enqueue\Client\RouteCollection;
 use Enqueue\Dbal\DbalConnectionFactory;
 use Enqueue\Dbal\DbalContext;
 use Enqueue\Fs\FsConnectionFactory;
@@ -58,9 +57,9 @@ public function testShouldBeFinal()
         $this->assertTrue($rc->isFinal());
     }
 
-    public function testCouldBeConstructedWithConfigAndQueueMetaAsArguments()
+    public function testCouldBeConstructedWithConfigAndRouteCollectionAsArguments()
     {
-        new DriverFactory($this->createConfigMock(), $this->createQueueMetaRegistryMock());
+        new DriverFactory($this->createConfigMock(), new RouteCollection([]));
     }
 
     public function testThrowIfPackageThatSupportSchemeNotInstalled()
@@ -72,7 +71,7 @@ public function testThrowIfPackageThatSupportSchemeNotInstalled()
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('To use given scheme "scheme5b7aa7d7cd213" a package has to be installed. Run "composer req thePackage theOtherPackage" to add it.');
-        $factory = new DriverFactory($this->createConfigMock(), $this->createQueueMetaRegistryMock());
+        $factory = new DriverFactory($this->createConfigMock(), new RouteCollection([]));
 
         $factory->create($this->createConnectionFactoryMock(), $scheme.'://foo', []);
     }
@@ -84,7 +83,7 @@ public function testThrowIfSchemeIsNotKnown()
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('A given scheme "scheme5b7aa862e70a5" is not supported. Maybe it is a custom driver, make sure you registered it with "Enqueue\Client\Resources::addDriver".');
 
-        $factory = new DriverFactory($this->createConfigMock(), $this->createQueueMetaRegistryMock());
+        $factory = new DriverFactory($this->createConfigMock(), new RouteCollection([]));
 
         $factory->create($this->createConnectionFactoryMock(), $scheme.'://foo', []);
     }
@@ -94,7 +93,7 @@ public function testThrowIfDsnInvalid()
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('The DSN is invalid. It does not have scheme separator ":".');
 
-        $factory = new DriverFactory($this->createConfigMock(), $this->createQueueMetaRegistryMock());
+        $factory = new DriverFactory($this->createConfigMock(), new RouteCollection([]));
 
         $factory->create($this->createConnectionFactoryMock(), 'invalidDsn', []);
     }
@@ -116,7 +115,7 @@ public function testReturnsExpectedFactories(
             ->willReturn($this->createMock($contextClass))
         ;
 
-        $driverFactory = new DriverFactory($this->createConfigMock(), $this->createQueueMetaRegistryMock());
+        $driverFactory = new DriverFactory($this->createConfigMock(), new RouteCollection([]));
 
         $driver = $driverFactory->create($connectionFactoryMock, $dsn, $conifg);
 
@@ -144,9 +143,9 @@ public static function provideDSN()
 
         yield ['kafka:', RdKafkaConnectionFactory::class, RdKafkaContext::class, [], RdKafkaDriver::class];
 
-        yield ['redis:', RedisConnectionFactory::class, RedisContext::class, [], RedisDriver::class];
+        yield ['redis:', RedisConnectionFactory::class, RedisContext::class, [], GenericDriver::class];
 
-        yield ['redis+predis:', RedisConnectionFactory::class, RedisContext::class, [], RedisDriver::class];
+        yield ['redis+predis:', RedisConnectionFactory::class, RedisContext::class, [], GenericDriver::class];
 
         yield ['sqs:', SqsConnectionFactory::class, SqsContext::class, [], SqsDriver::class];
 
@@ -166,9 +165,4 @@ private function createConfigMock(): Config
     {
         return $this->createMock(Config::class);
     }
-
-    private function createQueueMetaRegistryMock()
-    {
-        return $this->createMock(QueueMetaRegistry::class);
-    }
 }
diff --git a/Tests/Client/ResourcesTest.php b/Tests/Client/ResourcesTest.php
index 08feabf..7311f31 100644
--- a/Tests/Client/ResourcesTest.php
+++ b/Tests/Client/ResourcesTest.php
@@ -2,8 +2,8 @@
 
 namespace Enqueue\Tests\Client;
 
+use Enqueue\Client\Driver\AmqpDriver;
 use Enqueue\Client\Driver\RabbitMqDriver;
-use Enqueue\Client\Driver\RedisDriver;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Resources;
 use PHPUnit\Framework\TestCase;
@@ -29,17 +29,21 @@ public function testShouldGetAvailableDriverInExpectedFormat()
         $availableDrivers = Resources::getAvailableDrivers();
 
         $this->assertInternalType('array', $availableDrivers);
-        $this->assertArrayHasKey(RedisDriver::class, $availableDrivers);
+        $this->assertGreaterThan(0, count($availableDrivers));
+
+        $driverInfo = $availableDrivers[0];
+
+        $this->assertArrayHasKey('factoryClass', $driverInfo);
+        $this->assertSame(AmqpDriver::class, $driverInfo['factoryClass']);
 
-        $driverInfo = $availableDrivers[RedisDriver::class];
         $this->assertArrayHasKey('schemes', $driverInfo);
-        $this->assertSame(['redis'], $driverInfo['schemes']);
+        $this->assertSame(['amqp', 'amqps'], $driverInfo['schemes']);
 
         $this->assertArrayHasKey('requiredSchemeExtensions', $driverInfo);
         $this->assertSame([], $driverInfo['requiredSchemeExtensions']);
 
         $this->assertArrayHasKey('packages', $driverInfo);
-        $this->assertSame(['enqueue/enqueue', 'enqueue/redis'], $driverInfo['packages']);
+        $this->assertSame(['enqueue/enqueue', 'enqueue/amqp-bunny'], $driverInfo['packages']);
     }
 
     public function testShouldGetAvailableDriverWithRequiredExtensionInExpectedFormat()
@@ -47,9 +51,13 @@ public function testShouldGetAvailableDriverWithRequiredExtensionInExpectedForma
         $availableDrivers = Resources::getAvailableDrivers();
 
         $this->assertInternalType('array', $availableDrivers);
-        $this->assertArrayHasKey(RabbitMqDriver::class, $availableDrivers);
+        $this->assertGreaterThan(0, count($availableDrivers));
+
+        $driverInfo = $availableDrivers[1];
+
+        $this->assertArrayHasKey('factoryClass', $driverInfo);
+        $this->assertSame(RabbitMqDriver::class, $driverInfo['factoryClass']);
 
-        $driverInfo = $availableDrivers[RabbitMqDriver::class];
         $this->assertArrayHasKey('schemes', $driverInfo);
         $this->assertSame(['amqp', 'amqps'], $driverInfo['schemes']);
 
@@ -62,17 +70,24 @@ public function testShouldGetAvailableDriverWithRequiredExtensionInExpectedForma
 
     public function testShouldGetKnownDriversInExpectedFormat()
     {
-        $knownDrivers = Resources::getKnownDrivers();
+        $knownDrivers = Resources::getAvailableDrivers();
 
         $this->assertInternalType('array', $knownDrivers);
-        $this->assertArrayHasKey(RedisDriver::class, $knownDrivers);
+        $this->assertGreaterThan(0, count($knownDrivers));
+
+        $driverInfo = $knownDrivers[0];
+
+        $this->assertArrayHasKey('factoryClass', $driverInfo);
+        $this->assertSame(AmqpDriver::class, $driverInfo['factoryClass']);
 
-        $driverInfo = $knownDrivers[RedisDriver::class];
         $this->assertArrayHasKey('schemes', $driverInfo);
-        $this->assertSame(['redis'], $driverInfo['schemes']);
+        $this->assertSame(['amqp', 'amqps'], $driverInfo['schemes']);
 
         $this->assertArrayHasKey('requiredSchemeExtensions', $driverInfo);
         $this->assertSame([], $driverInfo['requiredSchemeExtensions']);
+
+        $this->assertArrayHasKey('packages', $driverInfo);
+        $this->assertSame(['enqueue/enqueue', 'enqueue/amqp-bunny'], $driverInfo['packages']);
     }
 
     public function testThrowsIfDriverClassNotImplementDriverFactoryInterfaceOnAddDriver()
@@ -107,14 +122,11 @@ public function testShouldAllowRegisterDriverThatIsNotInstalled()
     {
         Resources::addDriver('theDriverClass', ['foo'], ['barExtension'], ['foo']);
 
-        $knownDrivers = Resources::getKnownDrivers();
-        $this->assertInternalType('array', $knownDrivers);
-        $this->assertArrayHasKey('theDriverClass', $knownDrivers);
+        $availableDrivers = Resources::getKnownDrivers();
 
-        $availableDrivers = Resources::getAvailableDrivers();
+        $driverInfo = end($availableDrivers);
 
-        $this->assertInternalType('array', $availableDrivers);
-        $this->assertArrayNotHasKey('theDriverClass', $availableDrivers);
+        $this->assertSame('theDriverClass', $driverInfo['factoryClass']);
     }
 
     public function testShouldAllowGetPreviouslyRegisteredDriver()
@@ -130,10 +142,11 @@ public function testShouldAllowGetPreviouslyRegisteredDriver()
 
         $availableDrivers = Resources::getAvailableDrivers();
 
-        $this->assertInternalType('array', $availableDrivers);
-        $this->assertArrayHasKey($driverClass, $availableDrivers);
+        $driverInfo = end($availableDrivers);
+
+        $this->assertArrayHasKey('factoryClass', $driverInfo);
+        $this->assertSame($driverClass, $driverInfo['factoryClass']);
 
-        $driverInfo = $availableDrivers[$driverClass];
         $this->assertArrayHasKey('schemes', $driverInfo);
         $this->assertSame(['fooscheme', 'barscheme'], $driverInfo['schemes']);
 

From 9225eb36bd0942ca06ad8ea4214fcf6df6c2447e Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 21 Sep 2018 15:56:41 +0300
Subject: [PATCH 128/337] upd drivers

---
 .../SetRouterPropertiesExtension.php          |   1 -
 Client/Driver/AmqpDriver.php                  |   4 +-
 Client/Driver/GenericDriver.php               |  50 +++++----
 Client/Driver/RabbitMqDriver.php              |   4 +-
 Client/Driver/RabbitMqStompDriver.php         |  22 ++--
 Client/Driver/StompDriver.php                 |   4 +-
 Client/DriverPreSend.php                      |   7 +-
 Client/Message.php                            |  10 +-
 Client/PostSend.php                           |   7 +-
 Client/Producer.php                           |  27 ++---
 Client/Resources.php                          |  12 ++
 Client/TraceableProducer.php                  |   3 +-
 .../SetRouterPropertiesExtensionTest.php      |   1 -
 .../Client/Driver/GenericDriverTestsTrait.php | 104 ++++++++++++++----
 Tests/Client/DriverFactoryTest.php            |   8 ++
 Tests/Client/DriverPreSendTest.php            |   1 -
 Tests/Client/PostSendTest.php                 |   1 -
 Tests/Client/ProducerSendCommandTest.php      |  28 ++---
 Tests/Client/ProducerSendEventTest.php        |  45 ++------
 Tests/Client/TraceableProducerTest.php        |   7 +-
 20 files changed, 192 insertions(+), 154 deletions(-)

diff --git a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
index d294a9d..27ef6f3 100644
--- a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
+++ b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
@@ -43,6 +43,5 @@ public function onPreReceived(Context $context)
 
         // RouterProcessor is our default message processor when that header is not set
         $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $config->getRouterProcessorName());
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $config->getRouterQueueName());
     }
 }
diff --git a/Client/Driver/AmqpDriver.php b/Client/Driver/AmqpDriver.php
index 9b62ddc..875cc80 100644
--- a/Client/Driver/AmqpDriver.php
+++ b/Client/Driver/AmqpDriver.php
@@ -96,10 +96,10 @@ public function setupBroker(LoggerInterface $logger = null): void
     /**
      * @return AmqpQueue
      */
-    public function createQueue(string $clientQueuName): PsrQueue
+    protected function doCreateQueue(string $transportQueueName): PsrQueue
     {
         /** @var AmqpQueue $queue */
-        $queue = parent::createQueue($clientQueuName);
+        $queue = parent::doCreateQueue($transportQueueName);
         $queue->addFlag(AmqpQueue::FLAG_DURABLE);
 
         return $queue;
diff --git a/Client/Driver/GenericDriver.php b/Client/Driver/GenericDriver.php
index 64088d6..26a8277 100644
--- a/Client/Driver/GenericDriver.php
+++ b/Client/Driver/GenericDriver.php
@@ -62,48 +62,48 @@ public function sendToRouter(Message $message): void
 
     public function sendToProcessor(Message $message): void
     {
-        $processor = $message->getProperty(Config::PARAMETER_PROCESSOR_NAME);
-        if (false == $processor) {
-            throw new \LogicException('Processor name parameter is required but is not set');
-        }
-
         $topic = $message->getProperty(Config::PARAMETER_TOPIC_NAME);
         $command = $message->getProperty(Config::PARAMETER_COMMAND_NAME);
 
-        /** @var Route $route */
-        $route = null;
-        if ($topic) {
+        /** @var PsrQueue $queue */
+        $queue = null;
+        if ($topic && $processor = $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
             $route = $this->routeCollection->topicAndProcessor($topic, $processor);
             if (false == $route) {
                 throw new \LogicException(sprintf('There is no route for topic "%s" and processor "%s"', $topic, $processor));
             }
+
+            $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $this->config->getRouterProcessorName());
+            $queue = $this->createRouteQueue($route);
+        } elseif ($topic && false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
+            $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $this->config->getRouterProcessorName());
+
+            $queue = $this->createQueue($this->config->getRouterQueueName());
         } elseif ($command) {
             $route = $this->routeCollection->command($command);
             if (false == $route) {
-                throw new \LogicException(sprintf('There is no route for command "%s" and processor "%s"', $command, $processor));
+                throw new \LogicException(sprintf('There is no route for command "%s".', $command));
             }
 
-            if ($processor !== $route->getProcessor()) {
-                throw new \LogicException(sprintf('The command "%s" route was found but processors do not match. Given "%s", route "%s"', $command, $processor, $route->getProcessor()));
-            }
+            $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $route->getProcessor());
+            $queue = $this->createRouteQueue($route);
         } else {
             throw new \LogicException('Either topic or command parameter must be set.');
         }
 
         $transportMessage = $this->createTransportMessage($message);
-        $queue = $this->createRouteQueue($route);
 
         $producer = $this->context->createProducer();
 
-        if ($delay = $transportMessage->getProperty('X-Enqueue-Delay')) {
+        if (null !== $delay = $transportMessage->getProperty('X-Enqueue-Delay')) {
             $producer->setDeliveryDelay($delay * 1000);
         }
 
-        if ($expire = $transportMessage->getProperty('X-Enqueue-Expire')) {
+        if (null !== $expire = $transportMessage->getProperty('X-Enqueue-Expire')) {
             $producer->setTimeToLive($expire * 1000);
         }
 
-        if ($priority = $transportMessage->getProperty('X-Enqueue-Priority')) {
+        if (null !== $priority = $transportMessage->getProperty('X-Enqueue-Priority')) {
             $priorityMap = $this->getPriorityMap();
 
             $producer->setPriority($priorityMap[$priority]);
@@ -120,7 +120,7 @@ public function createQueue(string $clientQueueName): PsrQueue
     {
         $transportName = $this->createTransportQueueName($clientQueueName, true);
 
-        return $this->context->createQueue($transportName);
+        return $this->doCreateQueue($transportName);
     }
 
     public function createTransportMessage(Message $clientMessage): PsrMessage
@@ -163,10 +163,8 @@ public function createClientMessage(PsrMessage $transportMessage): Message
         $clientMessage->setBody($transportMessage->getBody());
         $clientMessage->setHeaders($transportMessage->getHeaders());
         $clientMessage->setProperties($transportMessage->getProperties());
-
         $clientMessage->setMessageId($transportMessage->getMessageId());
         $clientMessage->setTimestamp($transportMessage->getTimestamp());
-        $clientMessage->setPriority(MessagePriority::NORMAL);
         $clientMessage->setReplyTo($transportMessage->getReplyTo());
         $clientMessage->setCorrelationId($transportMessage->getCorrelationId());
 
@@ -216,7 +214,7 @@ protected function doSendToProcessor(PsrProducer $producer, PsrQueue $queue, Psr
 
     protected function createRouterTopic(): PsrTopic
     {
-        return $this->context->createTopic(
+        return $this->doCreateTopic(
             $this->createTransportRouterTopicName($this->config->getRouterTopicName(), true)
         );
     }
@@ -228,7 +226,7 @@ protected function createRouteQueue(Route $route): PsrQueue
             $route->isPrefixQueue()
         );
 
-        return $this->context->createQueue($transportName);
+        return $this->doCreateQueue($transportName);
     }
 
     protected function createTransportRouterTopicName(string $name, bool $prefix): string
@@ -246,6 +244,16 @@ protected function createTransportQueueName(string $name, bool $prefix): string
         return strtolower(implode($this->config->getSeparator(), array_filter([$clientPrefix, $clientAppName, $name])));
     }
 
+    protected function doCreateQueue(string $transportQueueName): PsrQueue
+    {
+        return $this->context->createQueue($transportQueueName);
+    }
+
+    protected function doCreateTopic(string $transportTopicName): PsrTopic
+    {
+        return $this->context->createTopic($transportTopicName);
+    }
+
     /**
      * [client message priority => transport message priority].
      *
diff --git a/Client/Driver/RabbitMqDriver.php b/Client/Driver/RabbitMqDriver.php
index 2d5611b..5e94b16 100644
--- a/Client/Driver/RabbitMqDriver.php
+++ b/Client/Driver/RabbitMqDriver.php
@@ -10,9 +10,9 @@ class RabbitMqDriver extends AmqpDriver
     /**
      * @return AmqpQueue
      */
-    public function createQueue(string $queueName): PsrQueue
+    protected function doCreateQueue(string $transportQueueName): PsrQueue
     {
-        $queue = parent::createQueue($queueName);
+        $queue = parent::doCreateQueue($transportQueueName);
         $queue->setArguments(['x-max-priority' => 4]);
 
         return $queue;
diff --git a/Client/Driver/RabbitMqStompDriver.php b/Client/Driver/RabbitMqStompDriver.php
index 4a4d697..4dd72f6 100644
--- a/Client/Driver/RabbitMqStompDriver.php
+++ b/Client/Driver/RabbitMqStompDriver.php
@@ -62,17 +62,6 @@ public function createTransportMessage(Message $message): PsrMessage
         return $transportMessage;
     }
 
-    /**
-     * @return StompDestination
-     */
-    public function createQueue(string $queueName): PsrQueue
-    {
-        $queue = parent::createQueue($queueName);
-        $queue->setHeader('x-max-priority', 4);
-
-        return $queue;
-    }
-
     public function setupBroker(LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
@@ -146,6 +135,17 @@ public function setupBroker(LoggerInterface $logger = null): void
         }
     }
 
+    /**
+     * @return StompDestination
+     */
+    protected function doCreateQueue(string $transportQueueName): PsrQueue
+    {
+        $queue = parent::doCreateQueue($transportQueueName);
+        $queue->setHeader('x-max-priority', 4);
+
+        return $queue;
+    }
+
     /**
      * @param StompProducer    $producer
      * @param StompDestination $topic
diff --git a/Client/Driver/StompDriver.php b/Client/Driver/StompDriver.php
index aedd95b..8ef9046 100644
--- a/Client/Driver/StompDriver.php
+++ b/Client/Driver/StompDriver.php
@@ -43,10 +43,10 @@ public function createTransportMessage(Message $message): PsrMessage
     /**
      * @return StompDestination
      */
-    public function createQueue(string $queueName): PsrQueue
+    protected function doCreateQueue(string $transportQueueName): PsrQueue
     {
         /** @var StompDestination $queue */
-        $queue = parent::createQueue($queueName);
+        $queue = parent::doCreateQueue($transportQueueName);
         $queue->setDurable(true);
         $queue->setAutoDelete(false);
         $queue->setExclusive(false);
diff --git a/Client/DriverPreSend.php b/Client/DriverPreSend.php
index 8a298cb..d940fc0 100644
--- a/Client/DriverPreSend.php
+++ b/Client/DriverPreSend.php
@@ -34,7 +34,12 @@ public function getDriver(): DriverInterface
 
     public function isEvent(): bool
     {
-        return Config::COMMAND_TOPIC !== $this->message->getProperty(Config::PARAMETER_TOPIC_NAME);
+        return (bool) $this->message->getProperty(Config::PARAMETER_TOPIC_NAME);
+    }
+
+    public function isCommand(): bool
+    {
+        return (bool) $this->message->getProperty(Config::PARAMETER_COMMAND_NAME);
     }
 
     public function getCommand(): string
diff --git a/Client/Message.php b/Client/Message.php
index d58371b..6f1186d 100644
--- a/Client/Message.php
+++ b/Client/Message.php
@@ -205,18 +205,12 @@ public function setDelay($delay)
         $this->delay = $delay;
     }
 
-    /**
-     * @param string $scope
-     */
-    public function setScope($scope)
+    public function setScope(string $scope): void
     {
         $this->scope = $scope;
     }
 
-    /**
-     * @return string
-     */
-    public function getScope()
+    public function getScope(): string
     {
         return $this->scope;
     }
diff --git a/Client/PostSend.php b/Client/PostSend.php
index 2e0ae62..5f575e2 100644
--- a/Client/PostSend.php
+++ b/Client/PostSend.php
@@ -34,7 +34,12 @@ public function getDriver(): DriverInterface
 
     public function isEvent(): bool
     {
-        return Config::COMMAND_TOPIC !== $this->message->getProperty(Config::PARAMETER_TOPIC_NAME);
+        return (bool) $this->message->getProperty(Config::PARAMETER_TOPIC_NAME);
+    }
+
+    public function isCommand(): bool
+    {
+        return (bool) $this->message->getProperty(Config::PARAMETER_COMMAND_NAME);
     }
 
     public function getCommand(): string
diff --git a/Client/Producer.php b/Client/Producer.php
index 7a4d18f..e236cd1 100644
--- a/Client/Producer.php
+++ b/Client/Producer.php
@@ -47,10 +47,8 @@ public function sendEvent(string $topic, $message): void
         $preSend = new PreSend($topic, $message, $this, $this->driver);
         $this->extension->onPreSendEvent($preSend);
 
-        $topic = $preSend->getTopic();
         $message = $preSend->getMessage();
-
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, $topic);
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, $preSend->getTopic());
 
         $this->doSend($message);
     }
@@ -81,7 +79,6 @@ public function sendCommand(string $command, $message, bool $needReply = false):
             }
         }
 
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, Config::COMMAND_TOPIC);
         $message->setProperty(Config::PARAMETER_COMMAND_NAME, $command);
         $message->setScope(Message::SCOPE_APP);
 
@@ -106,6 +103,10 @@ private function doSend(Message $message): void
             ));
         }
 
+        if ($message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
+            throw new \LogicException(sprintf('The %s property must not be set.', Config::PARAMETER_PROCESSOR_NAME));
+        }
+
         if (!$message->getMessageId()) {
             $message->setMessageId(UUID::generate());
         }
@@ -118,25 +119,11 @@ private function doSend(Message $message): void
             $message->setPriority(MessagePriority::NORMAL);
         }
 
-        if (Message::SCOPE_MESSAGE_BUS == $message->getScope()) {
-            if ($message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
-                throw new \LogicException(sprintf('The %s property must not be set for messages that are sent to message bus.', Config::PARAMETER_PROCESSOR_QUEUE_NAME));
-            }
-            if ($message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
-                throw new \LogicException(sprintf('The %s property must not be set for messages that are sent to message bus.', Config::PARAMETER_PROCESSOR_NAME));
-            }
+        $this->extension->onDriverPreSend(new DriverPreSend($message, $this, $this->driver));
 
-            $this->extension->onDriverPreSend(new DriverPreSend($message, $this, $this->driver));
+        if (Message::SCOPE_MESSAGE_BUS == $message->getScope()) {
             $this->driver->sendToRouter($message);
         } elseif (Message::SCOPE_APP == $message->getScope()) {
-            if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
-                $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $this->driver->getConfig()->getRouterProcessorName());
-            }
-            if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) {
-                $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $this->driver->getConfig()->getRouterQueueName());
-            }
-
-            $this->extension->onDriverPreSend(new DriverPreSend($message, $this, $this->driver));
             $this->driver->sendToProcessor($message);
         } else {
             throw new \LogicException(sprintf('The message scope "%s" is not supported.', $message->getScope()));
diff --git a/Client/Resources.php b/Client/Resources.php
index 09ee430..71f8c53 100644
--- a/Client/Resources.php
+++ b/Client/Resources.php
@@ -135,6 +135,18 @@ public static function getKnownDrivers(): array
                 'requiredSchemeExtensions' => [],
                 'package' => ['enqueue/enqueue', 'enqueue/dbal'],
             ];
+            $map[] = [
+                'schemes' => ['gearman'],
+                'factoryClass' => GenericDriver::class,
+                'requiredSchemeExtensions' => [],
+                'package' => ['enqueue/enqueue', 'enqueue/gearman'],
+            ];
+            $map[] = [
+                'schemes' => ['beanstalk'],
+                'factoryClass' => GenericDriver::class,
+                'requiredSchemeExtensions' => [],
+                'package' => ['enqueue/enqueue', 'enqueue/pheanstalk'],
+            ];
 
             self::$knownDrivers = $map;
         }
diff --git a/Client/TraceableProducer.php b/Client/TraceableProducer.php
index 40ff580..86ff9f8 100644
--- a/Client/TraceableProducer.php
+++ b/Client/TraceableProducer.php
@@ -32,7 +32,7 @@ public function sendCommand(string $command, $message, bool $needReply = false):
     {
         $result = $this->producer->sendCommand($command, $message, $needReply);
 
-        $this->collectTrace(Config::COMMAND_TOPIC, $command, $message);
+        $this->collectTrace(null, $command, $message);
 
         return $result;
     }
@@ -86,6 +86,7 @@ private function collectTrace(string $topic = null, string $command = null, $mes
             'contentType' => null,
             'messageId' => null,
         ];
+
         if ($message instanceof Message) {
             $trace['body'] = $message->getBody();
             $trace['headers'] = $message->getHeaders();
diff --git a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
index 6dc723c..f85ace3 100644
--- a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
@@ -56,7 +56,6 @@ public function testShouldSetRouterProcessorPropertyIfNotSetAndOnRouterQueue()
 
         $this->assertEquals([
             'enqueue.processor_name' => 'router-processor-name',
-            'enqueue.processor_queue_name' => 'router-queue',
         ], $message->getProperties());
     }
 
diff --git a/Tests/Client/Driver/GenericDriverTestsTrait.php b/Tests/Client/Driver/GenericDriverTestsTrait.php
index 4c03a14..8e1d3fe 100644
--- a/Tests/Client/Driver/GenericDriverTestsTrait.php
+++ b/Tests/Client/Driver/GenericDriverTestsTrait.php
@@ -723,6 +723,51 @@ public function testThrowIfNoRouteFoundForTopicMessageOnSendToProcessor()
         $driver->sendToProcessor($message);
     }
 
+    public function testShouldSetRouterProcessorIfProcessorPropertyEmptyOnSendToProcessor()
+    {
+        $queue = $this->createQueue('');
+        $transportMessage = $this->createMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with($this->getDefaultQueueTransportName())
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('topic', Route::TOPIC, 'expectedProcessor'),
+            ])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+
+        $driver->sendToProcessor($message);
+
+        $this->assertSame('router', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
+    }
+
     public function testShouldSendCommandMessageToProcessorToDefaultQueue()
     {
         $queue = $this->createQueue('');
@@ -834,53 +879,70 @@ public function testThrowIfNoRouteFoundForCommandMessageOnSendToProcessor()
         $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
 
         $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('There is no route for command "command" and processor "processor"');
+        $this->expectExceptionMessage('There is no route for command "command".');
         $driver->sendToProcessor($message);
     }
 
-    public function testThrowIfRouteProcessorDoesNotMatchMessageOneOnSendToProcessor()
+    public function testShouldOverwriteProcessorPropertySetByOneFromCommandRouteOnSendToProcessor()
     {
+        $queue = $this->createQueue('');
+        $transportMessage = $this->createMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
         $context = $this->createContextMock();
         $context
-            ->expects($this->never())
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with($this->getCustomQueueTransportName())
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
             ->method('createProducer')
+            ->willReturn($producer)
         ;
         $context
-            ->expects($this->never())
+            ->expects($this->once())
             ->method('createMessage')
+            ->willReturn($transportMessage)
         ;
 
         $driver = $this->createDriver(
             $context,
             $this->createDummyConfig(),
             new RouteCollection([
-                new Route('command', Route::COMMAND, 'anotherProcessor', ['queue' => 'custom']),
+                new Route('command', Route::COMMAND, 'expectedProcessor', ['queue' => 'custom']),
             ])
         );
 
         $message = new Message();
         $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processorShouldBeOverwritten');
 
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The command "command" route was found but processors do not match. Given "processor", route "anotherProcessor"');
         $driver->sendToProcessor($message);
-    }
 
-    public function testThrowIfProcessorIsNotSetOnSendToProcessor()
-    {
-        $driver = $this->createDriver(
-            $this->createContextMock(),
-            $this->createDummyConfig(),
-            new RouteCollection([])
-        );
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Processor name parameter is required but is not set');
-
-        $driver->sendToProcessor(new Message());
+        $this->assertSame('expectedProcessor', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
     }
 
+//    public function testThrowIfProcessorIsNotSetOnSendToProcessor()
+//    {
+//        $driver = $this->createDriver(
+//            $this->createContextMock(),
+//            $this->createDummyConfig(),
+//            new RouteCollection([])
+//        );
+//
+//        $this->expectException(\LogicException::class);
+//        $this->expectExceptionMessage('Processor name parameter is required but is not set');
+//
+//        $driver->sendToProcessor(new Message());
+//    }
+
     public function testThrowIfNeitherTopicNorCommandAreSentOnSendToProcessor()
     {
         $driver = $this->createDriver(
diff --git a/Tests/Client/DriverFactoryTest.php b/Tests/Client/DriverFactoryTest.php
index a51adb3..f17ce38 100644
--- a/Tests/Client/DriverFactoryTest.php
+++ b/Tests/Client/DriverFactoryTest.php
@@ -22,12 +22,16 @@
 use Enqueue\Dbal\DbalContext;
 use Enqueue\Fs\FsConnectionFactory;
 use Enqueue\Fs\FsContext;
+use Enqueue\Gearman\GearmanConnectionFactory;
+use Enqueue\Gearman\GearmanContext;
 use Enqueue\Gps\GpsConnectionFactory;
 use Enqueue\Gps\GpsContext;
 use Enqueue\Mongodb\MongodbConnectionFactory;
 use Enqueue\Mongodb\MongodbContext;
 use Enqueue\Null\NullConnectionFactory;
 use Enqueue\Null\NullContext;
+use Enqueue\Pheanstalk\PheanstalkConnectionFactory;
+use Enqueue\Pheanstalk\PheanstalkContext;
 use Enqueue\RdKafka\RdKafkaConnectionFactory;
 use Enqueue\RdKafka\RdKafkaContext;
 use Enqueue\Redis\RedisConnectionFactory;
@@ -154,6 +158,10 @@ public static function provideDSN()
         yield ['stomp+rabbitmq:', StompConnectionFactory::class, StompContext::class, [], RabbitMqStompDriver::class];
 
         yield ['stomp+foo+bar:', StompConnectionFactory::class, StompContext::class, [], StompDriver::class];
+
+        yield ['gearman:', GearmanConnectionFactory::class, GearmanContext::class, [], GenericDriver::class];
+
+        yield ['beanstalk:', PheanstalkConnectionFactory::class, PheanstalkContext::class, [], GenericDriver::class];
     }
 
     private function createConnectionFactoryMock(): PsrConnectionFactory
diff --git a/Tests/Client/DriverPreSendTest.php b/Tests/Client/DriverPreSendTest.php
index 62d493b..aa9ff39 100644
--- a/Tests/Client/DriverPreSendTest.php
+++ b/Tests/Client/DriverPreSendTest.php
@@ -48,7 +48,6 @@ public function testShouldAllowGetArgumentSetInConstructor()
     public function testShouldAllowGetCommand()
     {
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, Config::COMMAND_TOPIC);
         $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'theCommand');
 
         $context = new DriverPreSend(
diff --git a/Tests/Client/PostSendTest.php b/Tests/Client/PostSendTest.php
index fe78c14..9024034 100644
--- a/Tests/Client/PostSendTest.php
+++ b/Tests/Client/PostSendTest.php
@@ -48,7 +48,6 @@ public function testShouldAllowGetArgumentSetInConstructor()
     public function testShouldAllowGetCommand()
     {
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, Config::COMMAND_TOPIC);
         $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'theCommand');
 
         $context = new PostSend(
diff --git a/Tests/Client/ProducerSendCommandTest.php b/Tests/Client/ProducerSendCommandTest.php
index 057d227..67b9485 100644
--- a/Tests/Client/ProducerSendCommandTest.php
+++ b/Tests/Client/ProducerSendCommandTest.php
@@ -40,10 +40,7 @@ public function testShouldSendCommandToProcessor()
         $producer->sendCommand('command', $message);
 
         $expectedProperties = [
-            'enqueue.processor_name' => 'a_router_processor_name',
-            'enqueue.topic_name' => '__command__',
             'enqueue.command_name' => 'command',
-            'enqueue.processor_queue_name' => 'a_router_queue',
         ];
 
         self::assertEquals($expectedProperties, $message->getProperties());
@@ -139,9 +136,8 @@ public function testShouldSendCommandWithReplyAndCustomReplyQueueAndCorrelationI
     public function testShouldOverwriteExpectedMessageProperties()
     {
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topicShouldBeOverwritten');
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'topicShouldBeOverwritten');
-        $message->setScope('topicShouldBeOverwritten');
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'commandShouldBeOverwritten');
+        $message->setScope('scopeShouldBeOverwritten');
 
         $driver = $this->createDriverStub();
 
@@ -149,10 +145,7 @@ public function testShouldOverwriteExpectedMessageProperties()
         $producer->sendCommand('expectedCommand', $message);
 
         $expectedProperties = [
-            'enqueue.processor_name' => 'a_router_processor_name',
-            'enqueue.topic_name' => '__command__',
             'enqueue.command_name' => 'expectedCommand',
-            'enqueue.processor_queue_name' => 'a_router_queue',
         ];
 
         self::assertEquals($expectedProperties, $message->getProperties());
@@ -306,8 +299,8 @@ public function testShouldSendCommandToApplicationRouter()
             ->method('sendToProcessor')
             ->willReturnCallback(function (Message $message) {
                 self::assertSame('aBody', $message->getBody());
-                self::assertSame('a_router_processor_name', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
-                self::assertSame('a_router_queue', $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME));
+                self::assertNull($message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
+                self::assertSame('command', $message->getProperty(Config::PARAMETER_COMMAND_NAME));
             })
         ;
 
@@ -315,26 +308,23 @@ public function testShouldSendCommandToApplicationRouter()
         $producer->sendCommand('command', $message);
     }
 
-    public function testShouldSendCommandWithCustomProcessorAndQueueNamePropertiesSetToApplicationRouter()
+    public function testThrowWhenProcessorNamePropertySetToApplicationRouter()
     {
         $message = new Message();
         $message->setBody('aBody');
         $message->setScope(Message::SCOPE_APP);
         $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aCustomProcessor');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aCustomProcessorQueue');
 
         $driver = $this->createDriverStub();
         $driver
-            ->expects($this->once())
+            ->expects($this->never())
             ->method('sendToProcessor')
-            ->willReturnCallback(function (Message $message) {
-                self::assertSame('aBody', $message->getBody());
-                self::assertSame('aCustomProcessor', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
-                self::assertSame('aCustomProcessorQueue', $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME));
-            })
         ;
 
         $producer = new Producer($driver, $this->createRpcFactoryMock());
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The enqueue.processor_name property must not be set.');
         $producer->sendCommand('command', $message);
     }
 
diff --git a/Tests/Client/ProducerSendEventTest.php b/Tests/Client/ProducerSendEventTest.php
index a5d45d3..13cddbc 100644
--- a/Tests/Client/ProducerSendEventTest.php
+++ b/Tests/Client/ProducerSendEventTest.php
@@ -213,30 +213,7 @@ public function testThrowIfSendEventToMessageBusWithProcessorNamePropertySet()
         $producer = new Producer($driver, $this->createRpcFactoryMock());
 
         $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The enqueue.processor_name property must not be set for messages that are sent to message bus.');
-        $producer->sendEvent('topic', $message);
-    }
-
-    public function testThrowIfSendEventToMessageBusWithProcessorQueueNamePropertySet()
-    {
-        $message = new Message();
-        $message->setBody('');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aProcessorQueue');
-
-        $driver = $this->createDriverStub();
-        $driver
-            ->expects($this->never())
-            ->method('sendToRouter')
-        ;
-        $driver
-            ->expects($this->never())
-            ->method('sendToProcessor')
-        ;
-
-        $producer = new Producer($driver, $this->createRpcFactoryMock());
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The enqueue.processor_queue_name property must not be set for messages that are sent to message bus.');
+        $this->expectExceptionMessage('The enqueue.processor_name property must not be set.');
         $producer->sendEvent('topic', $message);
     }
 
@@ -256,8 +233,9 @@ public function testShouldSendEventToApplicationRouter()
             ->method('sendToProcessor')
             ->willReturnCallback(function (Message $message) {
                 self::assertSame('aBody', $message->getBody());
-                self::assertSame('a_router_processor_name', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
-                self::assertSame('a_router_queue', $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME));
+
+                // null means a driver sends a message to router processor.
+                self::assertNull($message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
             })
         ;
 
@@ -265,30 +243,23 @@ public function testShouldSendEventToApplicationRouter()
         $producer->sendEvent('topic', $message);
     }
 
-    public function testShouldSendEventWithCustomProcessorAndQueueNamePropertiesSetToApplicationRouter()
+    public function testThrowWhenProcessorNamePropertySetToApplicationRouter()
     {
         $message = new Message();
         $message->setBody('aBody');
         $message->setScope(Message::SCOPE_APP);
         $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aCustomProcessor');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aCustomProcessorQueue');
 
         $driver = $this->createDriverStub();
         $driver
             ->expects($this->never())
-            ->method('sendToRouter')
-        ;
-        $driver
-            ->expects($this->once())
             ->method('sendToProcessor')
-            ->willReturnCallback(function (Message $message) {
-                self::assertSame('aBody', $message->getBody());
-                self::assertSame('aCustomProcessor', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
-                self::assertSame('aCustomProcessorQueue', $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME));
-            })
         ;
 
         $producer = new Producer($driver, $this->createRpcFactoryMock());
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The enqueue.processor_name property must not be set.');
         $producer->sendEvent('topic', $message);
     }
 
diff --git a/Tests/Client/TraceableProducerTest.php b/Tests/Client/TraceableProducerTest.php
index 42c9c8b..82364ca 100644
--- a/Tests/Client/TraceableProducerTest.php
+++ b/Tests/Client/TraceableProducerTest.php
@@ -2,7 +2,6 @@
 
 namespace Enqueue\Tests\Client;
 
-use Enqueue\Client\Config;
 use Enqueue\Client\Message;
 use Enqueue\Client\ProducerInterface;
 use Enqueue\Client\TraceableProducer;
@@ -165,7 +164,7 @@ public function testShouldCollectInfoIfStringGivenAsCommandMessage()
 
         $this->assertSame([
             [
-                'topic' => Config::COMMAND_TOPIC,
+                'topic' => null,
                 'command' => 'aFooCommand',
                 'body' => 'aFooBody',
                 'headers' => [],
@@ -188,7 +187,7 @@ public function testShouldCollectInfoIfArrayGivenAsCommandMessage()
 
         $this->assertSame([
             [
-                'topic' => Config::COMMAND_TOPIC,
+                'topic' => null,
                 'command' => 'aFooCommand',
                 'body' => ['foo' => 'fooVal', 'bar' => 'barVal'],
                 'headers' => [],
@@ -222,7 +221,7 @@ public function testShouldCollectInfoIfCommandMessageObjectGivenAsMessage()
 
         $this->assertSame([
             [
-                'topic' => Config::COMMAND_TOPIC,
+                'topic' => null,
                 'command' => 'aFooCommand',
                 'body' => ['foo' => 'fooVal', 'bar' => 'barVal'],
                 'headers' => ['fooHeader' => 'fooVal'],

From c17b46020b16552f208ca968a9317f1d142ebd17 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 21 Sep 2018 18:20:09 +0300
Subject: [PATCH 129/337] upd client.

---
 Client/Config.php                             |   3 -
 Client/Driver/GenericDriver.php               |  20 +--
 Client/DriverInterface.php                    |   2 +
 .../Client/Driver/GenericDriverTestsTrait.php | 150 ++++++++++++++++--
 4 files changed, 149 insertions(+), 26 deletions(-)

diff --git a/Client/Config.php b/Client/Config.php
index 4e7dd39..f85d25d 100644
--- a/Client/Config.php
+++ b/Client/Config.php
@@ -13,9 +13,6 @@ class Config
      */
     const PARAMETER_PROCESSOR_QUEUE_NAME = 'enqueue.processor_queue_name';
 
-    /**
-     * @deprecated
-     */
     const DEFAULT_PROCESSOR_QUEUE_NAME = 'default';
 
     /**
diff --git a/Client/Driver/GenericDriver.php b/Client/Driver/GenericDriver.php
index 26a8277..c048b3f 100644
--- a/Client/Driver/GenericDriver.php
+++ b/Client/Driver/GenericDriver.php
@@ -123,6 +123,16 @@ public function createQueue(string $clientQueueName): PsrQueue
         return $this->doCreateQueue($transportName);
     }
 
+    public function createRouteQueue(Route $route): PsrQueue
+    {
+        $transportName = $this->createTransportQueueName(
+            $route->getQueue() ?: $this->config->getDefaultProcessorQueueName(),
+            $route->isPrefixQueue()
+        );
+
+        return $this->doCreateQueue($transportName);
+    }
+
     public function createTransportMessage(Message $clientMessage): PsrMessage
     {
         $headers = $clientMessage->getHeaders();
@@ -219,16 +229,6 @@ protected function createRouterTopic(): PsrTopic
         );
     }
 
-    protected function createRouteQueue(Route $route): PsrQueue
-    {
-        $transportName = $this->createTransportQueueName(
-            $route->getQueue() ?: $this->config->getDefaultProcessorQueueName(),
-            $route->isPrefixQueue()
-        );
-
-        return $this->doCreateQueue($transportName);
-    }
-
     protected function createTransportRouterTopicName(string $name, bool $prefix): string
     {
         $clientPrefix = $prefix ? $this->config->getPrefix() : '';
diff --git a/Client/DriverInterface.php b/Client/DriverInterface.php
index e671556..96144a5 100644
--- a/Client/DriverInterface.php
+++ b/Client/DriverInterface.php
@@ -21,6 +21,8 @@ public function sendToProcessor(Message $message): void;
 
     public function createQueue(string $queueName): PsrQueue;
 
+    public function createRouteQueue(Route $route): PsrQueue;
+
     /**
      * Prepare broker for work.
      * Creates all required queues, exchanges, topics, bindings etc.
diff --git a/Tests/Client/Driver/GenericDriverTestsTrait.php b/Tests/Client/Driver/GenericDriverTestsTrait.php
index 8e1d3fe..daafc66 100644
--- a/Tests/Client/Driver/GenericDriverTestsTrait.php
+++ b/Tests/Client/Driver/GenericDriverTestsTrait.php
@@ -929,19 +929,143 @@ public function testShouldOverwriteProcessorPropertySetByOneFromCommandRouteOnSe
         $this->assertSame('expectedProcessor', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
     }
 
-//    public function testThrowIfProcessorIsNotSetOnSendToProcessor()
-//    {
-//        $driver = $this->createDriver(
-//            $this->createContextMock(),
-//            $this->createDummyConfig(),
-//            new RouteCollection([])
-//        );
-//
-//        $this->expectException(\LogicException::class);
-//        $this->expectExceptionMessage('Processor name parameter is required but is not set');
-//
-//        $driver->sendToProcessor(new Message());
-//    }
+    public function testShouldNotInitDeliveryDelayOnSendMessageToProcessorIfPropertyNull()
+    {
+        $queue = $this->createQueue('');
+        $transportMessage = $this->createMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->never())
+            ->method('setDeliveryDelay')
+        ;
+        $producer
+            ->expects($this->once())
+            ->method('send')
+        ;
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('command', Route::COMMAND, 'expectedProcessor', ['queue' => 'custom']),
+            ])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
+        $message->setDelay(null);
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldNotInitPriorityOnSendMessageToProcessorIfPropertyNull()
+    {
+        $queue = $this->createQueue('');
+        $transportMessage = $this->createMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->never())
+            ->method('setPriority')
+        ;
+        $producer
+            ->expects($this->once())
+            ->method('send')
+        ;
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('command', Route::COMMAND, 'expectedProcessor', ['queue' => 'custom']),
+            ])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
+        $message->setPriority(null);
+
+        $driver->sendToProcessor($message);
+    }
+
+    public function testShouldNotInitTimeToLiveOnSendMessageToProcessorIfPropertyNull()
+    {
+        $queue = $this->createQueue('');
+        $transportMessage = $this->createMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->never())
+            ->method('setTimeToLive')
+        ;
+        $producer
+            ->expects($this->once())
+            ->method('send')
+        ;
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $driver = $this->createDriver(
+            $context,
+            $this->createDummyConfig(),
+            new RouteCollection([
+                new Route('command', Route::COMMAND, 'expectedProcessor', ['queue' => 'custom']),
+            ])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
+        $message->setExpire(null);
+
+        $driver->sendToProcessor($message);
+    }
 
     public function testThrowIfNeitherTopicNorCommandAreSentOnSendToProcessor()
     {

From 9e8e18e1daa58ad34dac7858162872a0ed01a4c5 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 21 Sep 2018 20:17:23 +0300
Subject: [PATCH 130/337] client fixes

---
 .../ConsumptionExtension/SetRouterPropertiesExtension.php | 8 +++++---
 Client/Driver/GenericDriver.php                           | 2 +-
 Client/Producer.php                                       | 4 ----
 .../SetRouterPropertiesExtensionTest.php                  | 2 ++
 Tests/Client/ProducerSendCommandTest.php                  | 4 ++--
 Tests/Client/ProducerSendEventTest.php                    | 4 ++--
 6 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
index 27ef6f3..a87a098 100644
--- a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
+++ b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
@@ -25,9 +25,6 @@ public function __construct(DriverInterface $driver)
         $this->driver = $driver;
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function onPreReceived(Context $context)
     {
         $message = $context->getPsrMessage();
@@ -43,5 +40,10 @@ public function onPreReceived(Context $context)
 
         // RouterProcessor is our default message processor when that header is not set
         $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $config->getRouterProcessorName());
+
+        $context->getLogger()->debug(
+            '[SetRouterPropertiesExtension] '.
+            sprintf('Set router processor "%s"', $config->getRouterProcessorName())
+        );
     }
 }
diff --git a/Client/Driver/GenericDriver.php b/Client/Driver/GenericDriver.php
index c048b3f..2cb8ca4 100644
--- a/Client/Driver/GenericDriver.php
+++ b/Client/Driver/GenericDriver.php
@@ -73,7 +73,7 @@ public function sendToProcessor(Message $message): void
                 throw new \LogicException(sprintf('There is no route for topic "%s" and processor "%s"', $topic, $processor));
             }
 
-            $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $this->config->getRouterProcessorName());
+            $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $route->getProcessor());
             $queue = $this->createRouteQueue($route);
         } elseif ($topic && false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
             $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $this->config->getRouterProcessorName());
diff --git a/Client/Producer.php b/Client/Producer.php
index e236cd1..977b4b5 100644
--- a/Client/Producer.php
+++ b/Client/Producer.php
@@ -115,10 +115,6 @@ private function doSend(Message $message): void
             $message->setTimestamp(time());
         }
 
-        if (!$message->getPriority()) {
-            $message->setPriority(MessagePriority::NORMAL);
-        }
-
         $this->extension->onDriverPreSend(new DriverPreSend($message, $this, $this->driver));
 
         if (Message::SCOPE_MESSAGE_BUS == $message->getScope()) {
diff --git a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
index f85ace3..686ece9 100644
--- a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
@@ -12,6 +12,7 @@
 use Enqueue\Test\ClassExtensionTrait;
 use Interop\Queue\PsrContext;
 use PHPUnit\Framework\TestCase;
+use Psr\Log\NullLogger;
 
 class SetRouterPropertiesExtensionTest extends TestCase
 {
@@ -48,6 +49,7 @@ public function testShouldSetRouterProcessorPropertyIfNotSetAndOnRouterQueue()
         $message = new NullMessage();
 
         $context = new Context($this->createPsrContextMock());
+        $context->setLogger(new NullLogger());
         $context->setPsrMessage($message);
         $context->setPsrQueue(new NullQueue('test.router-queue'));
 
diff --git a/Tests/Client/ProducerSendCommandTest.php b/Tests/Client/ProducerSendCommandTest.php
index 67b9485..21b1f5b 100644
--- a/Tests/Client/ProducerSendCommandTest.php
+++ b/Tests/Client/ProducerSendCommandTest.php
@@ -152,7 +152,7 @@ public function testShouldOverwriteExpectedMessageProperties()
         self::assertSame(Message::SCOPE_APP, $message->getScope());
     }
 
-    public function testShouldSendCommandWithNormalPriorityByDefault()
+    public function testShouldSendCommandWithoutPriorityByDefault()
     {
         $message = new Message();
 
@@ -166,7 +166,7 @@ public function testShouldSendCommandWithNormalPriorityByDefault()
         $producer = new Producer($driver, $this->createRpcFactoryMock());
         $producer->sendCommand('command', $message);
 
-        self::assertSame(MessagePriority::NORMAL, $message->getPriority());
+        self::assertNull($message->getPriority());
     }
 
     public function testShouldSendCommandWithCustomPriority()
diff --git a/Tests/Client/ProducerSendEventTest.php b/Tests/Client/ProducerSendEventTest.php
index 13cddbc..757c612 100644
--- a/Tests/Client/ProducerSendEventTest.php
+++ b/Tests/Client/ProducerSendEventTest.php
@@ -59,7 +59,7 @@ public function testShouldOverwriteTopicProperty()
         self::assertEquals($expectedProperties, $message->getProperties());
     }
 
-    public function testShouldSendEventWithNormalPriorityByDefault()
+    public function testShouldSendEventWithoutPriorityByDefault()
     {
         $message = new Message();
 
@@ -73,7 +73,7 @@ public function testShouldSendEventWithNormalPriorityByDefault()
         $producer = new Producer($driver, $this->createRpcFactoryMock());
         $producer->sendEvent('topic', $message);
 
-        self::assertSame(MessagePriority::NORMAL, $message->getPriority());
+        self::assertNull($message->getPriority());
     }
 
     public function testShouldSendEventWithCustomPriority()

From db7bc12abb8b069848aa979f9ddde4ced3697f75 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 21 Sep 2018 23:22:37 +0300
Subject: [PATCH 131/337] [client] migrate simple client to new concept. adopt
 route collection.

---
 Client/Driver/DbalDriver.php                  | 10 +++
 Client/Driver/FsDriver.php                    | 16 +++--
 Client/Driver/MongodbDriver.php               | 10 +++
 Client/Driver/RedisDriver.php                 | 27 +++++++
 Client/Driver/SqsDriver.php                   |  9 +++
 Client/Resources.php                          |  3 +-
 Tests/Client/Driver/AmqpDriverTest.php        |  5 ++
 Tests/Client/Driver/FsDriverTest.php          | 17 +----
 Tests/Client/Driver/GenericDriverTest.php     |  5 ++
 .../Client/Driver/GenericDriverTestsTrait.php | 70 +++++++++++--------
 Tests/Client/Driver/GpsDriverTest.php         |  5 ++
 Tests/Client/Driver/RabbitMqDriverTest.php    |  5 ++
 .../Client/Driver/RabbitMqStompDriverTest.php | 17 ++++-
 Tests/Client/Driver/RdKafkaDriverTest.php     |  5 ++
 Tests/Client/Driver/SqsDriverTest.php         |  5 ++
 Tests/Client/Driver/StompDriverTest.php       | 17 ++++-
 16 files changed, 170 insertions(+), 56 deletions(-)
 create mode 100644 Client/Driver/RedisDriver.php

diff --git a/Client/Driver/DbalDriver.php b/Client/Driver/DbalDriver.php
index 8b1f326..165bcf7 100644
--- a/Client/Driver/DbalDriver.php
+++ b/Client/Driver/DbalDriver.php
@@ -3,6 +3,8 @@
 namespace Enqueue\Client\Driver;
 
 use Enqueue\Dbal\DbalContext;
+use Enqueue\Dbal\DbalDestination;
+use Interop\Queue\PsrTopic;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -26,4 +28,12 @@ public function setupBroker(LoggerInterface $logger = null): void
         $log('Creating database table: "%s"', $this->getContext()->getTableName());
         $this->getContext()->createDataBaseTable();
     }
+
+    /**
+     * @return DbalDestination
+     */
+    protected function createRouterTopic(): PsrTopic
+    {
+        return $this->createQueue($this->getConfig()->getRouterQueueName());
+    }
 }
diff --git a/Client/Driver/FsDriver.php b/Client/Driver/FsDriver.php
index 376e419..1ce7032 100644
--- a/Client/Driver/FsDriver.php
+++ b/Client/Driver/FsDriver.php
@@ -4,13 +4,13 @@
 
 use Enqueue\Fs\FsContext;
 use Enqueue\Fs\FsDestination;
+use Interop\Queue\PsrTopic;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
 /**
  * @method FsContext getContext
  * @method FsDestination createQueue(string $name)
- * @method FsDestination createRouterTopic
  */
 class FsDriver extends GenericDriver
 {
@@ -27,13 +27,9 @@ public function setupBroker(LoggerInterface $logger = null): void
         };
 
         // setup router
-        $routerTopic = $this->createRouterTopic();
         $routerQueue = $this->createQueue($this->getConfig()->getRouterQueueName());
 
-        $log('Declare router exchange "%s" file: %s', $routerTopic->getTopicName(), $routerTopic->getFileInfo());
-        $this->getContext()->declareDestination($routerTopic);
-
-        $log('Declare router queue "%s" file: %s', $routerQueue->getQueueName(), $routerTopic->getFileInfo());
+        $log('Declare router queue "%s" file: %s', $routerQueue->getQueueName(), $routerQueue->getFileInfo());
         $this->getContext()->declareDestination($routerQueue);
 
         // setup queues
@@ -51,4 +47,12 @@ public function setupBroker(LoggerInterface $logger = null): void
             $declaredQueues[$queue->getQueueName()] = true;
         }
     }
+
+    /**
+     * @return FsDestination
+     */
+    protected function createRouterTopic(): PsrTopic
+    {
+        return $this->createQueue($this->getConfig()->getRouterQueueName());
+    }
 }
diff --git a/Client/Driver/MongodbDriver.php b/Client/Driver/MongodbDriver.php
index 19f2c57..ed31727 100644
--- a/Client/Driver/MongodbDriver.php
+++ b/Client/Driver/MongodbDriver.php
@@ -3,6 +3,8 @@
 namespace Enqueue\Client\Driver;
 
 use Enqueue\Mongodb\MongodbContext;
+use Enqueue\Mongodb\MongodbDestination;
+use Interop\Queue\PsrTopic;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -27,4 +29,12 @@ public function setupBroker(LoggerInterface $logger = null): void
         $log('Creating database and collection: "%s" "%s"', $contextConfig['dbname'], $contextConfig['collection_name']);
         $this->getContext()->createCollection();
     }
+
+    /**
+     * @return MongodbDestination
+     */
+    protected function createRouterTopic(): PsrTopic
+    {
+        return $this->createQueue($this->getConfig()->getRouterQueueName());
+    }
 }
diff --git a/Client/Driver/RedisDriver.php b/Client/Driver/RedisDriver.php
new file mode 100644
index 0000000..264409e
--- /dev/null
+++ b/Client/Driver/RedisDriver.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Enqueue\Client\Driver;
+
+use Enqueue\Redis\RedisContext;
+use Enqueue\Redis\RedisDestination;
+use Interop\Queue\PsrTopic;
+
+/**
+ * @method RedisContext getContext
+ * @method RedisDestination createQueue(string $name)
+ */
+class RedisDriver extends GenericDriver
+{
+    public function __construct(RedisContext $context, ...$args)
+    {
+        parent::__construct($context, ...$args);
+    }
+
+    /**
+     * @return RedisDestination
+     */
+    protected function createRouterTopic(): PsrTopic
+    {
+        return $this->createQueue($this->getConfig()->getRouterQueueName());
+    }
+}
diff --git a/Client/Driver/SqsDriver.php b/Client/Driver/SqsDriver.php
index 622c6f7..db94ad1 100644
--- a/Client/Driver/SqsDriver.php
+++ b/Client/Driver/SqsDriver.php
@@ -4,6 +4,7 @@
 
 use Enqueue\Sqs\SqsContext;
 use Enqueue\Sqs\SqsDestination;
+use Interop\Queue\PsrTopic;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -59,4 +60,12 @@ protected function createTransportQueueName(string $name, bool $prefix): string
 
         return str_replace('.', '_dot_', $name);
     }
+
+    /**
+     * @return SqsDestination
+     */
+    protected function createRouterTopic(): PsrTopic
+    {
+        return $this->createQueue($this->getConfig()->getRouterQueueName());
+    }
 }
diff --git a/Client/Resources.php b/Client/Resources.php
index 71f8c53..56fc764 100644
--- a/Client/Resources.php
+++ b/Client/Resources.php
@@ -11,6 +11,7 @@
 use Enqueue\Client\Driver\RabbitMqDriver;
 use Enqueue\Client\Driver\RabbitMqStompDriver;
 use Enqueue\Client\Driver\RdKafkaDriver;
+use Enqueue\Client\Driver\RedisDriver;
 use Enqueue\Client\Driver\SqsDriver;
 use Enqueue\Client\Driver\StompDriver;
 
@@ -81,7 +82,7 @@ public static function getKnownDrivers(): array
             ];
             $map[] = [
                 'schemes' => ['redis'],
-                'factoryClass' => GenericDriver::class,
+                'factoryClass' => RedisDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/redis'],
             ];
diff --git a/Tests/Client/Driver/AmqpDriverTest.php b/Tests/Client/Driver/AmqpDriverTest.php
index 91a7f52..e9c1d77 100644
--- a/Tests/Client/Driver/AmqpDriverTest.php
+++ b/Tests/Client/Driver/AmqpDriverTest.php
@@ -328,6 +328,11 @@ protected function createMessage(): PsrMessage
         return new AmqpMessage();
     }
 
+    protected function getRouterTransportName(): string
+    {
+        return 'aprefix.router';
+    }
+
     protected function assertTransportMessage(PsrMessage $transportMessage): void
     {
         $this->assertSame('body', $transportMessage->getBody());
diff --git a/Tests/Client/Driver/FsDriverTest.php b/Tests/Client/Driver/FsDriverTest.php
index f65cab0..bbc083b 100644
--- a/Tests/Client/Driver/FsDriverTest.php
+++ b/Tests/Client/Driver/FsDriverTest.php
@@ -37,7 +37,6 @@ public function testShouldBeSubClassOfGenericDriver()
 
     public function testShouldSetupBroker()
     {
-        $routerTopic = new FsDestination(TempFile::generate());
         $routerQueue = new FsDestination(TempFile::generate());
 
         $processorQueue = new FsDestination(TempFile::generate());
@@ -46,32 +45,22 @@ public function testShouldSetupBroker()
         // setup router
         $context
             ->expects($this->at(0))
-            ->method('createTopic')
-            ->willReturn($routerTopic)
-        ;
-        $context
-            ->expects($this->at(1))
             ->method('createQueue')
             ->willReturn($routerQueue)
         ;
         $context
-            ->expects($this->at(2))
-            ->method('declareDestination')
-            ->with($this->identicalTo($routerTopic))
-        ;
-        $context
-            ->expects($this->at(3))
+            ->expects($this->at(1))
             ->method('declareDestination')
             ->with($this->identicalTo($routerQueue))
         ;
         // setup processor queue
         $context
-            ->expects($this->at(4))
+            ->expects($this->at(2))
             ->method('createQueue')
             ->willReturn($processorQueue)
         ;
         $context
-            ->expects($this->at(5))
+            ->expects($this->at(3))
             ->method('declareDestination')
             ->with($this->identicalTo($processorQueue))
         ;
diff --git a/Tests/Client/Driver/GenericDriverTest.php b/Tests/Client/Driver/GenericDriverTest.php
index 31f92a5..ac4cd7a 100644
--- a/Tests/Client/Driver/GenericDriverTest.php
+++ b/Tests/Client/Driver/GenericDriverTest.php
@@ -56,6 +56,11 @@ protected function createMessage(): PsrMessage
         return new NullMessage();
     }
 
+    protected function getRouterTransportName(): string
+    {
+        return 'aprefix.router';
+    }
+
     protected function assertTransportMessage(PsrMessage $transportMessage): void
     {
         $this->assertSame('body', $transportMessage->getBody());
diff --git a/Tests/Client/Driver/GenericDriverTestsTrait.php b/Tests/Client/Driver/GenericDriverTestsTrait.php
index daafc66..78d2122 100644
--- a/Tests/Client/Driver/GenericDriverTestsTrait.php
+++ b/Tests/Client/Driver/GenericDriverTestsTrait.php
@@ -258,14 +258,12 @@ public function testShouldSendMessageToRouter()
         $producer
             ->expects($this->once())
             ->method('send')
-            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
-        ;
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createTopic')
-            ->willReturn($topic)
+            ->willReturnCallback(function (PsrTopic $topic, PsrMessage $message) use ($transportMessage) {
+                $this->assertSame($this->getRouterTransportName(), $topic->getTopicName());
+                $this->assertSame($transportMessage, $message);
+            })
         ;
+        $context = $this->createContextStub();
         $context
             ->expects($this->once())
             ->method('createProducer')
@@ -291,26 +289,19 @@ public function testShouldSendMessageToRouter()
 
     public function testShouldNotInitDeliveryDelayOnSendMessageToRouter()
     {
-        $topic = $this->createTopic('');
         $transportMessage = $this->createMessage();
 
         $producer = $this->createProducerMock();
         $producer
             ->expects($this->once())
             ->method('send')
-            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
         ;
         $producer
             ->expects($this->never())
             ->method('setDeliveryDelay')
         ;
 
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createTopic')
-            ->willReturn($topic)
-        ;
+        $context = $this->createContextStub();
         $context
             ->expects($this->once())
             ->method('createProducer')
@@ -337,26 +328,19 @@ public function testShouldNotInitDeliveryDelayOnSendMessageToRouter()
 
     public function testShouldNotInitTimeToLiveOnSendMessageToRouter()
     {
-        $topic = $this->createTopic('');
         $transportMessage = $this->createMessage();
 
         $producer = $this->createProducerMock();
         $producer
             ->expects($this->once())
             ->method('send')
-            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
         ;
         $producer
             ->expects($this->never())
             ->method('setTimeToLive')
         ;
 
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createTopic')
-            ->willReturn($topic)
-        ;
+        $context = $this->createContextStub();
         $context
             ->expects($this->once())
             ->method('createProducer')
@@ -383,26 +367,19 @@ public function testShouldNotInitTimeToLiveOnSendMessageToRouter()
 
     public function testShouldNotInitPriorityOnSendMessageToRouter()
     {
-        $topic = $this->createTopic('');
         $transportMessage = $this->createMessage();
 
         $producer = $this->createProducerMock();
         $producer
             ->expects($this->once())
             ->method('send')
-            ->with($this->identicalTo($topic), $this->identicalTo($transportMessage))
         ;
         $producer
             ->expects($this->never())
             ->method('setPriority')
         ;
 
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->once())
-            ->method('createTopic')
-            ->willReturn($topic)
-        ;
+        $context = $this->createContextStub();
         $context
             ->expects($this->once())
             ->method('createProducer')
@@ -1104,6 +1081,32 @@ abstract protected function createTopic(string $name): PsrTopic;
 
     abstract protected function createMessage(): PsrMessage;
 
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected function createContextStub(): PsrContext
+    {
+        $context = $this->createContextMock();
+
+        $context
+            ->expects($this->any())
+            ->method('createQueue')
+            ->willReturnCallback(function (string $name) {
+                return $this->createQueue($name);
+            })
+        ;
+
+        $context
+            ->expects($this->any())
+            ->method('createTopic')
+            ->willReturnCallback(function (string $name) {
+                return $this->createTopic($name);
+            })
+        ;
+
+        return $context;
+    }
+
     protected function assertTransportMessage(PsrMessage $transportMessage): void
     {
         $this->assertSame('body', $transportMessage->getBody());
@@ -1165,6 +1168,11 @@ protected function getCustomQueueTransportName(): string
         return 'aprefix.custom';
     }
 
+    protected function getRouterTransportName(): string
+    {
+        return 'aprefix.default';
+    }
+
     protected function getPrefixAppFooQueueTransportName(): string
     {
         return 'aprefix.anappname.afooqueue';
diff --git a/Tests/Client/Driver/GpsDriverTest.php b/Tests/Client/Driver/GpsDriverTest.php
index 15b8e63..8b48a36 100644
--- a/Tests/Client/Driver/GpsDriverTest.php
+++ b/Tests/Client/Driver/GpsDriverTest.php
@@ -134,4 +134,9 @@ protected function createMessage(): PsrMessage
     {
         return new GpsMessage();
     }
+
+    protected function getRouterTransportName(): string
+    {
+        return 'aprefix.router';
+    }
 }
diff --git a/Tests/Client/Driver/RabbitMqDriverTest.php b/Tests/Client/Driver/RabbitMqDriverTest.php
index 8f27232..295ac72 100644
--- a/Tests/Client/Driver/RabbitMqDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqDriverTest.php
@@ -106,6 +106,11 @@ protected function createMessage(): PsrMessage
         return new AmqpMessage();
     }
 
+    protected function getRouterTransportName(): string
+    {
+        return 'aprefix.router';
+    }
+
     protected function assertTransportMessage(PsrMessage $transportMessage): void
     {
         $this->assertSame('body', $transportMessage->getBody());
diff --git a/Tests/Client/Driver/RabbitMqStompDriverTest.php b/Tests/Client/Driver/RabbitMqStompDriverTest.php
index 8a866a8..c055d8b 100644
--- a/Tests/Client/Driver/RabbitMqStompDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqStompDriverTest.php
@@ -497,7 +497,11 @@ protected function createProducerMock(): PsrProducer
      */
     protected function createQueue(string $name): PsrQueue
     {
-        return new StompDestination();
+        $destination = new StompDestination();
+        $destination->setType(StompDestination::TYPE_QUEUE);
+        $destination->setStompName($name);
+
+        return $destination;
     }
 
     /**
@@ -505,7 +509,11 @@ protected function createQueue(string $name): PsrQueue
      */
     protected function createTopic(string $name): PsrTopic
     {
-        return new StompDestination();
+        $destination = new StompDestination();
+        $destination->setType(StompDestination::TYPE_TOPIC);
+        $destination->setStompName($name);
+
+        return $destination;
     }
 
     /**
@@ -556,6 +564,11 @@ protected function createDummyConfig(): Config
         );
     }
 
+    protected function getRouterTransportName(): string
+    {
+        return '/topic/aprefix.router';
+    }
+
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
diff --git a/Tests/Client/Driver/RdKafkaDriverTest.php b/Tests/Client/Driver/RdKafkaDriverTest.php
index 3c89fe1..899830e 100644
--- a/Tests/Client/Driver/RdKafkaDriverTest.php
+++ b/Tests/Client/Driver/RdKafkaDriverTest.php
@@ -118,6 +118,11 @@ protected function createMessage(): PsrMessage
         return new RdKafkaMessage();
     }
 
+    protected function getRouterTransportName(): string
+    {
+        return 'aprefix.router';
+    }
+
     /**
      * @return Config
      */
diff --git a/Tests/Client/Driver/SqsDriverTest.php b/Tests/Client/Driver/SqsDriverTest.php
index 3888326..bf8bef1 100644
--- a/Tests/Client/Driver/SqsDriverTest.php
+++ b/Tests/Client/Driver/SqsDriverTest.php
@@ -145,4 +145,9 @@ protected function getCustomQueueTransportName(): string
     {
         return 'aprefix_dot_custom';
     }
+
+    protected function getRouterTransportName(): string
+    {
+        return 'aprefix_dot_default';
+    }
 }
diff --git a/Tests/Client/Driver/StompDriverTest.php b/Tests/Client/Driver/StompDriverTest.php
index 556d593..b68e63e 100644
--- a/Tests/Client/Driver/StompDriverTest.php
+++ b/Tests/Client/Driver/StompDriverTest.php
@@ -126,7 +126,11 @@ protected function createProducerMock(): PsrProducer
      */
     protected function createQueue(string $name): PsrQueue
     {
-        return new StompDestination();
+        $destination = new StompDestination();
+        $destination->setType(StompDestination::TYPE_QUEUE);
+        $destination->setStompName($name);
+
+        return $destination;
     }
 
     /**
@@ -134,7 +138,11 @@ protected function createQueue(string $name): PsrQueue
      */
     protected function createTopic(string $name): PsrTopic
     {
-        return new StompDestination();
+        $destination = new StompDestination();
+        $destination->setType(StompDestination::TYPE_TOPIC);
+        $destination->setStompName($name);
+
+        return $destination;
     }
 
     /**
@@ -173,4 +181,9 @@ protected function createLoggerMock(): LoggerInterface
     {
         return $this->createMock(LoggerInterface::class);
     }
+
+    protected function getRouterTransportName(): string
+    {
+        return '/topic/aprefix.router';
+    }
 }

From c2c40033ac34c0e18a9badbdfd33c31a3aeb9e28 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 24 Sep 2018 11:16:14 +0300
Subject: [PATCH 132/337] [client] Generic driver should not support message
 bus

It should message to router queue, ignoring router topic options.
If a driver (that extends generic one) supports message bus feature
(routing between applications) it should overwrite createRouterTopic
method.
---
 Client/Driver/AmqpDriver.php                  | 31 ++++++++++---------
 Client/Driver/DbalDriver.php                  | 10 ------
 Client/Driver/FsDriver.php                    |  9 ------
 Client/Driver/GenericDriver.php               |  9 +++---
 Client/Driver/GpsDriver.php                   | 12 ++++++-
 Client/Driver/MongodbDriver.php               | 10 ------
 Client/Driver/RabbitMqStompDriver.php         |  4 +--
 Client/Driver/RdKafkaDriver.php               | 11 +++++++
 Client/Driver/RedisDriver.php                 |  9 ------
 Client/Driver/SqsDriver.php                   |  9 ------
 Client/Driver/StompDriver.php                 |  8 +++--
 Tests/Client/Driver/GenericDriverTest.php     |  5 ---
 .../Client/Driver/GenericDriverTestsTrait.php |  8 +++--
 13 files changed, 54 insertions(+), 81 deletions(-)

diff --git a/Client/Driver/AmqpDriver.php b/Client/Driver/AmqpDriver.php
index 875cc80..0d77d11 100644
--- a/Client/Driver/AmqpDriver.php
+++ b/Client/Driver/AmqpDriver.php
@@ -11,10 +11,10 @@
 use Interop\Amqp\AmqpQueue;
 use Interop\Amqp\AmqpTopic;
 use Interop\Amqp\Impl\AmqpBind;
+use Interop\Queue\PsrDestination;
 use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrProducer;
 use Interop\Queue\PsrQueue;
-use Interop\Queue\PsrTopic;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -93,6 +93,20 @@ public function setupBroker(LoggerInterface $logger = null): void
         }
     }
 
+    /**
+     * @return AmqpTopic
+     */
+    protected function createRouterTopic(): PsrDestination
+    {
+        $topic = $this->doCreateTopic(
+            $this->createTransportRouterTopicName($this->getConfig()->getRouterTopicName(), true)
+        );
+        $topic->setType(AmqpTopic::TYPE_FANOUT);
+        $topic->addFlag(AmqpTopic::FLAG_DURABLE);
+
+        return $topic;
+    }
+
     /**
      * @return AmqpQueue
      */
@@ -110,7 +124,7 @@ protected function doCreateQueue(string $transportQueueName): PsrQueue
      * @param AmqpTopic    $topic
      * @param AmqpMessage  $transportMessage
      */
-    protected function doSendToRouter(PsrProducer $producer, PsrTopic $topic, PsrMessage $transportMessage): void
+    protected function doSendToRouter(PsrProducer $producer, PsrDestination $topic, PsrMessage $transportMessage): void
     {
         // We should not handle priority, expiration, and delay at this stage.
         // The router will take care of it while re-sending the message to the final destinations.
@@ -119,17 +133,4 @@ protected function doSendToRouter(PsrProducer $producer, PsrTopic $topic, PsrMes
 
         $producer->send($topic, $transportMessage);
     }
-
-    /**
-     * @return AmqpTopic
-     */
-    protected function createRouterTopic(): PsrTopic
-    {
-        /** @var AmqpTopic $topic */
-        $topic = parent::createRouterTopic();
-        $topic->setType(AmqpTopic::TYPE_FANOUT);
-        $topic->addFlag(AmqpTopic::FLAG_DURABLE);
-
-        return $topic;
-    }
 }
diff --git a/Client/Driver/DbalDriver.php b/Client/Driver/DbalDriver.php
index 165bcf7..8b1f326 100644
--- a/Client/Driver/DbalDriver.php
+++ b/Client/Driver/DbalDriver.php
@@ -3,8 +3,6 @@
 namespace Enqueue\Client\Driver;
 
 use Enqueue\Dbal\DbalContext;
-use Enqueue\Dbal\DbalDestination;
-use Interop\Queue\PsrTopic;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -28,12 +26,4 @@ public function setupBroker(LoggerInterface $logger = null): void
         $log('Creating database table: "%s"', $this->getContext()->getTableName());
         $this->getContext()->createDataBaseTable();
     }
-
-    /**
-     * @return DbalDestination
-     */
-    protected function createRouterTopic(): PsrTopic
-    {
-        return $this->createQueue($this->getConfig()->getRouterQueueName());
-    }
 }
diff --git a/Client/Driver/FsDriver.php b/Client/Driver/FsDriver.php
index 1ce7032..7931ea3 100644
--- a/Client/Driver/FsDriver.php
+++ b/Client/Driver/FsDriver.php
@@ -4,7 +4,6 @@
 
 use Enqueue\Fs\FsContext;
 use Enqueue\Fs\FsDestination;
-use Interop\Queue\PsrTopic;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -47,12 +46,4 @@ public function setupBroker(LoggerInterface $logger = null): void
             $declaredQueues[$queue->getQueueName()] = true;
         }
     }
-
-    /**
-     * @return FsDestination
-     */
-    protected function createRouterTopic(): PsrTopic
-    {
-        return $this->createQueue($this->getConfig()->getRouterQueueName());
-    }
 }
diff --git a/Client/Driver/GenericDriver.php b/Client/Driver/GenericDriver.php
index 2cb8ca4..634eaf4 100644
--- a/Client/Driver/GenericDriver.php
+++ b/Client/Driver/GenericDriver.php
@@ -11,6 +11,7 @@
 use Enqueue\Client\Route;
 use Enqueue\Client\RouteCollection;
 use Interop\Queue\PsrContext;
+use Interop\Queue\PsrDestination;
 use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrProducer;
 use Interop\Queue\PsrQueue;
@@ -212,7 +213,7 @@ public function getRouteCollection(): RouteCollection
         return $this->routeCollection;
     }
 
-    protected function doSendToRouter(PsrProducer $producer, PsrTopic $topic, PsrMessage $transportMessage): void
+    protected function doSendToRouter(PsrProducer $producer, PsrDestination $topic, PsrMessage $transportMessage): void
     {
         $producer->send($topic, $transportMessage);
     }
@@ -222,11 +223,9 @@ protected function doSendToProcessor(PsrProducer $producer, PsrQueue $queue, Psr
         $producer->send($queue, $transportMessage);
     }
 
-    protected function createRouterTopic(): PsrTopic
+    protected function createRouterTopic(): PsrDestination
     {
-        return $this->doCreateTopic(
-            $this->createTransportRouterTopicName($this->config->getRouterTopicName(), true)
-        );
+        return $this->createQueue($this->getConfig()->getRouterQueueName());
     }
 
     protected function createTransportRouterTopicName(string $name, bool $prefix): string
diff --git a/Client/Driver/GpsDriver.php b/Client/Driver/GpsDriver.php
index 01dc943..572faac 100644
--- a/Client/Driver/GpsDriver.php
+++ b/Client/Driver/GpsDriver.php
@@ -5,13 +5,13 @@
 use Enqueue\Gps\GpsContext;
 use Enqueue\Gps\GpsQueue;
 use Enqueue\Gps\GpsTopic;
+use Interop\Queue\PsrDestination;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
 /**
  * @method GpsContext getContext
  * @method GpsQueue createQueue(string $name)
- * @method GpsTopic createRouterTopic
  */
 class GpsDriver extends GenericDriver
 {
@@ -51,4 +51,14 @@ public function setupBroker(LoggerInterface $logger = null): void
             $declaredQueues[$queue->getQueueName()] = true;
         }
     }
+
+    /**
+     * @return GpsTopic
+     */
+    protected function createRouterTopic(): PsrDestination
+    {
+        return $this->doCreateTopic(
+            $this->createTransportRouterTopicName($this->getConfig()->getRouterTopicName(), true)
+        );
+    }
 }
diff --git a/Client/Driver/MongodbDriver.php b/Client/Driver/MongodbDriver.php
index ed31727..19f2c57 100644
--- a/Client/Driver/MongodbDriver.php
+++ b/Client/Driver/MongodbDriver.php
@@ -3,8 +3,6 @@
 namespace Enqueue\Client\Driver;
 
 use Enqueue\Mongodb\MongodbContext;
-use Enqueue\Mongodb\MongodbDestination;
-use Interop\Queue\PsrTopic;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -29,12 +27,4 @@ public function setupBroker(LoggerInterface $logger = null): void
         $log('Creating database and collection: "%s" "%s"', $contextConfig['dbname'], $contextConfig['collection_name']);
         $this->getContext()->createCollection();
     }
-
-    /**
-     * @return MongodbDestination
-     */
-    protected function createRouterTopic(): PsrTopic
-    {
-        return $this->createQueue($this->getConfig()->getRouterQueueName());
-    }
 }
diff --git a/Client/Driver/RabbitMqStompDriver.php b/Client/Driver/RabbitMqStompDriver.php
index 4dd72f6..ace5209 100644
--- a/Client/Driver/RabbitMqStompDriver.php
+++ b/Client/Driver/RabbitMqStompDriver.php
@@ -9,10 +9,10 @@
 use Enqueue\Stomp\StompDestination;
 use Enqueue\Stomp\StompMessage;
 use Enqueue\Stomp\StompProducer;
+use Interop\Queue\PsrDestination;
 use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrProducer;
 use Interop\Queue\PsrQueue;
-use Interop\Queue\PsrTopic;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -151,7 +151,7 @@ protected function doCreateQueue(string $transportQueueName): PsrQueue
      * @param StompDestination $topic
      * @param StompMessage     $transportMessage
      */
-    protected function doSendToRouter(PsrProducer $producer, PsrTopic $topic, PsrMessage $transportMessage): void
+    protected function doSendToRouter(PsrProducer $producer, PsrDestination $topic, PsrMessage $transportMessage): void
     {
         // We should not handle priority, expiration, and delay at this stage.
         // The router will take care of it while re-sending the message to the final destinations.
diff --git a/Client/Driver/RdKafkaDriver.php b/Client/Driver/RdKafkaDriver.php
index 01b9e27..f83677d 100644
--- a/Client/Driver/RdKafkaDriver.php
+++ b/Client/Driver/RdKafkaDriver.php
@@ -4,6 +4,7 @@
 
 use Enqueue\RdKafka\RdKafkaContext;
 use Enqueue\RdKafka\RdKafkaTopic;
+use Interop\Queue\PsrDestination;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -43,4 +44,14 @@ public function setupBroker(LoggerInterface $logger = null): void
             $this->getContext()->createConsumer($queue);
         }
     }
+
+    /**
+     * @return RdKafkaTopic
+     */
+    protected function createRouterTopic(): PsrDestination
+    {
+        return $this->doCreateTopic(
+            $this->createTransportRouterTopicName($this->getConfig()->getRouterTopicName(), true)
+        );
+    }
 }
diff --git a/Client/Driver/RedisDriver.php b/Client/Driver/RedisDriver.php
index 264409e..641d3bf 100644
--- a/Client/Driver/RedisDriver.php
+++ b/Client/Driver/RedisDriver.php
@@ -4,7 +4,6 @@
 
 use Enqueue\Redis\RedisContext;
 use Enqueue\Redis\RedisDestination;
-use Interop\Queue\PsrTopic;
 
 /**
  * @method RedisContext getContext
@@ -16,12 +15,4 @@ public function __construct(RedisContext $context, ...$args)
     {
         parent::__construct($context, ...$args);
     }
-
-    /**
-     * @return RedisDestination
-     */
-    protected function createRouterTopic(): PsrTopic
-    {
-        return $this->createQueue($this->getConfig()->getRouterQueueName());
-    }
 }
diff --git a/Client/Driver/SqsDriver.php b/Client/Driver/SqsDriver.php
index db94ad1..622c6f7 100644
--- a/Client/Driver/SqsDriver.php
+++ b/Client/Driver/SqsDriver.php
@@ -4,7 +4,6 @@
 
 use Enqueue\Sqs\SqsContext;
 use Enqueue\Sqs\SqsDestination;
-use Interop\Queue\PsrTopic;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -60,12 +59,4 @@ protected function createTransportQueueName(string $name, bool $prefix): string
 
         return str_replace('.', '_dot_', $name);
     }
-
-    /**
-     * @return SqsDestination
-     */
-    protected function createRouterTopic(): PsrTopic
-    {
-        return $this->createQueue($this->getConfig()->getRouterQueueName());
-    }
 }
diff --git a/Client/Driver/StompDriver.php b/Client/Driver/StompDriver.php
index 8ef9046..95cc8b7 100644
--- a/Client/Driver/StompDriver.php
+++ b/Client/Driver/StompDriver.php
@@ -6,9 +6,9 @@
 use Enqueue\Stomp\StompContext;
 use Enqueue\Stomp\StompDestination;
 use Enqueue\Stomp\StompMessage;
+use Interop\Queue\PsrDestination;
 use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrQueue;
-use Interop\Queue\PsrTopic;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -57,10 +57,12 @@ protected function doCreateQueue(string $transportQueueName): PsrQueue
     /**
      * @return StompDestination
      */
-    protected function createRouterTopic(): PsrTopic
+    protected function createRouterTopic(): PsrDestination
     {
         /** @var StompDestination $topic */
-        $topic = parent::createRouterTopic();
+        $topic = $this->doCreateTopic(
+            $this->createTransportRouterTopicName($this->getConfig()->getRouterTopicName(), true)
+        );
         $topic->setDurable(true);
         $topic->setAutoDelete(false);
 
diff --git a/Tests/Client/Driver/GenericDriverTest.php b/Tests/Client/Driver/GenericDriverTest.php
index ac4cd7a..31f92a5 100644
--- a/Tests/Client/Driver/GenericDriverTest.php
+++ b/Tests/Client/Driver/GenericDriverTest.php
@@ -56,11 +56,6 @@ protected function createMessage(): PsrMessage
         return new NullMessage();
     }
 
-    protected function getRouterTransportName(): string
-    {
-        return 'aprefix.router';
-    }
-
     protected function assertTransportMessage(PsrMessage $transportMessage): void
     {
         $this->assertSame('body', $transportMessage->getBody());
diff --git a/Tests/Client/Driver/GenericDriverTestsTrait.php b/Tests/Client/Driver/GenericDriverTestsTrait.php
index 78d2122..5d5d4c8 100644
--- a/Tests/Client/Driver/GenericDriverTestsTrait.php
+++ b/Tests/Client/Driver/GenericDriverTestsTrait.php
@@ -9,6 +9,7 @@
 use Enqueue\Client\Route;
 use Enqueue\Client\RouteCollection;
 use Interop\Queue\PsrContext;
+use Interop\Queue\PsrDestination;
 use Interop\Queue\PsrMessage;
 use Interop\Queue\PsrProducer;
 use Interop\Queue\PsrQueue;
@@ -251,15 +252,16 @@ public function testShouldCreateTransportMessageFromClientOne()
 
     public function testShouldSendMessageToRouter()
     {
-        $topic = $this->createTopic('');
         $transportMessage = $this->createMessage();
 
         $producer = $this->createProducerMock();
         $producer
             ->expects($this->once())
             ->method('send')
-            ->willReturnCallback(function (PsrTopic $topic, PsrMessage $message) use ($transportMessage) {
-                $this->assertSame($this->getRouterTransportName(), $topic->getTopicName());
+            ->willReturnCallback(function (PsrDestination $topic, PsrMessage $message) use ($transportMessage) {
+                $this->assertSame(
+                    $this->getRouterTransportName(),
+                    $topic instanceof PsrTopic ? $topic->getTopicName() : $topic->getQueueName());
                 $this->assertSame($transportMessage, $message);
             })
         ;

From 946e400a43e74c870e8542ede69214a8da9581d8 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 27 Sep 2018 14:04:31 +0300
Subject: [PATCH 133/337] Update enqueue architecture. Get rid of meta
 (queue|topic), rely one routes.

---
 Client/ArrayProcessorRegistry.php             |  11 +-
 Client/ChainExtension.php                     |   1 +
 Client/CommandSubscriberInterface.php         |  29 +-
 Client/Driver/GenericDriver.php               |   4 +-
 Client/DriverInterface.php                    |   2 +-
 Client/Meta/QueueMeta.php                     |  57 ----
 Client/Meta/QueueMetaRegistry.php             |  95 -------
 Client/Meta/TopicMeta.php                     |  57 ----
 Client/Meta/TopicMetaRegistry.php             |  80 ------
 Client/ProcessorRegistryInterface.php         |   7 +-
 Client/TopicSubscriberInterface.php           |   3 +-
 Consumption/ChainExtension.php                |   5 +-
 Symfony/Client/ConsumeMessagesCommand.php     |  58 ++--
 .../ContainerAwareProcessorRegistry.php       |  61 ----
 Symfony/Client/ContainerProcessorRegistry.php |  29 ++
 .../AnalyzeRouteCollectionPass.php            |   2 +-
 .../BuildClientExtensionsPass.php             |  65 +++++
 .../BuildCommandSubscriberRoutesPass.php      |   6 +-
 .../BuildConsumptionExtensionsPass.php        |  65 +++++
 .../BuildProcessorRegistryPass.php            |  18 +-
 .../BuildProcessorRoutesPass.php              |   6 +-
 .../BuildTopicSubscriberRoutesPass.php        |   2 +-
 Symfony/Client/Meta/QueuesCommand.php         |  74 -----
 Symfony/Client/Meta/TopicsCommand.php         |  70 -----
 Symfony/Client/RoutesCommand.php              | 131 +++++++++
 Symfony/DependencyInjection/ClientFactory.php |  48 ++++
 .../DependencyInjection/TransportFactory.php  |  32 +--
 Tests/Client/ChainExtensionTest.php           |   7 +
 Tests/Client/Meta/QueueMetaRegistryTest.php   | 146 ----------
 Tests/Client/Meta/QueueMetaTest.php           |  39 ---
 Tests/Client/Meta/TopicMetaRegistryTest.php   | 124 ---------
 Tests/Client/Meta/TopicMetaTest.php           |  57 ----
 .../Client/ConsumeMessagesCommandTest.php     | 242 ++++++++++++----
 .../ContainerAwareProcessorRegistryTest.php   |  84 ------
 .../AnalyzeRouteCollectionPassTest.php        |   4 +-
 .../BuildClientExtensionsPassTest.php         | 240 ++++++++++++++++
 .../BuildCommandSubscriberRoutesPassTest.php  |  42 ++-
 .../BuildConsumptionExtensionsPassTest.php    | 240 ++++++++++++++++
 .../BuildProcessorRegistryPassTest.php        |  69 ++---
 .../BuildProcessorRoutesPassTest.php          |   8 +-
 .../BuildTopicSubscriberRoutesPassTest.php    |   4 +-
 .../Symfony/Client/Meta/QueuesCommandTest.php | 109 --------
 .../Symfony/Client/Meta/TopicsCommandTest.php |  91 ------
 Tests/Symfony/Client/RoutesCommandTest.php    | 262 ++++++++++++++++++
 .../DependencyInjection/ClientFactoryTest.php |  61 ++++
 .../TransportFactoryTest.php                  |  65 +----
 46 files changed, 1489 insertions(+), 1423 deletions(-)
 delete mode 100644 Client/Meta/QueueMeta.php
 delete mode 100644 Client/Meta/QueueMetaRegistry.php
 delete mode 100644 Client/Meta/TopicMeta.php
 delete mode 100644 Client/Meta/TopicMetaRegistry.php
 delete mode 100644 Symfony/Client/ContainerAwareProcessorRegistry.php
 create mode 100644 Symfony/Client/ContainerProcessorRegistry.php
 rename Symfony/{ => Client}/DependencyInjection/AnalyzeRouteCollectionPass.php (98%)
 create mode 100644 Symfony/Client/DependencyInjection/BuildClientExtensionsPass.php
 rename Symfony/{ => Client}/DependencyInjection/BuildCommandSubscriberRoutesPass.php (96%)
 create mode 100644 Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php
 rename Symfony/{ => Client}/DependencyInjection/BuildProcessorRegistryPass.php (69%)
 rename Symfony/{ => Client}/DependencyInjection/BuildProcessorRoutesPass.php (87%)
 rename Symfony/{ => Client}/DependencyInjection/BuildTopicSubscriberRoutesPass.php (98%)
 delete mode 100644 Symfony/Client/Meta/QueuesCommand.php
 delete mode 100644 Symfony/Client/Meta/TopicsCommand.php
 create mode 100644 Symfony/Client/RoutesCommand.php
 create mode 100644 Symfony/DependencyInjection/ClientFactory.php
 delete mode 100644 Tests/Client/Meta/QueueMetaRegistryTest.php
 delete mode 100644 Tests/Client/Meta/QueueMetaTest.php
 delete mode 100644 Tests/Client/Meta/TopicMetaRegistryTest.php
 delete mode 100644 Tests/Client/Meta/TopicMetaTest.php
 delete mode 100644 Tests/Symfony/Client/ContainerAwareProcessorRegistryTest.php
 rename Tests/Symfony/{ => Client}/DependencyInjection/AnalyzeRouteCollectionPassTest.php (97%)
 create mode 100644 Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php
 rename Tests/Symfony/{ => Client}/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php (89%)
 create mode 100644 Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php
 rename Tests/Symfony/{ => Client}/DependencyInjection/BuildProcessorRegistryPassTest.php (70%)
 rename Tests/Symfony/{ => Client}/DependencyInjection/BuildProcessorRoutesPassTest.php (97%)
 rename Tests/Symfony/{ => Client}/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php (98%)
 delete mode 100644 Tests/Symfony/Client/Meta/QueuesCommandTest.php
 delete mode 100644 Tests/Symfony/Client/Meta/TopicsCommandTest.php
 create mode 100644 Tests/Symfony/Client/RoutesCommandTest.php
 create mode 100644 Tests/Symfony/DependencyInjection/ClientFactoryTest.php

diff --git a/Client/ArrayProcessorRegistry.php b/Client/ArrayProcessorRegistry.php
index d6cb300..bbe1cfe 100644
--- a/Client/ArrayProcessorRegistry.php
+++ b/Client/ArrayProcessorRegistry.php
@@ -19,19 +19,12 @@ public function __construct(array $processors = [])
         $this->processors = $processors;
     }
 
-    /**
-     * @param string       $name
-     * @param PsrProcessor $processor
-     */
-    public function add($name, PsrProcessor $processor)
+    public function add(string $name, PsrProcessor $processor): void
     {
         $this->processors[$name] = $processor;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function get($processorName)
+    public function get(string $processorName): PsrProcessor
     {
         if (false == isset($this->processors[$processorName])) {
             throw new \LogicException(sprintf('Processor was not found. processorName: "%s"', $processorName));
diff --git a/Client/ChainExtension.php b/Client/ChainExtension.php
index 603b2b6..ba0fc88 100644
--- a/Client/ChainExtension.php
+++ b/Client/ChainExtension.php
@@ -14,6 +14,7 @@ final class ChainExtension implements ExtensionInterface
      */
     public function __construct(array $extensions)
     {
+        $this->extensions = [];
         array_walk($extensions, function (ExtensionInterface $extension) {
             $this->extensions[] = $extension;
         });
diff --git a/Client/CommandSubscriberInterface.php b/Client/CommandSubscriberInterface.php
index 99f6b27..5bbe78e 100644
--- a/Client/CommandSubscriberInterface.php
+++ b/Client/CommandSubscriberInterface.php
@@ -12,13 +12,34 @@ interface CommandSubscriberInterface
      * or
      *
      * [
-     *   'processorName' => 'aCommandName',
-     *   'queueName' => 'a_client_queue_name',
-     *   'queueNameHardcoded' => true,
+     *   'command' => 'aSubscribedCommand',
+     *   'processor' => 'aProcessorName',
+     *   'queue' => 'a_client_queue_name',
+     *   'prefix_queue' => true,
      *   'exclusive' => true,
      * ]
      *
-     * queueName, exclusive and queueNameHardcoded are optional.
+     * or
+     *
+     * [
+     *   [
+     *     'command' => 'aSubscribedCommand',
+     *     'processor' => 'aProcessorName',
+     *     'queue' => 'a_client_queue_name',
+     *     'prefix_queue' => true,
+     *     'exclusive' => true,
+     *   ],
+     *   [
+     *     'command' => 'aSubscribedCommand',
+     *     'processor' => 'aProcessorName',
+     *     'queue' => 'a_client_queue_name',
+     *     'prefix_queue' => true,
+     *     'exclusive' => true,
+     *   ]
+     * ]
+     *
+     * queue, processor, prefix_queue, and exclusive are optional.
+     * It is possible to pass other options, they could be accessible on a route instance through options.
      *
      * Note: If you set queueNameHardcoded to true then the queueName is used as is and therefor the driver is not used to create a transport queue name.
      *
diff --git a/Client/Driver/GenericDriver.php b/Client/Driver/GenericDriver.php
index 634eaf4..e7b2e93 100644
--- a/Client/Driver/GenericDriver.php
+++ b/Client/Driver/GenericDriver.php
@@ -117,9 +117,9 @@ public function setupBroker(LoggerInterface $logger = null): void
     {
     }
 
-    public function createQueue(string $clientQueueName): PsrQueue
+    public function createQueue(string $clientQueueName, bool $prefix = true): PsrQueue
     {
-        $transportName = $this->createTransportQueueName($clientQueueName, true);
+        $transportName = $this->createTransportQueueName($clientQueueName, $prefix);
 
         return $this->doCreateQueue($transportName);
     }
diff --git a/Client/DriverInterface.php b/Client/DriverInterface.php
index 96144a5..23d0661 100644
--- a/Client/DriverInterface.php
+++ b/Client/DriverInterface.php
@@ -19,7 +19,7 @@ public function sendToRouter(Message $message): void;
 
     public function sendToProcessor(Message $message): void;
 
-    public function createQueue(string $queueName): PsrQueue;
+    public function createQueue(string $queueName, bool $prefix = true): PsrQueue;
 
     public function createRouteQueue(Route $route): PsrQueue;
 
diff --git a/Client/Meta/QueueMeta.php b/Client/Meta/QueueMeta.php
deleted file mode 100644
index bee32bd..0000000
--- a/Client/Meta/QueueMeta.php
+++ /dev/null
@@ -1,57 +0,0 @@
-<?php
-
-namespace Enqueue\Client\Meta;
-
-class QueueMeta
-{
-    /**
-     * @var string
-     */
-    private $clientName;
-
-    /**
-     * @var string
-     */
-    private $transportName;
-
-    /**
-     * @var string[]
-     */
-    private $processors;
-
-    /**
-     * @param string   $clientName
-     * @param string   $transportName
-     * @param string[] $processors
-     */
-    public function __construct($clientName, $transportName, array $processors = [])
-    {
-        $this->clientName = $clientName;
-        $this->transportName = $transportName;
-        $this->processors = $processors;
-    }
-
-    /**
-     * @return string
-     */
-    public function getClientName()
-    {
-        return $this->clientName;
-    }
-
-    /**
-     * @return string
-     */
-    public function getTransportName()
-    {
-        return $this->transportName;
-    }
-
-    /**
-     * @return string[]
-     */
-    public function getProcessors()
-    {
-        return $this->processors;
-    }
-}
diff --git a/Client/Meta/QueueMetaRegistry.php b/Client/Meta/QueueMetaRegistry.php
deleted file mode 100644
index 29c2d69..0000000
--- a/Client/Meta/QueueMetaRegistry.php
+++ /dev/null
@@ -1,95 +0,0 @@
-<?php
-
-namespace Enqueue\Client\Meta;
-
-use Enqueue\Client\Config;
-
-class QueueMetaRegistry
-{
-    /**
-     * @var array
-     */
-    private $meta;
-
-    /**
-     * @var Config
-     */
-    private $config;
-
-    /**
-     * $meta = [
-     *   'aQueueName' => [
-     *     'transportName' => 'aTransportQueueName',
-     *     'processors' => ['aFooProcessorName', 'aBarProcessorName'],
-     *   ]
-     * ].
-     *
-     *
-     * @param Config $config
-     * @param array  $meta
-     */
-    public function __construct(Config $config, array $meta)
-    {
-        $this->config = $config;
-        $this->meta = $meta;
-    }
-
-    /**
-     * @param string      $queueName
-     * @param string|null $transportName
-     */
-    public function add($queueName, $transportName = null)
-    {
-        $this->meta[$queueName] = [
-            'transportName' => $transportName,
-            'processors' => [],
-        ];
-    }
-
-    /**
-     * @param string $queueName
-     * @param string $processorName
-     */
-    public function addProcessor($queueName, $processorName)
-    {
-        if (false == array_key_exists($queueName, $this->meta)) {
-            $this->add($queueName);
-        }
-
-        $this->meta[$queueName]['processors'][] = $processorName;
-    }
-
-    /**
-     * @param string $queueName
-     *
-     * @return QueueMeta
-     */
-    public function getQueueMeta($queueName)
-    {
-        if (false == array_key_exists($queueName, $this->meta)) {
-            throw new \InvalidArgumentException(sprintf(
-                'The queue meta not found. Requested name `%s`',
-                $queueName
-            ));
-        }
-
-        $transportName = $this->config->createTransportQueueName($queueName);
-
-        $meta = array_replace([
-            'processors' => [],
-            'transportName' => $transportName,
-        ], array_filter($this->meta[$queueName]));
-
-        return new QueueMeta($queueName, $meta['transportName'], $meta['processors']);
-    }
-
-    /**
-     * @return \Generator|QueueMeta[]
-     */
-    public function getQueuesMeta()
-    {
-        foreach (array_keys($this->meta) as $queueName) {
-            yield $this->getQueueMeta($queueName);
-        }
-    }
-}
diff --git a/Client/Meta/TopicMeta.php b/Client/Meta/TopicMeta.php
deleted file mode 100644
index abb0e33..0000000
--- a/Client/Meta/TopicMeta.php
+++ /dev/null
@@ -1,57 +0,0 @@
-<?php
-
-namespace Enqueue\Client\Meta;
-
-class TopicMeta
-{
-    /**
-     * @var string
-     */
-    private $name;
-
-    /**
-     * @var string
-     */
-    private $description;
-
-    /**
-     * @var string[]
-     */
-    private $processors;
-
-    /**
-     * @param string   $name
-     * @param string   $description
-     * @param string[] $processors
-     */
-    public function __construct($name, $description = '', array $processors = [])
-    {
-        $this->name = $name;
-        $this->description = $description;
-        $this->processors = $processors;
-    }
-
-    /**
-     * @return string
-     */
-    public function getName()
-    {
-        return $this->name;
-    }
-
-    /**
-     * @return string
-     */
-    public function getDescription()
-    {
-        return $this->description;
-    }
-
-    /**
-     * @return string[]
-     */
-    public function getProcessors()
-    {
-        return $this->processors;
-    }
-}
diff --git a/Client/Meta/TopicMetaRegistry.php b/Client/Meta/TopicMetaRegistry.php
deleted file mode 100644
index efceb9a..0000000
--- a/Client/Meta/TopicMetaRegistry.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-namespace Enqueue\Client\Meta;
-
-class TopicMetaRegistry
-{
-    /**
-     * @var array
-     */
-    protected $meta;
-
-    /**
-     * $meta = [
-     *   'aTopicName' => [
-     *     'description' => 'A desc',
-     *     'processors' => ['aProcessorNameFoo', 'aProcessorNameBar],
-     *   ],
-     * ].
-     *
-     * @param array $meta
-     */
-    public function __construct(array $meta)
-    {
-        $this->meta = $meta;
-    }
-
-    /**
-     * @param string $topicName
-     * @param string $description
-     */
-    public function add($topicName, $description = null)
-    {
-        $this->meta[$topicName] = [
-            'description' => $description,
-            'processors' => [],
-        ];
-    }
-
-    /**
-     * @param string $topicName
-     * @param string $processorName
-     */
-    public function addProcessor($topicName, $processorName)
-    {
-        if (false == array_key_exists($topicName, $this->meta)) {
-            $this->add($topicName);
-        }
-
-        $this->meta[$topicName]['processors'][] = $processorName;
-    }
-
-    /**
-     * @param string $topicName
-     *
-     * @return TopicMeta
-     */
-    public function getTopicMeta($topicName)
-    {
-        if (false == array_key_exists($topicName, $this->meta)) {
-            throw new \InvalidArgumentException(sprintf('The topic meta not found. Requested name `%s`', $topicName));
-        }
-
-        $topic = array_replace([
-            'description' => '',
-            'processors' => [],
-        ], $this->meta[$topicName]);
-
-        return new TopicMeta($topicName, $topic['description'], $topic['processors']);
-    }
-
-    /**
-     * @return \Generator|TopicMeta[]
-     */
-    public function getTopicsMeta()
-    {
-        foreach (array_keys($this->meta) as $topicName) {
-            yield $this->getTopicMeta($topicName);
-        }
-    }
-}
diff --git a/Client/ProcessorRegistryInterface.php b/Client/ProcessorRegistryInterface.php
index c83c113..f8894a9 100644
--- a/Client/ProcessorRegistryInterface.php
+++ b/Client/ProcessorRegistryInterface.php
@@ -6,10 +6,5 @@
 
 interface ProcessorRegistryInterface
 {
-    /**
-     * @param string $processorName
-     *
-     * @return PsrProcessor
-     */
-    public function get($processorName);
+    public function get(string $processorName): PsrProcessor;
 }
diff --git a/Client/TopicSubscriberInterface.php b/Client/TopicSubscriberInterface.php
index 4723c67..634a7ac 100644
--- a/Client/TopicSubscriberInterface.php
+++ b/Client/TopicSubscriberInterface.php
@@ -20,7 +20,8 @@ interface TopicSubscriberInterface
      *     'queue' => 'a_client_queue_name',
      * ]]
      *
-     * Note: If you set queueNameHardcoded to true then the queueName is used as is and therefor the driver is not used to create a transport queue name.
+     * Note: If you set prefix_queue to true then the queue is used as is and therefor the driver is not used to prepare a transport queue name.
+     * It is possible to pass other options, they could be accessible on a route instance through options.
      *
      * @return string|array
      */
diff --git a/Consumption/ChainExtension.php b/Consumption/ChainExtension.php
index d7a24ad..ae71f24 100644
--- a/Consumption/ChainExtension.php
+++ b/Consumption/ChainExtension.php
@@ -16,7 +16,10 @@ class ChainExtension implements ExtensionInterface
      */
     public function __construct(array $extensions)
     {
-        $this->extensions = $extensions;
+        $this->extensions = [];
+        array_walk($extensions, function (ExtensionInterface $extension) {
+            $this->extensions[] = $extension;
+        });
     }
 
     /**
diff --git a/Symfony/Client/ConsumeMessagesCommand.php b/Symfony/Client/ConsumeMessagesCommand.php
index 6d4987b..f38fc86 100644
--- a/Symfony/Client/ConsumeMessagesCommand.php
+++ b/Symfony/Client/ConsumeMessagesCommand.php
@@ -4,10 +4,9 @@
 
 use Enqueue\Client\DelegateProcessor;
 use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Meta\QueueMeta;
-use Enqueue\Client\Meta\QueueMetaRegistry;
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Extension\LoggerExtension;
+use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Symfony\Consumption\LimitsExtensionsCommandTrait;
 use Enqueue\Symfony\Consumption\QueueConsumerOptionsCommandTrait;
@@ -36,40 +35,24 @@ class ConsumeMessagesCommand extends Command
      */
     private $processor;
 
-    /**
-     * @var QueueMetaRegistry
-     */
-    private $queueMetaRegistry;
-
     /**
      * @var DriverInterface
      */
     private $driver;
 
-    /**
-     * @param QueueConsumerInterface $consumer
-     * @param DelegateProcessor      $processor
-     * @param QueueMetaRegistry      $queueMetaRegistry
-     * @param DriverInterface        $driver
-     */
     public function __construct(
         QueueConsumerInterface $consumer,
         DelegateProcessor $processor,
-        QueueMetaRegistry $queueMetaRegistry,
         DriverInterface $driver
     ) {
         parent::__construct(static::$defaultName);
 
         $this->consumer = $consumer;
         $this->processor = $processor;
-        $this->queueMetaRegistry = $queueMetaRegistry;
         $this->driver = $driver;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    protected function configure()
+    protected function configure(): void
     {
         $this->configureLimitsExtensions();
         $this->configureSetupBrokerExtension();
@@ -85,35 +68,36 @@ protected function configure()
         ;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): ?int
     {
         $this->setQueueConsumerOptions($this->consumer, $input);
 
-        $queueMetas = [];
-        if ($clientQueueNames = $input->getArgument('client-queue-names')) {
-            foreach ($clientQueueNames as $clientQueueName) {
-                $queueMetas[] = $this->queueMetaRegistry->getQueueMeta($clientQueueName);
-            }
-        } else {
-            /** @var QueueMeta[] $queueMetas */
-            $queueMetas = iterator_to_array($this->queueMetaRegistry->getQueuesMeta());
+        $clientQueueNames = $input->getArgument('client-queue-names');
+        if (empty($clientQueueNames)) {
+            $clientQueueNames[$this->driver->getConfig()->getDefaultProcessorQueueName()] = true;
+            $clientQueueNames[$this->driver->getConfig()->getRouterQueueName()] = true;
 
-            foreach ($queueMetas as $index => $queueMeta) {
-                if (in_array($queueMeta->getClientName(), $input->getOption('skip'), true)) {
-                    unset($queueMetas[$index]);
+            foreach ($this->driver->getRouteCollection()->all() as $route) {
+                if ($route->getQueue()) {
+                    $clientQueueNames[$route->getQueue()] = true;
                 }
             }
+
+            foreach ($input->getOption('skip') as $skipClientQueueName) {
+                unset($clientQueueNames[$skipClientQueueName]);
+            }
+
+            $clientQueueNames = array_keys($clientQueueNames);
         }
 
-        foreach ($queueMetas as $queueMeta) {
-            $queue = $this->driver->createQueue($queueMeta->getClientName());
+        foreach ($clientQueueNames as $clientQueueName) {
+            $queue = $this->driver->createQueue($clientQueueName);
             $this->consumer->bind($queue, $this->processor);
         }
 
         $this->consumer->consume($this->getRuntimeExtensions($input, $output));
+
+        return null;
     }
 
     /**
@@ -122,7 +106,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
      *
      * @return ChainExtension
      */
-    protected function getRuntimeExtensions(InputInterface $input, OutputInterface $output)
+    protected function getRuntimeExtensions(InputInterface $input, OutputInterface $output): ExtensionInterface
     {
         $extensions = [new LoggerExtension(new ConsoleLogger($output))];
         $extensions = array_merge($extensions, $this->getLimitsExtensions($input, $output));
diff --git a/Symfony/Client/ContainerAwareProcessorRegistry.php b/Symfony/Client/ContainerAwareProcessorRegistry.php
deleted file mode 100644
index 2b42cec..0000000
--- a/Symfony/Client/ContainerAwareProcessorRegistry.php
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-
-namespace Enqueue\Symfony\Client;
-
-use Enqueue\Client\ProcessorRegistryInterface;
-use Interop\Queue\PsrProcessor;
-use Symfony\Component\DependencyInjection\ContainerAwareInterface;
-use Symfony\Component\DependencyInjection\ContainerAwareTrait;
-
-class ContainerAwareProcessorRegistry implements ProcessorRegistryInterface, ContainerAwareInterface
-{
-    use ContainerAwareTrait;
-
-    /**
-     * @var PsrProcessor[]
-     */
-    protected $processors;
-
-    /**
-     * @param array $processors
-     */
-    public function __construct(array $processors = [])
-    {
-        $this->processors = $processors;
-    }
-
-    /**
-     * @param string $processorName
-     * @param string $serviceId
-     */
-    public function set($processorName, $serviceId)
-    {
-        $this->processors[$processorName] = $serviceId;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function get($processorName)
-    {
-        if (false == isset($this->processors[$processorName])) {
-            throw new \LogicException(sprintf('Processor was not found. processorName: "%s"', $processorName));
-        }
-
-        if (null === $this->container) {
-            throw new \LogicException('Container was not set');
-        }
-
-        $processor = $this->container->get($this->processors[$processorName]);
-
-        if (false == $processor instanceof PsrProcessor) {
-            throw new \LogicException(sprintf(
-                'Invalid instance of message processor. expected: "%s", got: "%s"',
-                PsrProcessor::class,
-                is_object($processor) ? get_class($processor) : gettype($processor)
-            ));
-        }
-
-        return $processor;
-    }
-}
diff --git a/Symfony/Client/ContainerProcessorRegistry.php b/Symfony/Client/ContainerProcessorRegistry.php
new file mode 100644
index 0000000..c2d06c8
--- /dev/null
+++ b/Symfony/Client/ContainerProcessorRegistry.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Enqueue\Symfony\Client;
+
+use Enqueue\Client\ProcessorRegistryInterface;
+use Interop\Queue\PsrProcessor;
+use Psr\Container\ContainerInterface;
+
+final class ContainerProcessorRegistry implements ProcessorRegistryInterface
+{
+    /**
+     * @var ContainerInterface
+     */
+    private $locator;
+
+    public function __construct(ContainerInterface $locator)
+    {
+        $this->locator = $locator;
+    }
+
+    public function get(string $processorName): PsrProcessor
+    {
+        if (false == $this->locator->has($processorName)) {
+            throw new \LogicException(sprintf('Service locator does not have a processor with name "%s".', $processorName));
+        }
+
+        return $this->locator->get($processorName);
+    }
+}
diff --git a/Symfony/DependencyInjection/AnalyzeRouteCollectionPass.php b/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php
similarity index 98%
rename from Symfony/DependencyInjection/AnalyzeRouteCollectionPass.php
rename to Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php
index b5bd11f..c8b140c 100644
--- a/Symfony/DependencyInjection/AnalyzeRouteCollectionPass.php
+++ b/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Enqueue\Symfony\DependencyInjection;
+namespace Enqueue\Symfony\Client\DependencyInjection;
 
 use Enqueue\Client\RouteCollection;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
diff --git a/Symfony/Client/DependencyInjection/BuildClientExtensionsPass.php b/Symfony/Client/DependencyInjection/BuildClientExtensionsPass.php
new file mode 100644
index 0000000..5cb516d
--- /dev/null
+++ b/Symfony/Client/DependencyInjection/BuildClientExtensionsPass.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Enqueue\Symfony\Client\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+final class BuildClientExtensionsPass implements CompilerPassInterface
+{
+    /**
+     * @var string
+     */
+    private $name;
+
+    public function __construct(string $clientName)
+    {
+        if (empty($clientName)) {
+            throw new \InvalidArgumentException('The name could not be empty.');
+        }
+
+        $this->name = $clientName;
+    }
+
+    public function process(ContainerBuilder $container): void
+    {
+        $extensionsId = sprintf('enqueue.client.%s.client_extensions', $this->name);
+        if (false == $container->hasDefinition($extensionsId)) {
+            return;
+        }
+
+        $tags = array_merge(
+            $container->findTaggedServiceIds('enqueue.client_extension'),
+            $container->findTaggedServiceIds('enqueue.client.extension') // TODO BC
+        );
+
+        $groupByPriority = [];
+        foreach ($tags as $serviceId => $tagAttributes) {
+            foreach ($tagAttributes as $tagAttribute) {
+                $client = $tagAttribute['client'] ?? 'default';
+
+                if ($client !== $this->name && 'all' !== $client) {
+                    continue;
+                }
+
+                $priority = (int) ($tagAttribute['priority'] ?? 0);
+
+                $groupByPriority[$priority][] = new Reference($serviceId);
+            }
+        }
+
+        krsort($groupByPriority, SORT_NUMERIC);
+
+        $flatExtensions = [];
+        foreach ($groupByPriority as $extension) {
+            $flatExtensions = array_merge($flatExtensions, $extension);
+        }
+
+        $extensionsService = $container->getDefinition($extensionsId);
+        $extensionsService->replaceArgument(0, array_merge(
+            $extensionsService->getArgument(0),
+            $flatExtensions
+        ));
+    }
+}
diff --git a/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPass.php b/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php
similarity index 96%
rename from Symfony/DependencyInjection/BuildCommandSubscriberRoutesPass.php
rename to Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php
index e9e16a3..3e8791a 100644
--- a/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPass.php
+++ b/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Enqueue\Symfony\DependencyInjection;
+namespace Enqueue\Symfony\Client\DependencyInjection;
 
 use Enqueue\Client\CommandSubscriberInterface;
 use Enqueue\Client\Route;
@@ -70,6 +70,10 @@ public function process(ContainerBuilder $container): void
                     throw new \LogicException('Command subscriber configuration is invalid. Should be an array or string.');
                 }
 
+                if (isset($commands['command'])) {
+                    $commands = [$commands];
+                }
+
                 foreach ($commands as $key => $params) {
                     if (is_string($params)) {
                         $routeCollection->add(new Route($params, Route::COMMAND, $serviceId, ['processor_service_id' => $serviceId]));
diff --git a/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php b/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php
new file mode 100644
index 0000000..8482225
--- /dev/null
+++ b/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Enqueue\Symfony\Client\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+final class BuildConsumptionExtensionsPass implements CompilerPassInterface
+{
+    /**
+     * @var string
+     */
+    private $name;
+
+    public function __construct(string $clientName)
+    {
+        if (empty($clientName)) {
+            throw new \InvalidArgumentException('The name could not be empty.');
+        }
+
+        $this->name = $clientName;
+    }
+
+    public function process(ContainerBuilder $container): void
+    {
+        $extensionsId = sprintf('enqueue.client.%s.consumption_extensions', $this->name);
+        if (false == $container->hasDefinition($extensionsId)) {
+            return;
+        }
+
+        $tags = array_merge(
+            $container->findTaggedServiceIds('enqueue.consumption_extension'),
+            $container->findTaggedServiceIds('enqueue.consumption.extension') // TODO BC
+        );
+
+        $groupByPriority = [];
+        foreach ($tags as $serviceId => $tagAttributes) {
+            foreach ($tagAttributes as $tagAttribute) {
+                $client = $tagAttribute['client'] ?? 'default';
+
+                if ($client !== $this->name && 'all' !== $client) {
+                    continue;
+                }
+
+                $priority = (int) ($tagAttribute['priority'] ?? 0);
+
+                $groupByPriority[$priority][] = new Reference($serviceId);
+            }
+        }
+
+        krsort($groupByPriority, SORT_NUMERIC);
+
+        $flatExtensions = [];
+        foreach ($groupByPriority as $extension) {
+            $flatExtensions = array_merge($flatExtensions, $extension);
+        }
+
+        $extensionsService = $container->getDefinition($extensionsId);
+        $extensionsService->replaceArgument(0, array_merge(
+            $extensionsService->getArgument(0),
+            $flatExtensions
+        ));
+    }
+}
diff --git a/Symfony/DependencyInjection/BuildProcessorRegistryPass.php b/Symfony/Client/DependencyInjection/BuildProcessorRegistryPass.php
similarity index 69%
rename from Symfony/DependencyInjection/BuildProcessorRegistryPass.php
rename to Symfony/Client/DependencyInjection/BuildProcessorRegistryPass.php
index 4620cef..d69935d 100644
--- a/Symfony/DependencyInjection/BuildProcessorRegistryPass.php
+++ b/Symfony/Client/DependencyInjection/BuildProcessorRegistryPass.php
@@ -1,10 +1,12 @@
 <?php
 
-namespace Enqueue\Symfony\DependencyInjection;
+namespace Enqueue\Symfony\Client\DependencyInjection;
 
 use Enqueue\Client\RouteCollection;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
 
 final class BuildProcessorRegistryPass implements CompilerPassInterface
 {
@@ -34,6 +36,11 @@ public function process(ContainerBuilder $container): void
             return;
         }
 
+        $routerProcessorId = sprintf('enqueue.client.%s.router_processor', $this->name);
+        if (false == $container->hasDefinition($routerProcessorId)) {
+            return;
+        }
+
         $routeCollection = RouteCollection::fromArray($container->getDefinition($routeCollectionId)->getArgument(0));
 
         $map = [];
@@ -42,13 +49,12 @@ public function process(ContainerBuilder $container): void
                 throw new \LogicException('The route option "processor_service_id" is required');
             }
 
-            $map[$route->getProcessor()] = $processorServiceId;
+            $map[$route->getProcessor()] = new Reference($processorServiceId);
         }
 
+        $map["%enqueue.client.{$this->name}.router_processor%"] = new Reference($routerProcessorId);
+
         $registry = $container->getDefinition($processorRegistryId);
-        $registry->replaceArgument(0, array_replace(
-            $registry->getArgument(0),
-            $map
-        ));
+        $registry->setArgument(0, ServiceLocatorTagPass::register($container, $map, $processorRegistryId));
     }
 }
diff --git a/Symfony/DependencyInjection/BuildProcessorRoutesPass.php b/Symfony/Client/DependencyInjection/BuildProcessorRoutesPass.php
similarity index 87%
rename from Symfony/DependencyInjection/BuildProcessorRoutesPass.php
rename to Symfony/Client/DependencyInjection/BuildProcessorRoutesPass.php
index 7a6853b..415b87a 100644
--- a/Symfony/DependencyInjection/BuildProcessorRoutesPass.php
+++ b/Symfony/Client/DependencyInjection/BuildProcessorRoutesPass.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Enqueue\Symfony\DependencyInjection;
+namespace Enqueue\Symfony\Client\DependencyInjection;
 
 use Enqueue\Client\Route;
 use Enqueue\Client\RouteCollection;
@@ -44,10 +44,10 @@ public function process(ContainerBuilder $container): void
                 $command = $tagAttribute['command'] ?? null;
 
                 if (false == $topic && false == $command) {
-                    throw new \LogicException('Either "topic" or "command" tag attribute must be set. None is set.');
+                    throw new \LogicException(sprintf('Either "topic" or "command" tag attribute must be set on service "%s". None is set.', $serviceId));
                 }
                 if ($topic && $command) {
-                    throw new \LogicException('Either "topic" or "command" tag attribute must be set. Both are set.');
+                    throw new \LogicException(sprintf('Either "topic" or "command" tag attribute must be set on service "%s". Both are set.', $serviceId));
                 }
 
                 $source = $command ?: $topic;
diff --git a/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPass.php b/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php
similarity index 98%
rename from Symfony/DependencyInjection/BuildTopicSubscriberRoutesPass.php
rename to Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php
index 869a9f0..73f3a01 100644
--- a/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPass.php
+++ b/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Enqueue\Symfony\DependencyInjection;
+namespace Enqueue\Symfony\Client\DependencyInjection;
 
 use Enqueue\Client\Route;
 use Enqueue\Client\RouteCollection;
diff --git a/Symfony/Client/Meta/QueuesCommand.php b/Symfony/Client/Meta/QueuesCommand.php
deleted file mode 100644
index 12c459d..0000000
--- a/Symfony/Client/Meta/QueuesCommand.php
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-
-namespace Enqueue\Symfony\Client\Meta;
-
-use Enqueue\Client\Meta\QueueMetaRegistry;
-use Symfony\Component\Console\Command\Command;
-use Symfony\Component\Console\Helper\Table;
-use Symfony\Component\Console\Helper\TableSeparator;
-use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Output\OutputInterface;
-
-class QueuesCommand extends Command
-{
-    protected static $defaultName = 'enqueue:queues';
-
-    /**
-     * @var QueueMetaRegistry
-     */
-    private $queueMetaRegistry;
-
-    /**
-     * @param QueueMetaRegistry $queueRegistry
-     */
-    public function __construct(QueueMetaRegistry $queueRegistry)
-    {
-        parent::__construct(static::$defaultName);
-
-        $this->queueMetaRegistry = $queueRegistry;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function configure()
-    {
-        $this
-            ->setAliases([
-                'enq:m:q',
-                'debug:enqueue:queues',
-            ])
-            ->setDescription('A command shows all available queues and some information about them.')
-        ;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function execute(InputInterface $input, OutputInterface $output)
-    {
-        $table = new Table($output);
-        $table->setHeaders(['Client Name', 'Transport Name', 'processors']);
-
-        $count = 0;
-        $firstRow = true;
-        foreach ($this->queueMetaRegistry->getQueuesMeta() as $queueMeta) {
-            if (false == $firstRow) {
-                $table->addRow(new TableSeparator());
-            }
-
-            $table->addRow([
-                $queueMeta->getClientName(),
-                $queueMeta->getClientName() == $queueMeta->getTransportName() ? '(same)' : $queueMeta->getTransportName(),
-                implode(PHP_EOL, $queueMeta->getProcessors()),
-            ]);
-
-            ++$count;
-            $firstRow = false;
-        }
-
-        $output->writeln(sprintf('Found %s destinations', $count));
-        $output->writeln('');
-        $table->render();
-    }
-}
diff --git a/Symfony/Client/Meta/TopicsCommand.php b/Symfony/Client/Meta/TopicsCommand.php
deleted file mode 100644
index 63b28bc..0000000
--- a/Symfony/Client/Meta/TopicsCommand.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-namespace Enqueue\Symfony\Client\Meta;
-
-use Enqueue\Client\Meta\TopicMetaRegistry;
-use Symfony\Component\Console\Command\Command;
-use Symfony\Component\Console\Helper\Table;
-use Symfony\Component\Console\Helper\TableSeparator;
-use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Output\OutputInterface;
-
-class TopicsCommand extends Command
-{
-    protected static $defaultName = 'enqueue:topics';
-
-    /**
-     * @var TopicMetaRegistry
-     */
-    private $topicRegistry;
-
-    /**
-     * @param TopicMetaRegistry $topicRegistry
-     */
-    public function __construct(TopicMetaRegistry $topicRegistry)
-    {
-        parent::__construct(static::$defaultName);
-
-        $this->topicRegistry = $topicRegistry;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function configure()
-    {
-        $this
-            ->setAliases([
-                'enq:m:t',
-                'debug:enqueue:topics',
-            ])
-            ->setDescription('A command shows all available topics and some information about them.')
-        ;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function execute(InputInterface $input, OutputInterface $output)
-    {
-        $table = new Table($output);
-        $table->setHeaders(['Topic', 'Description', 'processors']);
-
-        $count = 0;
-        $firstRow = true;
-        foreach ($this->topicRegistry->getTopicsMeta() as $topic) {
-            if (false == $firstRow) {
-                $table->addRow(new TableSeparator());
-            }
-
-            $table->addRow([$topic->getName(), $topic->getDescription(), implode(PHP_EOL, $topic->getProcessors())]);
-
-            ++$count;
-            $firstRow = false;
-        }
-
-        $output->writeln(sprintf('Found %s topics', $count));
-        $output->writeln('');
-        $table->render();
-    }
-}
diff --git a/Symfony/Client/RoutesCommand.php b/Symfony/Client/RoutesCommand.php
new file mode 100644
index 0000000..e4abc20
--- /dev/null
+++ b/Symfony/Client/RoutesCommand.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace Enqueue\Symfony\Client;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\Table;
+use Symfony\Component\Console\Helper\TableSeparator;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+final class RoutesCommand extends Command
+{
+    protected static $defaultName = 'enqueue:routes';
+
+    /**
+     * @var Config
+     */
+    private $config;
+
+    /**
+     * @var RouteCollection
+     */
+    private $routeCollection;
+
+    public function __construct(Config $config, RouteCollection $routeCollection)
+    {
+        parent::__construct(static::$defaultName);
+
+        $this->config = $config;
+        $this->routeCollection = $routeCollection;
+    }
+
+    protected function configure(): void
+    {
+        $this
+            ->setAliases(['debug:enqueue:routes'])
+            ->setDescription('A command lists all registered routes.')
+            ->addOption('show-route-options', null, InputOption::VALUE_NONE, 'Adds ability to hide options.');
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output): ?int
+    {
+        $routes = $this->routeCollection->all();
+        $output->writeln(sprintf('Found %s routes', count($routes)));
+        $output->writeln('');
+
+        if ($routes) {
+            $table = new Table($output);
+            $table->setHeaders(['Type', 'Source', 'Queue', 'Processor', 'Options']);
+
+            $firstRow = true;
+            foreach ($routes as $route) {
+                if (false == $firstRow) {
+                    $table->addRow(new TableSeparator());
+
+                    $firstRow = false;
+                }
+
+                if ($route->isCommand()) {
+                    continue;
+                }
+
+                $table->addRow([
+                    $this->formatSourceType($route),
+                    $route->getSource(),
+                    $this->formatQueue($route),
+                    $this->formatProcessor($route),
+                    $input->getOption('show-route-options') ? $this->formatOptions($route) : '(hidden)',
+                ]);
+            }
+
+            foreach ($this->routeCollection->all() as $route) {
+                if ($route->isTopic()) {
+                    continue;
+                }
+
+                $table->addRow([
+                    $this->formatSourceType($route),
+                    $route->getSource(),
+                    $this->formatQueue($route),
+                    $this->formatProcessor($route),
+                    $input->getOption('show-route-options') ? $this->formatOptions($route) : '(hidden)',
+                ]);
+            }
+
+            $table->render();
+        }
+
+        return null;
+    }
+
+    private function formatSourceType(Route $route): string
+    {
+        if ($route->isCommand()) {
+            return 'command';
+        }
+
+        if ($route->isTopic()) {
+            return 'topic';
+        }
+
+        return 'unknown';
+    }
+
+    private function formatProcessor(Route $route): string
+    {
+        if ($route->isProcessorExternal()) {
+            return 'n\a (external)';
+        }
+
+        $processor = $route->getProcessor();
+
+        return $route->isProcessorExclusive() ? $processor.' (exclusive)' : $processor;
+    }
+
+    private function formatQueue(Route $route): string
+    {
+        $queue = $route->getQueue() ?: $this->config->getDefaultProcessorQueueName();
+
+        return $route->isPrefixQueue() ? $queue.' (prefixed)' : $queue.' (as is)';
+    }
+
+    private function formatOptions(Route $route): string
+    {
+        return var_export($route->getOptions(), true);
+    }
+}
diff --git a/Symfony/DependencyInjection/ClientFactory.php b/Symfony/DependencyInjection/ClientFactory.php
new file mode 100644
index 0000000..a2c09af
--- /dev/null
+++ b/Symfony/DependencyInjection/ClientFactory.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Enqueue\Symfony\DependencyInjection;
+
+use Enqueue\Client\DriverInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * @internal
+ */
+final class ClientFactory
+{
+    /**
+     * @var string
+     */
+    private $name;
+
+    public function __construct(string $name)
+    {
+        if (empty($name)) {
+            throw new \InvalidArgumentException('The name could not be empty.');
+        }
+
+        $this->name = $name;
+    }
+
+    public function createDriver(ContainerBuilder $container, array $config): string
+    {
+        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
+        $driverId = sprintf('enqueue.client.%s.driver', $this->getName());
+        $driverFactoryId = sprintf('enqueue.client.%s.driver_factory', $this->getName());
+
+        $container->register($driverId, DriverInterface::class)
+            ->setFactory([new Reference($driverFactoryId), 'create'])
+            ->addArgument(new Reference($factoryId))
+            ->addArgument($config['dsn'])
+            ->addArgument($config)
+        ;
+
+        return $driverId;
+    }
+
+    public function getName(): string
+    {
+        return $this->name;
+    }
+}
diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index 4f1eab7..f54a34f 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -2,13 +2,12 @@
 
 namespace Enqueue\Symfony\DependencyInjection;
 
-use Enqueue\Client\DriverInterface;
+use Enqueue\ConnectionFactoryFactory;
 use Enqueue\ConnectionFactoryFactoryInterface;
 use Enqueue\Resources;
 use Interop\Queue\PsrConnectionFactory;
 use Interop\Queue\PsrContext;
 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
-use Symfony\Component\DependencyInjection\Alias;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Reference;
 
@@ -89,14 +88,10 @@ public function addConfiguration(ArrayNodeDefinition $builder): void
 
     public function createConnectionFactory(ContainerBuilder $container, array $config): string
     {
-        $factoryFactoryId = 'enqueue.connection_factory_factory';
         $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
+        $factoryFactoryId = sprintf('enqueue.transport.%s.connection_factory_factory', $this->getName());
 
-        if (array_key_exists('factory_class', $config)) {
-            $factoryFactoryId = sprintf('enqueue.transport.%s.connection_factory_factory', $this->getName());
-
-            $container->register($factoryFactoryId, $config['factory_class']);
-        }
+        $container->register($factoryFactoryId, $config['factory_class'] ?? ConnectionFactoryFactory::class);
 
         $factoryFactoryService = new Reference(
             array_key_exists('factory_service', $config) ? $config['factory_service'] : $factoryFactoryId
@@ -118,8 +113,6 @@ public function createConnectionFactory(ContainerBuilder $container, array $conf
             ;
         }
 
-        $container->setAlias('enqueue.transport.connection_factory', new Alias($factoryId, true));
-
         return $factoryId;
     }
 
@@ -132,28 +125,9 @@ public function createContext(ContainerBuilder $container, array $config): strin
             ->setFactory([new Reference($factoryId), 'createContext'])
         ;
 
-        $container->setAlias('enqueue.transport.context', new Alias($contextId, true));
-
         return $contextId;
     }
 
-    public function createDriver(ContainerBuilder $container, array $config): string
-    {
-        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
-        $driverId = sprintf('enqueue.client.%s.driver', $this->getName());
-
-        $container->register($driverId, DriverInterface::class)
-            ->setFactory([new Reference('enqueue.client.driver_factory'), 'create'])
-            ->addArgument(new Reference($factoryId))
-            ->addArgument($config['dsn'])
-            ->addArgument($config)
-        ;
-
-        $container->setAlias('enqueue.client.driver', new Alias($driverId, true));
-
-        return $driverId;
-    }
-
     public function getName(): string
     {
         return $this->name;
diff --git a/Tests/Client/ChainExtensionTest.php b/Tests/Client/ChainExtensionTest.php
index 9268c7b..a48da5f 100644
--- a/Tests/Client/ChainExtensionTest.php
+++ b/Tests/Client/ChainExtensionTest.php
@@ -27,6 +27,13 @@ public function testShouldBeFinal()
         $this->assertClassFinal(ChainExtension::class);
     }
 
+    public function testShouldInitEmptyExtensionsArrayOnConstruct()
+    {
+        $extension = new ChainExtension([]);
+
+        $this->assertAttributeSame([], 'extensions', $extension);
+    }
+
     public function testCouldBeConstructedWithExtensionsArray()
     {
         new ChainExtension([$this->createExtension(), $this->createExtension()]);
diff --git a/Tests/Client/Meta/QueueMetaRegistryTest.php b/Tests/Client/Meta/QueueMetaRegistryTest.php
deleted file mode 100644
index 787290f..0000000
--- a/Tests/Client/Meta/QueueMetaRegistryTest.php
+++ /dev/null
@@ -1,146 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Client\Meta;
-
-use Enqueue\Client\Config;
-use Enqueue\Client\Meta\QueueMeta;
-use Enqueue\Client\Meta\QueueMetaRegistry;
-use PHPUnit\Framework\TestCase;
-
-class QueueMetaRegistryTest extends TestCase
-{
-    public function testCouldBeConstructedWithQueues()
-    {
-        $meta = [
-            'aQueueName' => [],
-            'anotherQueueName' => [],
-        ];
-
-        $registry = new QueueMetaRegistry($this->createConfig(), $meta);
-
-        $this->assertAttributeEquals($meta, 'meta', $registry);
-    }
-
-    public function testShouldAllowAddQueueMetaUsingAddMethod()
-    {
-        $registry = new QueueMetaRegistry($this->createConfig(), []);
-
-        $registry->add('theFooQueueName', 'theTransportQueueName');
-        $registry->add('theBarQueueName');
-
-        $this->assertAttributeSame([
-            'theFooQueueName' => [
-                'transportName' => 'theTransportQueueName',
-                'processors' => [],
-            ],
-            'theBarQueueName' => [
-                'transportName' => null,
-                'processors' => [],
-            ],
-        ], 'meta', $registry);
-    }
-
-    public function testShouldAllowAddSubscriber()
-    {
-        $registry = new QueueMetaRegistry($this->createConfig(), []);
-
-        $registry->addProcessor('theFooQueueName', 'theFooProcessorName');
-        $registry->addProcessor('theFooQueueName', 'theBarProcessorName');
-        $registry->addProcessor('theBarQueueName', 'theBazProcessorName');
-
-        $this->assertAttributeSame([
-            'theFooQueueName' => [
-                'transportName' => null,
-                'processors' => ['theFooProcessorName', 'theBarProcessorName'],
-            ],
-            'theBarQueueName' => [
-                'transportName' => null,
-                'processors' => ['theBazProcessorName'],
-            ],
-        ], 'meta', $registry);
-    }
-
-    public function testThrowIfThereIsNotMetaForRequestedClientQueueName()
-    {
-        $registry = new QueueMetaRegistry($this->createConfig(), []);
-
-        $this->expectException(\InvalidArgumentException::class);
-        $this->expectExceptionMessage('The queue meta not found. Requested name `aName`');
-        $registry->getQueueMeta('aName');
-    }
-
-    public function testShouldAllowGetQueueByNameWithDefaultInfo()
-    {
-        $queues = [
-            'theQueueName' => [],
-        ];
-
-        $registry = new QueueMetaRegistry($this->createConfig(), $queues);
-
-        $queue = $registry->getQueueMeta('theQueueName');
-
-        $this->assertInstanceOf(QueueMeta::class, $queue);
-        $this->assertSame('theQueueName', $queue->getClientName());
-        $this->assertSame('aprefix.anappname.thequeuename', $queue->getTransportName());
-        $this->assertSame([], $queue->getProcessors());
-    }
-
-    public function testShouldAllowGetQueueByNameWithCustomInfo()
-    {
-        $queues = [
-            'theClientQueueName' => ['transportName' => 'theTransportName', 'processors' => ['theSubscriber']],
-        ];
-
-        $registry = new QueueMetaRegistry($this->createConfig(), $queues);
-
-        $queue = $registry->getQueueMeta('theClientQueueName');
-        $this->assertInstanceOf(QueueMeta::class, $queue);
-        $this->assertSame('theClientQueueName', $queue->getClientName());
-        $this->assertSame('theTransportName', $queue->getTransportName());
-        $this->assertSame(['theSubscriber'], $queue->getProcessors());
-    }
-
-    public function testShouldNotAllowToOverwriteDefaultTransportNameByEmptyValue()
-    {
-        $registry = new QueueMetaRegistry($this->createConfig(), [
-            'theClientQueueName' => ['transportName' => null, 'processors' => []],
-        ]);
-
-        $queue = $registry->getQueueMeta('theClientQueueName');
-        $this->assertInstanceOf(QueueMeta::class, $queue);
-        $this->assertSame('aprefix.anappname.theclientqueuename', $queue->getTransportName());
-    }
-
-    public function testShouldAllowGetAllQueues()
-    {
-        $queues = [
-            'fooQueueName' => [],
-            'barQueueName' => [],
-        ];
-
-        $registry = new QueueMetaRegistry($this->createConfig(), $queues);
-
-        $queues = $registry->getQueuesMeta();
-        $this->assertInstanceOf(\Generator::class, $queues);
-
-        $queues = iterator_to_array($queues);
-        /* @var QueueMeta[] $queues */
-
-        $this->assertContainsOnly(QueueMeta::class, $queues);
-        $this->assertCount(2, $queues);
-
-        $this->assertSame('fooQueueName', $queues[0]->getClientName());
-        $this->assertSame('aprefix.anappname.fooqueuename', $queues[0]->getTransportName());
-
-        $this->assertSame('barQueueName', $queues[1]->getClientName());
-        $this->assertSame('aprefix.anappname.barqueuename', $queues[1]->getTransportName());
-    }
-
-    /**
-     * @return Config
-     */
-    private function createConfig()
-    {
-        return new Config('aPrefix', 'anAppName', 'aRouterTopic', 'aRouterQueueName', 'aDefaultQueueName', 'aRouterProcessorName');
-    }
-}
diff --git a/Tests/Client/Meta/QueueMetaTest.php b/Tests/Client/Meta/QueueMetaTest.php
deleted file mode 100644
index fde6dc5..0000000
--- a/Tests/Client/Meta/QueueMetaTest.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Client\Meta;
-
-use Enqueue\Client\Meta\QueueMeta;
-use PHPUnit\Framework\TestCase;
-
-class QueueMetaTest extends TestCase
-{
-    public function testCouldBeConstructedWithExpectedArguments()
-    {
-        $meta = new QueueMeta('aClientName', 'aTransportName');
-
-        $this->assertAttributeEquals('aClientName', 'clientName', $meta);
-        $this->assertAttributeEquals('aTransportName', 'transportName', $meta);
-        $this->assertAttributeEquals([], 'processors', $meta);
-    }
-
-    public function testShouldAllowGetClientNameSetInConstructor()
-    {
-        $meta = new QueueMeta('theClientName', 'aTransportName');
-
-        $this->assertSame('theClientName', $meta->getClientName());
-    }
-
-    public function testShouldAllowGetTransportNameSetInConstructor()
-    {
-        $meta = new QueueMeta('aClientName', 'theTransportName');
-
-        $this->assertSame('theTransportName', $meta->getTransportName());
-    }
-
-    public function testShouldAllowGetSubscribersSetInConstructor()
-    {
-        $meta = new QueueMeta('aClientName', 'aTransportName', ['aSubscriber']);
-
-        $this->assertSame(['aSubscriber'], $meta->getProcessors());
-    }
-}
diff --git a/Tests/Client/Meta/TopicMetaRegistryTest.php b/Tests/Client/Meta/TopicMetaRegistryTest.php
deleted file mode 100644
index ce074b6..0000000
--- a/Tests/Client/Meta/TopicMetaRegistryTest.php
+++ /dev/null
@@ -1,124 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Client\Meta;
-
-use Enqueue\Client\Meta\TopicMeta;
-use Enqueue\Client\Meta\TopicMetaRegistry;
-use PHPUnit\Framework\TestCase;
-
-class TopicMetaRegistryTest extends TestCase
-{
-    public function testCouldBeConstructedWithTopics()
-    {
-        $topics = [
-            'aTopicName' => [],
-            'anotherTopicName' => [],
-        ];
-
-        $registry = new TopicMetaRegistry($topics);
-
-        $this->assertAttributeEquals($topics, 'meta', $registry);
-    }
-
-    public function testShouldAllowAddTopicMetaUsingAddMethod()
-    {
-        $registry = new TopicMetaRegistry([]);
-
-        $registry->add('theFooTopicName', 'aDescription');
-        $registry->add('theBarTopicName');
-
-        $this->assertAttributeSame([
-            'theFooTopicName' => [
-                'description' => 'aDescription',
-                'processors' => [],
-            ],
-            'theBarTopicName' => [
-                'description' => null,
-                'processors' => [],
-            ],
-        ], 'meta', $registry);
-    }
-
-    public function testShouldAllowAddSubscriber()
-    {
-        $registry = new TopicMetaRegistry([]);
-
-        $registry->addProcessor('theFooTopicName', 'theFooProcessorName');
-        $registry->addProcessor('theFooTopicName', 'theBarProcessorName');
-        $registry->addProcessor('theBarTopicName', 'theBazProcessorName');
-
-        $this->assertAttributeSame([
-            'theFooTopicName' => [
-                'description' => null,
-                'processors' => ['theFooProcessorName', 'theBarProcessorName'],
-            ],
-            'theBarTopicName' => [
-                'description' => null,
-                'processors' => ['theBazProcessorName'],
-            ],
-        ], 'meta', $registry);
-    }
-
-    public function testThrowIfThereIsNotMetaForRequestedTopicName()
-    {
-        $registry = new TopicMetaRegistry([]);
-
-        $this->setExpectedException(
-            \InvalidArgumentException::class,
-            'The topic meta not found. Requested name `aName`'
-        );
-        $registry->getTopicMeta('aName');
-    }
-
-    public function testShouldAllowGetTopicByNameWithDefaultInfo()
-    {
-        $topics = [
-            'theTopicName' => [],
-        ];
-
-        $registry = new TopicMetaRegistry($topics);
-
-        $topic = $registry->getTopicMeta('theTopicName');
-        $this->assertInstanceOf(TopicMeta::class, $topic);
-        $this->assertSame('theTopicName', $topic->getName());
-        $this->assertSame('', $topic->getDescription());
-        $this->assertSame([], $topic->getProcessors());
-    }
-
-    public function testShouldAllowGetTopicByNameWithCustomInfo()
-    {
-        $topics = [
-            'theTopicName' => ['description' => 'theDescription', 'processors' => ['theSubscriber']],
-        ];
-
-        $registry = new TopicMetaRegistry($topics);
-
-        $topic = $registry->getTopicMeta('theTopicName');
-        $this->assertInstanceOf(TopicMeta::class, $topic);
-        $this->assertSame('theTopicName', $topic->getName());
-        $this->assertSame('theDescription', $topic->getDescription());
-        $this->assertSame(['theSubscriber'], $topic->getProcessors());
-    }
-
-    public function testShouldAllowGetAllTopics()
-    {
-        $topics = [
-            'fooTopicName' => [],
-            'barTopicName' => [],
-        ];
-
-        $registry = new TopicMetaRegistry($topics);
-
-        $topics = $registry->getTopicsMeta();
-        $this->assertInstanceOf(\Generator::class, $topics);
-
-        $topics = iterator_to_array($topics);
-        /* @var TopicMeta[] $topics */
-
-        $this->assertContainsOnly(TopicMeta::class, $topics);
-        $this->assertCount(2, $topics);
-
-        $this->assertSame('fooTopicName', $topics[0]->getName());
-        $this->assertSame('barTopicName', $topics[1]->getName());
-    }
-}
diff --git a/Tests/Client/Meta/TopicMetaTest.php b/Tests/Client/Meta/TopicMetaTest.php
deleted file mode 100644
index 565a8f8..0000000
--- a/Tests/Client/Meta/TopicMetaTest.php
+++ /dev/null
@@ -1,57 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Client\Meta;
-
-use Enqueue\Client\Meta\TopicMeta;
-use PHPUnit\Framework\TestCase;
-
-class TopicMetaTest extends TestCase
-{
-    public function testCouldBeConstructedWithNameOnly()
-    {
-        $topic = new TopicMeta('aName');
-
-        $this->assertAttributeEquals('aName', 'name', $topic);
-        $this->assertAttributeEquals('', 'description', $topic);
-        $this->assertAttributeEquals([], 'processors', $topic);
-    }
-
-    public function testCouldBeConstructedWithNameAndDescriptionOnly()
-    {
-        $topic = new TopicMeta('aName', 'aDescription');
-
-        $this->assertAttributeEquals('aName', 'name', $topic);
-        $this->assertAttributeEquals('aDescription', 'description', $topic);
-        $this->assertAttributeEquals([], 'processors', $topic);
-    }
-
-    public function testCouldBeConstructedWithNameAndDescriptionAndSubscribers()
-    {
-        $topic = new TopicMeta('aName', 'aDescription', ['aSubscriber']);
-
-        $this->assertAttributeEquals('aName', 'name', $topic);
-        $this->assertAttributeEquals('aDescription', 'description', $topic);
-        $this->assertAttributeEquals(['aSubscriber'], 'processors', $topic);
-    }
-
-    public function testShouldAllowGetNameSetInConstructor()
-    {
-        $topic = new TopicMeta('theName', 'aDescription');
-
-        $this->assertSame('theName', $topic->getName());
-    }
-
-    public function testShouldAllowGetDescriptionSetInConstructor()
-    {
-        $topic = new TopicMeta('aName', 'theDescription');
-
-        $this->assertSame('theDescription', $topic->getDescription());
-    }
-
-    public function testShouldAllowGetSubscribersSetInConstructor()
-    {
-        $topic = new TopicMeta('aName', '', ['aSubscriber']);
-
-        $this->assertSame(['aSubscriber'], $topic->getProcessors());
-    }
-}
diff --git a/Tests/Symfony/Client/ConsumeMessagesCommandTest.php b/Tests/Symfony/Client/ConsumeMessagesCommandTest.php
index d2a08df..69af29f 100644
--- a/Tests/Symfony/Client/ConsumeMessagesCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeMessagesCommandTest.php
@@ -5,7 +5,8 @@
 use Enqueue\Client\Config;
 use Enqueue\Client\DelegateProcessor;
 use Enqueue\Client\DriverInterface;
-use Enqueue\Client\Meta\QueueMetaRegistry;
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Null\NullQueue;
@@ -21,8 +22,7 @@ public function testCouldBeConstructedWithRequiredAttributes()
         new ConsumeMessagesCommand(
             $this->createQueueConsumerMock(),
             $this->createDelegateProcessorMock(),
-            $this->createQueueMetaRegistry([]),
-            $this->createDriverMock()
+            $this->createDriverStub()
         );
     }
 
@@ -31,8 +31,7 @@ public function testShouldHaveCommandName()
         $command = new ConsumeMessagesCommand(
             $this->createQueueConsumerMock(),
             $this->createDelegateProcessorMock(),
-            $this->createQueueMetaRegistry([]),
-            $this->createDriverMock()
+            $this->createDriverStub()
         );
 
         $this->assertEquals('enqueue:consume', $command->getName());
@@ -43,8 +42,7 @@ public function testShouldHaveCommandAliases()
         $command = new ConsumeMessagesCommand(
             $this->createQueueConsumerMock(),
             $this->createDelegateProcessorMock(),
-            $this->createQueueMetaRegistry([]),
-            $this->createDriverMock()
+            $this->createDriverStub()
         );
 
         $this->assertEquals(['enq:c'], $command->getAliases());
@@ -55,8 +53,7 @@ public function testShouldHaveExpectedOptions()
         $command = new ConsumeMessagesCommand(
             $this->createQueueConsumerMock(),
             $this->createDelegateProcessorMock(),
-            $this->createQueueMetaRegistry([]),
-            $this->createDriverMock()
+            $this->createDriverStub()
         );
 
         $options = $command->getDefinition()->getOptions();
@@ -77,8 +74,7 @@ public function testShouldHaveExpectedArguments()
         $command = new ConsumeMessagesCommand(
             $this->createQueueConsumerMock(),
             $this->createDelegateProcessorMock(),
-            $this->createQueueMetaRegistry([]),
-            $this->createDriverMock()
+            $this->createDriverStub()
         );
 
         $arguments = $command->getDefinition()->getArguments();
@@ -87,10 +83,12 @@ public function testShouldHaveExpectedArguments()
         $this->assertArrayHasKey('client-queue-names', $arguments);
     }
 
-    public function testShouldExecuteConsumptionAndUseDefaultQueueName()
+    public function testShouldBindDefaultQueueOnly()
     {
         $queue = new NullQueue('');
 
+        $routeCollection = new RouteCollection([]);
+
         $processor = $this->createDelegateProcessorMock();
 
         $context = $this->createPsrContextMock();
@@ -111,11 +109,7 @@ public function testShouldExecuteConsumptionAndUseDefaultQueueName()
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
 
-        $queueMetaRegistry = $this->createQueueMetaRegistry([
-            'default' => [],
-        ]);
-
-        $driver = $this->createDriverMock();
+        $driver = $this->createDriverStub($routeCollection);
         $driver
             ->expects($this->once())
             ->method('createQueue')
@@ -123,16 +117,20 @@ public function testShouldExecuteConsumptionAndUseDefaultQueueName()
             ->willReturn($queue)
         ;
 
-        $command = new ConsumeMessagesCommand($consumer, $processor, $queueMetaRegistry, $driver);
+        $command = new ConsumeMessagesCommand($consumer, $processor, $driver);
 
         $tester = new CommandTester($command);
         $tester->execute([]);
     }
 
-    public function testShouldExecuteConsumptionAndUseCustomClientDestinationName()
+    public function testShouldBindDefaultQueueIfRouteDoesNotDefineQueue()
     {
         $queue = new NullQueue('');
 
+        $routeCollection = new RouteCollection([
+            new Route('topic', Route::TOPIC, 'processor'),
+        ]);
+
         $processor = $this->createDelegateProcessorMock();
 
         $context = $this->createPsrContextMock();
@@ -153,11 +151,79 @@ public function testShouldExecuteConsumptionAndUseCustomClientDestinationName()
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
 
-        $queueMetaRegistry = $this->createQueueMetaRegistry([
-            'non-default-queue' => [],
+        $driver = $this->createDriverStub($routeCollection);
+        $driver
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('default')
+            ->willReturn($queue)
+        ;
+
+        $command = new ConsumeMessagesCommand($consumer, $processor, $driver);
+
+        $tester = new CommandTester($command);
+        $tester->execute([]);
+    }
+
+    public function testShouldBindCustomExecuteConsumptionAndUseCustomClientDestinationName()
+    {
+        $defaultQueue = new NullQueue('');
+        $customQueue = new NullQueue('');
+
+        $routeCollection = new RouteCollection([
+            new Route('topic', Route::TOPIC, 'processor', ['queue' => 'custom']),
+        ]);
+
+        $processor = $this->createDelegateProcessorMock();
+
+        $driver = $this->createDriverStub($routeCollection);
+        $driver
+            ->expects($this->at(3))
+            ->method('createQueue')
+            ->with('default')
+            ->willReturn($defaultQueue)
+        ;
+        $driver
+            ->expects($this->at(4))
+            ->method('createQueue')
+            ->with('custom')
+            ->willReturn($customQueue)
+        ;
+
+        $consumer = $this->createQueueConsumerMock();
+        $consumer
+            ->expects($this->at(0))
+            ->method('bind')
+            ->with($this->identicalTo($defaultQueue), $this->identicalTo($processor))
+        ;
+        $consumer
+            ->expects($this->at(1))
+            ->method('bind')
+            ->with($this->identicalTo($customQueue), $this->identicalTo($processor))
+        ;
+        $consumer
+            ->expects($this->at(2))
+            ->method('consume')
+            ->with($this->isInstanceOf(ChainExtension::class))
+        ;
+
+        $command = new ConsumeMessagesCommand($consumer, $processor, $driver);
+
+        $tester = new CommandTester($command);
+        $tester->execute([]);
+    }
+
+    public function testShouldBindUserProvidedQueues()
+    {
+        $queue = new NullQueue('');
+
+        $routeCollection = new RouteCollection([
+            new Route('topic', Route::TOPIC, 'processor', ['queue' => 'custom']),
         ]);
 
-        $driver = $this->createDriverMock();
+        $processor = $this->createDelegateProcessorMock();
+
+        $driver = $this->createDriverStub($routeCollection);
         $driver
             ->expects($this->once())
             ->method('createQueue')
@@ -165,7 +231,19 @@ public function testShouldExecuteConsumptionAndUseCustomClientDestinationName()
             ->willReturn($queue)
         ;
 
-        $command = new ConsumeMessagesCommand($consumer, $processor, $queueMetaRegistry, $driver);
+        $consumer = $this->createQueueConsumerMock();
+        $consumer
+            ->expects($this->once())
+            ->method('bind')
+            ->with($this->identicalTo($queue), $this->identicalTo($processor))
+        ;
+        $consumer
+            ->expects($this->once())
+            ->method('consume')
+            ->with($this->isInstanceOf(ChainExtension::class))
+        ;
+
+        $command = new ConsumeMessagesCommand($consumer, $processor, $driver);
 
         $tester = new CommandTester($command);
         $tester->execute([
@@ -173,6 +251,58 @@ public function testShouldExecuteConsumptionAndUseCustomClientDestinationName()
         ]);
     }
 
+    public function testShouldBindQueuesOnlyOnce()
+    {
+        $defaultQueue = new NullQueue('');
+        $customQueue = new NullQueue('');
+
+        $routeCollection = new RouteCollection([
+            new Route('fooTopic', Route::TOPIC, 'processor', ['queue' => 'custom']),
+            new Route('barTopic', Route::TOPIC, 'processor', ['queue' => 'custom']),
+            new Route('ololoTopic', Route::TOPIC, 'processor', []),
+        ]);
+
+        $processor = $this->createDelegateProcessorMock();
+
+        $driver = $this->createDriverStub($routeCollection);
+        $driver
+            ->expects($this->at(3))
+            ->method('createQueue')
+            ->with('default')
+            ->willReturn($defaultQueue)
+        ;
+        $driver
+            ->expects($this->at(4))
+            ->method('createQueue')
+            ->with('custom')
+            ->willReturn($customQueue)
+        ;
+
+        $consumer = $this->createQueueConsumerMock();
+        $consumer
+            ->expects($this->at(0))
+            ->method('bind')
+            ->with($this->identicalTo($defaultQueue), $this->identicalTo($processor))
+        ;
+        $consumer
+            ->expects($this->at(1))
+            ->method('bind')
+            ->with($this->identicalTo($customQueue), $this->identicalTo($processor))
+        ;
+        $consumer
+            ->expects($this->at(2))
+            ->method('consume')
+            ->with($this->isInstanceOf(ChainExtension::class))
+        ;
+
+        $command = new ConsumeMessagesCommand($consumer, $processor, $driver);
+
+        $tester = new CommandTester($command);
+        $tester->execute([
+//            'client-queue-names' => ['non-default-queue'],
+        ]);
+    }
+
     public function testShouldSkipQueueConsumptionAndUseCustomClientDestinationName()
     {
         $queue = new NullQueue('');
@@ -187,7 +317,7 @@ public function testShouldSkipQueueConsumptionAndUseCustomClientDestinationName(
 
         $consumer = $this->createQueueConsumerMock();
         $consumer
-            ->expects($this->exactly(2))
+            ->expects($this->exactly(3))
             ->method('bind')
         ;
         $consumer
@@ -196,33 +326,33 @@ public function testShouldSkipQueueConsumptionAndUseCustomClientDestinationName(
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
 
-        $queueMetaRegistry = $this->createQueueMetaRegistry([
-            'fooQueue' => [
-                'transportName' => 'fooTransportQueue',
-            ],
-            'barQueue' => [
-                'transportName' => 'barTransportQueue',
-            ],
-            'ololoQueue' => [
-                'transportName' => 'ololoTransportQueue',
-            ],
+        $routeCollection = new RouteCollection([
+            new Route('fooTopic', Route::TOPIC, 'processor', ['queue' => 'fooQueue']),
+            new Route('barTopic', Route::TOPIC, 'processor', ['queue' => 'barQueue']),
+            new Route('ololoTopic', Route::TOPIC, 'processor', ['queue' => 'ololoQueue']),
         ]);
 
-        $driver = $this->createDriverMock();
+        $driver = $this->createDriverStub($routeCollection);
         $driver
-            ->expects($this->at(0))
+            ->expects($this->at(3))
+            ->method('createQueue')
+            ->with('default')
+            ->willReturn($queue)
+        ;
+        $driver
+            ->expects($this->at(4))
             ->method('createQueue')
             ->with('fooQueue')
             ->willReturn($queue)
         ;
         $driver
-            ->expects($this->at(1))
+            ->expects($this->at(5))
             ->method('createQueue')
             ->with('ololoQueue')
             ->willReturn($queue)
         ;
 
-        $command = new ConsumeMessagesCommand($consumer, $processor, $queueMetaRegistry, $driver);
+        $command = new ConsumeMessagesCommand($consumer, $processor, $driver);
 
         $tester = new CommandTester($command);
         $tester->execute([
@@ -230,25 +360,6 @@ public function testShouldSkipQueueConsumptionAndUseCustomClientDestinationName(
         ]);
     }
 
-    /**
-     * @param array $destinationNames
-     *
-     * @return QueueMetaRegistry
-     */
-    private function createQueueMetaRegistry(array $destinationNames)
-    {
-        $config = new Config(
-            'aPrefix',
-            'anApp',
-            'aRouterTopicName',
-            'aRouterQueueName',
-            'aDefaultQueueName',
-            'aRouterProcessorName'
-        );
-
-        return new QueueMetaRegistry($config, $destinationNames);
-    }
-
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext
      */
@@ -276,8 +387,21 @@ private function createQueueConsumerMock()
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|DriverInterface
      */
-    private function createDriverMock()
+    private function createDriverStub(RouteCollection $routeCollection = null): DriverInterface
     {
-        return $this->createMock(DriverInterface::class);
+        $driverMock = $this->createMock(DriverInterface::class);
+        $driverMock
+            ->expects($this->any())
+            ->method('getRouteCollection')
+            ->willReturn($routeCollection)
+        ;
+
+        $driverMock
+            ->expects($this->any())
+            ->method('getConfig')
+            ->willReturn(Config::create('aPrefix', 'anApp'))
+        ;
+
+        return $driverMock;
     }
 }
diff --git a/Tests/Symfony/Client/ContainerAwareProcessorRegistryTest.php b/Tests/Symfony/Client/ContainerAwareProcessorRegistryTest.php
deleted file mode 100644
index 1d641d4..0000000
--- a/Tests/Symfony/Client/ContainerAwareProcessorRegistryTest.php
+++ /dev/null
@@ -1,84 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Symfony\Client;
-
-use Enqueue\Client\ProcessorRegistryInterface;
-use Enqueue\Symfony\Client\ContainerAwareProcessorRegistry;
-use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrProcessor;
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\DependencyInjection\Container;
-
-class ContainerAwareProcessorRegistryTest extends TestCase
-{
-    use ClassExtensionTrait;
-
-    public function testShouldImplementProcessorRegistryInterface()
-    {
-        $this->assertClassImplements(ProcessorRegistryInterface::class, ContainerAwareProcessorRegistry::class);
-    }
-
-    public function testCouldBeConstructedWithoutAnyArgument()
-    {
-        new ContainerAwareProcessorRegistry();
-    }
-
-    public function testShouldThrowExceptionIfProcessorIsNotSet()
-    {
-        $this->setExpectedException(
-            \LogicException::class,
-            'Processor was not found. processorName: "processor-name"'
-        );
-
-        $registry = new ContainerAwareProcessorRegistry();
-        $registry->get('processor-name');
-    }
-
-    public function testShouldThrowExceptionIfContainerIsNotSet()
-    {
-        $this->setExpectedException(\LogicException::class, 'Container was not set');
-
-        $registry = new ContainerAwareProcessorRegistry();
-        $registry->set('processor-name', 'processor-id');
-
-        $registry->get('processor-name');
-    }
-
-    public function testShouldThrowExceptionIfInstanceOfProcessorIsInvalid()
-    {
-        $this->setExpectedException(\LogicException::class, 'Container was not set');
-
-        $processor = new \stdClass();
-
-        $container = new Container();
-        $container->set('processor-id', $processor);
-
-        $registry = new ContainerAwareProcessorRegistry();
-        $registry->set('processor-name', 'processor-id');
-
-        $registry->get('processor-name');
-    }
-
-    public function testShouldReturnInstanceOfProcessor()
-    {
-        $this->setExpectedException(\LogicException::class, 'Container was not set');
-
-        $processor = $this->createProcessorMock();
-
-        $container = new Container();
-        $container->set('processor-id', $processor);
-
-        $registry = new ContainerAwareProcessorRegistry();
-        $registry->set('processor-name', 'processor-id');
-
-        $this->assertSame($processor, $registry->get('processor-name'));
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProcessor
-     */
-    protected function createProcessorMock()
-    {
-        return $this->createMock(PsrProcessor::class);
-    }
-}
diff --git a/Tests/Symfony/DependencyInjection/AnalyzeRouteCollectionPassTest.php b/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php
similarity index 97%
rename from Tests/Symfony/DependencyInjection/AnalyzeRouteCollectionPassTest.php
rename to Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php
index be844d8..c2f7386 100644
--- a/Tests/Symfony/DependencyInjection/AnalyzeRouteCollectionPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php
@@ -1,9 +1,9 @@
 <?php
 
-namespace Enqueue\Tests\Symfony\DependencyInjection;
+namespace Enqueue\Tests\Symfony\Client\DependencyInjection;
 
 use Enqueue\Client\Route;
-use Enqueue\Symfony\DependencyInjection\AnalyzeRouteCollectionPass;
+use Enqueue\Symfony\Client\DependencyInjection\AnalyzeRouteCollectionPass;
 use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php
new file mode 100644
index 0000000..46f5491
--- /dev/null
+++ b/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php
@@ -0,0 +1,240 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\Client\DependencyInjection;
+
+use Enqueue\Client\ExtensionInterface;
+use Enqueue\Symfony\Client\DependencyInjection\BuildClientExtensionsPass;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+
+class BuildClientExtensionsPassTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementCompilerPassInterface()
+    {
+        $this->assertClassImplements(CompilerPassInterface::class, BuildClientExtensionsPass::class);
+    }
+
+    public function testShouldBeFinal()
+    {
+        $this->assertClassFinal(BuildClientExtensionsPass::class);
+    }
+
+    public function testCouldBeConstructedWithName()
+    {
+        $pass = new BuildClientExtensionsPass('aName');
+
+        $this->assertAttributeSame('aName', 'name', $pass);
+    }
+
+    public function testThrowIfNameEmptyOnConstruct()
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The name could not be empty.');
+        new BuildClientExtensionsPass('');
+    }
+
+    public function testShouldDoNothingIfExtensionsServiceIsNotRegistered()
+    {
+        $container = new ContainerBuilder();
+
+        //guard
+        $this->assertFalse($container->hasDefinition('enqueue.client.aName.client_extensions'));
+
+        $pass = new BuildClientExtensionsPass('aName');
+        $pass->process($container);
+    }
+
+    public function testShouldRegisterClientExtension()
+    {
+        $extensions = new Definition();
+        $extensions->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.aName.client_extensions', $extensions);
+
+        $container->register('aFooExtension', ExtensionInterface::class)
+            ->addTag('enqueue.client_extension', ['client' => 'aName'])
+        ;
+        $container->register('aBarExtension', ExtensionInterface::class)
+            ->addTag('enqueue.client_extension', ['client' => 'aName'])
+        ;
+
+        $pass = new BuildClientExtensionsPass('aName');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $extensions->getArgument(0));
+        $this->assertEquals([
+            new Reference('aFooExtension'),
+            new Reference('aBarExtension'),
+        ], $extensions->getArgument(0));
+    }
+
+    public function testShouldIgnoreOtherClientExtensions()
+    {
+        $extensions = new Definition();
+        $extensions->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.aName.client_extensions', $extensions);
+
+        $container->register('aFooExtension', ExtensionInterface::class)
+            ->addTag('enqueue.client_extension', ['client' => 'aName'])
+        ;
+        $container->register('aBarExtension', ExtensionInterface::class)
+            ->addTag('enqueue.client_extension', ['client' => 'anotherName'])
+        ;
+
+        $pass = new BuildClientExtensionsPass('aName');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $extensions->getArgument(0));
+        $this->assertEquals([
+            new Reference('aFooExtension'),
+        ], $extensions->getArgument(0));
+    }
+
+    public function testShouldAddExtensionIfClientAll()
+    {
+        $extensions = new Definition();
+        $extensions->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.aName.client_extensions', $extensions);
+
+        $container->register('aFooExtension', ExtensionInterface::class)
+            ->addTag('enqueue.client_extension', ['client' => 'all'])
+        ;
+        $container->register('aBarExtension', ExtensionInterface::class)
+            ->addTag('enqueue.client_extension', ['client' => 'anotherName'])
+        ;
+
+        $pass = new BuildClientExtensionsPass('aName');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $extensions->getArgument(0));
+        $this->assertEquals([
+            new Reference('aFooExtension'),
+        ], $extensions->getArgument(0));
+    }
+
+    public function testShouldTreatTagsWithoutClientAsDefaultClient()
+    {
+        $extensions = new Definition();
+        $extensions->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.client_extensions', $extensions);
+
+        $container->register('aFooExtension', ExtensionInterface::class)
+            ->addTag('enqueue.client_extension')
+        ;
+        $container->register('aBarExtension', ExtensionInterface::class)
+            ->addTag('enqueue.client_extension')
+        ;
+
+        $pass = new BuildClientExtensionsPass('default');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $extensions->getArgument(0));
+        $this->assertEquals([
+            new Reference('aFooExtension'),
+            new Reference('aBarExtension'),
+        ], $extensions->getArgument(0));
+    }
+
+    public function testShouldOrderExtensionsByPriority()
+    {
+        $container = new ContainerBuilder();
+
+        $extensions = new Definition();
+        $extensions->addArgument([]);
+        $container->setDefinition('enqueue.client.default.client_extensions', $extensions);
+
+        $extension = new Definition();
+        $extension->addTag('enqueue.client_extension', ['priority' => 6]);
+        $container->setDefinition('foo_extension', $extension);
+
+        $extension = new Definition();
+        $extension->addTag('enqueue.client_extension', ['priority' => -5]);
+        $container->setDefinition('bar_extension', $extension);
+
+        $extension = new Definition();
+        $extension->addTag('enqueue.client_extension', ['priority' => 2]);
+        $container->setDefinition('baz_extension', $extension);
+
+        $pass = new BuildClientExtensionsPass('default');
+        $pass->process($container);
+
+        $orderedExtensions = $extensions->getArgument(0);
+
+        $this->assertCount(3, $orderedExtensions);
+        $this->assertEquals(new Reference('foo_extension'), $orderedExtensions[0]);
+        $this->assertEquals(new Reference('baz_extension'), $orderedExtensions[1]);
+        $this->assertEquals(new Reference('bar_extension'), $orderedExtensions[2]);
+    }
+
+    public function testShouldAssumePriorityZeroIfPriorityIsNotSet()
+    {
+        $container = new ContainerBuilder();
+
+        $extensions = new Definition();
+        $extensions->addArgument([]);
+        $container->setDefinition('enqueue.client.default.client_extensions', $extensions);
+
+        $extension = new Definition();
+        $extension->addTag('enqueue.client_extension');
+        $container->setDefinition('foo_extension', $extension);
+
+        $extension = new Definition();
+        $extension->addTag('enqueue.client_extension', ['priority' => 1]);
+        $container->setDefinition('bar_extension', $extension);
+
+        $extension = new Definition();
+        $extension->addTag('enqueue.client_extension', ['priority' => -1]);
+        $container->setDefinition('baz_extension', $extension);
+
+        $pass = new BuildClientExtensionsPass('default');
+        $pass->process($container);
+
+        $orderedExtensions = $extensions->getArgument(0);
+
+        $this->assertCount(3, $orderedExtensions);
+        $this->assertEquals(new Reference('bar_extension'), $orderedExtensions[0]);
+        $this->assertEquals(new Reference('foo_extension'), $orderedExtensions[1]);
+        $this->assertEquals(new Reference('baz_extension'), $orderedExtensions[2]);
+    }
+
+    public function testShouldMergeWithAddedPreviously()
+    {
+        $extensions = new Definition();
+        $extensions->addArgument([
+            'aBarExtension' => 'aBarServiceIdAddedPreviously',
+            'aOloloExtension' => 'aOloloServiceIdAddedPreviously',
+        ]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.aName.client_extensions', $extensions);
+
+        $container->register('aFooExtension', ExtensionInterface::class)
+            ->addTag('enqueue.client_extension')
+        ;
+        $container->register('aBarExtension', ExtensionInterface::class)
+            ->addTag('enqueue.client_extension')
+        ;
+
+        $pass = new BuildClientExtensionsPass('aName');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $extensions->getArgument(0));
+        $this->assertEquals([
+            'aBarExtension' => 'aBarServiceIdAddedPreviously',
+            'aOloloExtension' => 'aOloloServiceIdAddedPreviously',
+        ], $extensions->getArgument(0));
+    }
+}
diff --git a/Tests/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
similarity index 89%
rename from Tests/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
rename to Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
index 3e4d69a..a93efe3 100644
--- a/Tests/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
@@ -1,11 +1,11 @@
 <?php
 
-namespace Enqueue\Tests\Symfony\DependencyInjection;
+namespace Enqueue\Tests\Symfony\Client\DependencyInjection;
 
 use Enqueue\Client\CommandSubscriberInterface;
 use Enqueue\Client\Route;
 use Enqueue\Client\RouteCollection;
-use Enqueue\Symfony\DependencyInjection\BuildCommandSubscriberRoutesPass;
+use Enqueue\Symfony\Client\DependencyInjection\BuildCommandSubscriberRoutesPass;
 use Enqueue\Test\ClassExtensionTrait;
 use Interop\Queue\PsrContext;
 use Interop\Queue\PsrMessage;
@@ -223,6 +223,44 @@ public function testShouldRegisterProcessorIfCommandsAreStrings()
         );
     }
 
+    public function testShouldRegisterProcessorIfParamSingleCommandArray()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $processor = $this->createCommandSubscriberProcessor([
+            'command' => 'fooCommand',
+            'processor' => 'aCustomFooProcessorName',
+            'anOption' => 'aFooVal',
+        ]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($processor))
+            ->addTag('enqueue.command_subscriber')
+        ;
+
+        $pass = new BuildCommandSubscriberRoutesPass('default');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+
+        $this->assertCount(1, $routeCollection->getArgument(0));
+
+        $this->assertEquals(
+            [
+                [
+                    'source' => 'fooCommand',
+                    'source_type' => 'enqueue.client.command_route',
+                    'processor' => 'aCustomFooProcessorName',
+                    'processor_service_id' => 'aFooProcessor',
+                    'anOption' => 'aFooVal',
+                ],
+            ],
+            $routeCollection->getArgument(0)
+        );
+    }
+
     public function testShouldRegisterProcessorIfCommandsAreParamArrays()
     {
         $routeCollection = new Definition(RouteCollection::class);
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php
new file mode 100644
index 0000000..68ad2ab
--- /dev/null
+++ b/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php
@@ -0,0 +1,240 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\Client\DependencyInjection;
+
+use Enqueue\Client\ExtensionInterface;
+use Enqueue\Symfony\Client\DependencyInjection\BuildConsumptionExtensionsPass;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+
+class BuildConsumptionExtensionsPassTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementCompilerPassInterface()
+    {
+        $this->assertClassImplements(CompilerPassInterface::class, BuildConsumptionExtensionsPass::class);
+    }
+
+    public function testShouldBeFinal()
+    {
+        $this->assertClassFinal(BuildConsumptionExtensionsPass::class);
+    }
+
+    public function testCouldBeConstructedWithName()
+    {
+        $pass = new BuildConsumptionExtensionsPass('aName');
+
+        $this->assertAttributeSame('aName', 'name', $pass);
+    }
+
+    public function testThrowIfNameEmptyOnConstruct()
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The name could not be empty.');
+        new BuildConsumptionExtensionsPass('');
+    }
+
+    public function testShouldDoNothingIfExtensionsServiceIsNotRegistered()
+    {
+        $container = new ContainerBuilder();
+
+        //guard
+        $this->assertFalse($container->hasDefinition('enqueue.client.aName.consumption_extensions'));
+
+        $pass = new BuildConsumptionExtensionsPass('aName');
+        $pass->process($container);
+    }
+
+    public function testShouldRegisterClientExtension()
+    {
+        $extensions = new Definition();
+        $extensions->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.aName.consumption_extensions', $extensions);
+
+        $container->register('aFooExtension', ExtensionInterface::class)
+            ->addTag('enqueue.consumption_extension', ['client' => 'aName'])
+        ;
+        $container->register('aBarExtension', ExtensionInterface::class)
+            ->addTag('enqueue.consumption_extension', ['client' => 'aName'])
+        ;
+
+        $pass = new BuildConsumptionExtensionsPass('aName');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $extensions->getArgument(0));
+        $this->assertEquals([
+            new Reference('aFooExtension'),
+            new Reference('aBarExtension'),
+        ], $extensions->getArgument(0));
+    }
+
+    public function testShouldIgnoreOtherClientExtensions()
+    {
+        $extensions = new Definition();
+        $extensions->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.aName.consumption_extensions', $extensions);
+
+        $container->register('aFooExtension', ExtensionInterface::class)
+            ->addTag('enqueue.consumption_extension', ['client' => 'aName'])
+        ;
+        $container->register('aBarExtension', ExtensionInterface::class)
+            ->addTag('enqueue.consumption_extension', ['client' => 'anotherName'])
+        ;
+
+        $pass = new BuildConsumptionExtensionsPass('aName');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $extensions->getArgument(0));
+        $this->assertEquals([
+            new Reference('aFooExtension'),
+        ], $extensions->getArgument(0));
+    }
+
+    public function testShouldAddExtensionIfClientAll()
+    {
+        $extensions = new Definition();
+        $extensions->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.aName.consumption_extensions', $extensions);
+
+        $container->register('aFooExtension', ExtensionInterface::class)
+            ->addTag('enqueue.consumption_extension', ['client' => 'all'])
+        ;
+        $container->register('aBarExtension', ExtensionInterface::class)
+            ->addTag('enqueue.consumption_extension', ['client' => 'anotherName'])
+        ;
+
+        $pass = new BuildConsumptionExtensionsPass('aName');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $extensions->getArgument(0));
+        $this->assertEquals([
+            new Reference('aFooExtension'),
+        ], $extensions->getArgument(0));
+    }
+
+    public function testShouldTreatTagsWithoutClientAsDefaultClient()
+    {
+        $extensions = new Definition();
+        $extensions->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.default.consumption_extensions', $extensions);
+
+        $container->register('aFooExtension', ExtensionInterface::class)
+            ->addTag('enqueue.consumption_extension')
+        ;
+        $container->register('aBarExtension', ExtensionInterface::class)
+            ->addTag('enqueue.consumption_extension')
+        ;
+
+        $pass = new BuildConsumptionExtensionsPass('default');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $extensions->getArgument(0));
+        $this->assertEquals([
+            new Reference('aFooExtension'),
+            new Reference('aBarExtension'),
+        ], $extensions->getArgument(0));
+    }
+
+    public function testShouldOrderExtensionsByPriority()
+    {
+        $container = new ContainerBuilder();
+
+        $extensions = new Definition();
+        $extensions->addArgument([]);
+        $container->setDefinition('enqueue.client.default.consumption_extensions', $extensions);
+
+        $extension = new Definition();
+        $extension->addTag('enqueue.consumption_extension', ['priority' => 6]);
+        $container->setDefinition('foo_extension', $extension);
+
+        $extension = new Definition();
+        $extension->addTag('enqueue.consumption_extension', ['priority' => -5]);
+        $container->setDefinition('bar_extension', $extension);
+
+        $extension = new Definition();
+        $extension->addTag('enqueue.consumption_extension', ['priority' => 2]);
+        $container->setDefinition('baz_extension', $extension);
+
+        $pass = new BuildConsumptionExtensionsPass('default');
+        $pass->process($container);
+
+        $orderedExtensions = $extensions->getArgument(0);
+
+        $this->assertCount(3, $orderedExtensions);
+        $this->assertEquals(new Reference('foo_extension'), $orderedExtensions[0]);
+        $this->assertEquals(new Reference('baz_extension'), $orderedExtensions[1]);
+        $this->assertEquals(new Reference('bar_extension'), $orderedExtensions[2]);
+    }
+
+    public function testShouldAssumePriorityZeroIfPriorityIsNotSet()
+    {
+        $container = new ContainerBuilder();
+
+        $extensions = new Definition();
+        $extensions->addArgument([]);
+        $container->setDefinition('enqueue.client.default.consumption_extensions', $extensions);
+
+        $extension = new Definition();
+        $extension->addTag('enqueue.consumption_extension');
+        $container->setDefinition('foo_extension', $extension);
+
+        $extension = new Definition();
+        $extension->addTag('enqueue.consumption_extension', ['priority' => 1]);
+        $container->setDefinition('bar_extension', $extension);
+
+        $extension = new Definition();
+        $extension->addTag('enqueue.consumption_extension', ['priority' => -1]);
+        $container->setDefinition('baz_extension', $extension);
+
+        $pass = new BuildConsumptionExtensionsPass('default');
+        $pass->process($container);
+
+        $orderedExtensions = $extensions->getArgument(0);
+
+        $this->assertCount(3, $orderedExtensions);
+        $this->assertEquals(new Reference('bar_extension'), $orderedExtensions[0]);
+        $this->assertEquals(new Reference('foo_extension'), $orderedExtensions[1]);
+        $this->assertEquals(new Reference('baz_extension'), $orderedExtensions[2]);
+    }
+
+    public function testShouldMergeWithAddedPreviously()
+    {
+        $extensions = new Definition();
+        $extensions->addArgument([
+            'aBarExtension' => 'aBarServiceIdAddedPreviously',
+            'aOloloExtension' => 'aOloloServiceIdAddedPreviously',
+        ]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.client.aName.consumption_extensions', $extensions);
+
+        $container->register('aFooExtension', ExtensionInterface::class)
+            ->addTag('enqueue.consumption_extension')
+        ;
+        $container->register('aBarExtension', ExtensionInterface::class)
+            ->addTag('enqueue.consumption_extension')
+        ;
+
+        $pass = new BuildConsumptionExtensionsPass('aName');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $extensions->getArgument(0));
+        $this->assertEquals([
+            'aBarExtension' => 'aBarServiceIdAddedPreviously',
+            'aOloloExtension' => 'aOloloServiceIdAddedPreviously',
+        ], $extensions->getArgument(0));
+    }
+}
diff --git a/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php
similarity index 70%
rename from Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
rename to Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php
index 70ad5df..5979652 100644
--- a/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php
@@ -1,14 +1,15 @@
 <?php
 
-namespace Enqueue\Tests\Symfony\DependencyInjection;
+namespace Enqueue\Tests\Symfony\Client\DependencyInjection;
 
 use Enqueue\Client\Route;
-use Enqueue\Symfony\DependencyInjection\BuildProcessorRegistryPass;
+use Enqueue\Symfony\Client\DependencyInjection\BuildProcessorRegistryPass;
 use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
 
 class BuildProcessorRegistryPassTest extends TestCase
 {
@@ -61,60 +62,43 @@ public function testShouldDoNothingIfProcessorRegistryCollectionServiceIsNotRegi
         $pass->process($container);
     }
 
-    public function testThrowIfProcessorServiceIdOptionNotSet()
+    public function testShouldDoNothingIfRouterProcessorServiceIsNotRegistered()
     {
         $container = new ContainerBuilder();
-        $container->register('enqueue.client.aName.route_collection')->addArgument([
-            (new Route('aCommand', Route::COMMAND, 'aProcessor'))->toArray(),
-        ]);
-        $container->register('enqueue.client.aName.processor_registry')->addArgument([]);
+        $container->register('enqueue.client.aName.route_collection');
+        $container->register('enqueue.client.aName.processor_registry')
+            ->addArgument([])
+        ;
 
-        $pass = new BuildProcessorRegistryPass('aName');
+        //guard
+        $this->assertFalse($container->hasDefinition('enqueue.client.aName.router_processor'));
 
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The route option "processor_service_id" is required');
+        $pass = new BuildProcessorRegistryPass('aName');
         $pass->process($container);
+
+        $this->assertSame([], $container->getDefinition('enqueue.client.aName.processor_registry')->getArgument(0));
     }
 
-    public function testShouldSetProcessorsMapToRegistryAsFirstArgument()
+    public function testThrowIfProcessorServiceIdOptionNotSet()
     {
-        $registry = new Definition();
-        $registry->addArgument([]);
-
         $container = new ContainerBuilder();
         $container->register('enqueue.client.aName.route_collection')->addArgument([
-            (new Route(
-                'aCommand',
-                Route::COMMAND,
-                'aBarProcessor',
-                ['processor_service_id' => 'aBarServiceId']
-            ))->toArray(),
-            (new Route(
-                'aTopic',
-                Route::TOPIC,
-                'aFooProcessor',
-                ['processor_service_id' => 'aFooServiceId']
-            ))->toArray(),
+            (new Route('aCommand', Route::COMMAND, 'aProcessor'))->toArray(),
         ]);
-        $container->setDefinition('enqueue.client.aName.processor_registry', $registry);
+        $container->register('enqueue.client.aName.processor_registry')->addArgument([]);
+        $container->register('enqueue.client.aName.router_processor');
 
         $pass = new BuildProcessorRegistryPass('aName');
-        $pass->process($container);
 
-        $this->assertInternalType('array', $registry->getArgument(0));
-        $this->assertEquals([
-            'aBarProcessor' => 'aBarServiceId',
-            'aFooProcessor' => 'aFooServiceId',
-        ], $registry->getArgument(0));
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The route option "processor_service_id" is required');
+        $pass->process($container);
     }
 
-    public function testShouldMergeWithAddedPreviously()
+    public function testShouldPassLocatorAsFirstArgument()
     {
         $registry = new Definition();
-        $registry->addArgument([
-            'aBarProcessor' => 'aBarServiceIdAddedPreviously',
-            'aOloloProcessor' => 'aOloloServiceIdAddedPreviously',
-        ]);
+        $registry->addArgument([]);
 
         $container = new ContainerBuilder();
         $container->register('enqueue.client.aName.route_collection')->addArgument([
@@ -132,15 +116,12 @@ public function testShouldMergeWithAddedPreviously()
             ))->toArray(),
         ]);
         $container->setDefinition('enqueue.client.aName.processor_registry', $registry);
+        $container->register('enqueue.client.aName.router_processor');
 
         $pass = new BuildProcessorRegistryPass('aName');
         $pass->process($container);
 
-        $this->assertInternalType('array', $registry->getArgument(0));
-        $this->assertEquals([
-            'aOloloProcessor' => 'aOloloServiceIdAddedPreviously',
-            'aBarProcessor' => 'aBarServiceId',
-            'aFooProcessor' => 'aFooServiceId',
-        ], $registry->getArgument(0));
+        $this->assertInstanceOf(Reference::class, $registry->getArgument(0));
+        $this->assertRegExp('/service_locator\..*?\.enqueue\.client\.aName\.processor_registry/', (string) $registry->getArgument(0));
     }
 }
diff --git a/Tests/Symfony/DependencyInjection/BuildProcessorRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php
similarity index 97%
rename from Tests/Symfony/DependencyInjection/BuildProcessorRoutesPassTest.php
rename to Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php
index 517f98b..b0d7af5 100644
--- a/Tests/Symfony/DependencyInjection/BuildProcessorRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php
@@ -1,10 +1,10 @@
 <?php
 
-namespace Enqueue\Tests\Symfony\DependencyInjection;
+namespace Enqueue\Tests\Symfony\Client\DependencyInjection;
 
 use Enqueue\Client\Route;
 use Enqueue\Client\RouteCollection;
-use Enqueue\Symfony\DependencyInjection\BuildProcessorRoutesPass;
+use Enqueue\Symfony\Client\DependencyInjection\BuildProcessorRoutesPass;
 use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
@@ -59,7 +59,7 @@ public function testThrowIfBothTopicAndCommandAttributesAreSet()
         $pass = new BuildProcessorRoutesPass('default');
 
         $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Either "topic" or "command" tag attribute must be set. Both are set.');
+        $this->expectExceptionMessage('Either "topic" or "command" tag attribute must be set on service "aFooProcessor". Both are set.');
         $pass->process($container);
     }
 
@@ -77,7 +77,7 @@ public function testThrowIfNeitherTopicNorCommandAttributesAreSet()
         $pass = new BuildProcessorRoutesPass('default');
 
         $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Either "topic" or "command" tag attribute must be set. None is set.');
+        $this->expectExceptionMessage('Either "topic" or "command" tag attribute must be set on service "aFooProcessor". None is set.');
         $pass->process($container);
     }
 
diff --git a/Tests/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
similarity index 98%
rename from Tests/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
rename to Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
index af3b0b1..0ad70a2 100644
--- a/Tests/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
@@ -1,11 +1,11 @@
 <?php
 
-namespace Enqueue\Tests\Symfony\DependencyInjection;
+namespace Enqueue\Tests\Symfony\Client\DependencyInjection;
 
 use Enqueue\Client\Route;
 use Enqueue\Client\RouteCollection;
 use Enqueue\Client\TopicSubscriberInterface;
-use Enqueue\Symfony\DependencyInjection\BuildTopicSubscriberRoutesPass;
+use Enqueue\Symfony\Client\DependencyInjection\BuildTopicSubscriberRoutesPass;
 use Enqueue\Test\ClassExtensionTrait;
 use Interop\Queue\PsrContext;
 use Interop\Queue\PsrMessage;
diff --git a/Tests/Symfony/Client/Meta/QueuesCommandTest.php b/Tests/Symfony/Client/Meta/QueuesCommandTest.php
deleted file mode 100644
index f0720c7..0000000
--- a/Tests/Symfony/Client/Meta/QueuesCommandTest.php
+++ /dev/null
@@ -1,109 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Symfony\Client\Meta;
-
-use Enqueue\Client\Meta\QueueMeta;
-use Enqueue\Client\Meta\QueueMetaRegistry;
-use Enqueue\Symfony\Client\Meta\QueuesCommand;
-use Enqueue\Test\ClassExtensionTrait;
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\Console\Command\Command;
-use Symfony\Component\Console\Tester\CommandTester;
-
-class QueuesCommandTest extends TestCase
-{
-    use ClassExtensionTrait;
-
-    public function testShouldBeSubClassOfCommand()
-    {
-        $this->assertClassExtends(Command::class, QueuesCommand::class);
-    }
-
-    public function testCouldBeConstructedWithQueueMetaRegistryAsFirstArgument()
-    {
-        new QueuesCommand($this->createQueueMetaRegistryStub());
-    }
-
-    public function testShouldHaveCommandName()
-    {
-        $command = new QueuesCommand($this->createQueueMetaRegistryStub());
-
-        $this->assertEquals('enqueue:queues', $command->getName());
-    }
-
-    public function testShouldHaveCommandAliases()
-    {
-        $command = new QueuesCommand($this->createQueueMetaRegistryStub());
-
-        $this->assertEquals(['enq:m:q', 'debug:enqueue:queues'], $command->getAliases());
-    }
-
-    public function testShouldShowMessageFoundZeroDestinationsIfAnythingInRegistry()
-    {
-        $command = new QueuesCommand($this->createQueueMetaRegistryStub());
-
-        $output = $this->executeCommand($command);
-
-        $this->assertContains('Found 0 destinations', $output);
-    }
-
-    public function testShouldShowMessageFoundTwoDestinations()
-    {
-        $command = new QueuesCommand($this->createQueueMetaRegistryStub([
-            new QueueMeta('aClientName', 'aDestinationName'),
-            new QueueMeta('anotherClientName', 'anotherDestinationName'),
-        ]));
-
-        $output = $this->executeCommand($command);
-
-        $this->assertContains('Found 2 destinations', $output);
-    }
-
-    public function testShouldShowInfoAboutDestinations()
-    {
-        $command = new QueuesCommand($this->createQueueMetaRegistryStub([
-            new QueueMeta('aFooClientName', 'aFooDestinationName', ['fooSubscriber']),
-            new QueueMeta('aBarClientName', 'aBarDestinationName', ['barSubscriber']),
-        ]));
-
-        $output = $this->executeCommand($command);
-
-        $this->assertContains('aFooClientName', $output);
-        $this->assertContains('aFooDestinationName', $output);
-        $this->assertContains('fooSubscriber', $output);
-        $this->assertContains('aBarClientName', $output);
-        $this->assertContains('aBarDestinationName', $output);
-        $this->assertContains('barSubscriber', $output);
-    }
-
-    /**
-     * @param Command  $command
-     * @param string[] $arguments
-     *
-     * @return string
-     */
-    protected function executeCommand(Command $command, array $arguments = [])
-    {
-        $tester = new CommandTester($command);
-        $tester->execute($arguments);
-
-        return $tester->getDisplay();
-    }
-
-    /**
-     * @param mixed $destinations
-     *
-     * @return \PHPUnit_Framework_MockObject_MockObject|QueueMetaRegistry
-     */
-    protected function createQueueMetaRegistryStub($destinations = [])
-    {
-        $registryMock = $this->createMock(QueueMetaRegistry::class);
-        $registryMock
-            ->expects($this->any())
-            ->method('getQueuesMeta')
-            ->willReturn($destinations)
-        ;
-
-        return $registryMock;
-    }
-}
diff --git a/Tests/Symfony/Client/Meta/TopicsCommandTest.php b/Tests/Symfony/Client/Meta/TopicsCommandTest.php
deleted file mode 100644
index 4efdd2f..0000000
--- a/Tests/Symfony/Client/Meta/TopicsCommandTest.php
+++ /dev/null
@@ -1,91 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Symfony\Client\Meta;
-
-use Enqueue\Client\Meta\TopicMetaRegistry;
-use Enqueue\Symfony\Client\Meta\TopicsCommand;
-use Enqueue\Test\ClassExtensionTrait;
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\Console\Command\Command;
-use Symfony\Component\Console\Tester\CommandTester;
-
-class TopicsCommandTest extends TestCase
-{
-    use ClassExtensionTrait;
-
-    public function testShouldBeSubClassOfCommand()
-    {
-        $this->assertClassExtends(Command::class, TopicsCommand::class);
-    }
-
-    public function testCouldBeConstructedWithTopicMetaRegistryAsFirstArgument()
-    {
-        new TopicsCommand(new TopicMetaRegistry([]));
-    }
-
-    public function testShouldHaveCommandName()
-    {
-        $command = new TopicsCommand(new TopicMetaRegistry([]));
-
-        $this->assertEquals('enqueue:topics', $command->getName());
-    }
-
-    public function testShouldHaveCommandAliases()
-    {
-        $command = new TopicsCommand(new TopicMetaRegistry([]));
-
-        $this->assertEquals(['enq:m:t', 'debug:enqueue:topics'], $command->getAliases());
-    }
-
-    public function testShouldShowMessageFoundZeroTopicsIfAnythingInRegistry()
-    {
-        $command = new TopicsCommand(new TopicMetaRegistry([]));
-
-        $output = $this->executeCommand($command);
-
-        $this->assertContains('Found 0 topics', $output);
-    }
-
-    public function testShouldShowMessageFoundTwoTopics()
-    {
-        $command = new TopicsCommand(new TopicMetaRegistry([
-            'fooTopic' => [],
-            'barTopic' => [],
-        ]));
-
-        $output = $this->executeCommand($command);
-
-        $this->assertContains('Found 2 topics', $output);
-    }
-
-    public function testShouldShowInfoAboutTopics()
-    {
-        $command = new TopicsCommand(new TopicMetaRegistry([
-            'fooTopic' => ['description' => 'fooDescription', 'processors' => ['fooSubscriber']],
-            'barTopic' => ['description' => 'barDescription', 'processors' => ['barSubscriber']],
-        ]));
-
-        $output = $this->executeCommand($command);
-
-        $this->assertContains('fooTopic', $output);
-        $this->assertContains('fooDescription', $output);
-        $this->assertContains('fooSubscriber', $output);
-        $this->assertContains('barTopic', $output);
-        $this->assertContains('barDescription', $output);
-        $this->assertContains('barSubscriber', $output);
-    }
-
-    /**
-     * @param Command  $command
-     * @param string[] $arguments
-     *
-     * @return string
-     */
-    protected function executeCommand(Command $command, array $arguments = [])
-    {
-        $tester = new CommandTester($command);
-        $tester->execute($arguments);
-
-        return $tester->getDisplay();
-    }
-}
diff --git a/Tests/Symfony/Client/RoutesCommandTest.php b/Tests/Symfony/Client/RoutesCommandTest.php
new file mode 100644
index 0000000..6100b64
--- /dev/null
+++ b/Tests/Symfony/Client/RoutesCommandTest.php
@@ -0,0 +1,262 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\Client;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\Route;
+use Enqueue\Client\RouteCollection;
+use Enqueue\Symfony\Client\RoutesCommand;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Tester\CommandTester;
+
+class RoutesCommandTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldBeSubClassOfCommand()
+    {
+        $this->assertClassExtends(Command::class, RoutesCommand::class);
+    }
+
+    public function testShouldBeFinal()
+    {
+        $this->assertClassFinal(RoutesCommand::class);
+    }
+
+    public function testCouldBeConstructedWithConfigAndRouteCollectionAsArguments()
+    {
+        new RoutesCommand(Config::create(), new RouteCollection([]));
+    }
+
+    public function testShouldHaveCommandName()
+    {
+        $command = new RoutesCommand(Config::create(), new RouteCollection([]));
+
+        $this->assertEquals('enqueue:routes', $command->getName());
+    }
+
+    public function testShouldHaveCommandAliases()
+    {
+        $command = new RoutesCommand(Config::create(), new RouteCollection([]));
+
+        $this->assertEquals(['debug:enqueue:routes'], $command->getAliases());
+    }
+
+    public function testShouldHaveExpectedOptions()
+    {
+        $command = new RoutesCommand(Config::create(), new RouteCollection([]));
+
+        $options = $command->getDefinition()->getOptions();
+        $this->assertCount(1, $options);
+
+        $this->assertArrayHasKey('show-route-options', $options);
+    }
+
+    public function testShouldHaveExpectedAttributes()
+    {
+        $command = new RoutesCommand(Config::create(), new RouteCollection([]));
+
+        $arguments = $command->getDefinition()->getArguments();
+        $this->assertCount(0, $arguments);
+    }
+
+    public function testShouldOutputEmptyRouteCollection()
+    {
+        $routeCollection = new RouteCollection([]);
+
+        $command = new RoutesCommand(Config::create(), $routeCollection);
+
+        $tester = new CommandTester($command);
+
+        $tester->execute([]);
+
+        $expectedOutput = <<<'OUTPUT'
+Found 0 routes
+
+
+OUTPUT;
+
+        $this->assertCommandOutput($expectedOutput, $tester);
+    }
+
+    public function testShouldOutputTopicRouteInfo()
+    {
+        $routeCollection = new RouteCollection([
+            new Route('fooTopic', Route::TOPIC, 'processor'),
+            new Route('barTopic', Route::TOPIC, 'processor'),
+        ]);
+
+        $command = new RoutesCommand(Config::create(), $routeCollection);
+
+        $tester = new CommandTester($command);
+
+        $tester->execute([]);
+
+        $expectedOutput = <<<'OUTPUT'
+Found 2 routes
+
++-------+----------+--------------------+-----------+----------+
+| Type  | Source   | Queue              | Processor | Options  |
++-------+----------+--------------------+-----------+----------+
+| topic | fooTopic | default (prefixed) | processor | (hidden) |
+| topic | barTopic | default (prefixed) | processor | (hidden) |
++-------+----------+--------------------+-----------+----------+
+
+OUTPUT;
+
+        $this->assertCommandOutput($expectedOutput, $tester);
+    }
+
+    public function testShouldOutputCommandRouteInfo()
+    {
+        $routeCollection = new RouteCollection([
+            new Route('fooCommand', Route::COMMAND, 'processor', ['foo' => 'fooVal', 'bar' => 'barVal']),
+            new Route('barCommand', Route::COMMAND, 'processor', ['foo' => 'fooVal', 'bar' => 'barVal']),
+        ]);
+
+        $command = new RoutesCommand(Config::create(), $routeCollection);
+
+        $tester = new CommandTester($command);
+
+        $tester->execute([]);
+
+        $expectedOutput = <<<'OUTPUT'
+Found 2 routes
+
++---------+------------+--------------------+-----------+----------+
+| Type    | Source     | Queue              | Processor | Options  |
++---------+------------+--------------------+-----------+----------+
+| command | fooCommand | default (prefixed) | processor | (hidden) |
+| command | barCommand | default (prefixed) | processor | (hidden) |
++---------+------------+--------------------+-----------+----------+
+
+OUTPUT;
+
+        $this->assertCommandOutput($expectedOutput, $tester);
+    }
+
+    public function testShouldCorrectlyOutputPrefixedCustomQueue()
+    {
+        $routeCollection = new RouteCollection([
+            new Route('fooCommand', Route::COMMAND, 'processor', ['queue' => 'foo']),
+            new Route('barTopic', Route::TOPIC, 'processor', ['queue' => 'bar']),
+        ]);
+
+        $command = new RoutesCommand(Config::create(), $routeCollection);
+
+        $tester = new CommandTester($command);
+
+        $exitCode = $tester->execute([]);
+        $this->assertSame(0, $exitCode);
+
+        $expectedOutput = <<<'OUTPUT'
+Found 2 routes
+
++---------+------------+----------------+-----------+----------+
+| Type    | Source     | Queue          | Processor | Options  |
++---------+------------+----------------+-----------+----------+
+| topic   | barTopic   | bar (prefixed) | processor | (hidden) |
+| command | fooCommand | foo (prefixed) | processor | (hidden) |
++---------+------------+----------------+-----------+----------+
+
+OUTPUT;
+
+        $this->assertCommandOutput($expectedOutput, $tester);
+    }
+
+    public function testShouldCorrectlyOutputNotPrefixedCustomQueue()
+    {
+        $routeCollection = new RouteCollection([
+            new Route('fooCommand', Route::COMMAND, 'processor', ['queue' => 'foo', 'prefix_queue' => false]),
+            new Route('barTopic', Route::TOPIC, 'processor', ['queue' => 'bar', 'prefix_queue' => false]),
+        ]);
+
+        $command = new RoutesCommand(Config::create(), $routeCollection);
+
+        $tester = new CommandTester($command);
+
+        $tester->execute([]);
+
+        $expectedOutput = <<<'OUTPUT'
+Found 2 routes
+
++---------+------------+-------------+-----------+----------+
+| Type    | Source     | Queue       | Processor | Options  |
++---------+------------+-------------+-----------+----------+
+| topic   | barTopic   | bar (as is) | processor | (hidden) |
+| command | fooCommand | foo (as is) | processor | (hidden) |
++---------+------------+-------------+-----------+----------+
+
+OUTPUT;
+
+        $this->assertCommandOutput($expectedOutput, $tester);
+    }
+
+    public function testShouldCorrectlyOutputExternalRoute()
+    {
+        $routeCollection = new RouteCollection([
+            new Route('fooCommand', Route::COMMAND, 'processor', ['external' => true]),
+            new Route('barTopic', Route::TOPIC, 'processor', ['external' => true]),
+        ]);
+
+        $command = new RoutesCommand(Config::create(), $routeCollection);
+
+        $tester = new CommandTester($command);
+
+        $tester->execute([]);
+
+        $expectedOutput = <<<OUTPUT
+Found 2 routes
+
++---------+------------+--------------------+----------------+----------+
+| Type    | Source     | Queue              | Processor      | Options  |
++---------+------------+--------------------+----------------+----------+
+| topic   | barTopic   | default (prefixed) | n\a (external) | (hidden) |
+| command | fooCommand | default (prefixed) | n\a (external) | (hidden) |
++---------+------------+--------------------+----------------+----------+
+
+OUTPUT;
+
+        $this->assertCommandOutput($expectedOutput, $tester);
+    }
+
+    public function testShouldOutputRouteOptions()
+    {
+        $routeCollection = new RouteCollection([
+            new Route('fooCommand', Route::COMMAND, 'processor', ['foo' => 'fooVal']),
+            new Route('barTopic', Route::TOPIC, 'processor', ['bar' => 'barVal']),
+        ]);
+
+        $command = new RoutesCommand(Config::create(), $routeCollection);
+
+        $tester = new CommandTester($command);
+
+        $tester->execute(['--show-route-options' => true]);
+
+        $expectedOutput = <<<'OUTPUT'
+Found 2 routes
+
++---------+------------+--------------------+-----------+----------------------+
+| Type    | Source     | Queue              | Processor | Options              |
++---------+------------+--------------------+-----------+----------------------+
+| topic   | barTopic   | default (prefixed) | processor | array (              |
+|         |            |                    |           |   'bar' => 'barVal', |
+|         |            |                    |           | )                    |
+| command | fooCommand | default (prefixed) | processor | array (              |
+|         |            |                    |           |   'foo' => 'fooVal', |
+|         |            |                    |           | )                    |
++---------+------------+--------------------+-----------+----------------------+
+
+OUTPUT;
+
+        $this->assertCommandOutput($expectedOutput, $tester);
+    }
+
+    private function assertCommandOutput(string $expected, CommandTester $tester): void
+    {
+        $this->assertSame(0, $tester->getStatusCode());
+        $this->assertSame($expected, $tester->getDisplay());
+    }
+}
diff --git a/Tests/Symfony/DependencyInjection/ClientFactoryTest.php b/Tests/Symfony/DependencyInjection/ClientFactoryTest.php
new file mode 100644
index 0000000..bb4cfe9
--- /dev/null
+++ b/Tests/Symfony/DependencyInjection/ClientFactoryTest.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\DependencyInjection;
+
+use Enqueue\Symfony\DependencyInjection\ClientFactory;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+class ClientFactoryTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldBeFinal()
+    {
+        $this->assertClassFinal(ClientFactory::class);
+    }
+
+    public function testShouldAllowGetNameSetInConstructor()
+    {
+        $transport = new ClientFactory('aName');
+
+        $this->assertEquals('aName', $transport->getName());
+    }
+
+    public function testThrowIfEmptyNameGivenOnConstruction()
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The name could not be empty.');
+
+        new ClientFactory('');
+    }
+
+    public function testShouldCreateDriverFromDsn()
+    {
+        $container = new ContainerBuilder();
+
+        $transport = new ClientFactory('default');
+
+        $serviceId = $transport->createDriver($container, ['dsn' => 'foo://bar/baz', 'foo' => 'fooVal']);
+
+        $this->assertEquals('enqueue.client.default.driver', $serviceId);
+
+        $this->assertTrue($container->hasDefinition('enqueue.client.default.driver'));
+
+        $this->assertNotEmpty($container->getDefinition('enqueue.client.default.driver')->getFactory());
+        $this->assertEquals(
+            [new Reference('enqueue.client.default.driver_factory'), 'create'],
+            $container->getDefinition('enqueue.client.default.driver')->getFactory())
+        ;
+        $this->assertEquals(
+            [
+                new Reference('enqueue.transport.default.connection_factory'),
+                'foo://bar/baz',
+                ['dsn' => 'foo://bar/baz', 'foo' => 'fooVal'],
+            ],
+            $container->getDefinition('enqueue.client.default.driver')->getArguments())
+        ;
+    }
+}
diff --git a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index f537751..8c2cc32 100644
--- a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -257,19 +257,13 @@ public function testShouldCreateConnectionFactoryFromDSN()
 
         $this->assertNotEmpty($container->getDefinition('enqueue.transport.default.connection_factory')->getFactory());
         $this->assertEquals(
-            [new Reference('enqueue.connection_factory_factory'), 'create'],
+            [new Reference('enqueue.transport.default.connection_factory_factory'), 'create'],
             $container->getDefinition('enqueue.transport.default.connection_factory')->getFactory())
         ;
         $this->assertSame(
             [['dsn' => 'foo://bar/baz']],
             $container->getDefinition('enqueue.transport.default.connection_factory')->getArguments())
         ;
-
-        $this->assertTrue($container->hasAlias('enqueue.transport.connection_factory'));
-        $this->assertEquals(
-            'enqueue.transport.default.connection_factory',
-            (string) $container->getAlias('enqueue.transport.connection_factory')
-        );
     }
 
     public function testShouldCreateConnectionFactoryUsingCustomFactoryClass()
@@ -299,12 +293,6 @@ public function testShouldCreateConnectionFactoryUsingCustomFactoryClass()
             [['dsn' => 'foo:']],
             $container->getDefinition('enqueue.transport.default.connection_factory')->getArguments())
         ;
-
-        $this->assertTrue($container->hasAlias('enqueue.transport.connection_factory'));
-        $this->assertEquals(
-            'enqueue.transport.default.connection_factory',
-            (string) $container->getAlias('enqueue.transport.connection_factory')
-        );
     }
 
     public function testShouldCreateConnectionFactoryUsingCustomFactoryService()
@@ -328,12 +316,6 @@ public function testShouldCreateConnectionFactoryUsingCustomFactoryService()
             [['dsn' => 'foo:']],
             $container->getDefinition('enqueue.transport.default.connection_factory')->getArguments())
         ;
-
-        $this->assertTrue($container->hasAlias('enqueue.transport.connection_factory'));
-        $this->assertEquals(
-            'enqueue.transport.default.connection_factory',
-            (string) $container->getAlias('enqueue.transport.connection_factory')
-        );
     }
 
     public function testShouldCreateConnectionFactoryUsingConnectionFactoryClassWithoutFactory()
@@ -354,12 +336,6 @@ public function testShouldCreateConnectionFactoryUsingConnectionFactoryClassWith
             [['dsn' => 'foo:']],
             $container->getDefinition('enqueue.transport.default.connection_factory')->getArguments())
         ;
-
-        $this->assertTrue($container->hasAlias('enqueue.transport.connection_factory'));
-        $this->assertEquals(
-            'enqueue.transport.default.connection_factory',
-            (string) $container->getAlias('enqueue.transport.connection_factory')
-        );
     }
 
     public function testShouldCreateContextFromDsn()
@@ -381,44 +357,5 @@ public function testShouldCreateContextFromDsn()
             [],
             $container->getDefinition('enqueue.transport.default.context')->getArguments())
         ;
-
-        $this->assertTrue($container->hasAlias('enqueue.transport.context'));
-        $this->assertEquals(
-            'enqueue.transport.default.context',
-            (string) $container->getAlias('enqueue.transport.context')
-        );
-    }
-
-    public function testShouldCreateDriverFromDsn()
-    {
-        $container = new ContainerBuilder();
-
-        $transport = new TransportFactory('default');
-
-        $serviceId = $transport->createDriver($container, ['dsn' => 'foo://bar/baz', 'foo' => 'fooVal']);
-
-        $this->assertEquals('enqueue.client.default.driver', $serviceId);
-
-        $this->assertTrue($container->hasDefinition('enqueue.client.default.driver'));
-
-        $this->assertNotEmpty($container->getDefinition('enqueue.client.default.driver')->getFactory());
-        $this->assertEquals(
-            [new Reference('enqueue.client.driver_factory'), 'create'],
-            $container->getDefinition('enqueue.client.default.driver')->getFactory())
-        ;
-        $this->assertEquals(
-            [
-                new Reference('enqueue.transport.default.connection_factory'),
-                'foo://bar/baz',
-                ['dsn' => 'foo://bar/baz', 'foo' => 'fooVal'],
-            ],
-            $container->getDefinition('enqueue.client.default.driver')->getArguments())
-        ;
-
-        $this->assertTrue($container->hasAlias('enqueue.client.driver'));
-        $this->assertEquals(
-            'enqueue.client.default.driver',
-            (string) $container->getAlias('enqueue.client.driver')
-        );
     }
 }

From d24772c457b37d905c29063f248e82a0ce29be51 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 28 Sep 2018 16:02:20 +0300
Subject: [PATCH 134/337] [enqueue] Do not use deprecated classes.

---
 Client/ArrayProcessorRegistry.php             |  10 +-
 .../DelayRedeliveredMessageExtension.php      |   2 +-
 .../ExclusiveCommandExtension.php             |   2 +-
 .../SetRouterPropertiesExtension.php          |   2 +-
 Client/DelegateProcessor.php                  |  10 +-
 Client/Driver/AmqpDriver.php                  |  12 +-
 Client/Driver/GenericDriver.php               |  24 +-
 Client/Driver/GpsDriver.php                   |   4 +-
 Client/Driver/RabbitMqStompDriver.php         |  12 +-
 Client/Driver/RdKafkaDriver.php               |   4 +-
 Client/Driver/StompDriver.php                 |   8 +-
 Client/DriverInterface.php                    |  10 +-
 Client/ProcessorRegistryInterface.php         |   4 +-
 Client/RouterProcessor.php                    |  10 +-
 Consumption/CallbackProcessor.php             |  10 +-
 Consumption/Context.php                       |  78 +++---
 Consumption/Extension/LoggerExtension.php     |  10 +-
 Consumption/Extension/ReplyExtension.php      |   8 +-
 Consumption/FallbackSubscriptionConsumer.php  |  12 +-
 Consumption/QueueConsumer.php                 |  68 +++---
 Consumption/QueueConsumerInterface.php        |   8 +-
 Consumption/Result.php                        |  32 +--
 Router/Recipient.php                          |  18 +-
 Router/RecipientListRouterInterface.php       |   6 +-
 Router/RouteRecipientListProcessor.php        |  10 +-
 Rpc/Promise.php                               |  14 +-
 Rpc/RpcClient.php                             |  26 +-
 Rpc/RpcFactory.php                            |   8 +-
 Symfony/Client/ContainerProcessorRegistry.php |   4 +-
 .../ContainerAwareConsumeMessagesCommand.php  |   8 +-
 Symfony/DefaultTransportFactory.php           |   4 +-
 .../DependencyInjection/TransportFactory.php  |   4 +-
 Tests/Client/ArrayProcessorRegistryTest.php   |   6 +-
 .../DelayRedeliveredMessageExtensionTest.php  |  20 +-
 .../ExclusiveCommandExtensionTest.php         |  12 +-
 .../SetRouterPropertiesExtensionTest.php      |  20 +-
 .../SetupBrokerExtensionTest.php              |   6 +-
 Tests/Client/DelegateProcessorTest.php        |  18 +-
 Tests/Client/Driver/AmqpDriverTest.php        |  14 +-
 Tests/Client/Driver/DbalDriverTest.php        |  12 +-
 Tests/Client/Driver/FsDriverTest.php          |  12 +-
 Tests/Client/Driver/GenericDriverTest.php     |  18 +-
 .../Client/Driver/GenericDriverTestsTrait.php |  22 +-
 Tests/Client/Driver/GpsDriverTest.php         |  12 +-
 Tests/Client/Driver/MongodbDriverTest.php     |  12 +-
 Tests/Client/Driver/RabbitMqDriverTest.php    |  14 +-
 .../Client/Driver/RabbitMqStompDriverTest.php |  14 +-
 Tests/Client/Driver/RdKafkaDriverTest.php     |  12 +-
 Tests/Client/Driver/SqsDriverTest.php         |  12 +-
 Tests/Client/Driver/StompDriverTest.php       |  14 +-
 Tests/Client/RouterProcessorTest.php          |   4 +-
 Tests/Consumption/CallbackProcessorTest.php   |   4 +-
 Tests/Consumption/ContextTest.php             | 106 ++++-----
 .../LimitConsumedMessagesExtensionTest.php    |  14 +-
 .../LimitConsumerMemoryExtensionTest.php      |  26 +-
 .../LimitConsumptionTimeExtensionTest.php     |  14 +-
 .../Extension/LoggerExtensionTest.php         |  36 +--
 .../Extension/NicenessExtensionTest.php       |  14 +-
 .../Extension/ReplyExtensionTest.php          |  24 +-
 .../FallbackSubscriptionConsumerTest.php      |  14 +-
 .../Mock/DummySubscriptionConsumer.php        |  12 +-
 Tests/Consumption/QueueConsumerTest.php       | 224 +++++++++---------
 Tests/Router/RecipientTest.php                |  12 +-
 .../RouteRecipientListProcessorTest.php       |  20 +-
 Tests/Rpc/PromiseTest.php                     |   4 +-
 Tests/Rpc/RpcClientTest.php                   |  56 ++---
 .../Client/ConsumeMessagesCommandTest.php     |  14 +-
 .../BuildCommandSubscriberRoutesPassTest.php  |  12 +-
 .../BuildTopicSubscriberRoutesPassTest.php    |  12 +-
 .../ConsumeMessagesCommandTest.php            |   6 +-
 ...ntainerAwareConsumeMessagesCommandTest.php |  14 +-
 .../Mock/QueueSubscriberProcessor.php         |  10 +-
 72 files changed, 666 insertions(+), 678 deletions(-)

diff --git a/Client/ArrayProcessorRegistry.php b/Client/ArrayProcessorRegistry.php
index bbe1cfe..5dede78 100644
--- a/Client/ArrayProcessorRegistry.php
+++ b/Client/ArrayProcessorRegistry.php
@@ -2,29 +2,29 @@
 
 namespace Enqueue\Client;
 
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Processor;
 
 class ArrayProcessorRegistry implements ProcessorRegistryInterface
 {
     /**
-     * @var PsrProcessor[]
+     * @var Processor[]
      */
     private $processors;
 
     /**
-     * @param PsrProcessor[] $processors
+     * @param Processor[] $processors
      */
     public function __construct(array $processors = [])
     {
         $this->processors = $processors;
     }
 
-    public function add(string $name, PsrProcessor $processor): void
+    public function add(string $name, Processor $processor): void
     {
         $this->processors[$name] = $processor;
     }
 
-    public function get(string $processorName): PsrProcessor
+    public function get(string $processorName): Processor
     {
         if (false == isset($this->processors[$processorName])) {
             throw new \LogicException(sprintf('Processor was not found. processorName: "%s"', $processorName));
diff --git a/Client/ConsumptionExtension/DelayRedeliveredMessageExtension.php b/Client/ConsumptionExtension/DelayRedeliveredMessageExtension.php
index 98fa483..0977f95 100644
--- a/Client/ConsumptionExtension/DelayRedeliveredMessageExtension.php
+++ b/Client/ConsumptionExtension/DelayRedeliveredMessageExtension.php
@@ -41,7 +41,7 @@ public function __construct(DriverInterface $driver, $delay)
      */
     public function onPreReceived(Context $context)
     {
-        $message = $context->getPsrMessage();
+        $message = $context->getInteropMessage();
         if (false == $message->isRedelivered()) {
             return;
         }
diff --git a/Client/ConsumptionExtension/ExclusiveCommandExtension.php b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
index 261ac02..cb50ca5 100644
--- a/Client/ConsumptionExtension/ExclusiveCommandExtension.php
+++ b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
@@ -30,7 +30,7 @@ public function __construct(DriverInterface $driver)
 
     public function onPreReceived(Context $context)
     {
-        $message = $context->getPsrMessage();
+        $message = $context->getInteropMessage();
         $queue = $context->getPsrQueue();
 
         if ($message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
diff --git a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
index a87a098..ba7e4ae 100644
--- a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
+++ b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
@@ -27,7 +27,7 @@ public function __construct(DriverInterface $driver)
 
     public function onPreReceived(Context $context)
     {
-        $message = $context->getPsrMessage();
+        $message = $context->getInteropMessage();
         if ($message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
             return;
         }
diff --git a/Client/DelegateProcessor.php b/Client/DelegateProcessor.php
index 6cb8f98..2996684 100644
--- a/Client/DelegateProcessor.php
+++ b/Client/DelegateProcessor.php
@@ -2,11 +2,11 @@
 
 namespace Enqueue\Client;
 
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Processor;
 
-class DelegateProcessor implements PsrProcessor
+class DelegateProcessor implements Processor
 {
     /**
      * @var ProcessorRegistryInterface
@@ -24,7 +24,7 @@ public function __construct(ProcessorRegistryInterface $registry)
     /**
      * {@inheritdoc}
      */
-    public function process(PsrMessage $message, PsrContext $context)
+    public function process(InteropMessage $message, Context $context)
     {
         $processorName = $message->getProperty(Config::PARAMETER_PROCESSOR_NAME);
         if (false == $processorName) {
diff --git a/Client/Driver/AmqpDriver.php b/Client/Driver/AmqpDriver.php
index 0d77d11..80017fc 100644
--- a/Client/Driver/AmqpDriver.php
+++ b/Client/Driver/AmqpDriver.php
@@ -11,9 +11,9 @@
 use Interop\Amqp\AmqpQueue;
 use Interop\Amqp\AmqpTopic;
 use Interop\Amqp\Impl\AmqpBind;
-use Interop\Queue\PsrDestination;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProducer;
+use Interop\Queue\Destination;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
@@ -31,7 +31,7 @@ public function __construct(AmqpContext $context, ...$args)
     /**
      * @return AmqpMessage
      */
-    public function createTransportMessage(Message $clientMessage): PsrMessage
+    public function createTransportMessage(Message $clientMessage): InteropMessage
     {
         /** @var AmqpMessage $transportMessage */
         $transportMessage = parent::createTransportMessage($clientMessage);
@@ -96,7 +96,7 @@ public function setupBroker(LoggerInterface $logger = null): void
     /**
      * @return AmqpTopic
      */
-    protected function createRouterTopic(): PsrDestination
+    protected function createRouterTopic(): Destination
     {
         $topic = $this->doCreateTopic(
             $this->createTransportRouterTopicName($this->getConfig()->getRouterTopicName(), true)
@@ -124,7 +124,7 @@ protected function doCreateQueue(string $transportQueueName): PsrQueue
      * @param AmqpTopic    $topic
      * @param AmqpMessage  $transportMessage
      */
-    protected function doSendToRouter(PsrProducer $producer, PsrDestination $topic, PsrMessage $transportMessage): void
+    protected function doSendToRouter(InteropProducer $producer, Destination $topic, InteropMessage $transportMessage): void
     {
         // We should not handle priority, expiration, and delay at this stage.
         // The router will take care of it while re-sending the message to the final destinations.
diff --git a/Client/Driver/GenericDriver.php b/Client/Driver/GenericDriver.php
index e7b2e93..54ca925 100644
--- a/Client/Driver/GenericDriver.php
+++ b/Client/Driver/GenericDriver.php
@@ -10,10 +10,10 @@
 use Enqueue\Client\MessagePriority;
 use Enqueue\Client\Route;
 use Enqueue\Client\RouteCollection;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrDestination;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProducer;
+use Interop\Queue\Context;
+use Interop\Queue\Destination;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\PsrQueue;
 use Interop\Queue\PsrTopic;
 use Psr\Log\LoggerInterface;
@@ -21,7 +21,7 @@
 class GenericDriver implements DriverInterface
 {
     /**
-     * @var PsrContext
+     * @var Context
      */
     private $context;
 
@@ -36,7 +36,7 @@ class GenericDriver implements DriverInterface
     private $routeCollection;
 
     public function __construct(
-        PsrContext $context,
+        Context $context,
         Config $config,
         RouteCollection $routeCollection
     ) {
@@ -134,7 +134,7 @@ public function createRouteQueue(Route $route): PsrQueue
         return $this->doCreateQueue($transportName);
     }
 
-    public function createTransportMessage(Message $clientMessage): PsrMessage
+    public function createTransportMessage(Message $clientMessage): InteropMessage
     {
         $headers = $clientMessage->getHeaders();
         $properties = $clientMessage->getProperties();
@@ -167,7 +167,7 @@ public function createTransportMessage(Message $clientMessage): PsrMessage
         return $transportMessage;
     }
 
-    public function createClientMessage(PsrMessage $transportMessage): Message
+    public function createClientMessage(InteropMessage $transportMessage): Message
     {
         $clientMessage = new Message();
 
@@ -203,7 +203,7 @@ public function getConfig(): Config
         return $this->config;
     }
 
-    public function getContext(): PsrContext
+    public function getContext(): Context
     {
         return $this->context;
     }
@@ -213,17 +213,17 @@ public function getRouteCollection(): RouteCollection
         return $this->routeCollection;
     }
 
-    protected function doSendToRouter(PsrProducer $producer, PsrDestination $topic, PsrMessage $transportMessage): void
+    protected function doSendToRouter(InteropProducer $producer, Destination $topic, InteropMessage $transportMessage): void
     {
         $producer->send($topic, $transportMessage);
     }
 
-    protected function doSendToProcessor(PsrProducer $producer, PsrQueue $queue, PsrMessage $transportMessage): void
+    protected function doSendToProcessor(InteropProducer $producer, PsrQueue $queue, InteropMessage $transportMessage): void
     {
         $producer->send($queue, $transportMessage);
     }
 
-    protected function createRouterTopic(): PsrDestination
+    protected function createRouterTopic(): Destination
     {
         return $this->createQueue($this->getConfig()->getRouterQueueName());
     }
diff --git a/Client/Driver/GpsDriver.php b/Client/Driver/GpsDriver.php
index 572faac..2a37b72 100644
--- a/Client/Driver/GpsDriver.php
+++ b/Client/Driver/GpsDriver.php
@@ -5,7 +5,7 @@
 use Enqueue\Gps\GpsContext;
 use Enqueue\Gps\GpsQueue;
 use Enqueue\Gps\GpsTopic;
-use Interop\Queue\PsrDestination;
+use Interop\Queue\Destination;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -55,7 +55,7 @@ public function setupBroker(LoggerInterface $logger = null): void
     /**
      * @return GpsTopic
      */
-    protected function createRouterTopic(): PsrDestination
+    protected function createRouterTopic(): Destination
     {
         return $this->doCreateTopic(
             $this->createTransportRouterTopicName($this->getConfig()->getRouterTopicName(), true)
diff --git a/Client/Driver/RabbitMqStompDriver.php b/Client/Driver/RabbitMqStompDriver.php
index ace5209..fc208c2 100644
--- a/Client/Driver/RabbitMqStompDriver.php
+++ b/Client/Driver/RabbitMqStompDriver.php
@@ -9,9 +9,9 @@
 use Enqueue\Stomp\StompDestination;
 use Enqueue\Stomp\StompMessage;
 use Enqueue\Stomp\StompProducer;
-use Interop\Queue\PsrDestination;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProducer;
+use Interop\Queue\Destination;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
@@ -33,7 +33,7 @@ public function __construct(StompContext $context, Config $config, RouteCollecti
     /**
      * @return StompMessage
      */
-    public function createTransportMessage(Message $message): PsrMessage
+    public function createTransportMessage(Message $message): InteropMessage
     {
         $transportMessage = parent::createTransportMessage($message);
 
@@ -151,7 +151,7 @@ protected function doCreateQueue(string $transportQueueName): PsrQueue
      * @param StompDestination $topic
      * @param StompMessage     $transportMessage
      */
-    protected function doSendToRouter(PsrProducer $producer, PsrDestination $topic, PsrMessage $transportMessage): void
+    protected function doSendToRouter(InteropProducer $producer, Destination $topic, InteropMessage $transportMessage): void
     {
         // We should not handle priority, expiration, and delay at this stage.
         // The router will take care of it while re-sending the message to the final destinations.
@@ -167,7 +167,7 @@ protected function doSendToRouter(PsrProducer $producer, PsrDestination $topic,
      * @param StompDestination $destination
      * @param StompMessage     $transportMessage
      */
-    protected function doSendToProcessor(PsrProducer $producer, PsrQueue $destination, PsrMessage $transportMessage): void
+    protected function doSendToProcessor(InteropProducer $producer, PsrQueue $destination, InteropMessage $transportMessage): void
     {
         if ($delay = $transportMessage->getProperty('X-Enqueue-Delay')) {
             $producer->setDeliveryDelay(null);
diff --git a/Client/Driver/RdKafkaDriver.php b/Client/Driver/RdKafkaDriver.php
index f83677d..1a10b22 100644
--- a/Client/Driver/RdKafkaDriver.php
+++ b/Client/Driver/RdKafkaDriver.php
@@ -4,7 +4,7 @@
 
 use Enqueue\RdKafka\RdKafkaContext;
 use Enqueue\RdKafka\RdKafkaTopic;
-use Interop\Queue\PsrDestination;
+use Interop\Queue\Destination;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -48,7 +48,7 @@ public function setupBroker(LoggerInterface $logger = null): void
     /**
      * @return RdKafkaTopic
      */
-    protected function createRouterTopic(): PsrDestination
+    protected function createRouterTopic(): Destination
     {
         return $this->doCreateTopic(
             $this->createTransportRouterTopicName($this->getConfig()->getRouterTopicName(), true)
diff --git a/Client/Driver/StompDriver.php b/Client/Driver/StompDriver.php
index 95cc8b7..0508626 100644
--- a/Client/Driver/StompDriver.php
+++ b/Client/Driver/StompDriver.php
@@ -6,8 +6,8 @@
 use Enqueue\Stomp\StompContext;
 use Enqueue\Stomp\StompDestination;
 use Enqueue\Stomp\StompMessage;
-use Interop\Queue\PsrDestination;
-use Interop\Queue\PsrMessage;
+use Interop\Queue\Destination;
+use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
@@ -31,7 +31,7 @@ public function setupBroker(LoggerInterface $logger = null): void
     /**
      * @return StompMessage
      */
-    public function createTransportMessage(Message $message): PsrMessage
+    public function createTransportMessage(Message $message): InteropMessage
     {
         /** @var StompMessage $transportMessage */
         $transportMessage = parent::createTransportMessage($message);
@@ -57,7 +57,7 @@ protected function doCreateQueue(string $transportQueueName): PsrQueue
     /**
      * @return StompDestination
      */
-    protected function createRouterTopic(): PsrDestination
+    protected function createRouterTopic(): Destination
     {
         /** @var StompDestination $topic */
         $topic = $this->doCreateTopic(
diff --git a/Client/DriverInterface.php b/Client/DriverInterface.php
index 23d0661..5955542 100644
--- a/Client/DriverInterface.php
+++ b/Client/DriverInterface.php
@@ -4,16 +4,16 @@
 
 namespace Enqueue\Client;
 
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 
 interface DriverInterface
 {
-    public function createTransportMessage(Message $message): PsrMessage;
+    public function createTransportMessage(Message $message): InteropMessage;
 
-    public function createClientMessage(PsrMessage $message): Message;
+    public function createClientMessage(InteropMessage $message): Message;
 
     public function sendToRouter(Message $message): void;
 
@@ -31,7 +31,7 @@ public function setupBroker(LoggerInterface $logger = null): void;
 
     public function getConfig(): Config;
 
-    public function getContext(): PsrContext;
+    public function getContext(): Context;
 
     public function getRouteCollection(): RouteCollection;
 }
diff --git a/Client/ProcessorRegistryInterface.php b/Client/ProcessorRegistryInterface.php
index f8894a9..2639e5e 100644
--- a/Client/ProcessorRegistryInterface.php
+++ b/Client/ProcessorRegistryInterface.php
@@ -2,9 +2,9 @@
 
 namespace Enqueue\Client;
 
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Processor;
 
 interface ProcessorRegistryInterface
 {
-    public function get(string $processorName): PsrProcessor;
+    public function get(string $processorName): Processor;
 }
diff --git a/Client/RouterProcessor.php b/Client/RouterProcessor.php
index 7918316..d106e29 100644
--- a/Client/RouterProcessor.php
+++ b/Client/RouterProcessor.php
@@ -3,11 +3,11 @@
 namespace Enqueue\Client;
 
 use Enqueue\Consumption\Result;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Processor;
 
-final class RouterProcessor implements PsrProcessor
+final class RouterProcessor implements Processor
 {
     /**
      * @var DriverInterface
@@ -19,7 +19,7 @@ public function __construct(DriverInterface $driver)
         $this->driver = $driver;
     }
 
-    public function process(PsrMessage $message, PsrContext $context): Result
+    public function process(InteropMessage $message, Context $context): Result
     {
         if ($message->getProperty(Config::PARAMETER_COMMAND_NAME)) {
             return Result::reject(sprintf(
diff --git a/Consumption/CallbackProcessor.php b/Consumption/CallbackProcessor.php
index b002235..988b765 100644
--- a/Consumption/CallbackProcessor.php
+++ b/Consumption/CallbackProcessor.php
@@ -2,11 +2,11 @@
 
 namespace Enqueue\Consumption;
 
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Processor;
 
-class CallbackProcessor implements PsrProcessor
+class CallbackProcessor implements Processor
 {
     /**
      * @var callable
@@ -24,7 +24,7 @@ public function __construct(callable $callback)
     /**
      * {@inheritdoc}
      */
-    public function process(PsrMessage $message, PsrContext $context)
+    public function process(InteropMessage $message, Context $context)
     {
         return call_user_func($this->callback, $message, $context);
     }
diff --git a/Consumption/Context.php b/Consumption/Context.php
index 0625e60..96fc9e8 100644
--- a/Consumption/Context.php
+++ b/Consumption/Context.php
@@ -3,29 +3,29 @@
 namespace Enqueue\Consumption;
 
 use Enqueue\Consumption\Exception\IllegalContextModificationException;
-use Interop\Queue\PsrConsumer;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Consumer;
+use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Processor;
 use Interop\Queue\PsrQueue;
 use Psr\Log\LoggerInterface;
 
 class Context
 {
     /**
-     * @var PsrContext
+     * @var InteropContext
      */
-    private $psrContext;
+    private $context;
 
     /**
-     * @var PsrConsumer
+     * @var Consumer
      */
-    private $psrConsumer;
+    private $consumer;
 
     /**
-     * @var PsrProcessor
+     * @var Processor
      */
-    private $psrProcessor;
+    private $processor;
 
     /**
      * @var LoggerInterface
@@ -33,9 +33,9 @@ class Context
     private $logger;
 
     /**
-     * @var PsrMessage
+     * @var InteropMessage
      */
-    private $psrMessage;
+    private $interopMessage;
 
     /**
      * @var \Exception
@@ -58,81 +58,81 @@ class Context
     private $executionInterrupted;
 
     /**
-     * @param PsrContext $psrContext
+     * @param InteropContext $interopContext
      */
-    public function __construct(PsrContext $psrContext)
+    public function __construct(InteropContext $interopContext)
     {
-        $this->psrContext = $psrContext;
+        $this->context = $interopContext;
 
         $this->executionInterrupted = false;
     }
 
     /**
-     * @return PsrMessage
+     * @return InteropMessage
      */
-    public function getPsrMessage()
+    public function getInteropMessage()
     {
-        return $this->psrMessage;
+        return $this->interopMessage;
     }
 
     /**
-     * @param PsrMessage $psrMessage
+     * @param InteropMessage $interopMessage
      */
-    public function setPsrMessage(PsrMessage $psrMessage)
+    public function setInteropMessage(InteropMessage $interopMessage)
     {
-        if ($this->psrMessage) {
+        if ($this->interopMessage) {
             throw new IllegalContextModificationException('The message could be set once');
         }
 
-        $this->psrMessage = $psrMessage;
+        $this->interopMessage = $interopMessage;
     }
 
     /**
-     * @return PsrContext
+     * @return InteropContext
      */
-    public function getPsrContext()
+    public function getContext()
     {
-        return $this->psrContext;
+        return $this->context;
     }
 
     /**
-     * @return PsrConsumer
+     * @return Consumer
      */
-    public function getPsrConsumer()
+    public function getConsumer()
     {
-        return $this->psrConsumer;
+        return $this->consumer;
     }
 
     /**
-     * @param PsrConsumer $psrConsumer
+     * @param Consumer $consumer
      */
-    public function setPsrConsumer(PsrConsumer $psrConsumer)
+    public function setConsumer(Consumer $consumer)
     {
-        if ($this->psrConsumer) {
+        if ($this->consumer) {
             throw new IllegalContextModificationException('The message consumer could be set once');
         }
 
-        $this->psrConsumer = $psrConsumer;
+        $this->consumer = $consumer;
     }
 
     /**
-     * @return PsrProcessor
+     * @return Processor
      */
-    public function getPsrProcessor()
+    public function getProcessor()
     {
-        return $this->psrProcessor;
+        return $this->processor;
     }
 
     /**
-     * @param PsrProcessor $psrProcessor
+     * @param Processor $processor
      */
-    public function setPsrProcessor(PsrProcessor $psrProcessor)
+    public function setProcessor(Processor $processor)
     {
-        if ($this->psrProcessor) {
+        if ($this->processor) {
             throw new IllegalContextModificationException('The message processor could be set once');
         }
 
-        $this->psrProcessor = $psrProcessor;
+        $this->processor = $processor;
     }
 
     /**
diff --git a/Consumption/Extension/LoggerExtension.php b/Consumption/Extension/LoggerExtension.php
index 0779c90..b498e21 100644
--- a/Consumption/Extension/LoggerExtension.php
+++ b/Consumption/Extension/LoggerExtension.php
@@ -6,7 +6,7 @@
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Consumption\Result;
-use Interop\Queue\PsrMessage;
+use Interop\Queue\Message as InteropMessage;
 use Psr\Log\LoggerInterface;
 
 class LoggerExtension implements ExtensionInterface
@@ -59,13 +59,13 @@ public function onPostReceived(Context $context)
             case Result::REJECT:
             case Result::REQUEUE:
                 if ($result->getReason()) {
-                    $this->logger->error($result->getReason(), $this->messageToLogContext($context->getPsrMessage()));
+                    $this->logger->error($result->getReason(), $this->messageToLogContext($context->getInteropMessage()));
                 }
 
                 break;
             case Result::ACK:
                 if ($result->getReason()) {
-                    $this->logger->info($result->getReason(), $this->messageToLogContext($context->getPsrMessage()));
+                    $this->logger->info($result->getReason(), $this->messageToLogContext($context->getInteropMessage()));
                 }
 
                 break;
@@ -75,11 +75,11 @@ public function onPostReceived(Context $context)
     }
 
     /**
-     * @param PsrMessage $message
+     * @param InteropMessage $message
      *
      * @return array
      */
-    private function messageToLogContext(PsrMessage $message)
+    private function messageToLogContext(InteropMessage $message)
     {
         return [
             'body' => $message->getBody(),
diff --git a/Consumption/Extension/ReplyExtension.php b/Consumption/Extension/ReplyExtension.php
index 0d7a76e..59d0a43 100644
--- a/Consumption/Extension/ReplyExtension.php
+++ b/Consumption/Extension/ReplyExtension.php
@@ -16,7 +16,7 @@ class ReplyExtension implements ExtensionInterface
      */
     public function onPostReceived(Context $context)
     {
-        $replyTo = $context->getPsrMessage()->getReplyTo();
+        $replyTo = $context->getInteropMessage()->getReplyTo();
         if (false == $replyTo) {
             return;
         }
@@ -31,13 +31,13 @@ public function onPostReceived(Context $context)
             return;
         }
 
-        $correlationId = $context->getPsrMessage()->getCorrelationId();
+        $correlationId = $context->getInteropMessage()->getCorrelationId();
         $replyMessage = clone $result->getReply();
         $replyMessage->setCorrelationId($correlationId);
 
-        $replyQueue = $context->getPsrContext()->createQueue($replyTo);
+        $replyQueue = $context->getContext()->createQueue($replyTo);
 
         $context->getLogger()->debug(sprintf('[ReplyExtension] Send reply to "%s"', $replyTo));
-        $context->getPsrContext()->createProducer()->send($replyQueue, $replyMessage);
+        $context->getContext()->createProducer()->send($replyQueue, $replyMessage);
     }
 }
diff --git a/Consumption/FallbackSubscriptionConsumer.php b/Consumption/FallbackSubscriptionConsumer.php
index e40d8bf..baee1f7 100644
--- a/Consumption/FallbackSubscriptionConsumer.php
+++ b/Consumption/FallbackSubscriptionConsumer.php
@@ -2,13 +2,13 @@
 
 namespace Enqueue\Consumption;
 
-use Interop\Queue\PsrConsumer;
+use Interop\Queue\Consumer;
 use Interop\Queue\PsrSubscriptionConsumer;
 
 final class FallbackSubscriptionConsumer implements PsrSubscriptionConsumer
 {
     /**
-     * an item contains an array: [PsrConsumer $consumer, callable $callback];.
+     * an item contains an array: [Consumer $consumer, callable $callback];.
      * an item key is a queue name.
      *
      * @var array
@@ -37,8 +37,8 @@ public function consume(int $timeout = 0): void
         while (true) {
             /**
              * @var string
-             * @var PsrConsumer $consumer
-             * @var callable    $processor
+             * @var Consumer $consumer
+             * @var callable $processor
              */
             foreach ($this->subscribers as $queueName => list($consumer, $callback)) {
                 $message = $consumer->receiveNoWait();
@@ -62,7 +62,7 @@ public function consume(int $timeout = 0): void
         }
     }
 
-    public function subscribe(PsrConsumer $consumer, callable $callback): void
+    public function subscribe(Consumer $consumer, callable $callback): void
     {
         $queueName = $consumer->getQueue()->getQueueName();
         if (array_key_exists($queueName, $this->subscribers)) {
@@ -76,7 +76,7 @@ public function subscribe(PsrConsumer $consumer, callable $callback): void
         $this->subscribers[$queueName] = [$consumer, $callback];
     }
 
-    public function unsubscribe(PsrConsumer $consumer): void
+    public function unsubscribe(Consumer $consumer): void
     {
         if (false == array_key_exists($consumer->getQueue()->getQueueName(), $this->subscribers)) {
             return;
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 5277be5..6504635 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -6,22 +6,22 @@
 use Enqueue\Consumption\Exception\InvalidArgumentException;
 use Enqueue\Consumption\Exception\LogicException;
 use Enqueue\Util\VarExport;
-use Interop\Queue\PsrConsumer;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Consumer;
+use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Exception\SubscriptionConsumerNotSupportedException;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Processor;
 use Interop\Queue\PsrQueue;
 use Interop\Queue\PsrSubscriptionConsumer;
-use Interop\Queue\SubscriptionConsumerNotSupportedException;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
 final class QueueConsumer implements QueueConsumerInterface
 {
     /**
-     * @var PsrContext
+     * @var InteropContext
      */
-    private $psrContext;
+    private $interopContext;
 
     /**
      * @var ExtensionInterface|ChainExtension
@@ -30,7 +30,7 @@ final class QueueConsumer implements QueueConsumerInterface
 
     /**
      * [
-     *   [PsrQueue, PsrProcessor],
+     *   [PsrQueue, Processor],
      * ].
      *
      * @var array
@@ -63,18 +63,18 @@ final class QueueConsumer implements QueueConsumerInterface
     private $fallbackSubscriptionConsumer;
 
     /**
-     * @param PsrContext                             $psrContext
+     * @param InteropContext                         $interopContext
      * @param ExtensionInterface|ChainExtension|null $extension
      * @param int|float                              $idleTimeout    the time in milliseconds queue consumer waits if no message received
      * @param int|float                              $receiveTimeout the time in milliseconds queue consumer waits for a message (10 ms by default)
      */
     public function __construct(
-        PsrContext $psrContext,
+        InteropContext $interopContext,
         ExtensionInterface $extension = null,
         float $idleTimeout = 0.,
         float $receiveTimeout = 10000.
     ) {
-        $this->psrContext = $psrContext;
+        $this->interopContext = $interopContext;
         $this->staticExtension = $extension ?: new ChainExtension([]);
         $this->idleTimeout = $idleTimeout;
         $this->receiveTimeout = $receiveTimeout;
@@ -100,37 +100,25 @@ public function getIdleTimeout(): float
         return $this->idleTimeout;
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function setReceiveTimeout(float $timeout): void
     {
         $this->receiveTimeout = $timeout;
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function getReceiveTimeout(): float
     {
         return $this->receiveTimeout;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getPsrContext(): PsrContext
+    public function getContext(): InteropContext
     {
-        return $this->psrContext;
+        return $this->interopContext;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function bind($queue, PsrProcessor $processor): QueueConsumerInterface
+    public function bind($queue, Processor $processor): QueueConsumerInterface
     {
         if (is_string($queue)) {
-            $queue = $this->psrContext->createQueue($queue);
+            $queue = $this->interopContext->createQueue($queue);
         }
 
         InvalidArgumentException::assertInstanceOf($queue, PsrQueue::class);
@@ -164,11 +152,11 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
             throw new \LogicException('There is nothing to consume. It is required to bind something before calling consume method.');
         }
 
-        /** @var PsrConsumer[] $consumers */
+        /** @var Consumer[] $consumers */
         $consumers = [];
         /** @var PsrQueue $queue */
         foreach ($this->boundProcessors as list($queue, $processor)) {
-            $consumers[$queue->getQueueName()] = $this->psrContext->createConsumer($queue);
+            $consumers[$queue->getQueueName()] = $this->interopContext->createConsumer($queue);
         }
 
         $this->extension = $runtimeExtension ?
@@ -176,7 +164,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
             $this->staticExtension
         ;
 
-        $context = new Context($this->psrContext);
+        $context = new Context($this->interopContext);
         $this->extension->onStart($context);
 
         if ($context->getLogger()) {
@@ -189,12 +177,12 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
         $this->logger->info('Start consuming');
 
         try {
-            $subscriptionConsumer = $this->psrContext->createSubscriptionConsumer();
+            $subscriptionConsumer = $this->interopContext->createSubscriptionConsumer();
         } catch (SubscriptionConsumerNotSupportedException $e) {
             $subscriptionConsumer = $this->fallbackSubscriptionConsumer;
         }
 
-        $callback = function (PsrMessage $message, PsrConsumer $consumer) use (&$context) {
+        $callback = function (InteropMessage $message, Consumer $consumer) use (&$context) {
             $currentProcessor = null;
 
             /** @var PsrQueue $queue */
@@ -208,12 +196,12 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
                 throw new \LogicException(sprintf('The processor for the queue "%s" could not be found.', $consumer->getQueue()->getQueueName()));
             }
 
-            $context = new Context($this->psrContext);
+            $context = new Context($this->interopContext);
             $context->setLogger($this->logger);
             $context->setPsrQueue($consumer->getQueue());
-            $context->setPsrConsumer($consumer);
-            $context->setPsrProcessor($currentProcessor);
-            $context->setPsrMessage($message);
+            $context->setConsumer($consumer);
+            $context->setProcessor($currentProcessor);
+            $context->setInteropMessage($message);
 
             $this->processMessage($consumer, $currentProcessor, $message, $context);
 
@@ -225,7 +213,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
         };
 
         foreach ($consumers as $consumer) {
-            /* @var PsrConsumer $consumer */
+            /* @var Consumer $consumer */
 
             $subscriptionConsumer->subscribe($consumer, $callback);
         }
@@ -250,7 +238,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
                 $this->logger->info(sprintf('Consuming interrupted'));
 
                 foreach ($consumers as $consumer) {
-                    /* @var PsrConsumer $consumer */
+                    /* @var Consumer $consumer */
 
                     $subscriptionConsumer->unsubscribe($consumer);
                 }
@@ -323,7 +311,7 @@ private function onInterruptionByException(ExtensionInterface $extension, Contex
         throw $exception;
     }
 
-    private function processMessage(PsrConsumer $consumer, PsrProcessor $processor, PsrMessage $message, Context $context)
+    private function processMessage(Consumer $consumer, Processor $processor, InteropMessage $message, Context $context)
     {
         $this->logger->info('Message received from the queue: '.$context->getPsrQueue()->getQueueName());
         $this->logger->debug('Headers: {headers}', ['headers' => new VarExport($message->getHeaders())]);
@@ -332,7 +320,7 @@ private function processMessage(PsrConsumer $consumer, PsrProcessor $processor,
 
         $this->extension->onPreReceived($context);
         if (!$context->getResult()) {
-            $result = $processor->process($message, $context->getPsrContext());
+            $result = $processor->process($message, $context->getContext());
             $context->setResult($result);
         }
 
diff --git a/Consumption/QueueConsumerInterface.php b/Consumption/QueueConsumerInterface.php
index 0add8ab..a3bbb47 100644
--- a/Consumption/QueueConsumerInterface.php
+++ b/Consumption/QueueConsumerInterface.php
@@ -2,8 +2,8 @@
 
 namespace Enqueue\Consumption;
 
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Context;
+use Interop\Queue\Processor;
 use Interop\Queue\PsrQueue;
 
 interface QueueConsumerInterface
@@ -22,12 +22,12 @@ public function setReceiveTimeout(float $timeout): void;
 
     public function getReceiveTimeout(): float;
 
-    public function getPsrContext(): PsrContext;
+    public function getContext(): Context;
 
     /**
      * @param string|PsrQueue $queueName
      */
-    public function bind($queueName, PsrProcessor $processor): self;
+    public function bind($queueName, Processor $processor): self;
 
     /**
      * @param string|PsrQueue $queueName
diff --git a/Consumption/Result.php b/Consumption/Result.php
index b78f997..926f256 100644
--- a/Consumption/Result.php
+++ b/Consumption/Result.php
@@ -2,25 +2,25 @@
 
 namespace Enqueue\Consumption;
 
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Processor;
 
 class Result
 {
     /**
-     * @see PsrProcessor::ACK for more details
+     * @see Processor::ACK for more details
      */
-    const ACK = PsrProcessor::ACK;
+    const ACK = Processor::ACK;
 
     /**
-     * @see PsrProcessor::ACK for more details
+     * @see Processor::ACK for more details
      */
-    const REJECT = PsrProcessor::REJECT;
+    const REJECT = Processor::REJECT;
 
     /**
-     * @see PsrProcessor::ACK for more details
+     * @see Processor::ACK for more details
      */
-    const REQUEUE = PsrProcessor::REQUEUE;
+    const REQUEUE = Processor::REQUEUE;
 
     /**
      * @var string
@@ -33,7 +33,7 @@ class Result
     private $reason;
 
     /**
-     * @var PsrMessage|null
+     * @var InteropMessage|null
      */
     private $reply;
 
@@ -72,7 +72,7 @@ public function getReason()
     }
 
     /**
-     * @return PsrMessage|null
+     * @return InteropMessage|null
      */
     public function getReply()
     {
@@ -80,9 +80,9 @@ public function getReply()
     }
 
     /**
-     * @param PsrMessage|null $reply
+     * @param InteropMessage|null $reply
      */
-    public function setReply(PsrMessage $reply = null)
+    public function setReply(InteropMessage $reply = null)
     {
         $this->reply = $reply;
     }
@@ -118,13 +118,13 @@ public static function requeue($reason = '')
     }
 
     /**
-     * @param PsrMessage  $replyMessage
-     * @param string      $status
-     * @param string|null $reason
+     * @param InteropMessage $replyMessage
+     * @param string         $status
+     * @param string|null    $reason
      *
      * @return static
      */
-    public static function reply(PsrMessage $replyMessage, $status = self::ACK, $reason = null)
+    public static function reply(InteropMessage $replyMessage, $status = self::ACK, $reason = null)
     {
         $status = null === $status ? self::ACK : $status;
 
diff --git a/Router/Recipient.php b/Router/Recipient.php
index dc0ab42..c7eac6a 100644
--- a/Router/Recipient.php
+++ b/Router/Recipient.php
@@ -2,33 +2,33 @@
 
 namespace Enqueue\Router;
 
-use Interop\Queue\PsrDestination;
-use Interop\Queue\PsrMessage;
+use Interop\Queue\Destination;
+use Interop\Queue\Message as InteropMessage;
 
 class Recipient
 {
     /**
-     * @var PsrDestination
+     * @var Destination
      */
     private $destination;
 
     /**
-     * @var PsrMessage
+     * @var InteropMessage
      */
     private $message;
 
     /**
-     * @param PsrDestination $destination
-     * @param PsrMessage     $message
+     * @param Destination    $destination
+     * @param InteropMessage $message
      */
-    public function __construct(PsrDestination $destination, PsrMessage $message)
+    public function __construct(Destination $destination, InteropMessage $message)
     {
         $this->destination = $destination;
         $this->message = $message;
     }
 
     /**
-     * @return PsrDestination
+     * @return Destination
      */
     public function getDestination()
     {
@@ -36,7 +36,7 @@ public function getDestination()
     }
 
     /**
-     * @return PsrMessage
+     * @return InteropMessage
      */
     public function getMessage()
     {
diff --git a/Router/RecipientListRouterInterface.php b/Router/RecipientListRouterInterface.php
index 78a354c..d00dd2a 100644
--- a/Router/RecipientListRouterInterface.php
+++ b/Router/RecipientListRouterInterface.php
@@ -2,14 +2,14 @@
 
 namespace Enqueue\Router;
 
-use Interop\Queue\PsrMessage;
+use Interop\Queue\Message as InteropMessage;
 
 interface RecipientListRouterInterface
 {
     /**
-     * @param PsrMessage $message
+     * @param InteropMessage $message
      *
      * @return \Traversable|Recipient[]
      */
-    public function route(PsrMessage $message);
+    public function route(InteropMessage $message);
 }
diff --git a/Router/RouteRecipientListProcessor.php b/Router/RouteRecipientListProcessor.php
index 59df355..22e32e2 100644
--- a/Router/RouteRecipientListProcessor.php
+++ b/Router/RouteRecipientListProcessor.php
@@ -2,11 +2,11 @@
 
 namespace Enqueue\Router;
 
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Processor;
 
-class RouteRecipientListProcessor implements PsrProcessor
+class RouteRecipientListProcessor implements Processor
 {
     /**
      * @var RecipientListRouterInterface
@@ -24,7 +24,7 @@ public function __construct(RecipientListRouterInterface $router)
     /**
      * {@inheritdoc}
      */
-    public function process(PsrMessage $message, PsrContext $context)
+    public function process(InteropMessage $message, Context $context)
     {
         $producer = $context->createProducer();
         foreach ($this->router->route($message) as $recipient) {
diff --git a/Rpc/Promise.php b/Rpc/Promise.php
index 91e0aaa..0a5d28f 100644
--- a/Rpc/Promise.php
+++ b/Rpc/Promise.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Rpc;
 
-use Interop\Queue\PsrMessage;
+use Interop\Queue\Message as InteropMessage;
 
 class Promise
 {
@@ -27,7 +27,7 @@ class Promise
     private $deleteReplyQueue;
 
     /**
-     * @var PsrMessage
+     * @var InteropMessage
      */
     private $message;
 
@@ -52,7 +52,7 @@ public function __construct(\Closure $receiveCallback, \Closure $receiveNoWaitCa
      *
      * @throws TimeoutException if the wait timeout is reached
      *
-     * @return PsrMessage
+     * @return InteropMessage
      */
     public function receive($timeout = null)
     {
@@ -72,7 +72,7 @@ public function receive($timeout = null)
     /**
      * Non blocking function. Returns message or null.
      *
-     * @return PsrMessage|null
+     * @return InteropMessage|null
      */
     public function receiveNoWait()
     {
@@ -109,15 +109,15 @@ public function isDeleteReplyQueue()
      * @param \Closure $cb
      * @param array    $args
      *
-     * @return PsrMessage
+     * @return InteropMessage
      */
     private function doReceive(\Closure $cb, ...$args)
     {
         $message = call_user_func_array($cb, $args);
 
-        if (null !== $message && false == $message instanceof PsrMessage) {
+        if (null !== $message && false == $message instanceof InteropMessage) {
             throw new \RuntimeException(sprintf(
-                'Expected "%s" but got: "%s"', PsrMessage::class, is_object($message) ? get_class($message) : gettype($message)));
+                'Expected "%s" but got: "%s"', InteropMessage::class, is_object($message) ? get_class($message) : gettype($message)));
         }
 
         return $message;
diff --git a/Rpc/RpcClient.php b/Rpc/RpcClient.php
index d429ac9..591a4cc 100644
--- a/Rpc/RpcClient.php
+++ b/Rpc/RpcClient.php
@@ -3,14 +3,14 @@
 namespace Enqueue\Rpc;
 
 use Enqueue\Util\UUID;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrDestination;
-use Interop\Queue\PsrMessage;
+use Interop\Queue\Context;
+use Interop\Queue\Destination;
+use Interop\Queue\Message as InteropMessage;
 
 class RpcClient
 {
     /**
-     * @var PsrContext
+     * @var Context
      */
     private $context;
 
@@ -20,37 +20,37 @@ class RpcClient
     private $rpcFactory;
 
     /**
-     * @param PsrContext $context
+     * @param Context    $context
      * @param RpcFactory $promiseFactory
      */
-    public function __construct(PsrContext $context, RpcFactory $promiseFactory = null)
+    public function __construct(Context $context, RpcFactory $promiseFactory = null)
     {
         $this->context = $context;
         $this->rpcFactory = $promiseFactory ?: new RpcFactory($context);
     }
 
     /**
-     * @param PsrDestination $destination
-     * @param PsrMessage     $message
+     * @param Destination    $destination
+     * @param InteropMessage $message
      * @param int            $timeout
      *
      * @throws TimeoutException if the wait timeout is reached
      *
-     * @return PsrMessage
+     * @return InteropMessage
      */
-    public function call(PsrDestination $destination, PsrMessage $message, $timeout)
+    public function call(Destination $destination, InteropMessage $message, $timeout)
     {
         return $this->callAsync($destination, $message, $timeout)->receive();
     }
 
     /**
-     * @param PsrDestination $destination
-     * @param PsrMessage     $message
+     * @param Destination    $destination
+     * @param InteropMessage $message
      * @param int            $timeout
      *
      * @return Promise
      */
-    public function callAsync(PsrDestination $destination, PsrMessage $message, $timeout)
+    public function callAsync(Destination $destination, InteropMessage $message, $timeout)
     {
         if ($timeout < 1) {
             throw new \InvalidArgumentException(sprintf('Timeout must be positive not zero integer. Got %s', $timeout));
diff --git a/Rpc/RpcFactory.php b/Rpc/RpcFactory.php
index 195fd95..bd054b1 100644
--- a/Rpc/RpcFactory.php
+++ b/Rpc/RpcFactory.php
@@ -2,19 +2,19 @@
 
 namespace Enqueue\Rpc;
 
-use Interop\Queue\PsrContext;
+use Interop\Queue\Context;
 
 class RpcFactory
 {
     /**
-     * @var PsrContext
+     * @var Context
      */
     private $context;
 
     /**
-     * @param PsrContext $context
+     * @param Context $context
      */
-    public function __construct(PsrContext $context)
+    public function __construct(Context $context)
     {
         $this->context = $context;
     }
diff --git a/Symfony/Client/ContainerProcessorRegistry.php b/Symfony/Client/ContainerProcessorRegistry.php
index c2d06c8..4031e42 100644
--- a/Symfony/Client/ContainerProcessorRegistry.php
+++ b/Symfony/Client/ContainerProcessorRegistry.php
@@ -3,7 +3,7 @@
 namespace Enqueue\Symfony\Client;
 
 use Enqueue\Client\ProcessorRegistryInterface;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Processor;
 use Psr\Container\ContainerInterface;
 
 final class ContainerProcessorRegistry implements ProcessorRegistryInterface
@@ -18,7 +18,7 @@ public function __construct(ContainerInterface $locator)
         $this->locator = $locator;
     }
 
-    public function get(string $processorName): PsrProcessor
+    public function get(string $processorName): Processor
     {
         if (false == $this->locator->has($processorName)) {
             throw new \LogicException(sprintf('Service locator does not have a processor with name "%s".', $processorName));
diff --git a/Symfony/Consumption/ContainerAwareConsumeMessagesCommand.php b/Symfony/Consumption/ContainerAwareConsumeMessagesCommand.php
index 8f2695d..e7607f6 100644
--- a/Symfony/Consumption/ContainerAwareConsumeMessagesCommand.php
+++ b/Symfony/Consumption/ContainerAwareConsumeMessagesCommand.php
@@ -6,7 +6,7 @@
 use Enqueue\Consumption\Extension\LoggerExtension;
 use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Consumption\QueueSubscriberInterface;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Processor;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputArgument;
 use Symfony\Component\Console\Input\InputInterface;
@@ -65,12 +65,12 @@ protected function execute(InputInterface $input, OutputInterface $output)
     {
         $this->setQueueConsumerOptions($this->consumer, $input);
 
-        /** @var PsrProcessor $processor */
+        /** @var Processor $processor */
         $processor = $this->container->get($input->getArgument('processor-service'));
-        if (false == $processor instanceof  PsrProcessor) {
+        if (false == $processor instanceof  Processor) {
             throw new \LogicException(sprintf(
                 'Invalid message processor service given. It must be an instance of %s but %s',
-                PsrProcessor::class,
+                Processor::class,
                 get_class($processor)
             ));
         }
diff --git a/Symfony/DefaultTransportFactory.php b/Symfony/DefaultTransportFactory.php
index 3076697..9f96f65 100644
--- a/Symfony/DefaultTransportFactory.php
+++ b/Symfony/DefaultTransportFactory.php
@@ -3,8 +3,8 @@
 namespace Enqueue\Symfony;
 
 use Enqueue\Client\DriverInterface;
+use Interop\Queue\Context;
 use Interop\Queue\PsrConnectionFactory;
-use Interop\Queue\PsrContext;
 use Symfony\Component\Config\Definition\Builder\VariableNodeDefinition;
 use Symfony\Component\DependencyInjection\Alias;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -60,7 +60,7 @@ public function createContext(ContainerBuilder $container, array $config): strin
         $contextId = sprintf('enqueue.transport.%s.context', $this->getName());
         $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
 
-        $container->register($contextId, PsrContext::class)
+        $container->register($contextId, Context::class)
             ->setFactory([new Reference($factoryId), 'createContext'])
         ;
 
diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index f54a34f..d50c94a 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -5,8 +5,8 @@
 use Enqueue\ConnectionFactoryFactory;
 use Enqueue\ConnectionFactoryFactoryInterface;
 use Enqueue\Resources;
+use Interop\Queue\Context;
 use Interop\Queue\PsrConnectionFactory;
-use Interop\Queue\PsrContext;
 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Reference;
@@ -121,7 +121,7 @@ public function createContext(ContainerBuilder $container, array $config): strin
         $contextId = sprintf('enqueue.transport.%s.context', $this->getName());
         $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
 
-        $container->register($contextId, PsrContext::class)
+        $container->register($contextId, Context::class)
             ->setFactory([new Reference($factoryId), 'createContext'])
         ;
 
diff --git a/Tests/Client/ArrayProcessorRegistryTest.php b/Tests/Client/ArrayProcessorRegistryTest.php
index eb5d684..f49ec8f 100644
--- a/Tests/Client/ArrayProcessorRegistryTest.php
+++ b/Tests/Client/ArrayProcessorRegistryTest.php
@@ -5,7 +5,7 @@
 use Enqueue\Client\ArrayProcessorRegistry;
 use Enqueue\Client\ProcessorRegistryInterface;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
 
 class ArrayProcessorRegistryTest extends TestCase
@@ -51,10 +51,10 @@ public function testShouldAllowGetProcessorAddedViaAddMethod()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProcessor
+     * @return \PHPUnit_Framework_MockObject_MockObject|Processor
      */
     protected function createProcessorMock()
     {
-        return $this->createMock(PsrProcessor::class);
+        return $this->createMock(Processor::class);
     }
 }
diff --git a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
index b61a341..698b821 100644
--- a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
@@ -9,7 +9,7 @@
 use Enqueue\Consumption\Result;
 use Enqueue\Null\NullMessage;
 use Enqueue\Null\NullQueue;
-use Interop\Queue\PsrContext;
+use Interop\Queue\Context as InteropContext;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 
@@ -61,9 +61,9 @@ public function testShouldSendDelayedMessageAndRejectOriginalMessage()
             )
         ;
 
-        $context = new Context($this->createPsrContextMock());
+        $context = new Context($this->createContextMock());
         $context->setPsrQueue($queue);
-        $context->setPsrMessage($originMessage);
+        $context->setInteropMessage($originMessage);
         $context->setLogger($logger);
 
         $this->assertNull($context->getResult());
@@ -92,8 +92,8 @@ public function testShouldDoNothingIfMessageIsNotRedelivered()
             ->method('sendToProcessor')
         ;
 
-        $context = new Context($this->createPsrContextMock());
-        $context->setPsrMessage($message);
+        $context = new Context($this->createContextMock());
+        $context->setInteropMessage($message);
 
         $extension = new DelayRedeliveredMessageExtension($driver, 12345);
         $extension->onPreReceived($context);
@@ -112,8 +112,8 @@ public function testShouldDoNothingIfMessageIsRedeliveredButResultWasAlreadySetO
             ->method('sendToProcessor')
         ;
 
-        $context = new Context($this->createPsrContextMock());
-        $context->setPsrMessage($message);
+        $context = new Context($this->createContextMock());
+        $context->setInteropMessage($message);
         $context->setResult('aStatus');
 
         $extension = new DelayRedeliveredMessageExtension($driver, 12345);
@@ -129,11 +129,11 @@ private function createDriverMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext
+     * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
      */
-    private function createPsrContextMock()
+    private function createContextMock(): InteropContext
     {
-        return $this->createMock(PsrContext::class);
+        return $this->createMock(InteropContext::class);
     }
 
     /**
diff --git a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
index d858c91..356bd95 100644
--- a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
@@ -41,7 +41,7 @@ public function testShouldDoNothingIfMessageHasTopicPropertySetOnPreReceive()
         $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'aTopic');
 
         $context = new Context(new NullContext());
-        $context->setPsrMessage($message);
+        $context->setInteropMessage($message);
 
         $driver = $this->createDriverStub();
         $driver
@@ -66,7 +66,7 @@ public function testShouldDoNothingIfMessageHasCommandPropertySetOnPreReceive()
         $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'aCommand');
 
         $context = new Context(new NullContext());
-        $context->setPsrMessage($message);
+        $context->setInteropMessage($message);
 
         $driver = $this->createDriverStub();
         $driver
@@ -91,7 +91,7 @@ public function testShouldDoNothingIfMessageHasProcessorPropertySetOnPreReceive(
         $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aProcessor');
 
         $context = new Context(new NullContext());
-        $context->setPsrMessage($message);
+        $context->setInteropMessage($message);
 
         $driver = $this->createDriverStub();
         $driver
@@ -116,7 +116,7 @@ public function testShouldDoNothingIfCurrentQueueHasNoExclusiveProcessor()
         $queue = new NullQueue('aBarQueueName');
 
         $context = new Context(new NullContext());
-        $context->setPsrMessage($message);
+        $context->setInteropMessage($message);
         $context->setPsrQueue($queue);
 
         $extension = new ExclusiveCommandExtension($this->createDriverStub(new RouteCollection([])));
@@ -134,7 +134,7 @@ public function testShouldSetCommandPropertiesIfCurrentQueueHasExclusiveCommandP
         $queue = new NullQueue('fooQueue');
 
         $context = new Context(new NullContext());
-        $context->setPsrMessage($message);
+        $context->setInteropMessage($message);
         $context->setPsrQueue($queue);
         $context->setLogger(new NullLogger());
 
@@ -175,7 +175,7 @@ public function testShouldDoNothingIfAnotherQueue()
         $queue = new NullQueue('barQueue');
 
         $context = new Context(new NullContext());
-        $context->setPsrMessage($message);
+        $context->setInteropMessage($message);
         $context->setPsrQueue($queue);
         $context->setLogger(new NullLogger());
 
diff --git a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
index 686ece9..dd673e3 100644
--- a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
@@ -10,7 +10,7 @@
 use Enqueue\Null\NullMessage;
 use Enqueue\Null\NullQueue;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrContext;
+use Interop\Queue\Context as InteropContext;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -48,9 +48,9 @@ public function testShouldSetRouterProcessorPropertyIfNotSetAndOnRouterQueue()
 
         $message = new NullMessage();
 
-        $context = new Context($this->createPsrContextMock());
+        $context = new Context($this->createContextMock());
         $context->setLogger(new NullLogger());
-        $context->setPsrMessage($message);
+        $context->setInteropMessage($message);
         $context->setPsrQueue(new NullQueue('test.router-queue'));
 
         $extension = new SetRouterPropertiesExtension($driver);
@@ -81,8 +81,8 @@ public function testShouldNotSetRouterProcessorPropertyIfNotSetAndNotOnRouterQue
 
         $message = new NullMessage();
 
-        $context = new Context($this->createPsrContextMock());
-        $context->setPsrMessage($message);
+        $context = new Context($this->createContextMock());
+        $context->setInteropMessage($message);
         $context->setPsrQueue(new NullQueue('test.another-queue'));
 
         $extension = new SetRouterPropertiesExtension($driver);
@@ -102,8 +102,8 @@ public function testShouldNotSetAnyPropertyIfProcessorNamePropertyAlreadySet()
         $message = new NullMessage();
         $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'non-router-processor');
 
-        $context = new Context($this->createPsrContextMock());
-        $context->setPsrMessage($message);
+        $context = new Context($this->createContextMock());
+        $context->setInteropMessage($message);
 
         $extension = new SetRouterPropertiesExtension($driver);
         $extension->onPreReceived($context);
@@ -114,11 +114,11 @@ public function testShouldNotSetAnyPropertyIfProcessorNamePropertyAlreadySet()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext
+     * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
      */
-    protected function createPsrContextMock()
+    protected function createContextMock(): InteropContext
     {
-        return $this->createMock(PsrContext::class);
+        return $this->createMock(InteropContext::class);
     }
 
     /**
diff --git a/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php b/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php
index 21b011f..1339dd4 100644
--- a/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php
@@ -7,7 +7,7 @@
 use Enqueue\Consumption\Context;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrContext;
+use Interop\Queue\Context as InteropContext;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -36,7 +36,7 @@ public function testShouldSetupBroker()
             ->with($this->identicalTo($logger))
         ;
 
-        $context = new Context($this->createMock(PsrContext::class));
+        $context = new Context($this->createMock(InteropContext::class));
         $context->setLogger($logger);
 
         $extension = new SetupBrokerExtension($driver);
@@ -54,7 +54,7 @@ public function testShouldSetupBrokerOnlyOnce()
             ->with($this->identicalTo($logger))
         ;
 
-        $context = new Context($this->createMock(PsrContext::class));
+        $context = new Context($this->createMock(InteropContext::class));
         $context->setLogger($logger);
 
         $extension = new SetupBrokerExtension($driver);
diff --git a/Tests/Client/DelegateProcessorTest.php b/Tests/Client/DelegateProcessorTest.php
index f107dde..4bf688f 100644
--- a/Tests/Client/DelegateProcessorTest.php
+++ b/Tests/Client/DelegateProcessorTest.php
@@ -6,8 +6,8 @@
 use Enqueue\Client\DelegateProcessor;
 use Enqueue\Client\ProcessorRegistryInterface;
 use Enqueue\Null\NullMessage;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Context;
+use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
 
 class DelegateProcessorTest extends TestCase
@@ -25,12 +25,12 @@ public function testShouldThrowExceptionIfProcessorNameIsNotSet()
         );
 
         $processor = new DelegateProcessor($this->createProcessorRegistryMock());
-        $processor->process(new NullMessage(), $this->createPsrContextMock());
+        $processor->process(new NullMessage(), $this->createContextMock());
     }
 
     public function testShouldProcessMessage()
     {
-        $session = $this->createPsrContextMock();
+        $session = $this->createContextMock();
         $message = new NullMessage();
         $message->setProperties([
             Config::PARAMETER_PROCESSOR_NAME => 'processor-name',
@@ -67,18 +67,18 @@ protected function createProcessorRegistryMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext
+     * @return \PHPUnit_Framework_MockObject_MockObject|Context
      */
-    protected function createPsrContextMock()
+    protected function createContextMock()
     {
-        return $this->createMock(PsrContext::class);
+        return $this->createMock(Context::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProcessor
+     * @return \PHPUnit_Framework_MockObject_MockObject|Processor
      */
     protected function createProcessorMock()
     {
-        return $this->createMock(PsrProcessor::class);
+        return $this->createMock(Processor::class);
     }
 }
diff --git a/Tests/Client/Driver/AmqpDriverTest.php b/Tests/Client/Driver/AmqpDriverTest.php
index e9c1d77..1d89acd 100644
--- a/Tests/Client/Driver/AmqpDriverTest.php
+++ b/Tests/Client/Driver/AmqpDriverTest.php
@@ -17,9 +17,9 @@
 use Interop\Amqp\Impl\AmqpMessage;
 use Interop\Amqp\Impl\AmqpQueue;
 use Interop\Amqp\Impl\AmqpTopic;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProducer;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\PsrQueue;
 use PHPUnit\Framework\TestCase;
 
@@ -291,7 +291,7 @@ protected function createDriver(...$args): DriverInterface
     /**
      * @return AmqpContext
      */
-    protected function createContextMock(): PsrContext
+    protected function createContextMock(): Context
     {
         return $this->createMock(AmqpContext::class);
     }
@@ -299,7 +299,7 @@ protected function createContextMock(): PsrContext
     /**
      * @return AmqpProducer
      */
-    protected function createProducerMock(): PsrProducer
+    protected function createProducerMock(): InteropProducer
     {
         return $this->createMock(AmqpProducer::class);
     }
@@ -323,7 +323,7 @@ protected function createTopic(string $name): AmqpTopic
     /**
      * @return AmqpMessage
      */
-    protected function createMessage(): PsrMessage
+    protected function createMessage(): InteropMessage
     {
         return new AmqpMessage();
     }
@@ -333,7 +333,7 @@ protected function getRouterTransportName(): string
         return 'aprefix.router';
     }
 
-    protected function assertTransportMessage(PsrMessage $transportMessage): void
+    protected function assertTransportMessage(InteropMessage $transportMessage): void
     {
         $this->assertSame('body', $transportMessage->getBody());
         $this->assertArraySubset([
diff --git a/Tests/Client/Driver/DbalDriverTest.php b/Tests/Client/Driver/DbalDriverTest.php
index c6fd10d..f8b8a67 100644
--- a/Tests/Client/Driver/DbalDriverTest.php
+++ b/Tests/Client/Driver/DbalDriverTest.php
@@ -11,9 +11,9 @@
 use Enqueue\Dbal\DbalMessage;
 use Enqueue\Dbal\DbalProducer;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProducer;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\PsrQueue;
 use Interop\Queue\PsrTopic;
 
@@ -61,7 +61,7 @@ protected function createDriver(...$args): DriverInterface
     /**
      * @return DbalContext
      */
-    protected function createContextMock(): PsrContext
+    protected function createContextMock(): Context
     {
         return $this->createMock(DbalContext::class);
     }
@@ -69,7 +69,7 @@ protected function createContextMock(): PsrContext
     /**
      * @return DbalProducer
      */
-    protected function createProducerMock(): PsrProducer
+    protected function createProducerMock(): InteropProducer
     {
         return $this->createMock(DbalProducer::class);
     }
@@ -93,7 +93,7 @@ protected function createTopic(string $name): PsrTopic
     /**
      * @return DbalMessage
      */
-    protected function createMessage(): PsrMessage
+    protected function createMessage(): InteropMessage
     {
         return new DbalMessage();
     }
diff --git a/Tests/Client/Driver/FsDriverTest.php b/Tests/Client/Driver/FsDriverTest.php
index bbc083b..522a76e 100644
--- a/Tests/Client/Driver/FsDriverTest.php
+++ b/Tests/Client/Driver/FsDriverTest.php
@@ -12,9 +12,9 @@
 use Enqueue\Fs\FsMessage;
 use Enqueue\Fs\FsProducer;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProducer;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\PsrQueue;
 use Interop\Queue\PsrTopic;
 use Makasim\File\TempFile;
@@ -86,7 +86,7 @@ protected function createDriver(...$args): DriverInterface
     /**
      * @return FsContext
      */
-    protected function createContextMock(): PsrContext
+    protected function createContextMock(): Context
     {
         return $this->createMock(FsContext::class);
     }
@@ -94,7 +94,7 @@ protected function createContextMock(): PsrContext
     /**
      * @return FsProducer
      */
-    protected function createProducerMock(): PsrProducer
+    protected function createProducerMock(): InteropProducer
     {
         return $this->createMock(FsProducer::class);
     }
@@ -118,7 +118,7 @@ protected function createTopic(string $name): PsrTopic
     /**
      * @return FsMessage
      */
-    protected function createMessage(): PsrMessage
+    protected function createMessage(): InteropMessage
     {
         return new FsMessage();
     }
diff --git a/Tests/Client/Driver/GenericDriverTest.php b/Tests/Client/Driver/GenericDriverTest.php
index 31f92a5..932ed3e 100644
--- a/Tests/Client/Driver/GenericDriverTest.php
+++ b/Tests/Client/Driver/GenericDriverTest.php
@@ -9,9 +9,9 @@
 use Enqueue\Null\NullQueue;
 use Enqueue\Null\NullTopic;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProducer;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\PsrQueue;
 use Interop\Queue\PsrTopic;
 use PHPUnit\Framework\TestCase;
@@ -31,14 +31,14 @@ protected function createDriver(...$args): DriverInterface
         return new GenericDriver(...$args);
     }
 
-    protected function createContextMock(): PsrContext
+    protected function createContextMock(): Context
     {
-        return $this->createMock(PsrContext::class);
+        return $this->createMock(Context::class);
     }
 
-    protected function createProducerMock(): PsrProducer
+    protected function createProducerMock(): InteropProducer
     {
-        return $this->createMock(PsrProducer::class);
+        return $this->createMock(InteropProducer::class);
     }
 
     protected function createQueue(string $name): PsrQueue
@@ -51,12 +51,12 @@ protected function createTopic(string $name): PsrTopic
         return new NullTopic($name);
     }
 
-    protected function createMessage(): PsrMessage
+    protected function createMessage(): InteropMessage
     {
         return new NullMessage();
     }
 
-    protected function assertTransportMessage(PsrMessage $transportMessage): void
+    protected function assertTransportMessage(InteropMessage $transportMessage): void
     {
         $this->assertSame('body', $transportMessage->getBody());
         $this->assertArraySubset([
diff --git a/Tests/Client/Driver/GenericDriverTestsTrait.php b/Tests/Client/Driver/GenericDriverTestsTrait.php
index 5d5d4c8..8d38d7c 100644
--- a/Tests/Client/Driver/GenericDriverTestsTrait.php
+++ b/Tests/Client/Driver/GenericDriverTestsTrait.php
@@ -8,10 +8,10 @@
 use Enqueue\Client\MessagePriority;
 use Enqueue\Client\Route;
 use Enqueue\Client\RouteCollection;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrDestination;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProducer;
+use Interop\Queue\Context;
+use Interop\Queue\Destination;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\PsrQueue;
 use Interop\Queue\PsrTopic;
 
@@ -28,7 +28,7 @@ public function testCouldBeConstructedWithRequiredArguments()
         $this->assertInstanceOf(DriverInterface::class, $driver);
     }
 
-    public function testShouldReturnPsrContextSetInConstructor()
+    public function testShouldReturnContextSetInConstructor()
     {
         $context = $this->createContextMock();
 
@@ -258,7 +258,7 @@ public function testShouldSendMessageToRouter()
         $producer
             ->expects($this->once())
             ->method('send')
-            ->willReturnCallback(function (PsrDestination $topic, PsrMessage $message) use ($transportMessage) {
+            ->willReturnCallback(function (Destination $topic, InteropMessage $message) use ($transportMessage) {
                 $this->assertSame(
                     $this->getRouterTransportName(),
                     $topic instanceof PsrTopic ? $topic->getTopicName() : $topic->getQueueName());
@@ -1070,23 +1070,23 @@ abstract protected function createDriver(...$args): DriverInterface;
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    abstract protected function createContextMock(): PsrContext;
+    abstract protected function createContextMock(): Context;
 
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    abstract protected function createProducerMock(): PsrProducer;
+    abstract protected function createProducerMock(): InteropProducer;
 
     abstract protected function createQueue(string $name): PsrQueue;
 
     abstract protected function createTopic(string $name): PsrTopic;
 
-    abstract protected function createMessage(): PsrMessage;
+    abstract protected function createMessage(): InteropMessage;
 
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    protected function createContextStub(): PsrContext
+    protected function createContextStub(): Context
     {
         $context = $this->createContextMock();
 
@@ -1109,7 +1109,7 @@ protected function createContextStub(): PsrContext
         return $context;
     }
 
-    protected function assertTransportMessage(PsrMessage $transportMessage): void
+    protected function assertTransportMessage(InteropMessage $transportMessage): void
     {
         $this->assertSame('body', $transportMessage->getBody());
         $this->assertEquals([
diff --git a/Tests/Client/Driver/GpsDriverTest.php b/Tests/Client/Driver/GpsDriverTest.php
index 8b48a36..d6623ac 100644
--- a/Tests/Client/Driver/GpsDriverTest.php
+++ b/Tests/Client/Driver/GpsDriverTest.php
@@ -13,9 +13,9 @@
 use Enqueue\Gps\GpsQueue;
 use Enqueue\Gps\GpsTopic;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProducer;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\PsrQueue;
 use Interop\Queue\PsrTopic;
 use PHPUnit\Framework\TestCase;
@@ -98,7 +98,7 @@ protected function createDriver(...$args): DriverInterface
     /**
      * @return GpsContext
      */
-    protected function createContextMock(): PsrContext
+    protected function createContextMock(): Context
     {
         return $this->createMock(GpsContext::class);
     }
@@ -106,7 +106,7 @@ protected function createContextMock(): PsrContext
     /**
      * @return GpsProducer
      */
-    protected function createProducerMock(): PsrProducer
+    protected function createProducerMock(): InteropProducer
     {
         return $this->createMock(GpsProducer::class);
     }
@@ -130,7 +130,7 @@ protected function createTopic(string $name): PsrTopic
     /**
      * @return GpsMessage
      */
-    protected function createMessage(): PsrMessage
+    protected function createMessage(): InteropMessage
     {
         return new GpsMessage();
     }
diff --git a/Tests/Client/Driver/MongodbDriverTest.php b/Tests/Client/Driver/MongodbDriverTest.php
index da9c7e6..bef90f6 100644
--- a/Tests/Client/Driver/MongodbDriverTest.php
+++ b/Tests/Client/Driver/MongodbDriverTest.php
@@ -11,9 +11,9 @@
 use Enqueue\Mongodb\MongodbMessage;
 use Enqueue\Mongodb\MongodbProducer;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProducer;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\PsrQueue;
 use Interop\Queue\PsrTopic;
 
@@ -65,7 +65,7 @@ protected function createDriver(...$args): DriverInterface
     /**
      * @return MongodbContext
      */
-    protected function createContextMock(): PsrContext
+    protected function createContextMock(): Context
     {
         return $this->createMock(MongodbContext::class);
     }
@@ -73,7 +73,7 @@ protected function createContextMock(): PsrContext
     /**
      * @return MongodbProducer
      */
-    protected function createProducerMock(): PsrProducer
+    protected function createProducerMock(): InteropProducer
     {
         return $this->createMock(MongodbProducer::class);
     }
@@ -97,7 +97,7 @@ protected function createTopic(string $name): PsrTopic
     /**
      * @return MongodbMessage
      */
-    protected function createMessage(): PsrMessage
+    protected function createMessage(): InteropMessage
     {
         return new MongodbMessage();
     }
diff --git a/Tests/Client/Driver/RabbitMqDriverTest.php b/Tests/Client/Driver/RabbitMqDriverTest.php
index 295ac72..53f5f87 100644
--- a/Tests/Client/Driver/RabbitMqDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqDriverTest.php
@@ -14,9 +14,9 @@
 use Interop\Amqp\Impl\AmqpMessage;
 use Interop\Amqp\Impl\AmqpQueue;
 use Interop\Amqp\Impl\AmqpTopic;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProducer;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\PsrQueue;
 use PHPUnit\Framework\TestCase;
 
@@ -69,7 +69,7 @@ protected function createDriver(...$args): DriverInterface
     /**
      * @return AmqpContext
      */
-    protected function createContextMock(): PsrContext
+    protected function createContextMock(): Context
     {
         return $this->createMock(AmqpContext::class);
     }
@@ -77,7 +77,7 @@ protected function createContextMock(): PsrContext
     /**
      * @return AmqpProducer
      */
-    protected function createProducerMock(): PsrProducer
+    protected function createProducerMock(): InteropProducer
     {
         return $this->createMock(AmqpProducer::class);
     }
@@ -101,7 +101,7 @@ protected function createTopic(string $name): AmqpTopic
     /**
      * @return AmqpMessage
      */
-    protected function createMessage(): PsrMessage
+    protected function createMessage(): InteropMessage
     {
         return new AmqpMessage();
     }
@@ -111,7 +111,7 @@ protected function getRouterTransportName(): string
         return 'aprefix.router';
     }
 
-    protected function assertTransportMessage(PsrMessage $transportMessage): void
+    protected function assertTransportMessage(InteropMessage $transportMessage): void
     {
         $this->assertSame('body', $transportMessage->getBody());
         $this->assertArraySubset([
diff --git a/Tests/Client/Driver/RabbitMqStompDriverTest.php b/Tests/Client/Driver/RabbitMqStompDriverTest.php
index c055d8b..e573038 100644
--- a/Tests/Client/Driver/RabbitMqStompDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqStompDriverTest.php
@@ -17,9 +17,9 @@
 use Enqueue\Stomp\StompMessage;
 use Enqueue\Stomp\StompProducer;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProducer;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\PsrQueue;
 use Interop\Queue\PsrTopic;
 use PHPUnit\Framework\TestCase;
@@ -479,7 +479,7 @@ protected function createDriver(...$args): DriverInterface
     /**
      * @return StompContext
      */
-    protected function createContextMock(): PsrContext
+    protected function createContextMock(): Context
     {
         return $this->createMock(StompContext::class);
     }
@@ -487,7 +487,7 @@ protected function createContextMock(): PsrContext
     /**
      * @return StompProducer
      */
-    protected function createProducerMock(): PsrProducer
+    protected function createProducerMock(): InteropProducer
     {
         return $this->createMock(StompProducer::class);
     }
@@ -519,12 +519,12 @@ protected function createTopic(string $name): PsrTopic
     /**
      * @return StompMessage
      */
-    protected function createMessage(): PsrMessage
+    protected function createMessage(): InteropMessage
     {
         return new StompMessage();
     }
 
-    protected function assertTransportMessage(PsrMessage $transportMessage): void
+    protected function assertTransportMessage(InteropMessage $transportMessage): void
     {
         $this->assertSame('body', $transportMessage->getBody());
         $this->assertEquals([
diff --git a/Tests/Client/Driver/RdKafkaDriverTest.php b/Tests/Client/Driver/RdKafkaDriverTest.php
index 899830e..ada2ebc 100644
--- a/Tests/Client/Driver/RdKafkaDriverTest.php
+++ b/Tests/Client/Driver/RdKafkaDriverTest.php
@@ -13,9 +13,9 @@
 use Enqueue\RdKafka\RdKafkaProducer;
 use Enqueue\RdKafka\RdKafkaTopic;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProducer;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\PsrQueue;
 use PHPUnit\Framework\TestCase;
 
@@ -81,7 +81,7 @@ protected function createDriver(...$args): DriverInterface
     /**
      * @return RdKafkaContext
      */
-    protected function createContextMock(): PsrContext
+    protected function createContextMock(): Context
     {
         return $this->createMock(RdKafkaContext::class);
     }
@@ -89,7 +89,7 @@ protected function createContextMock(): PsrContext
     /**
      * @return RdKafkaProducer
      */
-    protected function createProducerMock(): PsrProducer
+    protected function createProducerMock(): InteropProducer
     {
         return $this->createMock(RdKafkaProducer::class);
     }
@@ -113,7 +113,7 @@ protected function createTopic(string $name): RdKafkaTopic
     /**
      * @return RdKafkaMessage
      */
-    protected function createMessage(): PsrMessage
+    protected function createMessage(): InteropMessage
     {
         return new RdKafkaMessage();
     }
diff --git a/Tests/Client/Driver/SqsDriverTest.php b/Tests/Client/Driver/SqsDriverTest.php
index bf8bef1..f04987d 100644
--- a/Tests/Client/Driver/SqsDriverTest.php
+++ b/Tests/Client/Driver/SqsDriverTest.php
@@ -12,9 +12,9 @@
 use Enqueue\Sqs\SqsMessage;
 use Enqueue\Sqs\SqsProducer;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProducer;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\PsrQueue;
 use Interop\Queue\PsrTopic;
 use PHPUnit\Framework\TestCase;
@@ -84,7 +84,7 @@ protected function createDriver(...$args): DriverInterface
     /**
      * @return SqsContext
      */
-    protected function createContextMock(): PsrContext
+    protected function createContextMock(): Context
     {
         return $this->createMock(SqsContext::class);
     }
@@ -92,7 +92,7 @@ protected function createContextMock(): PsrContext
     /**
      * @return SqsProducer
      */
-    protected function createProducerMock(): PsrProducer
+    protected function createProducerMock(): InteropProducer
     {
         return $this->createMock(SqsProducer::class);
     }
@@ -116,7 +116,7 @@ protected function createTopic(string $name): PsrTopic
     /**
      * @return SqsMessage
      */
-    protected function createMessage(): PsrMessage
+    protected function createMessage(): InteropMessage
     {
         return new SqsMessage();
     }
diff --git a/Tests/Client/Driver/StompDriverTest.php b/Tests/Client/Driver/StompDriverTest.php
index b68e63e..e5ea51a 100644
--- a/Tests/Client/Driver/StompDriverTest.php
+++ b/Tests/Client/Driver/StompDriverTest.php
@@ -13,9 +13,9 @@
 use Enqueue\Stomp\StompMessage;
 use Enqueue\Stomp\StompProducer;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProducer;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\PsrQueue;
 use Interop\Queue\PsrTopic;
 use PHPUnit\Framework\TestCase;
@@ -108,7 +108,7 @@ protected function createDriver(...$args): DriverInterface
     /**
      * @return StompContext
      */
-    protected function createContextMock(): PsrContext
+    protected function createContextMock(): Context
     {
         return $this->createMock(StompContext::class);
     }
@@ -116,7 +116,7 @@ protected function createContextMock(): PsrContext
     /**
      * @return StompProducer
      */
-    protected function createProducerMock(): PsrProducer
+    protected function createProducerMock(): InteropProducer
     {
         return $this->createMock(StompProducer::class);
     }
@@ -148,12 +148,12 @@ protected function createTopic(string $name): PsrTopic
     /**
      * @return StompMessage
      */
-    protected function createMessage(): PsrMessage
+    protected function createMessage(): InteropMessage
     {
         return new StompMessage();
     }
 
-    protected function assertTransportMessage(PsrMessage $transportMessage): void
+    protected function assertTransportMessage(InteropMessage $transportMessage): void
     {
         $this->assertSame('body', $transportMessage->getBody());
         $this->assertEquals([
diff --git a/Tests/Client/RouterProcessorTest.php b/Tests/Client/RouterProcessorTest.php
index f69bb07..f38d448 100644
--- a/Tests/Client/RouterProcessorTest.php
+++ b/Tests/Client/RouterProcessorTest.php
@@ -12,7 +12,7 @@
 use Enqueue\Null\NullContext;
 use Enqueue\Null\NullMessage;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
 
 class RouterProcessorTest extends TestCase
@@ -21,7 +21,7 @@ class RouterProcessorTest extends TestCase
 
     public function testShouldImplementProcessorInterface()
     {
-        $this->assertClassImplements(PsrProcessor::class, RouterProcessor::class);
+        $this->assertClassImplements(Processor::class, RouterProcessor::class);
     }
 
     public function testShouldBeFinal()
diff --git a/Tests/Consumption/CallbackProcessorTest.php b/Tests/Consumption/CallbackProcessorTest.php
index 1b034d9..f134cfe 100644
--- a/Tests/Consumption/CallbackProcessorTest.php
+++ b/Tests/Consumption/CallbackProcessorTest.php
@@ -6,7 +6,7 @@
 use Enqueue\Null\NullContext;
 use Enqueue\Null\NullMessage;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
 
 class CallbackProcessorTest extends TestCase
@@ -15,7 +15,7 @@ class CallbackProcessorTest extends TestCase
 
     public function testShouldImplementProcessorInterface()
     {
-        $this->assertClassImplements(PsrProcessor::class, CallbackProcessor::class);
+        $this->assertClassImplements(Processor::class, CallbackProcessor::class);
     }
 
     public function testCouldBeConstructedWithCallableAsArgument()
diff --git a/Tests/Consumption/ContextTest.php b/Tests/Consumption/ContextTest.php
index a0f6b26..e5531c4 100644
--- a/Tests/Consumption/ContextTest.php
+++ b/Tests/Consumption/ContextTest.php
@@ -6,10 +6,10 @@
 use Enqueue\Consumption\Exception\IllegalContextModificationException;
 use Enqueue\Null\NullQueue;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrConsumer;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Consumer;
+use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -19,50 +19,50 @@ class ContextTest extends TestCase
 
     public function testCouldBeConstructedWithSessionAsFirstArgument()
     {
-        new Context($this->createPsrContext());
+        new Context($this->createContext());
     }
 
     public function testShouldAllowGetSessionSetInConstructor()
     {
-        $psrContext = $this->createPsrContext();
+        $interopContext = $this->createContext();
 
-        $context = new Context($psrContext);
+        $context = new Context($interopContext);
 
-        $this->assertSame($psrContext, $context->getPsrContext());
+        $this->assertSame($interopContext, $context->getContext());
     }
 
     public function testShouldAllowGetMessageConsumerPreviouslySet()
     {
-        $messageConsumer = $this->createPsrConsumer();
+        $messageConsumer = $this->createConsumer();
 
-        $context = new Context($this->createPsrContext());
-        $context->setPsrConsumer($messageConsumer);
+        $context = new Context($this->createContext());
+        $context->setConsumer($messageConsumer);
 
-        $this->assertSame($messageConsumer, $context->getPsrConsumer());
+        $this->assertSame($messageConsumer, $context->getConsumer());
     }
 
     public function testThrowOnTryToChangeMessageConsumerIfAlreadySet()
     {
-        $messageConsumer = $this->createPsrConsumer();
-        $anotherMessageConsumer = $this->createPsrConsumer();
+        $messageConsumer = $this->createConsumer();
+        $anotherMessageConsumer = $this->createConsumer();
 
-        $context = new Context($this->createPsrContext());
+        $context = new Context($this->createContext());
 
-        $context->setPsrConsumer($messageConsumer);
+        $context->setConsumer($messageConsumer);
 
         $this->expectException(IllegalContextModificationException::class);
 
-        $context->setPsrConsumer($anotherMessageConsumer);
+        $context->setConsumer($anotherMessageConsumer);
     }
 
     public function testShouldAllowGetMessageProducerPreviouslySet()
     {
         $processorMock = $this->createProcessorMock();
 
-        $context = new Context($this->createPsrContext());
-        $context->setPsrProcessor($processorMock);
+        $context = new Context($this->createContext());
+        $context->setProcessor($processorMock);
 
-        $this->assertSame($processorMock, $context->getPsrProcessor());
+        $this->assertSame($processorMock, $context->getProcessor());
     }
 
     public function testThrowOnTryToChangeProcessorIfAlreadySet()
@@ -70,20 +70,20 @@ public function testThrowOnTryToChangeProcessorIfAlreadySet()
         $processor = $this->createProcessorMock();
         $anotherProcessor = $this->createProcessorMock();
 
-        $context = new Context($this->createPsrContext());
+        $context = new Context($this->createContext());
 
-        $context->setPsrProcessor($processor);
+        $context->setProcessor($processor);
 
         $this->expectException(IllegalContextModificationException::class);
 
-        $context->setPsrProcessor($anotherProcessor);
+        $context->setProcessor($anotherProcessor);
     }
 
     public function testShouldAllowGetLoggerPreviouslySet()
     {
         $logger = new NullLogger();
 
-        $context = new Context($this->createPsrContext());
+        $context = new Context($this->createContext());
         $context->setLogger($logger);
 
         $this->assertSame($logger, $context->getLogger());
@@ -91,42 +91,42 @@ public function testShouldAllowGetLoggerPreviouslySet()
 
     public function testShouldSetExecutionInterruptedToFalseInConstructor()
     {
-        $context = new Context($this->createPsrContext());
+        $context = new Context($this->createContext());
 
         $this->assertFalse($context->isExecutionInterrupted());
     }
 
     public function testShouldAllowGetPreviouslySetMessage()
     {
-        /** @var PsrMessage $message */
-        $message = $this->createMock(PsrMessage::class);
+        /** @var InteropMessage $message */
+        $message = $this->createMock(InteropMessage::class);
 
-        $context = new Context($this->createPsrContext());
+        $context = new Context($this->createContext());
 
-        $context->setPsrMessage($message);
+        $context->setInteropMessage($message);
 
-        $this->assertSame($message, $context->getPsrMessage());
+        $this->assertSame($message, $context->getInteropMessage());
     }
 
     public function testThrowOnTryToChangeMessageIfAlreadySet()
     {
-        /** @var PsrMessage $message */
-        $message = $this->createMock(PsrMessage::class);
+        /** @var InteropMessage $message */
+        $message = $this->createMock(InteropMessage::class);
 
-        $context = new Context($this->createPsrContext());
+        $context = new Context($this->createContext());
 
         $this->expectException(IllegalContextModificationException::class);
         $this->expectExceptionMessage('The message could be set once');
 
-        $context->setPsrMessage($message);
-        $context->setPsrMessage($message);
+        $context->setInteropMessage($message);
+        $context->setInteropMessage($message);
     }
 
     public function testShouldAllowGetPreviouslySetException()
     {
         $exception = new \Exception();
 
-        $context = new Context($this->createPsrContext());
+        $context = new Context($this->createContext());
 
         $context->setException($exception);
 
@@ -137,7 +137,7 @@ public function testShouldAllowGetPreviouslySetResult()
     {
         $result = 'aResult';
 
-        $context = new Context($this->createPsrContext());
+        $context = new Context($this->createContext());
 
         $context->setResult($result);
 
@@ -148,7 +148,7 @@ public function testThrowOnTryToChangeResultIfAlreadySet()
     {
         $result = 'aResult';
 
-        $context = new Context($this->createPsrContext());
+        $context = new Context($this->createContext());
 
         $this->expectException(IllegalContextModificationException::class);
         $this->expectExceptionMessage('The result modification is not allowed');
@@ -159,7 +159,7 @@ public function testThrowOnTryToChangeResultIfAlreadySet()
 
     public function testShouldAllowGetPreviouslySetExecutionInterrupted()
     {
-        $context = new Context($this->createPsrContext());
+        $context = new Context($this->createContext());
 
         // guard
         $this->assertFalse($context->isExecutionInterrupted());
@@ -171,7 +171,7 @@ public function testShouldAllowGetPreviouslySetExecutionInterrupted()
 
     public function testThrowOnTryToRollbackExecutionInterruptedIfAlreadySetToTrue()
     {
-        $context = new Context($this->createPsrContext());
+        $context = new Context($this->createContext());
 
         $this->expectException(IllegalContextModificationException::class);
         $this->expectExceptionMessage('The execution once interrupted could not be roll backed');
@@ -182,7 +182,7 @@ public function testThrowOnTryToRollbackExecutionInterruptedIfAlreadySetToTrue()
 
     public function testNotThrowOnSettingExecutionInterruptedToTrueIfAlreadySetToTrue()
     {
-        $context = new Context($this->createPsrContext());
+        $context = new Context($this->createContext());
 
         $context->setExecutionInterrupted(true);
         $context->setExecutionInterrupted(true);
@@ -192,7 +192,7 @@ public function testShouldAllowGetPreviouslySetLogger()
     {
         $expectedLogger = new NullLogger();
 
-        $context = new Context($this->createPsrContext());
+        $context = new Context($this->createContext());
 
         $context->setLogger($expectedLogger);
 
@@ -201,7 +201,7 @@ public function testShouldAllowGetPreviouslySetLogger()
 
     public function testThrowOnSettingLoggerIfAlreadySet()
     {
-        $context = new Context($this->createPsrContext());
+        $context = new Context($this->createContext());
 
         $context->setLogger(new NullLogger());
 
@@ -213,7 +213,7 @@ public function testThrowOnSettingLoggerIfAlreadySet()
 
     public function testShouldAllowGetPreviouslySetQueue()
     {
-        $context = new Context($this->createPsrContext());
+        $context = new Context($this->createContext());
 
         $context->setPsrQueue($queue = new NullQueue(''));
 
@@ -222,7 +222,7 @@ public function testShouldAllowGetPreviouslySetQueue()
 
     public function testThrowOnSettingQueueNameIfAlreadySet()
     {
-        $context = new Context($this->createPsrContext());
+        $context = new Context($this->createContext());
 
         $context->setPsrQueue(new NullQueue(''));
 
@@ -233,26 +233,26 @@ public function testThrowOnSettingQueueNameIfAlreadySet()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext
+     * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
      */
-    protected function createPsrContext()
+    protected function createContext(): InteropContext
     {
-        return $this->createMock(PsrContext::class);
+        return $this->createMock(InteropContext::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrConsumer
+     * @return \PHPUnit_Framework_MockObject_MockObject|Consumer
      */
-    protected function createPsrConsumer()
+    protected function createConsumer()
     {
-        return $this->createMock(PsrConsumer::class);
+        return $this->createMock(Consumer::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProcessor
+     * @return \PHPUnit_Framework_MockObject_MockObject|Processor
      */
     protected function createProcessorMock()
     {
-        return $this->createMock(PsrProcessor::class);
+        return $this->createMock(Processor::class);
     }
 }
diff --git a/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php b/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
index 250c577..4c3d755 100644
--- a/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
@@ -4,9 +4,9 @@
 
 use Enqueue\Consumption\Context;
 use Enqueue\Consumption\Extension\LimitConsumedMessagesExtension;
-use Interop\Queue\PsrConsumer;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Consumer;
+use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 
@@ -97,12 +97,12 @@ public function testOnPostReceivedShouldInterruptExecutionIfMessageLimitExceeded
     /**
      * @return Context
      */
-    protected function createContext()
+    protected function createContext(): Context
     {
-        $context = new Context($this->createMock(PsrContext::class));
+        $context = new Context($this->createMock(InteropContext::class));
         $context->setLogger($this->createMock(LoggerInterface::class));
-        $context->setPsrConsumer($this->createMock(PsrConsumer::class));
-        $context->setPsrProcessor($this->createMock(PsrProcessor::class));
+        $context->setConsumer($this->createMock(Consumer::class));
+        $context->setProcessor($this->createMock(Processor::class));
 
         return $context;
     }
diff --git a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
index 74514e5..e42e559 100644
--- a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
@@ -4,9 +4,9 @@
 
 use Enqueue\Consumption\Context;
 use Enqueue\Consumption\Extension\LimitConsumerMemoryExtension;
-use Interop\Queue\PsrConsumer;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Consumer;
+use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 
@@ -26,7 +26,7 @@ public function testShouldThrowExceptionIfMemoryLimitIsNotInt()
 
     public function testOnIdleShouldInterruptExecutionIfMemoryLimitReached()
     {
-        $context = $this->createPsrContext();
+        $context = $this->createContext();
         $context->getLogger()
             ->expects($this->once())
             ->method('debug')
@@ -45,7 +45,7 @@ public function testOnIdleShouldInterruptExecutionIfMemoryLimitReached()
 
     public function testOnPostReceivedShouldInterruptExecutionIfMemoryLimitReached()
     {
-        $context = $this->createPsrContext();
+        $context = $this->createContext();
         $context->getLogger()
             ->expects($this->once())
             ->method('debug')
@@ -64,7 +64,7 @@ public function testOnPostReceivedShouldInterruptExecutionIfMemoryLimitReached()
 
     public function testOnBeforeReceivedShouldInterruptExecutionIfMemoryLimitReached()
     {
-        $context = $this->createPsrContext();
+        $context = $this->createContext();
         $context->getLogger()
             ->expects($this->once())
             ->method('debug')
@@ -83,7 +83,7 @@ public function testOnBeforeReceivedShouldInterruptExecutionIfMemoryLimitReached
 
     public function testOnBeforeReceiveShouldNotInterruptExecutionIfMemoryLimitIsNotReached()
     {
-        $context = $this->createPsrContext();
+        $context = $this->createContext();
 
         // guard
         $this->assertFalse($context->isExecutionInterrupted());
@@ -97,7 +97,7 @@ public function testOnBeforeReceiveShouldNotInterruptExecutionIfMemoryLimitIsNot
 
     public function testOnIdleShouldNotInterruptExecutionIfMemoryLimitIsNotReached()
     {
-        $context = $this->createPsrContext();
+        $context = $this->createContext();
 
         // guard
         $this->assertFalse($context->isExecutionInterrupted());
@@ -111,7 +111,7 @@ public function testOnIdleShouldNotInterruptExecutionIfMemoryLimitIsNotReached()
 
     public function testOnPostReceivedShouldNotInterruptExecutionIfMemoryLimitIsNotReached()
     {
-        $context = $this->createPsrContext();
+        $context = $this->createContext();
 
         // guard
         $this->assertFalse($context->isExecutionInterrupted());
@@ -126,12 +126,12 @@ public function testOnPostReceivedShouldNotInterruptExecutionIfMemoryLimitIsNotR
     /**
      * @return Context
      */
-    protected function createPsrContext()
+    protected function createContext(): Context
     {
-        $context = new Context($this->createMock(PsrContext::class));
+        $context = new Context($this->createMock(InteropContext::class));
         $context->setLogger($this->createMock(LoggerInterface::class));
-        $context->setPsrConsumer($this->createMock(PsrConsumer::class));
-        $context->setPsrProcessor($this->createMock(PsrProcessor::class));
+        $context->setConsumer($this->createMock(Consumer::class));
+        $context->setProcessor($this->createMock(Processor::class));
 
         return $context;
     }
diff --git a/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php b/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
index fffeb94..66485dc 100644
--- a/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
@@ -4,9 +4,9 @@
 
 use Enqueue\Consumption\Context;
 use Enqueue\Consumption\Extension\LimitConsumptionTimeExtension;
-use Interop\Queue\PsrConsumer;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Consumer;
+use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 
@@ -110,12 +110,12 @@ public function testOnPostReceivedShouldNotInterruptExecutionIfConsumptionTimeIs
     /**
      * @return Context
      */
-    protected function createContext()
+    protected function createContext(): Context
     {
-        $context = new Context($this->createMock(PsrContext::class));
+        $context = new Context($this->createMock(InteropContext::class));
         $context->setLogger($this->createMock(LoggerInterface::class));
-        $context->setPsrConsumer($this->createMock(PsrConsumer::class));
-        $context->setPsrProcessor($this->createMock(PsrProcessor::class));
+        $context->setConsumer($this->createMock(Consumer::class));
+        $context->setProcessor($this->createMock(Processor::class));
 
         return $context;
     }
diff --git a/Tests/Consumption/Extension/LoggerExtensionTest.php b/Tests/Consumption/Extension/LoggerExtensionTest.php
index 68bc700..6bd05ef 100644
--- a/Tests/Consumption/Extension/LoggerExtensionTest.php
+++ b/Tests/Consumption/Extension/LoggerExtensionTest.php
@@ -8,8 +8,8 @@
 use Enqueue\Consumption\Result;
 use Enqueue\Null\NullMessage;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrConsumer;
-use Interop\Queue\PsrContext;
+use Interop\Queue\Consumer;
+use Interop\Queue\Context as InteropContext;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 
@@ -33,7 +33,7 @@ public function testShouldSetLoggerToContextOnStart()
 
         $extension = new LoggerExtension($logger);
 
-        $context = new Context($this->createPsrContextMock());
+        $context = new Context($this->createContextMock());
 
         $extension->onStart($context);
 
@@ -51,7 +51,7 @@ public function testShouldAddInfoMessageOnStart()
 
         $extension = new LoggerExtension($logger);
 
-        $context = new Context($this->createPsrContextMock());
+        $context = new Context($this->createContextMock());
 
         $extension->onStart($context);
     }
@@ -70,9 +70,9 @@ public function testShouldLogRejectMessageStatus()
         $message = new NullMessage();
         $message->setBody('message body');
 
-        $context = new Context($this->createPsrContextMock());
+        $context = new Context($this->createContextMock());
         $context->setResult(Result::reject('reason'));
-        $context->setPsrMessage($message);
+        $context->setInteropMessage($message);
 
         $extension->onPostReceived($context);
     }
@@ -91,9 +91,9 @@ public function testShouldLogRequeueMessageStatus()
         $message = new NullMessage();
         $message->setBody('message body');
 
-        $context = new Context($this->createPsrContextMock());
+        $context = new Context($this->createContextMock());
         $context->setResult(Result::requeue('reason'));
-        $context->setPsrMessage($message);
+        $context->setInteropMessage($message);
 
         $extension->onPostReceived($context);
     }
@@ -108,7 +108,7 @@ public function testShouldNotLogRequeueMessageStatusIfReasonIsEmpty()
 
         $extension = new LoggerExtension($logger);
 
-        $context = new Context($this->createPsrContextMock());
+        $context = new Context($this->createContextMock());
         $context->setResult(Result::requeue());
 
         $extension->onPostReceived($context);
@@ -128,9 +128,9 @@ public function testShouldLogAckMessageStatus()
         $message = new NullMessage();
         $message->setBody('message body');
 
-        $context = new Context($this->createPsrContextMock());
+        $context = new Context($this->createContextMock());
         $context->setResult(Result::ack('reason'));
-        $context->setPsrMessage($message);
+        $context->setInteropMessage($message);
 
         $extension->onPostReceived($context);
     }
@@ -145,7 +145,7 @@ public function testShouldNotLogAckMessageStatusIfReasonIsEmpty()
 
         $extension = new LoggerExtension($logger);
 
-        $context = new Context($this->createPsrContextMock());
+        $context = new Context($this->createContextMock());
         $context->setResult(Result::ack());
 
         $extension->onPostReceived($context);
@@ -168,18 +168,18 @@ public function testShouldNotSetLoggerIfOneHasBeenSetOnStart()
 
         $extension = new LoggerExtension($logger);
 
-        $context = new Context($this->createPsrContextMock());
+        $context = new Context($this->createContextMock());
         $context->setLogger($alreadySetLogger);
 
         $extension->onStart($context);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext
+     * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
      */
-    protected function createPsrContextMock()
+    protected function createContextMock(): InteropContext
     {
-        return $this->createMock(PsrContext::class);
+        return $this->createMock(InteropContext::class);
     }
 
     /**
@@ -191,10 +191,10 @@ protected function createLogger()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrConsumer
+     * @return \PHPUnit_Framework_MockObject_MockObject|Consumer
      */
     protected function createConsumerMock()
     {
-        return $this->createMock(PsrConsumer::class);
+        return $this->createMock(Consumer::class);
     }
 }
diff --git a/Tests/Consumption/Extension/NicenessExtensionTest.php b/Tests/Consumption/Extension/NicenessExtensionTest.php
index 1cabdaf..36a80d3 100644
--- a/Tests/Consumption/Extension/NicenessExtensionTest.php
+++ b/Tests/Consumption/Extension/NicenessExtensionTest.php
@@ -4,9 +4,9 @@
 
 use Enqueue\Consumption\Context;
 use Enqueue\Consumption\Extension\NicenessExtension;
-use Interop\Queue\PsrConsumer;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Consumer;
+use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 
@@ -35,12 +35,12 @@ public function testShouldThrowWarningOnInvalidArgument()
     /**
      * @return Context
      */
-    protected function createContext()
+    protected function createContext(): Context
     {
-        $context = new Context($this->createMock(PsrContext::class));
+        $context = new Context($this->createMock(InteropContext::class));
         $context->setLogger($this->createMock(LoggerInterface::class));
-        $context->setPsrConsumer($this->createMock(PsrConsumer::class));
-        $context->setPsrProcessor($this->createMock(PsrProcessor::class));
+        $context->setConsumer($this->createMock(Consumer::class));
+        $context->setProcessor($this->createMock(Processor::class));
 
         return $context;
     }
diff --git a/Tests/Consumption/Extension/ReplyExtensionTest.php b/Tests/Consumption/Extension/ReplyExtensionTest.php
index 4912055..09802d3 100644
--- a/Tests/Consumption/Extension/ReplyExtensionTest.php
+++ b/Tests/Consumption/Extension/ReplyExtensionTest.php
@@ -9,8 +9,8 @@
 use Enqueue\Null\NullMessage;
 use Enqueue\Null\NullQueue;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrProducer;
+use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Producer as InteropProducer;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -61,7 +61,7 @@ public function testShouldDoNothingIfReceivedMessageNotHaveReplyToSet()
         $extension = new ReplyExtension();
 
         $context = new Context($this->createNeverUsedContextMock());
-        $context->setPsrMessage(new NullMessage());
+        $context->setInteropMessage(new NullMessage());
 
         $extension->onPostReceived($context);
     }
@@ -74,7 +74,7 @@ public function testShouldDoNothingIfContextResultIsNotInstanceOfResult()
         $message->setReplyTo('aReplyToQueue');
 
         $context = new Context($this->createNeverUsedContextMock());
-        $context->setPsrMessage($message);
+        $context->setInteropMessage($message);
         $context->setResult('notInstanceOfResult');
 
         $extension->onPostReceived($context);
@@ -88,7 +88,7 @@ public function testShouldDoNothingIfResultInstanceOfResultButReplyMessageNotSet
         $message->setReplyTo('aReplyToQueue');
 
         $context = new Context($this->createNeverUsedContextMock());
-        $context->setPsrMessage($message);
+        $context->setInteropMessage($message);
         $context->setResult(Result::ack());
 
         $extension->onPostReceived($context);
@@ -107,15 +107,15 @@ public function testShouldSendReplyMessageToReplyQueueOnPostReceived()
 
         $replyQueue = new NullQueue('aReplyName');
 
-        $producerMock = $this->createMock(PsrProducer::class);
+        $producerMock = $this->createMock(InteropProducer::class);
         $producerMock
             ->expects($this->once())
             ->method('send')
             ->with($replyQueue, $replyMessage)
         ;
 
-        /** @var \PHPUnit_Framework_MockObject_MockObject|PsrContext $contextMock */
-        $contextMock = $this->createMock(PsrContext::class);
+        /** @var \PHPUnit_Framework_MockObject_MockObject|InteropContext $contextMock */
+        $contextMock = $this->createMock(InteropContext::class);
         $contextMock
             ->expects($this->once())
             ->method('createQueue')
@@ -128,7 +128,7 @@ public function testShouldSendReplyMessageToReplyQueueOnPostReceived()
         ;
 
         $context = new Context($contextMock);
-        $context->setPsrMessage($message);
+        $context->setInteropMessage($message);
         $context->setResult(Result::reply($replyMessage));
         $context->setLogger(new NullLogger());
 
@@ -136,11 +136,11 @@ public function testShouldSendReplyMessageToReplyQueueOnPostReceived()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext
+     * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
      */
-    private function createNeverUsedContextMock()
+    private function createNeverUsedContextMock(): InteropContext
     {
-        $contextMock = $this->createMock(PsrContext::class);
+        $contextMock = $this->createMock(InteropContext::class);
         $contextMock
             ->expects($this->never())
             ->method('createProducer')
diff --git a/Tests/Consumption/FallbackSubscriptionConsumerTest.php b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
index b054180..95a9612 100644
--- a/Tests/Consumption/FallbackSubscriptionConsumerTest.php
+++ b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
@@ -3,8 +3,8 @@
 namespace Enqueue\Tests\Consumption;
 
 use Enqueue\Consumption\FallbackSubscriptionConsumer;
-use Interop\Queue\PsrConsumer;
-use Interop\Queue\PsrMessage;
+use Interop\Queue\Consumer;
+use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\PsrQueue;
 use Interop\Queue\PsrSubscriptionConsumer;
 
@@ -176,7 +176,7 @@ public function testShouldConsumeMessagesFromTwoQueuesInExpectedOrder()
         ;
 
         $actualOrder = [];
-        $callback = function (PsrMessage $message, PsrConsumer $consumer) use (&$actualOrder) {
+        $callback = function (InteropMessage $message, Consumer $consumer) use (&$actualOrder) {
             $actualOrder[] = [$message->getBody(), $consumer->getQueue()->getQueueName()];
         };
 
@@ -228,11 +228,11 @@ public function testShouldConsumeTillTimeoutIsReached()
     /**
      * @param null|mixed $body
      *
-     * @return PsrMessage|\PHPUnit_Framework_MockObject_MockObject
+     * @return InteropMessage|\PHPUnit_Framework_MockObject_MockObject
      */
     private function createMessageStub($body = null)
     {
-        $messageMock = $this->createMock(PsrMessage::class);
+        $messageMock = $this->createMock(InteropMessage::class);
         $messageMock
             ->expects($this->any())
             ->method('getBody')
@@ -245,7 +245,7 @@ private function createMessageStub($body = null)
     /**
      * @param null|mixed $queueName
      *
-     * @return PsrConsumer|\PHPUnit_Framework_MockObject_MockObject
+     * @return Consumer|\PHPUnit_Framework_MockObject_MockObject
      */
     private function createConsumerStub($queueName = null)
     {
@@ -255,7 +255,7 @@ private function createConsumerStub($queueName = null)
             ->method('getQueueName')
             ->willReturn($queueName);
 
-        $consumerMock = $this->createMock(PsrConsumer::class);
+        $consumerMock = $this->createMock(Consumer::class);
         $consumerMock
             ->expects($this->any())
             ->method('getQueue')
diff --git a/Tests/Consumption/Mock/DummySubscriptionConsumer.php b/Tests/Consumption/Mock/DummySubscriptionConsumer.php
index f832284..41a1f60 100644
--- a/Tests/Consumption/Mock/DummySubscriptionConsumer.php
+++ b/Tests/Consumption/Mock/DummySubscriptionConsumer.php
@@ -2,8 +2,8 @@
 
 namespace Enqueue\Tests\Consumption\Mock;
 
-use Interop\Queue\PsrConsumer;
-use Interop\Queue\PsrMessage;
+use Interop\Queue\Consumer;
+use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\PsrSubscriptionConsumer;
 
 class DummySubscriptionConsumer implements PsrSubscriptionConsumer
@@ -18,7 +18,7 @@ class DummySubscriptionConsumer implements PsrSubscriptionConsumer
     public function consume(int $timeout = 0): void
     {
         foreach ($this->messages as list($message, $queueName)) {
-            /** @var PsrMessage $message */
+            /** @var InteropMessage $message */
             /** @var string $queueName */
             if (false == call_user_func($this->subscriptions[$queueName][1], $message, $this->subscriptions[$queueName][0])) {
                 return;
@@ -26,12 +26,12 @@ public function consume(int $timeout = 0): void
         }
     }
 
-    public function subscribe(PsrConsumer $consumer, callable $callback): void
+    public function subscribe(Consumer $consumer, callable $callback): void
     {
         $this->subscriptions[$consumer->getQueue()->getQueueName()] = [$consumer, $callback];
     }
 
-    public function unsubscribe(PsrConsumer $consumer): void
+    public function unsubscribe(Consumer $consumer): void
     {
         unset($this->subscriptions[$consumer->getQueue()->getQueueName()]);
     }
@@ -41,7 +41,7 @@ public function unsubscribeAll(): void
         $this->subscriptions = [];
     }
 
-    public function addMessage(PsrMessage $message, string $queueName): void
+    public function addMessage(InteropMessage $message, string $queueName): void
     {
         $this->messages[] = [$message, $queueName];
     }
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 9a99606..1f63674 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -12,13 +12,13 @@
 use Enqueue\Null\NullQueue;
 use Enqueue\Tests\Consumption\Mock\BreakCycleExtension;
 use Enqueue\Tests\Consumption\Mock\DummySubscriptionConsumer;
-use Interop\Queue\PsrConsumer;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProcessor;
-use Interop\Queue\PsrQueue;
-use Interop\Queue\PsrSubscriptionConsumer;
-use Interop\Queue\SubscriptionConsumerNotSupportedException;
+use Interop\Queue\Consumer;
+use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Exception\SubscriptionConsumerNotSupportedException;
+use Interop\Queue\Message;
+use Interop\Queue\Processor;
+use Interop\Queue\Queue;
+use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -26,40 +26,40 @@ class QueueConsumerTest extends TestCase
 {
     public function testCouldBeConstructedWithConnectionAndExtensionsAsArguments()
     {
-        new QueueConsumer($this->createPsrContextStub(), null, 0);
+        new QueueConsumer($this->createContextStub(), null, 0);
     }
 
     public function testCouldBeConstructedWithConnectionOnly()
     {
-        new QueueConsumer($this->createPsrContextStub());
+        new QueueConsumer($this->createContextStub());
     }
 
     public function testCouldBeConstructedWithConnectionAndSingleExtension()
     {
-        new QueueConsumer($this->createPsrContextStub(), $this->createExtension());
+        new QueueConsumer($this->createContextStub(), $this->createExtension());
     }
 
     public function testShouldSetEmptyArrayToBoundProcessorsPropertyInConstructor()
     {
-        $consumer = new QueueConsumer($this->createPsrContextStub(), null, 0);
+        $consumer = new QueueConsumer($this->createContextStub(), null, 0);
 
         $this->assertAttributeSame([], 'boundProcessors', $consumer);
     }
 
     public function testShouldAllowGetConnectionSetInConstructor()
     {
-        $expectedConnection = $this->createPsrContextStub();
+        $expectedConnection = $this->createContextStub();
 
         $consumer = new QueueConsumer($expectedConnection, null, 0);
 
-        $this->assertSame($expectedConnection, $consumer->getPsrContext());
+        $this->assertSame($expectedConnection, $consumer->getContext());
     }
 
     public function testThrowIfQueueNameEmptyOnBind()
     {
         $processorMock = $this->createProcessorMock();
 
-        $consumer = new QueueConsumer($this->createPsrContextStub(), null, 0);
+        $consumer = new QueueConsumer($this->createContextStub(), null, 0);
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('The queue name must be not empty.');
@@ -70,7 +70,7 @@ public function testThrowIfQueueAlreadyBoundToProcessorOnBind()
     {
         $processorMock = $this->createProcessorMock();
 
-        $consumer = new QueueConsumer($this->createPsrContextStub(), null, 0);
+        $consumer = new QueueConsumer($this->createContextStub(), null, 0);
 
         $consumer->bind(new NullQueue('theQueueName'), $processorMock);
 
@@ -84,7 +84,7 @@ public function testShouldAllowBindProcessorToQueue()
         $queue = new NullQueue('theQueueName');
         $processorMock = $this->createProcessorMock();
 
-        $consumer = new QueueConsumer($this->createPsrContextStub(), null, 0);
+        $consumer = new QueueConsumer($this->createContextStub(), null, 0);
 
         $consumer->bind($queue, $processorMock);
 
@@ -95,7 +95,7 @@ public function testThrowIfQueueNeitherInstanceOfQueueNorString()
     {
         $processorMock = $this->createProcessorMock();
 
-        $consumer = new QueueConsumer($this->createPsrContextStub(), null, 0);
+        $consumer = new QueueConsumer($this->createContextStub(), null, 0);
 
         $this->expectException(InvalidArgumentException::class);
         $this->expectExceptionMessage('The argument must be an instance of Interop\Queue\PsrQueue but got stdClass.');
@@ -104,7 +104,7 @@ public function testThrowIfQueueNeitherInstanceOfQueueNorString()
 
     public function testCouldSetGetIdleTimeout()
     {
-        $consumer = new QueueConsumer($this->createPsrContextStub(), null, 0);
+        $consumer = new QueueConsumer($this->createContextStub(), null, 0);
 
         $consumer->setIdleTimeout(123456.1);
 
@@ -113,7 +113,7 @@ public function testCouldSetGetIdleTimeout()
 
     public function testCouldSetGetReceiveTimeout()
     {
-        $consumer = new QueueConsumer($this->createPsrContextStub(), null, 0);
+        $consumer = new QueueConsumer($this->createContextStub(), null, 0);
 
         $consumer->setReceiveTimeout(123456.1);
 
@@ -156,7 +156,7 @@ public function testShouldReturnSelfOnBind()
     {
         $processorMock = $this->createProcessorMock();
 
-        $consumer = new QueueConsumer($this->createPsrContextStub(), null, 0);
+        $consumer = new QueueConsumer($this->createContextStub(), null, 0);
 
         $this->assertSame($consumer, $consumer->bind(new NullQueue('foo_queue'), $processorMock));
     }
@@ -178,7 +178,7 @@ public function testShouldUseContextSubscriptionConsumerIfSupport()
             ->method('consume')
         ;
 
-        $contextMock = $this->createMock(PsrContext::class);
+        $contextMock = $this->createMock(InteropContext::class);
         $contextMock
             ->expects($this->once())
             ->method('createConsumer')
@@ -325,7 +325,7 @@ public function testShouldProcessFiveMessagesAndQuit()
         $subscriptionConsumerMock->addMessage($fourthMessageMock, 'foo_queue');
         $subscriptionConsumerMock->addMessage($fifthMessageMock, 'foo_queue');
 
-        $contextStub = $this->createPsrContextStub();
+        $contextStub = $this->createContextStub();
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -355,7 +355,7 @@ public function testShouldAckMessageIfProcessorReturnSuchStatus()
             ->with($this->identicalTo($messageMock))
         ;
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -381,7 +381,7 @@ public function testThrowIfProcessorReturnNull()
 
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -414,7 +414,7 @@ public function testShouldRejectMessageIfProcessorReturnSuchStatus()
             ->with($this->identicalTo($messageMock), false)
         ;
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -445,7 +445,7 @@ public function testShouldRequeueMessageIfProcessorReturnSuchStatus()
             ->with($this->identicalTo($messageMock), true)
         ;
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -471,7 +471,7 @@ public function testThrowIfProcessorReturnInvalidStatus()
 
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -509,7 +509,7 @@ public function testShouldNotPassMessageToProcessorIfItWasProcessedByExtension()
 
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -529,7 +529,7 @@ public function testShouldCallOnStartExtensionMethod()
     {
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
 
@@ -539,11 +539,11 @@ public function testShouldCallOnStartExtensionMethod()
             ->method('onStart')
             ->with($this->isInstanceOf(Context::class))
             ->willReturnCallback(function (Context $context) use ($contextStub) {
-                $this->assertSame($contextStub, $context->getPsrContext());
-                $this->assertNull($context->getPsrConsumer());
-                $this->assertNull($context->getPsrProcessor());
+                $this->assertSame($contextStub, $context->getContext());
+                $this->assertNull($context->getConsumer());
+                $this->assertNull($context->getProcessor());
                 $this->assertNull($context->getLogger());
-                $this->assertNull($context->getPsrMessage());
+                $this->assertNull($context->getInteropMessage());
                 $this->assertNull($context->getException());
                 $this->assertNull($context->getResult());
                 $this->assertNull($context->getPsrQueue());
@@ -563,7 +563,7 @@ public function testShouldCallOnIdleExtensionMethod()
     {
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
 
@@ -573,11 +573,11 @@ public function testShouldCallOnIdleExtensionMethod()
             ->method('onIdle')
             ->with($this->isInstanceOf(Context::class))
             ->willReturnCallback(function (Context $context) use ($contextStub) {
-                $this->assertSame($contextStub, $context->getPsrContext());
+                $this->assertSame($contextStub, $context->getContext());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
-                $this->assertNull($context->getPsrProcessor());
-                $this->assertNull($context->getPsrConsumer());
-                $this->assertNull($context->getPsrMessage());
+                $this->assertNull($context->getProcessor());
+                $this->assertNull($context->getConsumer());
+                $this->assertNull($context->getInteropMessage());
                 $this->assertNull($context->getException());
                 $this->assertNull($context->getResult());
                 $this->assertFalse($context->isExecutionInterrupted());
@@ -596,7 +596,7 @@ public function testShouldCallOnBeforeReceiveExtensionMethod()
     {
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorStub();
 
@@ -608,11 +608,11 @@ public function testShouldCallOnBeforeReceiveExtensionMethod()
             ->method('onBeforeReceive')
             ->with($this->isInstanceOf(Context::class))
             ->willReturnCallback(function (Context $context) use ($contextStub) {
-                $this->assertSame($contextStub, $context->getPsrContext());
+                $this->assertSame($contextStub, $context->getContext());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
-                $this->assertNull($context->getPsrProcessor());
-                $this->assertNull($context->getPsrConsumer());
-                $this->assertNull($context->getPsrMessage());
+                $this->assertNull($context->getProcessor());
+                $this->assertNull($context->getConsumer());
+                $this->assertNull($context->getInteropMessage());
                 $this->assertNull($context->getException());
                 $this->assertNull($context->getResult());
                 $this->assertNull($context->getPsrQueue());
@@ -637,7 +637,7 @@ public function testShouldCallOnPreReceivedExtensionMethodWithExpectedContext()
 
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorStub();
 
@@ -652,10 +652,10 @@ public function testShouldCallOnPreReceivedExtensionMethodWithExpectedContext()
                 $processorMock,
                 $expectedMessage
             ) {
-                $this->assertSame($contextStub, $context->getPsrContext());
-                $this->assertSame($consumerStub, $context->getPsrConsumer());
-                $this->assertSame($processorMock, $context->getPsrProcessor());
-                $this->assertSame($expectedMessage, $context->getPsrMessage());
+                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($consumerStub, $context->getConsumer());
+                $this->assertSame($processorMock, $context->getProcessor());
+                $this->assertSame($expectedMessage, $context->getInteropMessage());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
                 $this->assertNull($context->getException());
                 $this->assertNull($context->getResult());
@@ -680,7 +680,7 @@ public function testShouldCallOnResultExtensionMethodWithExpectedContext()
 
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorStub();
 
@@ -695,10 +695,10 @@ public function testShouldCallOnResultExtensionMethodWithExpectedContext()
                 $processorMock,
                 $expectedMessage
             ) {
-                $this->assertSame($contextStub, $context->getPsrContext());
-                $this->assertSame($consumerStub, $context->getPsrConsumer());
-                $this->assertSame($processorMock, $context->getPsrProcessor());
-                $this->assertSame($expectedMessage, $context->getPsrMessage());
+                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($consumerStub, $context->getConsumer());
+                $this->assertSame($processorMock, $context->getProcessor());
+                $this->assertSame($expectedMessage, $context->getInteropMessage());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
                 $this->assertNull($context->getException());
                 $this->assertSame(Result::ACK, $context->getResult());
@@ -723,7 +723,7 @@ public function testShouldCallOnPostReceivedExtensionMethodWithExpectedContext()
 
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorStub();
 
@@ -738,10 +738,10 @@ public function testShouldCallOnPostReceivedExtensionMethodWithExpectedContext()
                 $processorMock,
                 $expectedMessage
             ) {
-                $this->assertSame($contextStub, $context->getPsrContext());
-                $this->assertSame($consumerStub, $context->getPsrConsumer());
-                $this->assertSame($processorMock, $context->getPsrProcessor());
-                $this->assertSame($expectedMessage, $context->getPsrMessage());
+                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($consumerStub, $context->getConsumer());
+                $this->assertSame($processorMock, $context->getProcessor());
+                $this->assertSame($expectedMessage, $context->getInteropMessage());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
                 $this->assertNull($context->getException());
                 $this->assertSame(Result::ACK, $context->getResult());
@@ -761,7 +761,7 @@ public function testShouldAllowInterruptConsumingOnIdle()
     {
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
 
@@ -779,11 +779,11 @@ public function testShouldAllowInterruptConsumingOnIdle()
             ->method('onInterrupted')
             ->with($this->isInstanceOf(Context::class))
             ->willReturnCallback(function (Context $context) use ($contextStub) {
-                $this->assertSame($contextStub, $context->getPsrContext());
+                $this->assertSame($contextStub, $context->getContext());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
-                $this->assertNull($context->getPsrConsumer());
-                $this->assertNull($context->getPsrProcessor());
-                $this->assertNull($context->getPsrMessage());
+                $this->assertNull($context->getConsumer());
+                $this->assertNull($context->getProcessor());
+                $this->assertNull($context->getInteropMessage());
                 $this->assertNull($context->getException());
                 $this->assertNull($context->getResult());
                 $this->assertTrue($context->isExecutionInterrupted());
@@ -802,7 +802,7 @@ public function testShouldNotCloseContextWhenConsumptionInterrupted()
     {
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
         $contextStub
             ->expects($this->never())
             ->method('close')
@@ -837,7 +837,7 @@ public function testShouldNotCloseContextWhenConsumptionInterruptedByException()
 
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
         $contextStub
             ->expects($this->never())
             ->method('close')
@@ -876,7 +876,7 @@ public function testShouldSetMainExceptionAsPreviousToExceptionThrownOnInterrupt
 
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -918,7 +918,7 @@ public function testShouldAllowInterruptConsumingOnPreReceiveButProcessCurrentMe
 
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -946,10 +946,10 @@ public function testShouldAllowInterruptConsumingOnPreReceiveButProcessCurrentMe
                 $processorMock,
                 $expectedMessage
             ) {
-                $this->assertSame($contextStub, $context->getPsrContext());
-                $this->assertSame($consumerStub, $context->getPsrConsumer());
-                $this->assertSame($processorMock, $context->getPsrProcessor());
-                $this->assertSame($expectedMessage, $context->getPsrMessage());
+                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($consumerStub, $context->getConsumer());
+                $this->assertSame($processorMock, $context->getProcessor());
+                $this->assertSame($expectedMessage, $context->getInteropMessage());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
                 $this->assertNull($context->getException());
                 $this->assertSame(Result::ACK, $context->getResult());
@@ -974,7 +974,7 @@ public function testShouldAllowInterruptConsumingOnResult()
 
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -1002,10 +1002,10 @@ public function testShouldAllowInterruptConsumingOnResult()
                 $processorMock,
                 $expectedMessage
             ) {
-                $this->assertSame($contextStub, $context->getPsrContext());
-                $this->assertSame($consumerStub, $context->getPsrConsumer());
-                $this->assertSame($processorMock, $context->getPsrProcessor());
-                $this->assertSame($expectedMessage, $context->getPsrMessage());
+                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($consumerStub, $context->getConsumer());
+                $this->assertSame($processorMock, $context->getProcessor());
+                $this->assertSame($expectedMessage, $context->getInteropMessage());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
                 $this->assertNull($context->getException());
                 $this->assertSame(Result::ACK, $context->getResult());
@@ -1030,7 +1030,7 @@ public function testShouldAllowInterruptConsumingOnPostReceive()
 
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -1058,10 +1058,10 @@ public function testShouldAllowInterruptConsumingOnPostReceive()
                 $processorMock,
                 $expectedMessage
             ) {
-                $this->assertSame($contextStub, $context->getPsrContext());
-                $this->assertSame($consumerStub, $context->getPsrConsumer());
-                $this->assertSame($processorMock, $context->getPsrProcessor());
-                $this->assertSame($expectedMessage, $context->getPsrMessage());
+                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($consumerStub, $context->getConsumer());
+                $this->assertSame($processorMock, $context->getProcessor());
+                $this->assertSame($expectedMessage, $context->getInteropMessage());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
                 $this->assertNull($context->getException());
                 $this->assertSame(Result::ACK, $context->getResult());
@@ -1087,7 +1087,7 @@ public function testShouldCallOnInterruptedIfExceptionThrow()
 
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -1108,10 +1108,10 @@ public function testShouldCallOnInterruptedIfExceptionThrow()
                 $expectedMessage,
                 $expectedException
             ) {
-                $this->assertSame($contextStub, $context->getPsrContext());
-                $this->assertSame($consumerStub, $context->getPsrConsumer());
-                $this->assertSame($processorMock, $context->getPsrProcessor());
-                $this->assertSame($expectedMessage, $context->getPsrMessage());
+                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($consumerStub, $context->getConsumer());
+                $this->assertSame($processorMock, $context->getProcessor());
+                $this->assertSame($expectedMessage, $context->getInteropMessage());
                 $this->assertSame($expectedException, $context->getException());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
                 $this->assertNull($context->getResult());
@@ -1138,7 +1138,7 @@ public function testShouldCallExtensionPassedOnRuntime()
 
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -1190,7 +1190,7 @@ public function testShouldChangeLoggerOnStart()
 
         $consumerStub = $this->createConsumerStub('foo_queue');
 
-        $contextStub = $this->createPsrContextStub($consumerStub);
+        $contextStub = $this->createContextStub($consumerStub);
 
         $processorMock = $this->createProcessorMock();
         $processorMock
@@ -1269,7 +1269,7 @@ public function testShouldCallProcessorAsMessageComeAlong()
         $contextStub
             ->expects($this->any())
             ->method('createConsumer')
-            ->willReturnCallback(function (PsrQueue $queue) use ($consumers) {
+            ->willReturnCallback(function (Queue $queue) use ($consumers) {
                 return $consumers[$queue->getQueueName()];
             })
         ;
@@ -1301,21 +1301,21 @@ public function testShouldCallProcessorAsMessageComeAlong()
 
         $this->assertCount(3, $actualContexts);
 
-        $this->assertSame($firstMessage, $actualContexts[0]->getPsrMessage());
-        $this->assertSame($secondMessage, $actualContexts[1]->getPsrMessage());
-        $this->assertSame($thirdMessage, $actualContexts[2]->getPsrMessage());
+        $this->assertSame($firstMessage, $actualContexts[0]->getInteropMessage());
+        $this->assertSame($secondMessage, $actualContexts[1]->getInteropMessage());
+        $this->assertSame($thirdMessage, $actualContexts[2]->getInteropMessage());
 
-        $this->assertSame($fooConsumerStub, $actualContexts[0]->getPsrConsumer());
-        $this->assertSame($barConsumerStub, $actualContexts[1]->getPsrConsumer());
-        $this->assertSame($fooConsumerStub, $actualContexts[2]->getPsrConsumer());
+        $this->assertSame($fooConsumerStub, $actualContexts[0]->getConsumer());
+        $this->assertSame($barConsumerStub, $actualContexts[1]->getConsumer());
+        $this->assertSame($fooConsumerStub, $actualContexts[2]->getConsumer());
     }
 
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    private function createContextWithoutSubscriptionConsumerMock(): PsrContext
+    private function createContextWithoutSubscriptionConsumerMock(): InteropContext
     {
-        $contextMock = $this->createMock(PsrContext::class);
+        $contextMock = $this->createMock(InteropContext::class);
         $contextMock
             ->expects($this->any())
             ->method('createSubscriptionConsumer')
@@ -1326,9 +1326,9 @@ private function createContextWithoutSubscriptionConsumerMock(): PsrContext
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext
+     * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
      */
-    private function createPsrContextStub(PsrConsumer $consumer = null)
+    private function createContextStub(Consumer $consumer = null): InteropContext
     {
         $context = $this->createContextWithoutSubscriptionConsumerMock();
         $context
@@ -1341,7 +1341,7 @@ private function createPsrContextStub(PsrConsumer $consumer = null)
         $context
             ->expects($this->any())
             ->method('createConsumer')
-            ->willReturnCallback(function (PsrQueue $queue) use ($consumer) {
+            ->willReturnCallback(function (Queue $queue) use ($consumer) {
                 return $consumer ?: $this->createConsumerStub($queue);
             })
         ;
@@ -1355,15 +1355,15 @@ private function createPsrContextStub(PsrConsumer $consumer = null)
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProcessor
+     * @return \PHPUnit_Framework_MockObject_MockObject|Processor
      */
     private function createProcessorMock()
     {
-        return $this->createMock(PsrProcessor::class);
+        return $this->createMock(Processor::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProcessor
+     * @return \PHPUnit_Framework_MockObject_MockObject|Processor
      */
     private function createProcessorStub()
     {
@@ -1378,11 +1378,11 @@ private function createProcessorStub()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrMessage
+     * @return \PHPUnit_Framework_MockObject_MockObject|Message
      */
-    private function createMessageMock()
+    private function createMessageMock(): Message
     {
-        return $this->createMock(PsrMessage::class);
+        return $this->createMock(Message::class);
     }
 
     /**
@@ -1396,15 +1396,15 @@ private function createExtension()
     /**
      * @param null|mixed $queue
      *
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrConsumer
+     * @return \PHPUnit_Framework_MockObject_MockObject|Consumer
      */
-    private function createConsumerStub($queue = null)
+    private function createConsumerStub($queue = null): Consumer
     {
         if (is_string($queue)) {
             $queue = new NullQueue($queue);
         }
 
-        $consumerMock = $this->createMock(PsrConsumer::class);
+        $consumerMock = $this->createMock(Consumer::class);
         $consumerMock
             ->expects($this->any())
             ->method('getQueue')
@@ -1415,10 +1415,10 @@ private function createConsumerStub($queue = null)
     }
 
     /**
-     * @return PsrSubscriptionConsumer|\PHPUnit_Framework_MockObject_MockObject
+     * @return SubscriptionConsumer|\PHPUnit_Framework_MockObject_MockObject
      */
-    private function createSubscriptionConsumerMock()
+    private function createSubscriptionConsumerMock(): SubscriptionConsumer
     {
-        return $this->createMock(PsrSubscriptionConsumer::class);
+        return $this->createMock(SubscriptionConsumer::class);
     }
 }
diff --git a/Tests/Router/RecipientTest.php b/Tests/Router/RecipientTest.php
index 3f37372..57cc83e 100644
--- a/Tests/Router/RecipientTest.php
+++ b/Tests/Router/RecipientTest.php
@@ -3,26 +3,26 @@
 namespace Enqueue\Tests\Router;
 
 use Enqueue\Router\Recipient;
-use Interop\Queue\PsrDestination;
-use Interop\Queue\PsrMessage;
+use Interop\Queue\Destination;
+use Interop\Queue\Message as InteropMessage;
 use PHPUnit\Framework\TestCase;
 
 class RecipientTest extends TestCase
 {
     public function testShouldAllowGetMessageSetInConstructor()
     {
-        $message = $this->createMock(PsrMessage::class);
+        $message = $this->createMock(InteropMessage::class);
 
-        $recipient = new Recipient($this->createMock(PsrDestination::class), $message);
+        $recipient = new Recipient($this->createMock(Destination::class), $message);
 
         $this->assertSame($message, $recipient->getMessage());
     }
 
     public function testShouldAllowGetDestinationSetInConstructor()
     {
-        $destination = $this->createMock(PsrDestination::class);
+        $destination = $this->createMock(Destination::class);
 
-        $recipient = new Recipient($destination, $this->createMock(PsrMessage::class));
+        $recipient = new Recipient($destination, $this->createMock(InteropMessage::class));
 
         $this->assertSame($destination, $recipient->getDestination());
     }
diff --git a/Tests/Router/RouteRecipientListProcessorTest.php b/Tests/Router/RouteRecipientListProcessorTest.php
index b798043..38e0580 100644
--- a/Tests/Router/RouteRecipientListProcessorTest.php
+++ b/Tests/Router/RouteRecipientListProcessorTest.php
@@ -9,9 +9,9 @@
 use Enqueue\Router\RecipientListRouterInterface;
 use Enqueue\Router\RouteRecipientListProcessor;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrProcessor;
-use Interop\Queue\PsrProducer;
+use Interop\Queue\Context;
+use Interop\Queue\Processor;
+use Interop\Queue\Producer as InteropProducer;
 use PHPUnit\Framework\TestCase;
 
 class RouteRecipientListProcessorTest extends TestCase
@@ -20,7 +20,7 @@ class RouteRecipientListProcessorTest extends TestCase
 
     public function testShouldImplementProcessorInterface()
     {
-        $this->assertClassImplements(PsrProcessor::class, RouteRecipientListProcessor::class);
+        $this->assertClassImplements(Processor::class, RouteRecipientListProcessor::class);
     }
 
     public function testCouldBeConstructedWithRouterAsFirstArgument()
@@ -55,7 +55,7 @@ public function testShouldProduceRecipientsMessagesAndAckOriginalMessage()
             ->with($this->identicalTo($barRecipient->getDestination()), $this->identicalTo($barRecipient->getMessage()))
         ;
 
-        $sessionMock = $this->createPsrContextMock();
+        $sessionMock = $this->createContextMock();
         $sessionMock
             ->expects($this->once())
             ->method('createProducer')
@@ -70,19 +70,19 @@ public function testShouldProduceRecipientsMessagesAndAckOriginalMessage()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProducer
+     * @return \PHPUnit_Framework_MockObject_MockObject|InteropProducer
      */
     protected function createProducerMock()
     {
-        return $this->createMock(PsrProducer::class);
+        return $this->createMock(InteropProducer::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext
+     * @return \PHPUnit_Framework_MockObject_MockObject|Context
      */
-    protected function createPsrContextMock()
+    protected function createContextMock()
     {
-        return $this->createMock(PsrContext::class);
+        return $this->createMock(Context::class);
     }
 
     /**
diff --git a/Tests/Rpc/PromiseTest.php b/Tests/Rpc/PromiseTest.php
index 7202e67..2755ef4 100644
--- a/Tests/Rpc/PromiseTest.php
+++ b/Tests/Rpc/PromiseTest.php
@@ -122,7 +122,7 @@ public function testOnReceiveShouldThrowExceptionIfCallbackReturnNotMessageInsta
         $promise = new Promise($receivecb, function () {}, function () {});
 
         $this->expectException(\RuntimeException::class);
-        $this->expectExceptionMessage('Expected "Interop\Queue\PsrMessage" but got: "stdClass"');
+        $this->expectExceptionMessage('Expected "Interop\Queue\Message" but got: "stdClass"');
 
         $promise->receive();
     }
@@ -136,7 +136,7 @@ public function testOnReceiveNoWaitShouldThrowExceptionIfCallbackReturnNotMessag
         $promise = new Promise(function () {}, $receivecb, function () {});
 
         $this->expectException(\RuntimeException::class);
-        $this->expectExceptionMessage('Expected "Interop\Queue\PsrMessage" but got: "stdClass"');
+        $this->expectExceptionMessage('Expected "Interop\Queue\Message" but got: "stdClass"');
 
         $promise->receiveNoWait();
     }
diff --git a/Tests/Rpc/RpcClientTest.php b/Tests/Rpc/RpcClientTest.php
index ce7aa27..78f72bb 100644
--- a/Tests/Rpc/RpcClientTest.php
+++ b/Tests/Rpc/RpcClientTest.php
@@ -7,16 +7,16 @@
 use Enqueue\Null\NullQueue;
 use Enqueue\Rpc\Promise;
 use Enqueue\Rpc\RpcClient;
-use Interop\Queue\PsrConsumer;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrProducer;
+use Interop\Queue\Consumer;
+use Interop\Queue\Context;
+use Interop\Queue\Producer as InteropProducer;
 use PHPUnit\Framework\TestCase;
 
 class RpcClientTest extends TestCase
 {
-    public function testCouldBeConstructedWithPsrContextAsFirstArgument()
+    public function testCouldBeConstructedWithContextAsFirstArgument()
     {
-        new RpcClient($this->createPsrContextMock());
+        new RpcClient($this->createContextMock());
     }
 
     public function testShouldSetReplyToIfNotSet()
@@ -80,14 +80,14 @@ public function testShouldProduceMessageToQueue()
         $message->setCorrelationId('theCorrelationId');
         $message->setReplyTo('theReplyTo');
 
-        $producer = $this->createPsrProducerMock();
+        $producer = $this->createInteropProducerMock();
         $producer
             ->expects($this->once())
             ->method('send')
             ->with($this->identicalTo($queue), $this->identicalTo($message))
         ;
 
-        $context = $this->createPsrContextMock();
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
             ->method('createProducer')
@@ -110,7 +110,7 @@ public function testShouldReceiveMessageAndAckMessageIfCorrelationEquals()
         $receivedMessage = new NullMessage();
         $receivedMessage->setCorrelationId('theCorrelationId');
 
-        $consumer = $this->createPsrConsumerMock();
+        $consumer = $this->createConsumerMock();
         $consumer
             ->expects($this->once())
             ->method('receive')
@@ -127,11 +127,11 @@ public function testShouldReceiveMessageAndAckMessageIfCorrelationEquals()
             ->method('reject')
         ;
 
-        $context = $this->createPsrContextMock();
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
             ->method('createProducer')
-            ->willReturn($this->createPsrProducerMock())
+            ->willReturn($this->createInteropProducerMock())
         ;
         $context
             ->expects($this->atLeastOnce())
@@ -162,7 +162,7 @@ public function testShouldReceiveNoWaitMessageAndAckMessageIfCorrelationEquals()
         $receivedMessage = new NullMessage();
         $receivedMessage->setCorrelationId('theCorrelationId');
 
-        $consumer = $this->createPsrConsumerMock();
+        $consumer = $this->createConsumerMock();
         $consumer
             ->expects($this->once())
             ->method('receiveNoWait')
@@ -178,11 +178,11 @@ public function testShouldReceiveNoWaitMessageAndAckMessageIfCorrelationEquals()
             ->method('reject')
         ;
 
-        $context = $this->createPsrContextMock();
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
             ->method('createProducer')
-            ->willReturn($this->createPsrProducerMock())
+            ->willReturn($this->createInteropProducerMock())
         ;
         $context
             ->expects($this->atLeastOnce())
@@ -213,14 +213,14 @@ public function testShouldDeleteQueueAfterReceiveIfDeleteReplyQueueIsTrue()
         $receivedMessage = new NullMessage();
         $receivedMessage->setCorrelationId('theCorrelationId');
 
-        $consumer = $this->createPsrConsumerMock();
+        $consumer = $this->createConsumerMock();
         $consumer
             ->expects($this->once())
             ->method('receive')
             ->willReturn($receivedMessage)
         ;
 
-        $context = $this->getMockBuilder(PsrContext::class)
+        $context = $this->getMockBuilder(Context::class)
             ->disableOriginalConstructor()
             ->setMethods(['deleteQueue'])
             ->getMockForAbstractClass()
@@ -229,7 +229,7 @@ public function testShouldDeleteQueueAfterReceiveIfDeleteReplyQueueIsTrue()
         $context
             ->expects($this->once())
             ->method('createProducer')
-            ->willReturn($this->createPsrProducerMock())
+            ->willReturn($this->createInteropProducerMock())
         ;
         $context
             ->expects($this->atLeastOnce())
@@ -267,18 +267,18 @@ public function testShouldNotCallDeleteQueueIfDeleteReplyQueueIsTrueButContextHa
         $receivedMessage = new NullMessage();
         $receivedMessage->setCorrelationId('theCorrelationId');
 
-        $consumer = $this->createPsrConsumerMock();
+        $consumer = $this->createConsumerMock();
         $consumer
             ->expects($this->once())
             ->method('receive')
             ->willReturn($receivedMessage)
         ;
 
-        $context = $this->createPsrContextMock();
+        $context = $this->createContextMock();
         $context
             ->expects($this->once())
             ->method('createProducer')
-            ->willReturn($this->createPsrProducerMock())
+            ->willReturn($this->createInteropProducerMock())
         ;
         $context
             ->expects($this->once())
@@ -329,26 +329,26 @@ public function testShouldDoSyncCall()
     }
 
     /**
-     * @return PsrContext|\PHPUnit_Framework_MockObject_MockObject|PsrProducer
+     * @return Context|\PHPUnit_Framework_MockObject_MockObject|InteropProducer
      */
-    private function createPsrProducerMock()
+    private function createInteropProducerMock()
     {
-        return $this->createMock(PsrProducer::class);
+        return $this->createMock(InteropProducer::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrConsumer
+     * @return \PHPUnit_Framework_MockObject_MockObject|Consumer
      */
-    private function createPsrConsumerMock()
+    private function createConsumerMock()
     {
-        return $this->createMock(PsrConsumer::class);
+        return $this->createMock(Consumer::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext
+     * @return \PHPUnit_Framework_MockObject_MockObject|Context
      */
-    private function createPsrContextMock()
+    private function createContextMock()
     {
-        return $this->createMock(PsrContext::class);
+        return $this->createMock(Context::class);
     }
 }
diff --git a/Tests/Symfony/Client/ConsumeMessagesCommandTest.php b/Tests/Symfony/Client/ConsumeMessagesCommandTest.php
index 69af29f..a33905a 100644
--- a/Tests/Symfony/Client/ConsumeMessagesCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeMessagesCommandTest.php
@@ -11,7 +11,7 @@
 use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Null\NullQueue;
 use Enqueue\Symfony\Client\ConsumeMessagesCommand;
-use Interop\Queue\PsrContext;
+use Interop\Queue\Context;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\Console\Tester\CommandTester;
 
@@ -91,7 +91,7 @@ public function testShouldBindDefaultQueueOnly()
 
         $processor = $this->createDelegateProcessorMock();
 
-        $context = $this->createPsrContextMock();
+        $context = $this->createContextMock();
         $context
             ->expects($this->never())
             ->method('close')
@@ -133,7 +133,7 @@ public function testShouldBindDefaultQueueIfRouteDoesNotDefineQueue()
 
         $processor = $this->createDelegateProcessorMock();
 
-        $context = $this->createPsrContextMock();
+        $context = $this->createContextMock();
         $context
             ->expects($this->never())
             ->method('close')
@@ -309,7 +309,7 @@ public function testShouldSkipQueueConsumptionAndUseCustomClientDestinationName(
 
         $processor = $this->createDelegateProcessorMock();
 
-        $context = $this->createPsrContextMock();
+        $context = $this->createContextMock();
         $context
             ->expects($this->never())
             ->method('close')
@@ -361,11 +361,11 @@ public function testShouldSkipQueueConsumptionAndUseCustomClientDestinationName(
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext
+     * @return \PHPUnit_Framework_MockObject_MockObject|Context
      */
-    private function createPsrContextMock()
+    private function createContextMock()
     {
-        return $this->createMock(PsrContext::class);
+        return $this->createMock(Context::class);
     }
 
     /**
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
index a93efe3..9d37522 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
@@ -7,9 +7,9 @@
 use Enqueue\Client\RouteCollection;
 use Enqueue\Symfony\Client\DependencyInjection\BuildCommandSubscriberRoutesPass;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -55,7 +55,7 @@ public function testThrowIfTaggedProcessorIsBuiltByFactory()
         $container->register('enqueue.client.aName.route_collection', RouteCollection::class)
             ->addArgument([])
         ;
-        $container->register('aProcessor', PsrProcessor::class)
+        $container->register('aProcessor', Processor::class)
             ->setFactory('foo')
             ->addTag('enqueue.command_subscriber')
         ;
@@ -371,10 +371,10 @@ public function testShouldMergeExtractedRoutesWithAlreadySetInCollection()
 
     private function createCommandSubscriberProcessor($commandSubscriberReturns = ['aCommand'])
     {
-        $processor = new class() implements PsrProcessor, CommandSubscriberInterface {
+        $processor = new class() implements Processor, CommandSubscriberInterface {
             public static $return;
 
-            public function process(PsrMessage $message, PsrContext $context)
+            public function process(InteropMessage $message, Context $context)
             {
             }
 
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
index 0ad70a2..895cfb1 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
@@ -7,9 +7,9 @@
 use Enqueue\Client\TopicSubscriberInterface;
 use Enqueue\Symfony\Client\DependencyInjection\BuildTopicSubscriberRoutesPass;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -55,7 +55,7 @@ public function testThrowIfTaggedProcessorIsBuiltByFactory()
         $container->register('enqueue.client.aName.route_collection', RouteCollection::class)
             ->addArgument([])
         ;
-        $container->register('aProcessor', PsrProcessor::class)
+        $container->register('aProcessor', Processor::class)
             ->setFactory('foo')
             ->addTag('enqueue.topic_subscriber')
         ;
@@ -333,10 +333,10 @@ public function testShouldMergeExtractedRoutesWithAlreadySetInCollection()
 
     private function createTopicSubscriberProcessor($topicSubscriberReturns = ['aTopic'])
     {
-        $processor = new class() implements PsrProcessor, TopicSubscriberInterface {
+        $processor = new class() implements Processor, TopicSubscriberInterface {
             public static $return;
 
-            public function process(PsrMessage $message, PsrContext $context)
+            public function process(InteropMessage $message, Context $context)
             {
             }
 
diff --git a/Tests/Symfony/Consumption/ConsumeMessagesCommandTest.php b/Tests/Symfony/Consumption/ConsumeMessagesCommandTest.php
index a01fd8f..2e47a05 100644
--- a/Tests/Symfony/Consumption/ConsumeMessagesCommandTest.php
+++ b/Tests/Symfony/Consumption/ConsumeMessagesCommandTest.php
@@ -5,7 +5,7 @@
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Symfony\Consumption\ConsumeMessagesCommand;
-use Interop\Queue\PsrContext;
+use Interop\Queue\Context;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\Console\Tester\CommandTester;
 
@@ -69,11 +69,11 @@ public function testShouldExecuteConsumption()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext
+     * @return \PHPUnit_Framework_MockObject_MockObject|Context
      */
     private function createContextMock()
     {
-        return $this->createMock(PsrContext::class);
+        return $this->createMock(Context::class);
     }
 
     /**
diff --git a/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php b/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php
index 6a587ab..b5189fe 100644
--- a/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php
+++ b/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php
@@ -6,8 +6,8 @@
 use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Symfony\Consumption\ContainerAwareConsumeMessagesCommand;
 use Enqueue\Tests\Symfony\Consumption\Mock\QueueSubscriberProcessor;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Context;
+use Interop\Queue\Processor;
 use Interop\Queue\PsrQueue;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\Console\Tester\CommandTester;
@@ -64,7 +64,7 @@ public function testShouldThrowExceptionIfProcessorInstanceHasWrongClass()
         $tester = new CommandTester($command);
 
         $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Invalid message processor service given. It must be an instance of Interop\Queue\PsrProcessor but stdClass');
+        $this->expectExceptionMessage('Invalid message processor service given. It must be an instance of Interop\Queue\Processor but stdClass');
         $tester->execute([
             'processor-service' => 'processor-service',
             '--queue' => ['queue-name'],
@@ -175,11 +175,11 @@ public function testShouldExecuteConsumptionWhenProcessorImplementsQueueSubscrib
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext
+     * @return \PHPUnit_Framework_MockObject_MockObject|Context
      */
     protected function createContextMock()
     {
-        return $this->createMock(PsrContext::class);
+        return $this->createMock(Context::class);
     }
 
     /**
@@ -191,11 +191,11 @@ protected function createQueueMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrProcessor
+     * @return \PHPUnit_Framework_MockObject_MockObject|Processor
      */
     protected function createProcessor()
     {
-        return $this->createMock(PsrProcessor::class);
+        return $this->createMock(Processor::class);
     }
 
     /**
diff --git a/Tests/Symfony/Consumption/Mock/QueueSubscriberProcessor.php b/Tests/Symfony/Consumption/Mock/QueueSubscriberProcessor.php
index e71ad16..56b3c93 100644
--- a/Tests/Symfony/Consumption/Mock/QueueSubscriberProcessor.php
+++ b/Tests/Symfony/Consumption/Mock/QueueSubscriberProcessor.php
@@ -3,13 +3,13 @@
 namespace Enqueue\Tests\Symfony\Consumption\Mock;
 
 use Enqueue\Consumption\QueueSubscriberInterface;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Processor;
 
-class QueueSubscriberProcessor implements PsrProcessor, QueueSubscriberInterface
+class QueueSubscriberProcessor implements Processor, QueueSubscriberInterface
 {
-    public function process(PsrMessage $message, PsrContext $context)
+    public function process(InteropMessage $message, Context $context)
     {
     }
 

From 6e7c7d894d8a99a0cb8c27e271dffdfe090ea9bf Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 28 Sep 2018 16:12:34 +0300
Subject: [PATCH 135/337] [enqueue] Do not use deprecated classes.

---
 .../ExclusiveCommandExtension.php             |  2 +-
 .../SetRouterPropertiesExtension.php          |  2 +-
 Client/Driver/AmqpDriver.php                  |  4 ++--
 Client/Driver/GenericDriver.php               | 16 +++++++-------
 Client/Driver/RabbitMqDriver.php              |  4 ++--
 Client/Driver/RabbitMqStompDriver.php         |  6 ++---
 Client/Driver/SqsDriver.php                   |  2 +-
 Client/Driver/StompDriver.php                 |  4 ++--
 Client/DriverFactory.php                      |  4 ++--
 Client/DriverFactoryInterface.php             |  4 ++--
 Client/DriverInterface.php                    |  6 ++---
 ConnectionFactoryFactory.php                  |  4 ++--
 ConnectionFactoryFactoryInterface.php         |  4 ++--
 Consumption/Context.php                       | 20 ++++++++---------
 Consumption/FallbackSubscriptionConsumer.php  |  4 ++--
 Consumption/QueueConsumer.php                 | 22 +++++++++----------
 Consumption/QueueConsumerInterface.php        |  8 +++----
 Resources.php                                 |  8 +++----
 Symfony/DefaultTransportFactory.php           |  4 ++--
 .../DependencyInjection/TransportFactory.php  |  6 ++---
 .../DelayRedeliveredMessageExtensionTest.php  |  2 +-
 .../ExclusiveCommandExtensionTest.php         |  6 ++---
 .../SetRouterPropertiesExtensionTest.php      |  4 ++--
 Tests/Client/Driver/AmqpDriverTest.php        |  4 ++--
 Tests/Client/Driver/DbalDriverTest.php        |  8 +++----
 Tests/Client/Driver/FsDriverTest.php          |  8 +++----
 Tests/Client/Driver/GenericDriverTest.php     |  8 +++----
 .../Client/Driver/GenericDriverTestsTrait.php | 10 ++++-----
 Tests/Client/Driver/GpsDriverTest.php         |  8 +++----
 Tests/Client/Driver/MongodbDriverTest.php     |  8 +++----
 Tests/Client/Driver/RabbitMqDriverTest.php    |  4 ++--
 .../Client/Driver/RabbitMqStompDriverTest.php |  8 +++----
 Tests/Client/Driver/RdKafkaDriverTest.php     |  4 ++--
 Tests/Client/Driver/SqsDriverTest.php         |  8 +++----
 Tests/Client/Driver/StompDriverTest.php       |  8 +++----
 Tests/Client/DriverFactoryTest.php            |  6 ++---
 Tests/Consumption/ContextTest.php             |  8 +++----
 .../FallbackSubscriptionConsumerTest.php      | 10 ++++-----
 .../Mock/DummySubscriptionConsumer.php        |  4 ++--
 Tests/Consumption/QueueConsumerTest.php       |  6 ++---
 Tests/ResourcesTest.php                       | 10 ++++-----
 ...ntainerAwareConsumeMessagesCommandTest.php |  6 ++---
 42 files changed, 141 insertions(+), 141 deletions(-)

diff --git a/Client/ConsumptionExtension/ExclusiveCommandExtension.php b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
index cb50ca5..64a2d45 100644
--- a/Client/ConsumptionExtension/ExclusiveCommandExtension.php
+++ b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
@@ -31,7 +31,7 @@ public function __construct(DriverInterface $driver)
     public function onPreReceived(Context $context)
     {
         $message = $context->getInteropMessage();
-        $queue = $context->getPsrQueue();
+        $queue = $context->getInteropQueue();
 
         if ($message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
             return;
diff --git a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
index ba7e4ae..0b91834 100644
--- a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
+++ b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
@@ -34,7 +34,7 @@ public function onPreReceived(Context $context)
 
         $config = $this->driver->getConfig();
         $queue = $this->driver->createQueue($config->getRouterQueueName());
-        if ($context->getPsrQueue()->getQueueName() != $queue->getQueueName()) {
+        if ($context->getInteropQueue()->getQueueName() != $queue->getQueueName()) {
             return;
         }
 
diff --git a/Client/Driver/AmqpDriver.php b/Client/Driver/AmqpDriver.php
index 80017fc..6d36fc4 100644
--- a/Client/Driver/AmqpDriver.php
+++ b/Client/Driver/AmqpDriver.php
@@ -14,7 +14,7 @@
 use Interop\Queue\Destination;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Producer as InteropProducer;
-use Interop\Queue\PsrQueue;
+use Interop\Queue\Queue as InteropQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -110,7 +110,7 @@ protected function createRouterTopic(): Destination
     /**
      * @return AmqpQueue
      */
-    protected function doCreateQueue(string $transportQueueName): PsrQueue
+    protected function doCreateQueue(string $transportQueueName): InteropQueue
     {
         /** @var AmqpQueue $queue */
         $queue = parent::doCreateQueue($transportQueueName);
diff --git a/Client/Driver/GenericDriver.php b/Client/Driver/GenericDriver.php
index 54ca925..6391466 100644
--- a/Client/Driver/GenericDriver.php
+++ b/Client/Driver/GenericDriver.php
@@ -14,8 +14,8 @@
 use Interop\Queue\Destination;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Producer as InteropProducer;
-use Interop\Queue\PsrQueue;
-use Interop\Queue\PsrTopic;
+use Interop\Queue\Queue as InteropQueue;
+use Interop\Queue\Topic as InteropTopic;
 use Psr\Log\LoggerInterface;
 
 class GenericDriver implements DriverInterface
@@ -66,7 +66,7 @@ public function sendToProcessor(Message $message): void
         $topic = $message->getProperty(Config::PARAMETER_TOPIC_NAME);
         $command = $message->getProperty(Config::PARAMETER_COMMAND_NAME);
 
-        /** @var PsrQueue $queue */
+        /** @var InteropQueue $queue */
         $queue = null;
         if ($topic && $processor = $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
             $route = $this->routeCollection->topicAndProcessor($topic, $processor);
@@ -117,14 +117,14 @@ public function setupBroker(LoggerInterface $logger = null): void
     {
     }
 
-    public function createQueue(string $clientQueueName, bool $prefix = true): PsrQueue
+    public function createQueue(string $clientQueueName, bool $prefix = true): InteropQueue
     {
         $transportName = $this->createTransportQueueName($clientQueueName, $prefix);
 
         return $this->doCreateQueue($transportName);
     }
 
-    public function createRouteQueue(Route $route): PsrQueue
+    public function createRouteQueue(Route $route): InteropQueue
     {
         $transportName = $this->createTransportQueueName(
             $route->getQueue() ?: $this->config->getDefaultProcessorQueueName(),
@@ -218,7 +218,7 @@ protected function doSendToRouter(InteropProducer $producer, Destination $topic,
         $producer->send($topic, $transportMessage);
     }
 
-    protected function doSendToProcessor(InteropProducer $producer, PsrQueue $queue, InteropMessage $transportMessage): void
+    protected function doSendToProcessor(InteropProducer $producer, InteropQueue $queue, InteropMessage $transportMessage): void
     {
         $producer->send($queue, $transportMessage);
     }
@@ -243,12 +243,12 @@ protected function createTransportQueueName(string $name, bool $prefix): string
         return strtolower(implode($this->config->getSeparator(), array_filter([$clientPrefix, $clientAppName, $name])));
     }
 
-    protected function doCreateQueue(string $transportQueueName): PsrQueue
+    protected function doCreateQueue(string $transportQueueName): InteropQueue
     {
         return $this->context->createQueue($transportQueueName);
     }
 
-    protected function doCreateTopic(string $transportTopicName): PsrTopic
+    protected function doCreateTopic(string $transportTopicName): InteropTopic
     {
         return $this->context->createTopic($transportTopicName);
     }
diff --git a/Client/Driver/RabbitMqDriver.php b/Client/Driver/RabbitMqDriver.php
index 5e94b16..096de6d 100644
--- a/Client/Driver/RabbitMqDriver.php
+++ b/Client/Driver/RabbitMqDriver.php
@@ -3,14 +3,14 @@
 namespace  Enqueue\Client\Driver;
 
 use Interop\Amqp\AmqpQueue;
-use Interop\Queue\PsrQueue;
+use Interop\Queue\Queue as InteropQueue;
 
 class RabbitMqDriver extends AmqpDriver
 {
     /**
      * @return AmqpQueue
      */
-    protected function doCreateQueue(string $transportQueueName): PsrQueue
+    protected function doCreateQueue(string $transportQueueName): InteropQueue
     {
         $queue = parent::doCreateQueue($transportQueueName);
         $queue->setArguments(['x-max-priority' => 4]);
diff --git a/Client/Driver/RabbitMqStompDriver.php b/Client/Driver/RabbitMqStompDriver.php
index fc208c2..fc15953 100644
--- a/Client/Driver/RabbitMqStompDriver.php
+++ b/Client/Driver/RabbitMqStompDriver.php
@@ -12,7 +12,7 @@
 use Interop\Queue\Destination;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Producer as InteropProducer;
-use Interop\Queue\PsrQueue;
+use Interop\Queue\Queue as InteropQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -138,7 +138,7 @@ public function setupBroker(LoggerInterface $logger = null): void
     /**
      * @return StompDestination
      */
-    protected function doCreateQueue(string $transportQueueName): PsrQueue
+    protected function doCreateQueue(string $transportQueueName): InteropQueue
     {
         $queue = parent::doCreateQueue($transportQueueName);
         $queue->setHeader('x-max-priority', 4);
@@ -167,7 +167,7 @@ protected function doSendToRouter(InteropProducer $producer, Destination $topic,
      * @param StompDestination $destination
      * @param StompMessage     $transportMessage
      */
-    protected function doSendToProcessor(InteropProducer $producer, PsrQueue $destination, InteropMessage $transportMessage): void
+    protected function doSendToProcessor(InteropProducer $producer, InteropQueue $destination, InteropMessage $transportMessage): void
     {
         if ($delay = $transportMessage->getProperty('X-Enqueue-Delay')) {
             $producer->setDeliveryDelay(null);
diff --git a/Client/Driver/SqsDriver.php b/Client/Driver/SqsDriver.php
index 622c6f7..9672d39 100644
--- a/Client/Driver/SqsDriver.php
+++ b/Client/Driver/SqsDriver.php
@@ -9,7 +9,7 @@
 
 /**
  * @method SqsContext getContext
- * @method SqsDestination createQueue(string $clientQueueName): PsrQueue
+ * @method SqsDestination createQueue(string $clientQueueName): InteropQueue
  */
 class SqsDriver extends GenericDriver
 {
diff --git a/Client/Driver/StompDriver.php b/Client/Driver/StompDriver.php
index 0508626..7236505 100644
--- a/Client/Driver/StompDriver.php
+++ b/Client/Driver/StompDriver.php
@@ -8,7 +8,7 @@
 use Enqueue\Stomp\StompMessage;
 use Interop\Queue\Destination;
 use Interop\Queue\Message as InteropMessage;
-use Interop\Queue\PsrQueue;
+use Interop\Queue\Queue as InteropQueue;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -43,7 +43,7 @@ public function createTransportMessage(Message $message): InteropMessage
     /**
      * @return StompDestination
      */
-    protected function doCreateQueue(string $transportQueueName): PsrQueue
+    protected function doCreateQueue(string $transportQueueName): InteropQueue
     {
         /** @var StompDestination $queue */
         $queue = parent::doCreateQueue($transportQueueName);
diff --git a/Client/DriverFactory.php b/Client/DriverFactory.php
index 67d3607..071ec0c 100644
--- a/Client/DriverFactory.php
+++ b/Client/DriverFactory.php
@@ -8,7 +8,7 @@
 use Enqueue\Dsn\Dsn;
 use Enqueue\Stomp\StompConnectionFactory;
 use Interop\Amqp\AmqpConnectionFactory;
-use Interop\Queue\PsrConnectionFactory;
+use Interop\Queue\ConnectionFactory;
 
 final class DriverFactory implements DriverFactoryInterface
 {
@@ -28,7 +28,7 @@ public function __construct(Config $config, RouteCollection $routeCollection)
         $this->routeCollection = $routeCollection;
     }
 
-    public function create(PsrConnectionFactory $factory, string $dsn, array $config): DriverInterface
+    public function create(ConnectionFactory $factory, string $dsn, array $config): DriverInterface
     {
         $dsn = new Dsn($dsn);
 
diff --git a/Client/DriverFactoryInterface.php b/Client/DriverFactoryInterface.php
index ffc96ee..6a558b7 100644
--- a/Client/DriverFactoryInterface.php
+++ b/Client/DriverFactoryInterface.php
@@ -2,9 +2,9 @@
 
 namespace Enqueue\Client;
 
-use Interop\Queue\PsrConnectionFactory;
+use Interop\Queue\ConnectionFactory;
 
 interface DriverFactoryInterface
 {
-    public function create(PsrConnectionFactory $factory, string $dsn, array $config): DriverInterface;
+    public function create(ConnectionFactory $factory, string $dsn, array $config): DriverInterface;
 }
diff --git a/Client/DriverInterface.php b/Client/DriverInterface.php
index 5955542..f8b13d0 100644
--- a/Client/DriverInterface.php
+++ b/Client/DriverInterface.php
@@ -6,7 +6,7 @@
 
 use Interop\Queue\Context;
 use Interop\Queue\Message as InteropMessage;
-use Interop\Queue\PsrQueue;
+use Interop\Queue\Queue as InteropQueue;
 use Psr\Log\LoggerInterface;
 
 interface DriverInterface
@@ -19,9 +19,9 @@ public function sendToRouter(Message $message): void;
 
     public function sendToProcessor(Message $message): void;
 
-    public function createQueue(string $queueName, bool $prefix = true): PsrQueue;
+    public function createQueue(string $queueName, bool $prefix = true): InteropQueue;
 
-    public function createRouteQueue(Route $route): PsrQueue;
+    public function createRouteQueue(Route $route): InteropQueue;
 
     /**
      * Prepare broker for work.
diff --git a/ConnectionFactoryFactory.php b/ConnectionFactoryFactory.php
index 3aabfce..48c7f3d 100644
--- a/ConnectionFactoryFactory.php
+++ b/ConnectionFactoryFactory.php
@@ -3,11 +3,11 @@
 namespace Enqueue;
 
 use Enqueue\Dsn\Dsn;
-use Interop\Queue\PsrConnectionFactory;
+use Interop\Queue\ConnectionFactory;
 
 final class ConnectionFactoryFactory implements ConnectionFactoryFactoryInterface
 {
-    public function create($config): PsrConnectionFactory
+    public function create($config): ConnectionFactory
     {
         if (is_string($config)) {
             $config = ['dsn' => $config];
diff --git a/ConnectionFactoryFactoryInterface.php b/ConnectionFactoryFactoryInterface.php
index 22ee4cf..d55808c 100644
--- a/ConnectionFactoryFactoryInterface.php
+++ b/ConnectionFactoryFactoryInterface.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue;
 
-use Interop\Queue\PsrConnectionFactory;
+use Interop\Queue\ConnectionFactory;
 
 interface ConnectionFactoryFactoryInterface
 {
@@ -18,5 +18,5 @@ interface ConnectionFactoryFactoryInterface
      *
      * @throws \InvalidArgumentException if invalid config provided
      */
-    public function create($config): PsrConnectionFactory;
+    public function create($config): ConnectionFactory;
 }
diff --git a/Consumption/Context.php b/Consumption/Context.php
index 96fc9e8..4a0a7bd 100644
--- a/Consumption/Context.php
+++ b/Consumption/Context.php
@@ -7,7 +7,7 @@
 use Interop\Queue\Context as InteropContext;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Processor;
-use Interop\Queue\PsrQueue;
+use Interop\Queue\Queue as InteropQueue;
 use Psr\Log\LoggerInterface;
 
 class Context
@@ -48,9 +48,9 @@ class Context
     private $result;
 
     /**
-     * @var PsrQueue
+     * @var InteropQueue
      */
-    private $psrQueue;
+    private $InteropQueue;
 
     /**
      * @var bool
@@ -212,22 +212,22 @@ public function setLogger(LoggerInterface $logger)
     }
 
     /**
-     * @return PsrQueue
+     * @return InteropQueue
      */
-    public function getPsrQueue()
+    public function getInteropQueue()
     {
-        return $this->psrQueue;
+        return $this->InteropQueue;
     }
 
     /**
-     * @param PsrQueue $psrQueue
+     * @param InteropQueue $InteropQueue
      */
-    public function setPsrQueue(PsrQueue $psrQueue)
+    public function setInteropQueue(InteropQueue $InteropQueue)
     {
-        if ($this->psrQueue) {
+        if ($this->InteropQueue) {
             throw new IllegalContextModificationException('The queue modification is not allowed');
         }
 
-        $this->psrQueue = $psrQueue;
+        $this->InteropQueue = $InteropQueue;
     }
 }
diff --git a/Consumption/FallbackSubscriptionConsumer.php b/Consumption/FallbackSubscriptionConsumer.php
index baee1f7..cdd6fb1 100644
--- a/Consumption/FallbackSubscriptionConsumer.php
+++ b/Consumption/FallbackSubscriptionConsumer.php
@@ -3,9 +3,9 @@
 namespace Enqueue\Consumption;
 
 use Interop\Queue\Consumer;
-use Interop\Queue\PsrSubscriptionConsumer;
+use Interop\Queue\SubscriptionConsumer;
 
-final class FallbackSubscriptionConsumer implements PsrSubscriptionConsumer
+final class FallbackSubscriptionConsumer implements SubscriptionConsumer
 {
     /**
      * an item contains an array: [Consumer $consumer, callable $callback];.
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 6504635..8125616 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -11,8 +11,8 @@
 use Interop\Queue\Exception\SubscriptionConsumerNotSupportedException;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Processor;
-use Interop\Queue\PsrQueue;
-use Interop\Queue\PsrSubscriptionConsumer;
+use Interop\Queue\Queue as InteropQueue;
+use Interop\Queue\SubscriptionConsumer;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -30,7 +30,7 @@ final class QueueConsumer implements QueueConsumerInterface
 
     /**
      * [
-     *   [PsrQueue, Processor],
+     *   [InteropQueue, Processor],
      * ].
      *
      * @var array
@@ -58,7 +58,7 @@ final class QueueConsumer implements QueueConsumerInterface
     private $logger;
 
     /**
-     * @var PsrSubscriptionConsumer
+     * @var SubscriptionConsumer
      */
     private $fallbackSubscriptionConsumer;
 
@@ -121,7 +121,7 @@ public function bind($queue, Processor $processor): QueueConsumerInterface
             $queue = $this->interopContext->createQueue($queue);
         }
 
-        InvalidArgumentException::assertInstanceOf($queue, PsrQueue::class);
+        InvalidArgumentException::assertInstanceOf($queue, InteropQueue::class);
 
         if (empty($queue->getQueueName())) {
             throw new LogicException('The queue name must be not empty.');
@@ -154,7 +154,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
 
         /** @var Consumer[] $consumers */
         $consumers = [];
-        /** @var PsrQueue $queue */
+        /** @var InteropQueue $queue */
         foreach ($this->boundProcessors as list($queue, $processor)) {
             $consumers[$queue->getQueueName()] = $this->interopContext->createConsumer($queue);
         }
@@ -185,7 +185,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
         $callback = function (InteropMessage $message, Consumer $consumer) use (&$context) {
             $currentProcessor = null;
 
-            /** @var PsrQueue $queue */
+            /** @var InteropQueue $queue */
             foreach ($this->boundProcessors as list($queue, $processor)) {
                 if ($queue->getQueueName() === $consumer->getQueue()->getQueueName()) {
                     $currentProcessor = $processor;
@@ -198,7 +198,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
 
             $context = new Context($this->interopContext);
             $context->setLogger($this->logger);
-            $context->setPsrQueue($consumer->getQueue());
+            $context->setInteropQueue($consumer->getQueue());
             $context->setConsumer($consumer);
             $context->setProcessor($currentProcessor);
             $context->setInteropMessage($message);
@@ -262,9 +262,9 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
     /**
      * @internal
      *
-     * @param PsrSubscriptionConsumer $fallbackSubscriptionConsumer
+     * @param SubscriptionConsumer $fallbackSubscriptionConsumer
      */
-    public function setFallbackSubscriptionConsumer(PsrSubscriptionConsumer $fallbackSubscriptionConsumer): void
+    public function setFallbackSubscriptionConsumer(SubscriptionConsumer $fallbackSubscriptionConsumer): void
     {
         $this->fallbackSubscriptionConsumer = $fallbackSubscriptionConsumer;
     }
@@ -313,7 +313,7 @@ private function onInterruptionByException(ExtensionInterface $extension, Contex
 
     private function processMessage(Consumer $consumer, Processor $processor, InteropMessage $message, Context $context)
     {
-        $this->logger->info('Message received from the queue: '.$context->getPsrQueue()->getQueueName());
+        $this->logger->info('Message received from the queue: '.$context->getInteropQueue()->getQueueName());
         $this->logger->debug('Headers: {headers}', ['headers' => new VarExport($message->getHeaders())]);
         $this->logger->debug('Properties: {properties}', ['properties' => new VarExport($message->getProperties())]);
         $this->logger->debug('Payload: {payload}', ['payload' => new VarExport($message->getBody())]);
diff --git a/Consumption/QueueConsumerInterface.php b/Consumption/QueueConsumerInterface.php
index a3bbb47..8358ba3 100644
--- a/Consumption/QueueConsumerInterface.php
+++ b/Consumption/QueueConsumerInterface.php
@@ -4,7 +4,7 @@
 
 use Interop\Queue\Context;
 use Interop\Queue\Processor;
-use Interop\Queue\PsrQueue;
+use Interop\Queue\Queue as InteropQueue;
 
 interface QueueConsumerInterface
 {
@@ -25,13 +25,13 @@ public function getReceiveTimeout(): float;
     public function getContext(): Context;
 
     /**
-     * @param string|PsrQueue $queueName
+     * @param string|InteropQueue $queueName
      */
     public function bind($queueName, Processor $processor): self;
 
     /**
-     * @param string|PsrQueue $queueName
-     * @param mixed           $queue
+     * @param string|InteropQueue $queueName
+     * @param mixed               $queue
      */
     public function bindCallback($queue, callable $processor): self;
 
diff --git a/Resources.php b/Resources.php
index d58ba92..21172c6 100644
--- a/Resources.php
+++ b/Resources.php
@@ -16,12 +16,12 @@
 use Enqueue\Redis\RedisConnectionFactory;
 use Enqueue\Sqs\SqsConnectionFactory;
 use Enqueue\Stomp\StompConnectionFactory;
-use Interop\Queue\PsrConnectionFactory;
+use Interop\Queue\ConnectionFactory;
 
 final class Resources
 {
     /**
-     * [psr connection factory class => [
+     * [connection factory class => [
      *   schemes => [schemes strings],
      *   package => package name,
      * ].
@@ -173,8 +173,8 @@ public static function getKnownConnections(): array
     public static function addConnection(string $connectionFactoryClass, array $schemes, array $extensions, string $package): void
     {
         if (class_exists($connectionFactoryClass)) {
-            if (false == (new \ReflectionClass($connectionFactoryClass))->implementsInterface(PsrConnectionFactory::class)) {
-                throw new \InvalidArgumentException(sprintf('The connection factory class "%s" must implement "%s" interface.', $connectionFactoryClass, PsrConnectionFactory::class));
+            if (false == (new \ReflectionClass($connectionFactoryClass))->implementsInterface(ConnectionFactory::class)) {
+                throw new \InvalidArgumentException(sprintf('The connection factory class "%s" must implement "%s" interface.', $connectionFactoryClass, ConnectionFactory::class));
             }
         }
 
diff --git a/Symfony/DefaultTransportFactory.php b/Symfony/DefaultTransportFactory.php
index 9f96f65..eda667e 100644
--- a/Symfony/DefaultTransportFactory.php
+++ b/Symfony/DefaultTransportFactory.php
@@ -3,8 +3,8 @@
 namespace Enqueue\Symfony;
 
 use Enqueue\Client\DriverInterface;
+use Interop\Queue\ConnectionFactory;
 use Interop\Queue\Context;
-use Interop\Queue\PsrConnectionFactory;
 use Symfony\Component\Config\Definition\Builder\VariableNodeDefinition;
 use Symfony\Component\DependencyInjection\Alias;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -45,7 +45,7 @@ public function createConnectionFactory(ContainerBuilder $container, array $conf
     {
         $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
 
-        $container->register($factoryId, PsrConnectionFactory::class)
+        $container->register($factoryId, ConnectionFactory::class)
             ->setFactory([new Reference('enqueue.connection_factory_factory'), 'create'])
             ->addArgument($config['dsn'])
         ;
diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index d50c94a..7a80eea 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -5,8 +5,8 @@
 use Enqueue\ConnectionFactoryFactory;
 use Enqueue\ConnectionFactoryFactoryInterface;
 use Enqueue\Resources;
+use Interop\Queue\ConnectionFactory;
 use Interop\Queue\Context;
-use Interop\Queue\PsrConnectionFactory;
 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Reference;
@@ -74,7 +74,7 @@ public function addConfiguration(ArrayNodeDefinition $builder): void
                 ))
             ->end()
             ->scalarNode('connection_factory_class')
-                ->info(sprintf('The connection factory class should implement "%s" interface', PsrConnectionFactory::class))
+                ->info(sprintf('The connection factory class should implement "%s" interface', ConnectionFactory::class))
             ->end()
             ->scalarNode('factory_service')
                 ->info(sprintf('The factory class should implement "%s" interface', ConnectionFactoryFactoryInterface::class))
@@ -107,7 +107,7 @@ public function createConnectionFactory(ContainerBuilder $container, array $conf
                 ->addArgument($config)
             ;
         } else {
-            $container->register($factoryId, PsrConnectionFactory::class)
+            $container->register($factoryId, ConnectionFactory::class)
                 ->setFactory([$factoryFactoryService, 'create'])
                 ->addArgument($config)
             ;
diff --git a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
index 698b821..5d4b750 100644
--- a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
@@ -62,7 +62,7 @@ public function testShouldSendDelayedMessageAndRejectOriginalMessage()
         ;
 
         $context = new Context($this->createContextMock());
-        $context->setPsrQueue($queue);
+        $context->setInteropQueue($queue);
         $context->setInteropMessage($originMessage);
         $context->setLogger($logger);
 
diff --git a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
index 356bd95..ff10c80 100644
--- a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
@@ -117,7 +117,7 @@ public function testShouldDoNothingIfCurrentQueueHasNoExclusiveProcessor()
 
         $context = new Context(new NullContext());
         $context->setInteropMessage($message);
-        $context->setPsrQueue($queue);
+        $context->setInteropQueue($queue);
 
         $extension = new ExclusiveCommandExtension($this->createDriverStub(new RouteCollection([])));
 
@@ -135,7 +135,7 @@ public function testShouldSetCommandPropertiesIfCurrentQueueHasExclusiveCommandP
 
         $context = new Context(new NullContext());
         $context->setInteropMessage($message);
-        $context->setPsrQueue($queue);
+        $context->setInteropQueue($queue);
         $context->setLogger(new NullLogger());
 
         $routeCollection = new RouteCollection([
@@ -176,7 +176,7 @@ public function testShouldDoNothingIfAnotherQueue()
 
         $context = new Context(new NullContext());
         $context->setInteropMessage($message);
-        $context->setPsrQueue($queue);
+        $context->setInteropQueue($queue);
         $context->setLogger(new NullLogger());
 
         $routeCollection = new RouteCollection([
diff --git a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
index dd673e3..395042a 100644
--- a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
@@ -51,7 +51,7 @@ public function testShouldSetRouterProcessorPropertyIfNotSetAndOnRouterQueue()
         $context = new Context($this->createContextMock());
         $context->setLogger(new NullLogger());
         $context->setInteropMessage($message);
-        $context->setPsrQueue(new NullQueue('test.router-queue'));
+        $context->setInteropQueue(new NullQueue('test.router-queue'));
 
         $extension = new SetRouterPropertiesExtension($driver);
         $extension->onPreReceived($context);
@@ -83,7 +83,7 @@ public function testShouldNotSetRouterProcessorPropertyIfNotSetAndNotOnRouterQue
 
         $context = new Context($this->createContextMock());
         $context->setInteropMessage($message);
-        $context->setPsrQueue(new NullQueue('test.another-queue'));
+        $context->setInteropQueue(new NullQueue('test.another-queue'));
 
         $extension = new SetRouterPropertiesExtension($driver);
         $extension->onPreReceived($context);
diff --git a/Tests/Client/Driver/AmqpDriverTest.php b/Tests/Client/Driver/AmqpDriverTest.php
index 1d89acd..11539cb 100644
--- a/Tests/Client/Driver/AmqpDriverTest.php
+++ b/Tests/Client/Driver/AmqpDriverTest.php
@@ -20,7 +20,7 @@
 use Interop\Queue\Context;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Producer as InteropProducer;
-use Interop\Queue\PsrQueue;
+use Interop\Queue\Queue as InteropQueue;
 use PHPUnit\Framework\TestCase;
 
 class AmqpDriverTest extends TestCase
@@ -307,7 +307,7 @@ protected function createProducerMock(): InteropProducer
     /**
      * @return AmqpQueue
      */
-    protected function createQueue(string $name): PsrQueue
+    protected function createQueue(string $name): InteropQueue
     {
         return new AmqpQueue($name);
     }
diff --git a/Tests/Client/Driver/DbalDriverTest.php b/Tests/Client/Driver/DbalDriverTest.php
index f8b8a67..c9fbc6e 100644
--- a/Tests/Client/Driver/DbalDriverTest.php
+++ b/Tests/Client/Driver/DbalDriverTest.php
@@ -14,8 +14,8 @@
 use Interop\Queue\Context;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Producer as InteropProducer;
-use Interop\Queue\PsrQueue;
-use Interop\Queue\PsrTopic;
+use Interop\Queue\Queue as InteropQueue;
+use Interop\Queue\Topic as InteropTopic;
 
 class DbalDriverTest extends \PHPUnit_Framework_TestCase
 {
@@ -77,7 +77,7 @@ protected function createProducerMock(): InteropProducer
     /**
      * @return DbalDestination
      */
-    protected function createQueue(string $name): PsrQueue
+    protected function createQueue(string $name): InteropQueue
     {
         return new DbalDestination($name);
     }
@@ -85,7 +85,7 @@ protected function createQueue(string $name): PsrQueue
     /**
      * @return DbalDestination
      */
-    protected function createTopic(string $name): PsrTopic
+    protected function createTopic(string $name): InteropTopic
     {
         return new DbalDestination($name);
     }
diff --git a/Tests/Client/Driver/FsDriverTest.php b/Tests/Client/Driver/FsDriverTest.php
index 522a76e..f1cd02f 100644
--- a/Tests/Client/Driver/FsDriverTest.php
+++ b/Tests/Client/Driver/FsDriverTest.php
@@ -15,8 +15,8 @@
 use Interop\Queue\Context;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Producer as InteropProducer;
-use Interop\Queue\PsrQueue;
-use Interop\Queue\PsrTopic;
+use Interop\Queue\Queue as InteropQueue;
+use Interop\Queue\Topic as InteropTopic;
 use Makasim\File\TempFile;
 use PHPUnit\Framework\TestCase;
 
@@ -102,7 +102,7 @@ protected function createProducerMock(): InteropProducer
     /**
      * @return FsDestination
      */
-    protected function createQueue(string $name): PsrQueue
+    protected function createQueue(string $name): InteropQueue
     {
         return new FsDestination(new \SplFileInfo($name));
     }
@@ -110,7 +110,7 @@ protected function createQueue(string $name): PsrQueue
     /**
      * @return FsDestination
      */
-    protected function createTopic(string $name): PsrTopic
+    protected function createTopic(string $name): InteropTopic
     {
         return new FsDestination(new \SplFileInfo($name));
     }
diff --git a/Tests/Client/Driver/GenericDriverTest.php b/Tests/Client/Driver/GenericDriverTest.php
index 932ed3e..733e9ac 100644
--- a/Tests/Client/Driver/GenericDriverTest.php
+++ b/Tests/Client/Driver/GenericDriverTest.php
@@ -12,8 +12,8 @@
 use Interop\Queue\Context;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Producer as InteropProducer;
-use Interop\Queue\PsrQueue;
-use Interop\Queue\PsrTopic;
+use Interop\Queue\Queue as InteropQueue;
+use Interop\Queue\Topic as InteropTopic;
 use PHPUnit\Framework\TestCase;
 
 class GenericDriverTest extends TestCase
@@ -41,12 +41,12 @@ protected function createProducerMock(): InteropProducer
         return $this->createMock(InteropProducer::class);
     }
 
-    protected function createQueue(string $name): PsrQueue
+    protected function createQueue(string $name): InteropQueue
     {
         return new NullQueue($name);
     }
 
-    protected function createTopic(string $name): PsrTopic
+    protected function createTopic(string $name): InteropTopic
     {
         return new NullTopic($name);
     }
diff --git a/Tests/Client/Driver/GenericDriverTestsTrait.php b/Tests/Client/Driver/GenericDriverTestsTrait.php
index 8d38d7c..46c308b 100644
--- a/Tests/Client/Driver/GenericDriverTestsTrait.php
+++ b/Tests/Client/Driver/GenericDriverTestsTrait.php
@@ -12,8 +12,8 @@
 use Interop\Queue\Destination;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Producer as InteropProducer;
-use Interop\Queue\PsrQueue;
-use Interop\Queue\PsrTopic;
+use Interop\Queue\Queue as InteropQueue;
+use Interop\Queue\Topic as InteropTopic;
 
 trait GenericDriverTestsTrait
 {
@@ -261,7 +261,7 @@ public function testShouldSendMessageToRouter()
             ->willReturnCallback(function (Destination $topic, InteropMessage $message) use ($transportMessage) {
                 $this->assertSame(
                     $this->getRouterTransportName(),
-                    $topic instanceof PsrTopic ? $topic->getTopicName() : $topic->getQueueName());
+                    $topic instanceof InteropTopic ? $topic->getTopicName() : $topic->getQueueName());
                 $this->assertSame($transportMessage, $message);
             })
         ;
@@ -1077,9 +1077,9 @@ abstract protected function createContextMock(): Context;
      */
     abstract protected function createProducerMock(): InteropProducer;
 
-    abstract protected function createQueue(string $name): PsrQueue;
+    abstract protected function createQueue(string $name): InteropQueue;
 
-    abstract protected function createTopic(string $name): PsrTopic;
+    abstract protected function createTopic(string $name): InteropTopic;
 
     abstract protected function createMessage(): InteropMessage;
 
diff --git a/Tests/Client/Driver/GpsDriverTest.php b/Tests/Client/Driver/GpsDriverTest.php
index d6623ac..c0cac04 100644
--- a/Tests/Client/Driver/GpsDriverTest.php
+++ b/Tests/Client/Driver/GpsDriverTest.php
@@ -16,8 +16,8 @@
 use Interop\Queue\Context;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Producer as InteropProducer;
-use Interop\Queue\PsrQueue;
-use Interop\Queue\PsrTopic;
+use Interop\Queue\Queue as InteropQueue;
+use Interop\Queue\Topic as InteropTopic;
 use PHPUnit\Framework\TestCase;
 
 class GpsDriverTest extends TestCase
@@ -114,7 +114,7 @@ protected function createProducerMock(): InteropProducer
     /**
      * @return GpsQueue
      */
-    protected function createQueue(string $name): PsrQueue
+    protected function createQueue(string $name): InteropQueue
     {
         return new GpsQueue($name);
     }
@@ -122,7 +122,7 @@ protected function createQueue(string $name): PsrQueue
     /**
      * @return GpsTopic
      */
-    protected function createTopic(string $name): PsrTopic
+    protected function createTopic(string $name): InteropTopic
     {
         return new GpsTopic($name);
     }
diff --git a/Tests/Client/Driver/MongodbDriverTest.php b/Tests/Client/Driver/MongodbDriverTest.php
index bef90f6..e74f7bd 100644
--- a/Tests/Client/Driver/MongodbDriverTest.php
+++ b/Tests/Client/Driver/MongodbDriverTest.php
@@ -14,8 +14,8 @@
 use Interop\Queue\Context;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Producer as InteropProducer;
-use Interop\Queue\PsrQueue;
-use Interop\Queue\PsrTopic;
+use Interop\Queue\Queue as InteropQueue;
+use Interop\Queue\Topic as InteropTopic;
 
 class MongodbDriverTest extends \PHPUnit_Framework_TestCase
 {
@@ -81,7 +81,7 @@ protected function createProducerMock(): InteropProducer
     /**
      * @return MongodbDestination
      */
-    protected function createQueue(string $name): PsrQueue
+    protected function createQueue(string $name): InteropQueue
     {
         return new MongodbDestination($name);
     }
@@ -89,7 +89,7 @@ protected function createQueue(string $name): PsrQueue
     /**
      * @return MongodbDestination
      */
-    protected function createTopic(string $name): PsrTopic
+    protected function createTopic(string $name): InteropTopic
     {
         return new MongodbDestination($name);
     }
diff --git a/Tests/Client/Driver/RabbitMqDriverTest.php b/Tests/Client/Driver/RabbitMqDriverTest.php
index 53f5f87..d6604ed 100644
--- a/Tests/Client/Driver/RabbitMqDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqDriverTest.php
@@ -17,7 +17,7 @@
 use Interop\Queue\Context;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Producer as InteropProducer;
-use Interop\Queue\PsrQueue;
+use Interop\Queue\Queue as InteropQueue;
 use PHPUnit\Framework\TestCase;
 
 class RabbitMqDriverTest extends TestCase
@@ -85,7 +85,7 @@ protected function createProducerMock(): InteropProducer
     /**
      * @return AmqpQueue
      */
-    protected function createQueue(string $name): PsrQueue
+    protected function createQueue(string $name): InteropQueue
     {
         return new AmqpQueue($name);
     }
diff --git a/Tests/Client/Driver/RabbitMqStompDriverTest.php b/Tests/Client/Driver/RabbitMqStompDriverTest.php
index e573038..9011f64 100644
--- a/Tests/Client/Driver/RabbitMqStompDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqStompDriverTest.php
@@ -20,8 +20,8 @@
 use Interop\Queue\Context;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Producer as InteropProducer;
-use Interop\Queue\PsrQueue;
-use Interop\Queue\PsrTopic;
+use Interop\Queue\Queue as InteropQueue;
+use Interop\Queue\Topic as InteropTopic;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 
@@ -495,7 +495,7 @@ protected function createProducerMock(): InteropProducer
     /**
      * @return StompDestination
      */
-    protected function createQueue(string $name): PsrQueue
+    protected function createQueue(string $name): InteropQueue
     {
         $destination = new StompDestination();
         $destination->setType(StompDestination::TYPE_QUEUE);
@@ -507,7 +507,7 @@ protected function createQueue(string $name): PsrQueue
     /**
      * @return StompDestination
      */
-    protected function createTopic(string $name): PsrTopic
+    protected function createTopic(string $name): InteropTopic
     {
         $destination = new StompDestination();
         $destination->setType(StompDestination::TYPE_TOPIC);
diff --git a/Tests/Client/Driver/RdKafkaDriverTest.php b/Tests/Client/Driver/RdKafkaDriverTest.php
index ada2ebc..f3474e7 100644
--- a/Tests/Client/Driver/RdKafkaDriverTest.php
+++ b/Tests/Client/Driver/RdKafkaDriverTest.php
@@ -16,7 +16,7 @@
 use Interop\Queue\Context;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Producer as InteropProducer;
-use Interop\Queue\PsrQueue;
+use Interop\Queue\Queue as InteropQueue;
 use PHPUnit\Framework\TestCase;
 
 /**
@@ -97,7 +97,7 @@ protected function createProducerMock(): InteropProducer
     /**
      * @return RdKafkaTopic
      */
-    protected function createQueue(string $name): PsrQueue
+    protected function createQueue(string $name): InteropQueue
     {
         return new RdKafkaTopic($name);
     }
diff --git a/Tests/Client/Driver/SqsDriverTest.php b/Tests/Client/Driver/SqsDriverTest.php
index f04987d..2e3005e 100644
--- a/Tests/Client/Driver/SqsDriverTest.php
+++ b/Tests/Client/Driver/SqsDriverTest.php
@@ -15,8 +15,8 @@
 use Interop\Queue\Context;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Producer as InteropProducer;
-use Interop\Queue\PsrQueue;
-use Interop\Queue\PsrTopic;
+use Interop\Queue\Queue as InteropQueue;
+use Interop\Queue\Topic as InteropTopic;
 use PHPUnit\Framework\TestCase;
 
 class SqsDriverTest extends TestCase
@@ -100,7 +100,7 @@ protected function createProducerMock(): InteropProducer
     /**
      * @return SqsDestination
      */
-    protected function createQueue(string $name): PsrQueue
+    protected function createQueue(string $name): InteropQueue
     {
         return new SqsDestination($name);
     }
@@ -108,7 +108,7 @@ protected function createQueue(string $name): PsrQueue
     /**
      * @return SqsDestination
      */
-    protected function createTopic(string $name): PsrTopic
+    protected function createTopic(string $name): InteropTopic
     {
         return new SqsDestination($name);
     }
diff --git a/Tests/Client/Driver/StompDriverTest.php b/Tests/Client/Driver/StompDriverTest.php
index e5ea51a..26199d3 100644
--- a/Tests/Client/Driver/StompDriverTest.php
+++ b/Tests/Client/Driver/StompDriverTest.php
@@ -16,8 +16,8 @@
 use Interop\Queue\Context;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Producer as InteropProducer;
-use Interop\Queue\PsrQueue;
-use Interop\Queue\PsrTopic;
+use Interop\Queue\Queue as InteropQueue;
+use Interop\Queue\Topic as InteropTopic;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 
@@ -124,7 +124,7 @@ protected function createProducerMock(): InteropProducer
     /**
      * @return StompDestination
      */
-    protected function createQueue(string $name): PsrQueue
+    protected function createQueue(string $name): InteropQueue
     {
         $destination = new StompDestination();
         $destination->setType(StompDestination::TYPE_QUEUE);
@@ -136,7 +136,7 @@ protected function createQueue(string $name): PsrQueue
     /**
      * @return StompDestination
      */
-    protected function createTopic(string $name): PsrTopic
+    protected function createTopic(string $name): InteropTopic
     {
         $destination = new StompDestination();
         $destination->setType(StompDestination::TYPE_TOPIC);
diff --git a/Tests/Client/DriverFactoryTest.php b/Tests/Client/DriverFactoryTest.php
index f17ce38..c4f1ffb 100644
--- a/Tests/Client/DriverFactoryTest.php
+++ b/Tests/Client/DriverFactoryTest.php
@@ -42,7 +42,7 @@
 use Enqueue\Stomp\StompContext;
 use Interop\Amqp\AmqpConnectionFactory;
 use Interop\Amqp\AmqpContext;
-use Interop\Queue\PsrConnectionFactory;
+use Interop\Queue\ConnectionFactory;
 use PHPUnit\Framework\TestCase;
 
 class DriverFactoryTest extends TestCase
@@ -164,9 +164,9 @@ public static function provideDSN()
         yield ['beanstalk:', PheanstalkConnectionFactory::class, PheanstalkContext::class, [], GenericDriver::class];
     }
 
-    private function createConnectionFactoryMock(): PsrConnectionFactory
+    private function createConnectionFactoryMock(): ConnectionFactory
     {
-        return $this->createMock(PsrConnectionFactory::class);
+        return $this->createMock(ConnectionFactory::class);
     }
 
     private function createConfigMock(): Config
diff --git a/Tests/Consumption/ContextTest.php b/Tests/Consumption/ContextTest.php
index e5531c4..4b5ff36 100644
--- a/Tests/Consumption/ContextTest.php
+++ b/Tests/Consumption/ContextTest.php
@@ -215,21 +215,21 @@ public function testShouldAllowGetPreviouslySetQueue()
     {
         $context = new Context($this->createContext());
 
-        $context->setPsrQueue($queue = new NullQueue(''));
+        $context->setInteropQueue($queue = new NullQueue(''));
 
-        $this->assertSame($queue, $context->getPsrQueue());
+        $this->assertSame($queue, $context->getInteropQueue());
     }
 
     public function testThrowOnSettingQueueNameIfAlreadySet()
     {
         $context = new Context($this->createContext());
 
-        $context->setPsrQueue(new NullQueue(''));
+        $context->setInteropQueue(new NullQueue(''));
 
         $this->expectException(IllegalContextModificationException::class);
         $this->expectExceptionMessage('The queue modification is not allowed');
 
-        $context->setPsrQueue(new NullQueue(''));
+        $context->setInteropQueue(new NullQueue(''));
     }
 
     /**
diff --git a/Tests/Consumption/FallbackSubscriptionConsumerTest.php b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
index 95a9612..ff67b1a 100644
--- a/Tests/Consumption/FallbackSubscriptionConsumerTest.php
+++ b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
@@ -5,16 +5,16 @@
 use Enqueue\Consumption\FallbackSubscriptionConsumer;
 use Interop\Queue\Consumer;
 use Interop\Queue\Message as InteropMessage;
-use Interop\Queue\PsrQueue;
-use Interop\Queue\PsrSubscriptionConsumer;
+use Interop\Queue\Queue as InteropQueue;
+use Interop\Queue\SubscriptionConsumer;
 
 class FallbackSubscriptionConsumerTest extends \PHPUnit_Framework_TestCase
 {
-    public function testShouldImplementPsrSubscriptionConsumerInterface()
+    public function testShouldImplementSubscriptionConsumerInterface()
     {
         $rc = new \ReflectionClass(FallbackSubscriptionConsumer::class);
 
-        $this->assertTrue($rc->implementsInterface(PsrSubscriptionConsumer::class));
+        $this->assertTrue($rc->implementsInterface(SubscriptionConsumer::class));
     }
 
     public function testCouldBeConstructedWithoutAnyArguments()
@@ -249,7 +249,7 @@ private function createMessageStub($body = null)
      */
     private function createConsumerStub($queueName = null)
     {
-        $queueMock = $this->createMock(PsrQueue::class);
+        $queueMock = $this->createMock(InteropQueue::class);
         $queueMock
             ->expects($this->any())
             ->method('getQueueName')
diff --git a/Tests/Consumption/Mock/DummySubscriptionConsumer.php b/Tests/Consumption/Mock/DummySubscriptionConsumer.php
index 41a1f60..4035148 100644
--- a/Tests/Consumption/Mock/DummySubscriptionConsumer.php
+++ b/Tests/Consumption/Mock/DummySubscriptionConsumer.php
@@ -4,9 +4,9 @@
 
 use Interop\Queue\Consumer;
 use Interop\Queue\Message as InteropMessage;
-use Interop\Queue\PsrSubscriptionConsumer;
+use Interop\Queue\SubscriptionConsumer;
 
-class DummySubscriptionConsumer implements PsrSubscriptionConsumer
+class DummySubscriptionConsumer implements SubscriptionConsumer
 {
     private $subscriptions = [];
 
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 1f63674..096fa0e 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -98,7 +98,7 @@ public function testThrowIfQueueNeitherInstanceOfQueueNorString()
         $consumer = new QueueConsumer($this->createContextStub(), null, 0);
 
         $this->expectException(InvalidArgumentException::class);
-        $this->expectExceptionMessage('The argument must be an instance of Interop\Queue\PsrQueue but got stdClass.');
+        $this->expectExceptionMessage('The argument must be an instance of Interop\Queue\Queue but got stdClass.');
         $consumer->bind(new \stdClass(), $processorMock);
     }
 
@@ -546,7 +546,7 @@ public function testShouldCallOnStartExtensionMethod()
                 $this->assertNull($context->getInteropMessage());
                 $this->assertNull($context->getException());
                 $this->assertNull($context->getResult());
-                $this->assertNull($context->getPsrQueue());
+                $this->assertNull($context->getInteropQueue());
                 $this->assertFalse($context->isExecutionInterrupted());
             })
         ;
@@ -615,7 +615,7 @@ public function testShouldCallOnBeforeReceiveExtensionMethod()
                 $this->assertNull($context->getInteropMessage());
                 $this->assertNull($context->getException());
                 $this->assertNull($context->getResult());
-                $this->assertNull($context->getPsrQueue());
+                $this->assertNull($context->getInteropQueue());
                 $this->assertFalse($context->isExecutionInterrupted());
             })
         ;
diff --git a/Tests/ResourcesTest.php b/Tests/ResourcesTest.php
index 0131b4c..52bd13f 100644
--- a/Tests/ResourcesTest.php
+++ b/Tests/ResourcesTest.php
@@ -4,7 +4,7 @@
 
 use Enqueue\Redis\RedisConnectionFactory;
 use Enqueue\Resources;
-use Interop\Queue\PsrConnectionFactory;
+use Interop\Queue\ConnectionFactory;
 use PHPUnit\Framework\TestCase;
 
 class ResourcesTest extends TestCase
@@ -62,14 +62,14 @@ public function testShouldGetKnownConnectionsInExpectedFormat()
     public function testThrowsIfConnectionClassNotImplementConnectionFactoryInterfaceOnAddConnection()
     {
         $this->expectException(\InvalidArgumentException::class);
-        $this->expectExceptionMessage('The connection factory class "stdClass" must implement "Interop\Queue\PsrConnectionFactory" interface.');
+        $this->expectExceptionMessage('The connection factory class "stdClass" must implement "Interop\Queue\ConnectionFactory" interface.');
 
         Resources::addConnection(\stdClass::class, [], [], 'foo');
     }
 
     public function testThrowsIfNoSchemesProvidedOnAddConnection()
     {
-        $connectionClass = $this->getMockClass(PsrConnectionFactory::class);
+        $connectionClass = $this->getMockClass(ConnectionFactory::class);
 
         $this->expectException(\InvalidArgumentException::class);
         $this->expectExceptionMessage('Schemes could not be empty.');
@@ -79,7 +79,7 @@ public function testThrowsIfNoSchemesProvidedOnAddConnection()
 
     public function testThrowsIfNoPackageProvidedOnAddConnection()
     {
-        $connectionClass = $this->getMockClass(PsrConnectionFactory::class);
+        $connectionClass = $this->getMockClass(ConnectionFactory::class);
 
         $this->expectException(\InvalidArgumentException::class);
         $this->expectExceptionMessage('Package name could not be empty.');
@@ -103,7 +103,7 @@ public function testShouldAllowRegisterConnectionThatIsNotInstalled()
 
     public function testShouldAllowGetPreviouslyRegisteredConnection()
     {
-        $connectionClass = $this->getMockClass(PsrConnectionFactory::class);
+        $connectionClass = $this->getMockClass(ConnectionFactory::class);
 
         Resources::addConnection(
             $connectionClass,
diff --git a/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php b/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php
index b5189fe..2c04b07 100644
--- a/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php
+++ b/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php
@@ -8,7 +8,7 @@
 use Enqueue\Tests\Symfony\Consumption\Mock\QueueSubscriberProcessor;
 use Interop\Queue\Context;
 use Interop\Queue\Processor;
-use Interop\Queue\PsrQueue;
+use Interop\Queue\Queue as InteropQueue;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\Console\Tester\CommandTester;
 use Symfony\Component\DependencyInjection\Container;
@@ -183,11 +183,11 @@ protected function createContextMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrQueue
+     * @return \PHPUnit_Framework_MockObject_MockObject|InteropQueue
      */
     protected function createQueueMock()
     {
-        return $this->createMock(PsrQueue::class);
+        return $this->createMock(InteropQueue::class);
     }
 
     /**

From 28065cb4954d1c5c7dac0b45da498a8e4cb12f11 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 28 Sep 2018 18:28:33 +0300
Subject: [PATCH 136/337] [bundle] Do not use deprecated classes.

---
 Consumption/Context.php                  |  2 +-
 Consumption/Extension/ReplyExtension.php |  4 ++--
 Consumption/QueueConsumer.php            |  2 +-
 Tests/Consumption/ContextTest.php        |  2 +-
 Tests/Consumption/QueueConsumerTest.php  | 22 +++++++++++-----------
 5 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/Consumption/Context.php b/Consumption/Context.php
index 4a0a7bd..76935c1 100644
--- a/Consumption/Context.php
+++ b/Consumption/Context.php
@@ -90,7 +90,7 @@ public function setInteropMessage(InteropMessage $interopMessage)
     /**
      * @return InteropContext
      */
-    public function getContext()
+    public function getInteropContext()
     {
         return $this->context;
     }
diff --git a/Consumption/Extension/ReplyExtension.php b/Consumption/Extension/ReplyExtension.php
index 59d0a43..4435e55 100644
--- a/Consumption/Extension/ReplyExtension.php
+++ b/Consumption/Extension/ReplyExtension.php
@@ -35,9 +35,9 @@ public function onPostReceived(Context $context)
         $replyMessage = clone $result->getReply();
         $replyMessage->setCorrelationId($correlationId);
 
-        $replyQueue = $context->getContext()->createQueue($replyTo);
+        $replyQueue = $context->getInteropContext()->createQueue($replyTo);
 
         $context->getLogger()->debug(sprintf('[ReplyExtension] Send reply to "%s"', $replyTo));
-        $context->getContext()->createProducer()->send($replyQueue, $replyMessage);
+        $context->getInteropContext()->createProducer()->send($replyQueue, $replyMessage);
     }
 }
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 8125616..12d4bbf 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -320,7 +320,7 @@ private function processMessage(Consumer $consumer, Processor $processor, Intero
 
         $this->extension->onPreReceived($context);
         if (!$context->getResult()) {
-            $result = $processor->process($message, $context->getContext());
+            $result = $processor->process($message, $context->getInteropContext());
             $context->setResult($result);
         }
 
diff --git a/Tests/Consumption/ContextTest.php b/Tests/Consumption/ContextTest.php
index 4b5ff36..f110597 100644
--- a/Tests/Consumption/ContextTest.php
+++ b/Tests/Consumption/ContextTest.php
@@ -28,7 +28,7 @@ public function testShouldAllowGetSessionSetInConstructor()
 
         $context = new Context($interopContext);
 
-        $this->assertSame($interopContext, $context->getContext());
+        $this->assertSame($interopContext, $context->getInteropContext());
     }
 
     public function testShouldAllowGetMessageConsumerPreviouslySet()
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 096fa0e..5b655c1 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -539,7 +539,7 @@ public function testShouldCallOnStartExtensionMethod()
             ->method('onStart')
             ->with($this->isInstanceOf(Context::class))
             ->willReturnCallback(function (Context $context) use ($contextStub) {
-                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($contextStub, $context->getInteropContext());
                 $this->assertNull($context->getConsumer());
                 $this->assertNull($context->getProcessor());
                 $this->assertNull($context->getLogger());
@@ -573,7 +573,7 @@ public function testShouldCallOnIdleExtensionMethod()
             ->method('onIdle')
             ->with($this->isInstanceOf(Context::class))
             ->willReturnCallback(function (Context $context) use ($contextStub) {
-                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($contextStub, $context->getInteropContext());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
                 $this->assertNull($context->getProcessor());
                 $this->assertNull($context->getConsumer());
@@ -608,7 +608,7 @@ public function testShouldCallOnBeforeReceiveExtensionMethod()
             ->method('onBeforeReceive')
             ->with($this->isInstanceOf(Context::class))
             ->willReturnCallback(function (Context $context) use ($contextStub) {
-                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($contextStub, $context->getInteropContext());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
                 $this->assertNull($context->getProcessor());
                 $this->assertNull($context->getConsumer());
@@ -652,7 +652,7 @@ public function testShouldCallOnPreReceivedExtensionMethodWithExpectedContext()
                 $processorMock,
                 $expectedMessage
             ) {
-                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($contextStub, $context->getInteropContext());
                 $this->assertSame($consumerStub, $context->getConsumer());
                 $this->assertSame($processorMock, $context->getProcessor());
                 $this->assertSame($expectedMessage, $context->getInteropMessage());
@@ -695,7 +695,7 @@ public function testShouldCallOnResultExtensionMethodWithExpectedContext()
                 $processorMock,
                 $expectedMessage
             ) {
-                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($contextStub, $context->getInteropContext());
                 $this->assertSame($consumerStub, $context->getConsumer());
                 $this->assertSame($processorMock, $context->getProcessor());
                 $this->assertSame($expectedMessage, $context->getInteropMessage());
@@ -738,7 +738,7 @@ public function testShouldCallOnPostReceivedExtensionMethodWithExpectedContext()
                 $processorMock,
                 $expectedMessage
             ) {
-                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($contextStub, $context->getInteropContext());
                 $this->assertSame($consumerStub, $context->getConsumer());
                 $this->assertSame($processorMock, $context->getProcessor());
                 $this->assertSame($expectedMessage, $context->getInteropMessage());
@@ -779,7 +779,7 @@ public function testShouldAllowInterruptConsumingOnIdle()
             ->method('onInterrupted')
             ->with($this->isInstanceOf(Context::class))
             ->willReturnCallback(function (Context $context) use ($contextStub) {
-                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($contextStub, $context->getInteropContext());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
                 $this->assertNull($context->getConsumer());
                 $this->assertNull($context->getProcessor());
@@ -946,7 +946,7 @@ public function testShouldAllowInterruptConsumingOnPreReceiveButProcessCurrentMe
                 $processorMock,
                 $expectedMessage
             ) {
-                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($contextStub, $context->getInteropContext());
                 $this->assertSame($consumerStub, $context->getConsumer());
                 $this->assertSame($processorMock, $context->getProcessor());
                 $this->assertSame($expectedMessage, $context->getInteropMessage());
@@ -1002,7 +1002,7 @@ public function testShouldAllowInterruptConsumingOnResult()
                 $processorMock,
                 $expectedMessage
             ) {
-                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($contextStub, $context->getInteropContext());
                 $this->assertSame($consumerStub, $context->getConsumer());
                 $this->assertSame($processorMock, $context->getProcessor());
                 $this->assertSame($expectedMessage, $context->getInteropMessage());
@@ -1058,7 +1058,7 @@ public function testShouldAllowInterruptConsumingOnPostReceive()
                 $processorMock,
                 $expectedMessage
             ) {
-                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($contextStub, $context->getInteropContext());
                 $this->assertSame($consumerStub, $context->getConsumer());
                 $this->assertSame($processorMock, $context->getProcessor());
                 $this->assertSame($expectedMessage, $context->getInteropMessage());
@@ -1108,7 +1108,7 @@ public function testShouldCallOnInterruptedIfExceptionThrow()
                 $expectedMessage,
                 $expectedException
             ) {
-                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($contextStub, $context->getInteropContext());
                 $this->assertSame($consumerStub, $context->getConsumer());
                 $this->assertSame($processorMock, $context->getProcessor());
                 $this->assertSame($expectedMessage, $context->getInteropMessage());

From b26135c2b561f3508be7622ae2365e88137ae32c Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 28 Sep 2018 18:35:10 +0300
Subject: [PATCH 137/337] Remove tranposrt factories

---
 Symfony/AmqpTransportFactory.php         | 253 -----------------------
 Symfony/DefaultTransportFactory.php      |  93 ---------
 Symfony/DriverFactoryInterface.php       |  21 --
 Symfony/MissingTransportFactory.php      |  94 ---------
 Symfony/RabbitMqAmqpTransportFactory.php |  69 -------
 Symfony/TransportFactoryInterface.php    |  35 ----
 6 files changed, 565 deletions(-)
 delete mode 100644 Symfony/AmqpTransportFactory.php
 delete mode 100644 Symfony/DefaultTransportFactory.php
 delete mode 100644 Symfony/DriverFactoryInterface.php
 delete mode 100644 Symfony/MissingTransportFactory.php
 delete mode 100644 Symfony/RabbitMqAmqpTransportFactory.php
 delete mode 100644 Symfony/TransportFactoryInterface.php

diff --git a/Symfony/AmqpTransportFactory.php b/Symfony/AmqpTransportFactory.php
deleted file mode 100644
index 1be59c1..0000000
--- a/Symfony/AmqpTransportFactory.php
+++ /dev/null
@@ -1,253 +0,0 @@
-<?php
-
-namespace Enqueue\Symfony;
-
-use Enqueue\AmqpBunny\AmqpConnectionFactory as AmqpBunnyConnectionFactory;
-use Enqueue\AmqpExt\AmqpConnectionFactory as AmqpExtConnectionFactory;
-use Enqueue\AmqpLib\AmqpConnectionFactory as AmqpLibConnectionFactory;
-use Enqueue\Client\Driver\AmqpDriver;
-use Enqueue\ConnectionFactoryFactory;
-use Interop\Amqp\AmqpConnectionFactory;
-use Interop\Amqp\AmqpContext;
-use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\Definition;
-use Symfony\Component\DependencyInjection\Reference;
-
-class AmqpTransportFactory implements TransportFactoryInterface, DriverFactoryInterface
-{
-    /**
-     * @var string
-     */
-    private $name;
-
-    /**
-     * @param string $name
-     */
-    public function __construct($name = 'amqp')
-    {
-        $this->name = $name;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function addConfiguration(ArrayNodeDefinition $builder)
-    {
-        $transportsMap = static::getAvailableTransportsMap();
-
-        $builder
-            ->beforeNormalization()
-                ->ifTrue(function ($v) {
-                    return empty($v);
-                })
-                ->then(function ($v) {
-                    return ['dsn' => 'amqp:'];
-                })
-                ->ifString()
-                ->then(function ($v) {
-                    return ['dsn' => $v];
-                })
-            ->end()
-            ->children()
-                ->scalarNode('driver')
-                    ->validate()
-                    ->always(function ($v) use ($transportsMap) {
-                        $drivers = array_keys($transportsMap);
-                        if (empty($transportsMap)) {
-                            throw new \InvalidArgumentException('There is no amqp driver available. Please consider installing one of the packages: enqueue/amqp-ext, enqueue/amqp-lib, enqueue/amqp-bunny.');
-                        }
-
-                        if ($v && false == in_array($v, $drivers, true)) {
-                            throw new \InvalidArgumentException(sprintf('Unexpected driver given "%s". Available are "%s"', $v, implode('", "', $drivers)));
-                        }
-
-                        return $v;
-                    })
-                    ->end()
-                ->end()
-                ->scalarNode('dsn')
-                    ->info('The connection to AMQP broker set as a string. Other parameters could be used as defaults')
-                ->end()
-                ->scalarNode('host')
-                    ->info('The host to connect too. Note: Max 1024 characters')
-                ->end()
-                ->scalarNode('port')
-                    ->info('Port on the host.')
-                ->end()
-                ->scalarNode('user')
-                    ->info('The user name to use. Note: Max 128 characters.')
-                ->end()
-                ->scalarNode('pass')
-                    ->info('Password. Note: Max 128 characters.')
-                ->end()
-                ->scalarNode('vhost')
-                    ->info('The virtual host on the host. Note: Max 128 characters.')
-                ->end()
-                ->floatNode('connection_timeout')
-                    ->min(0)
-                    ->info('Connection timeout. Note: 0 or greater seconds. May be fractional.')
-                ->end()
-                ->floatNode('read_timeout')
-                    ->min(0)
-                    ->info('Timeout in for income activity. Note: 0 or greater seconds. May be fractional.')
-                ->end()
-                ->floatNode('write_timeout')
-                    ->min(0)
-                    ->info('Timeout in for outcome activity. Note: 0 or greater seconds. May be fractional.')
-                ->end()
-                ->floatNode('heartbeat')
-                    ->min(0)
-                    ->info('How often to send heartbeat. 0 means off.')
-                ->end()
-                ->booleanNode('persisted')->end()
-                ->booleanNode('lazy')->end()
-                ->enumNode('receive_method')
-                    ->values(['basic_get', 'basic_consume'])
-                    ->info('The receive strategy to be used. We suggest to use basic_consume as it is more performant. Though you need AMQP extension 1.9.1 or higher')
-                ->end()
-                ->floatNode('qos_prefetch_size')
-                    ->min(0)
-                    ->info('The server will send a message in advance if it is equal to or smaller in size than the available prefetch size. May be set to zero, meaning "no specific limit"')
-                ->end()
-                ->floatNode('qos_prefetch_count')
-                    ->min(0)
-                    ->info('Specifies a prefetch window in terms of whole messages')
-                ->end()
-                ->booleanNode('qos_global')
-                    ->info('If "false" the QoS settings apply to the current channel only. If this field is "true", they are applied to the entire connection.')
-                ->end()
-                ->variableNode('driver_options')
-                    ->info('The options that are specific to the amqp transport you chose. For example amqp+lib have insist, keepalive, stream options. amqp+bunny has tcp_nodelay extra option.')
-                ->end()
-                ->booleanNode('ssl_on')
-                    ->info('Should be true if you want to use secure connections. False by default')
-                ->end()
-                ->booleanNode('ssl_verify')
-                    ->info('This option determines whether ssl client verifies that the server cert is for the server it is known as. True by default.')
-                ->end()
-                ->scalarNode('ssl_cacert')
-                    ->info('Location of Certificate Authority file on local filesystem which should be used with the verify_peer context option to authenticate the identity of the remote peer. A string.')
-                ->end()
-                ->scalarNode('ssl_cert')
-                    ->info('Path to local certificate file on filesystem. It must be a PEM encoded file which contains your certificate and private key. A string')
-                ->end()
-                ->scalarNode('ssl_key')
-                    ->info('Path to local private key file on filesystem in case of separate files for certificate (local_cert) and private key. A string.')
-                ->end()
-        ;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function createConnectionFactory(ContainerBuilder $container, array $config)
-    {
-        if (array_key_exists('driver_options', $config) && is_array($config['driver_options'])) {
-            $driverOptions = $config['driver_options'];
-            unset($config['driver_options']);
-
-            $config = array_replace($driverOptions, $config);
-        }
-
-        $factory = new Definition(AmqpConnectionFactory::class);
-        $factory->setFactory([self::class, 'createConnectionFactoryFactory']);
-        $factory->setArguments([$config]);
-
-        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
-        $container->setDefinition($factoryId, $factory);
-
-        return $factoryId;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function createContext(ContainerBuilder $container, array $config)
-    {
-        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
-
-        $context = new Definition(AmqpContext::class);
-        $context->setPublic(true);
-        $context->setFactory([new Reference($factoryId), 'createContext']);
-
-        $contextId = sprintf('enqueue.transport.%s.context', $this->getName());
-        $container->setDefinition($contextId, $context);
-
-        return $contextId;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function createDriver(ContainerBuilder $container, array $config)
-    {
-        $driver = new Definition(AmqpDriver::class);
-        $driver->setPublic(true);
-        $driver->setArguments([
-            new Reference(sprintf('enqueue.transport.%s.context', $this->getName())),
-            new Reference('enqueue.client.config'),
-            new Reference('enqueue.client.meta.queue_meta_registry'),
-        ]);
-
-        $driverId = sprintf('enqueue.client.%s.driver', $this->getName());
-        $container->setDefinition($driverId, $driver);
-
-        return $driverId;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function getName()
-    {
-        return $this->name;
-    }
-
-    public static function createConnectionFactoryFactory(array $config)
-    {
-        if (false == empty($config['driver'])) {
-            $transportsMap = static::getAvailableTransportsMap();
-
-            if (false == array_key_exists($config['driver'], $transportsMap)) {
-                throw new \InvalidArgumentException(sprintf('Unexpected driver given "invalidDriver". Available are "%s"', implode('", "', array_keys($transportsMap))));
-            }
-
-            $connectionFactoryClass = $transportsMap[$config['driver']];
-
-            unset($config['driver']);
-
-            return new $connectionFactoryClass($config);
-        }
-
-        $dsn = array_key_exists('dsn', $config) ? $config['dsn'] : 'amqp:';
-        $factory = (new ConnectionFactoryFactory())->create($dsn);
-
-        if (false == $factory instanceof  AmqpConnectionFactory) {
-            throw new \LogicException(sprintf('Factory must be instance of "%s" but got "%s"', AmqpConnectionFactory::class, get_class($factory)));
-        }
-
-        $factoryClass = get_class($factory);
-
-        return new $factoryClass($config);
-    }
-
-    /**
-     * @return string[]
-     */
-    private static function getAvailableTransportsMap()
-    {
-        $map = [];
-        if (class_exists(AmqpExtConnectionFactory::class)) {
-            $map['ext'] = AmqpExtConnectionFactory::class;
-        }
-        if (class_exists(AmqpLibConnectionFactory::class)) {
-            $map['lib'] = AmqpLibConnectionFactory::class;
-        }
-        if (class_exists(AmqpBunnyConnectionFactory::class)) {
-            $map['bunny'] = AmqpBunnyConnectionFactory::class;
-        }
-
-        return $map;
-    }
-}
diff --git a/Symfony/DefaultTransportFactory.php b/Symfony/DefaultTransportFactory.php
deleted file mode 100644
index 3076697..0000000
--- a/Symfony/DefaultTransportFactory.php
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-
-namespace Enqueue\Symfony;
-
-use Enqueue\Client\DriverInterface;
-use Interop\Queue\PsrConnectionFactory;
-use Interop\Queue\PsrContext;
-use Symfony\Component\Config\Definition\Builder\VariableNodeDefinition;
-use Symfony\Component\DependencyInjection\Alias;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\Reference;
-
-class DefaultTransportFactory
-{
-    public function addConfiguration(VariableNodeDefinition $builder): void
-    {
-        $builder
-            ->beforeNormalization()
-                ->always(function ($v) {
-                    if (empty($v)) {
-                        return ['dsn' => 'null:'];
-                    }
-
-                    if (is_array($v)) {
-                        return $v;
-                    }
-
-                    if (is_string($v)) {
-                        return ['dsn' => $v];
-                    }
-
-                    throw new \LogicException(sprintf('The value must be array, null or string. Got "%s"', gettype($v)));
-                })
-            ->end()
-            ->pro
-            ->children()
-                ->scalarNode('dsn')->cannotBeEmpty()->end()
-                ->variableNode('config')->end()
-            ->end()
-        ->end()
-        ;
-    }
-
-    public function createConnectionFactory(ContainerBuilder $container, array $config): string
-    {
-        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
-
-        $container->register($factoryId, PsrConnectionFactory::class)
-            ->setFactory([new Reference('enqueue.connection_factory_factory'), 'create'])
-            ->addArgument($config['dsn'])
-        ;
-
-        $container->setAlias('enqueue.transport.connection_factory', new Alias($factoryId, true));
-
-        return $factoryId;
-    }
-
-    public function createContext(ContainerBuilder $container, array $config): string
-    {
-        $contextId = sprintf('enqueue.transport.%s.context', $this->getName());
-        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
-
-        $container->register($contextId, PsrContext::class)
-            ->setFactory([new Reference($factoryId), 'createContext'])
-        ;
-
-        $container->setAlias('enqueue.transport.context', new Alias($contextId, true));
-
-        return $contextId;
-    }
-
-    public function createDriver(ContainerBuilder $container, array $config): string
-    {
-        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
-        $driverId = sprintf('enqueue.client.%s.driver', $this->getName());
-
-        $container->register($driverId, DriverInterface::class)
-            ->setFactory([new Reference('enqueue.client.driver_factory'), 'create'])
-            ->addArgument(new Reference($factoryId))
-            ->addArgument($config['dsn'])
-            ->addArgument($config)
-        ;
-
-        $container->setAlias('enqueue.client.driver', new Alias($driverId, true));
-
-        return $driverId;
-    }
-
-    public function getName(): string
-    {
-        return 'default';
-    }
-}
diff --git a/Symfony/DriverFactoryInterface.php b/Symfony/DriverFactoryInterface.php
deleted file mode 100644
index 0b1ab02..0000000
--- a/Symfony/DriverFactoryInterface.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-namespace Enqueue\Symfony;
-
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-
-interface DriverFactoryInterface
-{
-    /**
-     * @param ContainerBuilder $container
-     * @param array            $config
-     *
-     * @return string The method must return a driver service id
-     */
-    public function createDriver(ContainerBuilder $container, array $config);
-
-    /**
-     * @return string
-     */
-    public function getName();
-}
diff --git a/Symfony/MissingTransportFactory.php b/Symfony/MissingTransportFactory.php
deleted file mode 100644
index 25e6791..0000000
--- a/Symfony/MissingTransportFactory.php
+++ /dev/null
@@ -1,94 +0,0 @@
-<?php
-
-namespace Enqueue\Symfony;
-
-use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-
-class MissingTransportFactory implements TransportFactoryInterface, DriverFactoryInterface
-{
-    /**
-     * @var string
-     */
-    private $name;
-    /**
-     * @var string[]
-     */
-    private $packages;
-
-    /**
-     * @param string   $name
-     * @param string[] $packages
-     */
-    public function __construct($name, array $packages)
-    {
-        $this->name = $name;
-        $this->packages = $packages;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function addConfiguration(ArrayNodeDefinition $builder)
-    {
-        if (1 == count($this->packages)) {
-            $message = sprintf(
-                'In order to use the transport "%s" install a package "%s"',
-                $this->getName(),
-                implode('", "', $this->packages)
-            );
-        } else {
-            $message = sprintf(
-                'In order to use the transport "%s" install one of the packages "%s"',
-                $this->getName(),
-                implode('", "', $this->packages)
-            );
-        }
-
-        $builder
-            ->info($message)
-            ->beforeNormalization()
-                ->always(function () {
-                    return [];
-                })
-                ->end()
-            ->validate()
-                ->always(function () use ($message) {
-                    throw new \InvalidArgumentException($message);
-                })
-                ->end()
-        ;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function createConnectionFactory(ContainerBuilder $container, array $config)
-    {
-        throw new \LogicException('Should not be called');
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function createContext(ContainerBuilder $container, array $config)
-    {
-        throw new \LogicException('Should not be called');
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function createDriver(ContainerBuilder $container, array $config)
-    {
-        throw new \LogicException('Should not be called');
-    }
-
-    /**
-     * @return string
-     */
-    public function getName()
-    {
-        return $this->name;
-    }
-}
diff --git a/Symfony/RabbitMqAmqpTransportFactory.php b/Symfony/RabbitMqAmqpTransportFactory.php
deleted file mode 100644
index f02aa15..0000000
--- a/Symfony/RabbitMqAmqpTransportFactory.php
+++ /dev/null
@@ -1,69 +0,0 @@
-<?php
-
-namespace Enqueue\Symfony;
-
-use Enqueue\AmqpTools\DelayStrategyTransportFactoryTrait;
-use Enqueue\Client\Driver\RabbitMqDriver;
-use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\Definition;
-use Symfony\Component\DependencyInjection\Reference;
-
-class RabbitMqAmqpTransportFactory extends AmqpTransportFactory
-{
-    use DelayStrategyTransportFactoryTrait;
-
-    /**
-     * @param string $name
-     */
-    public function __construct($name = 'rabbitmq_amqp')
-    {
-        parent::__construct($name);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function addConfiguration(ArrayNodeDefinition $builder)
-    {
-        parent::addConfiguration($builder);
-
-        $builder
-            ->children()
-                ->scalarNode('delay_strategy')
-                    ->defaultValue('dlx')
-                    ->info('The delay strategy to be used. Possible values are "dlx", "delayed_message_plugin" or service id')
-                ->end()
-        ;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function createConnectionFactory(ContainerBuilder $container, array $config)
-    {
-        $factoryId = parent::createConnectionFactory($container, $config);
-
-        $this->registerDelayStrategy($container, $config, $factoryId, $this->getName());
-
-        return $factoryId;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function createDriver(ContainerBuilder $container, array $config)
-    {
-        $driver = new Definition(RabbitMqDriver::class);
-        $driver->setPublic(true);
-        $driver->setArguments([
-            new Reference(sprintf('enqueue.transport.%s.context', $this->getName())),
-            new Reference('enqueue.client.config'),
-            new Reference('enqueue.client.meta.queue_meta_registry'),
-        ]);
-        $driverId = sprintf('enqueue.client.%s.driver', $this->getName());
-        $container->setDefinition($driverId, $driver);
-
-        return $driverId;
-    }
-}
diff --git a/Symfony/TransportFactoryInterface.php b/Symfony/TransportFactoryInterface.php
deleted file mode 100644
index d2be801..0000000
--- a/Symfony/TransportFactoryInterface.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-namespace Enqueue\Symfony;
-
-use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-
-interface TransportFactoryInterface
-{
-    /**
-     * @param ArrayNodeDefinition $builder
-     */
-    public function addConfiguration(ArrayNodeDefinition $builder);
-
-    /**
-     * @param ContainerBuilder $container
-     * @param array            $config
-     *
-     * @return string The method must return a factory service id
-     */
-    public function createConnectionFactory(ContainerBuilder $container, array $config);
-
-    /**
-     * @param ContainerBuilder $container
-     * @param array            $config
-     *
-     * @return string The method must return a context service id
-     */
-    public function createContext(ContainerBuilder $container, array $config);
-
-    /**
-     * @return string
-     */
-    public function getName();
-}

From 6c5128079a846bc2081c3800ca101ffc3a87baf5 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 28 Sep 2018 19:00:31 +0300
Subject: [PATCH 138/337] Merge master

---
 Client/Config.php                             | 37 ++--------
 .../ExclusiveCommandExtension.php             | 10 +--
 .../SetRouterPropertiesExtension.php          |  4 +-
 Client/DelegateProcessor.php                  |  4 +-
 Client/Driver/GenericDriver.php               | 18 ++---
 Client/Driver/RabbitMqStompDriver.php         |  4 +-
 Client/DriverPreSend.php                      |  8 +--
 Client/PostSend.php                           |  8 +--
 Client/Producer.php                           |  8 +--
 Client/RouterProcessor.php                    | 10 +--
 Tests/Client/ConfigTest.php                   | 70 -------------------
 .../ExclusiveCommandExtensionTest.php         | 16 ++---
 .../SetRouterPropertiesExtensionTest.php      |  6 +-
 Tests/Client/DelegateProcessorTest.php        |  4 +-
 Tests/Client/Driver/AmqpDriverTest.php        |  2 +-
 .../Client/Driver/GenericDriverTestsTrait.php | 64 ++++++++---------
 .../Client/Driver/RabbitMqStompDriverTest.php |  4 +-
 Tests/Client/DriverPreSendTest.php            |  4 +-
 Tests/Client/PostSendTest.php                 |  4 +-
 Tests/Client/ProducerSendCommandTest.php      | 14 ++--
 Tests/Client/ProducerSendEventTest.php        | 16 ++---
 Tests/Client/RouterProcessorTest.php          | 25 +++----
 22 files changed, 119 insertions(+), 221 deletions(-)

diff --git a/Client/Config.php b/Client/Config.php
index f85d25d..07bd3e0 100644
--- a/Client/Config.php
+++ b/Client/Config.php
@@ -4,21 +4,12 @@
 
 class Config
 {
-    const PARAMETER_TOPIC_NAME = 'enqueue.topic_name';
-    const PARAMETER_COMMAND_NAME = 'enqueue.command_name';
-    const PARAMETER_PROCESSOR_NAME = 'enqueue.processor_name';
-
-    /**
-     * @deprecated
-     */
-    const PARAMETER_PROCESSOR_QUEUE_NAME = 'enqueue.processor_queue_name';
-
-    const DEFAULT_PROCESSOR_QUEUE_NAME = 'default';
-
-    /**
-     * @deprecated
-     */
-    const COMMAND_TOPIC = '__command__';
+    const TOPIC_PARAMETER = 'enqueue.topic';
+    const COMMAND_PARAMETER = 'enqueue.command';
+    const PROCESSOR_PARAMETER = 'enqueue.processor';
+    const EXPIRE_PARAMETER = 'enqueue.expire';
+    const PRIORITY_PARAMETER = 'enqueue.priority';
+    const DELAY_PARAMETER = 'enqueue.delay';
 
     /**
      * @var string
@@ -118,22 +109,6 @@ public function getRouterProcessorName(): string
         return $this->routerProcessorName;
     }
 
-    /**
-     * @deprecated
-     */
-    public function createTransportRouterTopicName(string $name): string
-    {
-        return strtolower(implode('.', array_filter([trim($this->prefix), trim($name)])));
-    }
-
-    /**
-     * @deprecated
-     */
-    public function createTransportQueueName(string $name): string
-    {
-        return strtolower(implode('.', array_filter([trim($this->prefix), trim($this->appName), trim($name)])));
-    }
-
     /**
      * @deprecated
      *
diff --git a/Client/ConsumptionExtension/ExclusiveCommandExtension.php b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
index 64a2d45..2ebd8a6 100644
--- a/Client/ConsumptionExtension/ExclusiveCommandExtension.php
+++ b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
@@ -33,13 +33,13 @@ public function onPreReceived(Context $context)
         $message = $context->getInteropMessage();
         $queue = $context->getInteropQueue();
 
-        if ($message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
+        if ($message->getProperty(Config::TOPIC_PARAMETER)) {
             return;
         }
-        if ($message->getProperty(Config::PARAMETER_COMMAND_NAME)) {
+        if ($message->getProperty(Config::COMMAND_PARAMETER)) {
             return;
         }
-        if ($message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
+        if ($message->getProperty(Config::PROCESSOR_PARAMETER)) {
             return;
         }
 
@@ -51,8 +51,8 @@ public function onPreReceived(Context $context)
             $context->getLogger()->debug('[ExclusiveCommandExtension] This is a exclusive command queue and client\'s properties are not set. Setting them');
 
             $route = $this->queueToRouteMap[$queue->getQueueName()];
-            $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $route->getProcessor());
-            $message->setProperty(Config::PARAMETER_COMMAND_NAME, $route->getSource());
+            $message->setProperty(Config::PROCESSOR_PARAMETER, $route->getProcessor());
+            $message->setProperty(Config::COMMAND_PARAMETER, $route->getSource());
         }
     }
 
diff --git a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
index 0b91834..d5c3cee 100644
--- a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
+++ b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
@@ -28,7 +28,7 @@ public function __construct(DriverInterface $driver)
     public function onPreReceived(Context $context)
     {
         $message = $context->getInteropMessage();
-        if ($message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
+        if ($message->getProperty(Config::PROCESSOR_PARAMETER)) {
             return;
         }
 
@@ -39,7 +39,7 @@ public function onPreReceived(Context $context)
         }
 
         // RouterProcessor is our default message processor when that header is not set
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $config->getRouterProcessorName());
+        $message->setProperty(Config::PROCESSOR_PARAMETER, $config->getRouterProcessorName());
 
         $context->getLogger()->debug(
             '[SetRouterPropertiesExtension] '.
diff --git a/Client/DelegateProcessor.php b/Client/DelegateProcessor.php
index 2996684..920c4af 100644
--- a/Client/DelegateProcessor.php
+++ b/Client/DelegateProcessor.php
@@ -26,11 +26,11 @@ public function __construct(ProcessorRegistryInterface $registry)
      */
     public function process(InteropMessage $message, Context $context)
     {
-        $processorName = $message->getProperty(Config::PARAMETER_PROCESSOR_NAME);
+        $processorName = $message->getProperty(Config::PROCESSOR_PARAMETER);
         if (false == $processorName) {
             throw new \LogicException(sprintf(
                 'Got message without required parameter: "%s"',
-                Config::PARAMETER_PROCESSOR_NAME
+                Config::PROCESSOR_PARAMETER
             ));
         }
 
diff --git a/Client/Driver/GenericDriver.php b/Client/Driver/GenericDriver.php
index 6391466..627e63e 100644
--- a/Client/Driver/GenericDriver.php
+++ b/Client/Driver/GenericDriver.php
@@ -47,10 +47,10 @@ public function __construct(
 
     public function sendToRouter(Message $message): void
     {
-        if ($message->getProperty(Config::PARAMETER_COMMAND_NAME)) {
+        if ($message->getProperty(Config::COMMAND_PARAMETER)) {
             throw new \LogicException('Command must not be send to router but go directly to its processor.');
         }
-        if (false == $message->getProperty(Config::PARAMETER_TOPIC_NAME)) {
+        if (false == $message->getProperty(Config::TOPIC_PARAMETER)) {
             throw new \LogicException('Topic name parameter is required but is not set');
         }
 
@@ -63,21 +63,21 @@ public function sendToRouter(Message $message): void
 
     public function sendToProcessor(Message $message): void
     {
-        $topic = $message->getProperty(Config::PARAMETER_TOPIC_NAME);
-        $command = $message->getProperty(Config::PARAMETER_COMMAND_NAME);
+        $topic = $message->getProperty(Config::TOPIC_PARAMETER);
+        $command = $message->getProperty(Config::COMMAND_PARAMETER);
 
         /** @var InteropQueue $queue */
         $queue = null;
-        if ($topic && $processor = $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
+        if ($topic && $processor = $message->getProperty(Config::PROCESSOR_PARAMETER)) {
             $route = $this->routeCollection->topicAndProcessor($topic, $processor);
             if (false == $route) {
                 throw new \LogicException(sprintf('There is no route for topic "%s" and processor "%s"', $topic, $processor));
             }
 
-            $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $route->getProcessor());
+            $message->setProperty(Config::PROCESSOR_PARAMETER, $route->getProcessor());
             $queue = $this->createRouteQueue($route);
-        } elseif ($topic && false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
-            $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $this->config->getRouterProcessorName());
+        } elseif ($topic && false == $message->getProperty(Config::PROCESSOR_PARAMETER)) {
+            $message->setProperty(Config::PROCESSOR_PARAMETER, $this->config->getRouterProcessorName());
 
             $queue = $this->createQueue($this->config->getRouterQueueName());
         } elseif ($command) {
@@ -86,7 +86,7 @@ public function sendToProcessor(Message $message): void
                 throw new \LogicException(sprintf('There is no route for command "%s".', $command));
             }
 
-            $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $route->getProcessor());
+            $message->setProperty(Config::PROCESSOR_PARAMETER, $route->getProcessor());
             $queue = $this->createRouteQueue($route);
         } else {
             throw new \LogicException('Either topic or command parameter must be set.');
diff --git a/Client/Driver/RabbitMqStompDriver.php b/Client/Driver/RabbitMqStompDriver.php
index fc15953..77b4293 100644
--- a/Client/Driver/RabbitMqStompDriver.php
+++ b/Client/Driver/RabbitMqStompDriver.php
@@ -76,7 +76,7 @@ public function setupBroker(LoggerInterface $logger = null): void
         }
 
         // setup router
-        $routerExchange = $this->getConfig()->createTransportRouterTopicName($this->getConfig()->getRouterTopicName());
+        $routerExchange = $this->createTransportRouterTopicName($this->getConfig()->getRouterTopicName(), true);
         $log('Declare router exchange: %s', $routerExchange);
         $this->management->declareExchange($routerExchange, [
             'type' => 'fanout',
@@ -84,7 +84,7 @@ public function setupBroker(LoggerInterface $logger = null): void
             'auto_delete' => false,
         ]);
 
-        $routerQueue = $this->getConfig()->createTransportQueueName($this->getConfig()->getRouterQueueName());
+        $routerQueue = $this->createTransportQueueName($this->getConfig()->getRouterQueueName(), true);
         $log('Declare router queue: %s', $routerQueue);
         $this->management->declareQueue($routerQueue, [
             'auto_delete' => false,
diff --git a/Client/DriverPreSend.php b/Client/DriverPreSend.php
index d940fc0..8a92fd9 100644
--- a/Client/DriverPreSend.php
+++ b/Client/DriverPreSend.php
@@ -34,21 +34,21 @@ public function getDriver(): DriverInterface
 
     public function isEvent(): bool
     {
-        return (bool) $this->message->getProperty(Config::PARAMETER_TOPIC_NAME);
+        return (bool) $this->message->getProperty(Config::TOPIC_PARAMETER);
     }
 
     public function isCommand(): bool
     {
-        return (bool) $this->message->getProperty(Config::PARAMETER_COMMAND_NAME);
+        return (bool) $this->message->getProperty(Config::COMMAND_PARAMETER);
     }
 
     public function getCommand(): string
     {
-        return $this->message->getProperty(Config::PARAMETER_COMMAND_NAME);
+        return $this->message->getProperty(Config::COMMAND_PARAMETER);
     }
 
     public function getTopic(): string
     {
-        return $this->message->getProperty(Config::PARAMETER_TOPIC_NAME);
+        return $this->message->getProperty(Config::TOPIC_PARAMETER);
     }
 }
diff --git a/Client/PostSend.php b/Client/PostSend.php
index 5f575e2..5cfafb3 100644
--- a/Client/PostSend.php
+++ b/Client/PostSend.php
@@ -34,21 +34,21 @@ public function getDriver(): DriverInterface
 
     public function isEvent(): bool
     {
-        return (bool) $this->message->getProperty(Config::PARAMETER_TOPIC_NAME);
+        return (bool) $this->message->getProperty(Config::TOPIC_PARAMETER);
     }
 
     public function isCommand(): bool
     {
-        return (bool) $this->message->getProperty(Config::PARAMETER_COMMAND_NAME);
+        return (bool) $this->message->getProperty(Config::COMMAND_PARAMETER);
     }
 
     public function getCommand(): string
     {
-        return $this->message->getProperty(Config::PARAMETER_COMMAND_NAME);
+        return $this->message->getProperty(Config::COMMAND_PARAMETER);
     }
 
     public function getTopic(): string
     {
-        return $this->message->getProperty(Config::PARAMETER_TOPIC_NAME);
+        return $this->message->getProperty(Config::TOPIC_PARAMETER);
     }
 }
diff --git a/Client/Producer.php b/Client/Producer.php
index 977b4b5..e6aae73 100644
--- a/Client/Producer.php
+++ b/Client/Producer.php
@@ -48,7 +48,7 @@ public function sendEvent(string $topic, $message): void
         $this->extension->onPreSendEvent($preSend);
 
         $message = $preSend->getMessage();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, $preSend->getTopic());
+        $message->setProperty(Config::TOPIC_PARAMETER, $preSend->getTopic());
 
         $this->doSend($message);
     }
@@ -79,7 +79,7 @@ public function sendCommand(string $command, $message, bool $needReply = false):
             }
         }
 
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, $command);
+        $message->setProperty(Config::COMMAND_PARAMETER, $command);
         $message->setScope(Message::SCOPE_APP);
 
         $this->doSend($message);
@@ -103,8 +103,8 @@ private function doSend(Message $message): void
             ));
         }
 
-        if ($message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) {
-            throw new \LogicException(sprintf('The %s property must not be set.', Config::PARAMETER_PROCESSOR_NAME));
+        if ($message->getProperty(Config::PROCESSOR_PARAMETER)) {
+            throw new \LogicException(sprintf('The %s property must not be set.', Config::PROCESSOR_PARAMETER));
         }
 
         if (!$message->getMessageId()) {
diff --git a/Client/RouterProcessor.php b/Client/RouterProcessor.php
index d106e29..220d8ec 100644
--- a/Client/RouterProcessor.php
+++ b/Client/RouterProcessor.php
@@ -21,22 +21,22 @@ public function __construct(DriverInterface $driver)
 
     public function process(InteropMessage $message, Context $context): Result
     {
-        if ($message->getProperty(Config::PARAMETER_COMMAND_NAME)) {
+        if ($message->getProperty(Config::COMMAND_PARAMETER)) {
             return Result::reject(sprintf(
                 'Unexpected command "%s" got. Command must not go to the router.',
-                $message->getProperty(Config::PARAMETER_COMMAND_NAME)
+                $message->getProperty(Config::COMMAND_PARAMETER)
             ));
         }
 
-        $topic = $message->getProperty(Config::PARAMETER_TOPIC_NAME);
+        $topic = $message->getProperty(Config::TOPIC_PARAMETER);
         if (false == $topic) {
-            return Result::reject(sprintf('Topic property "%s" is required but not set or empty.', Config::PARAMETER_TOPIC_NAME));
+            return Result::reject(sprintf('Topic property "%s" is required but not set or empty.', Config::TOPIC_PARAMETER));
         }
 
         $count = 0;
         foreach ($this->driver->getRouteCollection()->topic($topic) as $route) {
             $clientMessage = $this->driver->createClientMessage($message);
-            $clientMessage->setProperty(Config::PARAMETER_PROCESSOR_NAME, $route->getProcessor());
+            $clientMessage->setProperty(Config::PROCESSOR_PARAMETER, $route->getProcessor());
 
             $this->driver->sendToProcessor($clientMessage);
 
diff --git a/Tests/Client/ConfigTest.php b/Tests/Client/ConfigTest.php
index d616b56..e130319 100644
--- a/Tests/Client/ConfigTest.php
+++ b/Tests/Client/ConfigTest.php
@@ -125,76 +125,6 @@ public function testShouldReturnDefaultQueueNameSetInConstructor()
         $this->assertEquals('aDefaultQueueName', $config->getDefaultProcessorQueueName());
     }
 
-    public function testShouldCreateRouterTopicName()
-    {
-        $config = new Config(
-            'aPrefix',
-            'aApp',
-            'aRouterTopicName',
-            'aRouterQueueName',
-            'aDefaultQueueName',
-            'aRouterProcessorName'
-        );
-
-        $this->assertEquals('aprefix.aname', $config->createTransportRouterTopicName('aName'));
-    }
-
-    public function testShouldCreateProcessorQueueName()
-    {
-        $config = new Config(
-            'aPrefix',
-            'aApp',
-            'aRouterTopicName',
-            'aRouterQueueName',
-            'aDefaultQueueName',
-            'aRouterProcessorName'
-        );
-
-        $this->assertEquals('aprefix.aapp.aname', $config->createTransportQueueName('aName'));
-    }
-
-    public function testShouldCreateProcessorQueueNameWithoutAppName()
-    {
-        $config = new Config(
-            'aPrefix',
-            '',
-            'aRouterTopicName',
-            'aRouterQueueName',
-            'aDefaultQueueName',
-            'aRouterProcessorName'
-        );
-
-        $this->assertEquals('aprefix.aname', $config->createTransportQueueName('aName'));
-    }
-
-    public function testShouldCreateProcessorQueueNameWithoutPrefix()
-    {
-        $config = new Config(
-            '',
-            'aApp',
-            'aRouterTopicName',
-            'aRouterQueueName',
-            'aDefaultQueueName',
-            'aRouterProcessorName'
-        );
-
-        $this->assertEquals('aapp.aname', $config->createTransportQueueName('aName'));
-    }
-
-    public function testShouldCreateProcessorQueueNameWithoutPrefixAndAppName()
-    {
-        $config = new Config(
-            '',
-            '',
-            'aRouterTopicName',
-            'aRouterQueueName',
-            'aDefaultQueueName',
-            'aRouterProcessorName'
-        );
-
-        $this->assertEquals('aname', $config->createTransportQueueName('aName'));
-    }
-
     public function testShouldCreateDefaultConfig()
     {
         $config = Config::create();
diff --git a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
index ff10c80..6343371 100644
--- a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
@@ -38,7 +38,7 @@ public function testCouldBeConstructedWithDriverAsFirstArgument()
     public function testShouldDoNothingIfMessageHasTopicPropertySetOnPreReceive()
     {
         $message = new NullMessage();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'aTopic');
+        $message->setProperty(Config::TOPIC_PARAMETER, 'aTopic');
 
         $context = new Context(new NullContext());
         $context->setInteropMessage($message);
@@ -56,14 +56,14 @@ public function testShouldDoNothingIfMessageHasTopicPropertySetOnPreReceive()
         self::assertNull($context->getResult());
 
         $this->assertEquals([
-            'enqueue.topic_name' => 'aTopic',
+            'enqueue.topic' => 'aTopic',
         ], $message->getProperties());
     }
 
     public function testShouldDoNothingIfMessageHasCommandPropertySetOnPreReceive()
     {
         $message = new NullMessage();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'aCommand');
+        $message->setProperty(Config::COMMAND_PARAMETER, 'aCommand');
 
         $context = new Context(new NullContext());
         $context->setInteropMessage($message);
@@ -81,14 +81,14 @@ public function testShouldDoNothingIfMessageHasCommandPropertySetOnPreReceive()
         self::assertNull($context->getResult());
 
         $this->assertEquals([
-            'enqueue.command_name' => 'aCommand',
+            'enqueue.command' => 'aCommand',
         ], $message->getProperties());
     }
 
     public function testShouldDoNothingIfMessageHasProcessorPropertySetOnPreReceive()
     {
         $message = new NullMessage();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aProcessor');
+        $message->setProperty(Config::PROCESSOR_PARAMETER, 'aProcessor');
 
         $context = new Context(new NullContext());
         $context->setInteropMessage($message);
@@ -106,7 +106,7 @@ public function testShouldDoNothingIfMessageHasProcessorPropertySetOnPreReceive(
         self::assertNull($context->getResult());
 
         $this->assertEquals([
-            'enqueue.processor_name' => 'aProcessor',
+            'enqueue.processor' => 'aProcessor',
         ], $message->getProperties());
     }
 
@@ -164,8 +164,8 @@ public function testShouldSetCommandPropertiesIfCurrentQueueHasExclusiveCommandP
         self::assertNull($context->getResult());
 
         $this->assertEquals([
-            'enqueue.processor_name' => 'theFooProcessor',
-            'enqueue.command_name' => 'fooCommand',
+            'enqueue.processor' => 'theFooProcessor',
+            'enqueue.command' => 'fooCommand',
         ], $message->getProperties());
     }
 
diff --git a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
index 395042a..e4ae8a6 100644
--- a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
@@ -57,7 +57,7 @@ public function testShouldSetRouterProcessorPropertyIfNotSetAndOnRouterQueue()
         $extension->onPreReceived($context);
 
         $this->assertEquals([
-            'enqueue.processor_name' => 'router-processor-name',
+            'enqueue.processor' => 'router-processor-name',
         ], $message->getProperties());
     }
 
@@ -100,7 +100,7 @@ public function testShouldNotSetAnyPropertyIfProcessorNamePropertyAlreadySet()
         ;
 
         $message = new NullMessage();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'non-router-processor');
+        $message->setProperty(Config::PROCESSOR_PARAMETER, 'non-router-processor');
 
         $context = new Context($this->createContextMock());
         $context->setInteropMessage($message);
@@ -109,7 +109,7 @@ public function testShouldNotSetAnyPropertyIfProcessorNamePropertyAlreadySet()
         $extension->onPreReceived($context);
 
         $this->assertEquals([
-            'enqueue.processor_name' => 'non-router-processor',
+            'enqueue.processor' => 'non-router-processor',
         ], $message->getProperties());
     }
 
diff --git a/Tests/Client/DelegateProcessorTest.php b/Tests/Client/DelegateProcessorTest.php
index 4bf688f..4242068 100644
--- a/Tests/Client/DelegateProcessorTest.php
+++ b/Tests/Client/DelegateProcessorTest.php
@@ -21,7 +21,7 @@ public function testShouldThrowExceptionIfProcessorNameIsNotSet()
     {
         $this->setExpectedException(
             \LogicException::class,
-            'Got message without required parameter: "enqueue.processor_name"'
+            'Got message without required parameter: "enqueue.processor"'
         );
 
         $processor = new DelegateProcessor($this->createProcessorRegistryMock());
@@ -33,7 +33,7 @@ public function testShouldProcessMessage()
         $session = $this->createContextMock();
         $message = new NullMessage();
         $message->setProperties([
-            Config::PARAMETER_PROCESSOR_NAME => 'processor-name',
+            Config::PROCESSOR_PARAMETER => 'processor-name',
         ]);
 
         $processor = $this->createProcessorMock();
diff --git a/Tests/Client/Driver/AmqpDriverTest.php b/Tests/Client/Driver/AmqpDriverTest.php
index 11539cb..e7d76e2 100644
--- a/Tests/Client/Driver/AmqpDriverTest.php
+++ b/Tests/Client/Driver/AmqpDriverTest.php
@@ -170,7 +170,7 @@ public function testShouldResetPriorityAndExpirationAndNeverCallProducerDelivery
         );
 
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
         $message->setExpire(123);
         $message->setPriority(MessagePriority::HIGH);
 
diff --git a/Tests/Client/Driver/GenericDriverTestsTrait.php b/Tests/Client/Driver/GenericDriverTestsTrait.php
index 46c308b..782add3 100644
--- a/Tests/Client/Driver/GenericDriverTestsTrait.php
+++ b/Tests/Client/Driver/GenericDriverTestsTrait.php
@@ -284,7 +284,7 @@ public function testShouldSendMessageToRouter()
         );
 
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
 
         $driver->sendToRouter($message);
     }
@@ -323,7 +323,7 @@ public function testShouldNotInitDeliveryDelayOnSendMessageToRouter()
 
         $message = new Message();
         $message->setDelay(456);
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
 
         $driver->sendToRouter($message);
     }
@@ -362,7 +362,7 @@ public function testShouldNotInitTimeToLiveOnSendMessageToRouter()
 
         $message = new Message();
         $message->setExpire(456);
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
 
         $driver->sendToRouter($message);
     }
@@ -401,7 +401,7 @@ public function testShouldNotInitPriorityOnSendMessageToRouter()
 
         $message = new Message();
         $message->setPriority(MessagePriority::HIGH);
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
 
         $driver->sendToRouter($message);
     }
@@ -429,7 +429,7 @@ public function testThrowIfCommandSetOnSendToRouter()
         );
 
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'aCommand');
+        $message->setProperty(Config::COMMAND_PARAMETER, 'aCommand');
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('Command must not be send to router but go directly to its processor.');
@@ -475,8 +475,8 @@ public function testShouldSendTopicMessageToProcessorToDefaultQueue()
         );
 
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
+        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
 
         $driver->sendToProcessor($message);
     }
@@ -519,8 +519,8 @@ public function testShouldSendTopicMessageToProcessorToCustomQueue()
         );
 
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
+        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
 
         $driver->sendToProcessor($message);
     }
@@ -569,8 +569,8 @@ public function testShouldInitDeliveryDelayIfDelayPropertyOnSendToProcessor()
 
         $message = new Message();
         $message->setDelay(456);
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
+        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
 
         $driver->sendToProcessor($message);
     }
@@ -619,8 +619,8 @@ public function testShouldSetInitTimeToLiveIfExpirePropertyOnSendToProcessor()
 
         $message = new Message();
         $message->setExpire(678);
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
+        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
 
         $driver->sendToProcessor($message);
     }
@@ -669,8 +669,8 @@ public function testShouldSetInitPriorityIfPriorityPropertyOnSendToProcessor()
 
         $message = new Message();
         $message->setPriority(MessagePriority::HIGH);
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
+        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
 
         $driver->sendToProcessor($message);
     }
@@ -694,8 +694,8 @@ public function testThrowIfNoRouteFoundForTopicMessageOnSendToProcessor()
         );
 
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
+        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('There is no route for topic "topic" and processor "processor"');
@@ -740,11 +740,11 @@ public function testShouldSetRouterProcessorIfProcessorPropertyEmptyOnSendToProc
         );
 
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
+        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
 
         $driver->sendToProcessor($message);
 
-        $this->assertSame('router', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
+        $this->assertSame('router', $message->getProperty(Config::PROCESSOR_PARAMETER));
     }
 
     public function testShouldSendCommandMessageToProcessorToDefaultQueue()
@@ -785,8 +785,8 @@ public function testShouldSendCommandMessageToProcessorToDefaultQueue()
         );
 
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::COMMAND_PARAMETER, 'command');
+        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
 
         $driver->sendToProcessor($message);
     }
@@ -829,8 +829,8 @@ public function testShouldSendCommandMessageToProcessorToCustomQueue()
         );
 
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::COMMAND_PARAMETER, 'command');
+        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
 
         $driver->sendToProcessor($message);
     }
@@ -854,8 +854,8 @@ public function testThrowIfNoRouteFoundForCommandMessageOnSendToProcessor()
         );
 
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::COMMAND_PARAMETER, 'command');
+        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('There is no route for command "command".');
@@ -900,12 +900,12 @@ public function testShouldOverwriteProcessorPropertySetByOneFromCommandRouteOnSe
         );
 
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processorShouldBeOverwritten');
+        $message->setProperty(Config::COMMAND_PARAMETER, 'command');
+        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processorShouldBeOverwritten');
 
         $driver->sendToProcessor($message);
 
-        $this->assertSame('expectedProcessor', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
+        $this->assertSame('expectedProcessor', $message->getProperty(Config::PROCESSOR_PARAMETER));
     }
 
     public function testShouldNotInitDeliveryDelayOnSendMessageToProcessorIfPropertyNull()
@@ -948,7 +948,7 @@ public function testShouldNotInitDeliveryDelayOnSendMessageToProcessorIfProperty
         );
 
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
+        $message->setProperty(Config::COMMAND_PARAMETER, 'command');
         $message->setDelay(null);
 
         $driver->sendToProcessor($message);
@@ -994,7 +994,7 @@ public function testShouldNotInitPriorityOnSendMessageToProcessorIfPropertyNull(
         );
 
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
+        $message->setProperty(Config::COMMAND_PARAMETER, 'command');
         $message->setPriority(null);
 
         $driver->sendToProcessor($message);
@@ -1040,7 +1040,7 @@ public function testShouldNotInitTimeToLiveOnSendMessageToProcessorIfPropertyNul
         );
 
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'command');
+        $message->setProperty(Config::COMMAND_PARAMETER, 'command');
         $message->setExpire(null);
 
         $driver->sendToProcessor($message);
@@ -1058,7 +1058,7 @@ public function testThrowIfNeitherTopicNorCommandAreSentOnSendToProcessor()
         $this->expectExceptionMessage('Queue name parameter is required but is not set');
 
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('Either topic or command parameter must be set.');
diff --git a/Tests/Client/Driver/RabbitMqStompDriverTest.php b/Tests/Client/Driver/RabbitMqStompDriverTest.php
index 9011f64..eda97f4 100644
--- a/Tests/Client/Driver/RabbitMqStompDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqStompDriverTest.php
@@ -250,8 +250,8 @@ public function shouldSendMessageToDelayExchangeIfDelaySet()
         );
 
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topic');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'processor');
+        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
+        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
         $message->setDelay(10);
 
         $driver->sendToProcessor($message);
diff --git a/Tests/Client/DriverPreSendTest.php b/Tests/Client/DriverPreSendTest.php
index aa9ff39..95ac891 100644
--- a/Tests/Client/DriverPreSendTest.php
+++ b/Tests/Client/DriverPreSendTest.php
@@ -48,7 +48,7 @@ public function testShouldAllowGetArgumentSetInConstructor()
     public function testShouldAllowGetCommand()
     {
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'theCommand');
+        $message->setProperty(Config::COMMAND_PARAMETER, 'theCommand');
 
         $context = new DriverPreSend(
             $message,
@@ -63,7 +63,7 @@ public function testShouldAllowGetCommand()
     public function testShouldAllowGetTopic()
     {
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'theTopic');
+        $message->setProperty(Config::TOPIC_PARAMETER, 'theTopic');
 
         $context = new DriverPreSend(
             $message,
diff --git a/Tests/Client/PostSendTest.php b/Tests/Client/PostSendTest.php
index 9024034..a68c12b 100644
--- a/Tests/Client/PostSendTest.php
+++ b/Tests/Client/PostSendTest.php
@@ -48,7 +48,7 @@ public function testShouldAllowGetArgumentSetInConstructor()
     public function testShouldAllowGetCommand()
     {
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'theCommand');
+        $message->setProperty(Config::COMMAND_PARAMETER, 'theCommand');
 
         $context = new PostSend(
             $message,
@@ -63,7 +63,7 @@ public function testShouldAllowGetCommand()
     public function testShouldAllowGetTopic()
     {
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'theTopic');
+        $message->setProperty(Config::TOPIC_PARAMETER, 'theTopic');
 
         $context = new PostSend(
             $message,
diff --git a/Tests/Client/ProducerSendCommandTest.php b/Tests/Client/ProducerSendCommandTest.php
index 21b1f5b..ea18550 100644
--- a/Tests/Client/ProducerSendCommandTest.php
+++ b/Tests/Client/ProducerSendCommandTest.php
@@ -40,7 +40,7 @@ public function testShouldSendCommandToProcessor()
         $producer->sendCommand('command', $message);
 
         $expectedProperties = [
-            'enqueue.command_name' => 'command',
+            'enqueue.command' => 'command',
         ];
 
         self::assertEquals($expectedProperties, $message->getProperties());
@@ -136,7 +136,7 @@ public function testShouldSendCommandWithReplyAndCustomReplyQueueAndCorrelationI
     public function testShouldOverwriteExpectedMessageProperties()
     {
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'commandShouldBeOverwritten');
+        $message->setProperty(Config::COMMAND_PARAMETER, 'commandShouldBeOverwritten');
         $message->setScope('scopeShouldBeOverwritten');
 
         $driver = $this->createDriverStub();
@@ -145,7 +145,7 @@ public function testShouldOverwriteExpectedMessageProperties()
         $producer->sendCommand('expectedCommand', $message);
 
         $expectedProperties = [
-            'enqueue.command_name' => 'expectedCommand',
+            'enqueue.command' => 'expectedCommand',
         ];
 
         self::assertEquals($expectedProperties, $message->getProperties());
@@ -299,8 +299,8 @@ public function testShouldSendCommandToApplicationRouter()
             ->method('sendToProcessor')
             ->willReturnCallback(function (Message $message) {
                 self::assertSame('aBody', $message->getBody());
-                self::assertNull($message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
-                self::assertSame('command', $message->getProperty(Config::PARAMETER_COMMAND_NAME));
+                self::assertNull($message->getProperty(Config::PROCESSOR_PARAMETER));
+                self::assertSame('command', $message->getProperty(Config::COMMAND_PARAMETER));
             })
         ;
 
@@ -313,7 +313,7 @@ public function testThrowWhenProcessorNamePropertySetToApplicationRouter()
         $message = new Message();
         $message->setBody('aBody');
         $message->setScope(Message::SCOPE_APP);
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aCustomProcessor');
+        $message->setProperty(Config::PROCESSOR_PARAMETER, 'aCustomProcessor');
 
         $driver = $this->createDriverStub();
         $driver
@@ -324,7 +324,7 @@ public function testThrowWhenProcessorNamePropertySetToApplicationRouter()
         $producer = new Producer($driver, $this->createRpcFactoryMock());
 
         $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The enqueue.processor_name property must not be set.');
+        $this->expectExceptionMessage('The enqueue.processor property must not be set.');
         $producer->sendCommand('command', $message);
     }
 
diff --git a/Tests/Client/ProducerSendEventTest.php b/Tests/Client/ProducerSendEventTest.php
index 757c612..e957e16 100644
--- a/Tests/Client/ProducerSendEventTest.php
+++ b/Tests/Client/ProducerSendEventTest.php
@@ -36,7 +36,7 @@ public function testShouldSendEventToRouter()
         $producer->sendEvent('topic', $message);
 
         $expectedProperties = [
-            'enqueue.topic_name' => 'topic',
+            'enqueue.topic' => 'topic',
         ];
 
         self::assertEquals($expectedProperties, $message->getProperties());
@@ -45,7 +45,7 @@ public function testShouldSendEventToRouter()
     public function testShouldOverwriteTopicProperty()
     {
         $message = new Message();
-        $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topicShouldBeOverwritten');
+        $message->setProperty(Config::TOPIC_PARAMETER, 'topicShouldBeOverwritten');
 
         $driver = $this->createDriverStub();
 
@@ -53,7 +53,7 @@ public function testShouldOverwriteTopicProperty()
         $producer->sendEvent('expectedTopic', $message);
 
         $expectedProperties = [
-            'enqueue.topic_name' => 'expectedTopic',
+            'enqueue.topic' => 'expectedTopic',
         ];
 
         self::assertEquals($expectedProperties, $message->getProperties());
@@ -198,7 +198,7 @@ public function testThrowIfSendEventToMessageBusWithProcessorNamePropertySet()
     {
         $message = new Message();
         $message->setBody('');
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aProcessor');
+        $message->setProperty(Config::PROCESSOR_PARAMETER, 'aProcessor');
 
         $driver = $this->createDriverStub();
         $driver
@@ -213,7 +213,7 @@ public function testThrowIfSendEventToMessageBusWithProcessorNamePropertySet()
         $producer = new Producer($driver, $this->createRpcFactoryMock());
 
         $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The enqueue.processor_name property must not be set.');
+        $this->expectExceptionMessage('The enqueue.processor property must not be set.');
         $producer->sendEvent('topic', $message);
     }
 
@@ -235,7 +235,7 @@ public function testShouldSendEventToApplicationRouter()
                 self::assertSame('aBody', $message->getBody());
 
                 // null means a driver sends a message to router processor.
-                self::assertNull($message->getProperty(Config::PARAMETER_PROCESSOR_NAME));
+                self::assertNull($message->getProperty(Config::PROCESSOR_PARAMETER));
             })
         ;
 
@@ -248,7 +248,7 @@ public function testThrowWhenProcessorNamePropertySetToApplicationRouter()
         $message = new Message();
         $message->setBody('aBody');
         $message->setScope(Message::SCOPE_APP);
-        $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aCustomProcessor');
+        $message->setProperty(Config::PROCESSOR_PARAMETER, 'aCustomProcessor');
 
         $driver = $this->createDriverStub();
         $driver
@@ -259,7 +259,7 @@ public function testThrowWhenProcessorNamePropertySetToApplicationRouter()
         $producer = new Producer($driver, $this->createRpcFactoryMock());
 
         $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The enqueue.processor_name property must not be set.');
+        $this->expectExceptionMessage('The enqueue.processor property must not be set.');
         $producer->sendEvent('topic', $message);
     }
 
diff --git a/Tests/Client/RouterProcessorTest.php b/Tests/Client/RouterProcessorTest.php
index f38d448..0c245d4 100644
--- a/Tests/Client/RouterProcessorTest.php
+++ b/Tests/Client/RouterProcessorTest.php
@@ -45,7 +45,7 @@ public function testShouldRejectIfTopicNotSet()
         $result = $router->process(new NullMessage(), new NullContext());
 
         $this->assertEquals(Result::REJECT, $result->getStatus());
-        $this->assertEquals('Topic property "enqueue.topic_name" is required but not set or empty.', $result->getReason());
+        $this->assertEquals('Topic property "enqueue.topic" is required but not set or empty.', $result->getReason());
     }
 
     public function testShouldRejectIfCommandSet()
@@ -53,7 +53,7 @@ public function testShouldRejectIfCommandSet()
         $router = new RouterProcessor($this->createDriverStub());
 
         $message = new NullMessage();
-        $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'aCommand');
+        $message->setProperty(Config::COMMAND_PARAMETER, 'aCommand');
 
         $result = $router->process($message, new NullContext());
 
@@ -66,7 +66,7 @@ public function testShouldRouteOriginalMessageToAllRecipients()
         $message = new NullMessage();
         $message->setBody('theBody');
         $message->setHeaders(['aHeader' => 'aHeaderVal']);
-        $message->setProperties(['aProp' => 'aPropVal', Config::PARAMETER_TOPIC_NAME => 'theTopicName']);
+        $message->setProperties(['aProp' => 'aPropVal', Config::TOPIC_PARAMETER => 'theTopicName']);
 
         /** @var Message[] $routedMessages */
         $routedMessages = new \ArrayObject();
@@ -103,16 +103,9 @@ public function testShouldRouteOriginalMessageToAllRecipients()
         $this->assertContainsOnly(Message::class, $routedMessages);
         $this->assertCount(3, $routedMessages);
 
-        $this->assertSame('aFooProcessor', $routedMessages[0]->getProperty(Config::PARAMETER_PROCESSOR_NAME));
-        $this->assertSame('aBarProcessor', $routedMessages[1]->getProperty(Config::PARAMETER_PROCESSOR_NAME));
-        $this->assertSame('aBazProcessor', $routedMessages[2]->getProperty(Config::PARAMETER_PROCESSOR_NAME));
-
-//        $this->assertEquals([
-//            'aProp' => 'aPropVal',
-//            'enqueue.topic_name' => 'theTopicName',
-//            'enqueue.processor_name' => 'aFooProcessor',
-//            'enqueue.processor_queue_name' => 'aQueueName',
-//        ], $routedMessages[0]->getProperties());
+        $this->assertSame('aFooProcessor', $routedMessages[0]->getProperty(Config::PROCESSOR_PARAMETER));
+        $this->assertSame('aBarProcessor', $routedMessages[1]->getProperty(Config::PROCESSOR_PARAMETER));
+        $this->assertSame('aBazProcessor', $routedMessages[2]->getProperty(Config::PROCESSOR_PARAMETER));
     }
 
     public function testShouldDoNothingIfNoRoutes()
@@ -120,7 +113,7 @@ public function testShouldDoNothingIfNoRoutes()
         $message = new NullMessage();
         $message->setBody('theBody');
         $message->setHeaders(['aHeader' => 'aHeaderVal']);
-        $message->setProperties(['aProp' => 'aPropVal', Config::PARAMETER_TOPIC_NAME => 'theTopicName']);
+        $message->setProperties(['aProp' => 'aPropVal', Config::TOPIC_PARAMETER => 'theTopicName']);
 
         /** @var Message[] $routedMessages */
         $routedMessages = new \ArrayObject();
@@ -158,7 +151,7 @@ public function testShouldDoNotModifyOriginalMessage()
         $message = new NullMessage();
         $message->setBody('theBody');
         $message->setHeaders(['aHeader' => 'aHeaderVal']);
-        $message->setProperties(['aProp' => 'aPropVal', Config::PARAMETER_TOPIC_NAME => 'theTopicName']);
+        $message->setProperties(['aProp' => 'aPropVal', Config::TOPIC_PARAMETER => 'theTopicName']);
 
         /** @var Message[] $routedMessages */
         $routedMessages = new \ArrayObject();
@@ -190,7 +183,7 @@ public function testShouldDoNotModifyOriginalMessage()
         $this->assertEquals(Result::ACK, $result->getStatus());
 
         $this->assertSame('theBody', $message->getBody());
-        $this->assertSame(['aProp' => 'aPropVal', Config::PARAMETER_TOPIC_NAME => 'theTopicName'], $message->getProperties());
+        $this->assertSame(['aProp' => 'aPropVal', Config::TOPIC_PARAMETER => 'theTopicName'], $message->getProperties());
         $this->assertSame(['aHeader' => 'aHeaderVal'], $message->getHeaders());
     }
 

From e3675815a9c14006f222c03a199399582890c37e Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 28 Sep 2018 21:05:19 +0300
Subject: [PATCH 139/337] [client] Reorganize configuration. Remove unused
 parameters.

---
 Client/Driver/GenericDriver.php                 | 6 +++---
 Client/Driver/RabbitMqStompDriver.php           | 2 +-
 Tests/Client/Driver/AmqpDriverTest.php          | 2 +-
 Tests/Client/Driver/GenericDriverTest.php       | 2 +-
 Tests/Client/Driver/GenericDriverTestsTrait.php | 6 +++---
 Tests/Client/Driver/RabbitMqDriverTest.php      | 2 +-
 Tests/Client/Driver/RabbitMqStompDriverTest.php | 2 +-
 Tests/Client/Driver/StompDriverTest.php         | 2 +-
 8 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/Client/Driver/GenericDriver.php b/Client/Driver/GenericDriver.php
index 627e63e..9a8e47f 100644
--- a/Client/Driver/GenericDriver.php
+++ b/Client/Driver/GenericDriver.php
@@ -96,7 +96,7 @@ public function sendToProcessor(Message $message): void
 
         $producer = $this->context->createProducer();
 
-        if (null !== $delay = $transportMessage->getProperty('X-Enqueue-Delay')) {
+        if (null !== $delay = $transportMessage->getProperty(Config::DELAY_PARAMETER)) {
             $producer->setDeliveryDelay($delay * 1000);
         }
 
@@ -161,7 +161,7 @@ public function createTransportMessage(Message $clientMessage): InteropMessage
         }
 
         if ($delay = $clientMessage->getDelay()) {
-            $transportMessage->setProperty('X-Enqueue-Delay', $delay);
+            $transportMessage->setProperty(Config::DELAY_PARAMETER, $delay);
         }
 
         return $transportMessage;
@@ -187,7 +187,7 @@ public function createClientMessage(InteropMessage $transportMessage): Message
             $clientMessage->setPriority($priority);
         }
 
-        if ($delay = $transportMessage->getProperty('X-Enqueue-Delay')) {
+        if ($delay = $transportMessage->getProperty(Config::DELAY_PARAMETER)) {
             $clientMessage->setDelay((int) $delay);
         }
 
diff --git a/Client/Driver/RabbitMqStompDriver.php b/Client/Driver/RabbitMqStompDriver.php
index 77b4293..dcf0a47 100644
--- a/Client/Driver/RabbitMqStompDriver.php
+++ b/Client/Driver/RabbitMqStompDriver.php
@@ -169,7 +169,7 @@ protected function doSendToRouter(InteropProducer $producer, Destination $topic,
      */
     protected function doSendToProcessor(InteropProducer $producer, InteropQueue $destination, InteropMessage $transportMessage): void
     {
-        if ($delay = $transportMessage->getProperty('X-Enqueue-Delay')) {
+        if ($delay = $transportMessage->getProperty(Config::DELAY_PARAMETER)) {
             $producer->setDeliveryDelay(null);
             $destination = $this->createDelayedTopic($destination);
         }
diff --git a/Tests/Client/Driver/AmqpDriverTest.php b/Tests/Client/Driver/AmqpDriverTest.php
index e7d76e2..d5d4c25 100644
--- a/Tests/Client/Driver/AmqpDriverTest.php
+++ b/Tests/Client/Driver/AmqpDriverTest.php
@@ -352,7 +352,7 @@ protected function assertTransportMessage(InteropMessage $transportMessage): voi
             'X-Enqueue-Content-Type' => 'ContentType',
             'X-Enqueue-Priority' => MessagePriority::HIGH,
             'X-Enqueue-Expire' => 123,
-            'X-Enqueue-Delay' => 345,
+            'enqueue.delay' => 345,
         ], $transportMessage->getProperties());
         $this->assertSame('theMessageId', $transportMessage->getMessageId());
         $this->assertSame(1000, $transportMessage->getTimestamp());
diff --git a/Tests/Client/Driver/GenericDriverTest.php b/Tests/Client/Driver/GenericDriverTest.php
index 733e9ac..f145859 100644
--- a/Tests/Client/Driver/GenericDriverTest.php
+++ b/Tests/Client/Driver/GenericDriverTest.php
@@ -71,7 +71,7 @@ protected function assertTransportMessage(InteropMessage $transportMessage): voi
             'X-Enqueue-Content-Type' => 'ContentType',
             'X-Enqueue-Priority' => MessagePriority::HIGH,
             'X-Enqueue-Expire' => 123,
-            'X-Enqueue-Delay' => 345,
+            'enqueue.delay' => 345,
         ], $transportMessage->getProperties());
         $this->assertSame('theMessageId', $transportMessage->getMessageId());
         $this->assertSame(1000, $transportMessage->getTimestamp());
diff --git a/Tests/Client/Driver/GenericDriverTestsTrait.php b/Tests/Client/Driver/GenericDriverTestsTrait.php
index 782add3..cf8a5f5 100644
--- a/Tests/Client/Driver/GenericDriverTestsTrait.php
+++ b/Tests/Client/Driver/GenericDriverTestsTrait.php
@@ -200,7 +200,7 @@ public function testShouldCreateClientMessageFromTransportOne()
         $transportMessage->setProperty('X-Enqueue-Content-Type', 'theContentType');
         $transportMessage->setProperty('X-Enqueue-Expire', '22');
         $transportMessage->setProperty('X-Enqueue-Priority', MessagePriority::HIGH);
-        $transportMessage->setProperty('X-Enqueue-Delay', '44');
+        $transportMessage->setProperty('enqueue.delay', '44');
         $transportMessage->setMessageId('theMessageId');
         $transportMessage->setTimestamp(1000);
         $transportMessage->setReplyTo('theReplyTo');
@@ -1124,7 +1124,7 @@ protected function assertTransportMessage(InteropMessage $transportMessage): voi
             'X-Enqueue-Content-Type' => 'ContentType',
             'X-Enqueue-Priority' => MessagePriority::HIGH,
             'X-Enqueue-Expire' => 123,
-            'X-Enqueue-Delay' => 345,
+            'enqueue.delay' => 345,
         ], $transportMessage->getProperties());
         $this->assertSame('theMessageId', $transportMessage->getMessageId());
         $this->assertSame(1000, $transportMessage->getTimestamp());
@@ -1143,7 +1143,7 @@ protected function assertClientMessage(Message $clientMessage): void
             'X-Enqueue-Content-Type' => 'theContentType',
             'X-Enqueue-Expire' => '22',
             'X-Enqueue-Priority' => MessagePriority::HIGH,
-            'X-Enqueue-Delay' => '44',
+            'enqueue.delay' => '44',
         ], $clientMessage->getProperties());
         $this->assertSame('theMessageId', $clientMessage->getMessageId());
         $this->assertSame(22, $clientMessage->getExpire());
diff --git a/Tests/Client/Driver/RabbitMqDriverTest.php b/Tests/Client/Driver/RabbitMqDriverTest.php
index d6604ed..eebb9b7 100644
--- a/Tests/Client/Driver/RabbitMqDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqDriverTest.php
@@ -130,7 +130,7 @@ protected function assertTransportMessage(InteropMessage $transportMessage): voi
             'X-Enqueue-Content-Type' => 'ContentType',
             'X-Enqueue-Priority' => MessagePriority::HIGH,
             'X-Enqueue-Expire' => 123,
-            'X-Enqueue-Delay' => 345,
+            'enqueue.delay' => 345,
         ], $transportMessage->getProperties());
         $this->assertSame('theMessageId', $transportMessage->getMessageId());
         $this->assertSame(1000, $transportMessage->getTimestamp());
diff --git a/Tests/Client/Driver/RabbitMqStompDriverTest.php b/Tests/Client/Driver/RabbitMqStompDriverTest.php
index eda97f4..22cda4e 100644
--- a/Tests/Client/Driver/RabbitMqStompDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqStompDriverTest.php
@@ -543,7 +543,7 @@ protected function assertTransportMessage(InteropMessage $transportMessage): voi
             'X-Enqueue-Content-Type' => 'ContentType',
             'X-Enqueue-Priority' => MessagePriority::HIGH,
             'X-Enqueue-Expire' => 123,
-            'X-Enqueue-Delay' => 345,
+            'enqueue.delay' => 345,
         ], $transportMessage->getProperties());
         $this->assertSame('theMessageId', $transportMessage->getMessageId());
         $this->assertSame(1000, $transportMessage->getTimestamp());
diff --git a/Tests/Client/Driver/StompDriverTest.php b/Tests/Client/Driver/StompDriverTest.php
index 26199d3..0a3415c 100644
--- a/Tests/Client/Driver/StompDriverTest.php
+++ b/Tests/Client/Driver/StompDriverTest.php
@@ -169,7 +169,7 @@ protected function assertTransportMessage(InteropMessage $transportMessage): voi
             'X-Enqueue-Content-Type' => 'ContentType',
             'X-Enqueue-Priority' => MessagePriority::HIGH,
             'X-Enqueue-Expire' => 123,
-            'X-Enqueue-Delay' => 345,
+            'enqueue.delay' => 345,
         ], $transportMessage->getProperties());
         $this->assertSame('theMessageId', $transportMessage->getMessageId());
         $this->assertSame(1000, $transportMessage->getTimestamp());

From 3340baccd75fc2de83c2ced642f07a281a1aa1ef Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 1 Oct 2018 17:08:35 +0300
Subject: [PATCH 140/337] Merge master

---
 Client/Config.php                             | 13 +--
 .../ExclusiveCommandExtension.php             | 10 +--
 .../SetRouterPropertiesExtension.php          |  4 +-
 Client/DelegateProcessor.php                  |  4 +-
 Client/Driver/GenericDriver.php               | 40 ++++-----
 Client/Driver/RabbitMqStompDriver.php         |  2 +-
 Client/DriverPreSend.php                      |  8 +-
 Client/PostSend.php                           |  8 +-
 Client/Producer.php                           |  8 +-
 Client/RouterProcessor.php                    | 10 +--
 .../ExclusiveCommandExtensionTest.php         |  6 +-
 .../SetRouterPropertiesExtensionTest.php      |  2 +-
 Tests/Client/DelegateProcessorTest.php        |  2 +-
 Tests/Client/Driver/AmqpDriverTest.php        | 10 +--
 Tests/Client/Driver/GenericDriverTest.php     |  9 +-
 .../Client/Driver/GenericDriverTestsTrait.php | 86 +++++++++----------
 Tests/Client/Driver/RabbitMqDriverTest.php    |  9 +-
 .../Client/Driver/RabbitMqStompDriverTest.php | 12 +--
 Tests/Client/Driver/StompDriverTest.php       |  9 +-
 Tests/Client/DriverPreSendTest.php            |  4 +-
 Tests/Client/PostSendTest.php                 |  4 +-
 Tests/Client/ProducerSendCommandTest.php      |  8 +-
 Tests/Client/ProducerSendEventTest.php        |  8 +-
 Tests/Client/RouterProcessorTest.php          | 16 ++--
 24 files changed, 148 insertions(+), 144 deletions(-)

diff --git a/Client/Config.php b/Client/Config.php
index 07bd3e0..0dc4ee5 100644
--- a/Client/Config.php
+++ b/Client/Config.php
@@ -4,12 +4,13 @@
 
 class Config
 {
-    const TOPIC_PARAMETER = 'enqueue.topic';
-    const COMMAND_PARAMETER = 'enqueue.command';
-    const PROCESSOR_PARAMETER = 'enqueue.processor';
-    const EXPIRE_PARAMETER = 'enqueue.expire';
-    const PRIORITY_PARAMETER = 'enqueue.priority';
-    const DELAY_PARAMETER = 'enqueue.delay';
+    const TOPIC = 'enqueue.topic';
+    const COMMAND = 'enqueue.command';
+    const PROCESSOR = 'enqueue.processor';
+    const EXPIRE = 'enqueue.expire';
+    const PRIORITY = 'enqueue.priority';
+    const DELAY = 'enqueue.delay';
+    const CONTENT_TYPE = 'enqueue.content_type';
 
     /**
      * @var string
diff --git a/Client/ConsumptionExtension/ExclusiveCommandExtension.php b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
index 2ebd8a6..e9bbbd8 100644
--- a/Client/ConsumptionExtension/ExclusiveCommandExtension.php
+++ b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
@@ -33,13 +33,13 @@ public function onPreReceived(Context $context)
         $message = $context->getInteropMessage();
         $queue = $context->getInteropQueue();
 
-        if ($message->getProperty(Config::TOPIC_PARAMETER)) {
+        if ($message->getProperty(Config::TOPIC)) {
             return;
         }
-        if ($message->getProperty(Config::COMMAND_PARAMETER)) {
+        if ($message->getProperty(Config::COMMAND)) {
             return;
         }
-        if ($message->getProperty(Config::PROCESSOR_PARAMETER)) {
+        if ($message->getProperty(Config::PROCESSOR)) {
             return;
         }
 
@@ -51,8 +51,8 @@ public function onPreReceived(Context $context)
             $context->getLogger()->debug('[ExclusiveCommandExtension] This is a exclusive command queue and client\'s properties are not set. Setting them');
 
             $route = $this->queueToRouteMap[$queue->getQueueName()];
-            $message->setProperty(Config::PROCESSOR_PARAMETER, $route->getProcessor());
-            $message->setProperty(Config::COMMAND_PARAMETER, $route->getSource());
+            $message->setProperty(Config::PROCESSOR, $route->getProcessor());
+            $message->setProperty(Config::COMMAND, $route->getSource());
         }
     }
 
diff --git a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
index d5c3cee..2ecc1dd 100644
--- a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
+++ b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
@@ -28,7 +28,7 @@ public function __construct(DriverInterface $driver)
     public function onPreReceived(Context $context)
     {
         $message = $context->getInteropMessage();
-        if ($message->getProperty(Config::PROCESSOR_PARAMETER)) {
+        if ($message->getProperty(Config::PROCESSOR)) {
             return;
         }
 
@@ -39,7 +39,7 @@ public function onPreReceived(Context $context)
         }
 
         // RouterProcessor is our default message processor when that header is not set
-        $message->setProperty(Config::PROCESSOR_PARAMETER, $config->getRouterProcessorName());
+        $message->setProperty(Config::PROCESSOR, $config->getRouterProcessorName());
 
         $context->getLogger()->debug(
             '[SetRouterPropertiesExtension] '.
diff --git a/Client/DelegateProcessor.php b/Client/DelegateProcessor.php
index 920c4af..705ff12 100644
--- a/Client/DelegateProcessor.php
+++ b/Client/DelegateProcessor.php
@@ -26,11 +26,11 @@ public function __construct(ProcessorRegistryInterface $registry)
      */
     public function process(InteropMessage $message, Context $context)
     {
-        $processorName = $message->getProperty(Config::PROCESSOR_PARAMETER);
+        $processorName = $message->getProperty(Config::PROCESSOR);
         if (false == $processorName) {
             throw new \LogicException(sprintf(
                 'Got message without required parameter: "%s"',
-                Config::PROCESSOR_PARAMETER
+                Config::PROCESSOR
             ));
         }
 
diff --git a/Client/Driver/GenericDriver.php b/Client/Driver/GenericDriver.php
index 9a8e47f..2da6b7d 100644
--- a/Client/Driver/GenericDriver.php
+++ b/Client/Driver/GenericDriver.php
@@ -47,10 +47,10 @@ public function __construct(
 
     public function sendToRouter(Message $message): void
     {
-        if ($message->getProperty(Config::COMMAND_PARAMETER)) {
+        if ($message->getProperty(Config::COMMAND)) {
             throw new \LogicException('Command must not be send to router but go directly to its processor.');
         }
-        if (false == $message->getProperty(Config::TOPIC_PARAMETER)) {
+        if (false == $message->getProperty(Config::TOPIC)) {
             throw new \LogicException('Topic name parameter is required but is not set');
         }
 
@@ -63,21 +63,21 @@ public function sendToRouter(Message $message): void
 
     public function sendToProcessor(Message $message): void
     {
-        $topic = $message->getProperty(Config::TOPIC_PARAMETER);
-        $command = $message->getProperty(Config::COMMAND_PARAMETER);
+        $topic = $message->getProperty(Config::TOPIC);
+        $command = $message->getProperty(Config::COMMAND);
 
         /** @var InteropQueue $queue */
         $queue = null;
-        if ($topic && $processor = $message->getProperty(Config::PROCESSOR_PARAMETER)) {
+        if ($topic && $processor = $message->getProperty(Config::PROCESSOR)) {
             $route = $this->routeCollection->topicAndProcessor($topic, $processor);
             if (false == $route) {
                 throw new \LogicException(sprintf('There is no route for topic "%s" and processor "%s"', $topic, $processor));
             }
 
-            $message->setProperty(Config::PROCESSOR_PARAMETER, $route->getProcessor());
+            $message->setProperty(Config::PROCESSOR, $route->getProcessor());
             $queue = $this->createRouteQueue($route);
-        } elseif ($topic && false == $message->getProperty(Config::PROCESSOR_PARAMETER)) {
-            $message->setProperty(Config::PROCESSOR_PARAMETER, $this->config->getRouterProcessorName());
+        } elseif ($topic && false == $message->getProperty(Config::PROCESSOR)) {
+            $message->setProperty(Config::PROCESSOR, $this->config->getRouterProcessorName());
 
             $queue = $this->createQueue($this->config->getRouterQueueName());
         } elseif ($command) {
@@ -86,7 +86,7 @@ public function sendToProcessor(Message $message): void
                 throw new \LogicException(sprintf('There is no route for command "%s".', $command));
             }
 
-            $message->setProperty(Config::PROCESSOR_PARAMETER, $route->getProcessor());
+            $message->setProperty(Config::PROCESSOR, $route->getProcessor());
             $queue = $this->createRouteQueue($route);
         } else {
             throw new \LogicException('Either topic or command parameter must be set.');
@@ -96,15 +96,15 @@ public function sendToProcessor(Message $message): void
 
         $producer = $this->context->createProducer();
 
-        if (null !== $delay = $transportMessage->getProperty(Config::DELAY_PARAMETER)) {
+        if (null !== $delay = $transportMessage->getProperty(Config::DELAY)) {
             $producer->setDeliveryDelay($delay * 1000);
         }
 
-        if (null !== $expire = $transportMessage->getProperty('X-Enqueue-Expire')) {
+        if (null !== $expire = $transportMessage->getProperty(Config::EXPIRE)) {
             $producer->setTimeToLive($expire * 1000);
         }
 
-        if (null !== $priority = $transportMessage->getProperty('X-Enqueue-Priority')) {
+        if (null !== $priority = $transportMessage->getProperty(Config::PRIORITY)) {
             $priorityMap = $this->getPriorityMap();
 
             $producer->setPriority($priorityMap[$priority]);
@@ -149,19 +149,19 @@ public function createTransportMessage(Message $clientMessage): InteropMessage
         $transportMessage->setCorrelationId($clientMessage->getCorrelationId());
 
         if ($contentType = $clientMessage->getContentType()) {
-            $transportMessage->setProperty('X-Enqueue-Content-Type', $contentType);
+            $transportMessage->setProperty(Config::CONTENT_TYPE, $contentType);
         }
 
         if ($priority = $clientMessage->getPriority()) {
-            $transportMessage->setProperty('X-Enqueue-Priority', $priority);
+            $transportMessage->setProperty(Config::PRIORITY, $priority);
         }
 
         if ($expire = $clientMessage->getExpire()) {
-            $transportMessage->setProperty('X-Enqueue-Expire', $expire);
+            $transportMessage->setProperty(Config::EXPIRE, $expire);
         }
 
         if ($delay = $clientMessage->getDelay()) {
-            $transportMessage->setProperty(Config::DELAY_PARAMETER, $delay);
+            $transportMessage->setProperty(Config::DELAY, $delay);
         }
 
         return $transportMessage;
@@ -179,19 +179,19 @@ public function createClientMessage(InteropMessage $transportMessage): Message
         $clientMessage->setReplyTo($transportMessage->getReplyTo());
         $clientMessage->setCorrelationId($transportMessage->getCorrelationId());
 
-        if ($contentType = $transportMessage->getProperty('X-Enqueue-Content-Type')) {
+        if ($contentType = $transportMessage->getProperty(Config::CONTENT_TYPE)) {
             $clientMessage->setContentType($contentType);
         }
 
-        if ($priority = $transportMessage->getProperty('X-Enqueue-Priority')) {
+        if ($priority = $transportMessage->getProperty(Config::PRIORITY)) {
             $clientMessage->setPriority($priority);
         }
 
-        if ($delay = $transportMessage->getProperty(Config::DELAY_PARAMETER)) {
+        if ($delay = $transportMessage->getProperty(Config::DELAY)) {
             $clientMessage->setDelay((int) $delay);
         }
 
-        if ($expire = $transportMessage->getProperty('X-Enqueue-Expire')) {
+        if ($expire = $transportMessage->getProperty(Config::EXPIRE)) {
             $clientMessage->setExpire((int) $expire);
         }
 
diff --git a/Client/Driver/RabbitMqStompDriver.php b/Client/Driver/RabbitMqStompDriver.php
index dcf0a47..c2ca768 100644
--- a/Client/Driver/RabbitMqStompDriver.php
+++ b/Client/Driver/RabbitMqStompDriver.php
@@ -169,7 +169,7 @@ protected function doSendToRouter(InteropProducer $producer, Destination $topic,
      */
     protected function doSendToProcessor(InteropProducer $producer, InteropQueue $destination, InteropMessage $transportMessage): void
     {
-        if ($delay = $transportMessage->getProperty(Config::DELAY_PARAMETER)) {
+        if ($delay = $transportMessage->getProperty(Config::DELAY)) {
             $producer->setDeliveryDelay(null);
             $destination = $this->createDelayedTopic($destination);
         }
diff --git a/Client/DriverPreSend.php b/Client/DriverPreSend.php
index 8a92fd9..cae615d 100644
--- a/Client/DriverPreSend.php
+++ b/Client/DriverPreSend.php
@@ -34,21 +34,21 @@ public function getDriver(): DriverInterface
 
     public function isEvent(): bool
     {
-        return (bool) $this->message->getProperty(Config::TOPIC_PARAMETER);
+        return (bool) $this->message->getProperty(Config::TOPIC);
     }
 
     public function isCommand(): bool
     {
-        return (bool) $this->message->getProperty(Config::COMMAND_PARAMETER);
+        return (bool) $this->message->getProperty(Config::COMMAND);
     }
 
     public function getCommand(): string
     {
-        return $this->message->getProperty(Config::COMMAND_PARAMETER);
+        return $this->message->getProperty(Config::COMMAND);
     }
 
     public function getTopic(): string
     {
-        return $this->message->getProperty(Config::TOPIC_PARAMETER);
+        return $this->message->getProperty(Config::TOPIC);
     }
 }
diff --git a/Client/PostSend.php b/Client/PostSend.php
index 5cfafb3..1c5ab9d 100644
--- a/Client/PostSend.php
+++ b/Client/PostSend.php
@@ -34,21 +34,21 @@ public function getDriver(): DriverInterface
 
     public function isEvent(): bool
     {
-        return (bool) $this->message->getProperty(Config::TOPIC_PARAMETER);
+        return (bool) $this->message->getProperty(Config::TOPIC);
     }
 
     public function isCommand(): bool
     {
-        return (bool) $this->message->getProperty(Config::COMMAND_PARAMETER);
+        return (bool) $this->message->getProperty(Config::COMMAND);
     }
 
     public function getCommand(): string
     {
-        return $this->message->getProperty(Config::COMMAND_PARAMETER);
+        return $this->message->getProperty(Config::COMMAND);
     }
 
     public function getTopic(): string
     {
-        return $this->message->getProperty(Config::TOPIC_PARAMETER);
+        return $this->message->getProperty(Config::TOPIC);
     }
 }
diff --git a/Client/Producer.php b/Client/Producer.php
index e6aae73..702ec38 100644
--- a/Client/Producer.php
+++ b/Client/Producer.php
@@ -48,7 +48,7 @@ public function sendEvent(string $topic, $message): void
         $this->extension->onPreSendEvent($preSend);
 
         $message = $preSend->getMessage();
-        $message->setProperty(Config::TOPIC_PARAMETER, $preSend->getTopic());
+        $message->setProperty(Config::TOPIC, $preSend->getTopic());
 
         $this->doSend($message);
     }
@@ -79,7 +79,7 @@ public function sendCommand(string $command, $message, bool $needReply = false):
             }
         }
 
-        $message->setProperty(Config::COMMAND_PARAMETER, $command);
+        $message->setProperty(Config::COMMAND, $command);
         $message->setScope(Message::SCOPE_APP);
 
         $this->doSend($message);
@@ -103,8 +103,8 @@ private function doSend(Message $message): void
             ));
         }
 
-        if ($message->getProperty(Config::PROCESSOR_PARAMETER)) {
-            throw new \LogicException(sprintf('The %s property must not be set.', Config::PROCESSOR_PARAMETER));
+        if ($message->getProperty(Config::PROCESSOR)) {
+            throw new \LogicException(sprintf('The %s property must not be set.', Config::PROCESSOR));
         }
 
         if (!$message->getMessageId()) {
diff --git a/Client/RouterProcessor.php b/Client/RouterProcessor.php
index 220d8ec..c4832fd 100644
--- a/Client/RouterProcessor.php
+++ b/Client/RouterProcessor.php
@@ -21,22 +21,22 @@ public function __construct(DriverInterface $driver)
 
     public function process(InteropMessage $message, Context $context): Result
     {
-        if ($message->getProperty(Config::COMMAND_PARAMETER)) {
+        if ($message->getProperty(Config::COMMAND)) {
             return Result::reject(sprintf(
                 'Unexpected command "%s" got. Command must not go to the router.',
-                $message->getProperty(Config::COMMAND_PARAMETER)
+                $message->getProperty(Config::COMMAND)
             ));
         }
 
-        $topic = $message->getProperty(Config::TOPIC_PARAMETER);
+        $topic = $message->getProperty(Config::TOPIC);
         if (false == $topic) {
-            return Result::reject(sprintf('Topic property "%s" is required but not set or empty.', Config::TOPIC_PARAMETER));
+            return Result::reject(sprintf('Topic property "%s" is required but not set or empty.', Config::TOPIC));
         }
 
         $count = 0;
         foreach ($this->driver->getRouteCollection()->topic($topic) as $route) {
             $clientMessage = $this->driver->createClientMessage($message);
-            $clientMessage->setProperty(Config::PROCESSOR_PARAMETER, $route->getProcessor());
+            $clientMessage->setProperty(Config::PROCESSOR, $route->getProcessor());
 
             $this->driver->sendToProcessor($clientMessage);
 
diff --git a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
index 6343371..8117645 100644
--- a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
@@ -38,7 +38,7 @@ public function testCouldBeConstructedWithDriverAsFirstArgument()
     public function testShouldDoNothingIfMessageHasTopicPropertySetOnPreReceive()
     {
         $message = new NullMessage();
-        $message->setProperty(Config::TOPIC_PARAMETER, 'aTopic');
+        $message->setProperty(Config::TOPIC, 'aTopic');
 
         $context = new Context(new NullContext());
         $context->setInteropMessage($message);
@@ -63,7 +63,7 @@ public function testShouldDoNothingIfMessageHasTopicPropertySetOnPreReceive()
     public function testShouldDoNothingIfMessageHasCommandPropertySetOnPreReceive()
     {
         $message = new NullMessage();
-        $message->setProperty(Config::COMMAND_PARAMETER, 'aCommand');
+        $message->setProperty(Config::COMMAND, 'aCommand');
 
         $context = new Context(new NullContext());
         $context->setInteropMessage($message);
@@ -88,7 +88,7 @@ public function testShouldDoNothingIfMessageHasCommandPropertySetOnPreReceive()
     public function testShouldDoNothingIfMessageHasProcessorPropertySetOnPreReceive()
     {
         $message = new NullMessage();
-        $message->setProperty(Config::PROCESSOR_PARAMETER, 'aProcessor');
+        $message->setProperty(Config::PROCESSOR, 'aProcessor');
 
         $context = new Context(new NullContext());
         $context->setInteropMessage($message);
diff --git a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
index e4ae8a6..2d21b90 100644
--- a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
@@ -100,7 +100,7 @@ public function testShouldNotSetAnyPropertyIfProcessorNamePropertyAlreadySet()
         ;
 
         $message = new NullMessage();
-        $message->setProperty(Config::PROCESSOR_PARAMETER, 'non-router-processor');
+        $message->setProperty(Config::PROCESSOR, 'non-router-processor');
 
         $context = new Context($this->createContextMock());
         $context->setInteropMessage($message);
diff --git a/Tests/Client/DelegateProcessorTest.php b/Tests/Client/DelegateProcessorTest.php
index 4242068..29b1031 100644
--- a/Tests/Client/DelegateProcessorTest.php
+++ b/Tests/Client/DelegateProcessorTest.php
@@ -33,7 +33,7 @@ public function testShouldProcessMessage()
         $session = $this->createContextMock();
         $message = new NullMessage();
         $message->setProperties([
-            Config::PROCESSOR_PARAMETER => 'processor-name',
+            Config::PROCESSOR => 'processor-name',
         ]);
 
         $processor = $this->createProcessorMock();
diff --git a/Tests/Client/Driver/AmqpDriverTest.php b/Tests/Client/Driver/AmqpDriverTest.php
index d5d4c25..4e4cc62 100644
--- a/Tests/Client/Driver/AmqpDriverTest.php
+++ b/Tests/Client/Driver/AmqpDriverTest.php
@@ -170,7 +170,7 @@ public function testShouldResetPriorityAndExpirationAndNeverCallProducerDelivery
         );
 
         $message = new Message();
-        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
+        $message->setProperty(Config::TOPIC, 'topic');
         $message->setExpire(123);
         $message->setPriority(MessagePriority::HIGH);
 
@@ -349,10 +349,10 @@ protected function assertTransportMessage(InteropMessage $transportMessage): voi
         ], $transportMessage->getHeaders());
         $this->assertEquals([
             'pkey' => 'pval',
-            'X-Enqueue-Content-Type' => 'ContentType',
-            'X-Enqueue-Priority' => MessagePriority::HIGH,
-            'X-Enqueue-Expire' => 123,
-            'enqueue.delay' => 345,
+            Config::CONTENT_TYPE => 'ContentType',
+            Config::PRIORITY => MessagePriority::HIGH,
+            Config::EXPIRE => 123,
+            Config::DELAY => 345,
         ], $transportMessage->getProperties());
         $this->assertSame('theMessageId', $transportMessage->getMessageId());
         $this->assertSame(1000, $transportMessage->getTimestamp());
diff --git a/Tests/Client/Driver/GenericDriverTest.php b/Tests/Client/Driver/GenericDriverTest.php
index f145859..98b6c22 100644
--- a/Tests/Client/Driver/GenericDriverTest.php
+++ b/Tests/Client/Driver/GenericDriverTest.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Tests\Client\Driver;
 
+use Enqueue\Client\Config;
 use Enqueue\Client\Driver\GenericDriver;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\MessagePriority;
@@ -68,10 +69,10 @@ protected function assertTransportMessage(InteropMessage $transportMessage): voi
         ], $transportMessage->getHeaders());
         $this->assertEquals([
             'pkey' => 'pval',
-            'X-Enqueue-Content-Type' => 'ContentType',
-            'X-Enqueue-Priority' => MessagePriority::HIGH,
-            'X-Enqueue-Expire' => 123,
-            'enqueue.delay' => 345,
+            Config::CONTENT_TYPE => 'ContentType',
+            Config::PRIORITY => MessagePriority::HIGH,
+            Config::EXPIRE => 123,
+            Config::DELAY => 345,
         ], $transportMessage->getProperties());
         $this->assertSame('theMessageId', $transportMessage->getMessageId());
         $this->assertSame(1000, $transportMessage->getTimestamp());
diff --git a/Tests/Client/Driver/GenericDriverTestsTrait.php b/Tests/Client/Driver/GenericDriverTestsTrait.php
index cf8a5f5..2d39018 100644
--- a/Tests/Client/Driver/GenericDriverTestsTrait.php
+++ b/Tests/Client/Driver/GenericDriverTestsTrait.php
@@ -197,9 +197,9 @@ public function testShouldCreateClientMessageFromTransportOne()
         $transportMessage->setBody('body');
         $transportMessage->setHeaders(['hkey' => 'hval']);
         $transportMessage->setProperty('pkey', 'pval');
-        $transportMessage->setProperty('X-Enqueue-Content-Type', 'theContentType');
-        $transportMessage->setProperty('X-Enqueue-Expire', '22');
-        $transportMessage->setProperty('X-Enqueue-Priority', MessagePriority::HIGH);
+        $transportMessage->setProperty(Config::CONTENT_TYPE, 'theContentType');
+        $transportMessage->setProperty(Config::EXPIRE, '22');
+        $transportMessage->setProperty(Config::PRIORITY, MessagePriority::HIGH);
         $transportMessage->setProperty('enqueue.delay', '44');
         $transportMessage->setMessageId('theMessageId');
         $transportMessage->setTimestamp(1000);
@@ -284,7 +284,7 @@ public function testShouldSendMessageToRouter()
         );
 
         $message = new Message();
-        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
+        $message->setProperty(Config::TOPIC, 'topic');
 
         $driver->sendToRouter($message);
     }
@@ -323,7 +323,7 @@ public function testShouldNotInitDeliveryDelayOnSendMessageToRouter()
 
         $message = new Message();
         $message->setDelay(456);
-        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
+        $message->setProperty(Config::TOPIC, 'topic');
 
         $driver->sendToRouter($message);
     }
@@ -362,7 +362,7 @@ public function testShouldNotInitTimeToLiveOnSendMessageToRouter()
 
         $message = new Message();
         $message->setExpire(456);
-        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
+        $message->setProperty(Config::TOPIC, 'topic');
 
         $driver->sendToRouter($message);
     }
@@ -401,7 +401,7 @@ public function testShouldNotInitPriorityOnSendMessageToRouter()
 
         $message = new Message();
         $message->setPriority(MessagePriority::HIGH);
-        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
+        $message->setProperty(Config::TOPIC, 'topic');
 
         $driver->sendToRouter($message);
     }
@@ -429,7 +429,7 @@ public function testThrowIfCommandSetOnSendToRouter()
         );
 
         $message = new Message();
-        $message->setProperty(Config::COMMAND_PARAMETER, 'aCommand');
+        $message->setProperty(Config::COMMAND, 'aCommand');
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('Command must not be send to router but go directly to its processor.');
@@ -475,8 +475,8 @@ public function testShouldSendTopicMessageToProcessorToDefaultQueue()
         );
 
         $message = new Message();
-        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
-        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
+        $message->setProperty(Config::TOPIC, 'topic');
+        $message->setProperty(Config::PROCESSOR, 'processor');
 
         $driver->sendToProcessor($message);
     }
@@ -519,8 +519,8 @@ public function testShouldSendTopicMessageToProcessorToCustomQueue()
         );
 
         $message = new Message();
-        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
-        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
+        $message->setProperty(Config::TOPIC, 'topic');
+        $message->setProperty(Config::PROCESSOR, 'processor');
 
         $driver->sendToProcessor($message);
     }
@@ -569,8 +569,8 @@ public function testShouldInitDeliveryDelayIfDelayPropertyOnSendToProcessor()
 
         $message = new Message();
         $message->setDelay(456);
-        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
-        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
+        $message->setProperty(Config::TOPIC, 'topic');
+        $message->setProperty(Config::PROCESSOR, 'processor');
 
         $driver->sendToProcessor($message);
     }
@@ -619,8 +619,8 @@ public function testShouldSetInitTimeToLiveIfExpirePropertyOnSendToProcessor()
 
         $message = new Message();
         $message->setExpire(678);
-        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
-        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
+        $message->setProperty(Config::TOPIC, 'topic');
+        $message->setProperty(Config::PROCESSOR, 'processor');
 
         $driver->sendToProcessor($message);
     }
@@ -669,8 +669,8 @@ public function testShouldSetInitPriorityIfPriorityPropertyOnSendToProcessor()
 
         $message = new Message();
         $message->setPriority(MessagePriority::HIGH);
-        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
-        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
+        $message->setProperty(Config::TOPIC, 'topic');
+        $message->setProperty(Config::PROCESSOR, 'processor');
 
         $driver->sendToProcessor($message);
     }
@@ -694,8 +694,8 @@ public function testThrowIfNoRouteFoundForTopicMessageOnSendToProcessor()
         );
 
         $message = new Message();
-        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
-        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
+        $message->setProperty(Config::TOPIC, 'topic');
+        $message->setProperty(Config::PROCESSOR, 'processor');
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('There is no route for topic "topic" and processor "processor"');
@@ -740,11 +740,11 @@ public function testShouldSetRouterProcessorIfProcessorPropertyEmptyOnSendToProc
         );
 
         $message = new Message();
-        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
+        $message->setProperty(Config::TOPIC, 'topic');
 
         $driver->sendToProcessor($message);
 
-        $this->assertSame('router', $message->getProperty(Config::PROCESSOR_PARAMETER));
+        $this->assertSame('router', $message->getProperty(Config::PROCESSOR));
     }
 
     public function testShouldSendCommandMessageToProcessorToDefaultQueue()
@@ -785,8 +785,8 @@ public function testShouldSendCommandMessageToProcessorToDefaultQueue()
         );
 
         $message = new Message();
-        $message->setProperty(Config::COMMAND_PARAMETER, 'command');
-        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
+        $message->setProperty(Config::COMMAND, 'command');
+        $message->setProperty(Config::PROCESSOR, 'processor');
 
         $driver->sendToProcessor($message);
     }
@@ -829,8 +829,8 @@ public function testShouldSendCommandMessageToProcessorToCustomQueue()
         );
 
         $message = new Message();
-        $message->setProperty(Config::COMMAND_PARAMETER, 'command');
-        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
+        $message->setProperty(Config::COMMAND, 'command');
+        $message->setProperty(Config::PROCESSOR, 'processor');
 
         $driver->sendToProcessor($message);
     }
@@ -854,8 +854,8 @@ public function testThrowIfNoRouteFoundForCommandMessageOnSendToProcessor()
         );
 
         $message = new Message();
-        $message->setProperty(Config::COMMAND_PARAMETER, 'command');
-        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
+        $message->setProperty(Config::COMMAND, 'command');
+        $message->setProperty(Config::PROCESSOR, 'processor');
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('There is no route for command "command".');
@@ -900,12 +900,12 @@ public function testShouldOverwriteProcessorPropertySetByOneFromCommandRouteOnSe
         );
 
         $message = new Message();
-        $message->setProperty(Config::COMMAND_PARAMETER, 'command');
-        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processorShouldBeOverwritten');
+        $message->setProperty(Config::COMMAND, 'command');
+        $message->setProperty(Config::PROCESSOR, 'processorShouldBeOverwritten');
 
         $driver->sendToProcessor($message);
 
-        $this->assertSame('expectedProcessor', $message->getProperty(Config::PROCESSOR_PARAMETER));
+        $this->assertSame('expectedProcessor', $message->getProperty(Config::PROCESSOR));
     }
 
     public function testShouldNotInitDeliveryDelayOnSendMessageToProcessorIfPropertyNull()
@@ -948,7 +948,7 @@ public function testShouldNotInitDeliveryDelayOnSendMessageToProcessorIfProperty
         );
 
         $message = new Message();
-        $message->setProperty(Config::COMMAND_PARAMETER, 'command');
+        $message->setProperty(Config::COMMAND, 'command');
         $message->setDelay(null);
 
         $driver->sendToProcessor($message);
@@ -994,7 +994,7 @@ public function testShouldNotInitPriorityOnSendMessageToProcessorIfPropertyNull(
         );
 
         $message = new Message();
-        $message->setProperty(Config::COMMAND_PARAMETER, 'command');
+        $message->setProperty(Config::COMMAND, 'command');
         $message->setPriority(null);
 
         $driver->sendToProcessor($message);
@@ -1040,7 +1040,7 @@ public function testShouldNotInitTimeToLiveOnSendMessageToProcessorIfPropertyNul
         );
 
         $message = new Message();
-        $message->setProperty(Config::COMMAND_PARAMETER, 'command');
+        $message->setProperty(Config::COMMAND, 'command');
         $message->setExpire(null);
 
         $driver->sendToProcessor($message);
@@ -1058,7 +1058,7 @@ public function testThrowIfNeitherTopicNorCommandAreSentOnSendToProcessor()
         $this->expectExceptionMessage('Queue name parameter is required but is not set');
 
         $message = new Message();
-        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
+        $message->setProperty(Config::PROCESSOR, 'processor');
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('Either topic or command parameter must be set.');
@@ -1121,10 +1121,10 @@ protected function assertTransportMessage(InteropMessage $transportMessage): voi
         ], $transportMessage->getHeaders());
         $this->assertEquals([
             'pkey' => 'pval',
-            'X-Enqueue-Content-Type' => 'ContentType',
-            'X-Enqueue-Priority' => MessagePriority::HIGH,
-            'X-Enqueue-Expire' => 123,
-            'enqueue.delay' => 345,
+            Config::CONTENT_TYPE => 'ContentType',
+            Config::PRIORITY => MessagePriority::HIGH,
+            Config::EXPIRE => 123,
+            Config::DELAY => 345,
         ], $transportMessage->getProperties());
         $this->assertSame('theMessageId', $transportMessage->getMessageId());
         $this->assertSame(1000, $transportMessage->getTimestamp());
@@ -1140,10 +1140,10 @@ protected function assertClientMessage(Message $clientMessage): void
         ], $clientMessage->getHeaders());
         $this->assertArraySubset([
             'pkey' => 'pval',
-            'X-Enqueue-Content-Type' => 'theContentType',
-            'X-Enqueue-Expire' => '22',
-            'X-Enqueue-Priority' => MessagePriority::HIGH,
-            'enqueue.delay' => '44',
+            Config::CONTENT_TYPE => 'theContentType',
+            Config::EXPIRE => '22',
+            Config::PRIORITY => MessagePriority::HIGH,
+            Config::DELAY => '44',
         ], $clientMessage->getProperties());
         $this->assertSame('theMessageId', $clientMessage->getMessageId());
         $this->assertSame(22, $clientMessage->getExpire());
diff --git a/Tests/Client/Driver/RabbitMqDriverTest.php b/Tests/Client/Driver/RabbitMqDriverTest.php
index eebb9b7..2f6e20a 100644
--- a/Tests/Client/Driver/RabbitMqDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqDriverTest.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Tests\Client\Driver;
 
+use Enqueue\Client\Config;
 use Enqueue\Client\Driver\AmqpDriver;
 use Enqueue\Client\Driver\GenericDriver;
 use Enqueue\Client\Driver\RabbitMqDriver;
@@ -127,10 +128,10 @@ protected function assertTransportMessage(InteropMessage $transportMessage): voi
         ], $transportMessage->getHeaders());
         $this->assertEquals([
             'pkey' => 'pval',
-            'X-Enqueue-Content-Type' => 'ContentType',
-            'X-Enqueue-Priority' => MessagePriority::HIGH,
-            'X-Enqueue-Expire' => 123,
-            'enqueue.delay' => 345,
+            Config::CONTENT_TYPE => 'ContentType',
+            Config::PRIORITY => MessagePriority::HIGH,
+            Config::EXPIRE => 123,
+            Config::DELAY => 345,
         ], $transportMessage->getProperties());
         $this->assertSame('theMessageId', $transportMessage->getMessageId());
         $this->assertSame(1000, $transportMessage->getTimestamp());
diff --git a/Tests/Client/Driver/RabbitMqStompDriverTest.php b/Tests/Client/Driver/RabbitMqStompDriverTest.php
index 22cda4e..5e6dc24 100644
--- a/Tests/Client/Driver/RabbitMqStompDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqStompDriverTest.php
@@ -250,8 +250,8 @@ public function shouldSendMessageToDelayExchangeIfDelaySet()
         );
 
         $message = new Message();
-        $message->setProperty(Config::TOPIC_PARAMETER, 'topic');
-        $message->setProperty(Config::PROCESSOR_PARAMETER, 'processor');
+        $message->setProperty(Config::TOPIC, 'topic');
+        $message->setProperty(Config::PROCESSOR, 'processor');
         $message->setDelay(10);
 
         $driver->sendToProcessor($message);
@@ -540,10 +540,10 @@ protected function assertTransportMessage(InteropMessage $transportMessage): voi
         ], $transportMessage->getHeaders());
         $this->assertEquals([
             'pkey' => 'pval',
-            'X-Enqueue-Content-Type' => 'ContentType',
-            'X-Enqueue-Priority' => MessagePriority::HIGH,
-            'X-Enqueue-Expire' => 123,
-            'enqueue.delay' => 345,
+            Config::CONTENT_TYPE => 'ContentType',
+            Config::PRIORITY => MessagePriority::HIGH,
+            Config::EXPIRE => 123,
+            Config::DELAY => 345,
         ], $transportMessage->getProperties());
         $this->assertSame('theMessageId', $transportMessage->getMessageId());
         $this->assertSame(1000, $transportMessage->getTimestamp());
diff --git a/Tests/Client/Driver/StompDriverTest.php b/Tests/Client/Driver/StompDriverTest.php
index 0a3415c..67764f3 100644
--- a/Tests/Client/Driver/StompDriverTest.php
+++ b/Tests/Client/Driver/StompDriverTest.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Tests\Client\Driver;
 
+use Enqueue\Client\Config;
 use Enqueue\Client\Driver\GenericDriver;
 use Enqueue\Client\Driver\StompDriver;
 use Enqueue\Client\DriverInterface;
@@ -166,10 +167,10 @@ protected function assertTransportMessage(InteropMessage $transportMessage): voi
         ], $transportMessage->getHeaders());
         $this->assertEquals([
             'pkey' => 'pval',
-            'X-Enqueue-Content-Type' => 'ContentType',
-            'X-Enqueue-Priority' => MessagePriority::HIGH,
-            'X-Enqueue-Expire' => 123,
-            'enqueue.delay' => 345,
+            Config::CONTENT_TYPE => 'ContentType',
+            Config::PRIORITY => MessagePriority::HIGH,
+            Config::EXPIRE => 123,
+            Config::DELAY => 345,
         ], $transportMessage->getProperties());
         $this->assertSame('theMessageId', $transportMessage->getMessageId());
         $this->assertSame(1000, $transportMessage->getTimestamp());
diff --git a/Tests/Client/DriverPreSendTest.php b/Tests/Client/DriverPreSendTest.php
index 95ac891..d31ac17 100644
--- a/Tests/Client/DriverPreSendTest.php
+++ b/Tests/Client/DriverPreSendTest.php
@@ -48,7 +48,7 @@ public function testShouldAllowGetArgumentSetInConstructor()
     public function testShouldAllowGetCommand()
     {
         $message = new Message();
-        $message->setProperty(Config::COMMAND_PARAMETER, 'theCommand');
+        $message->setProperty(Config::COMMAND, 'theCommand');
 
         $context = new DriverPreSend(
             $message,
@@ -63,7 +63,7 @@ public function testShouldAllowGetCommand()
     public function testShouldAllowGetTopic()
     {
         $message = new Message();
-        $message->setProperty(Config::TOPIC_PARAMETER, 'theTopic');
+        $message->setProperty(Config::TOPIC, 'theTopic');
 
         $context = new DriverPreSend(
             $message,
diff --git a/Tests/Client/PostSendTest.php b/Tests/Client/PostSendTest.php
index a68c12b..6586076 100644
--- a/Tests/Client/PostSendTest.php
+++ b/Tests/Client/PostSendTest.php
@@ -48,7 +48,7 @@ public function testShouldAllowGetArgumentSetInConstructor()
     public function testShouldAllowGetCommand()
     {
         $message = new Message();
-        $message->setProperty(Config::COMMAND_PARAMETER, 'theCommand');
+        $message->setProperty(Config::COMMAND, 'theCommand');
 
         $context = new PostSend(
             $message,
@@ -63,7 +63,7 @@ public function testShouldAllowGetCommand()
     public function testShouldAllowGetTopic()
     {
         $message = new Message();
-        $message->setProperty(Config::TOPIC_PARAMETER, 'theTopic');
+        $message->setProperty(Config::TOPIC, 'theTopic');
 
         $context = new PostSend(
             $message,
diff --git a/Tests/Client/ProducerSendCommandTest.php b/Tests/Client/ProducerSendCommandTest.php
index ea18550..92885aa 100644
--- a/Tests/Client/ProducerSendCommandTest.php
+++ b/Tests/Client/ProducerSendCommandTest.php
@@ -136,7 +136,7 @@ public function testShouldSendCommandWithReplyAndCustomReplyQueueAndCorrelationI
     public function testShouldOverwriteExpectedMessageProperties()
     {
         $message = new Message();
-        $message->setProperty(Config::COMMAND_PARAMETER, 'commandShouldBeOverwritten');
+        $message->setProperty(Config::COMMAND, 'commandShouldBeOverwritten');
         $message->setScope('scopeShouldBeOverwritten');
 
         $driver = $this->createDriverStub();
@@ -299,8 +299,8 @@ public function testShouldSendCommandToApplicationRouter()
             ->method('sendToProcessor')
             ->willReturnCallback(function (Message $message) {
                 self::assertSame('aBody', $message->getBody());
-                self::assertNull($message->getProperty(Config::PROCESSOR_PARAMETER));
-                self::assertSame('command', $message->getProperty(Config::COMMAND_PARAMETER));
+                self::assertNull($message->getProperty(Config::PROCESSOR));
+                self::assertSame('command', $message->getProperty(Config::COMMAND));
             })
         ;
 
@@ -313,7 +313,7 @@ public function testThrowWhenProcessorNamePropertySetToApplicationRouter()
         $message = new Message();
         $message->setBody('aBody');
         $message->setScope(Message::SCOPE_APP);
-        $message->setProperty(Config::PROCESSOR_PARAMETER, 'aCustomProcessor');
+        $message->setProperty(Config::PROCESSOR, 'aCustomProcessor');
 
         $driver = $this->createDriverStub();
         $driver
diff --git a/Tests/Client/ProducerSendEventTest.php b/Tests/Client/ProducerSendEventTest.php
index e957e16..0fdd780 100644
--- a/Tests/Client/ProducerSendEventTest.php
+++ b/Tests/Client/ProducerSendEventTest.php
@@ -45,7 +45,7 @@ public function testShouldSendEventToRouter()
     public function testShouldOverwriteTopicProperty()
     {
         $message = new Message();
-        $message->setProperty(Config::TOPIC_PARAMETER, 'topicShouldBeOverwritten');
+        $message->setProperty(Config::TOPIC, 'topicShouldBeOverwritten');
 
         $driver = $this->createDriverStub();
 
@@ -198,7 +198,7 @@ public function testThrowIfSendEventToMessageBusWithProcessorNamePropertySet()
     {
         $message = new Message();
         $message->setBody('');
-        $message->setProperty(Config::PROCESSOR_PARAMETER, 'aProcessor');
+        $message->setProperty(Config::PROCESSOR, 'aProcessor');
 
         $driver = $this->createDriverStub();
         $driver
@@ -235,7 +235,7 @@ public function testShouldSendEventToApplicationRouter()
                 self::assertSame('aBody', $message->getBody());
 
                 // null means a driver sends a message to router processor.
-                self::assertNull($message->getProperty(Config::PROCESSOR_PARAMETER));
+                self::assertNull($message->getProperty(Config::PROCESSOR));
             })
         ;
 
@@ -248,7 +248,7 @@ public function testThrowWhenProcessorNamePropertySetToApplicationRouter()
         $message = new Message();
         $message->setBody('aBody');
         $message->setScope(Message::SCOPE_APP);
-        $message->setProperty(Config::PROCESSOR_PARAMETER, 'aCustomProcessor');
+        $message->setProperty(Config::PROCESSOR, 'aCustomProcessor');
 
         $driver = $this->createDriverStub();
         $driver
diff --git a/Tests/Client/RouterProcessorTest.php b/Tests/Client/RouterProcessorTest.php
index 0c245d4..3a4596b 100644
--- a/Tests/Client/RouterProcessorTest.php
+++ b/Tests/Client/RouterProcessorTest.php
@@ -53,7 +53,7 @@ public function testShouldRejectIfCommandSet()
         $router = new RouterProcessor($this->createDriverStub());
 
         $message = new NullMessage();
-        $message->setProperty(Config::COMMAND_PARAMETER, 'aCommand');
+        $message->setProperty(Config::COMMAND, 'aCommand');
 
         $result = $router->process($message, new NullContext());
 
@@ -66,7 +66,7 @@ public function testShouldRouteOriginalMessageToAllRecipients()
         $message = new NullMessage();
         $message->setBody('theBody');
         $message->setHeaders(['aHeader' => 'aHeaderVal']);
-        $message->setProperties(['aProp' => 'aPropVal', Config::TOPIC_PARAMETER => 'theTopicName']);
+        $message->setProperties(['aProp' => 'aPropVal', Config::TOPIC => 'theTopicName']);
 
         /** @var Message[] $routedMessages */
         $routedMessages = new \ArrayObject();
@@ -103,9 +103,9 @@ public function testShouldRouteOriginalMessageToAllRecipients()
         $this->assertContainsOnly(Message::class, $routedMessages);
         $this->assertCount(3, $routedMessages);
 
-        $this->assertSame('aFooProcessor', $routedMessages[0]->getProperty(Config::PROCESSOR_PARAMETER));
-        $this->assertSame('aBarProcessor', $routedMessages[1]->getProperty(Config::PROCESSOR_PARAMETER));
-        $this->assertSame('aBazProcessor', $routedMessages[2]->getProperty(Config::PROCESSOR_PARAMETER));
+        $this->assertSame('aFooProcessor', $routedMessages[0]->getProperty(Config::PROCESSOR));
+        $this->assertSame('aBarProcessor', $routedMessages[1]->getProperty(Config::PROCESSOR));
+        $this->assertSame('aBazProcessor', $routedMessages[2]->getProperty(Config::PROCESSOR));
     }
 
     public function testShouldDoNothingIfNoRoutes()
@@ -113,7 +113,7 @@ public function testShouldDoNothingIfNoRoutes()
         $message = new NullMessage();
         $message->setBody('theBody');
         $message->setHeaders(['aHeader' => 'aHeaderVal']);
-        $message->setProperties(['aProp' => 'aPropVal', Config::TOPIC_PARAMETER => 'theTopicName']);
+        $message->setProperties(['aProp' => 'aPropVal', Config::TOPIC => 'theTopicName']);
 
         /** @var Message[] $routedMessages */
         $routedMessages = new \ArrayObject();
@@ -151,7 +151,7 @@ public function testShouldDoNotModifyOriginalMessage()
         $message = new NullMessage();
         $message->setBody('theBody');
         $message->setHeaders(['aHeader' => 'aHeaderVal']);
-        $message->setProperties(['aProp' => 'aPropVal', Config::TOPIC_PARAMETER => 'theTopicName']);
+        $message->setProperties(['aProp' => 'aPropVal', Config::TOPIC => 'theTopicName']);
 
         /** @var Message[] $routedMessages */
         $routedMessages = new \ArrayObject();
@@ -183,7 +183,7 @@ public function testShouldDoNotModifyOriginalMessage()
         $this->assertEquals(Result::ACK, $result->getStatus());
 
         $this->assertSame('theBody', $message->getBody());
-        $this->assertSame(['aProp' => 'aPropVal', Config::TOPIC_PARAMETER => 'theTopicName'], $message->getProperties());
+        $this->assertSame(['aProp' => 'aPropVal', Config::TOPIC => 'theTopicName'], $message->getProperties());
         $this->assertSame(['aHeader' => 'aHeaderVal'], $message->getHeaders());
     }
 

From 0cc122c105b6e5c5ac5c319566d1a2e4bd540892 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 1 Oct 2018 18:33:11 +0300
Subject: [PATCH 141/337] [client] Rename config options.

---
 Client/Config.php                             | 79 ++++++++++---------
 .../SetRouterPropertiesExtension.php          |  6 +-
 Client/Driver/AmqpDriver.php                  |  4 +-
 Client/Driver/FsDriver.php                    |  2 +-
 Client/Driver/GenericDriver.php               | 10 +--
 Client/Driver/GpsDriver.php                   |  4 +-
 Client/Driver/RabbitMqStompDriver.php         |  4 +-
 Client/Driver/RdKafkaDriver.php               |  4 +-
 Client/Driver/SqsDriver.php                   |  2 +-
 Client/Driver/StompDriver.php                 |  2 +-
 Symfony/Client/ConsumeMessagesCommand.php     |  4 +-
 Symfony/Client/RoutesCommand.php              |  2 +-
 Tests/Client/ConfigTest.php                   | 20 ++---
 13 files changed, 75 insertions(+), 68 deletions(-)

diff --git a/Client/Config.php b/Client/Config.php
index 0dc4ee5..ef68fb6 100644
--- a/Client/Config.php
+++ b/Client/Config.php
@@ -20,59 +20,66 @@ class Config
     /**
      * @var string
      */
-    private $appName;
+    private $separator;
 
     /**
      * @var string
      */
-    private $routerTopicName;
+    private $app;
 
     /**
      * @var string
      */
-    private $routerQueueName;
+    private $routerTopic;
 
     /**
      * @var string
      */
-    private $defaultProcessorQueueName;
+    private $routerQueue;
 
     /**
      * @var string
      */
-    private $routerProcessorName;
+    private $defaultQueue;
+
+    /**
+     * @var string
+     */
+    private $routerProcessor;
 
     /**
      * @var array
      */
     private $transportConfig;
 
-    public function __construct(string $prefix, string $appName, string $routerTopicName, string $routerQueueName, string $defaultProcessorQueueName, string $routerProcessorName, array $transportConfig = [])
+    public function __construct(string $prefix, string $app, string $routerTopic, string $routerQueue, string $defaultQueue, string $routerProcessor, array $transportConfig = [])
     {
         $this->prefix = trim($prefix);
-        $this->appName = trim($appName);
+        $this->app = trim($app);
 
-        $this->routerTopicName = trim($routerTopicName);
-        if (empty($this->routerTopicName)) {
+        $this->routerTopic = trim($routerTopic);
+        if (empty($this->routerTopic)) {
             throw new \InvalidArgumentException('Router topic is empty.');
         }
 
-        $this->routerQueueName = trim($routerQueueName);
-        if (empty($this->routerQueueName)) {
+        $this->routerQueue = trim($routerQueue);
+        if (empty($this->routerQueue)) {
             throw new \InvalidArgumentException('Router queue is empty.');
         }
 
-        $this->defaultProcessorQueueName = trim($defaultProcessorQueueName);
-        if (empty($this->defaultProcessorQueueName)) {
+        $this->defaultQueue = trim($defaultQueue);
+        if (empty($this->defaultQueue)) {
             throw new \InvalidArgumentException('Default processor queue name is empty.');
         }
 
-        $this->routerProcessorName = trim($routerProcessorName);
-        if (empty($this->routerProcessorName)) {
+        $this->routerProcessor = trim($routerProcessor);
+        if (empty($this->routerProcessor)) {
             throw new \InvalidArgumentException('Router processor name is empty.');
         }
 
         $this->transportConfig = $transportConfig;
+
+        $this->separator = '.';
     }
 
     public function getPrefix(): string
@@ -82,32 +89,32 @@ public function getPrefix(): string
 
     public function getSeparator(): string
     {
-        return '.';
+        return $this->separator;
     }
 
-    public function getAppName(): string
+    public function getApp(): string
     {
-        return $this->appName;
+        return $this->app;
     }
 
-    public function getRouterTopicName(): string
+    public function getRouterTopic(): string
     {
-        return $this->routerTopicName;
+        return $this->routerTopic;
     }
 
-    public function getRouterQueueName(): string
+    public function getRouterQueue(): string
     {
-        return $this->routerQueueName;
+        return $this->routerQueue;
     }
 
-    public function getDefaultProcessorQueueName(): string
+    public function getDefaultQueue(): string
     {
-        return $this->defaultProcessorQueueName;
+        return $this->defaultQueue;
     }
 
-    public function getRouterProcessorName(): string
+    public function getRouterProcessor(): string
     {
-        return $this->routerProcessorName;
+        return $this->routerProcessor;
     }
 
     /**
@@ -122,20 +129,20 @@ public function getTransportOption(string $name, $default = null)
 
     public static function create(
         string $prefix = null,
-        string $appName = null,
-        string $routerTopicName = null,
-        string $routerQueueName = null,
-        string $defaultProcessorQueueName = null,
-        string $routerProcessorName = null,
+        string $app = null,
+        string $routerTopic = null,
+        string $routerQueue = null,
+        string $defaultQueue = null,
+        string $routerProcessor = null,
         array $transportConfig = []
     ): self {
         return new static(
             $prefix ?: '',
-            $appName ?: '',
-            $routerTopicName ?: 'router',
-            $routerQueueName ?: 'default',
-            $defaultProcessorQueueName ?: 'default',
-            $routerProcessorName ?: 'router',
+            $app ?: '',
+            $routerTopic ?: 'router',
+            $routerQueue ?: 'default',
+            $defaultQueue ?: 'default',
+            $routerProcessor ?: 'router',
             $transportConfig
         );
     }
diff --git a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
index 2ecc1dd..ca6a32b 100644
--- a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
+++ b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
@@ -33,17 +33,17 @@ public function onPreReceived(Context $context)
         }
 
         $config = $this->driver->getConfig();
-        $queue = $this->driver->createQueue($config->getRouterQueueName());
+        $queue = $this->driver->createQueue($config->getRouterQueue());
         if ($context->getInteropQueue()->getQueueName() != $queue->getQueueName()) {
             return;
         }
 
         // RouterProcessor is our default message processor when that header is not set
-        $message->setProperty(Config::PROCESSOR, $config->getRouterProcessorName());
+        $message->setProperty(Config::PROCESSOR, $config->getRouterProcessor());
 
         $context->getLogger()->debug(
             '[SetRouterPropertiesExtension] '.
-            sprintf('Set router processor "%s"', $config->getRouterProcessorName())
+            sprintf('Set router processor "%s"', $config->getRouterProcessor())
         );
     }
 }
diff --git a/Client/Driver/AmqpDriver.php b/Client/Driver/AmqpDriver.php
index 6d36fc4..be62753 100644
--- a/Client/Driver/AmqpDriver.php
+++ b/Client/Driver/AmqpDriver.php
@@ -70,7 +70,7 @@ public function setupBroker(LoggerInterface $logger = null): void
         $log('Declare router exchange: %s', $routerTopic->getTopicName());
         $this->getContext()->declareTopic($routerTopic);
 
-        $routerQueue = $this->createQueue($this->getConfig()->getRouterQueueName());
+        $routerQueue = $this->createQueue($this->getConfig()->getRouterQueue());
         $log('Declare router queue: %s', $routerQueue->getQueueName());
         $this->getContext()->declareQueue($routerQueue);
 
@@ -99,7 +99,7 @@ public function setupBroker(LoggerInterface $logger = null): void
     protected function createRouterTopic(): Destination
     {
         $topic = $this->doCreateTopic(
-            $this->createTransportRouterTopicName($this->getConfig()->getRouterTopicName(), true)
+            $this->createTransportRouterTopicName($this->getConfig()->getRouterTopic(), true)
         );
         $topic->setType(AmqpTopic::TYPE_FANOUT);
         $topic->addFlag(AmqpTopic::FLAG_DURABLE);
diff --git a/Client/Driver/FsDriver.php b/Client/Driver/FsDriver.php
index 7931ea3..9de59c2 100644
--- a/Client/Driver/FsDriver.php
+++ b/Client/Driver/FsDriver.php
@@ -26,7 +26,7 @@ public function setupBroker(LoggerInterface $logger = null): void
         };
 
         // setup router
-        $routerQueue = $this->createQueue($this->getConfig()->getRouterQueueName());
+        $routerQueue = $this->createQueue($this->getConfig()->getRouterQueue());
 
         $log('Declare router queue "%s" file: %s', $routerQueue->getQueueName(), $routerQueue->getFileInfo());
         $this->getContext()->declareDestination($routerQueue);
diff --git a/Client/Driver/GenericDriver.php b/Client/Driver/GenericDriver.php
index 2da6b7d..740bbaf 100644
--- a/Client/Driver/GenericDriver.php
+++ b/Client/Driver/GenericDriver.php
@@ -77,9 +77,9 @@ public function sendToProcessor(Message $message): void
             $message->setProperty(Config::PROCESSOR, $route->getProcessor());
             $queue = $this->createRouteQueue($route);
         } elseif ($topic && false == $message->getProperty(Config::PROCESSOR)) {
-            $message->setProperty(Config::PROCESSOR, $this->config->getRouterProcessorName());
+            $message->setProperty(Config::PROCESSOR, $this->config->getRouterProcessor());
 
-            $queue = $this->createQueue($this->config->getRouterQueueName());
+            $queue = $this->createQueue($this->config->getRouterQueue());
         } elseif ($command) {
             $route = $this->routeCollection->command($command);
             if (false == $route) {
@@ -127,7 +127,7 @@ public function createQueue(string $clientQueueName, bool $prefix = true): Inter
     public function createRouteQueue(Route $route): InteropQueue
     {
         $transportName = $this->createTransportQueueName(
-            $route->getQueue() ?: $this->config->getDefaultProcessorQueueName(),
+            $route->getQueue() ?: $this->config->getDefaultQueue(),
             $route->isPrefixQueue()
         );
 
@@ -225,7 +225,7 @@ protected function doSendToProcessor(InteropProducer $producer, InteropQueue $qu
 
     protected function createRouterTopic(): Destination
     {
-        return $this->createQueue($this->getConfig()->getRouterQueueName());
+        return $this->createQueue($this->getConfig()->getRouterQueue());
     }
 
     protected function createTransportRouterTopicName(string $name, bool $prefix): string
@@ -238,7 +238,7 @@ protected function createTransportRouterTopicName(string $name, bool $prefix): s
     protected function createTransportQueueName(string $name, bool $prefix): string
     {
         $clientPrefix = $prefix ? $this->config->getPrefix() : '';
-        $clientAppName = $prefix ? $this->config->getAppName() : '';
+        $clientAppName = $prefix ? $this->config->getApp() : '';
 
         return strtolower(implode($this->config->getSeparator(), array_filter([$clientPrefix, $clientAppName, $name])));
     }
diff --git a/Client/Driver/GpsDriver.php b/Client/Driver/GpsDriver.php
index 2a37b72..37a7110 100644
--- a/Client/Driver/GpsDriver.php
+++ b/Client/Driver/GpsDriver.php
@@ -29,7 +29,7 @@ public function setupBroker(LoggerInterface $logger = null): void
 
         // setup router
         $routerTopic = $this->createRouterTopic();
-        $routerQueue = $this->createQueue($this->getConfig()->getRouterQueueName());
+        $routerQueue = $this->createQueue($this->getConfig()->getRouterQueue());
 
         $log('Subscribe router topic to queue: %s -> %s', $routerTopic->getTopicName(), $routerQueue->getQueueName());
         $this->getContext()->subscribe($routerTopic, $routerQueue);
@@ -58,7 +58,7 @@ public function setupBroker(LoggerInterface $logger = null): void
     protected function createRouterTopic(): Destination
     {
         return $this->doCreateTopic(
-            $this->createTransportRouterTopicName($this->getConfig()->getRouterTopicName(), true)
+            $this->createTransportRouterTopicName($this->getConfig()->getRouterTopic(), true)
         );
     }
 }
diff --git a/Client/Driver/RabbitMqStompDriver.php b/Client/Driver/RabbitMqStompDriver.php
index c2ca768..36bdd86 100644
--- a/Client/Driver/RabbitMqStompDriver.php
+++ b/Client/Driver/RabbitMqStompDriver.php
@@ -76,7 +76,7 @@ public function setupBroker(LoggerInterface $logger = null): void
         }
 
         // setup router
-        $routerExchange = $this->createTransportRouterTopicName($this->getConfig()->getRouterTopicName(), true);
+        $routerExchange = $this->createTransportRouterTopicName($this->getConfig()->getRouterTopic(), true);
         $log('Declare router exchange: %s', $routerExchange);
         $this->management->declareExchange($routerExchange, [
             'type' => 'fanout',
@@ -84,7 +84,7 @@ public function setupBroker(LoggerInterface $logger = null): void
             'auto_delete' => false,
         ]);
 
-        $routerQueue = $this->createTransportQueueName($this->getConfig()->getRouterQueueName(), true);
+        $routerQueue = $this->createTransportQueueName($this->getConfig()->getRouterQueue(), true);
         $log('Declare router queue: %s', $routerQueue);
         $this->management->declareQueue($routerQueue, [
             'auto_delete' => false,
diff --git a/Client/Driver/RdKafkaDriver.php b/Client/Driver/RdKafkaDriver.php
index 1a10b22..ecb711c 100644
--- a/Client/Driver/RdKafkaDriver.php
+++ b/Client/Driver/RdKafkaDriver.php
@@ -27,7 +27,7 @@ public function setupBroker(LoggerInterface $logger = null): void
         };
 
         // setup router
-        $routerQueue = $this->createQueue($this->getConfig()->getRouterQueueName());
+        $routerQueue = $this->createQueue($this->getConfig()->getRouterQueue());
         $log('Create router queue: %s', $routerQueue->getQueueName());
         $this->getContext()->createConsumer($routerQueue);
 
@@ -51,7 +51,7 @@ public function setupBroker(LoggerInterface $logger = null): void
     protected function createRouterTopic(): Destination
     {
         return $this->doCreateTopic(
-            $this->createTransportRouterTopicName($this->getConfig()->getRouterTopicName(), true)
+            $this->createTransportRouterTopicName($this->getConfig()->getRouterTopic(), true)
         );
     }
 }
diff --git a/Client/Driver/SqsDriver.php b/Client/Driver/SqsDriver.php
index 9672d39..bf66c05 100644
--- a/Client/Driver/SqsDriver.php
+++ b/Client/Driver/SqsDriver.php
@@ -26,7 +26,7 @@ public function setupBroker(LoggerInterface $logger = null): void
         };
 
         // setup router
-        $routerQueue = $this->createQueue($this->getConfig()->getRouterQueueName());
+        $routerQueue = $this->createQueue($this->getConfig()->getRouterQueue());
         $log('Declare router queue: %s', $routerQueue->getQueueName());
         $this->getContext()->declareQueue($routerQueue);
 
diff --git a/Client/Driver/StompDriver.php b/Client/Driver/StompDriver.php
index 7236505..7040c71 100644
--- a/Client/Driver/StompDriver.php
+++ b/Client/Driver/StompDriver.php
@@ -61,7 +61,7 @@ protected function createRouterTopic(): Destination
     {
         /** @var StompDestination $topic */
         $topic = $this->doCreateTopic(
-            $this->createTransportRouterTopicName($this->getConfig()->getRouterTopicName(), true)
+            $this->createTransportRouterTopicName($this->getConfig()->getRouterTopic(), true)
         );
         $topic->setDurable(true);
         $topic->setAutoDelete(false);
diff --git a/Symfony/Client/ConsumeMessagesCommand.php b/Symfony/Client/ConsumeMessagesCommand.php
index f38fc86..7278937 100644
--- a/Symfony/Client/ConsumeMessagesCommand.php
+++ b/Symfony/Client/ConsumeMessagesCommand.php
@@ -74,8 +74,8 @@ protected function execute(InputInterface $input, OutputInterface $output): ?int
 
         $clientQueueNames = $input->getArgument('client-queue-names');
         if (empty($clientQueueNames)) {
-            $clientQueueNames[$this->driver->getConfig()->getDefaultProcessorQueueName()] = true;
-            $clientQueueNames[$this->driver->getConfig()->getRouterQueueName()] = true;
+            $clientQueueNames[$this->driver->getConfig()->getDefaultQueue()] = true;
+            $clientQueueNames[$this->driver->getConfig()->getRouterQueue()] = true;
 
             foreach ($this->driver->getRouteCollection()->all() as $route) {
                 if ($route->getQueue()) {
diff --git a/Symfony/Client/RoutesCommand.php b/Symfony/Client/RoutesCommand.php
index e4abc20..132fd1b 100644
--- a/Symfony/Client/RoutesCommand.php
+++ b/Symfony/Client/RoutesCommand.php
@@ -119,7 +119,7 @@ private function formatProcessor(Route $route): string
 
     private function formatQueue(Route $route): string
     {
-        $queue = $route->getQueue() ?: $this->config->getDefaultProcessorQueueName();
+        $queue = $route->getQueue() ?: $this->config->getDefaultQueue();
 
         return $route->isPrefixQueue() ? $queue.' (prefixed)' : $queue.' (as is)';
     }
diff --git a/Tests/Client/ConfigTest.php b/Tests/Client/ConfigTest.php
index e130319..8e54508 100644
--- a/Tests/Client/ConfigTest.php
+++ b/Tests/Client/ConfigTest.php
@@ -49,7 +49,7 @@ public function testShouldReturnAppNameSetInConstructor()
             'aRouterProcessorName'
         );
 
-        $this->assertEquals('theApp', $config->getAppName());
+        $this->assertEquals('theApp', $config->getApp());
     }
 
     /**
@@ -66,7 +66,7 @@ public function testShouldTrimReturnAppNameSetInConstructor(string $empty)
             'aRouterProcessorName'
         );
 
-        $this->assertSame('', $config->getAppName());
+        $this->assertSame('', $config->getApp());
     }
 
     public function testShouldReturnRouterProcessorNameSetInConstructor()
@@ -80,7 +80,7 @@ public function testShouldReturnRouterProcessorNameSetInConstructor()
             'aRouterProcessorName'
         );
 
-        $this->assertEquals('aRouterProcessorName', $config->getRouterProcessorName());
+        $this->assertEquals('aRouterProcessorName', $config->getRouterProcessor());
     }
 
     public function testShouldReturnRouterTopicNameSetInConstructor()
@@ -94,7 +94,7 @@ public function testShouldReturnRouterTopicNameSetInConstructor()
             'aRouterProcessorName'
         );
 
-        $this->assertEquals('aRouterTopicName', $config->getRouterTopicName());
+        $this->assertEquals('aRouterTopicName', $config->getRouterTopic());
     }
 
     public function testShouldReturnRouterQueueNameSetInConstructor()
@@ -108,7 +108,7 @@ public function testShouldReturnRouterQueueNameSetInConstructor()
             'aRouterProcessorName'
         );
 
-        $this->assertEquals('aRouterQueueName', $config->getRouterQueueName());
+        $this->assertEquals('aRouterQueueName', $config->getRouterQueue());
     }
 
     public function testShouldReturnDefaultQueueNameSetInConstructor()
@@ -122,17 +122,17 @@ public function testShouldReturnDefaultQueueNameSetInConstructor()
             'aRouterProcessorName'
         );
 
-        $this->assertEquals('aDefaultQueueName', $config->getDefaultProcessorQueueName());
+        $this->assertEquals('aDefaultQueueName', $config->getDefaultQueue());
     }
 
     public function testShouldCreateDefaultConfig()
     {
         $config = Config::create();
 
-        $this->assertSame('default', $config->getDefaultProcessorQueueName());
-        $this->assertSame('router', $config->getRouterProcessorName());
-        $this->assertSame('default', $config->getRouterQueueName());
-        $this->assertSame('router', $config->getRouterTopicName());
+        $this->assertSame('default', $config->getDefaultQueue());
+        $this->assertSame('router', $config->getRouterProcessor());
+        $this->assertSame('default', $config->getRouterQueue());
+        $this->assertSame('router', $config->getRouterTopic());
     }
 
     /**

From 8438e41bb58a0de2712f3cfd7bad3a2483fd52a1 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 2 Oct 2018 15:16:49 +0300
Subject: [PATCH 142/337] [consumption] Add ability to consume from multiple
 transports.

---
 ...Registry.php => ArrayProcessorRegistry.php |   7 +-
 Client/DelegateProcessor.php                  |   1 +
 Consumption/ArrayQueueConsumerRegistry.php    |  36 +++
 .../QueueConsumerRegistryInterface.php        |  10 +
 ...face.php => ProcessorRegistryInterface.php |   4 +-
 .../ConfigurableConsumeCommand.php            |  90 ++++++
 ...MessagesCommand.php => ConsumeCommand.php} |  36 ++-
 .../ContainerAwareConsumeMessagesCommand.php  | 101 ------
 .../ContainerQueueConsumerRegistry.php        |  31 ++
 .../ContainerProcessorRegistry.php            |   4 +-
 .../BuildConsumptionExtensionsPass.php        |  62 ++++
 .../BuildProcessorRegistryPass.php            |  52 ++++
 .../ArrayProcessorRegistryTest.php            |   6 +-
 Tests/Client/DelegateProcessorTest.php        |   8 +-
 .../ArrayQueueConsumerRegistryTest.php        |  60 ++++
 .../ConfigurableConsumeCommandTest.php        | 288 ++++++++++++++++++
 .../Consumption/ConsumeCommandTest.php        | 126 ++++++++
 .../ConsumeMessagesCommandTest.php            |  86 ------
 ...ntainerAwareConsumeMessagesCommandTest.php | 208 -------------
 .../ContainerQueueConsumerRegistryTest.php    | 108 +++++++
 .../ContainerProcessorRegistryTest.php        | 108 +++++++
 .../BuildConsumptionExtensionsPassTest.php    | 240 +++++++++++++++
 .../BuildProcessorRegistryPassTest.php        | 169 ++++++++++
 23 files changed, 1417 insertions(+), 424 deletions(-)
 rename Client/ArrayProcessorRegistry.php => ArrayProcessorRegistry.php (79%)
 create mode 100644 Consumption/ArrayQueueConsumerRegistry.php
 create mode 100644 Consumption/QueueConsumerRegistryInterface.php
 rename Client/ProcessorRegistryInterface.php => ProcessorRegistryInterface.php (75%)
 create mode 100644 Symfony/Consumption/ConfigurableConsumeCommand.php
 rename Symfony/Consumption/{ConsumeMessagesCommand.php => ConsumeCommand.php} (59%)
 delete mode 100644 Symfony/Consumption/ContainerAwareConsumeMessagesCommand.php
 create mode 100644 Symfony/Consumption/ContainerQueueConsumerRegistry.php
 rename Symfony/{Client => }/ContainerProcessorRegistry.php (88%)
 create mode 100644 Symfony/DependencyInjection/BuildConsumptionExtensionsPass.php
 create mode 100644 Symfony/DependencyInjection/BuildProcessorRegistryPass.php
 rename Tests/{Client => }/ArrayProcessorRegistryTest.php (92%)
 create mode 100644 Tests/Consumption/ArrayQueueConsumerRegistryTest.php
 create mode 100644 Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
 create mode 100644 Tests/Symfony/Consumption/ConsumeCommandTest.php
 delete mode 100644 Tests/Symfony/Consumption/ConsumeMessagesCommandTest.php
 delete mode 100644 Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php
 create mode 100644 Tests/Symfony/Consumption/ContainerQueueConsumerRegistryTest.php
 create mode 100644 Tests/Symfony/ContainerProcessorRegistryTest.php
 create mode 100644 Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
 create mode 100644 Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php

diff --git a/Client/ArrayProcessorRegistry.php b/ArrayProcessorRegistry.php
similarity index 79%
rename from Client/ArrayProcessorRegistry.php
rename to ArrayProcessorRegistry.php
index 5dede78..592908c 100644
--- a/Client/ArrayProcessorRegistry.php
+++ b/ArrayProcessorRegistry.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Enqueue\Client;
+namespace Enqueue;
 
 use Interop\Queue\Processor;
 
@@ -16,7 +16,10 @@ class ArrayProcessorRegistry implements ProcessorRegistryInterface
      */
     public function __construct(array $processors = [])
     {
-        $this->processors = $processors;
+        $this->processors = [];
+        array_walk($processors, function (Processor $processor, string $key) {
+            $this->processors[$key] = $processor;
+        });
     }
 
     public function add(string $name, Processor $processor): void
diff --git a/Client/DelegateProcessor.php b/Client/DelegateProcessor.php
index 705ff12..18985b4 100644
--- a/Client/DelegateProcessor.php
+++ b/Client/DelegateProcessor.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Client;
 
+use Enqueue\ProcessorRegistryInterface;
 use Interop\Queue\Context;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Processor;
diff --git a/Consumption/ArrayQueueConsumerRegistry.php b/Consumption/ArrayQueueConsumerRegistry.php
new file mode 100644
index 0000000..3b93d74
--- /dev/null
+++ b/Consumption/ArrayQueueConsumerRegistry.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Enqueue\Consumption;
+
+class ArrayQueueConsumerRegistry implements QueueConsumerRegistryInterface
+{
+    /**
+     * @var QueueConsumerInterface[]
+     */
+    private $consumers;
+
+    /**
+     * @param QueueConsumerInterface[] $queueConsumers
+     */
+    public function __construct(array $queueConsumers = [])
+    {
+        $this->consumers = [];
+        array_walk($queueConsumers, function (QueueConsumerInterface $consumer, string $key) {
+            $this->consumers[$key] = $consumer;
+        });
+    }
+
+    public function add(string $name, QueueConsumerInterface $consumer): void
+    {
+        $this->consumers[$name] = $consumer;
+    }
+
+    public function get(string $name): QueueConsumerInterface
+    {
+        if (false == isset($this->consumers[$name])) {
+            throw new \LogicException(sprintf('QueueConsumer was not found, name: "%s".', $name));
+        }
+
+        return $this->consumers[$name];
+    }
+}
diff --git a/Consumption/QueueConsumerRegistryInterface.php b/Consumption/QueueConsumerRegistryInterface.php
new file mode 100644
index 0000000..189004e
--- /dev/null
+++ b/Consumption/QueueConsumerRegistryInterface.php
@@ -0,0 +1,10 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Enqueue\Consumption;
+
+interface QueueConsumerRegistryInterface
+{
+    public function get(string $name): QueueConsumerInterface;
+}
diff --git a/Client/ProcessorRegistryInterface.php b/ProcessorRegistryInterface.php
similarity index 75%
rename from Client/ProcessorRegistryInterface.php
rename to ProcessorRegistryInterface.php
index 2639e5e..5306c30 100644
--- a/Client/ProcessorRegistryInterface.php
+++ b/ProcessorRegistryInterface.php
@@ -1,6 +1,8 @@
 <?php
 
-namespace Enqueue\Client;
+declare(strict_types=1);
+
+namespace Enqueue;
 
 use Interop\Queue\Processor;
 
diff --git a/Symfony/Consumption/ConfigurableConsumeCommand.php b/Symfony/Consumption/ConfigurableConsumeCommand.php
new file mode 100644
index 0000000..86a300f
--- /dev/null
+++ b/Symfony/Consumption/ConfigurableConsumeCommand.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace Enqueue\Symfony\Consumption;
+
+use Enqueue\Consumption\ChainExtension;
+use Enqueue\Consumption\Extension\LoggerExtension;
+use Enqueue\Consumption\QueueConsumerRegistryInterface;
+use Enqueue\Consumption\QueueSubscriberInterface;
+use Enqueue\ProcessorRegistryInterface;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Logger\ConsoleLogger;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ConfigurableConsumeCommand extends Command
+{
+    use LimitsExtensionsCommandTrait;
+    use QueueConsumerOptionsCommandTrait;
+
+    protected static $defaultName = 'enqueue:transport:consume';
+
+    /**
+     * @var QueueConsumerRegistryInterface
+     */
+    protected $consumerRegistry;
+
+    /**
+     * @var ProcessorRegistryInterface
+     */
+    private $processorRegistry;
+
+    public function __construct(QueueConsumerRegistryInterface $consumerRegistry, ProcessorRegistryInterface $processorRegistry)
+    {
+        parent::__construct(static::$defaultName);
+
+        $this->consumerRegistry = $consumerRegistry;
+        $this->processorRegistry = $processorRegistry;
+    }
+
+    protected function configure(): void
+    {
+        $this->configureLimitsExtensions();
+        $this->configureQueueConsumerOptions();
+
+        $this
+            ->setDescription('A worker that consumes message from a broker. '.
+                'To use this broker you have to explicitly set a queue to consume from '.
+                'and a message processor service')
+            ->addArgument('processor', InputArgument::REQUIRED, 'A message processor.')
+            ->addArgument('queues', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'A queue to consume from', [])
+            ->addOption('transport', 't', InputOption::VALUE_OPTIONAL, 'The transport to consume messages from.', 'default')
+        ;
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output): ?int
+    {
+        $consumer = $this->consumerRegistry->get($input->getOption('transport'));
+
+        $this->setQueueConsumerOptions($consumer, $input);
+
+        $processor = $this->processorRegistry->get($input->getArgument('processor'));
+
+        $queues = $input->getArgument('queues');
+        if (empty($queues) && $processor instanceof QueueSubscriberInterface) {
+            $queues = $processor::getSubscribedQueues();
+        }
+
+        if (empty($queues)) {
+            throw new \LogicException(sprintf(
+                'The queue is not provided. The processor must implement "%s" interface and it must return not empty array of queues or a queue set using as a second argument.',
+                QueueSubscriberInterface::class
+            ));
+        }
+
+        $extensions = $this->getLimitsExtensions($input, $output);
+        array_unshift($extensions, new LoggerExtension(new ConsoleLogger($output)));
+
+        $runtimeExtensions = new ChainExtension($extensions);
+
+        foreach ($queues as $queue) {
+            $consumer->bind($queue, $processor);
+        }
+
+        $consumer->consume($runtimeExtensions);
+
+        return null;
+    }
+}
diff --git a/Symfony/Consumption/ConsumeMessagesCommand.php b/Symfony/Consumption/ConsumeCommand.php
similarity index 59%
rename from Symfony/Consumption/ConsumeMessagesCommand.php
rename to Symfony/Consumption/ConsumeCommand.php
index 06d88a9..2b4470b 100644
--- a/Symfony/Consumption/ConsumeMessagesCommand.php
+++ b/Symfony/Consumption/ConsumeCommand.php
@@ -5,14 +5,16 @@
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Extension\LoggerExtension;
 use Enqueue\Consumption\QueueConsumerInterface;
+use Enqueue\Consumption\QueueConsumerRegistryInterface;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Logger\ConsoleLogger;
 use Symfony\Component\Console\Output\OutputInterface;
 use Symfony\Component\DependencyInjection\ContainerAwareInterface;
 use Symfony\Component\DependencyInjection\ContainerAwareTrait;
 
-class ConsumeMessagesCommand extends Command implements ContainerAwareInterface
+class ConsumeCommand extends Command implements ContainerAwareInterface
 {
     use ContainerAwareTrait;
     use LimitsExtensionsCommandTrait;
@@ -21,46 +23,48 @@ class ConsumeMessagesCommand extends Command implements ContainerAwareInterface
     protected static $defaultName = 'enqueue:transport:consume';
 
     /**
-     * @var QueueConsumerInterface
+     * @var QueueConsumerRegistryInterface
      */
-    protected $consumer;
+    protected $consumerRegistry;
 
     /**
-     * @param QueueConsumerInterface $consumer
+     * [name => QueueConsumerInterface].
+     *
+     * @param QueueConsumerInterface[]
      */
-    public function __construct(QueueConsumerInterface $consumer)
+    public function __construct(QueueConsumerRegistryInterface $consumerRegistry)
     {
         parent::__construct(static::$defaultName);
 
-        $this->consumer = $consumer;
+        $this->consumerRegistry = $consumerRegistry;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    protected function configure()
+    protected function configure(): void
     {
         $this->configureLimitsExtensions();
         $this->configureQueueConsumerOptions();
 
         $this
+            ->addOption('transport', 't', InputOption::VALUE_OPTIONAL, 'The transport to consume messages from.', 'default')
             ->setDescription('A worker that consumes message from a broker. '.
                 'To use this broker you have to configure queue consumer before adding to the command')
         ;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): ?int
     {
-        $this->setQueueConsumerOptions($this->consumer, $input);
+        // QueueConsumer must be pre configured outside of the command!
+        $consumer = $this->consumerRegistry->get($input->getOption('transport'));
+
+        $this->setQueueConsumerOptions($consumer, $input);
 
         $extensions = $this->getLimitsExtensions($input, $output);
         array_unshift($extensions, new LoggerExtension(new ConsoleLogger($output)));
 
         $runtimeExtensions = new ChainExtension($extensions);
 
-        $this->consumer->consume($runtimeExtensions);
+        $consumer->consume($runtimeExtensions);
+
+        return null;
     }
 }
diff --git a/Symfony/Consumption/ContainerAwareConsumeMessagesCommand.php b/Symfony/Consumption/ContainerAwareConsumeMessagesCommand.php
deleted file mode 100644
index e7607f6..0000000
--- a/Symfony/Consumption/ContainerAwareConsumeMessagesCommand.php
+++ /dev/null
@@ -1,101 +0,0 @@
-<?php
-
-namespace Enqueue\Symfony\Consumption;
-
-use Enqueue\Consumption\ChainExtension;
-use Enqueue\Consumption\Extension\LoggerExtension;
-use Enqueue\Consumption\QueueConsumerInterface;
-use Enqueue\Consumption\QueueSubscriberInterface;
-use Interop\Queue\Processor;
-use Symfony\Component\Console\Command\Command;
-use Symfony\Component\Console\Input\InputArgument;
-use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Input\InputOption;
-use Symfony\Component\Console\Logger\ConsoleLogger;
-use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\DependencyInjection\ContainerAwareInterface;
-use Symfony\Component\DependencyInjection\ContainerAwareTrait;
-
-class ContainerAwareConsumeMessagesCommand extends Command implements ContainerAwareInterface
-{
-    use ContainerAwareTrait;
-    use LimitsExtensionsCommandTrait;
-    use QueueConsumerOptionsCommandTrait;
-
-    protected static $defaultName = 'enqueue:transport:consume';
-
-    /**
-     * @var QueueConsumerInterface
-     */
-    protected $consumer;
-
-    /**
-     * ConsumeMessagesCommand constructor.
-     *
-     * @param QueueConsumerInterface $consumer
-     */
-    public function __construct(QueueConsumerInterface $consumer)
-    {
-        parent::__construct(static::$defaultName);
-
-        $this->consumer = $consumer;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function configure()
-    {
-        $this->configureLimitsExtensions();
-        $this->configureQueueConsumerOptions();
-
-        $this
-            ->setDescription('A worker that consumes message from a broker. '.
-                'To use this broker you have to explicitly set a queue to consume from '.
-                'and a message processor service')
-            ->addArgument('processor-service', InputArgument::REQUIRED, 'A message processor service')
-            ->addOption('queue', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Queues to consume from', [])
-        ;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function execute(InputInterface $input, OutputInterface $output)
-    {
-        $this->setQueueConsumerOptions($this->consumer, $input);
-
-        /** @var Processor $processor */
-        $processor = $this->container->get($input->getArgument('processor-service'));
-        if (false == $processor instanceof  Processor) {
-            throw new \LogicException(sprintf(
-                'Invalid message processor service given. It must be an instance of %s but %s',
-                Processor::class,
-                get_class($processor)
-            ));
-        }
-
-        $queues = $input->getOption('queue');
-        if (empty($queues) && $processor instanceof QueueSubscriberInterface) {
-            $queues = $processor::getSubscribedQueues();
-        }
-
-        if (empty($queues)) {
-            throw new \LogicException(sprintf(
-                'The queues are not provided. The processor must implement "%s" interface and it must return not empty array of queues or queues set using --queue option.',
-                QueueSubscriberInterface::class
-            ));
-        }
-
-        $extensions = $this->getLimitsExtensions($input, $output);
-        array_unshift($extensions, new LoggerExtension(new ConsoleLogger($output)));
-
-        $runtimeExtensions = new ChainExtension($extensions);
-
-        foreach ($queues as $queue) {
-            $this->consumer->bind($queue, $processor);
-        }
-
-        $this->consumer->consume($runtimeExtensions);
-    }
-}
diff --git a/Symfony/Consumption/ContainerQueueConsumerRegistry.php b/Symfony/Consumption/ContainerQueueConsumerRegistry.php
new file mode 100644
index 0000000..2163b21
--- /dev/null
+++ b/Symfony/Consumption/ContainerQueueConsumerRegistry.php
@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Enqueue\Symfony\Consumption;
+
+use Enqueue\Consumption\QueueConsumerInterface;
+use Enqueue\Consumption\QueueConsumerRegistryInterface;
+use Psr\Container\ContainerInterface;
+
+final class ContainerQueueConsumerRegistry implements QueueConsumerRegistryInterface
+{
+    /**
+     * @var ContainerInterface
+     */
+    private $locator;
+
+    public function __construct(ContainerInterface $locator)
+    {
+        $this->locator = $locator;
+    }
+
+    public function get(string $name): QueueConsumerInterface
+    {
+        if (false == $this->locator->has($name)) {
+            throw new \LogicException(sprintf('Service locator does not have a queue consumer with name "%s".', $name));
+        }
+
+        return $this->locator->get($name);
+    }
+}
diff --git a/Symfony/Client/ContainerProcessorRegistry.php b/Symfony/ContainerProcessorRegistry.php
similarity index 88%
rename from Symfony/Client/ContainerProcessorRegistry.php
rename to Symfony/ContainerProcessorRegistry.php
index 4031e42..b259d23 100644
--- a/Symfony/Client/ContainerProcessorRegistry.php
+++ b/Symfony/ContainerProcessorRegistry.php
@@ -1,8 +1,8 @@
 <?php
 
-namespace Enqueue\Symfony\Client;
+namespace Enqueue\Symfony;
 
-use Enqueue\Client\ProcessorRegistryInterface;
+use Enqueue\ProcessorRegistryInterface;
 use Interop\Queue\Processor;
 use Psr\Container\ContainerInterface;
 
diff --git a/Symfony/DependencyInjection/BuildConsumptionExtensionsPass.php b/Symfony/DependencyInjection/BuildConsumptionExtensionsPass.php
new file mode 100644
index 0000000..f9a6a47
--- /dev/null
+++ b/Symfony/DependencyInjection/BuildConsumptionExtensionsPass.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace Enqueue\Symfony\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+final class BuildConsumptionExtensionsPass implements CompilerPassInterface
+{
+    /**
+     * @var string
+     */
+    private $name;
+
+    public function __construct(string $transportName)
+    {
+        if (empty($transportName)) {
+            throw new \InvalidArgumentException('The name could not be empty.');
+        }
+
+        $this->name = $transportName;
+    }
+
+    public function process(ContainerBuilder $container): void
+    {
+        $extensionsId = sprintf('enqueue.transport.%s.consumption_extensions', $this->name);
+        if (false == $container->hasDefinition($extensionsId)) {
+            return;
+        }
+
+        $tags = $container->findTaggedServiceIds('enqueue.transport.consumption_extension');
+
+        $groupByPriority = [];
+        foreach ($tags as $serviceId => $tagAttributes) {
+            foreach ($tagAttributes as $tagAttribute) {
+                $transport = $tagAttribute['transport'] ?? 'default';
+
+                if ($transport !== $this->name && 'all' !== $transport) {
+                    continue;
+                }
+
+                $priority = (int) ($tagAttribute['priority'] ?? 0);
+
+                $groupByPriority[$priority][] = new Reference($serviceId);
+            }
+        }
+
+        krsort($groupByPriority, SORT_NUMERIC);
+
+        $flatExtensions = [];
+        foreach ($groupByPriority as $extension) {
+            $flatExtensions = array_merge($flatExtensions, $extension);
+        }
+
+        $extensionsService = $container->getDefinition($extensionsId);
+        $extensionsService->replaceArgument(0, array_merge(
+            $extensionsService->getArgument(0),
+            $flatExtensions
+        ));
+    }
+}
diff --git a/Symfony/DependencyInjection/BuildProcessorRegistryPass.php b/Symfony/DependencyInjection/BuildProcessorRegistryPass.php
new file mode 100644
index 0000000..8493dc7
--- /dev/null
+++ b/Symfony/DependencyInjection/BuildProcessorRegistryPass.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace Enqueue\Symfony\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+final class BuildProcessorRegistryPass implements CompilerPassInterface
+{
+    /**
+     * @var string
+     */
+    private $name;
+
+    public function __construct(string $transportName)
+    {
+        if (empty($transportName)) {
+            throw new \InvalidArgumentException('The name could not be empty.');
+        }
+
+        $this->name = $transportName;
+    }
+
+    public function process(ContainerBuilder $container): void
+    {
+        $processorRegistryId = sprintf('enqueue.transport.%s.processor_registry', $this->name);
+        if (false == $container->hasDefinition($processorRegistryId)) {
+            return;
+        }
+
+        $tag = 'enqueue.transport.processor';
+        $map = [];
+        foreach ($container->findTaggedServiceIds($tag) as $serviceId => $tagAttributes) {
+            foreach ($tagAttributes as $tagAttribute) {
+                $transport = $tagAttribute['transport'] ?? 'default';
+
+                if ($transport !== $this->name && 'all' !== $transport) {
+                    continue;
+                }
+
+                $processor = $tagAttribute['processor'] ?? $serviceId;
+
+                $map[$processor] = new Reference($serviceId);
+            }
+        }
+
+        $registry = $container->getDefinition($processorRegistryId);
+        $registry->setArgument(0, ServiceLocatorTagPass::register($container, $map, $processorRegistryId));
+    }
+}
diff --git a/Tests/Client/ArrayProcessorRegistryTest.php b/Tests/ArrayProcessorRegistryTest.php
similarity index 92%
rename from Tests/Client/ArrayProcessorRegistryTest.php
rename to Tests/ArrayProcessorRegistryTest.php
index f49ec8f..50c8025 100644
--- a/Tests/Client/ArrayProcessorRegistryTest.php
+++ b/Tests/ArrayProcessorRegistryTest.php
@@ -1,9 +1,9 @@
 <?php
 
-namespace Enqueue\Tests\Client;
+namespace Enqueue\Tests;
 
-use Enqueue\Client\ArrayProcessorRegistry;
-use Enqueue\Client\ProcessorRegistryInterface;
+use Enqueue\ArrayProcessorRegistry;
+use Enqueue\ProcessorRegistryInterface;
 use Enqueue\Test\ClassExtensionTrait;
 use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
diff --git a/Tests/Client/DelegateProcessorTest.php b/Tests/Client/DelegateProcessorTest.php
index 29b1031..5ed49d8 100644
--- a/Tests/Client/DelegateProcessorTest.php
+++ b/Tests/Client/DelegateProcessorTest.php
@@ -4,8 +4,8 @@
 
 use Enqueue\Client\Config;
 use Enqueue\Client\DelegateProcessor;
-use Enqueue\Client\ProcessorRegistryInterface;
 use Enqueue\Null\NullMessage;
+use Enqueue\ProcessorRegistryInterface;
 use Interop\Queue\Context;
 use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
@@ -19,10 +19,8 @@ public function testCouldBeConstructedWithRequiredArguments()
 
     public function testShouldThrowExceptionIfProcessorNameIsNotSet()
     {
-        $this->setExpectedException(
-            \LogicException::class,
-            'Got message without required parameter: "enqueue.processor"'
-        );
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Got message without required parameter: "enqueue.processor"');
 
         $processor = new DelegateProcessor($this->createProcessorRegistryMock());
         $processor->process(new NullMessage(), $this->createContextMock());
diff --git a/Tests/Consumption/ArrayQueueConsumerRegistryTest.php b/Tests/Consumption/ArrayQueueConsumerRegistryTest.php
new file mode 100644
index 0000000..6cc5270
--- /dev/null
+++ b/Tests/Consumption/ArrayQueueConsumerRegistryTest.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace Enqueue\Tests\Consumption;
+
+use Enqueue\Consumption\ArrayQueueConsumerRegistry;
+use Enqueue\Consumption\QueueConsumerInterface;
+use Enqueue\Consumption\QueueConsumerRegistryInterface;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+
+class ArrayQueueConsumerRegistryTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementQueueConsumerRegistryInterface()
+    {
+        $this->assertClassImplements(QueueConsumerRegistryInterface::class, ArrayQueueConsumerRegistry::class);
+    }
+
+    public function testCouldBeConstructedWithoutAnyArgument()
+    {
+        new ArrayQueueConsumerRegistry();
+    }
+
+    public function testShouldThrowExceptionIfQueueConsumerIsNotSet()
+    {
+        $registry = new ArrayQueueConsumerRegistry();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('QueueConsumer was not found, name: "queueConsumer-name".');
+        $registry->get('queueConsumer-name');
+    }
+
+    public function testShouldAllowGetQueueConsumerAddedViaConstructor()
+    {
+        $queueConsumer = $this->createQueueConsumerMock();
+
+        $registry = new ArrayQueueConsumerRegistry(['aFooName' => $queueConsumer]);
+
+        $this->assertSame($queueConsumer, $registry->get('aFooName'));
+    }
+
+    public function testShouldAllowGetQueueConsumerAddedViaAddMethod()
+    {
+        $queueConsumer = $this->createQueueConsumerMock();
+
+        $registry = new ArrayQueueConsumerRegistry();
+        $registry->add('aFooName', $queueConsumer);
+
+        $this->assertSame($queueConsumer, $registry->get('aFooName'));
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected function createQueueConsumerMock(): QueueConsumerInterface
+    {
+        return $this->createMock(QueueConsumerInterface::class);
+    }
+}
diff --git a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
new file mode 100644
index 0000000..b41b6b8
--- /dev/null
+++ b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
@@ -0,0 +1,288 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\Consumption;
+
+use Enqueue\ArrayProcessorRegistry;
+use Enqueue\Consumption\ArrayQueueConsumerRegistry;
+use Enqueue\Consumption\ChainExtension;
+use Enqueue\Consumption\QueueConsumerInterface;
+use Enqueue\Consumption\QueueConsumerRegistryInterface;
+use Enqueue\Consumption\QueueSubscriberInterface;
+use Enqueue\ProcessorRegistryInterface;
+use Enqueue\Symfony\Consumption\ConfigurableConsumeCommand;
+use Interop\Queue\Context;
+use Interop\Queue\Message as InteropMessage;
+use Interop\Queue\Processor;
+use Interop\Queue\Queue as InteropQueue;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Tester\CommandTester;
+
+class ConfigurableConsumeCommandTest extends TestCase
+{
+    public function testCouldBeConstructedWithRequiredAttributes()
+    {
+        new ConfigurableConsumeCommand(
+            $this->createMock(QueueConsumerRegistryInterface::class),
+            $this->createMock(ProcessorRegistryInterface::class)
+        );
+    }
+
+    public function testShouldHaveCommandName()
+    {
+        $command = new ConfigurableConsumeCommand(
+            $this->createMock(QueueConsumerRegistryInterface::class),
+            $this->createMock(ProcessorRegistryInterface::class)
+        );
+
+        $this->assertEquals('enqueue:transport:consume', $command->getName());
+    }
+
+    public function testShouldHaveExpectedOptions()
+    {
+        $command = new ConfigurableConsumeCommand(
+            $this->createMock(QueueConsumerRegistryInterface::class),
+            $this->createMock(ProcessorRegistryInterface::class)
+        );
+
+        $options = $command->getDefinition()->getOptions();
+
+        $this->assertCount(7, $options);
+        $this->assertArrayHasKey('memory-limit', $options);
+        $this->assertArrayHasKey('message-limit', $options);
+        $this->assertArrayHasKey('time-limit', $options);
+        $this->assertArrayHasKey('idle-timeout', $options);
+        $this->assertArrayHasKey('receive-timeout', $options);
+        $this->assertArrayHasKey('niceness', $options);
+        $this->assertArrayHasKey('transport', $options);
+    }
+
+    public function testShouldHaveExpectedAttributes()
+    {
+        $command = new ConfigurableConsumeCommand(
+            $this->createMock(QueueConsumerRegistryInterface::class),
+            $this->createMock(ProcessorRegistryInterface::class)
+        );
+
+        $arguments = $command->getDefinition()->getArguments();
+
+        $this->assertCount(2, $arguments);
+        $this->assertArrayHasKey('processor', $arguments);
+        $this->assertArrayHasKey('queues', $arguments);
+    }
+
+    public function testThrowIfNeitherQueueOptionNorProcessorImplementsQueueSubscriberInterface()
+    {
+        $processor = $this->createProcessor();
+
+        $consumer = $this->createQueueConsumerMock();
+        $consumer
+            ->expects($this->never())
+            ->method('bind')
+        ;
+        $consumer
+            ->expects($this->never())
+            ->method('consume')
+        ;
+
+        $registry = new ArrayProcessorRegistry(['aProcessor' => $processor]);
+
+        $command = new ConfigurableConsumeCommand(
+            $this->createMock(QueueConsumerRegistryInterface::class),
+            $registry
+        );
+
+        $tester = new CommandTester($command);
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The queue is not provided. The processor must implement "Enqueue\Consumption\QueueSubscriberInterface" interface and it must return not empty array of queues or a queue set using as a second argument.');
+        $tester->execute([
+            'processor' => 'aProcessor',
+        ]);
+    }
+
+    public function testShouldExecuteConsumptionWithExplicitlySetQueue()
+    {
+        $processor = $this->createProcessor();
+
+        $consumer = $this->createQueueConsumerMock();
+        $consumer
+            ->expects($this->once())
+            ->method('bind')
+            ->with('queue-name', $this->identicalTo($processor))
+        ;
+        $consumer
+            ->expects($this->once())
+            ->method('consume')
+            ->with($this->isInstanceOf(ChainExtension::class))
+        ;
+
+        $processorRegistry = new ArrayProcessorRegistry(['processor-service' => $processor]);
+        $consumerRegistry = new ArrayQueueConsumerRegistry(['default' => $consumer]);
+
+        $command = new ConfigurableConsumeCommand(
+            $consumerRegistry,
+            $processorRegistry
+        );
+
+        $tester = new CommandTester($command);
+        $tester->execute([
+            'processor' => 'processor-service',
+            'queues' => ['queue-name'],
+        ]);
+    }
+
+    public function testShouldExecuteConsumptionWithSeveralCustomQueues()
+    {
+        $processor = $this->createProcessor();
+
+        $consumer = $this->createQueueConsumerMock();
+        $consumer
+            ->expects($this->at(0))
+            ->method('bind')
+            ->with('queue-name', $this->identicalTo($processor))
+        ;
+        $consumer
+            ->expects($this->at(1))
+            ->method('bind')
+            ->with('another-queue-name', $this->identicalTo($processor))
+        ;
+        $consumer
+            ->expects($this->at(2))
+            ->method('consume')
+            ->with($this->isInstanceOf(ChainExtension::class))
+        ;
+
+        $processorRegistry = new ArrayProcessorRegistry(['processor-service' => $processor]);
+        $consumerRegistry = new ArrayQueueConsumerRegistry(['default' => $consumer]);
+
+        $command = new ConfigurableConsumeCommand(
+            $consumerRegistry,
+            $processorRegistry
+        );
+
+        $tester = new CommandTester($command);
+        $tester->execute([
+            'processor' => 'processor-service',
+            'queues' => ['queue-name', 'another-queue-name'],
+        ]);
+    }
+
+    public function testShouldExecuteConsumptionWhenProcessorImplementsQueueSubscriberInterface()
+    {
+        $processor = new class() implements Processor, QueueSubscriberInterface {
+            public function process(InteropMessage $message, Context $context)
+            {
+            }
+
+            public static function getSubscribedQueues()
+            {
+                return ['fooSubscribedQueues', 'barSubscribedQueues'];
+            }
+        };
+
+        $consumer = $this->createQueueConsumerMock();
+        $consumer
+            ->expects($this->at(0))
+            ->method('bind')
+            ->with('fooSubscribedQueues', $this->identicalTo($processor))
+        ;
+        $consumer
+            ->expects($this->at(1))
+            ->method('bind')
+            ->with('barSubscribedQueues', $this->identicalTo($processor))
+        ;
+        $consumer
+            ->expects($this->at(2))
+            ->method('consume')
+            ->with($this->isInstanceOf(ChainExtension::class))
+        ;
+
+        $processorRegistry = new ArrayProcessorRegistry(['processor-service' => $processor]);
+        $consumerRegistry = new ArrayQueueConsumerRegistry(['default' => $consumer]);
+
+        $command = new ConfigurableConsumeCommand(
+            $consumerRegistry,
+            $processorRegistry
+        );
+
+        $tester = new CommandTester($command);
+        $tester->execute([
+            'processor' => 'processor-service',
+        ]);
+    }
+
+    public function testShouldExecuteConsumptionWithCustomTransportExplicitlySetQueue()
+    {
+        $processor = $this->createProcessor();
+
+        $fooConsumer = $this->createQueueConsumerMock();
+        $fooConsumer
+            ->expects($this->never())
+            ->method('bind')
+        ;
+        $fooConsumer
+            ->expects($this->never())
+            ->method('consume')
+            ->with($this->isInstanceOf(ChainExtension::class))
+        ;
+
+        $barConsumer = $this->createQueueConsumerMock();
+        $barConsumer
+            ->expects($this->once())
+            ->method('bind')
+            ->with('queue-name', $this->identicalTo($processor))
+        ;
+        $barConsumer
+            ->expects($this->once())
+            ->method('consume')
+            ->with($this->isInstanceOf(ChainExtension::class))
+        ;
+
+        $processorRegistry = new ArrayProcessorRegistry(['processor-service' => $processor]);
+        $consumerRegistry = new ArrayQueueConsumerRegistry(['foo' => $fooConsumer, 'bar' => $barConsumer]);
+
+        $command = new ConfigurableConsumeCommand(
+            $consumerRegistry,
+            $processorRegistry
+        );
+
+        $tester = new CommandTester($command);
+        $tester->execute([
+            'processor' => 'processor-service',
+            'queues' => ['queue-name'],
+            '--transport' => 'bar',
+        ]);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|Context
+     */
+    protected function createContextMock()
+    {
+        return $this->createMock(Context::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|InteropQueue
+     */
+    protected function createQueueMock()
+    {
+        return $this->createMock(InteropQueue::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|Processor
+     */
+    protected function createProcessor()
+    {
+        return $this->createMock(Processor::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumerInterface
+     */
+    protected function createQueueConsumerMock()
+    {
+        return $this->createMock(QueueConsumerInterface::class);
+    }
+}
diff --git a/Tests/Symfony/Consumption/ConsumeCommandTest.php b/Tests/Symfony/Consumption/ConsumeCommandTest.php
new file mode 100644
index 0000000..e08dbaa
--- /dev/null
+++ b/Tests/Symfony/Consumption/ConsumeCommandTest.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\Consumption;
+
+use Enqueue\Consumption\ArrayQueueConsumerRegistry;
+use Enqueue\Consumption\ChainExtension;
+use Enqueue\Consumption\QueueConsumerInterface;
+use Enqueue\Consumption\QueueConsumerRegistryInterface;
+use Enqueue\Symfony\Consumption\ConsumeCommand;
+use Interop\Queue\Context;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Tester\CommandTester;
+
+class ConsumeCommandTest extends TestCase
+{
+    public function testCouldBeConstructedWithRequiredAttributes()
+    {
+        new ConsumeCommand($this->createMock(QueueConsumerRegistryInterface::class));
+    }
+
+    public function testShouldHaveCommandName()
+    {
+        $command = new ConsumeCommand($this->createMock(QueueConsumerRegistryInterface::class));
+
+        $this->assertEquals('enqueue:transport:consume', $command->getName());
+    }
+
+    public function testShouldHaveExpectedOptions()
+    {
+        $command = new ConsumeCommand($this->createMock(QueueConsumerRegistryInterface::class));
+
+        $options = $command->getDefinition()->getOptions();
+
+        $this->assertCount(7, $options);
+        $this->assertArrayHasKey('memory-limit', $options);
+        $this->assertArrayHasKey('message-limit', $options);
+        $this->assertArrayHasKey('time-limit', $options);
+        $this->assertArrayHasKey('idle-timeout', $options);
+        $this->assertArrayHasKey('receive-timeout', $options);
+        $this->assertArrayHasKey('niceness', $options);
+        $this->assertArrayHasKey('transport', $options);
+    }
+
+    public function testShouldHaveExpectedAttributes()
+    {
+        $command = new ConsumeCommand($this->createMock(QueueConsumerRegistryInterface::class));
+
+        $arguments = $command->getDefinition()->getArguments();
+
+        $this->assertCount(0, $arguments);
+    }
+
+    public function testShouldExecuteDefaultConsumption()
+    {
+        $consumer = $this->createQueueConsumerMock();
+        $consumer
+            ->expects($this->once())
+            ->method('consume')
+            ->with($this->isInstanceOf(ChainExtension::class))
+        ;
+
+        $command = new ConsumeCommand(new ArrayQueueConsumerRegistry(['default' => $consumer]));
+
+        $tester = new CommandTester($command);
+        $tester->execute([]);
+    }
+
+    public function testShouldExecuteCustomConsumption()
+    {
+        $defaultConsumer = $this->createQueueConsumerMock();
+        $defaultConsumer
+            ->expects($this->never())
+            ->method('consume')
+        ;
+
+        $customConsumer = $this->createQueueConsumerMock();
+        $customConsumer
+            ->expects($this->once())
+            ->method('consume')
+            ->with($this->isInstanceOf(ChainExtension::class))
+        ;
+
+        $command = new ConsumeCommand(new ArrayQueueConsumerRegistry([
+            'default' => $defaultConsumer,
+            'custom' => $customConsumer,
+        ]));
+
+        $tester = new CommandTester($command);
+        $tester->execute(['--transport' => 'custom']);
+    }
+
+    public function testThrowIfNotDefinedTransportRequested()
+    {
+        $defaultConsumer = $this->createQueueConsumerMock();
+        $defaultConsumer
+            ->expects($this->never())
+            ->method('consume')
+        ;
+
+        $command = new ConsumeCommand(new ArrayQueueConsumerRegistry([
+            'default' => $defaultConsumer,
+        ]));
+
+        $tester = new CommandTester($command);
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('QueueConsumer was not found, name: "not-defined".');
+        $tester->execute(['--transport' => 'not-defined']);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|Context
+     */
+    private function createContextMock()
+    {
+        return $this->createMock(Context::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumerInterface
+     */
+    private function createQueueConsumerMock()
+    {
+        return $this->createMock(QueueConsumerInterface::class);
+    }
+}
diff --git a/Tests/Symfony/Consumption/ConsumeMessagesCommandTest.php b/Tests/Symfony/Consumption/ConsumeMessagesCommandTest.php
deleted file mode 100644
index 2e47a05..0000000
--- a/Tests/Symfony/Consumption/ConsumeMessagesCommandTest.php
+++ /dev/null
@@ -1,86 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Symfony\Consumption;
-
-use Enqueue\Consumption\ChainExtension;
-use Enqueue\Consumption\QueueConsumerInterface;
-use Enqueue\Symfony\Consumption\ConsumeMessagesCommand;
-use Interop\Queue\Context;
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\Console\Tester\CommandTester;
-
-class ConsumeMessagesCommandTest extends TestCase
-{
-    public function testCouldBeConstructedWithRequiredAttributes()
-    {
-        new ConsumeMessagesCommand($this->createQueueConsumerMock());
-    }
-
-    public function testShouldHaveCommandName()
-    {
-        $command = new ConsumeMessagesCommand($this->createQueueConsumerMock());
-
-        $this->assertEquals('enqueue:transport:consume', $command->getName());
-    }
-
-    public function testShouldHaveExpectedOptions()
-    {
-        $command = new ConsumeMessagesCommand($this->createQueueConsumerMock());
-
-        $options = $command->getDefinition()->getOptions();
-
-        $this->assertCount(6, $options);
-        $this->assertArrayHasKey('memory-limit', $options);
-        $this->assertArrayHasKey('message-limit', $options);
-        $this->assertArrayHasKey('time-limit', $options);
-        $this->assertArrayHasKey('idle-timeout', $options);
-        $this->assertArrayHasKey('receive-timeout', $options);
-        $this->assertArrayHasKey('niceness', $options);
-    }
-
-    public function testShouldHaveExpectedAttributes()
-    {
-        $command = new ConsumeMessagesCommand($this->createQueueConsumerMock());
-
-        $arguments = $command->getDefinition()->getArguments();
-
-        $this->assertCount(0, $arguments);
-    }
-
-    public function testShouldExecuteConsumption()
-    {
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->never())
-            ->method('close')
-        ;
-
-        $consumer = $this->createQueueConsumerMock();
-        $consumer
-            ->expects($this->once())
-            ->method('consume')
-            ->with($this->isInstanceOf(ChainExtension::class))
-        ;
-
-        $command = new ConsumeMessagesCommand($consumer);
-
-        $tester = new CommandTester($command);
-        $tester->execute([]);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Context
-     */
-    private function createContextMock()
-    {
-        return $this->createMock(Context::class);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumerInterface
-     */
-    private function createQueueConsumerMock()
-    {
-        return $this->createMock(QueueConsumerInterface::class);
-    }
-}
diff --git a/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php b/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php
deleted file mode 100644
index 2c04b07..0000000
--- a/Tests/Symfony/Consumption/ContainerAwareConsumeMessagesCommandTest.php
+++ /dev/null
@@ -1,208 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Symfony\Consumption;
-
-use Enqueue\Consumption\ChainExtension;
-use Enqueue\Consumption\QueueConsumerInterface;
-use Enqueue\Symfony\Consumption\ContainerAwareConsumeMessagesCommand;
-use Enqueue\Tests\Symfony\Consumption\Mock\QueueSubscriberProcessor;
-use Interop\Queue\Context;
-use Interop\Queue\Processor;
-use Interop\Queue\Queue as InteropQueue;
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\Console\Tester\CommandTester;
-use Symfony\Component\DependencyInjection\Container;
-
-class ContainerAwareConsumeMessagesCommandTest extends TestCase
-{
-    public function testCouldBeConstructedWithRequiredAttributes()
-    {
-        new ContainerAwareConsumeMessagesCommand($this->createQueueConsumerMock());
-    }
-
-    public function testShouldHaveCommandName()
-    {
-        $command = new ContainerAwareConsumeMessagesCommand($this->createQueueConsumerMock());
-
-        $this->assertEquals('enqueue:transport:consume', $command->getName());
-    }
-
-    public function testShouldHaveExpectedOptions()
-    {
-        $command = new ContainerAwareConsumeMessagesCommand($this->createQueueConsumerMock());
-
-        $options = $command->getDefinition()->getOptions();
-
-        $this->assertCount(7, $options);
-        $this->assertArrayHasKey('memory-limit', $options);
-        $this->assertArrayHasKey('message-limit', $options);
-        $this->assertArrayHasKey('time-limit', $options);
-        $this->assertArrayHasKey('queue', $options);
-        $this->assertArrayHasKey('idle-timeout', $options);
-        $this->assertArrayHasKey('receive-timeout', $options);
-        $this->assertArrayHasKey('niceness', $options);
-    }
-
-    public function testShouldHaveExpectedAttributes()
-    {
-        $command = new ContainerAwareConsumeMessagesCommand($this->createQueueConsumerMock());
-
-        $arguments = $command->getDefinition()->getArguments();
-
-        $this->assertCount(1, $arguments);
-        $this->assertArrayHasKey('processor-service', $arguments);
-    }
-
-    public function testShouldThrowExceptionIfProcessorInstanceHasWrongClass()
-    {
-        $container = new Container();
-        $container->set('processor-service', new \stdClass());
-
-        $command = new ContainerAwareConsumeMessagesCommand($this->createQueueConsumerMock());
-        $command->setContainer($container);
-
-        $tester = new CommandTester($command);
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Invalid message processor service given. It must be an instance of Interop\Queue\Processor but stdClass');
-        $tester->execute([
-            'processor-service' => 'processor-service',
-            '--queue' => ['queue-name'],
-        ]);
-    }
-
-    public function testThrowIfNeitherQueueOptionNorProcessorImplementsQueueSubscriberInterface()
-    {
-        $processor = $this->createProcessor();
-
-        $consumer = $this->createQueueConsumerMock();
-        $consumer
-            ->expects($this->never())
-            ->method('bind')
-        ;
-        $consumer
-            ->expects($this->never())
-            ->method('consume')
-        ;
-
-        $container = new Container();
-        $container->set('processor-service', $processor);
-
-        $command = new ContainerAwareConsumeMessagesCommand($consumer);
-        $command->setContainer($container);
-
-        $tester = new CommandTester($command);
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('The queues are not provided. The processor must implement "Enqueue\Consumption\QueueSubscriberInterface" interface and it must return not empty array of queues or queues set using --queue option.');
-        $tester->execute([
-            'processor-service' => 'processor-service',
-        ]);
-    }
-
-    public function testShouldExecuteConsumptionWithExplicitlySetQueueViaQueueOption()
-    {
-        $processor = $this->createProcessor();
-
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->never())
-            ->method('close')
-        ;
-
-        $consumer = $this->createQueueConsumerMock();
-        $consumer
-            ->expects($this->once())
-            ->method('bind')
-            ->with('queue-name', $this->identicalTo($processor))
-        ;
-        $consumer
-            ->expects($this->once())
-            ->method('consume')
-            ->with($this->isInstanceOf(ChainExtension::class))
-        ;
-
-        $container = new Container();
-        $container->set('processor-service', $processor);
-
-        $command = new ContainerAwareConsumeMessagesCommand($consumer);
-        $command->setContainer($container);
-
-        $tester = new CommandTester($command);
-        $tester->execute([
-            'processor-service' => 'processor-service',
-            '--queue' => ['queue-name'],
-        ]);
-    }
-
-    public function testShouldExecuteConsumptionWhenProcessorImplementsQueueSubscriberInterface()
-    {
-        $processor = new QueueSubscriberProcessor();
-
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->never())
-            ->method('close')
-        ;
-
-        $consumer = $this->createQueueConsumerMock();
-        $consumer
-            ->expects($this->at(0))
-            ->method('bind')
-            ->with('fooSubscribedQueues', $this->identicalTo($processor))
-        ;
-        $consumer
-            ->expects($this->at(1))
-            ->method('bind')
-            ->with('barSubscribedQueues', $this->identicalTo($processor))
-        ;
-        $consumer
-            ->expects($this->at(2))
-            ->method('consume')
-            ->with($this->isInstanceOf(ChainExtension::class))
-        ;
-
-        $container = new Container();
-        $container->set('processor-service', $processor);
-
-        $command = new ContainerAwareConsumeMessagesCommand($consumer);
-        $command->setContainer($container);
-
-        $tester = new CommandTester($command);
-        $tester->execute([
-            'processor-service' => 'processor-service',
-        ]);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Context
-     */
-    protected function createContextMock()
-    {
-        return $this->createMock(Context::class);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|InteropQueue
-     */
-    protected function createQueueMock()
-    {
-        return $this->createMock(InteropQueue::class);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Processor
-     */
-    protected function createProcessor()
-    {
-        return $this->createMock(Processor::class);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumerInterface
-     */
-    protected function createQueueConsumerMock()
-    {
-        return $this->createMock(QueueConsumerInterface::class);
-    }
-}
diff --git a/Tests/Symfony/Consumption/ContainerQueueConsumerRegistryTest.php b/Tests/Symfony/Consumption/ContainerQueueConsumerRegistryTest.php
new file mode 100644
index 0000000..e0dcdc7
--- /dev/null
+++ b/Tests/Symfony/Consumption/ContainerQueueConsumerRegistryTest.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\Consumption;
+
+use Enqueue\Consumption\QueueConsumerInterface;
+use Enqueue\Consumption\QueueConsumerRegistryInterface;
+use Enqueue\Symfony\Consumption\ContainerQueueConsumerRegistry;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+use Psr\Container\ContainerInterface;
+
+class ContainerQueueConsumerRegistryTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementQueueConsumerRegistryInterface()
+    {
+        $this->assertClassImplements(QueueConsumerRegistryInterface::class, ContainerQueueConsumerRegistry::class);
+    }
+
+    public function testShouldBeFinal()
+    {
+        $this->assertClassFinal(ContainerQueueConsumerRegistry::class);
+    }
+
+    public function testCouldBeConstructedWithContainerAsFirstArgument()
+    {
+        new ContainerQueueConsumerRegistry($this->createContainerMock());
+    }
+
+    public function testShouldAllowGetQueueConsumer()
+    {
+        $queueConsumerMock = $this->createQueueConsumerMock();
+
+        $containerMock = $this->createContainerMock();
+        $containerMock
+            ->expects($this->once())
+            ->method('has')
+            ->with('queueConsumer-name')
+            ->willReturn(true)
+        ;
+        $containerMock
+            ->expects($this->once())
+            ->method('get')
+            ->with('queueConsumer-name')
+            ->willReturn($queueConsumerMock)
+        ;
+
+        $registry = new ContainerQueueConsumerRegistry($containerMock);
+        $this->assertSame($queueConsumerMock, $registry->get('queueConsumer-name'));
+    }
+
+    public function testThrowErrorIfServiceDoesNotImplementQueueConsumerReturnType()
+    {
+        $containerMock = $this->createContainerMock();
+        $containerMock
+            ->expects($this->once())
+            ->method('has')
+            ->with('queueConsumer-name')
+            ->willReturn(true)
+        ;
+        $containerMock
+            ->expects($this->once())
+            ->method('get')
+            ->with('queueConsumer-name')
+            ->willReturn(new \stdClass())
+        ;
+
+        $registry = new ContainerQueueConsumerRegistry($containerMock);
+
+        $this->expectException(\TypeError::class);
+        $this->expectExceptionMessage('Return value of Enqueue\Symfony\Consumption\ContainerQueueConsumerRegistry::get() must implement interface Enqueue\Consumption\QueueConsumerInterface, instance of stdClass returned');
+        $registry->get('queueConsumer-name');
+    }
+
+    public function testShouldThrowExceptionIfQueueConsumerIsNotSet()
+    {
+        $containerMock = $this->createContainerMock();
+        $containerMock
+            ->expects($this->once())
+            ->method('has')
+            ->with('queueConsumer-name')
+            ->willReturn(false)
+        ;
+
+        $registry = new ContainerQueueConsumerRegistry($containerMock);
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Service locator does not have a queue consumer with name "queueConsumer-name".');
+        $registry->get('queueConsumer-name');
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createQueueConsumerMock(): QueueConsumerInterface
+    {
+        return $this->createMock(QueueConsumerInterface::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createContainerMock(): ContainerInterface
+    {
+        return $this->createMock(ContainerInterface::class);
+    }
+}
diff --git a/Tests/Symfony/ContainerProcessorRegistryTest.php b/Tests/Symfony/ContainerProcessorRegistryTest.php
new file mode 100644
index 0000000..fe84c2e
--- /dev/null
+++ b/Tests/Symfony/ContainerProcessorRegistryTest.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace Enqueue\Tests\Symfony;
+
+use Enqueue\ProcessorRegistryInterface;
+use Enqueue\Symfony\ContainerProcessorRegistry;
+use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\Processor;
+use PHPUnit\Framework\TestCase;
+use Psr\Container\ContainerInterface;
+
+class ContainerProcessorRegistryTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementProcessorRegistryInterface()
+    {
+        $this->assertClassImplements(ProcessorRegistryInterface::class, ContainerProcessorRegistry::class);
+    }
+
+    public function testShouldBeFinal()
+    {
+        $this->assertClassFinal(ContainerProcessorRegistry::class);
+    }
+
+    public function testCouldBeConstructedWithContainerAsFirstArgument()
+    {
+        new ContainerProcessorRegistry($this->createContainerMock());
+    }
+
+    public function testShouldAllowGetProcessor()
+    {
+        $processorMock = $this->createProcessorMock();
+
+        $containerMock = $this->createContainerMock();
+        $containerMock
+            ->expects($this->once())
+            ->method('has')
+            ->with('processor-name')
+            ->willReturn(true)
+        ;
+        $containerMock
+            ->expects($this->once())
+            ->method('get')
+            ->with('processor-name')
+            ->willReturn($processorMock)
+        ;
+
+        $registry = new ContainerProcessorRegistry($containerMock);
+        $this->assertSame($processorMock, $registry->get('processor-name'));
+    }
+
+    public function testThrowErrorIfServiceDoesNotImplementProcessorReturnType()
+    {
+        $containerMock = $this->createContainerMock();
+        $containerMock
+            ->expects($this->once())
+            ->method('has')
+            ->with('processor-name')
+            ->willReturn(true)
+        ;
+        $containerMock
+            ->expects($this->once())
+            ->method('get')
+            ->with('processor-name')
+            ->willReturn(new \stdClass())
+        ;
+
+        $registry = new ContainerProcessorRegistry($containerMock);
+
+        $this->expectException(\TypeError::class);
+        $this->expectExceptionMessage('Return value of Enqueue\Symfony\ContainerProcessorRegistry::get() must implement interface Interop\Queue\PsrProcessor, instance of stdClass returned');
+        $registry->get('processor-name');
+    }
+
+    public function testShouldThrowExceptionIfProcessorIsNotSet()
+    {
+        $containerMock = $this->createContainerMock();
+        $containerMock
+            ->expects($this->once())
+            ->method('has')
+            ->with('processor-name')
+            ->willReturn(false)
+        ;
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Service locator does not have a processor with name "processor-name".');
+
+        $registry = new ContainerProcessorRegistry($containerMock);
+        $registry->get('processor-name');
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createProcessorMock(): Processor
+    {
+        return $this->createMock(Processor::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createContainerMock(): ContainerInterface
+    {
+        return $this->createMock(ContainerInterface::class);
+    }
+}
diff --git a/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php b/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
new file mode 100644
index 0000000..6a7e83f
--- /dev/null
+++ b/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
@@ -0,0 +1,240 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\DependencyInjection;
+
+use Enqueue\Client\ExtensionInterface;
+use Enqueue\Symfony\DependencyInjection\BuildConsumptionExtensionsPass;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+
+class BuildConsumptionExtensionsPassTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementCompilerPassInterface()
+    {
+        $this->assertClassImplements(CompilerPassInterface::class, BuildConsumptionExtensionsPass::class);
+    }
+
+    public function testShouldBeFinal()
+    {
+        $this->assertClassFinal(BuildConsumptionExtensionsPass::class);
+    }
+
+    public function testCouldBeConstructedWithName()
+    {
+        $pass = new BuildConsumptionExtensionsPass('aName');
+
+        $this->assertAttributeSame('aName', 'name', $pass);
+    }
+
+    public function testThrowIfNameEmptyOnConstruct()
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The name could not be empty.');
+        new BuildConsumptionExtensionsPass('');
+    }
+
+    public function testShouldDoNothingIfExtensionsServiceIsNotRegistered()
+    {
+        $container = new ContainerBuilder();
+
+        //guard
+        $this->assertFalse($container->hasDefinition('enqueue.transport.aName.consumption_extensions'));
+
+        $pass = new BuildConsumptionExtensionsPass('aName');
+        $pass->process($container);
+    }
+
+    public function testShouldRegisterTransportExtension()
+    {
+        $extensions = new Definition();
+        $extensions->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.transport.aName.consumption_extensions', $extensions);
+
+        $container->register('aFooExtension', ExtensionInterface::class)
+            ->addTag('enqueue.transport.consumption_extension', ['transport' => 'aName'])
+        ;
+        $container->register('aBarExtension', ExtensionInterface::class)
+            ->addTag('enqueue.transport.consumption_extension', ['transport' => 'aName'])
+        ;
+
+        $pass = new BuildConsumptionExtensionsPass('aName');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $extensions->getArgument(0));
+        $this->assertEquals([
+            new Reference('aFooExtension'),
+            new Reference('aBarExtension'),
+        ], $extensions->getArgument(0));
+    }
+
+    public function testShouldIgnoreOtherTransportExtensions()
+    {
+        $extensions = new Definition();
+        $extensions->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.transport.aName.consumption_extensions', $extensions);
+
+        $container->register('aFooExtension', ExtensionInterface::class)
+            ->addTag('enqueue.transport.consumption_extension', ['transport' => 'aName'])
+        ;
+        $container->register('aBarExtension', ExtensionInterface::class)
+            ->addTag('enqueue.transport.consumption_extension', ['transport' => 'anotherName'])
+        ;
+
+        $pass = new BuildConsumptionExtensionsPass('aName');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $extensions->getArgument(0));
+        $this->assertEquals([
+            new Reference('aFooExtension'),
+        ], $extensions->getArgument(0));
+    }
+
+    public function testShouldAddExtensionIfTransportAll()
+    {
+        $extensions = new Definition();
+        $extensions->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.transport.aName.consumption_extensions', $extensions);
+
+        $container->register('aFooExtension', ExtensionInterface::class)
+            ->addTag('enqueue.transport.consumption_extension', ['transport' => 'all'])
+        ;
+        $container->register('aBarExtension', ExtensionInterface::class)
+            ->addTag('enqueue.transport.consumption_extension', ['transport' => 'anotherName'])
+        ;
+
+        $pass = new BuildConsumptionExtensionsPass('aName');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $extensions->getArgument(0));
+        $this->assertEquals([
+            new Reference('aFooExtension'),
+        ], $extensions->getArgument(0));
+    }
+
+    public function testShouldTreatTagsWithoutTransportAsDefaultTransport()
+    {
+        $extensions = new Definition();
+        $extensions->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.transport.default.consumption_extensions', $extensions);
+
+        $container->register('aFooExtension', ExtensionInterface::class)
+            ->addTag('enqueue.transport.consumption_extension')
+        ;
+        $container->register('aBarExtension', ExtensionInterface::class)
+            ->addTag('enqueue.transport.consumption_extension')
+        ;
+
+        $pass = new BuildConsumptionExtensionsPass('default');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $extensions->getArgument(0));
+        $this->assertEquals([
+            new Reference('aFooExtension'),
+            new Reference('aBarExtension'),
+        ], $extensions->getArgument(0));
+    }
+
+    public function testShouldOrderExtensionsByPriority()
+    {
+        $container = new ContainerBuilder();
+
+        $extensions = new Definition();
+        $extensions->addArgument([]);
+        $container->setDefinition('enqueue.transport.default.consumption_extensions', $extensions);
+
+        $extension = new Definition();
+        $extension->addTag('enqueue.transport.consumption_extension', ['priority' => 6]);
+        $container->setDefinition('foo_extension', $extension);
+
+        $extension = new Definition();
+        $extension->addTag('enqueue.transport.consumption_extension', ['priority' => -5]);
+        $container->setDefinition('bar_extension', $extension);
+
+        $extension = new Definition();
+        $extension->addTag('enqueue.transport.consumption_extension', ['priority' => 2]);
+        $container->setDefinition('baz_extension', $extension);
+
+        $pass = new BuildConsumptionExtensionsPass('default');
+        $pass->process($container);
+
+        $orderedExtensions = $extensions->getArgument(0);
+
+        $this->assertCount(3, $orderedExtensions);
+        $this->assertEquals(new Reference('foo_extension'), $orderedExtensions[0]);
+        $this->assertEquals(new Reference('baz_extension'), $orderedExtensions[1]);
+        $this->assertEquals(new Reference('bar_extension'), $orderedExtensions[2]);
+    }
+
+    public function testShouldAssumePriorityZeroIfPriorityIsNotSet()
+    {
+        $container = new ContainerBuilder();
+
+        $extensions = new Definition();
+        $extensions->addArgument([]);
+        $container->setDefinition('enqueue.transport.default.consumption_extensions', $extensions);
+
+        $extension = new Definition();
+        $extension->addTag('enqueue.transport.consumption_extension');
+        $container->setDefinition('foo_extension', $extension);
+
+        $extension = new Definition();
+        $extension->addTag('enqueue.transport.consumption_extension', ['priority' => 1]);
+        $container->setDefinition('bar_extension', $extension);
+
+        $extension = new Definition();
+        $extension->addTag('enqueue.transport.consumption_extension', ['priority' => -1]);
+        $container->setDefinition('baz_extension', $extension);
+
+        $pass = new BuildConsumptionExtensionsPass('default');
+        $pass->process($container);
+
+        $orderedExtensions = $extensions->getArgument(0);
+
+        $this->assertCount(3, $orderedExtensions);
+        $this->assertEquals(new Reference('bar_extension'), $orderedExtensions[0]);
+        $this->assertEquals(new Reference('foo_extension'), $orderedExtensions[1]);
+        $this->assertEquals(new Reference('baz_extension'), $orderedExtensions[2]);
+    }
+
+    public function testShouldMergeWithAddedPreviously()
+    {
+        $extensions = new Definition();
+        $extensions->addArgument([
+            'aBarExtension' => 'aBarServiceIdAddedPreviously',
+            'aOloloExtension' => 'aOloloServiceIdAddedPreviously',
+        ]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.transport.aName.consumption_extensions', $extensions);
+
+        $container->register('aFooExtension', ExtensionInterface::class)
+            ->addTag('enqueue.transport.consumption_extension')
+        ;
+        $container->register('aBarExtension', ExtensionInterface::class)
+            ->addTag('enqueue.transport.consumption_extension')
+        ;
+
+        $pass = new BuildConsumptionExtensionsPass('aName');
+        $pass->process($container);
+
+        $this->assertInternalType('array', $extensions->getArgument(0));
+        $this->assertEquals([
+            'aBarExtension' => 'aBarServiceIdAddedPreviously',
+            'aOloloExtension' => 'aOloloServiceIdAddedPreviously',
+        ], $extensions->getArgument(0));
+    }
+}
diff --git a/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php b/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
new file mode 100644
index 0000000..039024f
--- /dev/null
+++ b/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
@@ -0,0 +1,169 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\DependencyInjection;
+
+use Enqueue\ProcessorRegistryInterface;
+use Enqueue\Symfony\DependencyInjection\BuildProcessorRegistryPass;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+
+class BuildProcessorRegistryPassTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementCompilerPassInterface()
+    {
+        $this->assertClassImplements(CompilerPassInterface::class, BuildProcessorRegistryPass::class);
+    }
+
+    public function testShouldBeFinal()
+    {
+        $this->assertClassFinal(BuildProcessorRegistryPass::class);
+    }
+
+    public function testCouldBeConstructedWithName()
+    {
+        $pass = new BuildProcessorRegistryPass('aName');
+
+        $this->assertAttributeSame('aName', 'name', $pass);
+    }
+
+    public function testThrowIfNameEmptyOnConstruct()
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The name could not be empty.');
+        new BuildProcessorRegistryPass('');
+    }
+
+    public function testShouldDoNothingIfProcessorRegistryServiceIsNotRegistered()
+    {
+        $pass = new BuildProcessorRegistryPass('aName');
+        $pass->process(new ContainerBuilder());
+    }
+
+    public function testShouldRegisterProcessorWithMatchedName()
+    {
+        $registry = new Definition(ProcessorRegistryInterface::class);
+        $registry->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.transport.foo.processor_registry', $registry);
+        $container->register('aFooProcessor', 'aProcessorClass')
+            ->addTag('enqueue.transport.processor', ['transport' => 'foo'])
+        ;
+        $container->register('aProcessor', 'aProcessorClass')
+            ->addTag('enqueue.transport.processor', ['transport' => 'bar'])
+        ;
+
+        $pass = new BuildProcessorRegistryPass('foo');
+
+        $pass->process($container);
+
+        $this->assertInstanceOf(Reference::class, $registry->getArgument(0));
+
+        $this->assertLocatorServices($container, $registry->getArgument(0), [
+            'aFooProcessor' => 'aFooProcessor',
+        ]);
+    }
+
+    public function testShouldRegisterProcessorWithoutNameToDefaultTransport()
+    {
+        $registry = new Definition(ProcessorRegistryInterface::class);
+        $registry->addArgument(null);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.transport.default.processor_registry', $registry);
+        $container->register('aFooProcessor', 'aProcessorClass')
+            ->addTag('enqueue.transport.processor', [])
+        ;
+        $container->register('aProcessor', 'aProcessorClass')
+            ->addTag('enqueue.transport.processor', ['transport' => 'bar'])
+        ;
+
+        $pass = new BuildProcessorRegistryPass('default');
+
+        $pass->process($container);
+
+        $this->assertInstanceOf(Reference::class, $registry->getArgument(0));
+
+        $this->assertLocatorServices($container, $registry->getArgument(0), [
+            'aFooProcessor' => 'aFooProcessor',
+        ]);
+    }
+
+    public function testShouldRegisterProcessorIfTransportNameEqualsAll()
+    {
+        $registry = new Definition(ProcessorRegistryInterface::class);
+        $registry->addArgument(null);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.transport.default.processor_registry', $registry);
+        $container->register('aFooProcessor', 'aProcessorClass')
+            ->addTag('enqueue.transport.processor', ['transport' => 'all'])
+        ;
+        $container->register('aProcessor', 'aProcessorClass')
+            ->addTag('enqueue.transport.processor', ['transport' => 'bar'])
+        ;
+
+        $pass = new BuildProcessorRegistryPass('default');
+
+        $pass->process($container);
+
+        $this->assertInstanceOf(Reference::class, $registry->getArgument(0));
+
+        $this->assertLocatorServices($container, $registry->getArgument(0), [
+            'aFooProcessor' => 'aFooProcessor',
+        ]);
+    }
+
+    public function testShouldRegisterWithCustomProcessorName()
+    {
+        $registry = new Definition(ProcessorRegistryInterface::class);
+        $registry->addArgument(null);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('enqueue.transport.default.processor_registry', $registry);
+        $container->register('aFooProcessor', 'aProcessorClass')
+            ->addTag('enqueue.transport.processor', ['processor' => 'customProcessorName'])
+        ;
+
+        $pass = new BuildProcessorRegistryPass('default');
+
+        $pass->process($container);
+
+        $this->assertInstanceOf(Reference::class, $registry->getArgument(0));
+
+        $this->assertLocatorServices($container, $registry->getArgument(0), [
+            'customProcessorName' => 'aFooProcessor',
+        ]);
+    }
+
+    private function assertLocatorServices(ContainerBuilder $container, $locatorId, array $locatorServices)
+    {
+        $this->assertInstanceOf(Reference::class, $locatorId);
+        $locatorId = (string) $locatorId;
+
+        $this->assertTrue($container->hasDefinition($locatorId));
+        $this->assertRegExp('/service_locator\..*?\.enqueue\./', $locatorId);
+
+        $match = [];
+        if (false == preg_match('/(service_locator\..*?)\.enqueue\./', $locatorId, $match)) {
+            $this->fail('preg_match should not failed');
+        }
+
+        $this->assertTrue($container->hasDefinition($match[1]));
+        $locator = $container->getDefinition($match[1]);
+
+        $this->assertContainsOnly(ServiceClosureArgument::class, $locator->getArgument(0));
+        $actualServices = array_map(function (ServiceClosureArgument $value) {
+            return (string) $value->getValues()[0];
+        }, $locator->getArgument(0));
+
+        $this->assertEquals($locatorServices, $actualServices);
+    }
+}

From df3f675ca6feae08d833ef56c526dae10a4984c3 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 2 Oct 2018 19:55:48 +0300
Subject: [PATCH 143/337] Multiple transport support. Container based approach.

---
 Consumption/ArrayQueueConsumerRegistry.php    |  36 ------
 .../QueueConsumerRegistryInterface.php        |  10 --
 Container/Container.php                       |  32 +++++
 Container/NotFoundException.php               |   9 ++
 .../Consumption/ChooseLoggerCommandTrait.php  |  35 ++++++
 .../ConfigurableConsumeCommand.php            |  60 +++++++---
 Symfony/Consumption/ConsumeCommand.php        |  48 +++++---
 .../ContainerQueueConsumerRegistry.php        |  31 -----
 .../FormatTransportNameTrait.php              |  17 +++
 .../DependencyInjection/TransportFactory.php  |  83 +++++++++++--
 .../ArrayQueueConsumerRegistryTest.php        |  60 ----------
 .../ConfigurableConsumeCommandTest.php        | 112 +++++++++---------
 .../Consumption/ConsumeCommandTest.php        |  31 ++---
 .../ContainerQueueConsumerRegistryTest.php    | 108 -----------------
 .../TransportFactoryTest.php                  |  10 +-
 15 files changed, 322 insertions(+), 360 deletions(-)
 delete mode 100644 Consumption/ArrayQueueConsumerRegistry.php
 delete mode 100644 Consumption/QueueConsumerRegistryInterface.php
 create mode 100644 Container/Container.php
 create mode 100644 Container/NotFoundException.php
 create mode 100644 Symfony/Consumption/ChooseLoggerCommandTrait.php
 delete mode 100644 Symfony/Consumption/ContainerQueueConsumerRegistry.php
 create mode 100644 Symfony/DependencyInjection/FormatTransportNameTrait.php
 delete mode 100644 Tests/Consumption/ArrayQueueConsumerRegistryTest.php
 delete mode 100644 Tests/Symfony/Consumption/ContainerQueueConsumerRegistryTest.php

diff --git a/Consumption/ArrayQueueConsumerRegistry.php b/Consumption/ArrayQueueConsumerRegistry.php
deleted file mode 100644
index 3b93d74..0000000
--- a/Consumption/ArrayQueueConsumerRegistry.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-namespace Enqueue\Consumption;
-
-class ArrayQueueConsumerRegistry implements QueueConsumerRegistryInterface
-{
-    /**
-     * @var QueueConsumerInterface[]
-     */
-    private $consumers;
-
-    /**
-     * @param QueueConsumerInterface[] $queueConsumers
-     */
-    public function __construct(array $queueConsumers = [])
-    {
-        $this->consumers = [];
-        array_walk($queueConsumers, function (QueueConsumerInterface $consumer, string $key) {
-            $this->consumers[$key] = $consumer;
-        });
-    }
-
-    public function add(string $name, QueueConsumerInterface $consumer): void
-    {
-        $this->consumers[$name] = $consumer;
-    }
-
-    public function get(string $name): QueueConsumerInterface
-    {
-        if (false == isset($this->consumers[$name])) {
-            throw new \LogicException(sprintf('QueueConsumer was not found, name: "%s".', $name));
-        }
-
-        return $this->consumers[$name];
-    }
-}
diff --git a/Consumption/QueueConsumerRegistryInterface.php b/Consumption/QueueConsumerRegistryInterface.php
deleted file mode 100644
index 189004e..0000000
--- a/Consumption/QueueConsumerRegistryInterface.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Enqueue\Consumption;
-
-interface QueueConsumerRegistryInterface
-{
-    public function get(string $name): QueueConsumerInterface;
-}
diff --git a/Container/Container.php b/Container/Container.php
new file mode 100644
index 0000000..255def3
--- /dev/null
+++ b/Container/Container.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Enqueue\Container;
+
+use Psr\Container\ContainerInterface;
+
+class Container implements ContainerInterface
+{
+    /**
+     * @var array
+     */
+    private $services;
+
+    public function __construct(array $services)
+    {
+        $this->services = $services;
+    }
+
+    public function get($id)
+    {
+        if (false == $this->has($id)) {
+            throw new NotFoundException(sprintf('The service "%s" not found.', $id));
+        }
+
+        return $this->services[$id];
+    }
+
+    public function has($id)
+    {
+        return array_key_exists($id, $this->services);
+    }
+}
diff --git a/Container/NotFoundException.php b/Container/NotFoundException.php
new file mode 100644
index 0000000..fcc3386
--- /dev/null
+++ b/Container/NotFoundException.php
@@ -0,0 +1,9 @@
+<?php
+
+namespace Enqueue\Container;
+
+use Psr\Container\NotFoundExceptionInterface;
+
+class NotFoundException extends \InvalidArgumentException implements NotFoundExceptionInterface
+{
+}
diff --git a/Symfony/Consumption/ChooseLoggerCommandTrait.php b/Symfony/Consumption/ChooseLoggerCommandTrait.php
new file mode 100644
index 0000000..c229c14
--- /dev/null
+++ b/Symfony/Consumption/ChooseLoggerCommandTrait.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Enqueue\Symfony\Consumption;
+
+use Enqueue\Consumption\Extension\LoggerExtension;
+use Psr\Log\NullLogger;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Logger\ConsoleLogger;
+use Symfony\Component\Console\Output\OutputInterface;
+
+trait ChooseLoggerCommandTrait
+{
+    protected function configureLoggerExtension(): void
+    {
+        $this
+            ->addOption('logger', null, InputOption::VALUE_OPTIONAL, 'A logger to be used. Could be "default", "null", "stdout".', 'default')
+        ;
+    }
+
+    protected function getLoggerExtension(InputInterface $input, OutputInterface $output): ?LoggerExtension
+    {
+        $logger = $input->getOption('logger');
+        switch ($logger) {
+            case 'null':
+                return new LoggerExtension(new NullLogger());
+            case 'stdout':
+                return new LoggerExtension(new ConsoleLogger($output));
+            case 'default':
+                return null;
+            default:
+                throw new \LogicException(sprintf('The logger "%s" is not supported', $logger));
+        }
+    }
+}
diff --git a/Symfony/Consumption/ConfigurableConsumeCommand.php b/Symfony/Consumption/ConfigurableConsumeCommand.php
index 86a300f..68ae6da 100644
--- a/Symfony/Consumption/ConfigurableConsumeCommand.php
+++ b/Symfony/Consumption/ConfigurableConsumeCommand.php
@@ -3,46 +3,57 @@
 namespace Enqueue\Symfony\Consumption;
 
 use Enqueue\Consumption\ChainExtension;
-use Enqueue\Consumption\Extension\LoggerExtension;
-use Enqueue\Consumption\QueueConsumerRegistryInterface;
+use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Consumption\QueueSubscriberInterface;
 use Enqueue\ProcessorRegistryInterface;
+use Psr\Container\ContainerInterface;
+use Psr\Container\NotFoundExceptionInterface;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputArgument;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputOption;
-use Symfony\Component\Console\Logger\ConsoleLogger;
 use Symfony\Component\Console\Output\OutputInterface;
 
 class ConfigurableConsumeCommand extends Command
 {
     use LimitsExtensionsCommandTrait;
     use QueueConsumerOptionsCommandTrait;
+    use ChooseLoggerCommandTrait;
 
     protected static $defaultName = 'enqueue:transport:consume';
 
     /**
-     * @var QueueConsumerRegistryInterface
+     * @var ContainerInterface
      */
-    protected $consumerRegistry;
+    private $container;
 
     /**
-     * @var ProcessorRegistryInterface
+     * @var string
      */
-    private $processorRegistry;
+    private $queueConsumerIdPattern;
 
-    public function __construct(QueueConsumerRegistryInterface $consumerRegistry, ProcessorRegistryInterface $processorRegistry)
-    {
+    /**
+     * @var string
+     */
+    private $processorRegistryIdPattern;
+
+    public function __construct(
+        ContainerInterface $container,
+        string $queueConsumerIdPattern = 'enqueue.transport.%s.queue_consumer',
+        string $processorRegistryIdPattern = 'enqueue.transport.%s.processor_registry'
+    ) {
         parent::__construct(static::$defaultName);
 
-        $this->consumerRegistry = $consumerRegistry;
-        $this->processorRegistry = $processorRegistry;
+        $this->container = $container;
+        $this->queueConsumerIdPattern = $queueConsumerIdPattern;
+        $this->processorRegistryIdPattern = $processorRegistryIdPattern;
     }
 
     protected function configure(): void
     {
         $this->configureLimitsExtensions();
         $this->configureQueueConsumerOptions();
+        $this->configureLoggerExtension();
 
         $this
             ->setDescription('A worker that consumes message from a broker. '.
@@ -56,11 +67,17 @@ protected function configure(): void
 
     protected function execute(InputInterface $input, OutputInterface $output): ?int
     {
-        $consumer = $this->consumerRegistry->get($input->getOption('transport'));
+        $transport = $input->getOption('transport');
+
+        try {
+            $consumer = $this->getQueueConsumer($transport);
+        } catch (NotFoundExceptionInterface $e) {
+            throw new \LogicException(sprintf('Transport "%s" is not supported.', $transport), null, $e);
+        }
 
         $this->setQueueConsumerOptions($consumer, $input);
 
-        $processor = $this->processorRegistry->get($input->getArgument('processor'));
+        $processor = $this->getProcessorRegistry($transport)->get($input->getArgument('processor'));
 
         $queues = $input->getArgument('queues');
         if (empty($queues) && $processor instanceof QueueSubscriberInterface) {
@@ -75,16 +92,27 @@ protected function execute(InputInterface $input, OutputInterface $output): ?int
         }
 
         $extensions = $this->getLimitsExtensions($input, $output);
-        array_unshift($extensions, new LoggerExtension(new ConsoleLogger($output)));
 
-        $runtimeExtensions = new ChainExtension($extensions);
+        if ($loggerExtension = $this->getLoggerExtension($input, $output)) {
+            array_unshift($extensions, $loggerExtension);
+        }
 
         foreach ($queues as $queue) {
             $consumer->bind($queue, $processor);
         }
 
-        $consumer->consume($runtimeExtensions);
+        $consumer->consume(new ChainExtension($extensions));
 
         return null;
     }
+
+    private function getQueueConsumer(string $name): QueueConsumerInterface
+    {
+        return $this->container->get(sprintf($this->queueConsumerIdPattern, $name));
+    }
+
+    private function getProcessorRegistry(string $name): ProcessorRegistryInterface
+    {
+        return $this->container->get(sprintf($this->processorRegistryIdPattern, $name));
+    }
 }
diff --git a/Symfony/Consumption/ConsumeCommand.php b/Symfony/Consumption/ConsumeCommand.php
index 2b4470b..632a7de 100644
--- a/Symfony/Consumption/ConsumeCommand.php
+++ b/Symfony/Consumption/ConsumeCommand.php
@@ -3,46 +3,50 @@
 namespace Enqueue\Symfony\Consumption;
 
 use Enqueue\Consumption\ChainExtension;
-use Enqueue\Consumption\Extension\LoggerExtension;
 use Enqueue\Consumption\QueueConsumerInterface;
-use Enqueue\Consumption\QueueConsumerRegistryInterface;
+use Psr\Container\ContainerInterface;
+use Psr\Container\NotFoundExceptionInterface;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputOption;
-use Symfony\Component\Console\Logger\ConsoleLogger;
 use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\DependencyInjection\ContainerAwareInterface;
-use Symfony\Component\DependencyInjection\ContainerAwareTrait;
 
-class ConsumeCommand extends Command implements ContainerAwareInterface
+class ConsumeCommand extends Command
 {
-    use ContainerAwareTrait;
     use LimitsExtensionsCommandTrait;
     use QueueConsumerOptionsCommandTrait;
+    use ChooseLoggerCommandTrait;
 
     protected static $defaultName = 'enqueue:transport:consume';
 
     /**
-     * @var QueueConsumerRegistryInterface
+     * @var ContainerInterface
      */
-    protected $consumerRegistry;
+    private $container;
+
+    /**
+     * @var string
+     */
+    private $queueConsumerIdPattern;
 
     /**
      * [name => QueueConsumerInterface].
      *
      * @param QueueConsumerInterface[]
      */
-    public function __construct(QueueConsumerRegistryInterface $consumerRegistry)
+    public function __construct(ContainerInterface $container, string $queueConsumerIdPattern = 'enqueue.transport.%s.queue_consumer')
     {
         parent::__construct(static::$defaultName);
 
-        $this->consumerRegistry = $consumerRegistry;
+        $this->container = $container;
+        $this->queueConsumerIdPattern = $queueConsumerIdPattern;
     }
 
     protected function configure(): void
     {
         $this->configureLimitsExtensions();
         $this->configureQueueConsumerOptions();
+        $this->configureLoggerExtension();
 
         $this
             ->addOption('transport', 't', InputOption::VALUE_OPTIONAL, 'The transport to consume messages from.', 'default')
@@ -53,18 +57,30 @@ protected function configure(): void
 
     protected function execute(InputInterface $input, OutputInterface $output): ?int
     {
-        // QueueConsumer must be pre configured outside of the command!
-        $consumer = $this->consumerRegistry->get($input->getOption('transport'));
+        $transport = $input->getOption('transport');
+
+        try {
+            // QueueConsumer must be pre configured outside of the command!
+            $consumer = $this->getQueueConsumer($transport);
+        } catch (NotFoundExceptionInterface $e) {
+            throw new \LogicException(sprintf('Transport "%s" is not supported.', $transport), null, $e);
+        }
 
         $this->setQueueConsumerOptions($consumer, $input);
 
         $extensions = $this->getLimitsExtensions($input, $output);
-        array_unshift($extensions, new LoggerExtension(new ConsoleLogger($output)));
 
-        $runtimeExtensions = new ChainExtension($extensions);
+        if ($loggerExtension = $this->getLoggerExtension($input, $output)) {
+            array_unshift($extensions, $loggerExtension);
+        }
 
-        $consumer->consume($runtimeExtensions);
+        $consumer->consume(new ChainExtension($extensions));
 
         return null;
     }
+
+    private function getQueueConsumer(string $name): QueueConsumerInterface
+    {
+        return $this->container->get(sprintf($this->queueConsumerIdPattern, $name));
+    }
 }
diff --git a/Symfony/Consumption/ContainerQueueConsumerRegistry.php b/Symfony/Consumption/ContainerQueueConsumerRegistry.php
deleted file mode 100644
index 2163b21..0000000
--- a/Symfony/Consumption/ContainerQueueConsumerRegistry.php
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Enqueue\Symfony\Consumption;
-
-use Enqueue\Consumption\QueueConsumerInterface;
-use Enqueue\Consumption\QueueConsumerRegistryInterface;
-use Psr\Container\ContainerInterface;
-
-final class ContainerQueueConsumerRegistry implements QueueConsumerRegistryInterface
-{
-    /**
-     * @var ContainerInterface
-     */
-    private $locator;
-
-    public function __construct(ContainerInterface $locator)
-    {
-        $this->locator = $locator;
-    }
-
-    public function get(string $name): QueueConsumerInterface
-    {
-        if (false == $this->locator->has($name)) {
-            throw new \LogicException(sprintf('Service locator does not have a queue consumer with name "%s".', $name));
-        }
-
-        return $this->locator->get($name);
-    }
-}
diff --git a/Symfony/DependencyInjection/FormatTransportNameTrait.php b/Symfony/DependencyInjection/FormatTransportNameTrait.php
new file mode 100644
index 0000000..17a41d0
--- /dev/null
+++ b/Symfony/DependencyInjection/FormatTransportNameTrait.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Enqueue\Symfony\DependencyInjection;
+
+trait FormatTransportNameTrait
+{
+    abstract protected function getName(): string;
+
+    private function format(string $serviceName, $parameter = false): string
+    {
+        $pattern = 'enqueue.transport.%s.'.$serviceName;
+
+        $fullName = sprintf($pattern, $this->getName());
+
+        return $parameter ? "%$fullName%" : $fullName;
+    }
+}
diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index 7a80eea..029904a 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -4,7 +4,13 @@
 
 use Enqueue\ConnectionFactoryFactory;
 use Enqueue\ConnectionFactoryFactoryInterface;
+use Enqueue\Consumption\ChainExtension;
+use Enqueue\Consumption\QueueConsumer;
+use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Resources;
+use Enqueue\Rpc\RpcClient;
+use Enqueue\Rpc\RpcFactory;
+use Enqueue\Symfony\ContainerProcessorRegistry;
 use Interop\Queue\ConnectionFactory;
 use Interop\Queue\Context;
 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
@@ -16,6 +22,8 @@
  */
 final class TransportFactory
 {
+    use FormatTransportNameTrait;
+
     /**
      * @var string
      */
@@ -86,11 +94,44 @@ public function addConfiguration(ArrayNodeDefinition $builder): void
         ;
     }
 
-    public function createConnectionFactory(ContainerBuilder $container, array $config): string
+    public function build(ContainerBuilder $container, array $config): void
+    {
+        $this->buildConnectionFactory($container, $config);
+        $this->buildContext($container, $config);
+        $this->buildQueueConsumer($container, $config);
+        $this->buildRpcClient($container, $config);
+
+        $container->register($this->format('processor_registry'), ContainerProcessorRegistry::class);
+
+        $locator = 'enqueue.locator';
+        if ($container->hasDefinition($locator)) {
+            $locator = $container->getDefinition($locator);
+            $locator->replaceArgument(0, array_merge($locator->getArgument(0), [
+                $this->format('queue_consumer') => new Reference($this->format('queue_consumer')),
+                $this->format('processor_registry') => new Reference($this->format('processor_registry')),
+            ]));
+
+            return;
+        }
+
+        if ('default' === $this->name) {
+            $container->setAlias(RpcClient::class, $this->format('rpc_client'));
+            $container->setAlias(ConnectionFactory::class, $this->format('connection_factory'));
+            $container->setAlias(Context::class, $this->format('context'));
+            $container->setAlias(QueueConsumerInterface::class, $this->format('queue_consumer'));
+        }
+    }
+
+    public function getName(): string
+    {
+        return $this->name;
+    }
+
+    private function buildConnectionFactory(ContainerBuilder $container, array $config): void
     {
-        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
-        $factoryFactoryId = sprintf('enqueue.transport.%s.connection_factory_factory', $this->getName());
+        $factoryId = $this->format('connection_factory');
 
+        $factoryFactoryId = $this->format('connection_factory_factory');
         $container->register($factoryFactoryId, $config['factory_class'] ?? ConnectionFactoryFactory::class);
 
         $factoryFactoryService = new Reference(
@@ -112,24 +153,44 @@ public function createConnectionFactory(ContainerBuilder $container, array $conf
                 ->addArgument($config)
             ;
         }
-
-        return $factoryId;
     }
 
-    public function createContext(ContainerBuilder $container, array $config): string
+    private function buildContext(ContainerBuilder $container, array $config): void
     {
-        $contextId = sprintf('enqueue.transport.%s.context', $this->getName());
-        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
+        $contextId = $this->format('context');
+        $factoryId = $this->format('connection_factory');
 
         $container->register($contextId, Context::class)
             ->setFactory([new Reference($factoryId), 'createContext'])
         ;
+    }
+
+    private function buildQueueConsumer(ContainerBuilder $container, array $config): void
+    {
+        $container->setParameter($this->format('default_idle_time'), 0);
+        $container->setParameter($this->format('default_receive_timeout'), 10000);
+
+        $container->register($this->format('consumption_extensions'), ChainExtension::class)
+            ->addArgument([])
+        ;
 
-        return $contextId;
+        $container->register($this->format('queue_consumer'), QueueConsumer::class)
+            ->addArgument(new Reference($this->format('context')))
+            ->addArgument(new Reference($this->format('consumption_extensions')))
+            ->addArgument($this->format('default_idle_time', true))
+            ->addArgument($this->format('default_receive_timeout', true))
+        ;
     }
 
-    public function getName(): string
+    private function buildRpcClient(ContainerBuilder $container, array $config): void
     {
-        return $this->name;
+        $container->register($this->format('rpc_factory'), RpcFactory::class)
+            ->addArgument(new Reference($this->format('context')))
+        ;
+
+        $container->register($this->format('rpc_client'), RpcClient::class)
+            ->addArgument(new Reference($this->format('context')))
+            ->addArgument(new Reference($this->format('rpc_factory')))
+        ;
     }
 }
diff --git a/Tests/Consumption/ArrayQueueConsumerRegistryTest.php b/Tests/Consumption/ArrayQueueConsumerRegistryTest.php
deleted file mode 100644
index 6cc5270..0000000
--- a/Tests/Consumption/ArrayQueueConsumerRegistryTest.php
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Consumption;
-
-use Enqueue\Consumption\ArrayQueueConsumerRegistry;
-use Enqueue\Consumption\QueueConsumerInterface;
-use Enqueue\Consumption\QueueConsumerRegistryInterface;
-use Enqueue\Test\ClassExtensionTrait;
-use PHPUnit\Framework\TestCase;
-
-class ArrayQueueConsumerRegistryTest extends TestCase
-{
-    use ClassExtensionTrait;
-
-    public function testShouldImplementQueueConsumerRegistryInterface()
-    {
-        $this->assertClassImplements(QueueConsumerRegistryInterface::class, ArrayQueueConsumerRegistry::class);
-    }
-
-    public function testCouldBeConstructedWithoutAnyArgument()
-    {
-        new ArrayQueueConsumerRegistry();
-    }
-
-    public function testShouldThrowExceptionIfQueueConsumerIsNotSet()
-    {
-        $registry = new ArrayQueueConsumerRegistry();
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('QueueConsumer was not found, name: "queueConsumer-name".');
-        $registry->get('queueConsumer-name');
-    }
-
-    public function testShouldAllowGetQueueConsumerAddedViaConstructor()
-    {
-        $queueConsumer = $this->createQueueConsumerMock();
-
-        $registry = new ArrayQueueConsumerRegistry(['aFooName' => $queueConsumer]);
-
-        $this->assertSame($queueConsumer, $registry->get('aFooName'));
-    }
-
-    public function testShouldAllowGetQueueConsumerAddedViaAddMethod()
-    {
-        $queueConsumer = $this->createQueueConsumerMock();
-
-        $registry = new ArrayQueueConsumerRegistry();
-        $registry->add('aFooName', $queueConsumer);
-
-        $this->assertSame($queueConsumer, $registry->get('aFooName'));
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
-     */
-    protected function createQueueConsumerMock(): QueueConsumerInterface
-    {
-        return $this->createMock(QueueConsumerInterface::class);
-    }
-}
diff --git a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
index b41b6b8..786ce94 100644
--- a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
@@ -3,50 +3,40 @@
 namespace Enqueue\Tests\Symfony\Consumption;
 
 use Enqueue\ArrayProcessorRegistry;
-use Enqueue\Consumption\ArrayQueueConsumerRegistry;
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\QueueConsumerInterface;
-use Enqueue\Consumption\QueueConsumerRegistryInterface;
 use Enqueue\Consumption\QueueSubscriberInterface;
-use Enqueue\ProcessorRegistryInterface;
+use Enqueue\Container\Container;
 use Enqueue\Symfony\Consumption\ConfigurableConsumeCommand;
 use Interop\Queue\Context;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Processor;
 use Interop\Queue\Queue as InteropQueue;
 use PHPUnit\Framework\TestCase;
+use Psr\Container\ContainerInterface;
 use Symfony\Component\Console\Tester\CommandTester;
 
 class ConfigurableConsumeCommandTest extends TestCase
 {
     public function testCouldBeConstructedWithRequiredAttributes()
     {
-        new ConfigurableConsumeCommand(
-            $this->createMock(QueueConsumerRegistryInterface::class),
-            $this->createMock(ProcessorRegistryInterface::class)
-        );
+        new ConfigurableConsumeCommand($this->createMock(ContainerInterface::class));
     }
 
     public function testShouldHaveCommandName()
     {
-        $command = new ConfigurableConsumeCommand(
-            $this->createMock(QueueConsumerRegistryInterface::class),
-            $this->createMock(ProcessorRegistryInterface::class)
-        );
+        $command = new ConfigurableConsumeCommand($this->createMock(ContainerInterface::class));
 
         $this->assertEquals('enqueue:transport:consume', $command->getName());
     }
 
     public function testShouldHaveExpectedOptions()
     {
-        $command = new ConfigurableConsumeCommand(
-            $this->createMock(QueueConsumerRegistryInterface::class),
-            $this->createMock(ProcessorRegistryInterface::class)
-        );
+        $command = new ConfigurableConsumeCommand($this->createMock(ContainerInterface::class));
 
         $options = $command->getDefinition()->getOptions();
 
-        $this->assertCount(7, $options);
+        $this->assertCount(8, $options);
         $this->assertArrayHasKey('memory-limit', $options);
         $this->assertArrayHasKey('message-limit', $options);
         $this->assertArrayHasKey('time-limit', $options);
@@ -54,14 +44,12 @@ public function testShouldHaveExpectedOptions()
         $this->assertArrayHasKey('receive-timeout', $options);
         $this->assertArrayHasKey('niceness', $options);
         $this->assertArrayHasKey('transport', $options);
+        $this->assertArrayHasKey('logger', $options);
     }
 
     public function testShouldHaveExpectedAttributes()
     {
-        $command = new ConfigurableConsumeCommand(
-            $this->createMock(QueueConsumerRegistryInterface::class),
-            $this->createMock(ProcessorRegistryInterface::class)
-        );
+        $command = new ConfigurableConsumeCommand($this->createMock(ContainerInterface::class));
 
         $arguments = $command->getDefinition()->getArguments();
 
@@ -84,12 +72,10 @@ public function testThrowIfNeitherQueueOptionNorProcessorImplementsQueueSubscrib
             ->method('consume')
         ;
 
-        $registry = new ArrayProcessorRegistry(['aProcessor' => $processor]);
-
-        $command = new ConfigurableConsumeCommand(
-            $this->createMock(QueueConsumerRegistryInterface::class),
-            $registry
-        );
+        $command = new ConfigurableConsumeCommand(new Container([
+            'enqueue.transport.default.queue_consumer' => $consumer,
+            'enqueue.transport.default.processor_registry' => new ArrayProcessorRegistry(['aProcessor' => $processor]),
+        ]));
 
         $tester = new CommandTester($command);
 
@@ -116,18 +102,45 @@ public function testShouldExecuteConsumptionWithExplicitlySetQueue()
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
 
-        $processorRegistry = new ArrayProcessorRegistry(['processor-service' => $processor]);
-        $consumerRegistry = new ArrayQueueConsumerRegistry(['default' => $consumer]);
+        $command = new ConfigurableConsumeCommand(new Container([
+            'enqueue.transport.default.queue_consumer' => $consumer,
+            'enqueue.transport.default.processor_registry' => new ArrayProcessorRegistry(['processor-service' => $processor]),
+        ]));
+
+        $tester = new CommandTester($command);
+        $tester->execute([
+            'processor' => 'processor-service',
+            'queues' => ['queue-name'],
+        ]);
+    }
+
+    public function testThrowIfTransportNotDefined()
+    {
+        $processor = $this->createProcessor();
+
+        $consumer = $this->createQueueConsumerMock();
+        $consumer
+            ->expects($this->never())
+            ->method('bind')
+        ;
+        $consumer
+            ->expects($this->never())
+            ->method('consume')
+        ;
 
-        $command = new ConfigurableConsumeCommand(
-            $consumerRegistry,
-            $processorRegistry
-        );
+        $command = new ConfigurableConsumeCommand(new Container([
+            'enqueue.transport.default.queue_consumer' => $consumer,
+            'enqueue.transport.default.processor_registry' => new ArrayProcessorRegistry(['processor-service' => $processor]),
+        ]));
 
         $tester = new CommandTester($command);
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Transport "not-defined" is not supported.');
         $tester->execute([
             'processor' => 'processor-service',
             'queues' => ['queue-name'],
+            '--transport' => 'not-defined',
         ]);
     }
 
@@ -152,13 +165,10 @@ public function testShouldExecuteConsumptionWithSeveralCustomQueues()
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
 
-        $processorRegistry = new ArrayProcessorRegistry(['processor-service' => $processor]);
-        $consumerRegistry = new ArrayQueueConsumerRegistry(['default' => $consumer]);
-
-        $command = new ConfigurableConsumeCommand(
-            $consumerRegistry,
-            $processorRegistry
-        );
+        $command = new ConfigurableConsumeCommand(new Container([
+            'enqueue.transport.default.queue_consumer' => $consumer,
+            'enqueue.transport.default.processor_registry' => new ArrayProcessorRegistry(['processor-service' => $processor]),
+        ]));
 
         $tester = new CommandTester($command);
         $tester->execute([
@@ -197,13 +207,10 @@ public static function getSubscribedQueues()
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
 
-        $processorRegistry = new ArrayProcessorRegistry(['processor-service' => $processor]);
-        $consumerRegistry = new ArrayQueueConsumerRegistry(['default' => $consumer]);
-
-        $command = new ConfigurableConsumeCommand(
-            $consumerRegistry,
-            $processorRegistry
-        );
+        $command = new ConfigurableConsumeCommand(new Container([
+            'enqueue.transport.default.queue_consumer' => $consumer,
+            'enqueue.transport.default.processor_registry' => new ArrayProcessorRegistry(['processor-service' => $processor]),
+        ]));
 
         $tester = new CommandTester($command);
         $tester->execute([
@@ -238,13 +245,12 @@ public function testShouldExecuteConsumptionWithCustomTransportExplicitlySetQueu
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
 
-        $processorRegistry = new ArrayProcessorRegistry(['processor-service' => $processor]);
-        $consumerRegistry = new ArrayQueueConsumerRegistry(['foo' => $fooConsumer, 'bar' => $barConsumer]);
-
-        $command = new ConfigurableConsumeCommand(
-            $consumerRegistry,
-            $processorRegistry
-        );
+        $command = new ConfigurableConsumeCommand(new Container([
+            'enqueue.transport.foo.queue_consumer' => $fooConsumer,
+            'enqueue.transport.foo.processor_registry' => new ArrayProcessorRegistry(['processor-service' => $processor]),
+            'enqueue.transport.bar.queue_consumer' => $barConsumer,
+            'enqueue.transport.bar.processor_registry' => new ArrayProcessorRegistry(['processor-service' => $processor]),
+        ]));
 
         $tester = new CommandTester($command);
         $tester->execute([
diff --git a/Tests/Symfony/Consumption/ConsumeCommandTest.php b/Tests/Symfony/Consumption/ConsumeCommandTest.php
index e08dbaa..cd061c0 100644
--- a/Tests/Symfony/Consumption/ConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConsumeCommandTest.php
@@ -2,36 +2,36 @@
 
 namespace Enqueue\Tests\Symfony\Consumption;
 
-use Enqueue\Consumption\ArrayQueueConsumerRegistry;
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\QueueConsumerInterface;
-use Enqueue\Consumption\QueueConsumerRegistryInterface;
+use Enqueue\Container\Container;
 use Enqueue\Symfony\Consumption\ConsumeCommand;
 use Interop\Queue\Context;
 use PHPUnit\Framework\TestCase;
+use Psr\Container\ContainerInterface;
 use Symfony\Component\Console\Tester\CommandTester;
 
 class ConsumeCommandTest extends TestCase
 {
     public function testCouldBeConstructedWithRequiredAttributes()
     {
-        new ConsumeCommand($this->createMock(QueueConsumerRegistryInterface::class));
+        new ConsumeCommand($this->createMock(ContainerInterface::class));
     }
 
     public function testShouldHaveCommandName()
     {
-        $command = new ConsumeCommand($this->createMock(QueueConsumerRegistryInterface::class));
+        $command = new ConsumeCommand($this->createMock(ContainerInterface::class));
 
         $this->assertEquals('enqueue:transport:consume', $command->getName());
     }
 
     public function testShouldHaveExpectedOptions()
     {
-        $command = new ConsumeCommand($this->createMock(QueueConsumerRegistryInterface::class));
+        $command = new ConsumeCommand($this->createMock(ContainerInterface::class));
 
         $options = $command->getDefinition()->getOptions();
 
-        $this->assertCount(7, $options);
+        $this->assertCount(8, $options);
         $this->assertArrayHasKey('memory-limit', $options);
         $this->assertArrayHasKey('message-limit', $options);
         $this->assertArrayHasKey('time-limit', $options);
@@ -39,11 +39,12 @@ public function testShouldHaveExpectedOptions()
         $this->assertArrayHasKey('receive-timeout', $options);
         $this->assertArrayHasKey('niceness', $options);
         $this->assertArrayHasKey('transport', $options);
+        $this->assertArrayHasKey('logger', $options);
     }
 
     public function testShouldHaveExpectedAttributes()
     {
-        $command = new ConsumeCommand($this->createMock(QueueConsumerRegistryInterface::class));
+        $command = new ConsumeCommand($this->createMock(ContainerInterface::class));
 
         $arguments = $command->getDefinition()->getArguments();
 
@@ -59,7 +60,9 @@ public function testShouldExecuteDefaultConsumption()
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
 
-        $command = new ConsumeCommand(new ArrayQueueConsumerRegistry(['default' => $consumer]));
+        $command = new ConsumeCommand(new Container([
+            'enqueue.transport.default.queue_consumer' => $consumer,
+        ]));
 
         $tester = new CommandTester($command);
         $tester->execute([]);
@@ -80,9 +83,9 @@ public function testShouldExecuteCustomConsumption()
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
 
-        $command = new ConsumeCommand(new ArrayQueueConsumerRegistry([
-            'default' => $defaultConsumer,
-            'custom' => $customConsumer,
+        $command = new ConsumeCommand(new Container([
+            'enqueue.transport.default.queue_consumer' => $defaultConsumer,
+            'enqueue.transport.custom.queue_consumer' => $customConsumer,
         ]));
 
         $tester = new CommandTester($command);
@@ -97,14 +100,14 @@ public function testThrowIfNotDefinedTransportRequested()
             ->method('consume')
         ;
 
-        $command = new ConsumeCommand(new ArrayQueueConsumerRegistry([
-            'default' => $defaultConsumer,
+        $command = new ConsumeCommand(new Container([
+            'enqueue.transport.default.queue_consumer' => $defaultConsumer,
         ]));
 
         $tester = new CommandTester($command);
 
         $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('QueueConsumer was not found, name: "not-defined".');
+        $this->expectExceptionMessage('Transport "not-defined" is not supported.');
         $tester->execute(['--transport' => 'not-defined']);
     }
 
diff --git a/Tests/Symfony/Consumption/ContainerQueueConsumerRegistryTest.php b/Tests/Symfony/Consumption/ContainerQueueConsumerRegistryTest.php
deleted file mode 100644
index e0dcdc7..0000000
--- a/Tests/Symfony/Consumption/ContainerQueueConsumerRegistryTest.php
+++ /dev/null
@@ -1,108 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Symfony\Consumption;
-
-use Enqueue\Consumption\QueueConsumerInterface;
-use Enqueue\Consumption\QueueConsumerRegistryInterface;
-use Enqueue\Symfony\Consumption\ContainerQueueConsumerRegistry;
-use Enqueue\Test\ClassExtensionTrait;
-use PHPUnit\Framework\TestCase;
-use Psr\Container\ContainerInterface;
-
-class ContainerQueueConsumerRegistryTest extends TestCase
-{
-    use ClassExtensionTrait;
-
-    public function testShouldImplementQueueConsumerRegistryInterface()
-    {
-        $this->assertClassImplements(QueueConsumerRegistryInterface::class, ContainerQueueConsumerRegistry::class);
-    }
-
-    public function testShouldBeFinal()
-    {
-        $this->assertClassFinal(ContainerQueueConsumerRegistry::class);
-    }
-
-    public function testCouldBeConstructedWithContainerAsFirstArgument()
-    {
-        new ContainerQueueConsumerRegistry($this->createContainerMock());
-    }
-
-    public function testShouldAllowGetQueueConsumer()
-    {
-        $queueConsumerMock = $this->createQueueConsumerMock();
-
-        $containerMock = $this->createContainerMock();
-        $containerMock
-            ->expects($this->once())
-            ->method('has')
-            ->with('queueConsumer-name')
-            ->willReturn(true)
-        ;
-        $containerMock
-            ->expects($this->once())
-            ->method('get')
-            ->with('queueConsumer-name')
-            ->willReturn($queueConsumerMock)
-        ;
-
-        $registry = new ContainerQueueConsumerRegistry($containerMock);
-        $this->assertSame($queueConsumerMock, $registry->get('queueConsumer-name'));
-    }
-
-    public function testThrowErrorIfServiceDoesNotImplementQueueConsumerReturnType()
-    {
-        $containerMock = $this->createContainerMock();
-        $containerMock
-            ->expects($this->once())
-            ->method('has')
-            ->with('queueConsumer-name')
-            ->willReturn(true)
-        ;
-        $containerMock
-            ->expects($this->once())
-            ->method('get')
-            ->with('queueConsumer-name')
-            ->willReturn(new \stdClass())
-        ;
-
-        $registry = new ContainerQueueConsumerRegistry($containerMock);
-
-        $this->expectException(\TypeError::class);
-        $this->expectExceptionMessage('Return value of Enqueue\Symfony\Consumption\ContainerQueueConsumerRegistry::get() must implement interface Enqueue\Consumption\QueueConsumerInterface, instance of stdClass returned');
-        $registry->get('queueConsumer-name');
-    }
-
-    public function testShouldThrowExceptionIfQueueConsumerIsNotSet()
-    {
-        $containerMock = $this->createContainerMock();
-        $containerMock
-            ->expects($this->once())
-            ->method('has')
-            ->with('queueConsumer-name')
-            ->willReturn(false)
-        ;
-
-        $registry = new ContainerQueueConsumerRegistry($containerMock);
-
-        $this->expectException(\LogicException::class);
-        $this->expectExceptionMessage('Service locator does not have a queue consumer with name "queueConsumer-name".');
-        $registry->get('queueConsumer-name');
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
-     */
-    private function createQueueConsumerMock(): QueueConsumerInterface
-    {
-        return $this->createMock(QueueConsumerInterface::class);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
-     */
-    private function createContainerMock(): ContainerInterface
-    {
-        return $this->createMock(ContainerInterface::class);
-    }
-}
diff --git a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index 8c2cc32..57015b7 100644
--- a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -249,7 +249,7 @@ public function testShouldCreateConnectionFactoryFromDSN()
 
         $transport = new TransportFactory('default');
 
-        $serviceId = $transport->createConnectionFactory($container, ['dsn' => 'foo://bar/baz']);
+        $serviceId = $transport->build($container, ['dsn' => 'foo://bar/baz']);
 
         $this->assertEquals('enqueue.transport.default.connection_factory', $serviceId);
 
@@ -272,7 +272,7 @@ public function testShouldCreateConnectionFactoryUsingCustomFactoryClass()
 
         $transport = new TransportFactory('default');
 
-        $serviceId = $transport->createConnectionFactory($container, ['dsn' => 'foo:', 'factory_class' => 'theFactoryClass']);
+        $serviceId = $transport->build($container, ['dsn' => 'foo:', 'factory_class' => 'theFactoryClass']);
 
         $this->assertEquals('enqueue.transport.default.connection_factory', $serviceId);
 
@@ -301,7 +301,7 @@ public function testShouldCreateConnectionFactoryUsingCustomFactoryService()
 
         $transport = new TransportFactory('default');
 
-        $serviceId = $transport->createConnectionFactory($container, ['dsn' => 'foo:', 'factory_service' => 'theFactoryService']);
+        $serviceId = $transport->build($container, ['dsn' => 'foo:', 'factory_service' => 'theFactoryService']);
 
         $this->assertEquals('enqueue.transport.default.connection_factory', $serviceId);
 
@@ -324,7 +324,7 @@ public function testShouldCreateConnectionFactoryUsingConnectionFactoryClassWith
 
         $transport = new TransportFactory('default');
 
-        $serviceId = $transport->createConnectionFactory($container, ['dsn' => 'foo:', 'connection_factory_class' => 'theFactoryClass']);
+        $serviceId = $transport->build($container, ['dsn' => 'foo:', 'connection_factory_class' => 'theFactoryClass']);
 
         $this->assertEquals('enqueue.transport.default.connection_factory', $serviceId);
 
@@ -344,7 +344,7 @@ public function testShouldCreateContextFromDsn()
 
         $transport = new TransportFactory('default');
 
-        $serviceId = $transport->createContext($container, ['dsn' => 'foo://bar/baz']);
+        $serviceId = $transport->buildContext($container, ['dsn' => 'foo://bar/baz']);
 
         $this->assertEquals('enqueue.transport.default.context', $serviceId);
 

From e0e5d87e9ca2a17dfbe0605873790ef1daa0a46f Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 4 Oct 2018 13:58:47 +0300
Subject: [PATCH 144/337] Make cli commands multi transport\client.

---
 ...MessagesCommand.php => ConsumeCommand.php} |  97 +++++--
 Symfony/Client/ProduceCommand.php             |  83 ++++++
 Symfony/Client/ProduceMessageCommand.php      |  55 ----
 Symfony/Client/RoutesCommand.php              |  47 +++-
 Symfony/Client/SetupBrokerCommand.php         |  46 ++--
 .../QueueConsumerOptionsCommandTrait.php      |   4 +-
 .../FormatClientNameTrait.php                 |  17 ++
 .../DependencyInjection/TransportFactory.php  | 107 +++++---
 ...CommandTest.php => ConsumeCommandTest.php} | 219 ++++++++++-----
 Tests/Symfony/Client/ProduceCommandTest.php   | 253 ++++++++++++++++++
 .../Client/ProduceMessageCommandTest.php      |  75 ------
 Tests/Symfony/Client/RoutesCommandTest.php    | 123 ++++++++-
 .../Symfony/Client/SetupBrokerCommandTest.php |  85 +++++-
 .../ConfigurableConsumeCommandTest.php        |  10 +-
 .../Consumption/ConsumeCommandTest.php        |  11 +-
 .../QueueConsumerOptionsCommandTraitTest.php  |   4 +-
 .../TransportFactoryTest.php                  | 158 ++++++++---
 composer.json                                 |   3 +-
 18 files changed, 1020 insertions(+), 377 deletions(-)
 rename Symfony/Client/{ConsumeMessagesCommand.php => ConsumeCommand.php} (51%)
 create mode 100644 Symfony/Client/ProduceCommand.php
 delete mode 100644 Symfony/Client/ProduceMessageCommand.php
 create mode 100644 Symfony/DependencyInjection/FormatClientNameTrait.php
 rename Tests/Symfony/Client/{ConsumeMessagesCommandTest.php => ConsumeCommandTest.php} (65%)
 create mode 100644 Tests/Symfony/Client/ProduceCommandTest.php
 delete mode 100644 Tests/Symfony/Client/ProduceMessageCommandTest.php

diff --git a/Symfony/Client/ConsumeMessagesCommand.php b/Symfony/Client/ConsumeCommand.php
similarity index 51%
rename from Symfony/Client/ConsumeMessagesCommand.php
rename to Symfony/Client/ConsumeCommand.php
index 7278937..d8b4efb 100644
--- a/Symfony/Client/ConsumeMessagesCommand.php
+++ b/Symfony/Client/ConsumeCommand.php
@@ -2,14 +2,17 @@
 
 namespace Enqueue\Symfony\Client;
 
-use Enqueue\Client\DelegateProcessor;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Extension\LoggerExtension;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Consumption\QueueConsumerInterface;
+use Enqueue\Symfony\Consumption\ChooseLoggerCommandTrait;
 use Enqueue\Symfony\Consumption\LimitsExtensionsCommandTrait;
 use Enqueue\Symfony\Consumption\QueueConsumerOptionsCommandTrait;
+use Interop\Queue\Processor;
+use Psr\Container\ContainerInterface;
+use Psr\Container\NotFoundExceptionInterface;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputArgument;
 use Symfony\Component\Console\Input\InputInterface;
@@ -17,39 +20,47 @@
 use Symfony\Component\Console\Logger\ConsoleLogger;
 use Symfony\Component\Console\Output\OutputInterface;
 
-class ConsumeMessagesCommand extends Command
+class ConsumeCommand extends Command
 {
     use LimitsExtensionsCommandTrait;
     use SetupBrokerExtensionCommandTrait;
     use QueueConsumerOptionsCommandTrait;
+    use ChooseLoggerCommandTrait;
 
     protected static $defaultName = 'enqueue:consume';
 
     /**
-     * @var QueueConsumerInterface
+     * @var ContainerInterface
      */
-    private $consumer;
+    private $container;
 
     /**
-     * @var DelegateProcessor
+     * @var string
      */
-    private $processor;
+    private $queueConsumerIdPattern;
 
     /**
-     * @var DriverInterface
+     * @var string
      */
-    private $driver;
+    private $driverIdPattern;
+
+    /**
+     * @var string
+     */
+    private $processorIdPattern;
 
     public function __construct(
-        QueueConsumerInterface $consumer,
-        DelegateProcessor $processor,
-        DriverInterface $driver
+        ContainerInterface $container,
+        string $queueConsumerIdPattern = 'enqueue.client.%s.queue_consumer',
+        string $driverIdPattern = 'enqueue.client.%s.driver',
+        string $processorIdPatter = 'enqueue.client.%s.delegate_processor'
     ) {
-        parent::__construct(static::$defaultName);
+        parent::__construct(self::$defaultName);
 
-        $this->consumer = $consumer;
-        $this->processor = $processor;
-        $this->driver = $driver;
+        $this->container = $container;
+        $this->queueConsumerIdPattern = $queueConsumerIdPattern;
+        $this->driverIdPattern = $driverIdPattern;
+        $this->processorIdPattern = $processorIdPatter;
     }
 
     protected function configure(): void
@@ -57,6 +68,7 @@ protected function configure(): void
         $this->configureLimitsExtensions();
         $this->configureSetupBrokerExtension();
         $this->configureQueueConsumerOptions();
+        $this->configureLoggerExtension();
 
         $this
             ->setAliases(['enq:c'])
@@ -65,19 +77,31 @@ protected function configure(): void
                 'It select an appropriate message processor based on a message headers')
             ->addArgument('client-queue-names', InputArgument::IS_ARRAY, 'Queues to consume messages from')
             ->addOption('skip', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Queues to skip consumption of messages from', [])
+            ->addOption('client', 'c', InputOption::VALUE_OPTIONAL, 'The client to consume messages from.', 'default')
         ;
     }
 
     protected function execute(InputInterface $input, OutputInterface $output): ?int
     {
-        $this->setQueueConsumerOptions($this->consumer, $input);
+        $client = $input->getOption('client');
+
+        try {
+            $consumer = $this->getQueueConsumer($client);
+        } catch (NotFoundExceptionInterface $e) {
+            throw new \LogicException(sprintf('Client "%s" is not supported.', $client), null, $e);
+        }
+
+        $driver = $this->getDriver($client);
+        $processor = $this->getProcessor($client);
+
+        $this->setQueueConsumerOptions($consumer, $input);
 
         $clientQueueNames = $input->getArgument('client-queue-names');
         if (empty($clientQueueNames)) {
-            $clientQueueNames[$this->driver->getConfig()->getDefaultQueue()] = true;
-            $clientQueueNames[$this->driver->getConfig()->getRouterQueue()] = true;
+            $clientQueueNames[$driver->getConfig()->getDefaultQueue()] = true;
+            $clientQueueNames[$driver->getConfig()->getRouterQueue()] = true;
 
-            foreach ($this->driver->getRouteCollection()->all() as $route) {
+            foreach ($driver->getRouteCollection()->all() as $route) {
                 if ($route->getQueue()) {
                     $clientQueueNames[$route->getQueue()] = true;
                 }
@@ -91,30 +115,45 @@ protected function execute(InputInterface $input, OutputInterface $output): ?int
         }
 
         foreach ($clientQueueNames as $clientQueueName) {
-            $queue = $this->driver->createQueue($clientQueueName);
-            $this->consumer->bind($queue, $this->processor);
+            $queue = $driver->createQueue($clientQueueName);
+            $consumer->bind($queue, $processor);
         }
 
-        $this->consumer->consume($this->getRuntimeExtensions($input, $output));
+        $consumer->consume($this->getRuntimeExtensions($input, $output));
 
         return null;
     }
 
-    /**
-     * @param InputInterface  $input
-     * @param OutputInterface $output
-     *
-     * @return ChainExtension
-     */
     protected function getRuntimeExtensions(InputInterface $input, OutputInterface $output): ExtensionInterface
     {
         $extensions = [new LoggerExtension(new ConsoleLogger($output))];
         $extensions = array_merge($extensions, $this->getLimitsExtensions($input, $output));
 
-        if ($setupBrokerExtension = $this->getSetupBrokerExtension($input, $this->driver)) {
+        $driver = $this->getDriver($input->getOption('client'));
+
+        if ($setupBrokerExtension = $this->getSetupBrokerExtension($input, $driver)) {
             $extensions[] = $setupBrokerExtension;
         }
 
+        if ($loggerExtension = $this->getLoggerExtension($input, $output)) {
+            array_unshift($extensions, $loggerExtension);
+        }
+
         return new ChainExtension($extensions);
     }
+
+    private function getDriver(string $name): DriverInterface
+    {
+        return $this->container->get(sprintf($this->driverIdPattern, $name));
+    }
+
+    private function getQueueConsumer(string $name): QueueConsumerInterface
+    {
+        return $this->container->get(sprintf($this->queueConsumerIdPattern, $name));
+    }
+
+    private function getProcessor(string $name): Processor
+    {
+        return $this->container->get(sprintf($this->processorIdPattern, $name));
+    }
 }
diff --git a/Symfony/Client/ProduceCommand.php b/Symfony/Client/ProduceCommand.php
new file mode 100644
index 0000000..302b344
--- /dev/null
+++ b/Symfony/Client/ProduceCommand.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Enqueue\Symfony\Client;
+
+use Enqueue\Client\ProducerInterface;
+use Psr\Container\ContainerInterface;
+use Psr\Container\NotFoundExceptionInterface;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ProduceCommand extends Command
+{
+    protected static $defaultName = 'enqueue:produce';
+
+    /**
+     * @var ContainerInterface
+     */
+    private $container;
+
+    /**
+     * @var string
+     */
+    private $producerIdPattern;
+
+    public function __construct(ContainerInterface $container, string $producerIdPattern = 'enqueue.client.%s.producer')
+    {
+        parent::__construct(static::$defaultName);
+
+        $this->container = $container;
+        $this->producerIdPattern = $producerIdPattern;
+    }
+
+    protected function configure(): void
+    {
+        $this
+            ->setDescription('Sends an event to the topic')
+            ->addArgument('message', InputArgument::REQUIRED, 'A message')
+            ->addOption('client', 'c', InputOption::VALUE_OPTIONAL, 'The client to consume messages from.', 'default')
+            ->addOption('topic', null, InputOption::VALUE_OPTIONAL, 'The topic to send a message to')
+            ->addOption('command', null, InputOption::VALUE_OPTIONAL, 'The command to send a message to')
+        ;
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output): ?int
+    {
+        $topic = $input->getOption('topic');
+        $command = $input->getOption('command');
+        $message = $input->getArgument('message');
+        $client = $input->getOption('client');
+
+        if ($topic && $command) {
+            throw new \LogicException('Either topic or command option should be set, both are set.');
+        }
+
+        try {
+            $producer = $this->getProducer($client);
+        } catch (NotFoundExceptionInterface $e) {
+            throw new \LogicException(sprintf('Client "%s" is not supported.', $client), null, $e);
+        }
+
+        if ($topic) {
+            $producer->sendEvent($topic, $message);
+
+            $output->writeln('An event is sent');
+        } elseif ($command) {
+            $producer->sendCommand($command, $message);
+
+            $output->writeln('A command is sent');
+        } else {
+            throw new \LogicException('Either topic or command option should be set, none is set.');
+        }
+
+        return null;
+    }
+
+    private function getProducer(string $client): ProducerInterface
+    {
+        return $this->container->get(sprintf($this->producerIdPattern, $client));
+    }
+}
diff --git a/Symfony/Client/ProduceMessageCommand.php b/Symfony/Client/ProduceMessageCommand.php
deleted file mode 100644
index 5e83903..0000000
--- a/Symfony/Client/ProduceMessageCommand.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-
-namespace Enqueue\Symfony\Client;
-
-use Enqueue\Client\ProducerInterface;
-use Symfony\Component\Console\Command\Command;
-use Symfony\Component\Console\Input\InputArgument;
-use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Output\OutputInterface;
-
-class ProduceMessageCommand extends Command
-{
-    protected static $defaultName = 'enqueue:produce';
-
-    /**
-     * @var ProducerInterface
-     */
-    private $producer;
-
-    /**
-     * @param ProducerInterface $producer
-     */
-    public function __construct(ProducerInterface $producer)
-    {
-        parent::__construct(static::$defaultName);
-
-        $this->producer = $producer;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function configure()
-    {
-        $this
-            ->setAliases(['enq:p'])
-            ->setDescription('A command to send a message to topic')
-            ->addArgument('topic', InputArgument::REQUIRED, 'A topic to send message to')
-            ->addArgument('message', InputArgument::REQUIRED, 'A message to send')
-        ;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function execute(InputInterface $input, OutputInterface $output)
-    {
-        $this->producer->sendEvent(
-            $input->getArgument('topic'),
-            $input->getArgument('message')
-        );
-
-        $output->writeln('Message is sent');
-    }
-}
diff --git a/Symfony/Client/RoutesCommand.php b/Symfony/Client/RoutesCommand.php
index 132fd1b..605d244 100644
--- a/Symfony/Client/RoutesCommand.php
+++ b/Symfony/Client/RoutesCommand.php
@@ -2,9 +2,10 @@
 
 namespace Enqueue\Symfony\Client;
 
-use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Route;
-use Enqueue\Client\RouteCollection;
+use Psr\Container\ContainerInterface;
+use Psr\Container\NotFoundExceptionInterface;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Helper\Table;
 use Symfony\Component\Console\Helper\TableSeparator;
@@ -17,21 +18,26 @@ final class RoutesCommand extends Command
     protected static $defaultName = 'enqueue:routes';
 
     /**
-     * @var Config
+     * @var ContainerInterface
      */
-    private $config;
+    private $container;
 
     /**
-     * @var RouteCollection
+     * @var string
      */
-    private $routeCollection;
+    private $driverIdPatter;
 
-    public function __construct(Config $config, RouteCollection $routeCollection)
+    /**
+     * @var DriverInterface
+     */
+    private $driver;
+
+    public function __construct(ContainerInterface $container, string $driverIdPatter = 'enqueue.client.%s.driver')
     {
         parent::__construct(static::$defaultName);
 
-        $this->config = $config;
-        $this->routeCollection = $routeCollection;
+        $this->container = $container;
+        $this->driverIdPatter = $driverIdPatter;
     }
 
     protected function configure(): void
@@ -39,12 +45,22 @@ protected function configure(): void
         $this
             ->setAliases(['debug:enqueue:routes'])
             ->setDescription('A command lists all registered routes.')
-            ->addOption('show-route-options', null, InputOption::VALUE_NONE, 'Adds ability to hide options.');
+            ->addOption('show-route-options', null, InputOption::VALUE_NONE, 'Adds ability to hide options.')
+            ->addOption('client', 'c', InputOption::VALUE_OPTIONAL, 'The client to consume messages from.', 'default')
+        ;
+
+        $this->driver = null;
     }
 
     protected function execute(InputInterface $input, OutputInterface $output): ?int
     {
-        $routes = $this->routeCollection->all();
+        try {
+            $this->driver = $this->getDriver($input->getOption('client'));
+        } catch (NotFoundExceptionInterface $e) {
+            throw new \LogicException(sprintf('Client "%s" is not supported.', $input->getOption('client')), null, $e);
+        }
+
+        $routes = $this->driver->getRouteCollection()->all();
         $output->writeln(sprintf('Found %s routes', count($routes)));
         $output->writeln('');
 
@@ -73,7 +89,7 @@ protected function execute(InputInterface $input, OutputInterface $output): ?int
                 ]);
             }
 
-            foreach ($this->routeCollection->all() as $route) {
+            foreach ($routes as $route) {
                 if ($route->isTopic()) {
                     continue;
                 }
@@ -119,7 +135,7 @@ private function formatProcessor(Route $route): string
 
     private function formatQueue(Route $route): string
     {
-        $queue = $route->getQueue() ?: $this->config->getDefaultQueue();
+        $queue = $route->getQueue() ?: $this->driver->getConfig()->getDefaultQueue();
 
         return $route->isPrefixQueue() ? $queue.' (prefixed)' : $queue.' (as is)';
     }
@@ -128,4 +144,9 @@ private function formatOptions(Route $route): string
     {
         return var_export($route->getOptions(), true);
     }
+
+    private function getDriver(string $client): DriverInterface
+    {
+        return $this->container->get(sprintf($this->driverIdPatter, $client));
+    }
 }
diff --git a/Symfony/Client/SetupBrokerCommand.php b/Symfony/Client/SetupBrokerCommand.php
index 9bd8ee6..4a4f322 100644
--- a/Symfony/Client/SetupBrokerCommand.php
+++ b/Symfony/Client/SetupBrokerCommand.php
@@ -3,8 +3,11 @@
 namespace Enqueue\Symfony\Client;
 
 use Enqueue\Client\DriverInterface;
+use Psr\Container\ContainerInterface;
+use Psr\Container\NotFoundExceptionInterface;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Logger\ConsoleLogger;
 use Symfony\Component\Console\Output\OutputInterface;
 
@@ -13,38 +16,49 @@ class SetupBrokerCommand extends Command
     protected static $defaultName = 'enqueue:setup-broker';
 
     /**
-     * @var DriverInterface
+     * @var ContainerInterface
      */
-    private $driver;
+    private $container;
 
     /**
-     * @param DriverInterface $driver
+     * @var string
      */
-    public function __construct(DriverInterface $driver)
+    private $driverIdPattern;
+
+    public function __construct(ContainerInterface $container, string $driverIdPattern = 'enqueue.client.%s.driver')
     {
         parent::__construct(static::$defaultName);
 
-        $this->driver = $driver;
+        $this->container = $container;
+        $this->driverIdPattern = $driverIdPattern;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    protected function configure()
+    protected function configure(): void
     {
         $this
             ->setAliases(['enq:sb'])
-            ->setDescription('Creates all required queues')
+            ->setDescription('Setup broker. Configure the broker, creates queues, topics and so on.')
+            ->addOption('client', 'c', InputOption::VALUE_OPTIONAL, 'The client to consume messages from.', 'default')
         ;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): ?int
     {
-        $output->writeln('Setup Broker');
+        $client = $input->getOption('client');
 
-        $this->driver->setupBroker(new ConsoleLogger($output));
+        try {
+            $this->getDriver($client)->setupBroker(new ConsoleLogger($output));
+        } catch (NotFoundExceptionInterface $e) {
+            throw new \LogicException(sprintf('Client "%s" is not supported.', $client), null, $e);
+        }
+
+        $output->writeln('Broker set up');
+
+        return null;
+    }
+
+    private function getDriver(string $client): DriverInterface
+    {
+        return $this->container->get(sprintf($this->driverIdPattern, $client));
     }
 }
diff --git a/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php b/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php
index 00f75f0..c20e129 100644
--- a/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php
+++ b/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php
@@ -14,7 +14,7 @@ trait QueueConsumerOptionsCommandTrait
     protected function configureQueueConsumerOptions()
     {
         $this
-            ->addOption('idle-timeout', null, InputOption::VALUE_REQUIRED, 'The time in milliseconds queue consumer idle if no message has been received.')
+            ->addOption('idle-time', null, InputOption::VALUE_REQUIRED, 'The time in milliseconds queue consumer idle if no message has been received.')
             ->addOption('receive-timeout', null, InputOption::VALUE_REQUIRED, 'The time in milliseconds queue consumer waits for a message.')
         ;
     }
@@ -25,7 +25,7 @@ protected function configureQueueConsumerOptions()
      */
     protected function setQueueConsumerOptions(QueueConsumerInterface $consumer, InputInterface $input)
     {
-        if (null !== $idleTimeout = $input->getOption('idle-timeout')) {
+        if (null !== $idleTimeout = $input->getOption('idle-time')) {
             $consumer->setIdleTimeout((float) $idleTimeout);
         }
 
diff --git a/Symfony/DependencyInjection/FormatClientNameTrait.php b/Symfony/DependencyInjection/FormatClientNameTrait.php
new file mode 100644
index 0000000..8065fc3
--- /dev/null
+++ b/Symfony/DependencyInjection/FormatClientNameTrait.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Enqueue\Symfony\DependencyInjection;
+
+trait FormatClientNameTrait
+{
+    abstract protected function getName(): string;
+
+    private function format(string $serviceName, $parameter = false): string
+    {
+        $pattern = 'enqueue.client.%s.'.$serviceName;
+
+        $fullName = sprintf($pattern, $this->getName());
+
+        return $parameter ? "%$fullName%" : $fullName;
+    }
+}
diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index 029904a..12cc5fa 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -38,7 +38,7 @@ public function __construct(string $name)
         $this->name = $name;
     }
 
-    public function addConfiguration(ArrayNodeDefinition $builder): void
+    public function addTransportConfiguration(ArrayNodeDefinition $builder): void
     {
         $knownSchemes = array_keys(Resources::getKnownSchemes());
         $availableSchemes = array_keys(Resources::getAvailableSchemes());
@@ -94,32 +94,21 @@ public function addConfiguration(ArrayNodeDefinition $builder): void
         ;
     }
 
-    public function build(ContainerBuilder $container, array $config): void
+    public function addQueueConsumerConfiguration(ArrayNodeDefinition $builder): void
     {
-        $this->buildConnectionFactory($container, $config);
-        $this->buildContext($container, $config);
-        $this->buildQueueConsumer($container, $config);
-        $this->buildRpcClient($container, $config);
-
-        $container->register($this->format('processor_registry'), ContainerProcessorRegistry::class);
-
-        $locator = 'enqueue.locator';
-        if ($container->hasDefinition($locator)) {
-            $locator = $container->getDefinition($locator);
-            $locator->replaceArgument(0, array_merge($locator->getArgument(0), [
-                $this->format('queue_consumer') => new Reference($this->format('queue_consumer')),
-                $this->format('processor_registry') => new Reference($this->format('processor_registry')),
-            ]));
-
-            return;
-        }
-
-        if ('default' === $this->name) {
-            $container->setAlias(RpcClient::class, $this->format('rpc_client'));
-            $container->setAlias(ConnectionFactory::class, $this->format('connection_factory'));
-            $container->setAlias(Context::class, $this->format('context'));
-            $container->setAlias(QueueConsumerInterface::class, $this->format('queue_consumer'));
-        }
+        $builder
+            ->addDefaultsIfNotSet()->children()
+                ->integerNode('idle_time')
+                    ->min(0)
+                    ->defaultValue(0)
+                    ->info('the time in milliseconds queue consumer waits if no message received')
+                ->end()
+                ->integerNode('receive_timeout')
+                    ->min(0)
+                    ->defaultValue(10000)
+                    ->info('the time in milliseconds queue consumer waits for a message (100 ms by default)')
+                ->end()
+        ;
     }
 
     public function getName(): string
@@ -127,7 +116,7 @@ public function getName(): string
         return $this->name;
     }
 
-    private function buildConnectionFactory(ContainerBuilder $container, array $config): void
+    public function buildConnectionFactory(ContainerBuilder $container, array $config): void
     {
         $factoryId = $this->format('connection_factory');
 
@@ -153,44 +142,86 @@ private function buildConnectionFactory(ContainerBuilder $container, array $conf
                 ->addArgument($config)
             ;
         }
+
+        if ('default' === $this->name) {
+            $container->setAlias(ConnectionFactory::class, $this->format('connection_factory'));
+        }
     }
 
-    private function buildContext(ContainerBuilder $container, array $config): void
+    public function buildContext(ContainerBuilder $container, array $config): void
     {
-        $contextId = $this->format('context');
         $factoryId = $this->format('connection_factory');
+        $this->assertServiceExists($container, $factoryId);
+
+        $contextId = $this->format('context');
 
         $container->register($contextId, Context::class)
             ->setFactory([new Reference($factoryId), 'createContext'])
         ;
+
+        if ('default' === $this->name) {
+            $container->setAlias(Context::class, $this->format('context'));
+        }
     }
 
-    private function buildQueueConsumer(ContainerBuilder $container, array $config): void
+    public function buildQueueConsumer(ContainerBuilder $container, array $config): void
     {
-        $container->setParameter($this->format('default_idle_time'), 0);
-        $container->setParameter($this->format('default_receive_timeout'), 10000);
+        $contextId = $this->format('context');
+        $this->assertServiceExists($container, $contextId);
+
+        $container->setParameter($this->format('idle_time'), $config['idle_time'] ?? 0);
+        $container->setParameter($this->format('receive_timeout'), $config['receive_timeout'] ?? 10000);
 
         $container->register($this->format('consumption_extensions'), ChainExtension::class)
             ->addArgument([])
         ;
 
         $container->register($this->format('queue_consumer'), QueueConsumer::class)
-            ->addArgument(new Reference($this->format('context')))
+            ->addArgument(new Reference($contextId))
             ->addArgument(new Reference($this->format('consumption_extensions')))
-            ->addArgument($this->format('default_idle_time', true))
-            ->addArgument($this->format('default_receive_timeout', true))
+            ->addArgument($this->format('idle_time', true))
+            ->addArgument($this->format('receive_timeout', true))
         ;
+
+        $container->register($this->format('processor_registry'), ContainerProcessorRegistry::class);
+
+        $locatorId = 'enqueue.locator';
+        if ($container->hasDefinition($locatorId)) {
+            $locator = $container->getDefinition($locatorId);
+            $locator->replaceArgument(0, array_replace($locator->getArgument(0), [
+                $this->format('queue_consumer') => new Reference($this->format('queue_consumer')),
+                $this->format('processor_registry') => new Reference($this->format('processor_registry')),
+            ]));
+        }
+
+        if ('default' === $this->name) {
+            $container->setAlias(QueueConsumerInterface::class, $this->format('queue_consumer'));
+        }
     }
 
-    private function buildRpcClient(ContainerBuilder $container, array $config): void
+    public function buildRpcClient(ContainerBuilder $container, array $config): void
     {
+        $contextId = $this->format('context');
+        $this->assertServiceExists($container, $contextId);
+
         $container->register($this->format('rpc_factory'), RpcFactory::class)
-            ->addArgument(new Reference($this->format('context')))
+            ->addArgument(new Reference($contextId))
         ;
 
         $container->register($this->format('rpc_client'), RpcClient::class)
-            ->addArgument(new Reference($this->format('context')))
+            ->addArgument(new Reference($contextId))
             ->addArgument(new Reference($this->format('rpc_factory')))
         ;
+
+        if ('default' === $this->name) {
+            $container->setAlias(RpcClient::class, $this->format('rpc_client'));
+        }
+    }
+
+    private function assertServiceExists(ContainerBuilder $container, string $serviceId): void
+    {
+        if (false == $container->hasDefinition($serviceId)) {
+            throw new \InvalidArgumentException(sprintf('The service "%s" does not exist.', $serviceId));
+        }
     }
 }
diff --git a/Tests/Symfony/Client/ConsumeMessagesCommandTest.php b/Tests/Symfony/Client/ConsumeCommandTest.php
similarity index 65%
rename from Tests/Symfony/Client/ConsumeMessagesCommandTest.php
rename to Tests/Symfony/Client/ConsumeCommandTest.php
index a33905a..54e15ed 100644
--- a/Tests/Symfony/Client/ConsumeMessagesCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeCommandTest.php
@@ -9,73 +9,49 @@
 use Enqueue\Client\RouteCollection;
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\QueueConsumerInterface;
+use Enqueue\Container\Container;
 use Enqueue\Null\NullQueue;
-use Enqueue\Symfony\Client\ConsumeMessagesCommand;
-use Interop\Queue\Context;
+use Enqueue\Symfony\Client\ConsumeCommand;
 use PHPUnit\Framework\TestCase;
+use Psr\Container\ContainerInterface;
 use Symfony\Component\Console\Tester\CommandTester;
 
-class ConsumeMessagesCommandTest extends TestCase
+class ConsumeCommandTest extends TestCase
 {
     public function testCouldBeConstructedWithRequiredAttributes()
     {
-        new ConsumeMessagesCommand(
-            $this->createQueueConsumerMock(),
-            $this->createDelegateProcessorMock(),
-            $this->createDriverStub()
-        );
+        new ConsumeCommand($this->createMock(ContainerInterface::class));
     }
 
     public function testShouldHaveCommandName()
     {
-        $command = new ConsumeMessagesCommand(
-            $this->createQueueConsumerMock(),
-            $this->createDelegateProcessorMock(),
-            $this->createDriverStub()
-        );
+        $command = new ConsumeCommand($this->createMock(ContainerInterface::class));
 
         $this->assertEquals('enqueue:consume', $command->getName());
     }
 
-    public function testShouldHaveCommandAliases()
-    {
-        $command = new ConsumeMessagesCommand(
-            $this->createQueueConsumerMock(),
-            $this->createDelegateProcessorMock(),
-            $this->createDriverStub()
-        );
-
-        $this->assertEquals(['enq:c'], $command->getAliases());
-    }
-
     public function testShouldHaveExpectedOptions()
     {
-        $command = new ConsumeMessagesCommand(
-            $this->createQueueConsumerMock(),
-            $this->createDelegateProcessorMock(),
-            $this->createDriverStub()
-        );
+        $command = new ConsumeCommand($this->createMock(ContainerInterface::class));
 
         $options = $command->getDefinition()->getOptions();
 
-        $this->assertCount(8, $options);
+        $this->assertCount(10, $options);
         $this->assertArrayHasKey('memory-limit', $options);
         $this->assertArrayHasKey('message-limit', $options);
         $this->assertArrayHasKey('time-limit', $options);
-        $this->assertArrayHasKey('setup-broker', $options);
-        $this->assertArrayHasKey('idle-timeout', $options);
+        $this->assertArrayHasKey('idle-time', $options);
         $this->assertArrayHasKey('receive-timeout', $options);
-        $this->assertArrayHasKey('skip', $options);
         $this->assertArrayHasKey('niceness', $options);
+        $this->assertArrayHasKey('client', $options);
+        $this->assertArrayHasKey('logger', $options);
+        $this->assertArrayHasKey('skip', $options);
+        $this->assertArrayHasKey('setup-broker', $options);
     }
 
-    public function testShouldHaveExpectedArguments()
+    public function testShouldHaveExpectedAttributes()
     {
-        $command = new ConsumeMessagesCommand(
-            $this->createQueueConsumerMock(),
-            $this->createDelegateProcessorMock(),
-            $this->createDriverStub()
-        );
+        $command = new ConsumeCommand($this->createMock(ContainerInterface::class));
 
         $arguments = $command->getDefinition()->getArguments();
 
@@ -91,12 +67,6 @@ public function testShouldBindDefaultQueueOnly()
 
         $processor = $this->createDelegateProcessorMock();
 
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->never())
-            ->method('close')
-        ;
-
         $consumer = $this->createQueueConsumerMock();
         $consumer
             ->expects($this->once())
@@ -117,28 +87,125 @@ public function testShouldBindDefaultQueueOnly()
             ->willReturn($queue)
         ;
 
-        $command = new ConsumeMessagesCommand($consumer, $processor, $driver);
+        $command = new ConsumeCommand(new Container([
+            'enqueue.client.default.queue_consumer' => $consumer,
+            'enqueue.client.default.driver' => $driver,
+            'enqueue.client.default.delegate_processor' => $processor,
+        ]));
 
         $tester = new CommandTester($command);
         $tester->execute([]);
     }
 
-    public function testShouldBindDefaultQueueIfRouteDoesNotDefineQueue()
+    public function testShouldUseRequestedClient()
     {
+        $defaultProcessor = $this->createDelegateProcessorMock();
+
+        $defaultConsumer = $this->createQueueConsumerMock();
+        $defaultConsumer
+            ->expects($this->never())
+            ->method('bind')
+        ;
+        $defaultConsumer
+            ->expects($this->never())
+            ->method('consume')
+            ->with($this->isInstanceOf(ChainExtension::class))
+        ;
+
+        $defaultDriver = $this->createDriverStub(new RouteCollection([]));
+        $defaultDriver
+            ->expects($this->never())
+            ->method('createQueue')
+        ;
+
         $queue = new NullQueue('');
 
-        $routeCollection = new RouteCollection([
-            new Route('topic', Route::TOPIC, 'processor'),
+        $routeCollection = new RouteCollection([]);
+
+        $fooProcessor = $this->createDelegateProcessorMock();
+
+        $fooConsumer = $this->createQueueConsumerMock();
+        $fooConsumer
+            ->expects($this->once())
+            ->method('bind')
+            ->with($this->identicalTo($queue), $this->identicalTo($fooProcessor))
+        ;
+        $fooConsumer
+            ->expects($this->once())
+            ->method('consume')
+            ->with($this->isInstanceOf(ChainExtension::class))
+        ;
+
+        $fooDriver = $this->createDriverStub($routeCollection);
+        $fooDriver
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('default')
+            ->willReturn($queue)
+        ;
+
+        $command = new ConsumeCommand(new Container([
+            'enqueue.client.default.queue_consumer' => $defaultConsumer,
+            'enqueue.client.default.driver' => $defaultDriver,
+            'enqueue.client.default.delegate_processor' => $defaultProcessor,
+            'enqueue.client.foo.queue_consumer' => $fooConsumer,
+            'enqueue.client.foo.driver' => $fooDriver,
+            'enqueue.client.foo.delegate_processor' => $fooProcessor,
+        ]));
+
+        $tester = new CommandTester($command);
+        $tester->execute([
+            '--client' => 'foo',
         ]);
+    }
+
+    public function testThrowIfNotDefinedClientRequested()
+    {
+        $routeCollection = new RouteCollection([]);
 
         $processor = $this->createDelegateProcessorMock();
 
-        $context = $this->createContextMock();
-        $context
+        $consumer = $this->createQueueConsumerMock();
+        $consumer
+            ->expects($this->never())
+            ->method('bind')
+        ;
+        $consumer
             ->expects($this->never())
-            ->method('close')
+            ->method('consume')
         ;
 
+        $driver = $this->createDriverStub($routeCollection);
+        $driver
+            ->expects($this->never())
+            ->method('createQueue')
+        ;
+
+        $command = new ConsumeCommand(new Container([
+            'enqueue.client.default.queue_consumer' => $consumer,
+            'enqueue.client.default.driver' => $driver,
+            'enqueue.client.default.delegate_processor' => $processor,
+        ]));
+
+        $tester = new CommandTester($command);
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Client "not-defined" is not supported.');
+        $tester->execute([
+            '--client' => 'not-defined',
+        ]);
+    }
+
+    public function testShouldBindDefaultQueueIfRouteUseDifferentQueue()
+    {
+        $queue = new NullQueue('');
+
+        $routeCollection = new RouteCollection([
+            new Route('topic', Route::TOPIC, 'processor'),
+        ]);
+
+        $processor = $this->createDelegateProcessorMock();
+
         $consumer = $this->createQueueConsumerMock();
         $consumer
             ->expects($this->once())
@@ -159,7 +226,11 @@ public function testShouldBindDefaultQueueIfRouteDoesNotDefineQueue()
             ->willReturn($queue)
         ;
 
-        $command = new ConsumeMessagesCommand($consumer, $processor, $driver);
+        $command = new ConsumeCommand(new Container([
+            'enqueue.client.default.queue_consumer' => $consumer,
+            'enqueue.client.default.driver' => $driver,
+            'enqueue.client.default.delegate_processor' => $processor,
+        ]));
 
         $tester = new CommandTester($command);
         $tester->execute([]);
@@ -207,7 +278,11 @@ public function testShouldBindCustomExecuteConsumptionAndUseCustomClientDestinat
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
 
-        $command = new ConsumeMessagesCommand($consumer, $processor, $driver);
+        $command = new ConsumeCommand(new Container([
+            'enqueue.client.default.queue_consumer' => $consumer,
+            'enqueue.client.default.driver' => $driver,
+            'enqueue.client.default.delegate_processor' => $processor,
+        ]));
 
         $tester = new CommandTester($command);
         $tester->execute([]);
@@ -243,7 +318,11 @@ public function testShouldBindUserProvidedQueues()
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
 
-        $command = new ConsumeMessagesCommand($consumer, $processor, $driver);
+        $command = new ConsumeCommand(new Container([
+            'enqueue.client.default.queue_consumer' => $consumer,
+            'enqueue.client.default.driver' => $driver,
+            'enqueue.client.default.delegate_processor' => $processor,
+        ]));
 
         $tester = new CommandTester($command);
         $tester->execute([
@@ -295,12 +374,14 @@ public function testShouldBindQueuesOnlyOnce()
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
 
-        $command = new ConsumeMessagesCommand($consumer, $processor, $driver);
+        $command = new ConsumeCommand(new Container([
+            'enqueue.client.default.queue_consumer' => $consumer,
+            'enqueue.client.default.driver' => $driver,
+            'enqueue.client.default.delegate_processor' => $processor,
+        ]));
 
         $tester = new CommandTester($command);
-        $tester->execute([
-//            'client-queue-names' => ['non-default-queue'],
-        ]);
+        $tester->execute([]);
     }
 
     public function testShouldSkipQueueConsumptionAndUseCustomClientDestinationName()
@@ -309,12 +390,6 @@ public function testShouldSkipQueueConsumptionAndUseCustomClientDestinationName(
 
         $processor = $this->createDelegateProcessorMock();
 
-        $context = $this->createContextMock();
-        $context
-            ->expects($this->never())
-            ->method('close')
-        ;
-
         $consumer = $this->createQueueConsumerMock();
         $consumer
             ->expects($this->exactly(3))
@@ -352,7 +427,11 @@ public function testShouldSkipQueueConsumptionAndUseCustomClientDestinationName(
             ->willReturn($queue)
         ;
 
-        $command = new ConsumeMessagesCommand($consumer, $processor, $driver);
+        $command = new ConsumeCommand(new Container([
+            'enqueue.client.default.queue_consumer' => $consumer,
+            'enqueue.client.default.driver' => $driver,
+            'enqueue.client.default.delegate_processor' => $processor,
+        ]));
 
         $tester = new CommandTester($command);
         $tester->execute([
@@ -360,14 +439,6 @@ public function testShouldSkipQueueConsumptionAndUseCustomClientDestinationName(
         ]);
     }
 
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Context
-     */
-    private function createContextMock()
-    {
-        return $this->createMock(Context::class);
-    }
-
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|DelegateProcessor
      */
diff --git a/Tests/Symfony/Client/ProduceCommandTest.php b/Tests/Symfony/Client/ProduceCommandTest.php
new file mode 100644
index 0000000..51756b4
--- /dev/null
+++ b/Tests/Symfony/Client/ProduceCommandTest.php
@@ -0,0 +1,253 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\Client;
+
+use Enqueue\Client\ProducerInterface;
+use Enqueue\Container\Container;
+use Enqueue\Symfony\Client\ProduceCommand;
+use PHPUnit\Framework\TestCase;
+use Psr\Container\ContainerInterface;
+use Symfony\Component\Console\Tester\CommandTester;
+
+class ProduceCommandTest extends TestCase
+{
+    public function testCouldBeConstructedWithContainerAsFirstArgument()
+    {
+        new ProduceCommand($this->createMock(ContainerInterface::class));
+    }
+
+    public function testShouldHaveCommandName()
+    {
+        $command = new ProduceCommand($this->createMock(ContainerInterface::class));
+
+        $this->assertEquals('enqueue:produce', $command->getName());
+    }
+
+    public function testShouldHaveExpectedOptions()
+    {
+        $command = new ProduceCommand($this->createMock(ContainerInterface::class));
+
+        $options = $command->getDefinition()->getOptions();
+        $this->assertCount(3, $options);
+        $this->assertArrayHasKey('client', $options);
+        $this->assertArrayHasKey('topic', $options);
+        $this->assertArrayHasKey('command', $options);
+    }
+
+    public function testShouldHaveExpectedAttributes()
+    {
+        $command = new ProduceCommand($this->createMock(ContainerInterface::class));
+
+        $arguments = $command->getDefinition()->getArguments();
+        $this->assertCount(1, $arguments);
+
+        $this->assertArrayHasKey('message', $arguments);
+    }
+
+    public function testThrowIfNeitherTopicNorCommandOptionsAreSet()
+    {
+        $producerMock = $this->createProducerMock();
+        $producerMock
+            ->expects($this->never())
+            ->method('sendEvent')
+        ;
+        $producerMock
+            ->expects($this->never())
+            ->method('sendCommand')
+        ;
+
+        $command = new ProduceCommand(new Container([
+            'enqueue.client.default.producer' => $producerMock,
+        ]));
+
+        $tester = new CommandTester($command);
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Either topic or command option should be set, none is set.');
+        $tester->execute([
+            'message' => 'theMessage',
+        ]);
+    }
+
+    public function testThrowIfBothTopicAndCommandOptionsAreSet()
+    {
+        $producerMock = $this->createProducerMock();
+        $producerMock
+            ->expects($this->never())
+            ->method('sendEvent')
+        ;
+        $producerMock
+            ->expects($this->never())
+            ->method('sendCommand')
+        ;
+
+        $command = new ProduceCommand(new Container([
+            'enqueue.client.default.producer' => $producerMock,
+        ]));
+
+        $tester = new CommandTester($command);
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Either topic or command option should be set, both are set.');
+        $tester->execute([
+            'message' => 'theMessage',
+            '--topic' => 'theTopic',
+            '--command' => 'theCommand',
+        ]);
+    }
+
+    public function testShouldSendEventToDefaultTransport()
+    {
+        $producerMock = $this->createProducerMock();
+        $producerMock
+            ->expects($this->once())
+            ->method('sendEvent')
+            ->with('theTopic', 'theMessage')
+        ;
+        $producerMock
+            ->expects($this->never())
+            ->method('sendCommand')
+        ;
+
+        $command = new ProduceCommand(new Container([
+            'enqueue.client.default.producer' => $producerMock,
+        ]));
+
+        $tester = new CommandTester($command);
+        $tester->execute([
+            'message' => 'theMessage',
+            '--topic' => 'theTopic',
+        ]);
+    }
+
+    public function testShouldSendCommandToDefaultTransport()
+    {
+        $producerMock = $this->createProducerMock();
+        $producerMock
+            ->expects($this->once())
+            ->method('sendCommand')
+            ->with('theCommand', 'theMessage')
+        ;
+        $producerMock
+            ->expects($this->never())
+            ->method('sendEvent')
+        ;
+
+        $command = new ProduceCommand(new Container([
+            'enqueue.client.default.producer' => $producerMock,
+        ]));
+
+        $tester = new CommandTester($command);
+        $tester->execute([
+            'message' => 'theMessage',
+            '--command' => 'theCommand',
+        ]);
+    }
+
+    public function testShouldSendEventToFooTransport()
+    {
+        $defaultProducerMock = $this->createProducerMock();
+        $defaultProducerMock
+            ->expects($this->never())
+            ->method('sendEvent')
+        ;
+        $defaultProducerMock
+            ->expects($this->never())
+            ->method('sendCommand')
+        ;
+
+        $fooProducerMock = $this->createProducerMock();
+        $fooProducerMock
+            ->expects($this->once())
+            ->method('sendEvent')
+            ->with('theTopic', 'theMessage')
+        ;
+        $fooProducerMock
+            ->expects($this->never())
+            ->method('sendCommand')
+        ;
+
+        $command = new ProduceCommand(new Container([
+            'enqueue.client.default.producer' => $defaultProducerMock,
+            'enqueue.client.foo.producer' => $fooProducerMock,
+        ]));
+
+        $tester = new CommandTester($command);
+        $tester->execute([
+            'message' => 'theMessage',
+            '--topic' => 'theTopic',
+            '--client' => 'foo',
+        ]);
+    }
+
+    public function testShouldSendCommandToFooTransport()
+    {
+        $defaultProducerMock = $this->createProducerMock();
+        $defaultProducerMock
+            ->expects($this->never())
+            ->method('sendEvent')
+        ;
+        $defaultProducerMock
+            ->expects($this->never())
+            ->method('sendCommand')
+        ;
+
+        $fooProducerMock = $this->createProducerMock();
+        $fooProducerMock
+            ->expects($this->once())
+            ->method('sendCommand')
+            ->with('theCommand', 'theMessage')
+        ;
+        $fooProducerMock
+            ->expects($this->never())
+            ->method('sendEvent')
+        ;
+
+        $command = new ProduceCommand(new Container([
+            'enqueue.client.default.producer' => $defaultProducerMock,
+            'enqueue.client.foo.producer' => $fooProducerMock,
+        ]));
+
+        $tester = new CommandTester($command);
+        $tester->execute([
+            'message' => 'theMessage',
+            '--command' => 'theCommand',
+            '--client' => 'foo',
+        ]);
+    }
+
+    public function testThrowIfClientNotFound()
+    {
+        $defaultProducerMock = $this->createProducerMock();
+        $defaultProducerMock
+            ->expects($this->never())
+            ->method('sendEvent')
+        ;
+        $defaultProducerMock
+            ->expects($this->never())
+            ->method('sendCommand')
+        ;
+
+        $command = new ProduceCommand(new Container([
+            'enqueue.client.default.producer' => $defaultProducerMock,
+        ]));
+
+        $tester = new CommandTester($command);
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Client "bar" is not supported.');
+        $tester->execute([
+            'message' => 'theMessage',
+            '--command' => 'theCommand',
+            '--client' => 'bar',
+        ]);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|ProducerInterface
+     */
+    private function createProducerMock()
+    {
+        return $this->createMock(ProducerInterface::class);
+    }
+}
diff --git a/Tests/Symfony/Client/ProduceMessageCommandTest.php b/Tests/Symfony/Client/ProduceMessageCommandTest.php
deleted file mode 100644
index 2cd8095..0000000
--- a/Tests/Symfony/Client/ProduceMessageCommandTest.php
+++ /dev/null
@@ -1,75 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Symfony\Client;
-
-use Enqueue\Client\ProducerInterface;
-use Enqueue\Symfony\Client\ProduceMessageCommand;
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\Console\Tester\CommandTester;
-
-class ProduceMessageCommandTest extends TestCase
-{
-    public function testCouldBeConstructedWithMessageProducerAsFirstArgument()
-    {
-        new ProduceMessageCommand($this->createProducerMock());
-    }
-
-    public function testShouldHaveCommandName()
-    {
-        $command = new ProduceMessageCommand($this->createProducerMock());
-
-        $this->assertEquals('enqueue:produce', $command->getName());
-    }
-
-    public function testShouldHaveCommandAliases()
-    {
-        $command = new ProduceMessageCommand($this->createProducerMock());
-
-        $this->assertEquals(['enq:p'], $command->getAliases());
-    }
-
-    public function testShouldHaveExpectedOptions()
-    {
-        $command = new ProduceMessageCommand($this->createProducerMock());
-
-        $options = $command->getDefinition()->getOptions();
-        $this->assertCount(0, $options);
-    }
-
-    public function testShouldHaveExpectedAttributes()
-    {
-        $command = new ProduceMessageCommand($this->createProducerMock());
-
-        $arguments = $command->getDefinition()->getArguments();
-        $this->assertCount(2, $arguments);
-
-        $this->assertArrayHasKey('topic', $arguments);
-        $this->assertArrayHasKey('message', $arguments);
-    }
-
-    public function testShouldExecuteConsumptionAndUseDefaultQueueName()
-    {
-        $producerMock = $this->createProducerMock();
-        $producerMock
-            ->expects($this->once())
-            ->method('sendEvent')
-            ->with('theTopic', 'theMessage')
-        ;
-
-        $command = new ProduceMessageCommand($producerMock);
-
-        $tester = new CommandTester($command);
-        $tester->execute([
-            'topic' => 'theTopic',
-            'message' => 'theMessage',
-        ]);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|ProducerInterface
-     */
-    private function createProducerMock()
-    {
-        return $this->createMock(ProducerInterface::class);
-    }
-}
diff --git a/Tests/Symfony/Client/RoutesCommandTest.php b/Tests/Symfony/Client/RoutesCommandTest.php
index 6100b64..4f51884 100644
--- a/Tests/Symfony/Client/RoutesCommandTest.php
+++ b/Tests/Symfony/Client/RoutesCommandTest.php
@@ -3,11 +3,14 @@
 namespace Enqueue\Tests\Symfony\Client;
 
 use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Route;
 use Enqueue\Client\RouteCollection;
+use Enqueue\Container\Container;
 use Enqueue\Symfony\Client\RoutesCommand;
 use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
+use Psr\Container\ContainerInterface;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Tester\CommandTester;
 
@@ -27,36 +30,37 @@ public function testShouldBeFinal()
 
     public function testCouldBeConstructedWithConfigAndRouteCollectionAsArguments()
     {
-        new RoutesCommand(Config::create(), new RouteCollection([]));
+        new RoutesCommand($this->createMock(ContainerInterface::class));
     }
 
     public function testShouldHaveCommandName()
     {
-        $command = new RoutesCommand(Config::create(), new RouteCollection([]));
+        $command = new RoutesCommand($this->createMock(ContainerInterface::class));
 
         $this->assertEquals('enqueue:routes', $command->getName());
     }
 
     public function testShouldHaveCommandAliases()
     {
-        $command = new RoutesCommand(Config::create(), new RouteCollection([]));
+        $command = new RoutesCommand($this->createMock(ContainerInterface::class));
 
         $this->assertEquals(['debug:enqueue:routes'], $command->getAliases());
     }
 
     public function testShouldHaveExpectedOptions()
     {
-        $command = new RoutesCommand(Config::create(), new RouteCollection([]));
+        $command = new RoutesCommand($this->createMock(ContainerInterface::class));
 
         $options = $command->getDefinition()->getOptions();
-        $this->assertCount(1, $options);
+        $this->assertCount(2, $options);
 
         $this->assertArrayHasKey('show-route-options', $options);
+        $this->assertArrayHasKey('client', $options);
     }
 
     public function testShouldHaveExpectedAttributes()
     {
-        $command = new RoutesCommand(Config::create(), new RouteCollection([]));
+        $command = new RoutesCommand($this->createMock(ContainerInterface::class));
 
         $arguments = $command->getDefinition()->getArguments();
         $this->assertCount(0, $arguments);
@@ -66,7 +70,9 @@ public function testShouldOutputEmptyRouteCollection()
     {
         $routeCollection = new RouteCollection([]);
 
-        $command = new RoutesCommand(Config::create(), $routeCollection);
+        $command = new RoutesCommand(new Container([
+            'enqueue.client.default.driver' => $this->createDriverStub(Config::create(), $routeCollection),
+        ]));
 
         $tester = new CommandTester($command);
 
@@ -81,6 +87,64 @@ public function testShouldOutputEmptyRouteCollection()
         $this->assertCommandOutput($expectedOutput, $tester);
     }
 
+    public function testShouldUseFooDriver()
+    {
+        $routeCollection = new RouteCollection([
+            new Route('fooTopic', Route::TOPIC, 'processor'),
+        ]);
+
+        $defaultDriverMock = $this->createMock(DriverInterface::class);
+        $defaultDriverMock
+            ->expects($this->never())
+            ->method('getConfig')
+        ;
+
+        $defaultDriverMock
+            ->expects($this->never())
+            ->method('getRouteCollection')
+        ;
+
+        $fooDriverMock = $this->createDriverStub(Config::create(), $routeCollection);
+
+        $command = new RoutesCommand(new Container([
+            'enqueue.client.default.driver' => $defaultDriverMock,
+            'enqueue.client.foo.driver' => $fooDriverMock,
+        ]));
+
+        $tester = new CommandTester($command);
+        $tester->execute([
+            '--client' => 'foo',
+        ]);
+
+        $this->assertContains('Found 1 routes', $tester->getDisplay());
+    }
+
+    public function testThrowIfClientNotFound()
+    {
+        $defaultDriverMock = $this->createMock(DriverInterface::class);
+        $defaultDriverMock
+            ->expects($this->never())
+            ->method('getConfig')
+        ;
+
+        $defaultDriverMock
+            ->expects($this->never())
+            ->method('getRouteCollection')
+        ;
+
+        $command = new RoutesCommand(new Container([
+            'enqueue.client.default.driver' => $defaultDriverMock,
+        ]));
+
+        $tester = new CommandTester($command);
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Client "foo" is not supported.');
+        $tester->execute([
+            '--client' => 'foo',
+        ]);
+    }
+
     public function testShouldOutputTopicRouteInfo()
     {
         $routeCollection = new RouteCollection([
@@ -88,7 +152,9 @@ public function testShouldOutputTopicRouteInfo()
             new Route('barTopic', Route::TOPIC, 'processor'),
         ]);
 
-        $command = new RoutesCommand(Config::create(), $routeCollection);
+        $command = new RoutesCommand(new Container([
+            'enqueue.client.default.driver' => $this->createDriverStub(Config::create(), $routeCollection),
+        ]));
 
         $tester = new CommandTester($command);
 
@@ -116,7 +182,9 @@ public function testShouldOutputCommandRouteInfo()
             new Route('barCommand', Route::COMMAND, 'processor', ['foo' => 'fooVal', 'bar' => 'barVal']),
         ]);
 
-        $command = new RoutesCommand(Config::create(), $routeCollection);
+        $command = new RoutesCommand(new Container([
+            'enqueue.client.default.driver' => $this->createDriverStub(Config::create(), $routeCollection),
+        ]));
 
         $tester = new CommandTester($command);
 
@@ -144,7 +212,9 @@ public function testShouldCorrectlyOutputPrefixedCustomQueue()
             new Route('barTopic', Route::TOPIC, 'processor', ['queue' => 'bar']),
         ]);
 
-        $command = new RoutesCommand(Config::create(), $routeCollection);
+        $command = new RoutesCommand(new Container([
+            'enqueue.client.default.driver' => $this->createDriverStub(Config::create(), $routeCollection),
+        ]));
 
         $tester = new CommandTester($command);
 
@@ -173,7 +243,9 @@ public function testShouldCorrectlyOutputNotPrefixedCustomQueue()
             new Route('barTopic', Route::TOPIC, 'processor', ['queue' => 'bar', 'prefix_queue' => false]),
         ]);
 
-        $command = new RoutesCommand(Config::create(), $routeCollection);
+        $command = new RoutesCommand(new Container([
+            'enqueue.client.default.driver' => $this->createDriverStub(Config::create(), $routeCollection),
+        ]));
 
         $tester = new CommandTester($command);
 
@@ -201,7 +273,9 @@ public function testShouldCorrectlyOutputExternalRoute()
             new Route('barTopic', Route::TOPIC, 'processor', ['external' => true]),
         ]);
 
-        $command = new RoutesCommand(Config::create(), $routeCollection);
+        $command = new RoutesCommand(new Container([
+            'enqueue.client.default.driver' => $this->createDriverStub(Config::create(), $routeCollection),
+        ]));
 
         $tester = new CommandTester($command);
 
@@ -229,7 +303,9 @@ public function testShouldOutputRouteOptions()
             new Route('barTopic', Route::TOPIC, 'processor', ['bar' => 'barVal']),
         ]);
 
-        $command = new RoutesCommand(Config::create(), $routeCollection);
+        $command = new RoutesCommand(new Container([
+            'enqueue.client.default.driver' => $this->createDriverStub(Config::create(), $routeCollection),
+        ]));
 
         $tester = new CommandTester($command);
 
@@ -254,6 +330,27 @@ public function testShouldOutputRouteOptions()
         $this->assertCommandOutput($expectedOutput, $tester);
     }
 
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createDriverStub(Config $config, RouteCollection $routeCollection): DriverInterface
+    {
+        $driverMock = $this->createMock(DriverInterface::class);
+        $driverMock
+            ->expects($this->any())
+            ->method('getConfig')
+            ->willReturn($config)
+        ;
+
+        $driverMock
+            ->expects($this->any())
+            ->method('getRouteCollection')
+            ->willReturn($routeCollection)
+        ;
+
+        return $driverMock;
+    }
+
     private function assertCommandOutput(string $expected, CommandTester $tester): void
     {
         $this->assertSame(0, $tester->getStatusCode());
diff --git a/Tests/Symfony/Client/SetupBrokerCommandTest.php b/Tests/Symfony/Client/SetupBrokerCommandTest.php
index 740d6d1..65ee306 100644
--- a/Tests/Symfony/Client/SetupBrokerCommandTest.php
+++ b/Tests/Symfony/Client/SetupBrokerCommandTest.php
@@ -3,32 +3,53 @@
 namespace Enqueue\Tests\Symfony\Client;
 
 use Enqueue\Client\DriverInterface;
+use Enqueue\Container\Container;
 use Enqueue\Symfony\Client\SetupBrokerCommand;
 use PHPUnit\Framework\TestCase;
+use Psr\Container\ContainerInterface;
 use Symfony\Component\Console\Tester\CommandTester;
 
 class SetupBrokerCommandTest extends TestCase
 {
-    public function testCouldBeConstructedWithRequiredAttributes()
+    public function testCouldBeConstructedWithContainerAsFirstArgument()
     {
-        new \Enqueue\Symfony\Client\SetupBrokerCommand($this->createClientDriverMock());
+        new SetupBrokerCommand($this->createMock(ContainerInterface::class));
     }
 
     public function testShouldHaveCommandName()
     {
-        $command = new SetupBrokerCommand($this->createClientDriverMock());
+        $command = new SetupBrokerCommand($this->createMock(ContainerInterface::class));
 
         $this->assertEquals('enqueue:setup-broker', $command->getName());
     }
 
     public function testShouldHaveCommandAliases()
     {
-        $command = new SetupBrokerCommand($this->createClientDriverMock());
+        $command = new SetupBrokerCommand($this->createMock(ContainerInterface::class));
 
         $this->assertEquals(['enq:sb'], $command->getAliases());
     }
 
-    public function testShouldCreateQueues()
+    public function testShouldHaveExpectedOptions()
+    {
+        $command = new SetupBrokerCommand($this->createMock(ContainerInterface::class));
+
+        $options = $command->getDefinition()->getOptions();
+
+        $this->assertCount(1, $options);
+        $this->assertArrayHasKey('client', $options);
+    }
+
+    public function testShouldHaveExpectedAttributes()
+    {
+        $command = new SetupBrokerCommand($this->createMock(ContainerInterface::class));
+
+        $arguments = $command->getDefinition()->getArguments();
+
+        $this->assertCount(0, $arguments);
+    }
+
+    public function testShouldCallDriverSetupBrokerMethod()
     {
         $driver = $this->createClientDriverMock();
         $driver
@@ -36,12 +57,62 @@ public function testShouldCreateQueues()
             ->method('setupBroker')
         ;
 
-        $command = new SetupBrokerCommand($driver);
+        $command = new SetupBrokerCommand(new Container([
+            'enqueue.client.default.driver' => $driver,
+        ]));
 
         $tester = new CommandTester($command);
         $tester->execute([]);
 
-        $this->assertContains('Setup Broker', $tester->getDisplay());
+        $this->assertContains('Broker set up', $tester->getDisplay());
+    }
+
+    public function testShouldCallRequestedClientDriverSetupBrokerMethod()
+    {
+        $defaultDriver = $this->createClientDriverMock();
+        $defaultDriver
+            ->expects($this->never())
+            ->method('setupBroker')
+        ;
+
+        $fooDriver = $this->createClientDriverMock();
+        $fooDriver
+            ->expects($this->once())
+            ->method('setupBroker')
+        ;
+
+        $command = new SetupBrokerCommand(new Container([
+            'enqueue.client.default.driver' => $defaultDriver,
+            'enqueue.client.foo.driver' => $fooDriver,
+        ]));
+
+        $tester = new CommandTester($command);
+        $tester->execute([
+            '--client' => 'foo',
+        ]);
+
+        $this->assertContains('Broker set up', $tester->getDisplay());
+    }
+
+    public function testShouldThrowIfClientNotFound()
+    {
+        $defaultDriver = $this->createClientDriverMock();
+        $defaultDriver
+            ->expects($this->never())
+            ->method('setupBroker')
+        ;
+
+        $command = new SetupBrokerCommand(new Container([
+            'enqueue.client.default.driver' => $defaultDriver,
+        ]));
+
+        $tester = new CommandTester($command);
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Client "foo" is not supported.');
+        $tester->execute([
+            '--client' => 'foo',
+        ]);
     }
 
     /**
diff --git a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
index 786ce94..ef50ad2 100644
--- a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
@@ -40,7 +40,7 @@ public function testShouldHaveExpectedOptions()
         $this->assertArrayHasKey('memory-limit', $options);
         $this->assertArrayHasKey('message-limit', $options);
         $this->assertArrayHasKey('time-limit', $options);
-        $this->assertArrayHasKey('idle-timeout', $options);
+        $this->assertArrayHasKey('idle-time', $options);
         $this->assertArrayHasKey('receive-timeout', $options);
         $this->assertArrayHasKey('niceness', $options);
         $this->assertArrayHasKey('transport', $options);
@@ -260,14 +260,6 @@ public function testShouldExecuteConsumptionWithCustomTransportExplicitlySetQueu
         ]);
     }
 
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Context
-     */
-    protected function createContextMock()
-    {
-        return $this->createMock(Context::class);
-    }
-
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|InteropQueue
      */
diff --git a/Tests/Symfony/Consumption/ConsumeCommandTest.php b/Tests/Symfony/Consumption/ConsumeCommandTest.php
index cd061c0..70bc572 100644
--- a/Tests/Symfony/Consumption/ConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConsumeCommandTest.php
@@ -6,7 +6,6 @@
 use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Container\Container;
 use Enqueue\Symfony\Consumption\ConsumeCommand;
-use Interop\Queue\Context;
 use PHPUnit\Framework\TestCase;
 use Psr\Container\ContainerInterface;
 use Symfony\Component\Console\Tester\CommandTester;
@@ -35,7 +34,7 @@ public function testShouldHaveExpectedOptions()
         $this->assertArrayHasKey('memory-limit', $options);
         $this->assertArrayHasKey('message-limit', $options);
         $this->assertArrayHasKey('time-limit', $options);
-        $this->assertArrayHasKey('idle-timeout', $options);
+        $this->assertArrayHasKey('idle-time', $options);
         $this->assertArrayHasKey('receive-timeout', $options);
         $this->assertArrayHasKey('niceness', $options);
         $this->assertArrayHasKey('transport', $options);
@@ -111,14 +110,6 @@ public function testThrowIfNotDefinedTransportRequested()
         $tester->execute(['--transport' => 'not-defined']);
     }
 
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Context
-     */
-    private function createContextMock()
-    {
-        return $this->createMock(Context::class);
-    }
-
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumerInterface
      */
diff --git a/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php b/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php
index f26324c..7479172 100644
--- a/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php
+++ b/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php
@@ -16,7 +16,7 @@ public function testShouldAddExtensionsOptions()
         $options = $trait->getDefinition()->getOptions();
 
         $this->assertCount(2, $options);
-        $this->assertArrayHasKey('idle-timeout', $options);
+        $this->assertArrayHasKey('idle-time', $options);
         $this->assertArrayHasKey('receive-timeout', $options);
     }
 
@@ -38,7 +38,7 @@ public function testShouldSetQueueConsumerOptions()
 
         $tester = new CommandTester($trait);
         $tester->execute([
-            '--idle-timeout' => '123.1',
+            '--idle-time' => '123.1',
             '--receive-timeout' => '456.1',
         ]);
     }
diff --git a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index 57015b7..993c2b3 100644
--- a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -2,8 +2,14 @@
 
 namespace Enqueue\Tests\Symfony\DependencyInjection;
 
+use Enqueue\Consumption\ChainExtension;
+use Enqueue\Consumption\QueueConsumer;
+use Enqueue\Rpc\RpcClient;
+use Enqueue\Rpc\RpcFactory;
 use Enqueue\Symfony\DependencyInjection\TransportFactory;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\ConnectionFactory;
+use Interop\Queue\Context;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\Config\Definition\Builder\TreeBuilder;
 use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
@@ -41,7 +47,7 @@ public function testShouldAllowAddConfigurationAsStringDsn()
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addConfiguration($rootNode);
+        $transport->addTransportConfiguration($rootNode);
         $processor = new Processor();
         $config = $processor->process($tb->buildTree(), ['dsn://']);
 
@@ -59,7 +65,7 @@ public function testShouldAllowAddConfigurationAsDsnWithoutSlashes()
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addConfiguration($rootNode);
+        $transport->addTransportConfiguration($rootNode);
         $processor = new Processor();
         $config = $processor->process($tb->buildTree(), ['dsn:']);
 
@@ -72,7 +78,7 @@ public function testShouldSetNullTransportIfNullGiven()
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addConfiguration($rootNode);
+        $transport->addTransportConfiguration($rootNode);
         $processor = new Processor();
 
         $config = $processor->process($tb->buildTree(), [null]);
@@ -85,7 +91,7 @@ public function testShouldSetNullTransportIfEmptyStringGiven()
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addConfiguration($rootNode);
+        $transport->addTransportConfiguration($rootNode);
         $processor = new Processor();
 
         $config = $processor->process($tb->buildTree(), ['']);
@@ -98,7 +104,7 @@ public function testShouldSetNullTransportIfEmptyArrayGiven()
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addConfiguration($rootNode);
+        $transport->addTransportConfiguration($rootNode);
         $processor = new Processor();
 
         $config = $processor->process($tb->buildTree(), [[]]);
@@ -111,7 +117,7 @@ public function testThrowIfEmptyDsnGiven()
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addConfiguration($rootNode);
+        $transport->addTransportConfiguration($rootNode);
         $processor = new Processor();
 
         $this->expectException(InvalidConfigurationException::class);
@@ -125,7 +131,7 @@ public function testThrowIfFactoryClassAndFactoryServiceSetAtTheSameTime()
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addConfiguration($rootNode);
+        $transport->addTransportConfiguration($rootNode);
         $processor = new Processor();
 
         $this->expectException(\LogicException::class);
@@ -143,7 +149,7 @@ public function testThrowIfConnectionFactoryClassUsedWithFactoryClassAtTheSameTi
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addConfiguration($rootNode);
+        $transport->addTransportConfiguration($rootNode);
         $processor = new Processor();
 
         $this->expectException(\LogicException::class);
@@ -161,7 +167,7 @@ public function testThrowIfConnectionFactoryClassUsedWithFactoryServiceAtTheSame
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addConfiguration($rootNode);
+        $transport->addTransportConfiguration($rootNode);
         $processor = new Processor();
 
         $this->expectException(\LogicException::class);
@@ -179,7 +185,7 @@ public function testShouldAllowSetFactoryClass()
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addConfiguration($rootNode);
+        $transport->addTransportConfiguration($rootNode);
         $processor = new Processor();
 
         $config = $processor->process($tb->buildTree(), [[
@@ -197,7 +203,7 @@ public function testShouldAllowSetFactoryService()
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addConfiguration($rootNode);
+        $transport->addTransportConfiguration($rootNode);
         $processor = new Processor();
 
         $config = $processor->process($tb->buildTree(), [[
@@ -215,7 +221,7 @@ public function testShouldAllowSetConnectionFactoryClass()
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addConfiguration($rootNode);
+        $transport->addTransportConfiguration($rootNode);
         $processor = new Processor();
 
         $config = $processor->process($tb->buildTree(), [[
@@ -233,7 +239,7 @@ public function testThrowIfExtraOptionGiven()
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addConfiguration($rootNode);
+        $transport->addTransportConfiguration($rootNode);
         $processor = new Processor();
 
         $config = $processor->process($tb->buildTree(), [['dsn' => 'foo:', 'extraOption' => 'aVal']]);
@@ -243,15 +249,13 @@ public function testThrowIfExtraOptionGiven()
         );
     }
 
-    public function testShouldCreateConnectionFactoryFromDSN()
+    public function testShouldBuildConnectionFactoryFromDSN()
     {
         $container = new ContainerBuilder();
 
         $transport = new TransportFactory('default');
 
-        $serviceId = $transport->build($container, ['dsn' => 'foo://bar/baz']);
-
-        $this->assertEquals('enqueue.transport.default.connection_factory', $serviceId);
+        $transport->buildConnectionFactory($container, ['dsn' => 'foo://bar/baz']);
 
         $this->assertTrue($container->hasDefinition('enqueue.transport.default.connection_factory'));
 
@@ -266,15 +270,13 @@ public function testShouldCreateConnectionFactoryFromDSN()
         ;
     }
 
-    public function testShouldCreateConnectionFactoryUsingCustomFactoryClass()
+    public function testShouldBuildConnectionFactoryUsingCustomFactoryClass()
     {
         $container = new ContainerBuilder();
 
         $transport = new TransportFactory('default');
 
-        $serviceId = $transport->build($container, ['dsn' => 'foo:', 'factory_class' => 'theFactoryClass']);
-
-        $this->assertEquals('enqueue.transport.default.connection_factory', $serviceId);
+        $transport->buildConnectionFactory($container, ['dsn' => 'foo:', 'factory_class' => 'theFactoryClass']);
 
         $this->assertTrue($container->hasDefinition('enqueue.transport.default.connection_factory_factory'));
         $this->assertSame(
@@ -295,15 +297,13 @@ public function testShouldCreateConnectionFactoryUsingCustomFactoryClass()
         ;
     }
 
-    public function testShouldCreateConnectionFactoryUsingCustomFactoryService()
+    public function testShouldBuildConnectionFactoryUsingCustomFactoryService()
     {
         $container = new ContainerBuilder();
 
         $transport = new TransportFactory('default');
 
-        $serviceId = $transport->build($container, ['dsn' => 'foo:', 'factory_service' => 'theFactoryService']);
-
-        $this->assertEquals('enqueue.transport.default.connection_factory', $serviceId);
+        $transport->buildConnectionFactory($container, ['dsn' => 'foo:', 'factory_service' => 'theFactoryService']);
 
         $this->assertTrue($container->hasDefinition('enqueue.transport.default.connection_factory'));
 
@@ -318,15 +318,13 @@ public function testShouldCreateConnectionFactoryUsingCustomFactoryService()
         ;
     }
 
-    public function testShouldCreateConnectionFactoryUsingConnectionFactoryClassWithoutFactory()
+    public function testShouldBuildConnectionFactoryUsingConnectionFactoryClassWithoutFactory()
     {
         $container = new ContainerBuilder();
 
         $transport = new TransportFactory('default');
 
-        $serviceId = $transport->build($container, ['dsn' => 'foo:', 'connection_factory_class' => 'theFactoryClass']);
-
-        $this->assertEquals('enqueue.transport.default.connection_factory', $serviceId);
+        $transport->buildConnectionFactory($container, ['dsn' => 'foo:', 'connection_factory_class' => 'theFactoryClass']);
 
         $this->assertTrue($container->hasDefinition('enqueue.transport.default.connection_factory'));
 
@@ -338,15 +336,14 @@ public function testShouldCreateConnectionFactoryUsingConnectionFactoryClassWith
         ;
     }
 
-    public function testShouldCreateContextFromDsn()
+    public function testShouldBuildContext()
     {
         $container = new ContainerBuilder();
+        $container->register('enqueue.transport.default.connection_factory', ConnectionFactory::class);
 
         $transport = new TransportFactory('default');
 
-        $serviceId = $transport->buildContext($container, ['dsn' => 'foo://bar/baz']);
-
-        $this->assertEquals('enqueue.transport.default.context', $serviceId);
+        $transport->buildContext($container, []);
 
         $this->assertNotEmpty($container->getDefinition('enqueue.transport.default.context')->getFactory());
         $this->assertEquals(
@@ -358,4 +355,99 @@ public function testShouldCreateContextFromDsn()
             $container->getDefinition('enqueue.transport.default.context')->getArguments())
         ;
     }
+
+    public function testThrowIfBuildContextCalledButConnectionFactoryServiceDoesNotExist()
+    {
+        $container = new ContainerBuilder();
+
+        $transport = new TransportFactory('default');
+
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The service "enqueue.transport.default.connection_factory" does not exist.');
+        $transport->buildContext($container, []);
+    }
+
+    public function testShouldBuildQueueConsumerWithDefaultOptions()
+    {
+        $container = new ContainerBuilder();
+        $container->register('enqueue.transport.default.context', Context::class);
+
+        $transport = new TransportFactory('default');
+
+        $transport->buildQueueConsumer($container, []);
+
+        $this->assertSame(0, $container->getParameter('enqueue.transport.default.idle_time'));
+        $this->assertSame(10000, $container->getParameter('enqueue.transport.default.receive_timeout'));
+
+        $this->assertTrue($container->hasDefinition('enqueue.transport.default.consumption_extensions'));
+        $this->assertSame(ChainExtension::class, $container->getDefinition('enqueue.transport.default.consumption_extensions')->getClass());
+        $this->assertSame([[]], $container->getDefinition('enqueue.transport.default.consumption_extensions')->getArguments());
+
+        $this->assertTrue($container->hasDefinition('enqueue.transport.default.queue_consumer'));
+        $this->assertSame(QueueConsumer::class, $container->getDefinition('enqueue.transport.default.queue_consumer')->getClass());
+        $this->assertEquals([
+            new Reference('enqueue.transport.default.context'),
+            new Reference('enqueue.transport.default.consumption_extensions'),
+            '%enqueue.transport.default.idle_time%',
+            '%enqueue.transport.default.receive_timeout%',
+        ], $container->getDefinition('enqueue.transport.default.queue_consumer')->getArguments());
+    }
+
+    public function testShouldBuildQueueConsumerWithCustomOptions()
+    {
+        $container = new ContainerBuilder();
+        $container->register('enqueue.transport.default.context', Context::class);
+
+        $transport = new TransportFactory('default');
+
+        $transport->buildQueueConsumer($container, [
+            'idle_time' => 123,
+            'receive_timeout' => 567,
+        ]);
+
+        $this->assertSame(123, $container->getParameter('enqueue.transport.default.idle_time'));
+        $this->assertSame(567, $container->getParameter('enqueue.transport.default.receive_timeout'));
+    }
+
+    public function testThrowIfBuildQueueConsumerCalledButContextServiceDoesNotExist()
+    {
+        $container = new ContainerBuilder();
+
+        $transport = new TransportFactory('default');
+
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The service "enqueue.transport.default.context" does not exist.');
+        $transport->buildQueueConsumer($container, []);
+    }
+
+    public function testShouldBuildRpcClientWithDefaultOptions()
+    {
+        $container = new ContainerBuilder();
+        $container->register('enqueue.transport.default.context', Context::class);
+
+        $transport = new TransportFactory('default');
+
+        $transport->buildRpcClient($container, []);
+
+        $this->assertTrue($container->hasDefinition('enqueue.transport.default.rpc_factory'));
+        $this->assertSame(RpcFactory::class, $container->getDefinition('enqueue.transport.default.rpc_factory')->getClass());
+
+        $this->assertTrue($container->hasDefinition('enqueue.transport.default.rpc_client'));
+        $this->assertSame(RpcClient::class, $container->getDefinition('enqueue.transport.default.rpc_client')->getClass());
+        $this->assertEquals([
+            new Reference('enqueue.transport.default.context'),
+            new Reference('enqueue.transport.default.rpc_factory'),
+        ], $container->getDefinition('enqueue.transport.default.rpc_client')->getArguments());
+    }
+
+    public function testThrowIfBuildRpcClientCalledButContextServiceDoesNotExist()
+    {
+        $container = new ContainerBuilder();
+
+        $transport = new TransportFactory('default');
+
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The service "enqueue.transport.default.context" does not exist.');
+        $transport->buildRpcClient($container, []);
+    }
 }
diff --git a/composer.json b/composer.json
index 08c14be..d8d1a48 100644
--- a/composer.json
+++ b/composer.json
@@ -11,7 +11,8 @@
         "queue-interop/queue-interop": "0.7.x-dev",
         "enqueue/null": "0.9.x-dev",
         "ramsey/uuid": "^2|^3.5",
-        "psr/log": "^1"
+        "psr/log": "^1",
+        "psr/container": "^1"
     },
     "require-dev": {
         "phpunit/phpunit": "~5.5",

From db325c29b5a99ab57dedf324aeb2f538c6e1385b Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 4 Oct 2018 20:11:16 +0300
Subject: [PATCH 145/337] [consumption] Rework QueueConsumer extension points.

---
 Consumption/BoundProcessor.php                |  35 ++++
 Consumption/ChainExtension.php                |   7 +-
 Consumption/Context/Start.php                 | 144 ++++++++++++++
 Consumption/EmptyExtensionTrait.php           |   7 +-
 Consumption/ExtensionInterface.php            |   8 +-
 Consumption/QueueConsumer.php                 | 129 +++++++-----
 Consumption/QueueConsumerInterface.php        |  18 +-
 Symfony/Client/RoutesCommand.php              |   6 +-
 .../QueueConsumerOptionsCommandTrait.php      |   2 +-
 Tests/Consumption/QueueConsumerTest.php       | 187 +++++++++++-------
 10 files changed, 404 insertions(+), 139 deletions(-)
 create mode 100644 Consumption/BoundProcessor.php
 create mode 100644 Consumption/Context/Start.php

diff --git a/Consumption/BoundProcessor.php b/Consumption/BoundProcessor.php
new file mode 100644
index 0000000..54fa3f1
--- /dev/null
+++ b/Consumption/BoundProcessor.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Enqueue\Consumption;
+
+use Interop\Queue\Processor;
+use Interop\Queue\Queue;
+
+final class BoundProcessor
+{
+    /**
+     * @var Queue
+     */
+    private $queue;
+
+    /**
+     * @var Processor
+     */
+    private $processor;
+
+    public function __construct(Queue $queue, Processor $processor)
+    {
+        $this->queue = $queue;
+        $this->processor = $processor;
+    }
+
+    public function getQueue(): Queue
+    {
+        return $this->queue;
+    }
+
+    public function getProcessor(): Processor
+    {
+        return $this->processor;
+    }
+}
diff --git a/Consumption/ChainExtension.php b/Consumption/ChainExtension.php
index ae71f24..85fc1b3 100644
--- a/Consumption/ChainExtension.php
+++ b/Consumption/ChainExtension.php
@@ -2,6 +2,8 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\Start;
+
 class ChainExtension implements ExtensionInterface
 {
     use EmptyExtensionTrait;
@@ -22,10 +24,7 @@ public function __construct(array $extensions)
         });
     }
 
-    /**
-     * @param Context $context
-     */
-    public function onStart(Context $context)
+    public function onStart(Start $context): void
     {
         foreach ($this->extensions as $extension) {
             $extension->onStart($context);
diff --git a/Consumption/Context/Start.php b/Consumption/Context/Start.php
new file mode 100644
index 0000000..88a12e7
--- /dev/null
+++ b/Consumption/Context/Start.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace Enqueue\Consumption\Context;
+
+use Enqueue\Consumption\BoundProcessor;
+use Interop\Queue\Context;
+use Psr\Log\LoggerInterface;
+
+final class Start
+{
+    /**
+     * @var Context
+     */
+    private $context;
+
+    /**
+     * @var LoggerInterface
+     */
+    private $logger;
+
+    /**
+     * @var BoundProcessor[]
+     */
+    private $processors;
+
+    /**
+     * @var int
+     */
+    private $receiveTimeout;
+
+    /**
+     * @var int
+     */
+    private $idleTime;
+
+    /**
+     * @var int
+     */
+    private $startTime;
+
+    /**
+     * @var bool
+     */
+    private $executionInterrupted;
+
+    /**
+     * @param BoundProcessor[] $processors
+     */
+    public function __construct(Context $context, LoggerInterface $logger, array $processors, int $receiveTimeout, int $idleTime, int $startTime)
+    {
+        $this->context = $context;
+        $this->logger = $logger;
+        $this->processors = $processors;
+        $this->receiveTimeout = $receiveTimeout;
+        $this->idleTime = $idleTime;
+        $this->startTime = $startTime;
+
+        $this->executionInterrupted = false;
+    }
+
+    public function getInteropContext(): Context
+    {
+        return $this->context;
+    }
+
+    public function getLogger(): LoggerInterface
+    {
+        return $this->logger;
+    }
+
+    public function changeLogger(LoggerInterface $logger): void
+    {
+        $this->logger = $logger;
+    }
+
+    /**
+     * In milliseconds.
+     */
+    public function getReceiveTimeout(): int
+    {
+        return $this->receiveTimeout;
+    }
+
+    /**
+     * In milliseconds.
+     */
+    public function changeReceiveTimeout(int $timeout): void
+    {
+        $this->receiveTimeout = $timeout;
+    }
+
+    /**
+     * In milliseconds.
+     */
+    public function getIdleTime(): int
+    {
+        return $this->idleTime;
+    }
+
+    /**
+     * In milliseconds.
+     */
+    public function changeIdleTime(int $time): void
+    {
+        $this->idleTime = $time;
+    }
+
+    /**
+     * In milliseconds.
+     */
+    public function getStartTime(): int
+    {
+        return $this->startTime;
+    }
+
+    /**
+     * @return BoundProcessor[]
+     */
+    public function getBoundProcessors(): array
+    {
+        return $this->processors;
+    }
+
+    /**
+     * @param BoundProcessor[] $processors
+     */
+    public function changeBoundProcessors(array $processors): void
+    {
+        $this->processors = [];
+        array_walk($processors, function (BoundProcessor $processor) {
+            $this->processors[] = $processor;
+        });
+    }
+
+    public function isExecutionInterrupted(): bool
+    {
+        return $this->executionInterrupted;
+    }
+
+    public function interruptExecution(): void
+    {
+        $this->executionInterrupted = true;
+    }
+}
diff --git a/Consumption/EmptyExtensionTrait.php b/Consumption/EmptyExtensionTrait.php
index 0f6b849..f474d11 100644
--- a/Consumption/EmptyExtensionTrait.php
+++ b/Consumption/EmptyExtensionTrait.php
@@ -2,12 +2,11 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\Start;
+
 trait EmptyExtensionTrait
 {
-    /**
-     * @param Context $context
-     */
-    public function onStart(Context $context)
+    public function onStart(Start $context): void
     {
     }
 
diff --git a/Consumption/ExtensionInterface.php b/Consumption/ExtensionInterface.php
index 2a5d7bb..902b969 100644
--- a/Consumption/ExtensionInterface.php
+++ b/Consumption/ExtensionInterface.php
@@ -2,15 +2,15 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\Start;
+
 interface ExtensionInterface
 {
     /**
-     * Executed only once at the very begining of the consumption.
+     * Executed only once at the very beginning of the consumption.
      * At this stage the context does not contain processor, consumer and queue.
-     *
-     * @param Context $context
      */
-    public function onStart(Context $context);
+    public function onStart(Start $context): void;
 
     /**
      * Executed at every new cycle before we asked a broker for a new message.
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 12d4bbf..ca6672c 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\Exception\ConsumptionInterruptedException;
 use Enqueue\Consumption\Exception\InvalidArgumentException;
 use Enqueue\Consumption\Exception\LogicException;
@@ -29,18 +30,14 @@ final class QueueConsumer implements QueueConsumerInterface
     private $staticExtension;
 
     /**
-     * [
-     *   [InteropQueue, Processor],
-     * ].
-     *
-     * @var array
+     * @var BoundProcessor[]
      */
     private $boundProcessors;
 
     /**
      * @var int|float in milliseconds
      */
-    private $idleTimeout;
+    private $idleTime;
 
     /**
      * @var int|float in milliseconds
@@ -63,49 +60,49 @@ final class QueueConsumer implements QueueConsumerInterface
     private $fallbackSubscriptionConsumer;
 
     /**
-     * @param InteropContext                         $interopContext
-     * @param ExtensionInterface|ChainExtension|null $extension
-     * @param int|float                              $idleTimeout    the time in milliseconds queue consumer waits if no message received
-     * @param int|float                              $receiveTimeout the time in milliseconds queue consumer waits for a message (10 ms by default)
+     * @param BoundProcessor[] $boundProcessors
+     * @param int|float        $idleTime        the time in milliseconds queue consumer waits if no message received
+     * @param int|float        $receiveTimeout  the time in milliseconds queue consumer waits for a message (10 ms by default)
      */
     public function __construct(
         InteropContext $interopContext,
         ExtensionInterface $extension = null,
-        float $idleTimeout = 0.,
-        float $receiveTimeout = 10000.
+        array $boundProcessors = [],
+        LoggerInterface $logger = null,
+        int $idleTime = 0,
+        int $receiveTimeout = 10000
     ) {
         $this->interopContext = $interopContext;
-        $this->staticExtension = $extension ?: new ChainExtension([]);
-        $this->idleTimeout = $idleTimeout;
+        $this->idleTime = $idleTime;
         $this->receiveTimeout = $receiveTimeout;
 
+        $this->staticExtension = $extension ?: new ChainExtension([]);
+        $this->logger = $logger ?: new NullLogger();
+
         $this->boundProcessors = [];
-        $this->logger = new NullLogger();
+        array_walk($boundProcessors, function (BoundProcessor $processor) {
+            $this->boundProcessors[] = $processor;
+        });
+
         $this->fallbackSubscriptionConsumer = new FallbackSubscriptionConsumer();
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function setIdleTimeout(float $timeout): void
+    public function setIdleTime(int $time): void
     {
-        $this->idleTimeout = $timeout;
+        $this->idleTime = $time;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getIdleTimeout(): float
+    public function getIdleTime(): int
     {
-        return $this->idleTimeout;
+        return $this->idleTime;
     }
 
-    public function setReceiveTimeout(float $timeout): void
+    public function setReceiveTimeout(int $timeout): void
     {
         $this->receiveTimeout = $timeout;
     }
 
-    public function getReceiveTimeout(): float
+    public function getReceiveTimeout(): int
     {
         return $this->receiveTimeout;
     }
@@ -130,49 +127,75 @@ public function bind($queue, Processor $processor): QueueConsumerInterface
             throw new LogicException(sprintf('The queue was already bound. Queue: %s', $queue->getQueueName()));
         }
 
-        $this->boundProcessors[$queue->getQueueName()] = [$queue, $processor];
+        $this->boundProcessors[$queue->getQueueName()] = new BoundProcessor($queue, $processor);
 
         return $this;
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function bindCallback($queue, callable $processor): QueueConsumerInterface
     {
         return $this->bind($queue, new CallbackProcessor($processor));
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function consume(ExtensionInterface $runtimeExtension = null): void
     {
+        /*
+         * onStart
+         * onPreSubscribe
+         * onPreConsume
+         * onPostConsume
+         * onReceived
+         * onResult
+         * onPostReceived
+         * onEnd
+         */
+
+        $this->extension = $runtimeExtension ?
+            new ChainExtension([$this->staticExtension, $runtimeExtension]) :
+            $this->staticExtension
+        ;
+
+        $startTime = (int) (microtime(true) * 1000);
+
+        $start = new Start(
+            $this->interopContext,
+            $this->logger,
+            $this->boundProcessors,
+            $this->receiveTimeout,
+            $this->idleTime,
+            $startTime
+        );
+
+        $this->extension->onStart($start);
+
+        $this->logger = $start->getLogger();
+        $this->idleTime = $start->getIdleTime();
+        $this->receiveTimeout = $start->getReceiveTimeout();
+        $this->boundProcessors = $start->getBoundProcessors();
+
         if (empty($this->boundProcessors)) {
             throw new \LogicException('There is nothing to consume. It is required to bind something before calling consume method.');
         }
 
         /** @var Consumer[] $consumers */
         $consumers = [];
-        /** @var InteropQueue $queue */
-        foreach ($this->boundProcessors as list($queue, $processor)) {
+        foreach ($this->boundProcessors as $boundProcessor) {
+            $queue = $boundProcessor->getQueue();
+
             $consumers[$queue->getQueueName()] = $this->interopContext->createConsumer($queue);
         }
 
-        $this->extension = $runtimeExtension ?
-            new ChainExtension([$this->staticExtension, $runtimeExtension]) :
-            $this->staticExtension
-        ;
-
+        // todo remove
         $context = new Context($this->interopContext);
-        $this->extension->onStart($context);
-
-        if ($context->getLogger()) {
-            $this->logger = $context->getLogger();
-        } else {
-            $this->logger = new NullLogger();
-            $context->setLogger($this->logger);
-        }
+        $context->setLogger($this->logger);
+//        $this->extension->onStart($context);
+//
+//        if ($context->getLogger()) {
+//            $this->logger = $context->getLogger();
+//        } else {
+//            $this->logger = new NullLogger();
+//            $context->setLogger($this->logger);
+//        }
 
         $this->logger->info('Start consuming');
 
@@ -185,10 +208,10 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
         $callback = function (InteropMessage $message, Consumer $consumer) use (&$context) {
             $currentProcessor = null;
 
-            /** @var InteropQueue $queue */
-            foreach ($this->boundProcessors as list($queue, $processor)) {
+            foreach ($this->boundProcessors as $boundProcessor) {
+                $queue = $boundProcessor->getQueue();
                 if ($queue->getQueueName() === $consumer->getQueue()->getQueueName()) {
-                    $currentProcessor = $processor;
+                    $currentProcessor = $boundProcessor->getProcessor();
                 }
             }
 
@@ -228,7 +251,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
 
                 $subscriptionConsumer->consume($this->receiveTimeout);
 
-                $this->idleTimeout && usleep($this->idleTimeout * 1000);
+                $this->idleTime && usleep($this->idleTime * 1000);
                 $this->extension->onIdle($context);
 
                 if ($context->isExecutionInterrupted()) {
diff --git a/Consumption/QueueConsumerInterface.php b/Consumption/QueueConsumerInterface.php
index 8358ba3..e561cdd 100644
--- a/Consumption/QueueConsumerInterface.php
+++ b/Consumption/QueueConsumerInterface.php
@@ -9,18 +9,24 @@
 interface QueueConsumerInterface
 {
     /**
-     * Milliseconds.
+     * In milliseconds.
      */
-    public function setIdleTimeout(float $timeout): void;
+    public function setIdleTime(int $time): void;
 
-    public function getIdleTimeout(): float;
+    /**
+     * In milliseconds.
+     */
+    public function getIdleTime(): int;
 
     /**
-     * Milliseconds.
+     * In milliseconds.
      */
-    public function setReceiveTimeout(float $timeout): void;
+    public function setReceiveTimeout(int $timeout): void;
 
-    public function getReceiveTimeout(): float;
+    /**
+     * In milliseconds.
+     */
+    public function getReceiveTimeout(): int;
 
     public function getContext(): Context;
 
diff --git a/Symfony/Client/RoutesCommand.php b/Symfony/Client/RoutesCommand.php
index 605d244..82bcacf 100644
--- a/Symfony/Client/RoutesCommand.php
+++ b/Symfony/Client/RoutesCommand.php
@@ -13,7 +13,7 @@
 use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Output\OutputInterface;
 
-final class RoutesCommand extends Command
+class RoutesCommand extends Command
 {
     protected static $defaultName = 'enqueue:routes';
 
@@ -150,3 +150,7 @@ private function getDriver(string $client): DriverInterface
         return $this->container->get(sprintf($this->driverIdPatter, $client));
     }
 }
+
+function enqueue()
+{
+}
diff --git a/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php b/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php
index c20e129..d924f63 100644
--- a/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php
+++ b/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php
@@ -26,7 +26,7 @@ protected function configureQueueConsumerOptions()
     protected function setQueueConsumerOptions(QueueConsumerInterface $consumer, InputInterface $input)
     {
         if (null !== $idleTimeout = $input->getOption('idle-time')) {
-            $consumer->setIdleTimeout((float) $idleTimeout);
+            $consumer->setIdleTime((float) $idleTimeout);
         }
 
         if (null !== $receiveTimeout = $input->getOption('receive-timeout')) {
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 5b655c1..548f5fe 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -2,9 +2,11 @@
 
 namespace Enqueue\Tests\Consumption;
 
+use Enqueue\Consumption\BoundProcessor;
 use Enqueue\Consumption\CallbackProcessor;
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\Exception\InvalidArgumentException;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Consumption\QueueConsumer;
@@ -20,46 +22,75 @@
 use Interop\Queue\Queue;
 use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
 class QueueConsumerTest extends TestCase
 {
-    public function testCouldBeConstructedWithConnectionAndExtensionsAsArguments()
+    public function testCouldBeConstructedWithAllArguments()
     {
-        new QueueConsumer($this->createContextStub(), null, 0);
+        new QueueConsumer($this->createContextStub(), null, [], null, 0, 0);
     }
 
-    public function testCouldBeConstructedWithConnectionOnly()
+    public function testCouldBeConstructedWithContextOnly()
     {
         new QueueConsumer($this->createContextStub());
     }
 
-    public function testCouldBeConstructedWithConnectionAndSingleExtension()
+    public function testCouldBeConstructedWithContextAndSingleExtension()
     {
         new QueueConsumer($this->createContextStub(), $this->createExtension());
     }
 
     public function testShouldSetEmptyArrayToBoundProcessorsPropertyInConstructor()
     {
-        $consumer = new QueueConsumer($this->createContextStub(), null, 0);
+        $consumer = new QueueConsumer($this->createContextStub(), null, [], null, 0, 0);
 
         $this->assertAttributeSame([], 'boundProcessors', $consumer);
     }
 
-    public function testShouldAllowGetConnectionSetInConstructor()
+    public function testShouldSetProvidedBoundProcessorsToThePropertyInConstructor()
     {
-        $expectedConnection = $this->createContextStub();
+        $boundProcessors = [
+            new BoundProcessor(new NullQueue('foo'), $this->createProcessorMock()),
+            new BoundProcessor(new NullQueue('bar'), $this->createProcessorMock()),
+        ];
+
+        $consumer = new QueueConsumer($this->createContextStub(), null, $boundProcessors, null, 0, 0);
+
+        $this->assertAttributeSame($boundProcessors, 'boundProcessors', $consumer);
+    }
+
+    public function testShouldSetNullLoggerIfNoneProvidedInConstructor()
+    {
+        $consumer = new QueueConsumer($this->createContextStub(), null, [], null, 0, 0);
+
+        $this->assertAttributeInstanceOf(NullLogger::class, 'logger', $consumer);
+    }
+
+    public function testShouldSetProvidedLoggerToThePropertyInConstructor()
+    {
+        $expectedLogger = $this->createMock(LoggerInterface::class);
+
+        $consumer = new QueueConsumer($this->createContextStub(), null, [], $expectedLogger, 0, 0);
+
+        $this->assertAttributeSame($expectedLogger, 'logger', $consumer);
+    }
 
-        $consumer = new QueueConsumer($expectedConnection, null, 0);
+    public function testShouldAllowGetContextSetInConstructor()
+    {
+        $expectedContext = $this->createContextStub();
+
+        $consumer = new QueueConsumer($expectedContext, null, [], null, 0, 0);
 
-        $this->assertSame($expectedConnection, $consumer->getContext());
+        $this->assertSame($expectedContext, $consumer->getContext());
     }
 
     public function testThrowIfQueueNameEmptyOnBind()
     {
         $processorMock = $this->createProcessorMock();
 
-        $consumer = new QueueConsumer($this->createContextStub(), null, 0);
+        $consumer = new QueueConsumer($this->createContextStub());
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('The queue name must be not empty.');
@@ -70,7 +101,7 @@ public function testThrowIfQueueAlreadyBoundToProcessorOnBind()
     {
         $processorMock = $this->createProcessorMock();
 
-        $consumer = new QueueConsumer($this->createContextStub(), null, 0);
+        $consumer = new QueueConsumer($this->createContextStub());
 
         $consumer->bind(new NullQueue('theQueueName'), $processorMock);
 
@@ -84,40 +115,44 @@ public function testShouldAllowBindProcessorToQueue()
         $queue = new NullQueue('theQueueName');
         $processorMock = $this->createProcessorMock();
 
-        $consumer = new QueueConsumer($this->createContextStub(), null, 0);
+        $consumer = new QueueConsumer($this->createContextStub());
 
         $consumer->bind($queue, $processorMock);
 
-        $this->assertAttributeSame(['theQueueName' => [$queue, $processorMock]], 'boundProcessors', $consumer);
+        $this->assertAttributeEquals(
+            ['theQueueName' => new BoundProcessor($queue, $processorMock)],
+            'boundProcessors',
+            $consumer
+        );
     }
 
     public function testThrowIfQueueNeitherInstanceOfQueueNorString()
     {
         $processorMock = $this->createProcessorMock();
 
-        $consumer = new QueueConsumer($this->createContextStub(), null, 0);
+        $consumer = new QueueConsumer($this->createContextStub());
 
         $this->expectException(InvalidArgumentException::class);
         $this->expectExceptionMessage('The argument must be an instance of Interop\Queue\Queue but got stdClass.');
         $consumer->bind(new \stdClass(), $processorMock);
     }
 
-    public function testCouldSetGetIdleTimeout()
+    public function testCouldSetGetIdleTime()
     {
-        $consumer = new QueueConsumer($this->createContextStub(), null, 0);
+        $consumer = new QueueConsumer($this->createContextStub());
 
-        $consumer->setIdleTimeout(123456.1);
+        $consumer->setIdleTime(123456);
 
-        $this->assertSame(123456.1, $consumer->getIdleTimeout());
+        $this->assertSame(123456, $consumer->getIdleTime());
     }
 
     public function testCouldSetGetReceiveTimeout()
     {
-        $consumer = new QueueConsumer($this->createContextStub(), null, 0);
+        $consumer = new QueueConsumer($this->createContextStub());
 
-        $consumer->setReceiveTimeout(123456.1);
+        $consumer->setReceiveTimeout(123456);
 
-        $this->assertSame(123456.1, $consumer->getReceiveTimeout());
+        $this->assertSame(123456, $consumer->getReceiveTimeout());
     }
 
     public function testShouldAllowBindCallbackToQueueName()
@@ -136,7 +171,7 @@ public function testShouldAllowBindCallbackToQueueName()
             ->willReturn($queue)
         ;
 
-        $consumer = new QueueConsumer($context, null, 0);
+        $consumer = new QueueConsumer($context);
 
         $consumer->bindCallback($queueName, $callback);
 
@@ -146,17 +181,16 @@ public function testShouldAllowBindCallbackToQueueName()
         $this->assertCount(1, $boundProcessors);
         $this->assertArrayHasKey($queueName, $boundProcessors);
 
-        $this->assertInternalType('array', $boundProcessors[$queueName]);
-        $this->assertCount(2, $boundProcessors[$queueName]);
-        $this->assertSame($queue, $boundProcessors[$queueName][0]);
-        $this->assertInstanceOf(CallbackProcessor::class, $boundProcessors[$queueName][1]);
+        $this->assertInstanceOf(BoundProcessor::class, $boundProcessors[$queueName]);
+        $this->assertSame($queue, $boundProcessors[$queueName]->getQueue());
+        $this->assertInstanceOf(CallbackProcessor::class, $boundProcessors[$queueName]->getProcessor());
     }
 
     public function testShouldReturnSelfOnBind()
     {
         $processorMock = $this->createProcessorMock();
 
-        $consumer = new QueueConsumer($this->createContextStub(), null, 0);
+        $consumer = new QueueConsumer($this->createContextStub());
 
         $this->assertSame($consumer, $consumer->bind(new NullQueue('foo_queue'), $processorMock));
     }
@@ -271,7 +305,7 @@ public function testShouldSubscribeToGivenQueueWithExpectedTimeout()
             ->method('process')
         ;
 
-        $queueConsumer = new QueueConsumer($contextMock, new BreakCycleExtension(1), 0, 12345);
+        $queueConsumer = new QueueConsumer($contextMock, new BreakCycleExtension(1), [], null, 0, 12345);
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind($expectedQueue, $processorMock);
         $queueConsumer->consume();
@@ -302,7 +336,7 @@ public function testShouldSubscribeToGivenQueueAndQuitAfterFifthIdleCycle()
             ->method('process')
         ;
 
-        $queueConsumer = new QueueConsumer($contextMock, new BreakCycleExtension(5), 0);
+        $queueConsumer = new QueueConsumer($contextMock, new BreakCycleExtension(5));
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind($expectedQueue, $processorMock);
         $queueConsumer->consume();
@@ -365,7 +399,7 @@ public function testShouldAckMessageIfProcessorReturnSuchStatus()
             ->willReturn(Result::ACK)
         ;
 
-        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1), 0);
+        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1));
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -391,7 +425,7 @@ public function testThrowIfProcessorReturnNull()
             ->willReturn(null)
         ;
 
-        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1), 0);
+        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1));
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -424,7 +458,7 @@ public function testShouldRejectMessageIfProcessorReturnSuchStatus()
             ->willReturn(Result::REJECT)
         ;
 
-        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1), 0);
+        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1));
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -455,7 +489,7 @@ public function testShouldRequeueMessageIfProcessorReturnSuchStatus()
             ->willReturn(Result::REQUEUE)
         ;
 
-        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1), 0);
+        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1));
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -481,7 +515,7 @@ public function testThrowIfProcessorReturnInvalidStatus()
             ->willReturn('invalidStatus')
         ;
 
-        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1), 0);
+        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1));
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -518,7 +552,7 @@ public function testShouldNotPassMessageToProcessorIfItWasProcessedByExtension()
         ;
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
-        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -537,22 +571,43 @@ public function testShouldCallOnStartExtensionMethod()
         $extension
             ->expects($this->once())
             ->method('onStart')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use ($contextStub) {
+            ->with($this->isInstanceOf(Start::class))
+            ->willReturnCallback(function (Start $context) use ($contextStub) {
                 $this->assertSame($contextStub, $context->getInteropContext());
-                $this->assertNull($context->getConsumer());
-                $this->assertNull($context->getProcessor());
-                $this->assertNull($context->getLogger());
-                $this->assertNull($context->getInteropMessage());
-                $this->assertNull($context->getException());
-                $this->assertNull($context->getResult());
-                $this->assertNull($context->getInteropQueue());
-                $this->assertFalse($context->isExecutionInterrupted());
+                $this->assertInstanceOf(NullLogger::class, $context->getLogger());
             })
         ;
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
-        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
+        $queueConsumer->setFallbackSubscriptionConsumer(new DummySubscriptionConsumer());
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
+
+        $queueConsumer->consume();
+    }
+
+    public function testShouldCallOnStartWithLoggerProvidedInConstructor()
+    {
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createContextStub($consumerStub);
+
+        $processorMock = $this->createProcessorMock();
+
+        $expectedLogger = $this->createMock(LoggerInterface::class);
+
+        $extension = $this->createExtension();
+        $extension
+            ->expects($this->once())
+            ->method('onStart')
+            ->with($this->isInstanceOf(Start::class))
+            ->willReturnCallback(function (Start $context) use ($expectedLogger) {
+                $this->assertSame($expectedLogger, $context->getLogger());
+            })
+        ;
+
+        $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, [], $expectedLogger);
         $queueConsumer->setFallbackSubscriptionConsumer(new DummySubscriptionConsumer());
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -585,7 +640,7 @@ public function testShouldCallOnIdleExtensionMethod()
         ;
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
-        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
         $queueConsumer->setFallbackSubscriptionConsumer(new DummySubscriptionConsumer());
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -621,7 +676,7 @@ public function testShouldCallOnBeforeReceiveExtensionMethod()
         ;
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
-        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
         $queueConsumer->setFallbackSubscriptionConsumer(new DummySubscriptionConsumer());
         $queueConsumer->bind($queue, $processorMock);
 
@@ -664,7 +719,7 @@ public function testShouldCallOnPreReceivedExtensionMethodWithExpectedContext()
         ;
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
-        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -707,7 +762,7 @@ public function testShouldCallOnResultExtensionMethodWithExpectedContext()
         ;
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
-        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -750,7 +805,7 @@ public function testShouldCallOnPostReceivedExtensionMethodWithExpectedContext()
         ;
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
-        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -791,7 +846,7 @@ public function testShouldAllowInterruptConsumingOnIdle()
         ;
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
-        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
         $queueConsumer->setFallbackSubscriptionConsumer(new DummySubscriptionConsumer());
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -821,7 +876,7 @@ public function testShouldNotCloseContextWhenConsumptionInterrupted()
         ;
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
-        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
         $queueConsumer->setFallbackSubscriptionConsumer(new DummySubscriptionConsumer());
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -850,7 +905,7 @@ public function testShouldNotCloseContextWhenConsumptionInterruptedByException()
             ->willThrowException($expectedException)
         ;
 
-        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1), 0);
+        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1));
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -893,7 +948,7 @@ public function testShouldSetMainExceptionAsPreviousToExceptionThrownOnInterrupt
         ;
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
-        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -958,7 +1013,7 @@ public function testShouldAllowInterruptConsumingOnPreReceiveButProcessCurrentMe
         ;
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
-        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -1014,7 +1069,7 @@ public function testShouldAllowInterruptConsumingOnResult()
         ;
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
-        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -1070,7 +1125,7 @@ public function testShouldAllowInterruptConsumingOnPostReceive()
         ;
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
-        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -1120,7 +1175,7 @@ public function testShouldCallOnInterruptedIfExceptionThrow()
         ;
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
-        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -1151,7 +1206,7 @@ public function testShouldCallExtensionPassedOnRuntime()
         $runtimeExtension
             ->expects($this->once())
             ->method('onStart')
-            ->with($this->isInstanceOf(Context::class))
+            ->with($this->isInstanceOf(Start::class))
         ;
         $runtimeExtension
             ->expects($this->once())
@@ -1174,7 +1229,7 @@ public function testShouldCallExtensionPassedOnRuntime()
             ->with($this->isInstanceOf(Context::class))
         ;
 
-        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1), 0);
+        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1));
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -1205,9 +1260,9 @@ public function testShouldChangeLoggerOnStart()
         $extension
             ->expects($this->atLeastOnce())
             ->method('onStart')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use ($expectedLogger) {
-                $context->setLogger($expectedLogger);
+            ->with($this->isInstanceOf(Start::class))
+            ->willReturnCallback(function (Start $context) use ($expectedLogger) {
+                $context->changeLogger($expectedLogger);
             })
         ;
         $extension
@@ -1229,7 +1284,7 @@ public function testShouldChangeLoggerOnStart()
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
 
-        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, 0);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
@@ -1290,7 +1345,7 @@ public function testShouldCallProcessorAsMessageComeAlong()
             })
         ;
 
-        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(3), 0);
+        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(3));
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer
             ->bind($queue1, $processorMock)

From 4f9e378eacdeade314d0b8b8dda40c00fd920d0e Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 5 Oct 2018 10:34:47 +0300
Subject: [PATCH 146/337] [consumption] fix onStart extension tests.

---
 .../SetupBrokerExtension.php                  |  7 ++-----
 Consumption/Extension/LoggerExtension.php     | 11 +++++-----
 Consumption/Extension/NicenessExtension.php   |  7 ++-----
 Consumption/Extension/SignalExtension.php     |  6 ++----
 .../QueueConsumerOptionsCommandTrait.php      |  4 ++--
 .../FlushSpoolProducerExtensionTest.php       |  6 +++++-
 .../SetupBrokerExtensionTest.php              |  8 +++----
 Tests/Consumption/ChainExtensionTest.php      | 12 ++++++++++-
 .../Extension/LoggerExtensionTest.php         |  9 ++++----
 .../Extension/NicenessExtensionTest.php       | 21 +++++++------------
 .../Extension/ReplyExtensionTest.php          |  3 ++-
 Tests/Symfony/Client/ConsumeCommandTest.php   | 14 +++++++++++++
 Tests/Symfony/Client/ProduceCommandTest.php   | 14 +++++++++++++
 Tests/Symfony/Client/RoutesCommandTest.php    |  4 ++--
 .../Symfony/Client/SetupBrokerCommandTest.php | 14 +++++++++++++
 .../ConfigurableConsumeCommandTest.php        | 14 +++++++++++++
 .../Consumption/ConsumeCommandTest.php        | 14 +++++++++++++
 .../QueueConsumerOptionsCommandTraitTest.php  | 10 ++++-----
 18 files changed, 124 insertions(+), 54 deletions(-)

diff --git a/Client/ConsumptionExtension/SetupBrokerExtension.php b/Client/ConsumptionExtension/SetupBrokerExtension.php
index 8b6aecb..d055ffd 100644
--- a/Client/ConsumptionExtension/SetupBrokerExtension.php
+++ b/Client/ConsumptionExtension/SetupBrokerExtension.php
@@ -3,7 +3,7 @@
 namespace Enqueue\Client\ConsumptionExtension;
 
 use Enqueue\Client\DriverInterface;
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface;
 
@@ -30,10 +30,7 @@ public function __construct(DriverInterface $driver)
         $this->isDone = false;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function onStart(Context $context)
+    public function onStart(Start $context): void
     {
         if (false == $this->isDone) {
             $this->isDone = true;
diff --git a/Consumption/Extension/LoggerExtension.php b/Consumption/Extension/LoggerExtension.php
index b498e21..7fff127 100644
--- a/Consumption/Extension/LoggerExtension.php
+++ b/Consumption/Extension/LoggerExtension.php
@@ -3,11 +3,13 @@
 namespace Enqueue\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Consumption\Result;
 use Interop\Queue\Message as InteropMessage;
 use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
 
 class LoggerExtension implements ExtensionInterface
 {
@@ -26,19 +28,16 @@ public function __construct(LoggerInterface $logger)
         $this->logger = $logger;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function onStart(Context $context)
+    public function onStart(Start $context): void
     {
-        if ($context->getLogger()) {
+        if ($context->getLogger() && false == $context->getLogger() instanceof NullLogger) {
             $context->getLogger()->debug(sprintf(
                 'Skip setting context\'s logger "%s". Another one "%s" has already been set.',
                 get_class($this->logger),
                 get_class($context->getLogger())
             ));
         } else {
-            $context->setLogger($this->logger);
+            $context->changeLogger($this->logger);
             $this->logger->debug(sprintf('Set context\'s logger "%s"', get_class($this->logger)));
         }
     }
diff --git a/Consumption/Extension/NicenessExtension.php b/Consumption/Extension/NicenessExtension.php
index 01a5383..6df4f14 100644
--- a/Consumption/Extension/NicenessExtension.php
+++ b/Consumption/Extension/NicenessExtension.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Consumption\Extension;
 
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface;
 
@@ -32,10 +32,7 @@ public function __construct($niceness)
         $this->niceness = $niceness;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function onStart(Context $context)
+    public function onStart(Start $context): void
     {
         if (0 !== $this->niceness) {
             $changed = @proc_nice($this->niceness);
diff --git a/Consumption/Extension/SignalExtension.php b/Consumption/Extension/SignalExtension.php
index a8b53e8..e4b416d 100644
--- a/Consumption/Extension/SignalExtension.php
+++ b/Consumption/Extension/SignalExtension.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\Exception\LogicException;
 use Enqueue\Consumption\ExtensionInterface;
@@ -22,10 +23,7 @@ class SignalExtension implements ExtensionInterface
      */
     protected $logger;
 
-    /**
-     * {@inheritdoc}
-     */
-    public function onStart(Context $context)
+    public function onStart(Start $context): void
     {
         if (false == extension_loaded('pcntl')) {
             throw new LogicException('The pcntl extension is required in order to catch signals.');
diff --git a/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php b/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php
index d924f63..61d1302 100644
--- a/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php
+++ b/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php
@@ -26,11 +26,11 @@ protected function configureQueueConsumerOptions()
     protected function setQueueConsumerOptions(QueueConsumerInterface $consumer, InputInterface $input)
     {
         if (null !== $idleTimeout = $input->getOption('idle-time')) {
-            $consumer->setIdleTime((float) $idleTimeout);
+            $consumer->setIdleTime((int) $idleTimeout);
         }
 
         if (null !== $receiveTimeout = $input->getOption('receive-timeout')) {
-            $consumer->setReceiveTimeout((float) $receiveTimeout);
+            $consumer->setReceiveTimeout((int) $receiveTimeout);
         }
     }
 }
diff --git a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
index 1469c99..2beabe5 100644
--- a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
@@ -5,9 +5,11 @@
 use Enqueue\Client\ConsumptionExtension\FlushSpoolProducerExtension;
 use Enqueue\Client\SpoolProducer;
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
+use Psr\Log\NullLogger;
 
 class FlushSpoolProducerExtensionTest extends TestCase
 {
@@ -32,7 +34,9 @@ public function testShouldDoNothingOnStart()
         ;
 
         $extension = new FlushSpoolProducerExtension($producer);
-        $extension->onStart($this->createContextMock());
+        $extension->onStart(
+            new Start($this->createMock(\Interop\Queue\Context::class), new NullLogger(), [], 0, 0, 0)
+        );
     }
 
     public function testShouldDoNothingOnBeforeReceive()
diff --git a/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php b/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php
index 1339dd4..6ab0a9e 100644
--- a/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php
@@ -4,7 +4,7 @@
 
 use Enqueue\Client\ConsumptionExtension\SetupBrokerExtension;
 use Enqueue\Client\DriverInterface;
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Test\ClassExtensionTrait;
 use Interop\Queue\Context as InteropContext;
@@ -36,8 +36,7 @@ public function testShouldSetupBroker()
             ->with($this->identicalTo($logger))
         ;
 
-        $context = new Context($this->createMock(InteropContext::class));
-        $context->setLogger($logger);
+        $context = new Start($this->createMock(InteropContext::class), $logger, [], 0, 0, 0);
 
         $extension = new SetupBrokerExtension($driver);
         $extension->onStart($context);
@@ -54,8 +53,7 @@ public function testShouldSetupBrokerOnlyOnce()
             ->with($this->identicalTo($logger))
         ;
 
-        $context = new Context($this->createMock(InteropContext::class));
-        $context->setLogger($logger);
+        $context = new Start($this->createMock(InteropContext::class), $logger, [], 0, 0, 0);
 
         $extension = new SetupBrokerExtension($driver);
         $extension->onStart($context);
diff --git a/Tests/Consumption/ChainExtensionTest.php b/Tests/Consumption/ChainExtensionTest.php
index 4c412c6..a07e2eb 100644
--- a/Tests/Consumption/ChainExtensionTest.php
+++ b/Tests/Consumption/ChainExtensionTest.php
@@ -4,9 +4,11 @@
 
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
 
 class ChainExtensionTest extends TestCase
 {
@@ -24,7 +26,7 @@ public function testCouldBeConstructedWithExtensionsArray()
 
     public function testShouldProxyOnStartToAllInternalExtensions()
     {
-        $context = $this->createContextMock();
+        $context = new Start($this->createMock(\Interop\Queue\Context::class), $this->createLoggerMock(), [], 0, 0, 0);
 
         $fooExtension = $this->createExtension();
         $fooExtension
@@ -176,6 +178,14 @@ public function testShouldProxyOnInterruptedToAllInternalExtensions()
         $extensions->onInterrupted($context);
     }
 
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected function createLoggerMock(): LoggerInterface
+    {
+        return $this->createMock(LoggerInterface::class);
+    }
+
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|Context
      */
diff --git a/Tests/Consumption/Extension/LoggerExtensionTest.php b/Tests/Consumption/Extension/LoggerExtensionTest.php
index 6bd05ef..8131410 100644
--- a/Tests/Consumption/Extension/LoggerExtensionTest.php
+++ b/Tests/Consumption/Extension/LoggerExtensionTest.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Tests\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\Extension\LoggerExtension;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Consumption\Result;
@@ -12,6 +13,7 @@
 use Interop\Queue\Context as InteropContext;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
 
 class LoggerExtensionTest extends TestCase
 {
@@ -33,7 +35,7 @@ public function testShouldSetLoggerToContextOnStart()
 
         $extension = new LoggerExtension($logger);
 
-        $context = new Context($this->createContextMock());
+        $context = new Start($this->createContextMock(), new NullLogger(), [], 0, 0, 0);
 
         $extension->onStart($context);
 
@@ -51,7 +53,7 @@ public function testShouldAddInfoMessageOnStart()
 
         $extension = new LoggerExtension($logger);
 
-        $context = new Context($this->createContextMock());
+        $context = new Start($this->createContextMock(), new NullLogger(), [], 0, 0, 0);
 
         $extension->onStart($context);
     }
@@ -168,8 +170,7 @@ public function testShouldNotSetLoggerIfOneHasBeenSetOnStart()
 
         $extension = new LoggerExtension($logger);
 
-        $context = new Context($this->createContextMock());
-        $context->setLogger($alreadySetLogger);
+        $context = new Start($this->createContextMock(), $alreadySetLogger, [], 0, 0, 0);
 
         $extension->onStart($context);
     }
diff --git a/Tests/Consumption/Extension/NicenessExtensionTest.php b/Tests/Consumption/Extension/NicenessExtensionTest.php
index 36a80d3..26bbf03 100644
--- a/Tests/Consumption/Extension/NicenessExtensionTest.php
+++ b/Tests/Consumption/Extension/NicenessExtensionTest.php
@@ -2,13 +2,11 @@
 
 namespace Enqueue\Tests\Consumption\Extension;
 
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\Extension\NicenessExtension;
-use Interop\Queue\Consumer;
 use Interop\Queue\Context as InteropContext;
-use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
-use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
 
 class NicenessExtensionTest extends TestCase
 {
@@ -28,20 +26,17 @@ public function testShouldThrowWarningOnInvalidArgument()
         $this->expectException(\InvalidArgumentException::class);
         $this->expectExceptionMessage('proc_nice(): Only a super user may attempt to increase the priority of a process');
 
+        $context = new Start($this->createContextMock(), new NullLogger(), [], 0, 0, 0);
+
         $extension = new NicenessExtension(-1);
-        $extension->onStart($this->createContext());
+        $extension->onStart($context);
     }
 
     /**
-     * @return Context
+     * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
      */
-    protected function createContext(): Context
+    protected function createContextMock(): InteropContext
     {
-        $context = new Context($this->createMock(InteropContext::class));
-        $context->setLogger($this->createMock(LoggerInterface::class));
-        $context->setConsumer($this->createMock(Consumer::class));
-        $context->setProcessor($this->createMock(Processor::class));
-
-        return $context;
+        return $this->createMock(InteropContext::class);
     }
 }
diff --git a/Tests/Consumption/Extension/ReplyExtensionTest.php b/Tests/Consumption/Extension/ReplyExtensionTest.php
index 09802d3..3287095 100644
--- a/Tests/Consumption/Extension/ReplyExtensionTest.php
+++ b/Tests/Consumption/Extension/ReplyExtensionTest.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Tests\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\Extension\ReplyExtension;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Consumption\Result;
@@ -39,7 +40,7 @@ public function testShouldDoNothingOnStart()
     {
         $extension = new ReplyExtension();
 
-        $extension->onStart(new Context($this->createNeverUsedContextMock()));
+        $extension->onStart(new Start($this->createNeverUsedContextMock(), new NullLogger(), [], 0, 0, 0));
     }
 
     public function testShouldDoNothingOnBeforeReceive()
diff --git a/Tests/Symfony/Client/ConsumeCommandTest.php b/Tests/Symfony/Client/ConsumeCommandTest.php
index 54e15ed..a600efa 100644
--- a/Tests/Symfony/Client/ConsumeCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeCommandTest.php
@@ -12,12 +12,26 @@
 use Enqueue\Container\Container;
 use Enqueue\Null\NullQueue;
 use Enqueue\Symfony\Client\ConsumeCommand;
+use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
 use Psr\Container\ContainerInterface;
+use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Tester\CommandTester;
 
 class ConsumeCommandTest extends TestCase
 {
+    use ClassExtensionTrait;
+
+    public function testShouldBeSubClassOfCommand()
+    {
+        $this->assertClassExtends(Command::class, ConsumeCommand::class);
+    }
+
+    public function testShouldNotBeFinal()
+    {
+        $this->assertClassNotFinal(ConsumeCommand::class);
+    }
+
     public function testCouldBeConstructedWithRequiredAttributes()
     {
         new ConsumeCommand($this->createMock(ContainerInterface::class));
diff --git a/Tests/Symfony/Client/ProduceCommandTest.php b/Tests/Symfony/Client/ProduceCommandTest.php
index 51756b4..321b4ee 100644
--- a/Tests/Symfony/Client/ProduceCommandTest.php
+++ b/Tests/Symfony/Client/ProduceCommandTest.php
@@ -5,12 +5,26 @@
 use Enqueue\Client\ProducerInterface;
 use Enqueue\Container\Container;
 use Enqueue\Symfony\Client\ProduceCommand;
+use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
 use Psr\Container\ContainerInterface;
+use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Tester\CommandTester;
 
 class ProduceCommandTest extends TestCase
 {
+    use ClassExtensionTrait;
+
+    public function testShouldBeSubClassOfCommand()
+    {
+        $this->assertClassExtends(Command::class, ProduceCommand::class);
+    }
+
+    public function testShouldNotBeFinal()
+    {
+        $this->assertClassNotFinal(ProduceCommand::class);
+    }
+
     public function testCouldBeConstructedWithContainerAsFirstArgument()
     {
         new ProduceCommand($this->createMock(ContainerInterface::class));
diff --git a/Tests/Symfony/Client/RoutesCommandTest.php b/Tests/Symfony/Client/RoutesCommandTest.php
index 4f51884..daab37e 100644
--- a/Tests/Symfony/Client/RoutesCommandTest.php
+++ b/Tests/Symfony/Client/RoutesCommandTest.php
@@ -23,9 +23,9 @@ public function testShouldBeSubClassOfCommand()
         $this->assertClassExtends(Command::class, RoutesCommand::class);
     }
 
-    public function testShouldBeFinal()
+    public function testShouldNotBeFinal()
     {
-        $this->assertClassFinal(RoutesCommand::class);
+        $this->assertClassNotFinal(RoutesCommand::class);
     }
 
     public function testCouldBeConstructedWithConfigAndRouteCollectionAsArguments()
diff --git a/Tests/Symfony/Client/SetupBrokerCommandTest.php b/Tests/Symfony/Client/SetupBrokerCommandTest.php
index 65ee306..b81daf2 100644
--- a/Tests/Symfony/Client/SetupBrokerCommandTest.php
+++ b/Tests/Symfony/Client/SetupBrokerCommandTest.php
@@ -5,12 +5,26 @@
 use Enqueue\Client\DriverInterface;
 use Enqueue\Container\Container;
 use Enqueue\Symfony\Client\SetupBrokerCommand;
+use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
 use Psr\Container\ContainerInterface;
+use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Tester\CommandTester;
 
 class SetupBrokerCommandTest extends TestCase
 {
+    use ClassExtensionTrait;
+
+    public function testShouldBeSubClassOfCommand()
+    {
+        $this->assertClassExtends(Command::class, SetupBrokerCommand::class);
+    }
+
+    public function testShouldNotBeFinal()
+    {
+        $this->assertClassNotFinal(SetupBrokerCommand::class);
+    }
+
     public function testCouldBeConstructedWithContainerAsFirstArgument()
     {
         new SetupBrokerCommand($this->createMock(ContainerInterface::class));
diff --git a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
index ef50ad2..ae6c4be 100644
--- a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
@@ -8,16 +8,30 @@
 use Enqueue\Consumption\QueueSubscriberInterface;
 use Enqueue\Container\Container;
 use Enqueue\Symfony\Consumption\ConfigurableConsumeCommand;
+use Enqueue\Test\ClassExtensionTrait;
 use Interop\Queue\Context;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Processor;
 use Interop\Queue\Queue as InteropQueue;
 use PHPUnit\Framework\TestCase;
 use Psr\Container\ContainerInterface;
+use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Tester\CommandTester;
 
 class ConfigurableConsumeCommandTest extends TestCase
 {
+    use ClassExtensionTrait;
+
+    public function testShouldBeSubClassOfCommand()
+    {
+        $this->assertClassExtends(Command::class, ConfigurableConsumeCommand::class);
+    }
+
+    public function testShouldNotBeFinal()
+    {
+        $this->assertClassNotFinal(ConfigurableConsumeCommand::class);
+    }
+
     public function testCouldBeConstructedWithRequiredAttributes()
     {
         new ConfigurableConsumeCommand($this->createMock(ContainerInterface::class));
diff --git a/Tests/Symfony/Consumption/ConsumeCommandTest.php b/Tests/Symfony/Consumption/ConsumeCommandTest.php
index 70bc572..fe38c6c 100644
--- a/Tests/Symfony/Consumption/ConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConsumeCommandTest.php
@@ -6,12 +6,26 @@
 use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Container\Container;
 use Enqueue\Symfony\Consumption\ConsumeCommand;
+use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
 use Psr\Container\ContainerInterface;
+use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Tester\CommandTester;
 
 class ConsumeCommandTest extends TestCase
 {
+    use ClassExtensionTrait;
+
+    public function testShouldBeSubClassOfCommand()
+    {
+        $this->assertClassExtends(Command::class, ConsumeCommand::class);
+    }
+
+    public function testShouldNotBeFinal()
+    {
+        $this->assertClassNotFinal(ConsumeCommand::class);
+    }
+
     public function testCouldBeConstructedWithRequiredAttributes()
     {
         new ConsumeCommand($this->createMock(ContainerInterface::class));
diff --git a/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php b/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php
index 7479172..befee29 100644
--- a/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php
+++ b/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php
@@ -25,21 +25,21 @@ public function testShouldSetQueueConsumerOptions()
         $consumer = $this->createQueueConsumer();
         $consumer
             ->expects($this->once())
-            ->method('setIdleTimeout')
-            ->with($this->identicalTo(123.1))
+            ->method('setIdleTime')
+            ->with($this->identicalTo(123))
         ;
         $consumer
             ->expects($this->once())
             ->method('setReceiveTimeout')
-            ->with($this->identicalTo(456.1))
+            ->with($this->identicalTo(456))
         ;
 
         $trait = new QueueConsumerOptionsCommand($consumer);
 
         $tester = new CommandTester($trait);
         $tester->execute([
-            '--idle-time' => '123.1',
-            '--receive-timeout' => '456.1',
+            '--idle-time' => '123',
+            '--receive-timeout' => '456',
         ]);
     }
 

From 91ae859ea69b9258b379156ca06e1aeb15d87225 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 5 Oct 2018 10:57:09 +0300
Subject: [PATCH 147/337] [consumption] Add preSubscribe extension point.

---
 Consumption/ChainExtension.php           | 26 ++++-------
 Consumption/Context/PreSubscribe.php     | 59 ++++++++++++++++++++++++
 Consumption/Context/Start.php            |  2 +-
 Consumption/EmptyExtensionTrait.php      | 23 ++-------
 Consumption/ExtensionInterface.php       |  6 +++
 Consumption/QueueConsumer.php            | 16 ++++++-
 Tests/Consumption/ChainExtensionTest.php | 54 +++++++++++++++++++++-
 Tests/Consumption/QueueConsumerTest.php  | 57 ++++++++++++++++++++++-
 8 files changed, 203 insertions(+), 40 deletions(-)
 create mode 100644 Consumption/Context/PreSubscribe.php

diff --git a/Consumption/ChainExtension.php b/Consumption/ChainExtension.php
index 85fc1b3..2b9d717 100644
--- a/Consumption/ChainExtension.php
+++ b/Consumption/ChainExtension.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
 
 class ChainExtension implements ExtensionInterface
@@ -31,9 +32,13 @@ public function onStart(Start $context): void
         }
     }
 
-    /**
-     * @param Context $context
-     */
+    public function preSubscribe(PreSubscribe $context): void
+    {
+        foreach ($this->extensions as $extension) {
+            $extension->preSubscribe($context);
+        }
+    }
+
     public function onBeforeReceive(Context $context)
     {
         foreach ($this->extensions as $extension) {
@@ -41,9 +46,6 @@ public function onBeforeReceive(Context $context)
         }
     }
 
-    /**
-     * @param Context $context
-     */
     public function onPreReceived(Context $context)
     {
         foreach ($this->extensions as $extension) {
@@ -51,9 +53,6 @@ public function onPreReceived(Context $context)
         }
     }
 
-    /**
-     * @param Context $context
-     */
     public function onResult(Context $context)
     {
         foreach ($this->extensions as $extension) {
@@ -61,9 +60,6 @@ public function onResult(Context $context)
         }
     }
 
-    /**
-     * @param Context $context
-     */
     public function onPostReceived(Context $context)
     {
         foreach ($this->extensions as $extension) {
@@ -71,9 +67,6 @@ public function onPostReceived(Context $context)
         }
     }
 
-    /**
-     * @param Context $context
-     */
     public function onIdle(Context $context)
     {
         foreach ($this->extensions as $extension) {
@@ -81,9 +74,6 @@ public function onIdle(Context $context)
         }
     }
 
-    /**
-     * @param Context $context
-     */
     public function onInterrupted(Context $context)
     {
         foreach ($this->extensions as $extension) {
diff --git a/Consumption/Context/PreSubscribe.php b/Consumption/Context/PreSubscribe.php
new file mode 100644
index 0000000..be14ba3
--- /dev/null
+++ b/Consumption/Context/PreSubscribe.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Enqueue\Consumption\Context;
+
+use Interop\Queue\Consumer;
+use Interop\Queue\Context;
+use Interop\Queue\Processor;
+use Psr\Log\LoggerInterface;
+
+class PreSubscribe
+{
+    /**
+     * @var Context
+     */
+    private $context;
+
+    /**
+     * @var Processor
+     */
+    private $processor;
+
+    /**
+     * @var Consumer
+     */
+    private $consumer;
+
+    /**
+     * @var LoggerInterface
+     */
+    private $logger;
+
+    public function __construct(Context $context, Processor $processor, Consumer $consumer, LoggerInterface $logger)
+    {
+        $this->context = $context;
+        $this->processor = $processor;
+        $this->consumer = $consumer;
+        $this->logger = $logger;
+    }
+
+    public function getContext(): Context
+    {
+        return $this->context;
+    }
+
+    public function getProcessor(): Processor
+    {
+        return $this->processor;
+    }
+
+    public function getConsumer(): Consumer
+    {
+        return $this->consumer;
+    }
+
+    public function getLogger(): LoggerInterface
+    {
+        return $this->logger;
+    }
+}
diff --git a/Consumption/Context/Start.php b/Consumption/Context/Start.php
index 88a12e7..f3cd292 100644
--- a/Consumption/Context/Start.php
+++ b/Consumption/Context/Start.php
@@ -58,7 +58,7 @@ public function __construct(Context $context, LoggerInterface $logger, array $pr
         $this->executionInterrupted = false;
     }
 
-    public function getInteropContext(): Context
+    public function getContext(): Context
     {
         return $this->context;
     }
diff --git a/Consumption/EmptyExtensionTrait.php b/Consumption/EmptyExtensionTrait.php
index f474d11..0ad021d 100644
--- a/Consumption/EmptyExtensionTrait.php
+++ b/Consumption/EmptyExtensionTrait.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
 
 trait EmptyExtensionTrait
@@ -10,44 +11,30 @@ public function onStart(Start $context): void
     {
     }
 
-    /**
-     * @param Context $context
-     */
+    public function preSubscribe(PreSubscribe $preSubscribe): void
+    {
+    }
+
     public function onBeforeReceive(Context $context)
     {
     }
 
-    /**
-     * @param Context $context
-     */
     public function onPreReceived(Context $context)
     {
     }
 
-    /**
-     * @param Context $context
-     */
     public function onResult(Context $context)
     {
     }
 
-    /**
-     * @param Context $context
-     */
     public function onPostReceived(Context $context)
     {
     }
 
-    /**
-     * @param Context $context
-     */
     public function onIdle(Context $context)
     {
     }
 
-    /**
-     * @param Context $context
-     */
     public function onInterrupted(Context $context)
     {
     }
diff --git a/Consumption/ExtensionInterface.php b/Consumption/ExtensionInterface.php
index 902b969..e29f0f7 100644
--- a/Consumption/ExtensionInterface.php
+++ b/Consumption/ExtensionInterface.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
 
 interface ExtensionInterface
@@ -12,6 +13,11 @@ interface ExtensionInterface
      */
     public function onStart(Start $context): void;
 
+    /**
+     * The method is called for each BoundProcessor before calling SubscriptionConsumer::subscribe method.
+     */
+    public function preSubscribe(PreSubscribe $context): void;
+
     /**
      * Executed at every new cycle before we asked a broker for a new message.
      * At this stage the context already contains processor, consumer and queue.
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index ca6672c..db8530f 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\Exception\ConsumptionInterruptedException;
 use Enqueue\Consumption\Exception\InvalidArgumentException;
@@ -168,6 +169,10 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
 
         $this->extension->onStart($start);
 
+        // todo
+        if ($start->isExecutionInterrupted()) {
+        }
+
         $this->logger = $start->getLogger();
         $this->idleTime = $start->getIdleTime();
         $this->receiveTimeout = $start->getReceiveTimeout();
@@ -182,7 +187,16 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
         foreach ($this->boundProcessors as $boundProcessor) {
             $queue = $boundProcessor->getQueue();
 
-            $consumers[$queue->getQueueName()] = $this->interopContext->createConsumer($queue);
+            $preSubscribe = new PreSubscribe(
+                $this->interopContext,
+                $boundProcessor->getProcessor(),
+                $this->interopContext->createConsumer($queue),
+                $this->logger
+            );
+
+            $this->extension->preSubscribe($preSubscribe);
+
+            $consumers[$queue->getQueueName()] = $preSubscribe->getConsumer();
         }
 
         // todo remove
diff --git a/Tests/Consumption/ChainExtensionTest.php b/Tests/Consumption/ChainExtensionTest.php
index a07e2eb..d0eacdf 100644
--- a/Tests/Consumption/ChainExtensionTest.php
+++ b/Tests/Consumption/ChainExtensionTest.php
@@ -4,6 +4,7 @@
 
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Test\ClassExtensionTrait;
@@ -26,7 +27,7 @@ public function testCouldBeConstructedWithExtensionsArray()
 
     public function testShouldProxyOnStartToAllInternalExtensions()
     {
-        $context = new Start($this->createMock(\Interop\Queue\Context::class), $this->createLoggerMock(), [], 0, 0, 0);
+        $context = new Start($this->createInteropContextMock(), $this->createLoggerMock(), [], 0, 0, 0);
 
         $fooExtension = $this->createExtension();
         $fooExtension
@@ -46,6 +47,33 @@ public function testShouldProxyOnStartToAllInternalExtensions()
         $extensions->onStart($context);
     }
 
+    public function testShouldProxyPreSubscribeToAllInternalExtensions()
+    {
+        $context = new PreSubscribe(
+            $this->createInteropContextMock(),
+            $this->createInteropProcessorMock(),
+            $this->createInteropConsumerMock(),
+            $this->createLoggerMock()
+        );
+
+        $fooExtension = $this->createExtension();
+        $fooExtension
+            ->expects($this->once())
+            ->method('preSubscribe')
+            ->with($this->identicalTo($context))
+        ;
+        $barExtension = $this->createExtension();
+        $barExtension
+            ->expects($this->once())
+            ->method('preSubscribe')
+            ->with($this->identicalTo($context))
+        ;
+
+        $extensions = new ChainExtension([$fooExtension, $barExtension]);
+
+        $extensions->preSubscribe($context);
+    }
+
     public function testShouldProxyOnBeforeReceiveToAllInternalExtensions()
     {
         $context = $this->createContextMock();
@@ -186,6 +214,30 @@ protected function createLoggerMock(): LoggerInterface
         return $this->createMock(LoggerInterface::class);
     }
 
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected function createInteropContextMock(): \Interop\Queue\Context
+    {
+        return $this->createMock(\Interop\Queue\Context::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected function createInteropConsumerMock(): \Interop\Queue\Consumer
+    {
+        return $this->createMock(\Interop\Queue\Consumer::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected function createInteropProcessorMock(): \Interop\Queue\Processor
+    {
+        return $this->createMock(\Interop\Queue\Processor::class);
+    }
+
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|Context
      */
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 548f5fe..897d801 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -6,6 +6,7 @@
 use Enqueue\Consumption\CallbackProcessor;
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\Exception\InvalidArgumentException;
 use Enqueue\Consumption\ExtensionInterface;
@@ -573,7 +574,7 @@ public function testShouldCallOnStartExtensionMethod()
             ->method('onStart')
             ->with($this->isInstanceOf(Start::class))
             ->willReturnCallback(function (Start $context) use ($contextStub) {
-                $this->assertSame($contextStub, $context->getInteropContext());
+                $this->assertSame($contextStub, $context->getContext());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
             })
         ;
@@ -614,6 +615,60 @@ public function testShouldCallOnStartWithLoggerProvidedInConstructor()
         $queueConsumer->consume();
     }
 
+    public function testShouldCallPreSubscribeExtensionMethod()
+    {
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createContextStub($consumerStub);
+
+        $processorMock = $this->createProcessorMock();
+
+        $extension = $this->createExtension();
+        $extension
+            ->expects($this->once())
+            ->method('preSubscribe')
+            ->with($this->isInstanceOf(PreSubscribe::class))
+            ->willReturnCallback(function (PreSubscribe $context) use ($contextStub, $consumerStub, $processorMock) {
+                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($consumerStub, $context->getConsumer());
+                $this->assertSame($processorMock, $context->getProcessor());
+                $this->assertInstanceOf(NullLogger::class, $context->getLogger());
+            })
+        ;
+
+        $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
+        $queueConsumer->setFallbackSubscriptionConsumer(new DummySubscriptionConsumer());
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
+
+        $queueConsumer->consume();
+    }
+
+    public function testShouldCallPreSubscribeForEachBoundProcessor()
+    {
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createContextStub($consumerStub);
+
+        $processorMock = $this->createProcessorMock();
+
+        $extension = $this->createExtension();
+        $extension
+            ->expects($this->exactly(3))
+            ->method('preSubscribe')
+            ->with($this->isInstanceOf(PreSubscribe::class))
+        ;
+
+        $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
+        $queueConsumer->setFallbackSubscriptionConsumer(new DummySubscriptionConsumer());
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
+        $queueConsumer->bind(new NullQueue('bar_queue'), $processorMock);
+        $queueConsumer->bind(new NullQueue('baz_queue'), $processorMock);
+
+        $queueConsumer->consume();
+    }
+
     public function testShouldCallOnIdleExtensionMethod()
     {
         $consumerStub = $this->createConsumerStub('foo_queue');

From 5262f762d7efac3fbdc955d3d61fee3622b150d2 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 5 Oct 2018 12:37:11 +0300
Subject: [PATCH 148/337] Fix tests.

---
 Symfony/DependencyInjection/TransportFactory.php           | 2 ++
 Tests/Symfony/DependencyInjection/TransportFactoryTest.php | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index 12cc5fa..f9b99bf 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -179,6 +179,8 @@ public function buildQueueConsumer(ContainerBuilder $container, array $config):
         $container->register($this->format('queue_consumer'), QueueConsumer::class)
             ->addArgument(new Reference($contextId))
             ->addArgument(new Reference($this->format('consumption_extensions')))
+            ->addArgument([])
+            ->addArgument(null)
             ->addArgument($this->format('idle_time', true))
             ->addArgument($this->format('receive_timeout', true))
         ;
diff --git a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index 993c2b3..e235425 100644
--- a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -388,6 +388,8 @@ public function testShouldBuildQueueConsumerWithDefaultOptions()
         $this->assertEquals([
             new Reference('enqueue.transport.default.context'),
             new Reference('enqueue.transport.default.consumption_extensions'),
+            [],
+            null,
             '%enqueue.transport.default.idle_time%',
             '%enqueue.transport.default.receive_timeout%',
         ], $container->getDefinition('enqueue.transport.default.queue_consumer')->getArguments());

From d733cc3f4278bafff7b1177cfa183a79f20a2e76 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 5 Oct 2018 16:27:56 +0300
Subject: [PATCH 149/337] [consumption] Add preConsume extension point, remove
 beforeReceive.

---
 Consumption/ChainExtension.php                |  9 +-
 Consumption/Context/PreConsume.php            | 97 +++++++++++++++++++
 Consumption/Context/PreSubscribe.php          |  2 +-
 Consumption/EmptyExtensionTrait.php           |  5 +-
 .../LimitConsumedMessagesExtension.php        | 38 +++-----
 .../LimitConsumerMemoryExtension.php          | 36 ++++---
 .../LimitConsumptionTimeExtension.php         | 36 ++++---
 Consumption/Extension/SignalExtension.php     | 73 +++++---------
 Consumption/ExtensionInterface.php            | 13 +--
 Consumption/QueueConsumer.php                 | 33 ++++---
 .../FlushSpoolProducerExtensionTest.php       | 31 +++++-
 Tests/Consumption/ChainExtensionTest.php      | 37 +++++--
 .../LimitConsumedMessagesExtensionTest.php    | 96 +++++++++++++++---
 .../LimitConsumerMemoryExtensionTest.php      | 57 +++++++++--
 .../LimitConsumptionTimeExtensionTest.php     | 53 ++++++++--
 .../Extension/ReplyExtensionTest.php          | 29 +++++-
 Tests/Consumption/QueueConsumerTest.php       | 72 ++++++++++----
 17 files changed, 521 insertions(+), 196 deletions(-)
 create mode 100644 Consumption/Context/PreConsume.php

diff --git a/Consumption/ChainExtension.php b/Consumption/ChainExtension.php
index 2b9d717..9118193 100644
--- a/Consumption/ChainExtension.php
+++ b/Consumption/ChainExtension.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
 
@@ -32,17 +33,17 @@ public function onStart(Start $context): void
         }
     }
 
-    public function preSubscribe(PreSubscribe $context): void
+    public function onPreSubscribe(PreSubscribe $context): void
     {
         foreach ($this->extensions as $extension) {
-            $extension->preSubscribe($context);
+            $extension->onPreSubscribe($context);
         }
     }
 
-    public function onBeforeReceive(Context $context)
+    public function onPreConsume(PreConsume $context): void
     {
         foreach ($this->extensions as $extension) {
-            $extension->onBeforeReceive($context);
+            $extension->onPreConsume($context);
         }
     }
 
diff --git a/Consumption/Context/PreConsume.php b/Consumption/Context/PreConsume.php
new file mode 100644
index 0000000..a6d83d5
--- /dev/null
+++ b/Consumption/Context/PreConsume.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace Enqueue\Consumption\Context;
+
+use Interop\Queue\Context;
+use Interop\Queue\SubscriptionConsumer;
+use Psr\Log\LoggerInterface;
+
+final class PreConsume
+{
+    /**
+     * @var Context
+     */
+    private $context;
+
+    /**
+     * @var SubscriptionConsumer
+     */
+    private $subscriptionConsumer;
+
+    /**
+     * @var LoggerInterface
+     */
+    private $logger;
+
+    /**
+     * @var int
+     */
+    private $cycle;
+
+    /**
+     * @var int
+     */
+    private $receiveTimeout;
+
+    /**
+     * @var int
+     */
+    private $startTime;
+
+    /**
+     * @var bool
+     */
+    private $executionInterrupted;
+
+    public function __construct(Context $context, SubscriptionConsumer $subscriptionConsumer, LoggerInterface $logger, int $cycle, int $receiveTimeout, int $startTime)
+    {
+        $this->context = $context;
+        $this->subscriptionConsumer = $subscriptionConsumer;
+        $this->logger = $logger;
+        $this->cycle = $cycle;
+        $this->receiveTimeout = $receiveTimeout;
+        $this->startTime = $startTime;
+
+        $this->executionInterrupted = false;
+    }
+
+    public function getContext(): Context
+    {
+        return $this->context;
+    }
+
+    public function getSubscriptionConsumer(): SubscriptionConsumer
+    {
+        return $this->subscriptionConsumer;
+    }
+
+    public function getLogger(): LoggerInterface
+    {
+        return $this->logger;
+    }
+
+    public function getCycle(): int
+    {
+        return $this->cycle;
+    }
+
+    public function getReceiveTimeout(): int
+    {
+        return $this->receiveTimeout;
+    }
+
+    public function getStartTime(): int
+    {
+        return $this->startTime;
+    }
+
+    public function isExecutionInterrupted(): bool
+    {
+        return $this->executionInterrupted;
+    }
+
+    public function interruptExecution(): void
+    {
+        $this->executionInterrupted = true;
+    }
+}
diff --git a/Consumption/Context/PreSubscribe.php b/Consumption/Context/PreSubscribe.php
index be14ba3..dbc74bb 100644
--- a/Consumption/Context/PreSubscribe.php
+++ b/Consumption/Context/PreSubscribe.php
@@ -7,7 +7,7 @@
 use Interop\Queue\Processor;
 use Psr\Log\LoggerInterface;
 
-class PreSubscribe
+final class PreSubscribe
 {
     /**
      * @var Context
diff --git a/Consumption/EmptyExtensionTrait.php b/Consumption/EmptyExtensionTrait.php
index 0ad021d..0979662 100644
--- a/Consumption/EmptyExtensionTrait.php
+++ b/Consumption/EmptyExtensionTrait.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
 
@@ -11,11 +12,11 @@ public function onStart(Start $context): void
     {
     }
 
-    public function preSubscribe(PreSubscribe $preSubscribe): void
+    public function onPreSubscribe(PreSubscribe $preSubscribe): void
     {
     }
 
-    public function onBeforeReceive(Context $context)
+    public function onPreConsume(PreConsume $context): void
     {
     }
 
diff --git a/Consumption/Extension/LimitConsumedMessagesExtension.php b/Consumption/Extension/LimitConsumedMessagesExtension.php
index ef6ec52..492e968 100644
--- a/Consumption/Extension/LimitConsumedMessagesExtension.php
+++ b/Consumption/Extension/LimitConsumedMessagesExtension.php
@@ -3,8 +3,10 @@
 namespace Enqueue\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface;
+use Psr\Log\LoggerInterface;
 
 class LimitConsumedMessagesExtension implements ExtensionInterface
 {
@@ -23,51 +25,41 @@ class LimitConsumedMessagesExtension implements ExtensionInterface
     /**
      * @param int $messageLimit
      */
-    public function __construct($messageLimit)
+    public function __construct(int $messageLimit)
     {
-        if (false == is_int($messageLimit)) {
-            throw new \InvalidArgumentException(sprintf(
-                'Expected message limit is int but got: "%s"',
-                is_object($messageLimit) ? get_class($messageLimit) : gettype($messageLimit)
-            ));
-        }
-
         $this->messageLimit = $messageLimit;
         $this->messageConsumed = 0;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function onBeforeReceive(Context $context)
+    public function onPreConsume(PreConsume $context): void
     {
         // this is added here to handle an edge case. when a user sets zero as limit.
-        $this->checkMessageLimit($context);
+        if ($this->shouldBeStopped($context->getLogger())) {
+            $context->interruptExecution();
+        }
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function onPostReceived(Context $context)
     {
         ++$this->messageConsumed;
 
-        $this->checkMessageLimit($context);
+        if ($this->shouldBeStopped($context->getLogger())) {
+            $context->setExecutionInterrupted(true);
+        }
     }
 
-    /**
-     * @param Context $context
-     */
-    protected function checkMessageLimit(Context $context)
+    protected function shouldBeStopped(LoggerInterface $logger): bool
     {
         if ($this->messageConsumed >= $this->messageLimit) {
-            $context->getLogger()->debug(sprintf(
+            $logger->debug(sprintf(
                 '[LimitConsumedMessagesExtension] Message consumption is interrupted since the message limit reached.'.
                 ' limit: "%s"',
                 $this->messageLimit
             ));
 
-            $context->setExecutionInterrupted(true);
+            return true;
         }
+
+        return false;
     }
 }
diff --git a/Consumption/Extension/LimitConsumerMemoryExtension.php b/Consumption/Extension/LimitConsumerMemoryExtension.php
index c03686f..78c6d05 100644
--- a/Consumption/Extension/LimitConsumerMemoryExtension.php
+++ b/Consumption/Extension/LimitConsumerMemoryExtension.php
@@ -3,8 +3,10 @@
 namespace Enqueue\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface;
+use Psr\Log\LoggerInterface;
 
 class LimitConsumerMemoryExtension implements ExtensionInterface
 {
@@ -30,44 +32,40 @@ public function __construct($memoryLimit)
         $this->memoryLimit = $memoryLimit * 1024 * 1024;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function onBeforeReceive(Context $context)
+    public function onPreConsume(PreConsume $context): void
     {
-        $this->checkMemory($context);
+        if ($this->shouldBeStopped($context->getLogger())) {
+            $context->interruptExecution();
+        }
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function onPostReceived(Context $context)
     {
-        $this->checkMemory($context);
+        if ($this->shouldBeStopped($context->getLogger())) {
+            $context->setExecutionInterrupted(true);
+        }
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function onIdle(Context $context)
     {
-        $this->checkMemory($context);
+        if ($this->shouldBeStopped($context->getLogger())) {
+            $context->setExecutionInterrupted(true);
+        }
     }
 
-    /**
-     * @param Context $context
-     */
-    protected function checkMemory(Context $context)
+    protected function shouldBeStopped(LoggerInterface $logger): bool
     {
         $memoryUsage = memory_get_usage(true);
         if ($memoryUsage >= $this->memoryLimit) {
-            $context->getLogger()->debug(sprintf(
+            $logger->debug(sprintf(
                 '[LimitConsumerMemoryExtension] Interrupt execution as memory limit reached. limit: "%s", used: "%s"',
                 $this->memoryLimit,
                 $memoryUsage
             ));
 
-            $context->setExecutionInterrupted(true);
+            return true;
         }
+
+        return false;
     }
 }
diff --git a/Consumption/Extension/LimitConsumptionTimeExtension.php b/Consumption/Extension/LimitConsumptionTimeExtension.php
index 65221f5..c87e3ca 100644
--- a/Consumption/Extension/LimitConsumptionTimeExtension.php
+++ b/Consumption/Extension/LimitConsumptionTimeExtension.php
@@ -3,8 +3,10 @@
 namespace Enqueue\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface;
+use Psr\Log\LoggerInterface;
 
 class LimitConsumptionTimeExtension implements ExtensionInterface
 {
@@ -23,45 +25,41 @@ public function __construct(\DateTime $timeLimit)
         $this->timeLimit = $timeLimit;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function onBeforeReceive(Context $context)
+    public function onPreConsume(PreConsume $context): void
     {
-        $this->checkTime($context);
+        if ($this->shouldBeStopped($context->getLogger())) {
+            $context->interruptExecution();
+        }
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function onIdle(Context $context)
     {
-        $this->checkTime($context);
+        if ($this->shouldBeStopped($context->getLogger())) {
+            $context->setExecutionInterrupted(true);
+        }
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function onPostReceived(Context $context)
     {
-        $this->checkTime($context);
+        if ($this->shouldBeStopped($context->getLogger())) {
+            $context->setExecutionInterrupted(true);
+        }
     }
 
-    /**
-     * @param Context $context
-     */
-    protected function checkTime(Context $context)
+    protected function shouldBeStopped(LoggerInterface $logger): bool
     {
         $now = new \DateTime();
         if ($now >= $this->timeLimit) {
-            $context->getLogger()->debug(sprintf(
+            $logger->debug(sprintf(
                 '[LimitConsumptionTimeExtension] Execution interrupted as limit time has passed.'.
                 ' now: "%s", time-limit: "%s"',
                 $now->format(DATE_ISO8601),
                 $this->timeLimit->format(DATE_ISO8601)
             ));
 
-            $context->setExecutionInterrupted(true);
+            return true;
         }
+
+        return false;
     }
 }
diff --git a/Consumption/Extension/SignalExtension.php b/Consumption/Extension/SignalExtension.php
index e4b416d..22393a0 100644
--- a/Consumption/Extension/SignalExtension.php
+++ b/Consumption/Extension/SignalExtension.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\Exception\LogicException;
@@ -29,77 +30,47 @@ public function onStart(Start $context): void
             throw new LogicException('The pcntl extension is required in order to catch signals.');
         }
 
-        if (function_exists('pcntl_async_signals')) {
-            pcntl_async_signals(true);
-        }
+        pcntl_async_signals(true);
 
         pcntl_signal(SIGTERM, [$this, 'handleSignal']);
         pcntl_signal(SIGQUIT, [$this, 'handleSignal']);
         pcntl_signal(SIGINT, [$this, 'handleSignal']);
 
+        $this->logger = $context->getLogger();
         $this->interruptConsumption = false;
     }
 
-    /**
-     * @param Context $context
-     */
-    public function onBeforeReceive(Context $context)
+    public function onPreConsume(PreConsume $context): void
     {
         $this->logger = $context->getLogger();
 
-        $this->dispatchSignal();
-
-        $this->interruptExecutionIfNeeded($context);
+        if ($this->shouldBeStopped($context->getLogger())) {
+            $context->interruptExecution();
+        }
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function onPreReceived(Context $context)
     {
-        $this->interruptExecutionIfNeeded($context);
+        if ($this->shouldBeStopped($context->getLogger())) {
+            $context->setExecutionInterrupted(true);
+        }
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function onPostReceived(Context $context)
     {
-        $this->dispatchSignal();
-
-        $this->interruptExecutionIfNeeded($context);
+        if ($this->shouldBeStopped($context->getLogger())) {
+            $context->setExecutionInterrupted(true);
+        }
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function onIdle(Context $context)
     {
-        $this->dispatchSignal();
-
-        $this->interruptExecutionIfNeeded($context);
-    }
-
-    /**
-     * @param Context $context
-     */
-    public function interruptExecutionIfNeeded(Context $context)
-    {
-        if (false == $context->isExecutionInterrupted() && $this->interruptConsumption) {
-            if ($this->logger) {
-                $this->logger->debug('[SignalExtension] Interrupt execution');
-            }
-
-            $context->setExecutionInterrupted($this->interruptConsumption);
-
-            $this->interruptConsumption = false;
+        if ($this->shouldBeStopped($context->getLogger())) {
+            $context->setExecutionInterrupted(true);
         }
     }
 
-    /**
-     * @param int $signal
-     */
-    public function handleSignal($signal)
+    public function handleSignal(int $signal): void
     {
         if ($this->logger) {
             $this->logger->debug(sprintf('[SignalExtension] Caught signal: %s', $signal));
@@ -120,10 +91,16 @@ public function handleSignal($signal)
         }
     }
 
-    private function dispatchSignal()
+    private function shouldBeStopped(LoggerInterface $logger): bool
     {
-        if (false == function_exists('pcntl_async_signals')) {
-            pcntl_signal_dispatch();
+        if ($this->interruptConsumption) {
+            $logger->debug('[SignalExtension] Interrupt execution');
+
+            $this->interruptConsumption = false;
+
+            return true;
         }
+
+        return false;
     }
 }
diff --git a/Consumption/ExtensionInterface.php b/Consumption/ExtensionInterface.php
index e29f0f7..286930c 100644
--- a/Consumption/ExtensionInterface.php
+++ b/Consumption/ExtensionInterface.php
@@ -2,30 +2,27 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
 
 interface ExtensionInterface
 {
     /**
-     * Executed only once at the very beginning of the consumption.
-     * At this stage the context does not contain processor, consumer and queue.
+     * Executed only once at the very beginning of the QueueConsumer::consume method call.
      */
     public function onStart(Start $context): void;
 
     /**
      * The method is called for each BoundProcessor before calling SubscriptionConsumer::subscribe method.
      */
-    public function preSubscribe(PreSubscribe $context): void;
+    public function onPreSubscribe(PreSubscribe $context): void;
 
     /**
-     * Executed at every new cycle before we asked a broker for a new message.
-     * At this stage the context already contains processor, consumer and queue.
+     * Executed at every new cycle before calling SubscriptionConsumer::consume method.
      * The consumption could be interrupted at this step.
-     *
-     * @param Context $context
      */
-    public function onBeforeReceive(Context $context);
+    public function onPreConsume(PreConsume $context): void;
 
     /**
      * Executed when a new message is received from a broker but before it was passed to processor
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index db8530f..c8391fd 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\Exception\ConsumptionInterruptedException;
@@ -184,19 +185,10 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
 
         /** @var Consumer[] $consumers */
         $consumers = [];
-        foreach ($this->boundProcessors as $boundProcessor) {
+        foreach ($this->boundProcessors as $queueName => $boundProcessor) {
             $queue = $boundProcessor->getQueue();
 
-            $preSubscribe = new PreSubscribe(
-                $this->interopContext,
-                $boundProcessor->getProcessor(),
-                $this->interopContext->createConsumer($queue),
-                $this->logger
-            );
-
-            $this->extension->preSubscribe($preSubscribe);
-
-            $consumers[$queue->getQueueName()] = $preSubscribe->getConsumer();
+            $consumers[$queue->getQueueName()] = $this->interopContext->createConsumer($queue);
         }
 
         // todo remove
@@ -249,17 +241,28 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
             return true;
         };
 
-        foreach ($consumers as $consumer) {
+        foreach ($consumers as $queueName => $consumer) {
             /* @var Consumer $consumer */
 
+            $preSubscribe = new PreSubscribe(
+                $this->interopContext,
+                $this->boundProcessors[$queueName]->getProcessor(),
+                $consumer,
+                $this->logger
+            );
+
+            $this->extension->onPreSubscribe($preSubscribe);
+
             $subscriptionConsumer->subscribe($consumer, $callback);
         }
 
+        $cycle = 1;
         while (true) {
             try {
-                $this->extension->onBeforeReceive($context);
+                $preConsume = new PreConsume($this->interopContext, $subscriptionConsumer, $this->logger, $cycle, $this->receiveTimeout, $startTime);
+                $this->extension->onPreConsume($preConsume);
 
-                if ($context->isExecutionInterrupted()) {
+                if ($preConsume->isExecutionInterrupted()) {
                     throw new ConsumptionInterruptedException();
                 }
 
@@ -293,6 +296,8 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
 
                 return;
             }
+
+            ++$cycle;
         }
     }
 
diff --git a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
index 2beabe5..2314b9f 100644
--- a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
@@ -5,9 +5,11 @@
 use Enqueue\Client\ConsumptionExtension\FlushSpoolProducerExtension;
 use Enqueue\Client\SpoolProducer;
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -39,7 +41,7 @@ public function testShouldDoNothingOnStart()
         );
     }
 
-    public function testShouldDoNothingOnBeforeReceive()
+    public function testShouldDoNothingOnPreConsume()
     {
         $producer = $this->createSpoolProducerMock();
         $producer
@@ -47,8 +49,17 @@ public function testShouldDoNothingOnBeforeReceive()
             ->method('flush')
         ;
 
+        $context = new PreConsume(
+            $this->createInteropContextMock(),
+            $this->createSubscriptionConsumerMock(),
+            new NullLogger(),
+            1,
+            2,
+            3
+        );
+
         $extension = new FlushSpoolProducerExtension($producer);
-        $extension->onBeforeReceive($this->createContextMock());
+        $extension->onPreConsume($context);
     }
 
     public function testShouldDoNothingOnPreReceived()
@@ -111,6 +122,22 @@ public function testShouldFlushSpoolProducerOnPostReceived()
         $extension->onPostReceived($this->createContextMock());
     }
 
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createInteropContextMock(): \Interop\Queue\Context
+    {
+        return $this->createMock(\Interop\Queue\Context::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createSubscriptionConsumerMock(): SubscriptionConsumer
+    {
+        return $this->createMock(SubscriptionConsumer::class);
+    }
+
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|Context
      */
diff --git a/Tests/Consumption/ChainExtensionTest.php b/Tests/Consumption/ChainExtensionTest.php
index d0eacdf..3c32b01 100644
--- a/Tests/Consumption/ChainExtensionTest.php
+++ b/Tests/Consumption/ChainExtensionTest.php
@@ -4,12 +4,15 @@
 
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
 
 class ChainExtensionTest extends TestCase
 {
@@ -47,7 +50,7 @@ public function testShouldProxyOnStartToAllInternalExtensions()
         $extensions->onStart($context);
     }
 
-    public function testShouldProxyPreSubscribeToAllInternalExtensions()
+    public function testShouldProxyOnPreSubscribeToAllInternalExtensions()
     {
         $context = new PreSubscribe(
             $this->createInteropContextMock(),
@@ -59,41 +62,47 @@ public function testShouldProxyPreSubscribeToAllInternalExtensions()
         $fooExtension = $this->createExtension();
         $fooExtension
             ->expects($this->once())
-            ->method('preSubscribe')
+            ->method('onPreSubscribe')
             ->with($this->identicalTo($context))
         ;
         $barExtension = $this->createExtension();
         $barExtension
             ->expects($this->once())
-            ->method('preSubscribe')
+            ->method('onPreSubscribe')
             ->with($this->identicalTo($context))
         ;
 
         $extensions = new ChainExtension([$fooExtension, $barExtension]);
 
-        $extensions->preSubscribe($context);
+        $extensions->onPreSubscribe($context);
     }
 
-    public function testShouldProxyOnBeforeReceiveToAllInternalExtensions()
+    public function testShouldProxyOnPreConsumeToAllInternalExtensions()
     {
-        $context = $this->createContextMock();
+        $context = new PreConsume(
+            $this->createInteropContextMock(),
+            $this->createSubscriptionConsumerMock(),
+            new NullLogger(),
+            1,
+            2,
+            3
+        );
 
         $fooExtension = $this->createExtension();
         $fooExtension
             ->expects($this->once())
-            ->method('onBeforeReceive')
+            ->method('onPreConsume')
             ->with($this->identicalTo($context))
         ;
         $barExtension = $this->createExtension();
         $barExtension
             ->expects($this->once())
-            ->method('onBeforeReceive')
+            ->method('onPreConsume')
             ->with($this->identicalTo($context))
         ;
 
         $extensions = new ChainExtension([$fooExtension, $barExtension]);
-
-        $extensions->onBeforeReceive($context);
+        $extensions->onPreConsume($context);
     }
 
     public function testShouldProxyOnPreReceiveToAllInternalExtensions()
@@ -253,4 +262,12 @@ protected function createExtension()
     {
         return $this->createMock(ExtensionInterface::class);
     }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createSubscriptionConsumerMock(): SubscriptionConsumer
+    {
+        return $this->createMock(SubscriptionConsumer::class);
+    }
 }
diff --git a/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php b/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
index 4c3d755..f0b2990 100644
--- a/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
@@ -3,10 +3,12 @@
 namespace Enqueue\Tests\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Extension\LimitConsumedMessagesExtension;
 use Interop\Queue\Consumer;
 use Interop\Queue\Context as InteropContext;
 use Interop\Queue\Processor;
+use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 
@@ -17,26 +19,61 @@ public function testCouldBeConstructedWithRequiredArguments()
         new LimitConsumedMessagesExtension(12345);
     }
 
-    public function testShouldThrowExceptionIfMessageLimitIsNotInt()
+    public function testOnPreConsumeShouldInterruptWhenLimitIsReached()
     {
-        $this->setExpectedException(
-            \InvalidArgumentException::class,
-            'Expected message limit is int but got: "double"'
+        $logger = $this->createLoggerMock();
+        $logger
+            ->expects($this->once())
+            ->method('debug')
+            ->with('[LimitConsumedMessagesExtension] Message consumption is interrupted since'.
+                ' the message limit reached. limit: "3"')
+        ;
+
+        $context = new PreConsume(
+            $this->createInteropContextMock(),
+            $this->createSubscriptionConsumerMock(),
+            $logger,
+            1,
+            2,
+            3
         );
 
-        new LimitConsumedMessagesExtension(0.0);
+        // guard
+        $this->assertFalse($context->isExecutionInterrupted());
+
+        // test
+        $extension = new LimitConsumedMessagesExtension(3);
+
+        $extension->onPreConsume($context);
+        $this->assertFalse($context->isExecutionInterrupted());
+
+        $extension->onPostReceived($this->createContext());
+        $extension->onPostReceived($this->createContext());
+        $extension->onPostReceived($this->createContext());
+
+        $extension->onPreConsume($context);
+        $this->assertTrue($context->isExecutionInterrupted());
     }
 
-    public function testOnBeforeReceiveShouldInterruptExecutionIfLimitIsZero()
+    public function testOnPreConsumeShouldInterruptExecutionIfLimitIsZero()
     {
-        $context = $this->createContext();
-        $context->getLogger()
+        $logger = $this->createLoggerMock();
+        $logger
             ->expects($this->once())
             ->method('debug')
             ->with('[LimitConsumedMessagesExtension] Message consumption is interrupted since'.
                 ' the message limit reached. limit: "0"')
         ;
 
+        $context = new PreConsume(
+            $this->createInteropContextMock(),
+            $this->createSubscriptionConsumerMock(),
+            $logger,
+            1,
+            2,
+            3
+        );
+
         // guard
         $this->assertFalse($context->isExecutionInterrupted());
 
@@ -44,20 +81,29 @@ public function testOnBeforeReceiveShouldInterruptExecutionIfLimitIsZero()
         $extension = new LimitConsumedMessagesExtension(0);
 
         // consume 1
-        $extension->onBeforeReceive($context);
+        $extension->onPreConsume($context);
         $this->assertTrue($context->isExecutionInterrupted());
     }
 
-    public function testOnBeforeReceiveShouldInterruptExecutionIfLimitIsLessThatZero()
+    public function testOnPreConsumeShouldInterruptExecutionIfLimitIsLessThatZero()
     {
-        $context = $this->createContext();
-        $context->getLogger()
+        $logger = $this->createLoggerMock();
+        $logger
             ->expects($this->once())
             ->method('debug')
             ->with('[LimitConsumedMessagesExtension] Message consumption is interrupted since'.
                 ' the message limit reached. limit: "-1"')
         ;
 
+        $context = new PreConsume(
+            $this->createInteropContextMock(),
+            $this->createSubscriptionConsumerMock(),
+            $logger,
+            1,
+            2,
+            3
+        );
+
         // guard
         $this->assertFalse($context->isExecutionInterrupted());
 
@@ -65,7 +111,7 @@ public function testOnBeforeReceiveShouldInterruptExecutionIfLimitIsLessThatZero
         $extension = new LimitConsumedMessagesExtension(-1);
 
         // consume 1
-        $extension->onBeforeReceive($context);
+        $extension->onPreConsume($context);
         $this->assertTrue($context->isExecutionInterrupted());
     }
 
@@ -94,6 +140,14 @@ public function testOnPostReceivedShouldInterruptExecutionIfMessageLimitExceeded
         $this->assertTrue($context->isExecutionInterrupted());
     }
 
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected function createInteropContextMock(): \Interop\Queue\Context
+    {
+        return $this->createMock(\Interop\Queue\Context::class);
+    }
+
     /**
      * @return Context
      */
@@ -106,4 +160,20 @@ protected function createContext(): Context
 
         return $context;
     }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createSubscriptionConsumerMock(): SubscriptionConsumer
+    {
+        return $this->createMock(SubscriptionConsumer::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createLoggerMock(): LoggerInterface
+    {
+        return $this->createMock(LoggerInterface::class);
+    }
 }
diff --git a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
index e42e559..25fa51c 100644
--- a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
@@ -3,12 +3,15 @@
 namespace Enqueue\Tests\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Extension\LimitConsumerMemoryExtension;
 use Interop\Queue\Consumer;
 use Interop\Queue\Context as InteropContext;
 use Interop\Queue\Processor;
+use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
 
 class LimitConsumerMemoryExtensionTest extends TestCase
 {
@@ -62,35 +65,51 @@ public function testOnPostReceivedShouldInterruptExecutionIfMemoryLimitReached()
         $this->assertTrue($context->isExecutionInterrupted());
     }
 
-    public function testOnBeforeReceivedShouldInterruptExecutionIfMemoryLimitReached()
+    public function testOnPreConsumeShouldInterruptExecutionIfMemoryLimitReached()
     {
-        $context = $this->createContext();
-        $context->getLogger()
+        $logger = $this->createLoggerMock();
+        $logger
             ->expects($this->once())
             ->method('debug')
             ->with($this->stringContains('[LimitConsumerMemoryExtension] Interrupt execution as memory limit reached.'))
         ;
 
+        $context = new PreConsume(
+            $this->createInteropContextMock(),
+            $this->createSubscriptionConsumerMock(),
+            $logger,
+            1,
+            2,
+            3
+        );
+
         // guard
         $this->assertFalse($context->isExecutionInterrupted());
 
         // test
         $extension = new LimitConsumerMemoryExtension(1);
-        $extension->onBeforeReceive($context);
+        $extension->onPreConsume($context);
 
         $this->assertTrue($context->isExecutionInterrupted());
     }
 
-    public function testOnBeforeReceiveShouldNotInterruptExecutionIfMemoryLimitIsNotReached()
+    public function testOnPreConsumeShouldNotInterruptExecutionIfMemoryLimitIsNotReached()
     {
-        $context = $this->createContext();
+        $context = new PreConsume(
+            $this->createInteropContextMock(),
+            $this->createSubscriptionConsumerMock(),
+            new NullLogger(),
+            1,
+            2,
+            3
+        );
 
         // guard
         $this->assertFalse($context->isExecutionInterrupted());
 
         // test
         $extension = new LimitConsumerMemoryExtension(PHP_INT_MAX);
-        $extension->onBeforeReceive($context);
+        $extension->onPreConsume($context);
 
         $this->assertFalse($context->isExecutionInterrupted());
     }
@@ -123,6 +142,14 @@ public function testOnPostReceivedShouldNotInterruptExecutionIfMemoryLimitIsNotR
         $this->assertFalse($context->isExecutionInterrupted());
     }
 
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected function createInteropContextMock(): \Interop\Queue\Context
+    {
+        return $this->createMock(\Interop\Queue\Context::class);
+    }
+
     /**
      * @return Context
      */
@@ -135,4 +162,20 @@ protected function createContext(): Context
 
         return $context;
     }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createSubscriptionConsumerMock(): SubscriptionConsumer
+    {
+        return $this->createMock(SubscriptionConsumer::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createLoggerMock(): LoggerInterface
+    {
+        return $this->createMock(LoggerInterface::class);
+    }
 }
diff --git a/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php b/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
index 66485dc..0e07c09 100644
--- a/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
@@ -3,12 +3,15 @@
 namespace Enqueue\Tests\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Extension\LimitConsumptionTimeExtension;
 use Interop\Queue\Consumer;
 use Interop\Queue\Context as InteropContext;
 use Interop\Queue\Processor;
+use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
 
 class LimitConsumptionTimeExtensionTest extends TestCase
 {
@@ -17,9 +20,16 @@ public function testCouldBeConstructedWithRequiredArguments()
         new LimitConsumptionTimeExtension(new \DateTime('+1 day'));
     }
 
-    public function testOnBeforeReceiveShouldInterruptExecutionIfConsumptionTimeExceeded()
+    public function testOnPreConsumeShouldInterruptExecutionIfConsumptionTimeExceeded()
     {
-        $context = $this->createContext();
+        $context = new PreConsume(
+            $this->createInteropContextMock(),
+            $this->createSubscriptionConsumerMock(),
+            new NullLogger(),
+            1,
+            2,
+            3
+        );
 
         // guard
         $this->assertFalse($context->isExecutionInterrupted());
@@ -27,7 +37,7 @@ public function testOnBeforeReceiveShouldInterruptExecutionIfConsumptionTimeExce
         // test
         $extension = new LimitConsumptionTimeExtension(new \DateTime('-2 second'));
 
-        $extension->onBeforeReceive($context);
+        $extension->onPreConsume($context);
 
         $this->assertTrue($context->isExecutionInterrupted());
     }
@@ -62,9 +72,16 @@ public function testOnPostReceivedShouldInterruptExecutionIfConsumptionTimeExcee
         $this->assertTrue($context->isExecutionInterrupted());
     }
 
-    public function testOnBeforeReceiveShouldNotInterruptExecutionIfConsumptionTimeIsNotExceeded()
+    public function testOnPreConsumeShouldNotInterruptExecutionIfConsumptionTimeIsNotExceeded()
     {
-        $context = $this->createContext();
+        $context = new PreConsume(
+            $this->createInteropContextMock(),
+            $this->createSubscriptionConsumerMock(),
+            new NullLogger(),
+            1,
+            2,
+            3
+        );
 
         // guard
         $this->assertFalse($context->isExecutionInterrupted());
@@ -72,7 +89,7 @@ public function testOnBeforeReceiveShouldNotInterruptExecutionIfConsumptionTimeI
         // test
         $extension = new LimitConsumptionTimeExtension(new \DateTime('+2 second'));
 
-        $extension->onBeforeReceive($context);
+        $extension->onPreConsume($context);
 
         $this->assertFalse($context->isExecutionInterrupted());
     }
@@ -107,6 +124,14 @@ public function testOnPostReceivedShouldNotInterruptExecutionIfConsumptionTimeIs
         $this->assertFalse($context->isExecutionInterrupted());
     }
 
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected function createInteropContextMock(): \Interop\Queue\Context
+    {
+        return $this->createMock(\Interop\Queue\Context::class);
+    }
+
     /**
      * @return Context
      */
@@ -119,4 +144,20 @@ protected function createContext(): Context
 
         return $context;
     }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createSubscriptionConsumerMock(): SubscriptionConsumer
+    {
+        return $this->createMock(SubscriptionConsumer::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createLoggerMock(): LoggerInterface
+    {
+        return $this->createMock(LoggerInterface::class);
+    }
 }
diff --git a/Tests/Consumption/Extension/ReplyExtensionTest.php b/Tests/Consumption/Extension/ReplyExtensionTest.php
index 3287095..825bece 100644
--- a/Tests/Consumption/Extension/ReplyExtensionTest.php
+++ b/Tests/Consumption/Extension/ReplyExtensionTest.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Tests\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\Extension\ReplyExtension;
 use Enqueue\Consumption\ExtensionInterface;
@@ -12,6 +13,7 @@
 use Enqueue\Test\ClassExtensionTrait;
 use Interop\Queue\Context as InteropContext;
 use Interop\Queue\Producer as InteropProducer;
+use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -43,11 +45,18 @@ public function testShouldDoNothingOnStart()
         $extension->onStart(new Start($this->createNeverUsedContextMock(), new NullLogger(), [], 0, 0, 0));
     }
 
-    public function testShouldDoNothingOnBeforeReceive()
+    public function testShouldDoNothingOnPreConsume()
     {
         $extension = new ReplyExtension();
 
-        $extension->onBeforeReceive(new Context($this->createNeverUsedContextMock()));
+        $extension->onPreConsume(new PreConsume(
+            $this->createInteropContextMock(),
+            $this->createSubscriptionConsumerMock(),
+            new NullLogger(),
+            1,
+            2,
+            3
+        ));
     }
 
     public function testShouldDoNothingOnInterrupted()
@@ -136,6 +145,22 @@ public function testShouldSendReplyMessageToReplyQueueOnPostReceived()
         $extension->onPostReceived($context);
     }
 
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected function createInteropContextMock(): \Interop\Queue\Context
+    {
+        return $this->createMock(\Interop\Queue\Context::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createSubscriptionConsumerMock(): SubscriptionConsumer
+    {
+        return $this->createMock(SubscriptionConsumer::class);
+    }
+
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
      */
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 897d801..4897a24 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -6,6 +6,7 @@
 use Enqueue\Consumption\CallbackProcessor;
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\Exception\InvalidArgumentException;
@@ -626,7 +627,7 @@ public function testShouldCallPreSubscribeExtensionMethod()
         $extension = $this->createExtension();
         $extension
             ->expects($this->once())
-            ->method('preSubscribe')
+            ->method('onPreSubscribe')
             ->with($this->isInstanceOf(PreSubscribe::class))
             ->willReturnCallback(function (PreSubscribe $context) use ($contextStub, $consumerStub, $processorMock) {
                 $this->assertSame($contextStub, $context->getContext());
@@ -655,7 +656,7 @@ public function testShouldCallPreSubscribeForEachBoundProcessor()
         $extension = $this->createExtension();
         $extension
             ->expects($this->exactly(3))
-            ->method('preSubscribe')
+            ->method('onPreSubscribe')
             ->with($this->isInstanceOf(PreSubscribe::class))
         ;
 
@@ -702,7 +703,7 @@ public function testShouldCallOnIdleExtensionMethod()
         $queueConsumer->consume();
     }
 
-    public function testShouldCallOnBeforeReceiveExtensionMethod()
+    public function testShouldCallOnPreConsumeExtensionMethod()
     {
         $consumerStub = $this->createConsumerStub('foo_queue');
 
@@ -715,17 +716,15 @@ public function testShouldCallOnBeforeReceiveExtensionMethod()
         $extension = $this->createExtension();
         $extension
             ->expects($this->once())
-            ->method('onBeforeReceive')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use ($contextStub) {
-                $this->assertSame($contextStub, $context->getInteropContext());
+            ->method('onPreConsume')
+            ->with($this->isInstanceOf(PreConsume::class))
+            ->willReturnCallback(function (PreConsume $context) use ($contextStub) {
+                $this->assertSame($contextStub, $context->getContext());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
-                $this->assertNull($context->getProcessor());
-                $this->assertNull($context->getConsumer());
-                $this->assertNull($context->getInteropMessage());
-                $this->assertNull($context->getException());
-                $this->assertNull($context->getResult());
-                $this->assertNull($context->getInteropQueue());
+                $this->assertInstanceOf(SubscriptionConsumer::class, $context->getSubscriptionConsumer());
+                $this->assertSame(10000, $context->getReceiveTimeout());
+                $this->assertSame(1, $context->getCycle());
+                $this->assertGreaterThan(0, $context->getStartTime());
                 $this->assertFalse($context->isExecutionInterrupted());
             })
         ;
@@ -738,6 +737,30 @@ public function testShouldCallOnBeforeReceiveExtensionMethod()
         $queueConsumer->consume();
     }
 
+    public function testShouldCallOnPreConsumeExpectedAmountOfTimes()
+    {
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createContextStub($consumerStub);
+
+        $processorMock = $this->createProcessorStub();
+
+        $queue = new NullQueue('foo_queue');
+
+        $extension = $this->createExtension();
+        $extension
+            ->expects($this->exactly(3))
+            ->method('onPreConsume')
+        ;
+
+        $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(3)]);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
+        $queueConsumer->setFallbackSubscriptionConsumer(new DummySubscriptionConsumer());
+        $queueConsumer->bind($queue, $processorMock);
+
+        $queueConsumer->consume();
+    }
+
     public function testShouldCallOnPreReceivedExtensionMethodWithExpectedContext()
     {
         $expectedMessage = $this->createMessageMock();
@@ -1265,8 +1288,13 @@ public function testShouldCallExtensionPassedOnRuntime()
         ;
         $runtimeExtension
             ->expects($this->once())
-            ->method('onBeforeReceive')
-            ->with($this->isInstanceOf(Context::class))
+            ->method('onPreSubscribe')
+            ->with($this->isInstanceOf(PreSubscribe::class))
+        ;
+        $runtimeExtension
+            ->expects($this->once())
+            ->method('onPreConsume')
+            ->with($this->isInstanceOf(PreConsume::class))
         ;
         $runtimeExtension
             ->expects($this->once())
@@ -1322,9 +1350,17 @@ public function testShouldChangeLoggerOnStart()
         ;
         $extension
             ->expects($this->atLeastOnce())
-            ->method('onBeforeReceive')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use ($expectedLogger) {
+            ->method('onPreSubscribe')
+            ->with($this->isInstanceOf(PreSubscribe::class))
+            ->willReturnCallback(function (PreSubscribe $context) use ($expectedLogger) {
+                $this->assertSame($expectedLogger, $context->getLogger());
+            })
+        ;
+        $extension
+            ->expects($this->atLeastOnce())
+            ->method('onPreConsume')
+            ->with($this->isInstanceOf(PreConsume::class))
+            ->willReturnCallback(function (PreConsume $context) use ($expectedLogger) {
                 $this->assertSame($expectedLogger, $context->getLogger());
             })
         ;

From 5371c4e4a909e642bc82cd4fb8401c8d473d0b23 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Sun, 7 Oct 2018 22:16:11 +0300
Subject: [PATCH 150/337] [consumption] onPreReceived -> onMessageReceived

---
 .../DelayRedeliveredMessageExtension.php      |   9 +-
 .../ExclusiveCommandExtension.php             |   9 +-
 .../SetRouterPropertiesExtension.php          |   8 +-
 Client/ExtensionInterface.php                 |  10 --
 Consumption/ChainExtension.php                |   5 +-
 Consumption/Context/MessageReceived.php       |  92 ++++++++++++
 Consumption/EmptyExtensionTrait.php           |   3 +-
 Consumption/Extension/SignalExtension.php     |   6 +-
 Consumption/ExtensionInterface.php            |  12 +-
 Consumption/QueueConsumer.php                 |  86 ++++++-----
 .../DelayRedeliveredMessageExtensionTest.php  |  83 ++++++++---
 .../ExclusiveCommandExtensionTest.php         | 134 +++++++++++++-----
 .../FlushSpoolProducerExtensionTest.php       |  74 ----------
 .../SetRouterPropertiesExtensionTest.php      |  69 +++++++--
 Tests/Consumption/ChainExtensionTest.php      |  18 ++-
 .../Extension/ReplyExtensionTest.php          |  37 -----
 Tests/Consumption/QueueConsumerTest.php       | 101 +++----------
 17 files changed, 404 insertions(+), 352 deletions(-)
 create mode 100644 Consumption/Context/MessageReceived.php

diff --git a/Client/ConsumptionExtension/DelayRedeliveredMessageExtension.php b/Client/ConsumptionExtension/DelayRedeliveredMessageExtension.php
index 0977f95..4ac26ff 100644
--- a/Client/ConsumptionExtension/DelayRedeliveredMessageExtension.php
+++ b/Client/ConsumptionExtension/DelayRedeliveredMessageExtension.php
@@ -3,7 +3,7 @@
 namespace Enqueue\Client\ConsumptionExtension;
 
 use Enqueue\Client\DriverInterface;
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Consumption\Result;
@@ -36,12 +36,9 @@ public function __construct(DriverInterface $driver, $delay)
         $this->delay = $delay;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function onPreReceived(Context $context)
+    public function onMessageReceived(MessageReceived $context): void
     {
-        $message = $context->getInteropMessage();
+        $message = $context->getMessage();
         if (false == $message->isRedelivered()) {
             return;
         }
diff --git a/Client/ConsumptionExtension/ExclusiveCommandExtension.php b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
index e9bbbd8..5397456 100644
--- a/Client/ConsumptionExtension/ExclusiveCommandExtension.php
+++ b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
@@ -5,7 +5,7 @@
 use Enqueue\Client\Config;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Route;
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\EmptyExtensionTrait as ConsumptionEmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface as ConsumptionExtensionInterface;
 
@@ -28,11 +28,9 @@ public function __construct(DriverInterface $driver)
         $this->driver = $driver;
     }
 
-    public function onPreReceived(Context $context)
+    public function onMessageReceived(MessageReceived $context): void
     {
-        $message = $context->getInteropMessage();
-        $queue = $context->getInteropQueue();
-
+        $message = $context->getMessage();
         if ($message->getProperty(Config::TOPIC)) {
             return;
         }
@@ -47,6 +45,7 @@ public function onPreReceived(Context $context)
             $this->queueToRouteMap = $this->buildMap();
         }
 
+        $queue = $context->getConsumer()->getQueue();
         if (array_key_exists($queue->getQueueName(), $this->queueToRouteMap)) {
             $context->getLogger()->debug('[ExclusiveCommandExtension] This is a exclusive command queue and client\'s properties are not set. Setting them');
 
diff --git a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
index ca6a32b..cc3d8d3 100644
--- a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
+++ b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
@@ -4,7 +4,7 @@
 
 use Enqueue\Client\Config;
 use Enqueue\Client\DriverInterface;
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface;
 
@@ -25,16 +25,16 @@ public function __construct(DriverInterface $driver)
         $this->driver = $driver;
     }
 
-    public function onPreReceived(Context $context)
+    public function onMessageReceived(MessageReceived $context): void
     {
-        $message = $context->getInteropMessage();
+        $message = $context->getMessage();
         if ($message->getProperty(Config::PROCESSOR)) {
             return;
         }
 
         $config = $this->driver->getConfig();
         $queue = $this->driver->createQueue($config->getRouterQueue());
-        if ($context->getInteropQueue()->getQueueName() != $queue->getQueueName()) {
+        if ($context->getConsumer()->getQueue()->getQueueName() != $queue->getQueueName()) {
             return;
         }
 
diff --git a/Client/ExtensionInterface.php b/Client/ExtensionInterface.php
index 1a6b27d..a0b413d 100644
--- a/Client/ExtensionInterface.php
+++ b/Client/ExtensionInterface.php
@@ -11,14 +11,4 @@ public function onPreSendCommand(PreSend $context): void;
     public function onDriverPreSend(DriverPreSend $context): void;
 
     public function onPostSend(PostSend $context): void;
-
-//    /**
-//     * @deprecated
-//     */
-//    public function onPreSend($topic, Message $message);
-//
-//    /**
-//     * @deprecated
-//     */
-//    public function onPostSend($topic, Message $message);
 }
diff --git a/Consumption/ChainExtension.php b/Consumption/ChainExtension.php
index 9118193..fcb248a 100644
--- a/Consumption/ChainExtension.php
+++ b/Consumption/ChainExtension.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
@@ -47,10 +48,10 @@ public function onPreConsume(PreConsume $context): void
         }
     }
 
-    public function onPreReceived(Context $context)
+    public function onMessageReceived(MessageReceived $context): void
     {
         foreach ($this->extensions as $extension) {
-            $extension->onPreReceived($context);
+            $extension->onMessageReceived($context);
         }
     }
 
diff --git a/Consumption/Context/MessageReceived.php b/Consumption/Context/MessageReceived.php
new file mode 100644
index 0000000..44d5232
--- /dev/null
+++ b/Consumption/Context/MessageReceived.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace Enqueue\Consumption\Context;
+
+use Enqueue\Consumption\Result;
+use Interop\Queue\Consumer;
+use Interop\Queue\Context;
+use Interop\Queue\Message;
+use Interop\Queue\Processor;
+use Psr\Log\LoggerInterface;
+
+final class MessageReceived
+{
+    /**
+     * @var Context
+     */
+    private $context;
+
+    /**
+     * @var Consumer
+     */
+    private $consumer;
+
+    /**
+     * @var Message
+     */
+    private $message;
+
+    /**
+     * @var Processor
+     */
+    private $processor;
+
+    /**
+     * @var LoggerInterface
+     */
+    private $logger;
+
+    /**
+     * @var Result|null
+     */
+    private $result;
+
+    public function __construct(Context $context, Consumer $consumer, Message $message, Processor $processor, LoggerInterface $logger)
+    {
+        $this->context = $context;
+        $this->consumer = $consumer;
+        $this->message = $message;
+        $this->processor = $processor;
+        $this->logger = $logger;
+    }
+
+    public function getContext(): Context
+    {
+        return $this->context;
+    }
+
+    public function getConsumer(): Consumer
+    {
+        return $this->consumer;
+    }
+
+    public function getMessage(): Message
+    {
+        return $this->message;
+    }
+
+    public function getProcessor(): Processor
+    {
+        return $this->processor;
+    }
+
+    public function changeProcessor(Processor $processor): void
+    {
+        $this->processor = $processor;
+    }
+
+    public function getLogger(): LoggerInterface
+    {
+        return $this->logger;
+    }
+
+    public function getResult(): ?Result
+    {
+        return $this->result;
+    }
+
+    public function setResult(Result $result): void
+    {
+        $this->result = $result;
+    }
+}
diff --git a/Consumption/EmptyExtensionTrait.php b/Consumption/EmptyExtensionTrait.php
index 0979662..67fc8a7 100644
--- a/Consumption/EmptyExtensionTrait.php
+++ b/Consumption/EmptyExtensionTrait.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
@@ -20,7 +21,7 @@ public function onPreConsume(PreConsume $context): void
     {
     }
 
-    public function onPreReceived(Context $context)
+    public function onMessageReceived(MessageReceived $context): void
     {
     }
 
diff --git a/Consumption/Extension/SignalExtension.php b/Consumption/Extension/SignalExtension.php
index 22393a0..e4d484d 100644
--- a/Consumption/Extension/SignalExtension.php
+++ b/Consumption/Extension/SignalExtension.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\EmptyExtensionTrait;
@@ -49,11 +50,8 @@ public function onPreConsume(PreConsume $context): void
         }
     }
 
-    public function onPreReceived(Context $context)
+    public function onMessageReceived(MessageReceived $context): void
     {
-        if ($this->shouldBeStopped($context->getLogger())) {
-            $context->setExecutionInterrupted(true);
-        }
     }
 
     public function onPostReceived(Context $context)
diff --git a/Consumption/ExtensionInterface.php b/Consumption/ExtensionInterface.php
index 286930c..4e6f4de 100644
--- a/Consumption/ExtensionInterface.php
+++ b/Consumption/ExtensionInterface.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
@@ -25,14 +26,11 @@ public function onPreSubscribe(PreSubscribe $context): void;
     public function onPreConsume(PreConsume $context): void;
 
     /**
-     * Executed when a new message is received from a broker but before it was passed to processor
-     * The context contains a message.
-     * The extension may set a status. If the status is set the exception is thrown
-     * The consumption could be interrupted at this step but it exits after the message is processed.
-     *
-     * @param Context $context
+     * Executed as soon as a a message is received, before it is passed to a processor
+     * The extension may set a result. If the result is set the processor is not called
+     * The processor could be changed or decorated at this point.
      */
-    public function onPreReceived(Context $context);
+    public function onMessageReceived(MessageReceived $context): void;
 
     /**
      * Executed when a message is processed by a processor or a result was set in onPreReceived method.
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index c8391fd..5d420ba 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
@@ -212,27 +213,54 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
         }
 
         $callback = function (InteropMessage $message, Consumer $consumer) use (&$context) {
-            $currentProcessor = null;
-
-            foreach ($this->boundProcessors as $boundProcessor) {
-                $queue = $boundProcessor->getQueue();
-                if ($queue->getQueueName() === $consumer->getQueue()->getQueueName()) {
-                    $currentProcessor = $boundProcessor->getProcessor();
-                }
+            $queue = $consumer->getQueue();
+            if (false == array_key_exists($queue->getQueueName(), $this->boundProcessors)) {
+                throw new \LogicException(sprintf('The processor for the queue "%s" could not be found.', $queue->getQueueName()));
             }
 
-            if (false == $currentProcessor) {
-                throw new \LogicException(sprintf('The processor for the queue "%s" could not be found.', $consumer->getQueue()->getQueueName()));
-            }
+            $processor = $this->boundProcessors[$queue->getQueueName()]->getProcessor();
 
+//            $this->logger->info('Message received from the queue: '.$context->getInteropQueue()->getQueueName());
+//            $this->logger->debug('Headers: {headers}', ['headers' => new VarExport($message->getHeaders())]);
+//            $this->logger->debug('Properties: {properties}', ['properties' => new VarExport($message->getProperties())]);
+//            $this->logger->debug('Payload: {payload}', ['payload' => new VarExport($message->getBody())]);
+
+            // TODO remove
             $context = new Context($this->interopContext);
             $context->setLogger($this->logger);
             $context->setInteropQueue($consumer->getQueue());
             $context->setConsumer($consumer);
-            $context->setProcessor($currentProcessor);
+            $context->setProcessor($processor);
             $context->setInteropMessage($message);
 
-            $this->processMessage($consumer, $currentProcessor, $message, $context);
+            $messageReceived = new MessageReceived($this->interopContext, $consumer, $message, $processor, $this->logger);
+            $this->extension->onMessageReceived($messageReceived);
+            $result = $messageReceived->getResult();
+            $processor = $messageReceived->getProcessor();
+            if (null === $result) {
+                $result = $processor->process($message, $context->getInteropContext());
+                $context->setResult($result);
+            }
+
+            $this->extension->onResult($context);
+
+            switch ($result) {
+                case Result::ACK:
+                    $consumer->acknowledge($message);
+                    break;
+                case Result::REJECT:
+                    $consumer->reject($message, false);
+                    break;
+                case Result::REQUEUE:
+                    $consumer->reject($message, true);
+                    break;
+                default:
+                    throw new \LogicException(sprintf('Status is not supported: %s', $result));
+            }
+
+            $this->logger->info(sprintf('Message processed: %s', $result));
+
+            $this->extension->onPostReceived($context);
 
             if ($context->isExecutionInterrupted()) {
                 throw new ConsumptionInterruptedException();
@@ -352,38 +380,4 @@ private function onInterruptionByException(ExtensionInterface $extension, Contex
 
         throw $exception;
     }
-
-    private function processMessage(Consumer $consumer, Processor $processor, InteropMessage $message, Context $context)
-    {
-        $this->logger->info('Message received from the queue: '.$context->getInteropQueue()->getQueueName());
-        $this->logger->debug('Headers: {headers}', ['headers' => new VarExport($message->getHeaders())]);
-        $this->logger->debug('Properties: {properties}', ['properties' => new VarExport($message->getProperties())]);
-        $this->logger->debug('Payload: {payload}', ['payload' => new VarExport($message->getBody())]);
-
-        $this->extension->onPreReceived($context);
-        if (!$context->getResult()) {
-            $result = $processor->process($message, $context->getInteropContext());
-            $context->setResult($result);
-        }
-
-        $this->extension->onResult($context);
-
-        switch ($context->getResult()) {
-            case Result::ACK:
-                $consumer->acknowledge($message);
-                break;
-            case Result::REJECT:
-                $consumer->reject($message, false);
-                break;
-            case Result::REQUEUE:
-                $consumer->reject($message, true);
-                break;
-            default:
-                throw new \LogicException(sprintf('Status is not supported: %s', $context->getResult()));
-        }
-
-        $this->logger->info(sprintf('Message processed: %s', $context->getResult()));
-
-        $this->extension->onPostReceived($context);
-    }
 }
diff --git a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
index 5d4b750..cf4161a 100644
--- a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
@@ -5,13 +5,16 @@
 use Enqueue\Client\ConsumptionExtension\DelayRedeliveredMessageExtension;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Message;
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Result;
 use Enqueue\Null\NullMessage;
 use Enqueue\Null\NullQueue;
+use Interop\Queue\Consumer;
 use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
 
 class DelayRedeliveredMessageExtensionTest extends TestCase
 {
@@ -61,17 +64,20 @@ public function testShouldSendDelayedMessageAndRejectOriginalMessage()
             )
         ;
 
-        $context = new Context($this->createContextMock());
-        $context->setInteropQueue($queue);
-        $context->setInteropMessage($originMessage);
-        $context->setLogger($logger);
+        $messageReceived = new MessageReceived(
+            $this->createContextMock(),
+            $this->createConsumerStub($queue),
+            $originMessage,
+            $this->createProcessorMock(),
+            $logger
+        );
 
-        $this->assertNull($context->getResult());
+        $this->assertNull($messageReceived->getResult());
 
         $extension = new DelayRedeliveredMessageExtension($driver, 12345);
-        $extension->onPreReceived($context);
+        $extension->onMessageReceived($messageReceived);
 
-        $result = $context->getResult();
+        $result = $messageReceived->getResult();
         $this->assertInstanceOf(Result::class, $result);
         $this->assertSame(Result::REJECT, $result->getStatus());
         $this->assertSame('A new copy of the message was sent with a delay. The original message is rejected', $result->getReason());
@@ -92,13 +98,18 @@ public function testShouldDoNothingIfMessageIsNotRedelivered()
             ->method('sendToProcessor')
         ;
 
-        $context = new Context($this->createContextMock());
-        $context->setInteropMessage($message);
+        $messageReceived = new MessageReceived(
+            $this->createContextMock(),
+            $this->createConsumerStub(null),
+            $message,
+            $this->createProcessorMock(),
+            new NullLogger()
+        );
 
         $extension = new DelayRedeliveredMessageExtension($driver, 12345);
-        $extension->onPreReceived($context);
+        $extension->onMessageReceived($messageReceived);
 
-        $this->assertNull($context->getResult());
+        $this->assertNull($messageReceived->getResult());
     }
 
     public function testShouldDoNothingIfMessageIsRedeliveredButResultWasAlreadySetOnContext()
@@ -112,24 +123,29 @@ public function testShouldDoNothingIfMessageIsRedeliveredButResultWasAlreadySetO
             ->method('sendToProcessor')
         ;
 
-        $context = new Context($this->createContextMock());
-        $context->setInteropMessage($message);
-        $context->setResult('aStatus');
+        $messageReceived = new MessageReceived(
+            $this->createContextMock(),
+            $this->createConsumerStub(null),
+            $message,
+            $this->createProcessorMock(),
+            new NullLogger()
+        );
+        $messageReceived->setResult(Result::ack());
 
         $extension = new DelayRedeliveredMessageExtension($driver, 12345);
-        $extension->onPreReceived($context);
+        $extension->onMessageReceived($messageReceived);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|DriverInterface
+     * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    private function createDriverMock()
+    private function createDriverMock(): DriverInterface
     {
         return $this->createMock(DriverInterface::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
+     * @return \PHPUnit_Framework_MockObject_MockObject
      */
     private function createContextMock(): InteropContext
     {
@@ -137,9 +153,34 @@ private function createContextMock(): InteropContext
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|LoggerInterface
+     * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    private function createLoggerMock()
+    private function createProcessorMock(): Processor
+    {
+        return $this->createMock(Processor::class);
+    }
+
+    /**
+     * @param mixed $queue
+     *
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createConsumerStub($queue): Consumer
+    {
+        $consumerMock = $this->createMock(Consumer::class);
+        $consumerMock
+            ->expects($this->any())
+            ->method('getQueue')
+            ->willReturn($queue)
+        ;
+
+        return $consumerMock;
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createLoggerMock(): LoggerInterface
     {
         return $this->createMock(LoggerInterface::class);
     }
diff --git a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
index 8117645..8a54971 100644
--- a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
@@ -7,12 +7,14 @@
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Route;
 use Enqueue\Client\RouteCollection;
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\ExtensionInterface as ConsumptionExtensionInterface;
-use Enqueue\Null\NullContext;
 use Enqueue\Null\NullMessage;
 use Enqueue\Null\NullQueue;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\Consumer;
+use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -40,23 +42,28 @@ public function testShouldDoNothingIfMessageHasTopicPropertySetOnPreReceive()
         $message = new NullMessage();
         $message->setProperty(Config::TOPIC, 'aTopic');
 
-        $context = new Context(new NullContext());
-        $context->setInteropMessage($message);
-
         $driver = $this->createDriverStub();
         $driver
             ->expects($this->never())
             ->method('createQueue')
         ;
 
+        $messageReceived = new MessageReceived(
+            $this->createContextMock(),
+            $this->createConsumerStub(null),
+            $message,
+            $this->createProcessorMock(),
+            new NullLogger()
+        );
+
         $extension = new ExclusiveCommandExtension($driver);
 
-        $extension->onPreReceived($context);
+        $extension->onMessageReceived($messageReceived);
 
-        self::assertNull($context->getResult());
+        self::assertNull($messageReceived->getResult());
 
         $this->assertEquals([
-            'enqueue.topic' => 'aTopic',
+            Config::TOPIC => 'aTopic',
         ], $message->getProperties());
     }
 
@@ -65,8 +72,13 @@ public function testShouldDoNothingIfMessageHasCommandPropertySetOnPreReceive()
         $message = new NullMessage();
         $message->setProperty(Config::COMMAND, 'aCommand');
 
-        $context = new Context(new NullContext());
-        $context->setInteropMessage($message);
+        $messageReceived = new MessageReceived(
+            $this->createContextMock(),
+            $this->createConsumerStub(null),
+            $message,
+            $this->createProcessorMock(),
+            new NullLogger()
+        );
 
         $driver = $this->createDriverStub();
         $driver
@@ -76,12 +88,12 @@ public function testShouldDoNothingIfMessageHasCommandPropertySetOnPreReceive()
 
         $extension = new ExclusiveCommandExtension($driver);
 
-        $extension->onPreReceived($context);
+        $extension->onMessageReceived($messageReceived);
 
-        self::assertNull($context->getResult());
+        self::assertNull($messageReceived->getResult());
 
         $this->assertEquals([
-            'enqueue.command' => 'aCommand',
+            Config::COMMAND => 'aCommand',
         ], $message->getProperties());
     }
 
@@ -90,8 +102,13 @@ public function testShouldDoNothingIfMessageHasProcessorPropertySetOnPreReceive(
         $message = new NullMessage();
         $message->setProperty(Config::PROCESSOR, 'aProcessor');
 
-        $context = new Context(new NullContext());
-        $context->setInteropMessage($message);
+        $messageReceived = new MessageReceived(
+            $this->createContextMock(),
+            $this->createConsumerStub(null),
+            $message,
+            $this->createProcessorMock(),
+            new NullLogger()
+        );
 
         $driver = $this->createDriverStub();
         $driver
@@ -101,12 +118,12 @@ public function testShouldDoNothingIfMessageHasProcessorPropertySetOnPreReceive(
 
         $extension = new ExclusiveCommandExtension($driver);
 
-        $extension->onPreReceived($context);
+        $extension->onMessageReceived($messageReceived);
 
-        self::assertNull($context->getResult());
+        self::assertNull($messageReceived->getResult());
 
         $this->assertEquals([
-            'enqueue.processor' => 'aProcessor',
+            Config::PROCESSOR => 'aProcessor',
         ], $message->getProperties());
     }
 
@@ -115,15 +132,19 @@ public function testShouldDoNothingIfCurrentQueueHasNoExclusiveProcessor()
         $message = new NullMessage();
         $queue = new NullQueue('aBarQueueName');
 
-        $context = new Context(new NullContext());
-        $context->setInteropMessage($message);
-        $context->setInteropQueue($queue);
+        $messageReceived = new MessageReceived(
+            $this->createContextMock(),
+            $this->createConsumerStub($queue),
+            $message,
+            $this->createProcessorMock(),
+            new NullLogger()
+        );
 
         $extension = new ExclusiveCommandExtension($this->createDriverStub(new RouteCollection([])));
 
-        $extension->onPreReceived($context);
+        $extension->onMessageReceived($messageReceived);
 
-        self::assertNull($context->getResult());
+        self::assertNull($messageReceived->getResult());
 
         $this->assertEquals([], $message->getProperties());
     }
@@ -133,10 +154,13 @@ public function testShouldSetCommandPropertiesIfCurrentQueueHasExclusiveCommandP
         $message = new NullMessage();
         $queue = new NullQueue('fooQueue');
 
-        $context = new Context(new NullContext());
-        $context->setInteropMessage($message);
-        $context->setInteropQueue($queue);
-        $context->setLogger(new NullLogger());
+        $messageReceived = new MessageReceived(
+            $this->createContextMock(),
+            $this->createConsumerStub($queue),
+            $message,
+            $this->createProcessorMock(),
+            new NullLogger()
+        );
 
         $routeCollection = new RouteCollection([
             new Route('fooCommand', Route::COMMAND, 'theFooProcessor', [
@@ -159,13 +183,13 @@ public function testShouldSetCommandPropertiesIfCurrentQueueHasExclusiveCommandP
         ;
 
         $extension = new ExclusiveCommandExtension($driver);
-        $extension->onPreReceived($context);
+        $extension->onMessageReceived($messageReceived);
 
-        self::assertNull($context->getResult());
+        self::assertNull($messageReceived->getResult());
 
         $this->assertEquals([
-            'enqueue.processor' => 'theFooProcessor',
-            'enqueue.command' => 'fooCommand',
+            Config::PROCESSOR => 'theFooProcessor',
+            Config::COMMAND => 'fooCommand',
         ], $message->getProperties());
     }
 
@@ -174,10 +198,13 @@ public function testShouldDoNothingIfAnotherQueue()
         $message = new NullMessage();
         $queue = new NullQueue('barQueue');
 
-        $context = new Context(new NullContext());
-        $context->setInteropMessage($message);
-        $context->setInteropQueue($queue);
-        $context->setLogger(new NullLogger());
+        $messageReceived = new MessageReceived(
+            $this->createContextMock(),
+            $this->createConsumerStub($queue),
+            $message,
+            $this->createProcessorMock(),
+            new NullLogger()
+        );
 
         $routeCollection = new RouteCollection([
             new Route('fooCommand', Route::COMMAND, 'theFooProcessor', [
@@ -200,9 +227,9 @@ public function testShouldDoNothingIfAnotherQueue()
         ;
 
         $extension = new ExclusiveCommandExtension($driver);
-        $extension->onPreReceived($context);
+        $extension->onMessageReceived($messageReceived);
 
-        self::assertNull($context->getResult());
+        self::assertNull($messageReceived->getResult());
 
         $this->assertEquals([], $message->getProperties());
     }
@@ -221,4 +248,37 @@ private function createDriverStub(RouteCollection $routeCollection = null): Driv
 
         return $driver;
     }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createContextMock(): InteropContext
+    {
+        return $this->createMock(InteropContext::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createProcessorMock(): Processor
+    {
+        return $this->createMock(Processor::class);
+    }
+
+    /**
+     * @param mixed $queue
+     *
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createConsumerStub($queue): Consumer
+    {
+        $consumerMock = $this->createMock(Consumer::class);
+        $consumerMock
+            ->expects($this->any())
+            ->method('getQueue')
+            ->willReturn($queue)
+        ;
+
+        return $consumerMock;
+    }
 }
diff --git a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
index 2314b9f..42f6dd5 100644
--- a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
@@ -5,13 +5,10 @@
 use Enqueue\Client\ConsumptionExtension\FlushSpoolProducerExtension;
 use Enqueue\Client\SpoolProducer;
 use Enqueue\Consumption\Context;
-use Enqueue\Consumption\Context\PreConsume;
-use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Test\ClassExtensionTrait;
 use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
-use Psr\Log\NullLogger;
 
 class FlushSpoolProducerExtensionTest extends TestCase
 {
@@ -27,77 +24,6 @@ public function testCouldBeConstructedWithSpoolProducerAsFirstArgument()
         new FlushSpoolProducerExtension($this->createSpoolProducerMock());
     }
 
-    public function testShouldDoNothingOnStart()
-    {
-        $producer = $this->createSpoolProducerMock();
-        $producer
-            ->expects(self::never())
-            ->method('flush')
-        ;
-
-        $extension = new FlushSpoolProducerExtension($producer);
-        $extension->onStart(
-            new Start($this->createMock(\Interop\Queue\Context::class), new NullLogger(), [], 0, 0, 0)
-        );
-    }
-
-    public function testShouldDoNothingOnPreConsume()
-    {
-        $producer = $this->createSpoolProducerMock();
-        $producer
-            ->expects(self::never())
-            ->method('flush')
-        ;
-
-        $context = new PreConsume(
-            $this->createInteropContextMock(),
-            $this->createSubscriptionConsumerMock(),
-            new NullLogger(),
-            1,
-            2,
-            3
-        );
-
-        $extension = new FlushSpoolProducerExtension($producer);
-        $extension->onPreConsume($context);
-    }
-
-    public function testShouldDoNothingOnPreReceived()
-    {
-        $producer = $this->createSpoolProducerMock();
-        $producer
-            ->expects(self::never())
-            ->method('flush')
-        ;
-
-        $extension = new FlushSpoolProducerExtension($producer);
-        $extension->onPreReceived($this->createContextMock());
-    }
-
-    public function testShouldDoNothingOnResult()
-    {
-        $producer = $this->createSpoolProducerMock();
-        $producer
-            ->expects(self::never())
-            ->method('flush')
-        ;
-
-        $extension = new FlushSpoolProducerExtension($producer);
-        $extension->onResult($this->createContextMock());
-    }
-
-    public function testShouldDoNothingOnIdle()
-    {
-        $producer = $this->createSpoolProducerMock();
-        $producer
-            ->expects(self::never())
-            ->method('flush')
-        ;
-
-        $extension = new FlushSpoolProducerExtension($producer);
-        $extension->onIdle($this->createContextMock());
-    }
-
     public function testShouldFlushSpoolProducerOnInterrupted()
     {
         $producer = $this->createSpoolProducerMock();
diff --git a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
index 2d21b90..c72c6e9 100644
--- a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
@@ -5,12 +5,14 @@
 use Enqueue\Client\Config;
 use Enqueue\Client\ConsumptionExtension\SetRouterPropertiesExtension;
 use Enqueue\Client\DriverInterface;
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Null\NullMessage;
 use Enqueue\Null\NullQueue;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\Consumer;
 use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -48,16 +50,19 @@ public function testShouldSetRouterProcessorPropertyIfNotSetAndOnRouterQueue()
 
         $message = new NullMessage();
 
-        $context = new Context($this->createContextMock());
-        $context->setLogger(new NullLogger());
-        $context->setInteropMessage($message);
-        $context->setInteropQueue(new NullQueue('test.router-queue'));
+        $messageReceived = new MessageReceived(
+            $this->createContextMock(),
+            $this->createConsumerStub(new NullQueue('test.router-queue')),
+            $message,
+            $this->createProcessorMock(),
+            new NullLogger()
+        );
 
         $extension = new SetRouterPropertiesExtension($driver);
-        $extension->onPreReceived($context);
+        $extension->onMessageReceived($messageReceived);
 
         $this->assertEquals([
-            'enqueue.processor' => 'router-processor-name',
+            Config::PROCESSOR => 'router-processor-name',
         ], $message->getProperties());
     }
 
@@ -81,12 +86,16 @@ public function testShouldNotSetRouterProcessorPropertyIfNotSetAndNotOnRouterQue
 
         $message = new NullMessage();
 
-        $context = new Context($this->createContextMock());
-        $context->setInteropMessage($message);
-        $context->setInteropQueue(new NullQueue('test.another-queue'));
+        $messageReceived = new MessageReceived(
+            $this->createContextMock(),
+            $this->createConsumerStub(new NullQueue('test.another-queue')),
+            $message,
+            $this->createProcessorMock(),
+            new NullLogger()
+        );
 
         $extension = new SetRouterPropertiesExtension($driver);
-        $extension->onPreReceived($context);
+        $extension->onMessageReceived($messageReceived);
 
         $this->assertEquals([], $message->getProperties());
     }
@@ -102,11 +111,16 @@ public function testShouldNotSetAnyPropertyIfProcessorNamePropertyAlreadySet()
         $message = new NullMessage();
         $message->setProperty(Config::PROCESSOR, 'non-router-processor');
 
-        $context = new Context($this->createContextMock());
-        $context->setInteropMessage($message);
+        $messageReceived = new MessageReceived(
+            $this->createContextMock(),
+            $this->createConsumerStub(null),
+            $message,
+            $this->createProcessorMock(),
+            new NullLogger()
+        );
 
         $extension = new SetRouterPropertiesExtension($driver);
-        $extension->onPreReceived($context);
+        $extension->onMessageReceived($messageReceived);
 
         $this->assertEquals([
             'enqueue.processor' => 'non-router-processor',
@@ -124,8 +138,33 @@ protected function createContextMock(): InteropContext
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|DriverInterface
      */
-    protected function createDriverMock()
+    protected function createDriverMock(): DriverInterface
     {
         return $this->createMock(DriverInterface::class);
     }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createProcessorMock(): Processor
+    {
+        return $this->createMock(Processor::class);
+    }
+
+    /**
+     * @param mixed $queue
+     *
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createConsumerStub($queue): Consumer
+    {
+        $consumerMock = $this->createMock(Consumer::class);
+        $consumerMock
+            ->expects($this->any())
+            ->method('getQueue')
+            ->willReturn($queue)
+        ;
+
+        return $consumerMock;
+    }
 }
diff --git a/Tests/Consumption/ChainExtensionTest.php b/Tests/Consumption/ChainExtensionTest.php
index 3c32b01..6171ef0 100644
--- a/Tests/Consumption/ChainExtensionTest.php
+++ b/Tests/Consumption/ChainExtensionTest.php
@@ -4,11 +4,15 @@
 
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\Consumer;
+use Interop\Queue\Message;
+use Interop\Queue\Processor;
 use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
@@ -107,24 +111,30 @@ public function testShouldProxyOnPreConsumeToAllInternalExtensions()
 
     public function testShouldProxyOnPreReceiveToAllInternalExtensions()
     {
-        $context = $this->createContextMock();
+        $context = new MessageReceived(
+            $this->createInteropContextMock(),
+            $this->createMock(Consumer::class),
+            $this->createMock(Message::class),
+            $this->createMock(Processor::class),
+            new NullLogger()
+        );
 
         $fooExtension = $this->createExtension();
         $fooExtension
             ->expects($this->once())
-            ->method('onPreReceived')
+            ->method('onMessageReceived')
             ->with($this->identicalTo($context))
         ;
         $barExtension = $this->createExtension();
         $barExtension
             ->expects($this->once())
-            ->method('onPreReceived')
+            ->method('onMessageReceived')
             ->with($this->identicalTo($context))
         ;
 
         $extensions = new ChainExtension([$fooExtension, $barExtension]);
 
-        $extensions->onPreReceived($context);
+        $extensions->onMessageReceived($context);
     }
 
     public function testShouldProxyOnResultToAllInternalExtensions()
diff --git a/Tests/Consumption/Extension/ReplyExtensionTest.php b/Tests/Consumption/Extension/ReplyExtensionTest.php
index 825bece..b34427a 100644
--- a/Tests/Consumption/Extension/ReplyExtensionTest.php
+++ b/Tests/Consumption/Extension/ReplyExtensionTest.php
@@ -3,8 +3,6 @@
 namespace Enqueue\Tests\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
-use Enqueue\Consumption\Context\PreConsume;
-use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\Extension\ReplyExtension;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Consumption\Result;
@@ -31,41 +29,6 @@ public function testCouldBeConstructedWithoutAnyArguments()
         new ReplyExtension();
     }
 
-    public function testShouldDoNothingOnPreReceived()
-    {
-        $extension = new ReplyExtension();
-
-        $extension->onPreReceived(new Context($this->createNeverUsedContextMock()));
-    }
-
-    public function testShouldDoNothingOnStart()
-    {
-        $extension = new ReplyExtension();
-
-        $extension->onStart(new Start($this->createNeverUsedContextMock(), new NullLogger(), [], 0, 0, 0));
-    }
-
-    public function testShouldDoNothingOnPreConsume()
-    {
-        $extension = new ReplyExtension();
-
-        $extension->onPreConsume(new PreConsume(
-            $this->createInteropContextMock(),
-            $this->createSubscriptionConsumerMock(),
-            new NullLogger(),
-            1,
-            2,
-            3
-        ));
-    }
-
-    public function testShouldDoNothingOnInterrupted()
-    {
-        $extension = new ReplyExtension();
-
-        $extension->onInterrupted(new Context($this->createNeverUsedContextMock()));
-    }
-
     public function testShouldDoNothingIfReceivedMessageNotHaveReplyToSet()
     {
         $extension = new ReplyExtension();
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 4897a24..60c4c8f 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -6,6 +6,7 @@
 use Enqueue\Consumption\CallbackProcessor;
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
@@ -531,10 +532,10 @@ public function testShouldNotPassMessageToProcessorIfItWasProcessedByExtension()
         $extension = $this->createExtension();
         $extension
             ->expects($this->once())
-            ->method('onPreReceived')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) {
-                $context->setResult(Result::ACK);
+            ->method('onMessageReceived')
+            ->with($this->isInstanceOf(MessageReceived::class))
+            ->willReturnCallback(function (MessageReceived $context) {
+                $context->setResult(Result::ack());
             })
         ;
 
@@ -777,22 +778,20 @@ public function testShouldCallOnPreReceivedExtensionMethodWithExpectedContext()
         $extension = $this->createExtension();
         $extension
             ->expects($this->once())
-            ->method('onPreReceived')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use (
+            ->method('onMessageReceived')
+            ->with($this->isInstanceOf(MessageReceived::class))
+            ->willReturnCallback(function (MessageReceived $context) use (
                 $contextStub,
                 $consumerStub,
                 $processorMock,
                 $expectedMessage
             ) {
-                $this->assertSame($contextStub, $context->getInteropContext());
+                $this->assertSame($contextStub, $context->getContext());
                 $this->assertSame($consumerStub, $context->getConsumer());
                 $this->assertSame($processorMock, $context->getProcessor());
-                $this->assertSame($expectedMessage, $context->getInteropMessage());
+                $this->assertSame($expectedMessage, $context->getMessage());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
-                $this->assertNull($context->getException());
                 $this->assertNull($context->getResult());
-                $this->assertFalse($context->isExecutionInterrupted());
             })
         ;
 
@@ -1042,62 +1041,6 @@ public function testShouldSetMainExceptionAsPreviousToExceptionThrownOnInterrupt
         $this->fail('Exception throw is expected.');
     }
 
-    public function testShouldAllowInterruptConsumingOnPreReceiveButProcessCurrentMessage()
-    {
-        $expectedMessage = $this->createMessageMock();
-
-        $subscriptionConsumerMock = new DummySubscriptionConsumer();
-        $subscriptionConsumerMock->addMessage($expectedMessage, 'foo_queue');
-
-        $consumerStub = $this->createConsumerStub('foo_queue');
-
-        $contextStub = $this->createContextStub($consumerStub);
-
-        $processorMock = $this->createProcessorMock();
-        $processorMock
-            ->expects($this->once())
-            ->method('process')
-            ->willReturn(Result::ACK)
-        ;
-
-        $extension = $this->createExtension();
-        $extension
-            ->expects($this->once())
-            ->method('onPreReceived')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) {
-                $context->setExecutionInterrupted(true);
-            })
-        ;
-        $extension
-            ->expects($this->atLeastOnce())
-            ->method('onInterrupted')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use (
-                $contextStub,
-                $consumerStub,
-                $processorMock,
-                $expectedMessage
-            ) {
-                $this->assertSame($contextStub, $context->getInteropContext());
-                $this->assertSame($consumerStub, $context->getConsumer());
-                $this->assertSame($processorMock, $context->getProcessor());
-                $this->assertSame($expectedMessage, $context->getInteropMessage());
-                $this->assertInstanceOf(NullLogger::class, $context->getLogger());
-                $this->assertNull($context->getException());
-                $this->assertSame(Result::ACK, $context->getResult());
-                $this->assertTrue($context->isExecutionInterrupted());
-            })
-        ;
-
-        $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
-        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
-        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
-        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
-
-        $queueConsumer->consume();
-    }
-
     public function testShouldAllowInterruptConsumingOnResult()
     {
         $expectedMessage = $this->createMessageMock();
@@ -1298,8 +1241,8 @@ public function testShouldCallExtensionPassedOnRuntime()
         ;
         $runtimeExtension
             ->expects($this->once())
-            ->method('onPreReceived')
-            ->with($this->isInstanceOf(Context::class))
+            ->method('onMessageReceived')
+            ->with($this->isInstanceOf(MessageReceived::class))
         ;
         $runtimeExtension
             ->expects($this->once())
@@ -1366,9 +1309,9 @@ public function testShouldChangeLoggerOnStart()
         ;
         $extension
             ->expects($this->atLeastOnce())
-            ->method('onPreReceived')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use ($expectedLogger) {
+            ->method('onMessageReceived')
+            ->with($this->isInstanceOf(MessageReceived::class))
+            ->willReturnCallback(function (MessageReceived $context) use ($expectedLogger) {
                 $this->assertSame($expectedLogger, $context->getLogger());
             })
         ;
@@ -1423,15 +1366,15 @@ public function testShouldCallProcessorAsMessageComeAlong()
         $processorMock = $this->createProcessorStub();
         $anotherProcessorMock = $this->createProcessorStub();
 
-        /** @var Context[] $actualContexts */
+        /** @var MessageReceived[] $actualContexts */
         $actualContexts = [];
 
         $extension = $this->createExtension();
         $extension
             ->expects($this->any())
-            ->method('onPreReceived')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use (&$actualContexts) {
+            ->method('onMessageReceived')
+            ->with($this->isInstanceOf(MessageReceived::class))
+            ->willReturnCallback(function (MessageReceived $context) use (&$actualContexts) {
                 $actualContexts[] = clone $context;
             })
         ;
@@ -1447,9 +1390,9 @@ public function testShouldCallProcessorAsMessageComeAlong()
 
         $this->assertCount(3, $actualContexts);
 
-        $this->assertSame($firstMessage, $actualContexts[0]->getInteropMessage());
-        $this->assertSame($secondMessage, $actualContexts[1]->getInteropMessage());
-        $this->assertSame($thirdMessage, $actualContexts[2]->getInteropMessage());
+        $this->assertSame($firstMessage, $actualContexts[0]->getMessage());
+        $this->assertSame($secondMessage, $actualContexts[1]->getMessage());
+        $this->assertSame($thirdMessage, $actualContexts[2]->getMessage());
 
         $this->assertSame($fooConsumerStub, $actualContexts[0]->getConsumer());
         $this->assertSame($barConsumerStub, $actualContexts[1]->getConsumer());

From 1af792bca94bcfba5ed2323a8f3e13c219b16329 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 8 Oct 2018 09:02:27 +0300
Subject: [PATCH 151/337] [consumption] use custom MessageResutl context on
 onResult.

---
 Consumption/ChainExtension.php                |  3 +-
 Consumption/Context.php                       |  6 +-
 Consumption/Context/MessageReceived.php       | 21 ++++-
 Consumption/Context/MessageResult.php         | 81 +++++++++++++++++++
 Consumption/EmptyExtensionTrait.php           |  3 +-
 Consumption/ExtensionInterface.php            | 11 ++-
 Consumption/QueueConsumer.php                 | 22 +++--
 .../DelayRedeliveredMessageExtensionTest.php  |  3 +
 .../ExclusiveCommandExtensionTest.php         |  6 ++
 .../SetRouterPropertiesExtensionTest.php      |  3 +
 Tests/Consumption/ChainExtensionTest.php      | 10 ++-
 Tests/Consumption/ContextTest.php             | 13 ---
 Tests/Consumption/QueueConsumerTest.php       | 76 ++---------------
 13 files changed, 154 insertions(+), 104 deletions(-)
 create mode 100644 Consumption/Context/MessageResult.php

diff --git a/Consumption/ChainExtension.php b/Consumption/ChainExtension.php
index fcb248a..5eca375 100644
--- a/Consumption/ChainExtension.php
+++ b/Consumption/ChainExtension.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Consumption;
 
 use Enqueue\Consumption\Context\MessageReceived;
+use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
@@ -55,7 +56,7 @@ public function onMessageReceived(MessageReceived $context): void
         }
     }
 
-    public function onResult(Context $context)
+    public function onResult(MessageResult $context): void
     {
         foreach ($this->extensions as $extension) {
             $extension->onResult($context);
diff --git a/Consumption/Context.php b/Consumption/Context.php
index 76935c1..822adbe 100644
--- a/Consumption/Context.php
+++ b/Consumption/Context.php
@@ -164,9 +164,9 @@ public function getResult()
      */
     public function setResult($result)
     {
-        if ($this->result) {
-            throw new IllegalContextModificationException('The result modification is not allowed');
-        }
+//        if ($this->result) {
+//            throw new IllegalContextModificationException('The result modification is not allowed');
+//        }
 
         $this->result = $result;
     }
diff --git a/Consumption/Context/MessageReceived.php b/Consumption/Context/MessageReceived.php
index 44d5232..ad6b6b9 100644
--- a/Consumption/Context/MessageReceived.php
+++ b/Consumption/Context/MessageReceived.php
@@ -36,17 +36,29 @@ final class MessageReceived
      */
     private $logger;
 
+    /**
+     * @var int
+     */
+    private $receivedAt;
+
     /**
      * @var Result|null
      */
     private $result;
 
-    public function __construct(Context $context, Consumer $consumer, Message $message, Processor $processor, LoggerInterface $logger)
-    {
+    public function __construct(
+        Context $context,
+        Consumer $consumer,
+        Message $message,
+        Processor $processor,
+        int $receivedAt,
+        LoggerInterface $logger
+    ) {
         $this->context = $context;
         $this->consumer = $consumer;
         $this->message = $message;
         $this->processor = $processor;
+        $this->receivedAt = $receivedAt;
         $this->logger = $logger;
     }
 
@@ -80,6 +92,11 @@ public function getLogger(): LoggerInterface
         return $this->logger;
     }
 
+    public function getReceivedAt(): int
+    {
+        return $this->receivedAt;
+    }
+
     public function getResult(): ?Result
     {
         return $this->result;
diff --git a/Consumption/Context/MessageResult.php b/Consumption/Context/MessageResult.php
new file mode 100644
index 0000000..fccda66
--- /dev/null
+++ b/Consumption/Context/MessageResult.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Enqueue\Consumption\Context;
+
+use Enqueue\Consumption\Result;
+use Interop\Queue\Context;
+use Interop\Queue\Message;
+use Psr\Log\LoggerInterface;
+
+final class MessageResult
+{
+    /**
+     * @var Context
+     */
+    private $context;
+
+    /**
+     * @var Message
+     */
+    private $message;
+
+    /**
+     * @var Result|string|object|null
+     */
+    private $result;
+
+    /**
+     * @var int
+     */
+    private $receivedAt;
+
+    /**
+     * @var LoggerInterface
+     */
+    private $logger;
+
+    public function __construct(Context $context, Message $message, $result, int $receivedAt, LoggerInterface $logger)
+    {
+        $this->context = $context;
+        $this->message = $message;
+        $this->logger = $logger;
+        $this->result = $result;
+        $this->receivedAt = $receivedAt;
+    }
+
+    public function getContext(): Context
+    {
+        return $this->context;
+    }
+
+    public function getMessage(): Message
+    {
+        return $this->message;
+    }
+
+    public function getLogger(): LoggerInterface
+    {
+        return $this->logger;
+    }
+
+    public function getReceivedAt(): int
+    {
+        return $this->receivedAt;
+    }
+
+    /**
+     * @return Result|null|object|string
+     */
+    public function getResult()
+    {
+        return $this->result;
+    }
+
+    /**
+     * @param Result|string|object|null $result
+     */
+    public function changeResult($result): void
+    {
+        $this->result = $result;
+    }
+}
diff --git a/Consumption/EmptyExtensionTrait.php b/Consumption/EmptyExtensionTrait.php
index 67fc8a7..b250cd2 100644
--- a/Consumption/EmptyExtensionTrait.php
+++ b/Consumption/EmptyExtensionTrait.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Consumption;
 
 use Enqueue\Consumption\Context\MessageReceived;
+use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
@@ -25,7 +26,7 @@ public function onMessageReceived(MessageReceived $context): void
     {
     }
 
-    public function onResult(Context $context)
+    public function onResult(MessageResult $context): void
     {
     }
 
diff --git a/Consumption/ExtensionInterface.php b/Consumption/ExtensionInterface.php
index 4e6f4de..0e76161 100644
--- a/Consumption/ExtensionInterface.php
+++ b/Consumption/ExtensionInterface.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Consumption;
 
 use Enqueue\Consumption\Context\MessageReceived;
+use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
@@ -33,13 +34,11 @@ public function onPreConsume(PreConsume $context): void;
     public function onMessageReceived(MessageReceived $context): void;
 
     /**
-     * Executed when a message is processed by a processor or a result was set in onPreReceived method.
-     * BUT before the message status was sent to the broker
-     * The consumption could be interrupted at this step but it exits after the message is processed.
-     *
-     * @param Context $context
+     * Executed when a message is processed by a processor or a result was set in onMessageReceived extension method.
+     * BEFORE the message status was sent to the broker
+     * The result could be changed at this point.
      */
-    public function onResult(Context $context);
+    public function onResult(MessageResult $context): void;
 
     /**
      * Executed when a message is processed by a processor.
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 5d420ba..81724d6 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Consumption;
 
 use Enqueue\Consumption\Context\MessageReceived;
+use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
@@ -143,13 +144,14 @@ public function bindCallback($queue, callable $processor): QueueConsumerInterfac
     public function consume(ExtensionInterface $runtimeExtension = null): void
     {
         /*
-         * onStart
-         * onPreSubscribe
-         * onPreConsume
-         * onPostConsume
-         * onReceived
+         * onStart             +
+         * onPreSubscribe      +
+         * onPreConsume        +
+         * onMessageReceived   +
          * onResult
+         * onProcessorException
          * onPostReceived
+         * onPostConsume
          * onEnd
          */
 
@@ -213,6 +215,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
         }
 
         $callback = function (InteropMessage $message, Consumer $consumer) use (&$context) {
+            $receivedAt = (int) (microtime(true) * 1000);
             $queue = $consumer->getQueue();
             if (false == array_key_exists($queue->getQueueName(), $this->boundProcessors)) {
                 throw new \LogicException(sprintf('The processor for the queue "%s" could not be found.', $queue->getQueueName()));
@@ -233,7 +236,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
             $context->setProcessor($processor);
             $context->setInteropMessage($message);
 
-            $messageReceived = new MessageReceived($this->interopContext, $consumer, $message, $processor, $this->logger);
+            $messageReceived = new MessageReceived($this->interopContext, $consumer, $message, $processor, $receivedAt, $this->logger);
             $this->extension->onMessageReceived($messageReceived);
             $result = $messageReceived->getResult();
             $processor = $messageReceived->getProcessor();
@@ -242,7 +245,12 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
                 $context->setResult($result);
             }
 
-            $this->extension->onResult($context);
+            $messageResult = new MessageResult($this->interopContext, $message, $result, $receivedAt, $this->logger);
+            $this->extension->onResult($messageResult);
+            $result = $messageResult->getResult();
+
+            //todo
+            $context->setResult($result);
 
             switch ($result) {
                 case Result::ACK:
diff --git a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
index cf4161a..8b6019e 100644
--- a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
@@ -69,6 +69,7 @@ public function testShouldSendDelayedMessageAndRejectOriginalMessage()
             $this->createConsumerStub($queue),
             $originMessage,
             $this->createProcessorMock(),
+            1,
             $logger
         );
 
@@ -103,6 +104,7 @@ public function testShouldDoNothingIfMessageIsNotRedelivered()
             $this->createConsumerStub(null),
             $message,
             $this->createProcessorMock(),
+            1,
             new NullLogger()
         );
 
@@ -128,6 +130,7 @@ public function testShouldDoNothingIfMessageIsRedeliveredButResultWasAlreadySetO
             $this->createConsumerStub(null),
             $message,
             $this->createProcessorMock(),
+            1,
             new NullLogger()
         );
         $messageReceived->setResult(Result::ack());
diff --git a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
index 8a54971..11e1c10 100644
--- a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
@@ -53,6 +53,7 @@ public function testShouldDoNothingIfMessageHasTopicPropertySetOnPreReceive()
             $this->createConsumerStub(null),
             $message,
             $this->createProcessorMock(),
+            1,
             new NullLogger()
         );
 
@@ -77,6 +78,7 @@ public function testShouldDoNothingIfMessageHasCommandPropertySetOnPreReceive()
             $this->createConsumerStub(null),
             $message,
             $this->createProcessorMock(),
+            1,
             new NullLogger()
         );
 
@@ -107,6 +109,7 @@ public function testShouldDoNothingIfMessageHasProcessorPropertySetOnPreReceive(
             $this->createConsumerStub(null),
             $message,
             $this->createProcessorMock(),
+            1,
             new NullLogger()
         );
 
@@ -137,6 +140,7 @@ public function testShouldDoNothingIfCurrentQueueHasNoExclusiveProcessor()
             $this->createConsumerStub($queue),
             $message,
             $this->createProcessorMock(),
+            1,
             new NullLogger()
         );
 
@@ -159,6 +163,7 @@ public function testShouldSetCommandPropertiesIfCurrentQueueHasExclusiveCommandP
             $this->createConsumerStub($queue),
             $message,
             $this->createProcessorMock(),
+            1,
             new NullLogger()
         );
 
@@ -203,6 +208,7 @@ public function testShouldDoNothingIfAnotherQueue()
             $this->createConsumerStub($queue),
             $message,
             $this->createProcessorMock(),
+            1,
             new NullLogger()
         );
 
diff --git a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
index c72c6e9..dc12c22 100644
--- a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
@@ -55,6 +55,7 @@ public function testShouldSetRouterProcessorPropertyIfNotSetAndOnRouterQueue()
             $this->createConsumerStub(new NullQueue('test.router-queue')),
             $message,
             $this->createProcessorMock(),
+            1,
             new NullLogger()
         );
 
@@ -91,6 +92,7 @@ public function testShouldNotSetRouterProcessorPropertyIfNotSetAndNotOnRouterQue
             $this->createConsumerStub(new NullQueue('test.another-queue')),
             $message,
             $this->createProcessorMock(),
+            1,
             new NullLogger()
         );
 
@@ -116,6 +118,7 @@ public function testShouldNotSetAnyPropertyIfProcessorNamePropertyAlreadySet()
             $this->createConsumerStub(null),
             $message,
             $this->createProcessorMock(),
+            1,
             new NullLogger()
         );
 
diff --git a/Tests/Consumption/ChainExtensionTest.php b/Tests/Consumption/ChainExtensionTest.php
index 6171ef0..dbd5135 100644
--- a/Tests/Consumption/ChainExtensionTest.php
+++ b/Tests/Consumption/ChainExtensionTest.php
@@ -5,6 +5,7 @@
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Context;
 use Enqueue\Consumption\Context\MessageReceived;
+use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
@@ -116,6 +117,7 @@ public function testShouldProxyOnPreReceiveToAllInternalExtensions()
             $this->createMock(Consumer::class),
             $this->createMock(Message::class),
             $this->createMock(Processor::class),
+            1,
             new NullLogger()
         );
 
@@ -139,7 +141,13 @@ public function testShouldProxyOnPreReceiveToAllInternalExtensions()
 
     public function testShouldProxyOnResultToAllInternalExtensions()
     {
-        $context = $this->createContextMock();
+        $context = new MessageResult(
+            $this->createInteropContextMock(),
+            $this->createMock(Message::class),
+            'aResult',
+            1,
+            new NullLogger()
+        );
 
         $fooExtension = $this->createExtension();
         $fooExtension
diff --git a/Tests/Consumption/ContextTest.php b/Tests/Consumption/ContextTest.php
index f110597..41fd211 100644
--- a/Tests/Consumption/ContextTest.php
+++ b/Tests/Consumption/ContextTest.php
@@ -144,19 +144,6 @@ public function testShouldAllowGetPreviouslySetResult()
         $this->assertSame($result, $context->getResult());
     }
 
-    public function testThrowOnTryToChangeResultIfAlreadySet()
-    {
-        $result = 'aResult';
-
-        $context = new Context($this->createContext());
-
-        $this->expectException(IllegalContextModificationException::class);
-        $this->expectExceptionMessage('The result modification is not allowed');
-
-        $context->setResult($result);
-        $context->setResult($result);
-    }
-
     public function testShouldAllowGetPreviouslySetExecutionInterrupted()
     {
         $context = new Context($this->createContext());
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 60c4c8f..bb56d6b 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -7,6 +7,7 @@
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Context;
 use Enqueue\Consumption\Context\MessageReceived;
+use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
@@ -820,21 +821,12 @@ public function testShouldCallOnResultExtensionMethodWithExpectedContext()
         $extension
             ->expects($this->once())
             ->method('onResult')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use (
-                $contextStub,
-                $consumerStub,
-                $processorMock,
-                $expectedMessage
-            ) {
-                $this->assertSame($contextStub, $context->getInteropContext());
-                $this->assertSame($consumerStub, $context->getConsumer());
-                $this->assertSame($processorMock, $context->getProcessor());
-                $this->assertSame($expectedMessage, $context->getInteropMessage());
+            ->with($this->isInstanceOf(MessageResult::class))
+            ->willReturnCallback(function (MessageResult $context) use ($contextStub, $expectedMessage) {
+                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($expectedMessage, $context->getMessage());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
-                $this->assertNull($context->getException());
                 $this->assertSame(Result::ACK, $context->getResult());
-                $this->assertFalse($context->isExecutionInterrupted());
             })
         ;
 
@@ -1041,62 +1033,6 @@ public function testShouldSetMainExceptionAsPreviousToExceptionThrownOnInterrupt
         $this->fail('Exception throw is expected.');
     }
 
-    public function testShouldAllowInterruptConsumingOnResult()
-    {
-        $expectedMessage = $this->createMessageMock();
-
-        $subscriptionConsumerMock = new DummySubscriptionConsumer();
-        $subscriptionConsumerMock->addMessage($expectedMessage, 'foo_queue');
-
-        $consumerStub = $this->createConsumerStub('foo_queue');
-
-        $contextStub = $this->createContextStub($consumerStub);
-
-        $processorMock = $this->createProcessorMock();
-        $processorMock
-            ->expects($this->once())
-            ->method('process')
-            ->willReturn(Result::ACK)
-        ;
-
-        $extension = $this->createExtension();
-        $extension
-            ->expects($this->once())
-            ->method('onResult')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) {
-                $context->setExecutionInterrupted(true);
-            })
-        ;
-        $extension
-            ->expects($this->atLeastOnce())
-            ->method('onInterrupted')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use (
-                $contextStub,
-                $consumerStub,
-                $processorMock,
-                $expectedMessage
-            ) {
-                $this->assertSame($contextStub, $context->getInteropContext());
-                $this->assertSame($consumerStub, $context->getConsumer());
-                $this->assertSame($processorMock, $context->getProcessor());
-                $this->assertSame($expectedMessage, $context->getInteropMessage());
-                $this->assertInstanceOf(NullLogger::class, $context->getLogger());
-                $this->assertNull($context->getException());
-                $this->assertSame(Result::ACK, $context->getResult());
-                $this->assertTrue($context->isExecutionInterrupted());
-            })
-        ;
-
-        $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
-        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
-        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
-        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
-
-        $queueConsumer->consume();
-    }
-
     public function testShouldAllowInterruptConsumingOnPostReceive()
     {
         $expectedMessage = $this->createMessageMock();
@@ -1247,7 +1183,7 @@ public function testShouldCallExtensionPassedOnRuntime()
         $runtimeExtension
             ->expects($this->once())
             ->method('onResult')
-            ->with($this->isInstanceOf(Context::class))
+            ->with($this->isInstanceOf(MessageResult::class))
         ;
         $runtimeExtension
             ->expects($this->once())

From cfef521125ee2993f261cbd12d5906a19b1217f9 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 8 Oct 2018 10:02:45 +0300
Subject: [PATCH 152/337] [consumption] Introduce ProcessorException extension
 point. Split Extension Interface.

---
 Consumption/ChainExtension.php                |  8 ++
 Consumption/Context/ProcessorException.php    | 84 +++++++++++++++++
 Consumption/EmptyExtensionTrait.php           |  5 +
 Consumption/ExtensionInterface.php            | 38 +-------
 .../MessageReceivedExtensionInterface.php     | 15 +++
 .../MessageResultExtensionInterface.php       | 15 +++
 Consumption/PreConsumeExtensionInterface.php  | 14 +++
 .../PreSubscribeExtensionInterface.php        | 13 +++
 .../ProcessorExceptionExtensionInterface.php  | 14 +++
 Consumption/QueueConsumer.php                 | 16 +++-
 Consumption/StartExtensionInterface.php       | 13 +++
 Tests/Consumption/QueueConsumerTest.php       | 94 +++++++++++++++++++
 12 files changed, 290 insertions(+), 39 deletions(-)
 create mode 100644 Consumption/Context/ProcessorException.php
 create mode 100644 Consumption/MessageReceivedExtensionInterface.php
 create mode 100644 Consumption/MessageResultExtensionInterface.php
 create mode 100644 Consumption/PreConsumeExtensionInterface.php
 create mode 100644 Consumption/PreSubscribeExtensionInterface.php
 create mode 100644 Consumption/ProcessorExceptionExtensionInterface.php
 create mode 100644 Consumption/StartExtensionInterface.php

diff --git a/Consumption/ChainExtension.php b/Consumption/ChainExtension.php
index 5eca375..c96300d 100644
--- a/Consumption/ChainExtension.php
+++ b/Consumption/ChainExtension.php
@@ -6,6 +6,7 @@
 use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
+use Enqueue\Consumption\Context\ProcessorException;
 use Enqueue\Consumption\Context\Start;
 
 class ChainExtension implements ExtensionInterface
@@ -63,6 +64,13 @@ public function onResult(MessageResult $context): void
         }
     }
 
+    public function onProcessorException(ProcessorException $context): void
+    {
+        foreach ($this->extensions as $extension) {
+            $extension->onProcessorException($context);
+        }
+    }
+
     public function onPostReceived(Context $context)
     {
         foreach ($this->extensions as $extension) {
diff --git a/Consumption/Context/ProcessorException.php b/Consumption/Context/ProcessorException.php
new file mode 100644
index 0000000..139bd67
--- /dev/null
+++ b/Consumption/Context/ProcessorException.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace Enqueue\Consumption\Context;
+
+use Enqueue\Consumption\Result;
+use Interop\Queue\Context;
+use Interop\Queue\Message;
+use Psr\Log\LoggerInterface;
+
+final class ProcessorException
+{
+    /**
+     * @var Context
+     */
+    private $context;
+
+    /**
+     * @var Message
+     */
+    private $message;
+
+    /**
+     * @var \Exception
+     */
+    private $exception;
+
+    /**
+     * @var Result|string|object|null
+     */
+    private $result;
+
+    /**
+     * @var int
+     */
+    private $receivedAt;
+    /**
+     * @var LoggerInterface
+     */
+    private $logger;
+
+    public function __construct(Context $context, Message $message, \Exception $exception, int $receivedAt, LoggerInterface $logger)
+    {
+        $this->context = $context;
+        $this->message = $message;
+        $this->exception = $exception;
+        $this->logger = $logger;
+        $this->receivedAt = $receivedAt;
+    }
+
+    public function getContext(): Context
+    {
+        return $this->context;
+    }
+
+    public function getMessage(): Message
+    {
+        return $this->message;
+    }
+
+    public function getException(): \Exception
+    {
+        return $this->exception;
+    }
+
+    public function getLogger(): LoggerInterface
+    {
+        return $this->logger;
+    }
+
+    public function getReceivedAt(): int
+    {
+        return $this->receivedAt;
+    }
+
+    public function getResult(): ?Result
+    {
+        return $this->result;
+    }
+
+    public function setResult(Result $result): void
+    {
+        $this->result = $result;
+    }
+}
diff --git a/Consumption/EmptyExtensionTrait.php b/Consumption/EmptyExtensionTrait.php
index b250cd2..4c75c12 100644
--- a/Consumption/EmptyExtensionTrait.php
+++ b/Consumption/EmptyExtensionTrait.php
@@ -6,6 +6,7 @@
 use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
+use Enqueue\Consumption\Context\ProcessorException;
 use Enqueue\Consumption\Context\Start;
 
 trait EmptyExtensionTrait
@@ -30,6 +31,10 @@ public function onResult(MessageResult $context): void
     {
     }
 
+    public function onProcessorException(ProcessorException $context): void
+    {
+    }
+
     public function onPostReceived(Context $context)
     {
     }
diff --git a/Consumption/ExtensionInterface.php b/Consumption/ExtensionInterface.php
index 0e76161..1f21d19 100644
--- a/Consumption/ExtensionInterface.php
+++ b/Consumption/ExtensionInterface.php
@@ -2,44 +2,8 @@
 
 namespace Enqueue\Consumption;
 
-use Enqueue\Consumption\Context\MessageReceived;
-use Enqueue\Consumption\Context\MessageResult;
-use Enqueue\Consumption\Context\PreConsume;
-use Enqueue\Consumption\Context\PreSubscribe;
-use Enqueue\Consumption\Context\Start;
-
-interface ExtensionInterface
+interface ExtensionInterface extends StartExtensionInterface, PreSubscribeExtensionInterface, PreConsumeExtensionInterface, MessageReceivedExtensionInterface, MessageResultExtensionInterface, ProcessorExceptionExtensionInterface
 {
-    /**
-     * Executed only once at the very beginning of the QueueConsumer::consume method call.
-     */
-    public function onStart(Start $context): void;
-
-    /**
-     * The method is called for each BoundProcessor before calling SubscriptionConsumer::subscribe method.
-     */
-    public function onPreSubscribe(PreSubscribe $context): void;
-
-    /**
-     * Executed at every new cycle before calling SubscriptionConsumer::consume method.
-     * The consumption could be interrupted at this step.
-     */
-    public function onPreConsume(PreConsume $context): void;
-
-    /**
-     * Executed as soon as a a message is received, before it is passed to a processor
-     * The extension may set a result. If the result is set the processor is not called
-     * The processor could be changed or decorated at this point.
-     */
-    public function onMessageReceived(MessageReceived $context): void;
-
-    /**
-     * Executed when a message is processed by a processor or a result was set in onMessageReceived extension method.
-     * BEFORE the message status was sent to the broker
-     * The result could be changed at this point.
-     */
-    public function onResult(MessageResult $context): void;
-
     /**
      * Executed when a message is processed by a processor.
      * The context contains a status, which could not be changed.
diff --git a/Consumption/MessageReceivedExtensionInterface.php b/Consumption/MessageReceivedExtensionInterface.php
new file mode 100644
index 0000000..10afc9c
--- /dev/null
+++ b/Consumption/MessageReceivedExtensionInterface.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Enqueue\Consumption;
+
+use Enqueue\Consumption\Context\MessageReceived;
+
+interface MessageReceivedExtensionInterface
+{
+    /**
+     * Executed as soon as a a message is received, before it is passed to a processor
+     * The extension may set a result. If the result is set the processor is not called
+     * The processor could be changed or decorated at this point.
+     */
+    public function onMessageReceived(MessageReceived $context): void;
+}
diff --git a/Consumption/MessageResultExtensionInterface.php b/Consumption/MessageResultExtensionInterface.php
new file mode 100644
index 0000000..01d9e53
--- /dev/null
+++ b/Consumption/MessageResultExtensionInterface.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Enqueue\Consumption;
+
+use Enqueue\Consumption\Context\MessageResult;
+
+interface MessageResultExtensionInterface
+{
+    /**
+     * Executed when a message is processed by a processor or a result was set in onMessageReceived extension method.
+     * BEFORE the message status was sent to the broker
+     * The result could be changed at this point.
+     */
+    public function onResult(MessageResult $context): void;
+}
diff --git a/Consumption/PreConsumeExtensionInterface.php b/Consumption/PreConsumeExtensionInterface.php
new file mode 100644
index 0000000..0b13fd8
--- /dev/null
+++ b/Consumption/PreConsumeExtensionInterface.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace Enqueue\Consumption;
+
+use Enqueue\Consumption\Context\PreConsume;
+
+interface PreConsumeExtensionInterface
+{
+    /**
+     * Executed at every new cycle before calling SubscriptionConsumer::consume method.
+     * The consumption could be interrupted at this step.
+     */
+    public function onPreConsume(PreConsume $context): void;
+}
diff --git a/Consumption/PreSubscribeExtensionInterface.php b/Consumption/PreSubscribeExtensionInterface.php
new file mode 100644
index 0000000..a6899dd
--- /dev/null
+++ b/Consumption/PreSubscribeExtensionInterface.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Enqueue\Consumption;
+
+use Enqueue\Consumption\Context\PreSubscribe;
+
+interface PreSubscribeExtensionInterface
+{
+    /**
+     * The method is called for each BoundProcessor before calling SubscriptionConsumer::subscribe method.
+     */
+    public function onPreSubscribe(PreSubscribe $context): void;
+}
diff --git a/Consumption/ProcessorExceptionExtensionInterface.php b/Consumption/ProcessorExceptionExtensionInterface.php
new file mode 100644
index 0000000..dc17d94
--- /dev/null
+++ b/Consumption/ProcessorExceptionExtensionInterface.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace Enqueue\Consumption;
+
+use Enqueue\Consumption\Context\ProcessorException;
+
+interface ProcessorExceptionExtensionInterface
+{
+    /**
+     * Execute if a processor throws an exception.
+     * The result could be set, if result is not set the exception is thrown again.
+     */
+    public function onProcessorException(ProcessorException $context): void;
+}
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 81724d6..7b4a374 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -6,6 +6,7 @@
 use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
+use Enqueue\Consumption\Context\ProcessorException;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\Exception\ConsumptionInterruptedException;
 use Enqueue\Consumption\Exception\InvalidArgumentException;
@@ -241,8 +242,19 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
             $result = $messageReceived->getResult();
             $processor = $messageReceived->getProcessor();
             if (null === $result) {
-                $result = $processor->process($message, $context->getInteropContext());
-                $context->setResult($result);
+                try {
+                    $result = $processor->process($message, $context->getInteropContext());
+
+                    $context->setResult($result);
+                } catch (\Exception $e) {
+                    $processorException = new ProcessorException($this->interopContext, $message, $e, $receivedAt, $this->logger);
+                    $this->extension->onProcessorException($processorException);
+
+                    $result = $processorException->getResult();
+                    if (null === $result) {
+                        throw $e;
+                    }
+                }
             }
 
             $messageResult = new MessageResult($this->interopContext, $message, $result, $receivedAt, $this->logger);
diff --git a/Consumption/StartExtensionInterface.php b/Consumption/StartExtensionInterface.php
new file mode 100644
index 0000000..9857106
--- /dev/null
+++ b/Consumption/StartExtensionInterface.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Enqueue\Consumption;
+
+use Enqueue\Consumption\Context\Start;
+
+interface StartExtensionInterface
+{
+    /**
+     * Executed only once at the very beginning of the QueueConsumer::consume method call.
+     */
+    public function onStart(Start $context): void;
+}
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index bb56d6b..714bf20 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -10,6 +10,7 @@
 use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
+use Enqueue\Consumption\Context\ProcessorException;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\Exception\InvalidArgumentException;
 use Enqueue\Consumption\ExtensionInterface;
@@ -838,6 +839,99 @@ public function testShouldCallOnResultExtensionMethodWithExpectedContext()
         $queueConsumer->consume();
     }
 
+    public function testShouldCallOnProcessorExceptionExtensionMethodWithExpectedContext()
+    {
+        $exception = new \LogicException('Exception exception');
+
+        $expectedMessage = $this->createMessageMock();
+
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($expectedMessage, 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createContextStub($consumerStub);
+
+        $processorMock = $this->createProcessorStub();
+        $processorMock
+            ->expects($this->once())
+            ->method('process')
+            ->willThrowException($exception)
+        ;
+
+        $extension = $this->createExtension();
+        $extension
+            ->expects($this->never())
+            ->method('onResult')
+        ;
+        $extension
+            ->expects($this->once())
+            ->method('onProcessorException')
+            ->with($this->isInstanceOf(ProcessorException::class))
+            ->willReturnCallback(function (ProcessorException $context) use ($contextStub, $expectedMessage, $exception) {
+                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($expectedMessage, $context->getMessage());
+                $this->assertSame($exception, $context->getException());
+                $this->assertGreaterThan(1, $context->getReceivedAt());
+                $this->assertInstanceOf(NullLogger::class, $context->getLogger());
+                $this->assertNull($context->getResult());
+            })
+        ;
+
+        $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Exception exception');
+        $queueConsumer->consume();
+    }
+
+    public function testShouldContinueConsumptionIfResultSetOnProcessorExceptionExtension()
+    {
+        $result = Result::ack();
+
+        $expectedMessage = $this->createMessageMock();
+
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($expectedMessage, 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createContextStub($consumerStub);
+
+        $processorMock = $this->createProcessorStub();
+        $processorMock
+            ->expects($this->once())
+            ->method('process')
+            ->willThrowException(new \LogicException())
+        ;
+
+        $extension = $this->createExtension();
+        $extension
+            ->expects($this->once())
+            ->method('onProcessorException')
+            ->willReturnCallback(function (ProcessorException $context) use ($result) {
+                $context->setResult($result);
+            })
+        ;
+        $extension
+            ->expects($this->once())
+            ->method('onResult')
+            ->willReturnCallback(function (MessageResult $context) use ($result) {
+                $this->assertSame($result, $context->getResult());
+            })
+        ;
+
+        $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
+
+        $queueConsumer->consume();
+    }
+
     public function testShouldCallOnPostReceivedExtensionMethodWithExpectedContext()
     {
         $expectedMessage = $this->createMessageMock();

From 0be6f7a741b602f958aad40252bc27db561bdf80 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 8 Oct 2018 10:10:13 +0300
Subject: [PATCH 153/337] [consumption] add already_ack result, queue consumer
 does nothing on it.

---
 Consumption/QueueConsumer.php           |  2 ++
 Consumption/Result.php                  |  2 ++
 Tests/Consumption/QueueConsumerTest.php | 34 +++++++++++++++++++++++++
 3 files changed, 38 insertions(+)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 7b4a374..b6af29a 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -274,6 +274,8 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
                 case Result::REQUEUE:
                     $consumer->reject($message, true);
                     break;
+                case Result::ALREADY_ACKNOWLEDGED:
+                    break;
                 default:
                     throw new \LogicException(sprintf('Status is not supported: %s', $result));
             }
diff --git a/Consumption/Result.php b/Consumption/Result.php
index 926f256..b94b744 100644
--- a/Consumption/Result.php
+++ b/Consumption/Result.php
@@ -22,6 +22,8 @@ class Result
      */
     const REQUEUE = Processor::REQUEUE;
 
+    const ALREADY_ACKNOWLEDGED = 'enqueue.already_acknowledged';
+
     /**
      * @var string
      */
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 714bf20..b540ddc 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -470,6 +470,40 @@ public function testShouldRejectMessageIfProcessorReturnSuchStatus()
         $queueConsumer->consume();
     }
 
+    public function testShouldDoNothingIfProcessorReturnsAlreadyAcknowledged()
+    {
+        $messageMock = $this->createMessageMock();
+
+        $subscriptionConsumerMock = new DummySubscriptionConsumer();
+        $subscriptionConsumerMock->addMessage($messageMock, 'foo_queue');
+
+        $consumerStub = $this->createConsumerStub('foo_queue');
+        $consumerStub
+            ->expects($this->never())
+            ->method('reject')
+        ;
+        $consumerStub
+            ->expects($this->never())
+            ->method('acknowledge')
+        ;
+
+        $contextStub = $this->createContextStub($consumerStub);
+
+        $processorMock = $this->createProcessorMock();
+        $processorMock
+            ->expects($this->once())
+            ->method('process')
+            ->with($this->identicalTo($messageMock))
+            ->willReturn(Result::ALREADY_ACKNOWLEDGED)
+        ;
+
+        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1));
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
+
+        $queueConsumer->consume();
+    }
+
     public function testShouldRequeueMessageIfProcessorReturnSuchStatus()
     {
         $messageMock = $this->createMessageMock();

From 4fb6679efef31689d2b8d01e3b2eb814884f5795 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 8 Oct 2018 11:27:00 +0300
Subject: [PATCH 154/337] [consumption] post  message received

---
 .../FlushSpoolProducerExtension.php           |  6 +-
 Consumption/ChainExtension.php                |  5 +-
 Consumption/Context/PostMessageReceived.php   | 95 +++++++++++++++++++
 Consumption/EmptyExtensionTrait.php           |  7 +-
 .../LimitConsumedMessagesExtension.php        |  6 +-
 .../LimitConsumerMemoryExtension.php          |  5 +-
 .../LimitConsumptionTimeExtension.php         |  5 +-
 Consumption/Extension/LoggerExtension.php     | 11 +--
 Consumption/Extension/ReplyExtension.php      | 15 ++-
 Consumption/Extension/SignalExtension.php     |  5 +-
 Consumption/ExtensionInterface.php            | 11 +--
 .../PostMessageReceivedExtensionInterface.php | 15 +++
 Consumption/QueueConsumer.php                 | 30 +++---
 .../FlushSpoolProducerExtensionTest.php       | 13 ++-
 Tests/Consumption/ChainExtensionTest.php      | 15 ++-
 .../LimitConsumedMessagesExtensionTest.php    | 39 ++++++--
 .../LimitConsumerMemoryExtensionTest.php      | 36 +++++--
 .../LimitConsumptionTimeExtensionTest.php     | 30 ++++--
 .../Extension/LoggerExtensionTest.php         | 61 ++++++++----
 .../Extension/ReplyExtensionTest.php          | 73 +++++++-------
 .../Consumption/Mock/BreakCycleExtension.php  |  9 +-
 Tests/Consumption/QueueConsumerTest.php       | 32 +++----
 22 files changed, 361 insertions(+), 163 deletions(-)
 create mode 100644 Consumption/Context/PostMessageReceived.php
 create mode 100644 Consumption/PostMessageReceivedExtensionInterface.php

diff --git a/Client/ConsumptionExtension/FlushSpoolProducerExtension.php b/Client/ConsumptionExtension/FlushSpoolProducerExtension.php
index efb6848..c3e903c 100644
--- a/Client/ConsumptionExtension/FlushSpoolProducerExtension.php
+++ b/Client/ConsumptionExtension/FlushSpoolProducerExtension.php
@@ -4,6 +4,7 @@
 
 use Enqueue\Client\SpoolProducer;
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface;
 
@@ -24,10 +25,7 @@ public function __construct(SpoolProducer $producer)
         $this->producer = $producer;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function onPostReceived(Context $context)
+    public function onPostMessageReceived(PostMessageReceived $context): void
     {
         $this->producer->flush();
     }
diff --git a/Consumption/ChainExtension.php b/Consumption/ChainExtension.php
index c96300d..8db839e 100644
--- a/Consumption/ChainExtension.php
+++ b/Consumption/ChainExtension.php
@@ -4,6 +4,7 @@
 
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\ProcessorException;
@@ -71,10 +72,10 @@ public function onProcessorException(ProcessorException $context): void
         }
     }
 
-    public function onPostReceived(Context $context)
+    public function onPostMessageReceived(PostMessageReceived $context): void
     {
         foreach ($this->extensions as $extension) {
-            $extension->onPostReceived($context);
+            $extension->onPostMessageReceived($context);
         }
     }
 
diff --git a/Consumption/Context/PostMessageReceived.php b/Consumption/Context/PostMessageReceived.php
new file mode 100644
index 0000000..85ecaec
--- /dev/null
+++ b/Consumption/Context/PostMessageReceived.php
@@ -0,0 +1,95 @@
+<?php
+
+namespace Enqueue\Consumption\Context;
+
+use Enqueue\Consumption\Result;
+use Interop\Queue\Context;
+use Interop\Queue\Message;
+use Psr\Log\LoggerInterface;
+
+final class PostMessageReceived
+{
+    /**
+     * @var Context
+     */
+    private $context;
+
+    /**
+     * @var Message
+     */
+    private $message;
+
+    /**
+     * @var LoggerInterface
+     */
+    private $logger;
+
+    /**
+     * @var int
+     */
+    private $receivedAt;
+
+    /**
+     * @var Result|string|object|null
+     */
+    private $result;
+
+    /**
+     * @var bool
+     */
+    private $executionInterrupted;
+
+    public function __construct(
+        Context $context,
+        Message $message,
+        $result,
+        int $receivedAt,
+        LoggerInterface $logger
+    ) {
+        $this->context = $context;
+        $this->message = $message;
+        $this->result = $result;
+        $this->receivedAt = $receivedAt;
+        $this->logger = $logger;
+
+        $this->executionInterrupted = false;
+    }
+
+    public function getContext(): Context
+    {
+        return $this->context;
+    }
+
+    public function getMessage(): Message
+    {
+        return $this->message;
+    }
+
+    public function getLogger(): LoggerInterface
+    {
+        return $this->logger;
+    }
+
+    public function getReceivedAt(): int
+    {
+        return $this->receivedAt;
+    }
+
+    /**
+     * @return Result|null|object|string
+     */
+    public function getResult()
+    {
+        return $this->result;
+    }
+
+    public function isExecutionInterrupted(): bool
+    {
+        return $this->executionInterrupted;
+    }
+
+    public function interruptExecution(): void
+    {
+        $this->executionInterrupted = true;
+    }
+}
diff --git a/Consumption/EmptyExtensionTrait.php b/Consumption/EmptyExtensionTrait.php
index 4c75c12..39b381e 100644
--- a/Consumption/EmptyExtensionTrait.php
+++ b/Consumption/EmptyExtensionTrait.php
@@ -4,6 +4,7 @@
 
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\ProcessorException;
@@ -27,15 +28,15 @@ public function onMessageReceived(MessageReceived $context): void
     {
     }
 
-    public function onResult(MessageResult $context): void
+    public function onPostMessageReceived(PostMessageReceived $context): void
     {
     }
 
-    public function onProcessorException(ProcessorException $context): void
+    public function onResult(MessageResult $context): void
     {
     }
 
-    public function onPostReceived(Context $context)
+    public function onProcessorException(ProcessorException $context): void
     {
     }
 
diff --git a/Consumption/Extension/LimitConsumedMessagesExtension.php b/Consumption/Extension/LimitConsumedMessagesExtension.php
index 492e968..dbd0cf2 100644
--- a/Consumption/Extension/LimitConsumedMessagesExtension.php
+++ b/Consumption/Extension/LimitConsumedMessagesExtension.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Consumption\Extension;
 
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface;
@@ -39,12 +39,12 @@ public function onPreConsume(PreConsume $context): void
         }
     }
 
-    public function onPostReceived(Context $context)
+    public function onPostMessageReceived(PostMessageReceived $context): void
     {
         ++$this->messageConsumed;
 
         if ($this->shouldBeStopped($context->getLogger())) {
-            $context->setExecutionInterrupted(true);
+            $context->interruptExecution();
         }
     }
 
diff --git a/Consumption/Extension/LimitConsumerMemoryExtension.php b/Consumption/Extension/LimitConsumerMemoryExtension.php
index 78c6d05..a7d10a4 100644
--- a/Consumption/Extension/LimitConsumerMemoryExtension.php
+++ b/Consumption/Extension/LimitConsumerMemoryExtension.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface;
@@ -39,10 +40,10 @@ public function onPreConsume(PreConsume $context): void
         }
     }
 
-    public function onPostReceived(Context $context)
+    public function onPostMessageReceived(PostMessageReceived $context): void
     {
         if ($this->shouldBeStopped($context->getLogger())) {
-            $context->setExecutionInterrupted(true);
+            $context->interruptExecution();
         }
     }
 
diff --git a/Consumption/Extension/LimitConsumptionTimeExtension.php b/Consumption/Extension/LimitConsumptionTimeExtension.php
index c87e3ca..8b621df 100644
--- a/Consumption/Extension/LimitConsumptionTimeExtension.php
+++ b/Consumption/Extension/LimitConsumptionTimeExtension.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface;
@@ -39,10 +40,10 @@ public function onIdle(Context $context)
         }
     }
 
-    public function onPostReceived(Context $context)
+    public function onPostMessageReceived(PostMessageReceived $context): void
     {
         if ($this->shouldBeStopped($context->getLogger())) {
-            $context->setExecutionInterrupted(true);
+            $context->interruptExecution();
         }
     }
 
diff --git a/Consumption/Extension/LoggerExtension.php b/Consumption/Extension/LoggerExtension.php
index 7fff127..a3b11eb 100644
--- a/Consumption/Extension/LoggerExtension.php
+++ b/Consumption/Extension/LoggerExtension.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Consumption\Extension;
 
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface;
@@ -42,10 +42,7 @@ public function onStart(Start $context): void
         }
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function onPostReceived(Context $context)
+    public function onPostMessageReceived(PostMessageReceived $context): void
     {
         if (false == $context->getResult() instanceof Result) {
             return;
@@ -58,13 +55,13 @@ public function onPostReceived(Context $context)
             case Result::REJECT:
             case Result::REQUEUE:
                 if ($result->getReason()) {
-                    $this->logger->error($result->getReason(), $this->messageToLogContext($context->getInteropMessage()));
+                    $this->logger->error($result->getReason(), $this->messageToLogContext($context->getMessage()));
                 }
 
                 break;
             case Result::ACK:
                 if ($result->getReason()) {
-                    $this->logger->info($result->getReason(), $this->messageToLogContext($context->getInteropMessage()));
+                    $this->logger->info($result->getReason(), $this->messageToLogContext($context->getMessage()));
                 }
 
                 break;
diff --git a/Consumption/Extension/ReplyExtension.php b/Consumption/Extension/ReplyExtension.php
index 4435e55..d7f7dcf 100644
--- a/Consumption/Extension/ReplyExtension.php
+++ b/Consumption/Extension/ReplyExtension.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Consumption\Extension;
 
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Consumption\Result;
@@ -11,12 +11,9 @@ class ReplyExtension implements ExtensionInterface
 {
     use EmptyExtensionTrait;
 
-    /**
-     * {@inheritdoc}
-     */
-    public function onPostReceived(Context $context)
+    public function onPostMessageReceived(PostMessageReceived $context): void
     {
-        $replyTo = $context->getInteropMessage()->getReplyTo();
+        $replyTo = $context->getMessage()->getReplyTo();
         if (false == $replyTo) {
             return;
         }
@@ -31,13 +28,13 @@ public function onPostReceived(Context $context)
             return;
         }
 
-        $correlationId = $context->getInteropMessage()->getCorrelationId();
+        $correlationId = $context->getMessage()->getCorrelationId();
         $replyMessage = clone $result->getReply();
         $replyMessage->setCorrelationId($correlationId);
 
-        $replyQueue = $context->getInteropContext()->createQueue($replyTo);
+        $replyQueue = $context->getContext()->createQueue($replyTo);
 
         $context->getLogger()->debug(sprintf('[ReplyExtension] Send reply to "%s"', $replyTo));
-        $context->getInteropContext()->createProducer()->send($replyQueue, $replyMessage);
+        $context->getContext()->createProducer()->send($replyQueue, $replyMessage);
     }
 }
diff --git a/Consumption/Extension/SignalExtension.php b/Consumption/Extension/SignalExtension.php
index e4d484d..75ebc01 100644
--- a/Consumption/Extension/SignalExtension.php
+++ b/Consumption/Extension/SignalExtension.php
@@ -4,6 +4,7 @@
 
 use Enqueue\Consumption\Context;
 use Enqueue\Consumption\Context\MessageReceived;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\EmptyExtensionTrait;
@@ -54,10 +55,10 @@ public function onMessageReceived(MessageReceived $context): void
     {
     }
 
-    public function onPostReceived(Context $context)
+    public function onPostMessageReceived(PostMessageReceived $context): void
     {
         if ($this->shouldBeStopped($context->getLogger())) {
-            $context->setExecutionInterrupted(true);
+            $context->interruptExecution();
         }
     }
 
diff --git a/Consumption/ExtensionInterface.php b/Consumption/ExtensionInterface.php
index 1f21d19..3f57652 100644
--- a/Consumption/ExtensionInterface.php
+++ b/Consumption/ExtensionInterface.php
@@ -2,17 +2,8 @@
 
 namespace Enqueue\Consumption;
 
-interface ExtensionInterface extends StartExtensionInterface, PreSubscribeExtensionInterface, PreConsumeExtensionInterface, MessageReceivedExtensionInterface, MessageResultExtensionInterface, ProcessorExceptionExtensionInterface
+interface ExtensionInterface extends StartExtensionInterface, PreSubscribeExtensionInterface, PreConsumeExtensionInterface, MessageReceivedExtensionInterface, PostMessageReceivedExtensionInterface, MessageResultExtensionInterface, ProcessorExceptionExtensionInterface
 {
-    /**
-     * Executed when a message is processed by a processor.
-     * The context contains a status, which could not be changed.
-     * The consumption could be interrupted at this step but it exits after the message is processed.
-     *
-     * @param Context $context
-     */
-    public function onPostReceived(Context $context);
-
     /**
      * Called each time at the end of the cycle if nothing was done.
      *
diff --git a/Consumption/PostMessageReceivedExtensionInterface.php b/Consumption/PostMessageReceivedExtensionInterface.php
new file mode 100644
index 0000000..febee4a
--- /dev/null
+++ b/Consumption/PostMessageReceivedExtensionInterface.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Enqueue\Consumption;
+
+use Enqueue\Consumption\Context\PostMessageReceived;
+
+interface PostMessageReceivedExtensionInterface
+{
+    /**
+     * Executed at the very end of consumption callback. The message has already been acknowledged.
+     * The message result could not be changed.
+     * The consumption could be interrupted at this point.
+     */
+    public function onPostMessageReceived(PostMessageReceived $context): void;
+}
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index b6af29a..04adbcb 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -4,6 +4,7 @@
 
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\ProcessorException;
@@ -151,7 +152,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
          * onMessageReceived   +
          * onResult
          * onProcessorException
-         * onPostReceived
+         * onPostMessageReceived
          * onPostConsume
          * onEnd
          */
@@ -207,7 +208,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
 //            $context->setLogger($this->logger);
 //        }
 
-        $this->logger->info('Start consuming');
+//        $this->logger->info('Start consuming');
 
         try {
             $subscriptionConsumer = $this->interopContext->createSubscriptionConsumer();
@@ -280,11 +281,12 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
                     throw new \LogicException(sprintf('Status is not supported: %s', $result));
             }
 
-            $this->logger->info(sprintf('Message processed: %s', $result));
+//            $this->logger->info(sprintf('Message processed: %s', $result));
 
-            $this->extension->onPostReceived($context);
+            $postMessageReceived = new PostMessageReceived($this->interopContext, $message, $result, $receivedAt, $this->logger);
+            $this->extension->onPostMessageReceived($postMessageReceived);
 
-            if ($context->isExecutionInterrupted()) {
+            if ($postMessageReceived->isExecutionInterrupted()) {
                 throw new ConsumptionInterruptedException();
             }
 
@@ -369,8 +371,8 @@ public function setFallbackSubscriptionConsumer(SubscriptionConsumer $fallbackSu
      */
     private function onInterruptionByException(ExtensionInterface $extension, Context $context)
     {
-        $this->logger = $context->getLogger();
-        $this->logger->error(sprintf('Consuming interrupted by exception'));
+//        $this->logger = $context->getLogger();
+//        $this->logger->error(sprintf('Consuming interrupted by exception'));
 
         $exception = $context->getException();
 
@@ -378,13 +380,13 @@ private function onInterruptionByException(ExtensionInterface $extension, Contex
             $this->extension->onInterrupted($context);
         } catch (\Exception $e) {
             // logic is similar to one in Symfony's ExceptionListener::onKernelException
-            $this->logger->error(sprintf(
-                'Exception thrown when handling an exception (%s: %s at %s line %s)',
-                get_class($e),
-                $e->getMessage(),
-                $e->getFile(),
-                $e->getLine()
-            ));
+//            $this->logger->error(sprintf(
+//                'Exception thrown when handling an exception (%s: %s at %s line %s)',
+//                get_class($e),
+//                $e->getMessage(),
+//                $e->getFile(),
+//                $e->getLine()
+//            ));
 
             $wrapper = $e;
             while ($prev = $wrapper->getPrevious()) {
diff --git a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
index 42f6dd5..9def2e9 100644
--- a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
@@ -5,10 +5,13 @@
 use Enqueue\Client\ConsumptionExtension\FlushSpoolProducerExtension;
 use Enqueue\Client\SpoolProducer;
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\Message;
 use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
+use Psr\Log\NullLogger;
 
 class FlushSpoolProducerExtensionTest extends TestCase
 {
@@ -44,8 +47,16 @@ public function testShouldFlushSpoolProducerOnPostReceived()
             ->method('flush')
         ;
 
+        $context = new PostMessageReceived(
+            $this->createInteropContextMock(),
+            $this->createMock(Message::class),
+            'aResult',
+            1,
+            new NullLogger()
+        );
+
         $extension = new FlushSpoolProducerExtension($producer);
-        $extension->onPostReceived($this->createContextMock());
+        $extension->onPostMessageReceived($context);
     }
 
     /**
diff --git a/Tests/Consumption/ChainExtensionTest.php b/Tests/Consumption/ChainExtensionTest.php
index dbd5135..f203793 100644
--- a/Tests/Consumption/ChainExtensionTest.php
+++ b/Tests/Consumption/ChainExtensionTest.php
@@ -6,6 +6,7 @@
 use Enqueue\Consumption\Context;
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\Start;
@@ -169,24 +170,30 @@ public function testShouldProxyOnResultToAllInternalExtensions()
 
     public function testShouldProxyOnPostReceiveToAllInternalExtensions()
     {
-        $context = $this->createContextMock();
+        $context = new PostMessageReceived(
+            $this->createInteropContextMock(),
+            $this->createMock(Message::class),
+            'aResult',
+            1,
+            new NullLogger()
+        );
 
         $fooExtension = $this->createExtension();
         $fooExtension
             ->expects($this->once())
-            ->method('onPostReceived')
+            ->method('onPostMessageReceived')
             ->with($this->identicalTo($context))
         ;
         $barExtension = $this->createExtension();
         $barExtension
             ->expects($this->once())
-            ->method('onPostReceived')
+            ->method('onPostMessageReceived')
             ->with($this->identicalTo($context))
         ;
 
         $extensions = new ChainExtension([$fooExtension, $barExtension]);
 
-        $extensions->onPostReceived($context);
+        $extensions->onPostMessageReceived($context);
     }
 
     public function testShouldProxyOnIdleToAllInternalExtensions()
diff --git a/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php b/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
index f0b2990..52099ef 100644
--- a/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
@@ -3,14 +3,17 @@
 namespace Enqueue\Tests\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Extension\LimitConsumedMessagesExtension;
 use Interop\Queue\Consumer;
 use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Message;
 use Interop\Queue\Processor;
 use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
 
 class LimitConsumedMessagesExtensionTest extends TestCase
 {
@@ -47,9 +50,17 @@ public function testOnPreConsumeShouldInterruptWhenLimitIsReached()
         $extension->onPreConsume($context);
         $this->assertFalse($context->isExecutionInterrupted());
 
-        $extension->onPostReceived($this->createContext());
-        $extension->onPostReceived($this->createContext());
-        $extension->onPostReceived($this->createContext());
+        $postReceivedMessage = new PostMessageReceived(
+            $this->createInteropContextMock(),
+            $this->createMock(Message::class),
+            'aResult',
+            1,
+            new NullLogger()
+        );
+
+        $extension->onPostMessageReceived($postReceivedMessage);
+        $extension->onPostMessageReceived($postReceivedMessage);
+        $extension->onPostMessageReceived($postReceivedMessage);
 
         $extension->onPreConsume($context);
         $this->assertTrue($context->isExecutionInterrupted());
@@ -117,27 +128,35 @@ public function testOnPreConsumeShouldInterruptExecutionIfLimitIsLessThatZero()
 
     public function testOnPostReceivedShouldInterruptExecutionIfMessageLimitExceeded()
     {
-        $context = $this->createContext();
-        $context->getLogger()
+        $logger = $this->createLoggerMock();
+        $logger
             ->expects($this->once())
             ->method('debug')
             ->with('[LimitConsumedMessagesExtension] Message consumption is interrupted since'.
                 ' the message limit reached. limit: "2"')
         ;
 
+        $postReceivedMessage = new PostMessageReceived(
+            $this->createInteropContextMock(),
+            $this->createMock(Message::class),
+            'aResult',
+            1,
+            $logger
+        );
+
         // guard
-        $this->assertFalse($context->isExecutionInterrupted());
+        $this->assertFalse($postReceivedMessage->isExecutionInterrupted());
 
         // test
         $extension = new LimitConsumedMessagesExtension(2);
 
         // consume 1
-        $extension->onPostReceived($context);
-        $this->assertFalse($context->isExecutionInterrupted());
+        $extension->onPostMessageReceived($postReceivedMessage);
+        $this->assertFalse($postReceivedMessage->isExecutionInterrupted());
 
         // consume 2 and exit
-        $extension->onPostReceived($context);
-        $this->assertTrue($context->isExecutionInterrupted());
+        $extension->onPostMessageReceived($postReceivedMessage);
+        $this->assertTrue($postReceivedMessage->isExecutionInterrupted());
     }
 
     /**
diff --git a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
index 25fa51c..7d0f01b 100644
--- a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
@@ -3,10 +3,12 @@
 namespace Enqueue\Tests\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Extension\LimitConsumerMemoryExtension;
 use Interop\Queue\Consumer;
 use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Message;
 use Interop\Queue\Processor;
 use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
@@ -48,21 +50,29 @@ public function testOnIdleShouldInterruptExecutionIfMemoryLimitReached()
 
     public function testOnPostReceivedShouldInterruptExecutionIfMemoryLimitReached()
     {
-        $context = $this->createContext();
-        $context->getLogger()
+        $logger = $this->createLoggerMock();
+        $logger
             ->expects($this->once())
             ->method('debug')
             ->with($this->stringContains('[LimitConsumerMemoryExtension] Interrupt execution as memory limit reached.'))
         ;
 
+        $postReceivedMessage = new PostMessageReceived(
+            $this->createInteropContextMock(),
+            $this->createMock(Message::class),
+            'aResult',
+            1,
+            $logger
+        );
+
         // guard
-        $this->assertFalse($context->isExecutionInterrupted());
+        $this->assertFalse($postReceivedMessage->isExecutionInterrupted());
 
         // test
         $extension = new LimitConsumerMemoryExtension(1);
-        $extension->onPostReceived($context);
+        $extension->onPostMessageReceived($postReceivedMessage);
 
-        $this->assertTrue($context->isExecutionInterrupted());
+        $this->assertTrue($postReceivedMessage->isExecutionInterrupted());
     }
 
     public function testOnPreConsumeShouldInterruptExecutionIfMemoryLimitReached()
@@ -128,18 +138,24 @@ public function testOnIdleShouldNotInterruptExecutionIfMemoryLimitIsNotReached()
         $this->assertFalse($context->isExecutionInterrupted());
     }
 
-    public function testOnPostReceivedShouldNotInterruptExecutionIfMemoryLimitIsNotReached()
+    public function testOnPostMessageReceivedShouldNotInterruptExecutionIfMemoryLimitIsNotReached()
     {
-        $context = $this->createContext();
+        $postReceivedMessage = new PostMessageReceived(
+            $this->createInteropContextMock(),
+            $this->createMock(Message::class),
+            'aResult',
+            1,
+            new NullLogger()
+        );
 
         // guard
-        $this->assertFalse($context->isExecutionInterrupted());
+        $this->assertFalse($postReceivedMessage->isExecutionInterrupted());
 
         // test
         $extension = new LimitConsumerMemoryExtension(PHP_INT_MAX);
-        $extension->onPostReceived($context);
+        $extension->onPostMessageReceived($postReceivedMessage);
 
-        $this->assertFalse($context->isExecutionInterrupted());
+        $this->assertFalse($postReceivedMessage->isExecutionInterrupted());
     }
 
     /**
diff --git a/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php b/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
index 0e07c09..5519b3c 100644
--- a/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
@@ -3,10 +3,12 @@
 namespace Enqueue\Tests\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Extension\LimitConsumptionTimeExtension;
 use Interop\Queue\Consumer;
 use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Message;
 use Interop\Queue\Processor;
 use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
@@ -59,17 +61,23 @@ public function testOnIdleShouldInterruptExecutionIfConsumptionTimeExceeded()
 
     public function testOnPostReceivedShouldInterruptExecutionIfConsumptionTimeExceeded()
     {
-        $context = $this->createContext();
+        $postReceivedMessage = new PostMessageReceived(
+            $this->createInteropContextMock(),
+            $this->createMock(Message::class),
+            'aResult',
+            1,
+            new NullLogger()
+        );
 
         // guard
-        $this->assertFalse($context->isExecutionInterrupted());
+        $this->assertFalse($postReceivedMessage->isExecutionInterrupted());
 
         // test
         $extension = new LimitConsumptionTimeExtension(new \DateTime('-2 second'));
 
-        $extension->onPostReceived($context);
+        $extension->onPostMessageReceived($postReceivedMessage);
 
-        $this->assertTrue($context->isExecutionInterrupted());
+        $this->assertTrue($postReceivedMessage->isExecutionInterrupted());
     }
 
     public function testOnPreConsumeShouldNotInterruptExecutionIfConsumptionTimeIsNotExceeded()
@@ -111,17 +119,23 @@ public function testOnIdleShouldNotInterruptExecutionIfConsumptionTimeIsNotExcee
 
     public function testOnPostReceivedShouldNotInterruptExecutionIfConsumptionTimeIsNotExceeded()
     {
-        $context = $this->createContext();
+        $postReceivedMessage = new PostMessageReceived(
+            $this->createInteropContextMock(),
+            $this->createMock(Message::class),
+            'aResult',
+            1,
+            new NullLogger()
+        );
 
         // guard
-        $this->assertFalse($context->isExecutionInterrupted());
+        $this->assertFalse($postReceivedMessage->isExecutionInterrupted());
 
         // test
         $extension = new LimitConsumptionTimeExtension(new \DateTime('+2 second'));
 
-        $extension->onPostReceived($context);
+        $extension->onPostMessageReceived($postReceivedMessage);
 
-        $this->assertFalse($context->isExecutionInterrupted());
+        $this->assertFalse($postReceivedMessage->isExecutionInterrupted());
     }
 
     /**
diff --git a/Tests/Consumption/Extension/LoggerExtensionTest.php b/Tests/Consumption/Extension/LoggerExtensionTest.php
index 8131410..94cba2e 100644
--- a/Tests/Consumption/Extension/LoggerExtensionTest.php
+++ b/Tests/Consumption/Extension/LoggerExtensionTest.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Tests\Consumption\Extension;
 
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\Extension\LoggerExtension;
 use Enqueue\Consumption\ExtensionInterface;
@@ -11,6 +11,7 @@
 use Enqueue\Test\ClassExtensionTrait;
 use Interop\Queue\Consumer;
 use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Message;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
@@ -72,11 +73,15 @@ public function testShouldLogRejectMessageStatus()
         $message = new NullMessage();
         $message->setBody('message body');
 
-        $context = new Context($this->createContextMock());
-        $context->setResult(Result::reject('reason'));
-        $context->setInteropMessage($message);
+        $postReceivedMessage = new PostMessageReceived(
+            $this->createContextMock(),
+            $message,
+            Result::reject('reason'),
+            1,
+            $logger
+        );
 
-        $extension->onPostReceived($context);
+        $extension->onPostMessageReceived($postReceivedMessage);
     }
 
     public function testShouldLogRequeueMessageStatus()
@@ -93,11 +98,15 @@ public function testShouldLogRequeueMessageStatus()
         $message = new NullMessage();
         $message->setBody('message body');
 
-        $context = new Context($this->createContextMock());
-        $context->setResult(Result::requeue('reason'));
-        $context->setInteropMessage($message);
+        $postReceivedMessage = new PostMessageReceived(
+            $this->createContextMock(),
+            $message,
+            Result::requeue('reason'),
+            1,
+            $logger
+        );
 
-        $extension->onPostReceived($context);
+        $extension->onPostMessageReceived($postReceivedMessage);
     }
 
     public function testShouldNotLogRequeueMessageStatusIfReasonIsEmpty()
@@ -110,10 +119,15 @@ public function testShouldNotLogRequeueMessageStatusIfReasonIsEmpty()
 
         $extension = new LoggerExtension($logger);
 
-        $context = new Context($this->createContextMock());
-        $context->setResult(Result::requeue());
+        $postReceivedMessage = new PostMessageReceived(
+            $this->createContextMock(),
+            $this->createMock(Message::class),
+            Result::requeue(),
+            1,
+            $logger
+        );
 
-        $extension->onPostReceived($context);
+        $extension->onPostMessageReceived($postReceivedMessage);
     }
 
     public function testShouldLogAckMessageStatus()
@@ -130,11 +144,15 @@ public function testShouldLogAckMessageStatus()
         $message = new NullMessage();
         $message->setBody('message body');
 
-        $context = new Context($this->createContextMock());
-        $context->setResult(Result::ack('reason'));
-        $context->setInteropMessage($message);
+        $postReceivedMessage = new PostMessageReceived(
+            $this->createContextMock(),
+            $message,
+            Result::ack('reason'),
+            1,
+            $logger
+        );
 
-        $extension->onPostReceived($context);
+        $extension->onPostMessageReceived($postReceivedMessage);
     }
 
     public function testShouldNotLogAckMessageStatusIfReasonIsEmpty()
@@ -147,10 +165,15 @@ public function testShouldNotLogAckMessageStatusIfReasonIsEmpty()
 
         $extension = new LoggerExtension($logger);
 
-        $context = new Context($this->createContextMock());
-        $context->setResult(Result::ack());
+        $postReceivedMessage = new PostMessageReceived(
+            $this->createContextMock(),
+            $this->createMock(Message::class),
+            Result::ack(),
+            1,
+            $logger
+        );
 
-        $extension->onPostReceived($context);
+        $extension->onPostMessageReceived($postReceivedMessage);
     }
 
     public function testShouldNotSetLoggerIfOneHasBeenSetOnStart()
diff --git a/Tests/Consumption/Extension/ReplyExtensionTest.php b/Tests/Consumption/Extension/ReplyExtensionTest.php
index b34427a..c027c4b 100644
--- a/Tests/Consumption/Extension/ReplyExtensionTest.php
+++ b/Tests/Consumption/Extension/ReplyExtensionTest.php
@@ -2,16 +2,15 @@
 
 namespace Enqueue\Tests\Consumption\Extension;
 
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Extension\ReplyExtension;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Consumption\Result;
 use Enqueue\Null\NullMessage;
 use Enqueue\Null\NullQueue;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Context;
 use Interop\Queue\Producer as InteropProducer;
-use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -33,10 +32,15 @@ public function testShouldDoNothingIfReceivedMessageNotHaveReplyToSet()
     {
         $extension = new ReplyExtension();
 
-        $context = new Context($this->createNeverUsedContextMock());
-        $context->setInteropMessage(new NullMessage());
+        $postReceivedMessage = new PostMessageReceived(
+            $this->createNeverUsedContextMock(),
+            new NullMessage(),
+            'aResult',
+            1,
+            new NullLogger()
+        );
 
-        $extension->onPostReceived($context);
+        $extension->onPostMessageReceived($postReceivedMessage);
     }
 
     public function testShouldDoNothingIfContextResultIsNotInstanceOfResult()
@@ -46,11 +50,15 @@ public function testShouldDoNothingIfContextResultIsNotInstanceOfResult()
         $message = new NullMessage();
         $message->setReplyTo('aReplyToQueue');
 
-        $context = new Context($this->createNeverUsedContextMock());
-        $context->setInteropMessage($message);
-        $context->setResult('notInstanceOfResult');
+        $postReceivedMessage = new PostMessageReceived(
+            $this->createNeverUsedContextMock(),
+            $message,
+            'notInstanceOfResult',
+            1,
+            new NullLogger()
+        );
 
-        $extension->onPostReceived($context);
+        $extension->onPostMessageReceived($postReceivedMessage);
     }
 
     public function testShouldDoNothingIfResultInstanceOfResultButReplyMessageNotSet()
@@ -60,11 +68,15 @@ public function testShouldDoNothingIfResultInstanceOfResultButReplyMessageNotSet
         $message = new NullMessage();
         $message->setReplyTo('aReplyToQueue');
 
-        $context = new Context($this->createNeverUsedContextMock());
-        $context->setInteropMessage($message);
-        $context->setResult(Result::ack());
+        $postReceivedMessage = new PostMessageReceived(
+            $this->createNeverUsedContextMock(),
+            $message,
+            Result::ack(),
+            1,
+            new NullLogger()
+        );
 
-        $extension->onPostReceived($context);
+        $extension->onPostMessageReceived($postReceivedMessage);
     }
 
     public function testShouldSendReplyMessageToReplyQueueOnPostReceived()
@@ -87,8 +99,8 @@ public function testShouldSendReplyMessageToReplyQueueOnPostReceived()
             ->with($replyQueue, $replyMessage)
         ;
 
-        /** @var \PHPUnit_Framework_MockObject_MockObject|InteropContext $contextMock */
-        $contextMock = $this->createMock(InteropContext::class);
+        /** @var \PHPUnit_Framework_MockObject_MockObject|Context $contextMock */
+        $contextMock = $this->createMock(Context::class);
         $contextMock
             ->expects($this->once())
             ->method('createQueue')
@@ -100,36 +112,31 @@ public function testShouldSendReplyMessageToReplyQueueOnPostReceived()
             ->willReturn($producerMock)
         ;
 
-        $context = new Context($contextMock);
-        $context->setInteropMessage($message);
-        $context->setResult(Result::reply($replyMessage));
-        $context->setLogger(new NullLogger());
+        $postReceivedMessage = new PostMessageReceived(
+            $contextMock,
+            $message,
+            Result::reply($replyMessage),
+            1,
+            new NullLogger()
+        );
 
-        $extension->onPostReceived($context);
+        $extension->onPostMessageReceived($postReceivedMessage);
     }
 
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    protected function createInteropContextMock(): \Interop\Queue\Context
+    protected function createInteropContextMock(): Context
     {
-        return $this->createMock(\Interop\Queue\Context::class);
+        return $this->createMock(Context::class);
     }
 
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    private function createSubscriptionConsumerMock(): SubscriptionConsumer
+    private function createNeverUsedContextMock(): Context
     {
-        return $this->createMock(SubscriptionConsumer::class);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
-     */
-    private function createNeverUsedContextMock(): InteropContext
-    {
-        $contextMock = $this->createMock(InteropContext::class);
+        $contextMock = $this->createMock(Context::class);
         $contextMock
             ->expects($this->never())
             ->method('createProducer')
diff --git a/Tests/Consumption/Mock/BreakCycleExtension.php b/Tests/Consumption/Mock/BreakCycleExtension.php
index b61d8dd..63de3e1 100644
--- a/Tests/Consumption/Mock/BreakCycleExtension.php
+++ b/Tests/Consumption/Mock/BreakCycleExtension.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Tests\Consumption\Mock;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface;
 
@@ -19,9 +20,13 @@ public function __construct($limit)
         $this->limit = $limit;
     }
 
-    public function onPostReceived(Context $context)
+    public function onPostMessageReceived(PostMessageReceived $context): void
     {
-        $this->onIdle($context);
+        if ($this->cycles >= $this->limit) {
+            $context->interruptExecution();
+        } else {
+            ++$this->cycles;
+        }
     }
 
     public function onIdle(Context $context)
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index b540ddc..40417dd 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -8,6 +8,7 @@
 use Enqueue\Consumption\Context;
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
+use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\ProcessorException;
@@ -966,7 +967,7 @@ public function testShouldContinueConsumptionIfResultSetOnProcessorExceptionExte
         $queueConsumer->consume();
     }
 
-    public function testShouldCallOnPostReceivedExtensionMethodWithExpectedContext()
+    public function testShouldCallOnPostMessageReceivedExtensionMethodWithExpectedContext()
     {
         $expectedMessage = $this->createMessageMock();
 
@@ -982,20 +983,15 @@ public function testShouldCallOnPostReceivedExtensionMethodWithExpectedContext()
         $extension = $this->createExtension();
         $extension
             ->expects($this->once())
-            ->method('onPostReceived')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use (
+            ->method('onPostMessageReceived')
+            ->with($this->isInstanceOf(PostMessageReceived::class))
+            ->willReturnCallback(function (PostMessageReceived $context) use (
                 $contextStub,
-                $consumerStub,
-                $processorMock,
                 $expectedMessage
             ) {
-                $this->assertSame($contextStub, $context->getInteropContext());
-                $this->assertSame($consumerStub, $context->getConsumer());
-                $this->assertSame($processorMock, $context->getProcessor());
-                $this->assertSame($expectedMessage, $context->getInteropMessage());
+                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($expectedMessage, $context->getMessage());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
-                $this->assertNull($context->getException());
                 $this->assertSame(Result::ACK, $context->getResult());
                 $this->assertFalse($context->isExecutionInterrupted());
             })
@@ -1161,7 +1157,7 @@ public function testShouldSetMainExceptionAsPreviousToExceptionThrownOnInterrupt
         $this->fail('Exception throw is expected.');
     }
 
-    public function testShouldAllowInterruptConsumingOnPostReceive()
+    public function testShouldAllowInterruptConsumingOnPostMessageReceived()
     {
         $expectedMessage = $this->createMessageMock();
 
@@ -1182,10 +1178,10 @@ public function testShouldAllowInterruptConsumingOnPostReceive()
         $extension = $this->createExtension();
         $extension
             ->expects($this->once())
-            ->method('onPostReceived')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) {
-                $context->setExecutionInterrupted(true);
+            ->method('onPostMessageReceived')
+            ->with($this->isInstanceOf(PostMessageReceived::class))
+            ->willReturnCallback(function (PostMessageReceived $context) {
+                $context->interruptExecution(true);
             })
         ;
         $extension
@@ -1315,8 +1311,8 @@ public function testShouldCallExtensionPassedOnRuntime()
         ;
         $runtimeExtension
             ->expects($this->once())
-            ->method('onPostReceived')
-            ->with($this->isInstanceOf(Context::class))
+            ->method('onPostMessageReceived')
+            ->with($this->isInstanceOf(PostMessageReceived::class))
         ;
 
         $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1));

From 86e15bedc29ef609c10f2fe682e927dc523e2c9d Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 8 Oct 2018 11:40:10 +0300
Subject: [PATCH 155/337] remove idle_time.

---
 Consumption/Context/Start.php                 | 24 +------------------
 Consumption/QueueConsumer.php                 | 21 ----------------
 Consumption/QueueConsumerInterface.php        | 10 --------
 .../QueueConsumerOptionsCommandTrait.php      |  5 ----
 .../DependencyInjection/TransportFactory.php  |  7 ------
 Tests/Consumption/QueueConsumerTest.php       | 23 ++++++------------
 Tests/Symfony/Client/ConsumeCommandTest.php   |  3 +--
 .../ConfigurableConsumeCommandTest.php        |  3 +--
 .../Consumption/ConsumeCommandTest.php        |  3 +--
 .../QueueConsumerOptionsCommandTraitTest.php  |  9 +------
 .../TransportFactoryTest.php                  |  4 ----
 11 files changed, 12 insertions(+), 100 deletions(-)

diff --git a/Consumption/Context/Start.php b/Consumption/Context/Start.php
index f3cd292..d1a7be3 100644
--- a/Consumption/Context/Start.php
+++ b/Consumption/Context/Start.php
@@ -28,11 +28,6 @@ final class Start
      */
     private $receiveTimeout;
 
-    /**
-     * @var int
-     */
-    private $idleTime;
-
     /**
      * @var int
      */
@@ -46,13 +41,12 @@ final class Start
     /**
      * @param BoundProcessor[] $processors
      */
-    public function __construct(Context $context, LoggerInterface $logger, array $processors, int $receiveTimeout, int $idleTime, int $startTime)
+    public function __construct(Context $context, LoggerInterface $logger, array $processors, int $receiveTimeout, int $startTime)
     {
         $this->context = $context;
         $this->logger = $logger;
         $this->processors = $processors;
         $this->receiveTimeout = $receiveTimeout;
-        $this->idleTime = $idleTime;
         $this->startTime = $startTime;
 
         $this->executionInterrupted = false;
@@ -89,22 +83,6 @@ public function changeReceiveTimeout(int $timeout): void
         $this->receiveTimeout = $timeout;
     }
 
-    /**
-     * In milliseconds.
-     */
-    public function getIdleTime(): int
-    {
-        return $this->idleTime;
-    }
-
-    /**
-     * In milliseconds.
-     */
-    public function changeIdleTime(int $time): void
-    {
-        $this->idleTime = $time;
-    }
-
     /**
      * In milliseconds.
      */
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 04adbcb..4b41491 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -40,11 +40,6 @@ final class QueueConsumer implements QueueConsumerInterface
      */
     private $boundProcessors;
 
-    /**
-     * @var int|float in milliseconds
-     */
-    private $idleTime;
-
     /**
      * @var int|float in milliseconds
      */
@@ -67,7 +62,6 @@ final class QueueConsumer implements QueueConsumerInterface
 
     /**
      * @param BoundProcessor[] $boundProcessors
-     * @param int|float        $idleTime        the time in milliseconds queue consumer waits if no message received
      * @param int|float        $receiveTimeout  the time in milliseconds queue consumer waits for a message (10 ms by default)
      */
     public function __construct(
@@ -75,11 +69,9 @@ public function __construct(
         ExtensionInterface $extension = null,
         array $boundProcessors = [],
         LoggerInterface $logger = null,
-        int $idleTime = 0,
         int $receiveTimeout = 10000
     ) {
         $this->interopContext = $interopContext;
-        $this->idleTime = $idleTime;
         $this->receiveTimeout = $receiveTimeout;
 
         $this->staticExtension = $extension ?: new ChainExtension([]);
@@ -93,16 +85,6 @@ public function __construct(
         $this->fallbackSubscriptionConsumer = new FallbackSubscriptionConsumer();
     }
 
-    public function setIdleTime(int $time): void
-    {
-        $this->idleTime = $time;
-    }
-
-    public function getIdleTime(): int
-    {
-        return $this->idleTime;
-    }
-
     public function setReceiveTimeout(int $timeout): void
     {
         $this->receiveTimeout = $timeout;
@@ -169,7 +151,6 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
             $this->logger,
             $this->boundProcessors,
             $this->receiveTimeout,
-            $this->idleTime,
             $startTime
         );
 
@@ -180,7 +161,6 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
         }
 
         $this->logger = $start->getLogger();
-        $this->idleTime = $start->getIdleTime();
         $this->receiveTimeout = $start->getReceiveTimeout();
         $this->boundProcessors = $start->getBoundProcessors();
 
@@ -320,7 +300,6 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
 
                 $subscriptionConsumer->consume($this->receiveTimeout);
 
-                $this->idleTime && usleep($this->idleTime * 1000);
                 $this->extension->onIdle($context);
 
                 if ($context->isExecutionInterrupted()) {
diff --git a/Consumption/QueueConsumerInterface.php b/Consumption/QueueConsumerInterface.php
index e561cdd..ed22938 100644
--- a/Consumption/QueueConsumerInterface.php
+++ b/Consumption/QueueConsumerInterface.php
@@ -8,16 +8,6 @@
 
 interface QueueConsumerInterface
 {
-    /**
-     * In milliseconds.
-     */
-    public function setIdleTime(int $time): void;
-
-    /**
-     * In milliseconds.
-     */
-    public function getIdleTime(): int;
-
     /**
      * In milliseconds.
      */
diff --git a/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php b/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php
index 61d1302..9df852d 100644
--- a/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php
+++ b/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php
@@ -14,7 +14,6 @@ trait QueueConsumerOptionsCommandTrait
     protected function configureQueueConsumerOptions()
     {
         $this
-            ->addOption('idle-time', null, InputOption::VALUE_REQUIRED, 'The time in milliseconds queue consumer idle if no message has been received.')
             ->addOption('receive-timeout', null, InputOption::VALUE_REQUIRED, 'The time in milliseconds queue consumer waits for a message.')
         ;
     }
@@ -25,10 +24,6 @@ protected function configureQueueConsumerOptions()
      */
     protected function setQueueConsumerOptions(QueueConsumerInterface $consumer, InputInterface $input)
     {
-        if (null !== $idleTimeout = $input->getOption('idle-time')) {
-            $consumer->setIdleTime((int) $idleTimeout);
-        }
-
         if (null !== $receiveTimeout = $input->getOption('receive-timeout')) {
             $consumer->setReceiveTimeout((int) $receiveTimeout);
         }
diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index f9b99bf..b60420c 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -98,11 +98,6 @@ public function addQueueConsumerConfiguration(ArrayNodeDefinition $builder): voi
     {
         $builder
             ->addDefaultsIfNotSet()->children()
-                ->integerNode('idle_time')
-                    ->min(0)
-                    ->defaultValue(0)
-                    ->info('the time in milliseconds queue consumer waits if no message received')
-                ->end()
                 ->integerNode('receive_timeout')
                     ->min(0)
                     ->defaultValue(10000)
@@ -169,7 +164,6 @@ public function buildQueueConsumer(ContainerBuilder $container, array $config):
         $contextId = $this->format('context');
         $this->assertServiceExists($container, $contextId);
 
-        $container->setParameter($this->format('idle_time'), $config['idle_time'] ?? 0);
         $container->setParameter($this->format('receive_timeout'), $config['receive_timeout'] ?? 10000);
 
         $container->register($this->format('consumption_extensions'), ChainExtension::class)
@@ -181,7 +175,6 @@ public function buildQueueConsumer(ContainerBuilder $container, array $config):
             ->addArgument(new Reference($this->format('consumption_extensions')))
             ->addArgument([])
             ->addArgument(null)
-            ->addArgument($this->format('idle_time', true))
             ->addArgument($this->format('receive_timeout', true))
         ;
 
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 40417dd..ad64a0e 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -35,7 +35,7 @@ class QueueConsumerTest extends TestCase
 {
     public function testCouldBeConstructedWithAllArguments()
     {
-        new QueueConsumer($this->createContextStub(), null, [], null, 0, 0);
+        new QueueConsumer($this->createContextStub(), null, [], null, 0);
     }
 
     public function testCouldBeConstructedWithContextOnly()
@@ -50,7 +50,7 @@ public function testCouldBeConstructedWithContextAndSingleExtension()
 
     public function testShouldSetEmptyArrayToBoundProcessorsPropertyInConstructor()
     {
-        $consumer = new QueueConsumer($this->createContextStub(), null, [], null, 0, 0);
+        $consumer = new QueueConsumer($this->createContextStub(), null, [], null, 0);
 
         $this->assertAttributeSame([], 'boundProcessors', $consumer);
     }
@@ -62,14 +62,14 @@ public function testShouldSetProvidedBoundProcessorsToThePropertyInConstructor()
             new BoundProcessor(new NullQueue('bar'), $this->createProcessorMock()),
         ];
 
-        $consumer = new QueueConsumer($this->createContextStub(), null, $boundProcessors, null, 0, 0);
+        $consumer = new QueueConsumer($this->createContextStub(), null, $boundProcessors, null, 0);
 
         $this->assertAttributeSame($boundProcessors, 'boundProcessors', $consumer);
     }
 
     public function testShouldSetNullLoggerIfNoneProvidedInConstructor()
     {
-        $consumer = new QueueConsumer($this->createContextStub(), null, [], null, 0, 0);
+        $consumer = new QueueConsumer($this->createContextStub(), null, [], null, 0);
 
         $this->assertAttributeInstanceOf(NullLogger::class, 'logger', $consumer);
     }
@@ -78,7 +78,7 @@ public function testShouldSetProvidedLoggerToThePropertyInConstructor()
     {
         $expectedLogger = $this->createMock(LoggerInterface::class);
 
-        $consumer = new QueueConsumer($this->createContextStub(), null, [], $expectedLogger, 0, 0);
+        $consumer = new QueueConsumer($this->createContextStub(), null, [], $expectedLogger, 0);
 
         $this->assertAttributeSame($expectedLogger, 'logger', $consumer);
     }
@@ -87,7 +87,7 @@ public function testShouldAllowGetContextSetInConstructor()
     {
         $expectedContext = $this->createContextStub();
 
-        $consumer = new QueueConsumer($expectedContext, null, [], null, 0, 0);
+        $consumer = new QueueConsumer($expectedContext, null, [], null, 0);
 
         $this->assertSame($expectedContext, $consumer->getContext());
     }
@@ -143,15 +143,6 @@ public function testThrowIfQueueNeitherInstanceOfQueueNorString()
         $consumer->bind(new \stdClass(), $processorMock);
     }
 
-    public function testCouldSetGetIdleTime()
-    {
-        $consumer = new QueueConsumer($this->createContextStub());
-
-        $consumer->setIdleTime(123456);
-
-        $this->assertSame(123456, $consumer->getIdleTime());
-    }
-
     public function testCouldSetGetReceiveTimeout()
     {
         $consumer = new QueueConsumer($this->createContextStub());
@@ -311,7 +302,7 @@ public function testShouldSubscribeToGivenQueueWithExpectedTimeout()
             ->method('process')
         ;
 
-        $queueConsumer = new QueueConsumer($contextMock, new BreakCycleExtension(1), [], null, 0, 12345);
+        $queueConsumer = new QueueConsumer($contextMock, new BreakCycleExtension(1), [], null, 12345);
         $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
         $queueConsumer->bind($expectedQueue, $processorMock);
         $queueConsumer->consume();
diff --git a/Tests/Symfony/Client/ConsumeCommandTest.php b/Tests/Symfony/Client/ConsumeCommandTest.php
index a600efa..e57696a 100644
--- a/Tests/Symfony/Client/ConsumeCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeCommandTest.php
@@ -50,11 +50,10 @@ public function testShouldHaveExpectedOptions()
 
         $options = $command->getDefinition()->getOptions();
 
-        $this->assertCount(10, $options);
+        $this->assertCount(9, $options);
         $this->assertArrayHasKey('memory-limit', $options);
         $this->assertArrayHasKey('message-limit', $options);
         $this->assertArrayHasKey('time-limit', $options);
-        $this->assertArrayHasKey('idle-time', $options);
         $this->assertArrayHasKey('receive-timeout', $options);
         $this->assertArrayHasKey('niceness', $options);
         $this->assertArrayHasKey('client', $options);
diff --git a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
index ae6c4be..b402960 100644
--- a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
@@ -50,11 +50,10 @@ public function testShouldHaveExpectedOptions()
 
         $options = $command->getDefinition()->getOptions();
 
-        $this->assertCount(8, $options);
+        $this->assertCount(7, $options);
         $this->assertArrayHasKey('memory-limit', $options);
         $this->assertArrayHasKey('message-limit', $options);
         $this->assertArrayHasKey('time-limit', $options);
-        $this->assertArrayHasKey('idle-time', $options);
         $this->assertArrayHasKey('receive-timeout', $options);
         $this->assertArrayHasKey('niceness', $options);
         $this->assertArrayHasKey('transport', $options);
diff --git a/Tests/Symfony/Consumption/ConsumeCommandTest.php b/Tests/Symfony/Consumption/ConsumeCommandTest.php
index fe38c6c..a8ead2d 100644
--- a/Tests/Symfony/Consumption/ConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConsumeCommandTest.php
@@ -44,11 +44,10 @@ public function testShouldHaveExpectedOptions()
 
         $options = $command->getDefinition()->getOptions();
 
-        $this->assertCount(8, $options);
+        $this->assertCount(7, $options);
         $this->assertArrayHasKey('memory-limit', $options);
         $this->assertArrayHasKey('message-limit', $options);
         $this->assertArrayHasKey('time-limit', $options);
-        $this->assertArrayHasKey('idle-time', $options);
         $this->assertArrayHasKey('receive-timeout', $options);
         $this->assertArrayHasKey('niceness', $options);
         $this->assertArrayHasKey('transport', $options);
diff --git a/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php b/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php
index befee29..c221f02 100644
--- a/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php
+++ b/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php
@@ -15,19 +15,13 @@ public function testShouldAddExtensionsOptions()
 
         $options = $trait->getDefinition()->getOptions();
 
-        $this->assertCount(2, $options);
-        $this->assertArrayHasKey('idle-time', $options);
+        $this->assertCount(1, $options);
         $this->assertArrayHasKey('receive-timeout', $options);
     }
 
     public function testShouldSetQueueConsumerOptions()
     {
         $consumer = $this->createQueueConsumer();
-        $consumer
-            ->expects($this->once())
-            ->method('setIdleTime')
-            ->with($this->identicalTo(123))
-        ;
         $consumer
             ->expects($this->once())
             ->method('setReceiveTimeout')
@@ -38,7 +32,6 @@ public function testShouldSetQueueConsumerOptions()
 
         $tester = new CommandTester($trait);
         $tester->execute([
-            '--idle-time' => '123',
             '--receive-timeout' => '456',
         ]);
     }
diff --git a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index e235425..5da72c7 100644
--- a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -376,7 +376,6 @@ public function testShouldBuildQueueConsumerWithDefaultOptions()
 
         $transport->buildQueueConsumer($container, []);
 
-        $this->assertSame(0, $container->getParameter('enqueue.transport.default.idle_time'));
         $this->assertSame(10000, $container->getParameter('enqueue.transport.default.receive_timeout'));
 
         $this->assertTrue($container->hasDefinition('enqueue.transport.default.consumption_extensions'));
@@ -390,7 +389,6 @@ public function testShouldBuildQueueConsumerWithDefaultOptions()
             new Reference('enqueue.transport.default.consumption_extensions'),
             [],
             null,
-            '%enqueue.transport.default.idle_time%',
             '%enqueue.transport.default.receive_timeout%',
         ], $container->getDefinition('enqueue.transport.default.queue_consumer')->getArguments());
     }
@@ -403,11 +401,9 @@ public function testShouldBuildQueueConsumerWithCustomOptions()
         $transport = new TransportFactory('default');
 
         $transport->buildQueueConsumer($container, [
-            'idle_time' => 123,
             'receive_timeout' => 567,
         ]);
 
-        $this->assertSame(123, $container->getParameter('enqueue.transport.default.idle_time'));
         $this->assertSame(567, $container->getParameter('enqueue.transport.default.receive_timeout'));
     }
 

From 646e4cc435ca94e8c8ab23f1ba5d951e25dc0055 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 8 Oct 2018 12:20:15 +0300
Subject: [PATCH 156/337] [consumption] onIdle -> onPostConsume

---
 Consumption/ChainExtension.php                |  5 +-
 Consumption/Context/PostConsume.php           | 97 +++++++++++++++++++
 Consumption/EmptyExtensionTrait.php           |  3 +-
 .../LimitConsumerMemoryExtension.php          |  6 +-
 .../LimitConsumptionTimeExtension.php         |  6 +-
 Consumption/Extension/SignalExtension.php     |  6 +-
 Consumption/ExtensionInterface.php            |  9 +-
 Consumption/PostConsumeExtensionInterface.php | 14 +++
 Consumption/QueueConsumer.php                 | 14 ++-
 Tests/Consumption/ChainExtensionTest.php      | 24 +++--
 .../LimitConsumerMemoryExtensionTest.php      | 39 +++++---
 .../LimitConsumptionTimeExtensionTest.php     | 35 +++++--
 .../Consumption/Mock/BreakCycleExtension.php  |  6 +-
 Tests/Consumption/QueueConsumerTest.php       | 66 ++++---------
 14 files changed, 228 insertions(+), 102 deletions(-)
 create mode 100644 Consumption/Context/PostConsume.php
 create mode 100644 Consumption/PostConsumeExtensionInterface.php

diff --git a/Consumption/ChainExtension.php b/Consumption/ChainExtension.php
index 8db839e..86f1e36 100644
--- a/Consumption/ChainExtension.php
+++ b/Consumption/ChainExtension.php
@@ -4,6 +4,7 @@
 
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
+use Enqueue\Consumption\Context\PostConsume;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
@@ -79,10 +80,10 @@ public function onPostMessageReceived(PostMessageReceived $context): void
         }
     }
 
-    public function onIdle(Context $context)
+    public function onPostConsume(PostConsume $context): void
     {
         foreach ($this->extensions as $extension) {
-            $extension->onIdle($context);
+            $extension->onPostConsume($context);
         }
     }
 
diff --git a/Consumption/Context/PostConsume.php b/Consumption/Context/PostConsume.php
new file mode 100644
index 0000000..0add003
--- /dev/null
+++ b/Consumption/Context/PostConsume.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace Enqueue\Consumption\Context;
+
+use Interop\Queue\Context;
+use Interop\Queue\SubscriptionConsumer;
+use Psr\Log\LoggerInterface;
+
+final class PostConsume
+{
+    /**
+     * @var Context
+     */
+    private $context;
+
+    /**
+     * @var SubscriptionConsumer
+     */
+    private $subscriptionConsumer;
+
+    /**
+     * @var int
+     */
+    private $receivedMessagesCount;
+
+    /**
+     * @var int
+     */
+    private $cycle;
+
+    /**
+     * @var int
+     */
+    private $startTime;
+
+    /**
+     * @var LoggerInterface
+     */
+    private $logger;
+
+    /**
+     * @var bool
+     */
+    private $executionInterrupted;
+
+    public function __construct(Context $context, SubscriptionConsumer $subscriptionConsumer, int $receivedMessagesCount, int $cycle, int $startTime, LoggerInterface $logger)
+    {
+        $this->context = $context;
+        $this->subscriptionConsumer = $subscriptionConsumer;
+        $this->receivedMessagesCount = $receivedMessagesCount;
+        $this->cycle = $cycle;
+        $this->startTime = $startTime;
+        $this->logger = $logger;
+
+        $this->executionInterrupted = false;
+    }
+
+    public function getContext(): Context
+    {
+        return $this->context;
+    }
+
+    public function getSubscriptionConsumer(): SubscriptionConsumer
+    {
+        return $this->subscriptionConsumer;
+    }
+
+    public function getReceivedMessagesCount(): int
+    {
+        return $this->receivedMessagesCount;
+    }
+
+    public function getCycle(): int
+    {
+        return $this->cycle;
+    }
+
+    public function getStartTime(): int
+    {
+        return $this->startTime;
+    }
+
+    public function getLogger(): LoggerInterface
+    {
+        return $this->logger;
+    }
+
+    public function isExecutionInterrupted(): bool
+    {
+        return $this->executionInterrupted;
+    }
+
+    public function interruptExecution(): void
+    {
+        $this->executionInterrupted = true;
+    }
+}
diff --git a/Consumption/EmptyExtensionTrait.php b/Consumption/EmptyExtensionTrait.php
index 39b381e..fec117d 100644
--- a/Consumption/EmptyExtensionTrait.php
+++ b/Consumption/EmptyExtensionTrait.php
@@ -4,6 +4,7 @@
 
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
+use Enqueue\Consumption\Context\PostConsume;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
@@ -40,7 +41,7 @@ public function onProcessorException(ProcessorException $context): void
     {
     }
 
-    public function onIdle(Context $context)
+    public function onPostConsume(PostConsume $context): void
     {
     }
 
diff --git a/Consumption/Extension/LimitConsumerMemoryExtension.php b/Consumption/Extension/LimitConsumerMemoryExtension.php
index a7d10a4..fabdfcc 100644
--- a/Consumption/Extension/LimitConsumerMemoryExtension.php
+++ b/Consumption/Extension/LimitConsumerMemoryExtension.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Consumption\Extension;
 
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PostConsume;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\EmptyExtensionTrait;
@@ -47,10 +47,10 @@ public function onPostMessageReceived(PostMessageReceived $context): void
         }
     }
 
-    public function onIdle(Context $context)
+    public function onPostConsume(PostConsume $context): void
     {
         if ($this->shouldBeStopped($context->getLogger())) {
-            $context->setExecutionInterrupted(true);
+            $context->interruptExecution();
         }
     }
 
diff --git a/Consumption/Extension/LimitConsumptionTimeExtension.php b/Consumption/Extension/LimitConsumptionTimeExtension.php
index 8b621df..e360e31 100644
--- a/Consumption/Extension/LimitConsumptionTimeExtension.php
+++ b/Consumption/Extension/LimitConsumptionTimeExtension.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Consumption\Extension;
 
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PostConsume;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\EmptyExtensionTrait;
@@ -33,10 +33,10 @@ public function onPreConsume(PreConsume $context): void
         }
     }
 
-    public function onIdle(Context $context)
+    public function onPostConsume(PostConsume $context): void
     {
         if ($this->shouldBeStopped($context->getLogger())) {
-            $context->setExecutionInterrupted(true);
+            $context->interruptExecution();
         }
     }
 
diff --git a/Consumption/Extension/SignalExtension.php b/Consumption/Extension/SignalExtension.php
index 75ebc01..3abfb3c 100644
--- a/Consumption/Extension/SignalExtension.php
+++ b/Consumption/Extension/SignalExtension.php
@@ -2,8 +2,8 @@
 
 namespace Enqueue\Consumption\Extension;
 
-use Enqueue\Consumption\Context;
 use Enqueue\Consumption\Context\MessageReceived;
+use Enqueue\Consumption\Context\PostConsume;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\Start;
@@ -62,10 +62,10 @@ public function onPostMessageReceived(PostMessageReceived $context): void
         }
     }
 
-    public function onIdle(Context $context)
+    public function onPostConsume(PostConsume $context): void
     {
         if ($this->shouldBeStopped($context->getLogger())) {
-            $context->setExecutionInterrupted(true);
+            $context->interruptExecution();
         }
     }
 
diff --git a/Consumption/ExtensionInterface.php b/Consumption/ExtensionInterface.php
index 3f57652..d4c3961 100644
--- a/Consumption/ExtensionInterface.php
+++ b/Consumption/ExtensionInterface.php
@@ -2,15 +2,8 @@
 
 namespace Enqueue\Consumption;
 
-interface ExtensionInterface extends StartExtensionInterface, PreSubscribeExtensionInterface, PreConsumeExtensionInterface, MessageReceivedExtensionInterface, PostMessageReceivedExtensionInterface, MessageResultExtensionInterface, ProcessorExceptionExtensionInterface
+interface ExtensionInterface extends StartExtensionInterface, PreSubscribeExtensionInterface, PreConsumeExtensionInterface, MessageReceivedExtensionInterface, PostMessageReceivedExtensionInterface, MessageResultExtensionInterface, ProcessorExceptionExtensionInterface, PostConsumeExtensionInterface
 {
-    /**
-     * Called each time at the end of the cycle if nothing was done.
-     *
-     * @param Context $context
-     */
-    public function onIdle(Context $context);
-
     /**
      * Called when the consumption was interrupted by an extension or exception
      * In case of exception it will be present in the context.
diff --git a/Consumption/PostConsumeExtensionInterface.php b/Consumption/PostConsumeExtensionInterface.php
new file mode 100644
index 0000000..a8706f6
--- /dev/null
+++ b/Consumption/PostConsumeExtensionInterface.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace Enqueue\Consumption;
+
+use Enqueue\Consumption\Context\PostConsume;
+
+interface PostConsumeExtensionInterface
+{
+    /**
+     * The method is called after SubscriptionConsumer::consume method exits.
+     * The consumption could be interrupted at this point.
+     */
+    public function onPostConsume(PostConsume $context): void;
+}
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 4b41491..47fe4d0 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -4,6 +4,7 @@
 
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
+use Enqueue\Consumption\Context\PostConsume;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
@@ -196,7 +197,11 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
             $subscriptionConsumer = $this->fallbackSubscriptionConsumer;
         }
 
-        $callback = function (InteropMessage $message, Consumer $consumer) use (&$context) {
+        $receivedMessagesCount = 0;
+
+        $callback = function (InteropMessage $message, Consumer $consumer) use (&$context, &$receivedMessagesCount) {
+            ++$receivedMessagesCount;
+
             $receivedAt = (int) (microtime(true) * 1000);
             $queue = $consumer->getQueue();
             if (false == array_key_exists($queue->getQueueName(), $this->boundProcessors)) {
@@ -291,6 +296,8 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
         $cycle = 1;
         while (true) {
             try {
+                $receivedMessagesCount = 0;
+
                 $preConsume = new PreConsume($this->interopContext, $subscriptionConsumer, $this->logger, $cycle, $this->receiveTimeout, $startTime);
                 $this->extension->onPreConsume($preConsume);
 
@@ -300,9 +307,10 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
 
                 $subscriptionConsumer->consume($this->receiveTimeout);
 
-                $this->extension->onIdle($context);
+                $postConsume = new PostConsume($this->interopContext, $subscriptionConsumer, $receivedMessagesCount, $cycle, $startTime, $this->logger);
+                $this->extension->onPostConsume($postConsume);
 
-                if ($context->isExecutionInterrupted()) {
+                if ($postConsume->isExecutionInterrupted()) {
                     throw new ConsumptionInterruptedException();
                 }
             } catch (ConsumptionInterruptedException $e) {
diff --git a/Tests/Consumption/ChainExtensionTest.php b/Tests/Consumption/ChainExtensionTest.php
index f203793..4902427 100644
--- a/Tests/Consumption/ChainExtensionTest.php
+++ b/Tests/Consumption/ChainExtensionTest.php
@@ -6,6 +6,7 @@
 use Enqueue\Consumption\Context;
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
+use Enqueue\Consumption\Context\PostConsume;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
@@ -36,7 +37,7 @@ public function testCouldBeConstructedWithExtensionsArray()
 
     public function testShouldProxyOnStartToAllInternalExtensions()
     {
-        $context = new Start($this->createInteropContextMock(), $this->createLoggerMock(), [], 0, 0, 0);
+        $context = new Start($this->createInteropContextMock(), $this->createLoggerMock(), [], 0, 0);
 
         $fooExtension = $this->createExtension();
         $fooExtension
@@ -196,26 +197,33 @@ public function testShouldProxyOnPostReceiveToAllInternalExtensions()
         $extensions->onPostMessageReceived($context);
     }
 
-    public function testShouldProxyOnIdleToAllInternalExtensions()
+    public function testShouldProxyOnPostConsumeToAllInternalExtensions()
     {
-        $context = $this->createContextMock();
+        $postConsume = new PostConsume(
+            $this->createInteropContextMock(),
+            $this->createSubscriptionConsumerMock(),
+            1,
+            1,
+            1,
+            new NullLogger()
+        );
 
         $fooExtension = $this->createExtension();
         $fooExtension
             ->expects($this->once())
-            ->method('onIdle')
-            ->with($this->identicalTo($context))
+            ->method('onPostConsume')
+            ->with($this->identicalTo($postConsume))
         ;
         $barExtension = $this->createExtension();
         $barExtension
             ->expects($this->once())
-            ->method('onIdle')
-            ->with($this->identicalTo($context))
+            ->method('onPostConsume')
+            ->with($this->identicalTo($postConsume))
         ;
 
         $extensions = new ChainExtension([$fooExtension, $barExtension]);
 
-        $extensions->onIdle($context);
+        $extensions->onPostConsume($postConsume);
     }
 
     public function testShouldProxyOnInterruptedToAllInternalExtensions()
diff --git a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
index 7d0f01b..2b4cc86 100644
--- a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Tests\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PostConsume;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Extension\LimitConsumerMemoryExtension;
@@ -29,23 +30,32 @@ public function testShouldThrowExceptionIfMemoryLimitIsNotInt()
         new LimitConsumerMemoryExtension(0.0);
     }
 
-    public function testOnIdleShouldInterruptExecutionIfMemoryLimitReached()
+    public function testOnPostConsumeShouldInterruptExecutionIfMemoryLimitReached()
     {
-        $context = $this->createContext();
-        $context->getLogger()
+        $logger = $this->createLoggerMock();
+        $logger
             ->expects($this->once())
             ->method('debug')
             ->with($this->stringContains('[LimitConsumerMemoryExtension] Interrupt execution as memory limit reached.'))
         ;
 
+        $postConsume = new PostConsume(
+            $this->createInteropContextMock(),
+            $this->createSubscriptionConsumerMock(),
+            1,
+            1,
+            1,
+            $logger
+        );
+
         // guard
-        $this->assertFalse($context->isExecutionInterrupted());
+        $this->assertFalse($postConsume->isExecutionInterrupted());
 
         // test
         $extension = new LimitConsumerMemoryExtension(1);
-        $extension->onIdle($context);
+        $extension->onPostConsume($postConsume);
 
-        $this->assertTrue($context->isExecutionInterrupted());
+        $this->assertTrue($postConsume->isExecutionInterrupted());
     }
 
     public function testOnPostReceivedShouldInterruptExecutionIfMemoryLimitReached()
@@ -124,18 +134,25 @@ public function testOnPreConsumeShouldNotInterruptExecutionIfMemoryLimitIsNotRea
         $this->assertFalse($context->isExecutionInterrupted());
     }
 
-    public function testOnIdleShouldNotInterruptExecutionIfMemoryLimitIsNotReached()
+    public function testOnPostConsumeShouldNotInterruptExecutionIfMemoryLimitIsNotReached()
     {
-        $context = $this->createContext();
+        $postConsume = new PostConsume(
+            $this->createInteropContextMock(),
+            $this->createSubscriptionConsumerMock(),
+            1,
+            1,
+            1,
+            new NullLogger()
+        );
 
         // guard
-        $this->assertFalse($context->isExecutionInterrupted());
+        $this->assertFalse($postConsume->isExecutionInterrupted());
 
         // test
         $extension = new LimitConsumerMemoryExtension(PHP_INT_MAX);
-        $extension->onIdle($context);
+        $extension->onPostConsume($postConsume);
 
-        $this->assertFalse($context->isExecutionInterrupted());
+        $this->assertFalse($postConsume->isExecutionInterrupted());
     }
 
     public function testOnPostMessageReceivedShouldNotInterruptExecutionIfMemoryLimitIsNotReached()
diff --git a/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php b/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
index 5519b3c..a824d59 100644
--- a/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Tests\Consumption\Extension;
 
 use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PostConsume;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Extension\LimitConsumptionTimeExtension;
@@ -44,19 +45,26 @@ public function testOnPreConsumeShouldInterruptExecutionIfConsumptionTimeExceede
         $this->assertTrue($context->isExecutionInterrupted());
     }
 
-    public function testOnIdleShouldInterruptExecutionIfConsumptionTimeExceeded()
+    public function testOnPostConsumeShouldInterruptExecutionIfConsumptionTimeExceeded()
     {
-        $context = $this->createContext();
+        $postConsume = new PostConsume(
+            $this->createInteropContextMock(),
+            $this->createSubscriptionConsumerMock(),
+            1,
+            1,
+            1,
+            new NullLogger()
+        );
 
         // guard
-        $this->assertFalse($context->isExecutionInterrupted());
+        $this->assertFalse($postConsume->isExecutionInterrupted());
 
         // test
         $extension = new LimitConsumptionTimeExtension(new \DateTime('-2 second'));
 
-        $extension->onIdle($context);
+        $extension->onPostConsume($postConsume);
 
-        $this->assertTrue($context->isExecutionInterrupted());
+        $this->assertTrue($postConsume->isExecutionInterrupted());
     }
 
     public function testOnPostReceivedShouldInterruptExecutionIfConsumptionTimeExceeded()
@@ -102,19 +110,26 @@ public function testOnPreConsumeShouldNotInterruptExecutionIfConsumptionTimeIsNo
         $this->assertFalse($context->isExecutionInterrupted());
     }
 
-    public function testOnIdleShouldNotInterruptExecutionIfConsumptionTimeIsNotExceeded()
+    public function testOnPostConsumeShouldNotInterruptExecutionIfConsumptionTimeIsNotExceeded()
     {
-        $context = $this->createContext();
+        $postConsume = new PostConsume(
+            $this->createInteropContextMock(),
+            $this->createSubscriptionConsumerMock(),
+            1,
+            1,
+            1,
+            new NullLogger()
+        );
 
         // guard
-        $this->assertFalse($context->isExecutionInterrupted());
+        $this->assertFalse($postConsume->isExecutionInterrupted());
 
         // test
         $extension = new LimitConsumptionTimeExtension(new \DateTime('+2 second'));
 
-        $extension->onIdle($context);
+        $extension->onPostConsume($postConsume);
 
-        $this->assertFalse($context->isExecutionInterrupted());
+        $this->assertFalse($postConsume->isExecutionInterrupted());
     }
 
     public function testOnPostReceivedShouldNotInterruptExecutionIfConsumptionTimeIsNotExceeded()
diff --git a/Tests/Consumption/Mock/BreakCycleExtension.php b/Tests/Consumption/Mock/BreakCycleExtension.php
index 63de3e1..983e4e1 100644
--- a/Tests/Consumption/Mock/BreakCycleExtension.php
+++ b/Tests/Consumption/Mock/BreakCycleExtension.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Tests\Consumption\Mock;
 
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\PostConsume;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface;
@@ -29,10 +29,10 @@ public function onPostMessageReceived(PostMessageReceived $context): void
         }
     }
 
-    public function onIdle(Context $context)
+    public function onPostConsume(PostConsume $context): void
     {
         if ($this->cycles >= $this->limit) {
-            $context->setExecutionInterrupted(true);
+            $context->interruptExecution();
         } else {
             ++$this->cycles;
         }
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index ad64a0e..5a5efbd 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -8,6 +8,7 @@
 use Enqueue\Consumption\Context;
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
+use Enqueue\Consumption\Context\PostConsume;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\PreSubscribe;
@@ -308,7 +309,7 @@ public function testShouldSubscribeToGivenQueueWithExpectedTimeout()
         $queueConsumer->consume();
     }
 
-    public function testShouldSubscribeToGivenQueueAndQuitAfterFifthIdleCycle()
+    public function testShouldSubscribeToGivenQueueAndQuitAfterFifthConsumeCycle()
     {
         $expectedQueue = new NullQueue('theQueueName');
 
@@ -699,34 +700,35 @@ public function testShouldCallPreSubscribeForEachBoundProcessor()
         $queueConsumer->consume();
     }
 
-    public function testShouldCallOnIdleExtensionMethod()
+    public function testShouldCallOnPostConsumeExtensionMethod()
     {
         $consumerStub = $this->createConsumerStub('foo_queue');
 
         $contextStub = $this->createContextStub($consumerStub);
 
+        $subscriptionConsumer = new DummySubscriptionConsumer();
+
         $processorMock = $this->createProcessorMock();
 
         $extension = $this->createExtension();
         $extension
             ->expects($this->once())
-            ->method('onIdle')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use ($contextStub) {
-                $this->assertSame($contextStub, $context->getInteropContext());
+            ->method('onPostConsume')
+            ->with($this->isInstanceOf(PostConsume::class))
+            ->willReturnCallback(function (PostConsume $context) use ($contextStub, $subscriptionConsumer) {
+                $this->assertSame($contextStub, $context->getContext());
+                $this->assertSame($subscriptionConsumer, $context->getSubscriptionConsumer());
+                $this->assertSame(1, $context->getCycle());
+                $this->assertSame(0, $context->getReceivedMessagesCount());
+                $this->assertGreaterThan(1, $context->getStartTime());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
-                $this->assertNull($context->getProcessor());
-                $this->assertNull($context->getConsumer());
-                $this->assertNull($context->getInteropMessage());
-                $this->assertNull($context->getException());
-                $this->assertNull($context->getResult());
                 $this->assertFalse($context->isExecutionInterrupted());
             })
         ;
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
         $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
-        $queueConsumer->setFallbackSubscriptionConsumer(new DummySubscriptionConsumer());
+        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumer);
         $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
 
         $queueConsumer->consume();
@@ -996,7 +998,7 @@ public function testShouldCallOnPostMessageReceivedExtensionMethodWithExpectedCo
         $queueConsumer->consume();
     }
 
-    public function testShouldAllowInterruptConsumingOnIdle()
+    public function testShouldAllowInterruptConsumingOnPostConsume()
     {
         $consumerStub = $this->createConsumerStub('foo_queue');
 
@@ -1007,10 +1009,10 @@ public function testShouldAllowInterruptConsumingOnIdle()
         $extension = $this->createExtension();
         $extension
             ->expects($this->once())
-            ->method('onIdle')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) {
-                $context->setExecutionInterrupted(true);
+            ->method('onPostConsume')
+            ->with($this->isInstanceOf(PostConsume::class))
+            ->willReturnCallback(function (PostConsume $context) {
+                $context->interruptExecution();
             })
         ;
         $extension
@@ -1037,36 +1039,6 @@ public function testShouldAllowInterruptConsumingOnIdle()
         $queueConsumer->consume();
     }
 
-    public function testShouldNotCloseContextWhenConsumptionInterrupted()
-    {
-        $consumerStub = $this->createConsumerStub('foo_queue');
-
-        $contextStub = $this->createContextStub($consumerStub);
-        $contextStub
-            ->expects($this->never())
-            ->method('close')
-        ;
-
-        $processorMock = $this->createProcessorMock();
-
-        $extension = $this->createExtension();
-        $extension
-            ->expects($this->once())
-            ->method('onIdle')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) {
-                $context->setExecutionInterrupted(true);
-            })
-        ;
-
-        $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
-        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions);
-        $queueConsumer->setFallbackSubscriptionConsumer(new DummySubscriptionConsumer());
-        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
-
-        $queueConsumer->consume();
-    }
-
     public function testShouldNotCloseContextWhenConsumptionInterruptedByException()
     {
         $expectedException = new \Exception();

From 92ad2de0b74f2c3965c89f90271cc5a3a5834132 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 8 Oct 2018 13:20:43 +0300
Subject: [PATCH 157/337] [consumption] Remove onInterrupted. Add onEnd. Remove
 context object and consumption interrupted exception.

---
 .../FlushSpoolProducerExtension.php           |   4 +-
 Consumption/ChainExtension.php                |   5 +-
 Consumption/Context.php                       | 233 -----------------
 Consumption/Context/End.php                   |  63 +++++
 Consumption/EmptyExtensionTrait.php           |   3 +-
 Consumption/EndExtensionInterface.php         |  13 +
 .../ConsumptionInterruptedException.php       |   7 -
 Consumption/ExtensionInterface.php            |   9 +-
 Consumption/QueueConsumer.php                 | 156 ++++-------
 .../FlushSpoolProducerExtensionTest.php       |  28 +-
 Tests/Consumption/ChainExtensionTest.php      |  33 +--
 Tests/Consumption/ContextTest.php             | 245 ------------------
 .../ConsumptionInterruptedExceptionTest.php   |  28 --
 .../LimitConsumedMessagesExtensionTest.php    |  22 +-
 .../LimitConsumerMemoryExtensionTest.php      |  22 +-
 .../LimitConsumptionTimeExtensionTest.php     |  22 +-
 Tests/Consumption/QueueConsumerTest.php       | 148 ++++-------
 17 files changed, 215 insertions(+), 826 deletions(-)
 delete mode 100644 Consumption/Context.php
 create mode 100644 Consumption/Context/End.php
 create mode 100644 Consumption/EndExtensionInterface.php
 delete mode 100644 Consumption/Exception/ConsumptionInterruptedException.php
 delete mode 100644 Tests/Consumption/ContextTest.php
 delete mode 100644 Tests/Consumption/Exception/ConsumptionInterruptedExceptionTest.php

diff --git a/Client/ConsumptionExtension/FlushSpoolProducerExtension.php b/Client/ConsumptionExtension/FlushSpoolProducerExtension.php
index c3e903c..19c83d0 100644
--- a/Client/ConsumptionExtension/FlushSpoolProducerExtension.php
+++ b/Client/ConsumptionExtension/FlushSpoolProducerExtension.php
@@ -3,7 +3,7 @@
 namespace Enqueue\Client\ConsumptionExtension;
 
 use Enqueue\Client\SpoolProducer;
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\End;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\ExtensionInterface;
@@ -30,7 +30,7 @@ public function onPostMessageReceived(PostMessageReceived $context): void
         $this->producer->flush();
     }
 
-    public function onInterrupted(Context $context)
+    public function onEnd(End $context): void
     {
         $this->producer->flush();
     }
diff --git a/Consumption/ChainExtension.php b/Consumption/ChainExtension.php
index 86f1e36..32e3f18 100644
--- a/Consumption/ChainExtension.php
+++ b/Consumption/ChainExtension.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\End;
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PostConsume;
@@ -87,10 +88,10 @@ public function onPostConsume(PostConsume $context): void
         }
     }
 
-    public function onInterrupted(Context $context)
+    public function onEnd(End $context): void
     {
         foreach ($this->extensions as $extension) {
-            $extension->onInterrupted($context);
+            $extension->onEnd($context);
         }
     }
 }
diff --git a/Consumption/Context.php b/Consumption/Context.php
deleted file mode 100644
index 822adbe..0000000
--- a/Consumption/Context.php
+++ /dev/null
@@ -1,233 +0,0 @@
-<?php
-
-namespace Enqueue\Consumption;
-
-use Enqueue\Consumption\Exception\IllegalContextModificationException;
-use Interop\Queue\Consumer;
-use Interop\Queue\Context as InteropContext;
-use Interop\Queue\Message as InteropMessage;
-use Interop\Queue\Processor;
-use Interop\Queue\Queue as InteropQueue;
-use Psr\Log\LoggerInterface;
-
-class Context
-{
-    /**
-     * @var InteropContext
-     */
-    private $context;
-
-    /**
-     * @var Consumer
-     */
-    private $consumer;
-
-    /**
-     * @var Processor
-     */
-    private $processor;
-
-    /**
-     * @var LoggerInterface
-     */
-    private $logger;
-
-    /**
-     * @var InteropMessage
-     */
-    private $interopMessage;
-
-    /**
-     * @var \Exception
-     */
-    private $exception;
-
-    /**
-     * @var Result|string
-     */
-    private $result;
-
-    /**
-     * @var InteropQueue
-     */
-    private $InteropQueue;
-
-    /**
-     * @var bool
-     */
-    private $executionInterrupted;
-
-    /**
-     * @param InteropContext $interopContext
-     */
-    public function __construct(InteropContext $interopContext)
-    {
-        $this->context = $interopContext;
-
-        $this->executionInterrupted = false;
-    }
-
-    /**
-     * @return InteropMessage
-     */
-    public function getInteropMessage()
-    {
-        return $this->interopMessage;
-    }
-
-    /**
-     * @param InteropMessage $interopMessage
-     */
-    public function setInteropMessage(InteropMessage $interopMessage)
-    {
-        if ($this->interopMessage) {
-            throw new IllegalContextModificationException('The message could be set once');
-        }
-
-        $this->interopMessage = $interopMessage;
-    }
-
-    /**
-     * @return InteropContext
-     */
-    public function getInteropContext()
-    {
-        return $this->context;
-    }
-
-    /**
-     * @return Consumer
-     */
-    public function getConsumer()
-    {
-        return $this->consumer;
-    }
-
-    /**
-     * @param Consumer $consumer
-     */
-    public function setConsumer(Consumer $consumer)
-    {
-        if ($this->consumer) {
-            throw new IllegalContextModificationException('The message consumer could be set once');
-        }
-
-        $this->consumer = $consumer;
-    }
-
-    /**
-     * @return Processor
-     */
-    public function getProcessor()
-    {
-        return $this->processor;
-    }
-
-    /**
-     * @param Processor $processor
-     */
-    public function setProcessor(Processor $processor)
-    {
-        if ($this->processor) {
-            throw new IllegalContextModificationException('The message processor could be set once');
-        }
-
-        $this->processor = $processor;
-    }
-
-    /**
-     * @return \Throwable
-     */
-    public function getException()
-    {
-        return $this->exception;
-    }
-
-    /**
-     * @param \Throwable $exception
-     */
-    public function setException(\Throwable $exception)
-    {
-        $this->exception = $exception;
-    }
-
-    /**
-     * @return Result|string
-     */
-    public function getResult()
-    {
-        return $this->result;
-    }
-
-    /**
-     * @param Result|string $result
-     */
-    public function setResult($result)
-    {
-//        if ($this->result) {
-//            throw new IllegalContextModificationException('The result modification is not allowed');
-//        }
-
-        $this->result = $result;
-    }
-
-    /**
-     * @return bool
-     */
-    public function isExecutionInterrupted()
-    {
-        return $this->executionInterrupted;
-    }
-
-    /**
-     * @param bool $executionInterrupted
-     */
-    public function setExecutionInterrupted($executionInterrupted)
-    {
-        if (false == $executionInterrupted && $this->executionInterrupted) {
-            throw new IllegalContextModificationException('The execution once interrupted could not be roll backed');
-        }
-
-        $this->executionInterrupted = $executionInterrupted;
-    }
-
-    /**
-     * @return LoggerInterface
-     */
-    public function getLogger()
-    {
-        return $this->logger;
-    }
-
-    /**
-     * @param LoggerInterface $logger
-     */
-    public function setLogger(LoggerInterface $logger)
-    {
-        if ($this->logger) {
-            throw new IllegalContextModificationException('The logger modification is not allowed');
-        }
-
-        $this->logger = $logger;
-    }
-
-    /**
-     * @return InteropQueue
-     */
-    public function getInteropQueue()
-    {
-        return $this->InteropQueue;
-    }
-
-    /**
-     * @param InteropQueue $InteropQueue
-     */
-    public function setInteropQueue(InteropQueue $InteropQueue)
-    {
-        if ($this->InteropQueue) {
-            throw new IllegalContextModificationException('The queue modification is not allowed');
-        }
-
-        $this->InteropQueue = $InteropQueue;
-    }
-}
diff --git a/Consumption/Context/End.php b/Consumption/Context/End.php
new file mode 100644
index 0000000..467f6d3
--- /dev/null
+++ b/Consumption/Context/End.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace Enqueue\Consumption\Context;
+
+use Interop\Queue\Context;
+use Psr\Log\LoggerInterface;
+
+final class End
+{
+    /**
+     * @var Context
+     */
+    private $context;
+
+    /**
+     * @var int
+     */
+    private $startTime;
+
+    /**
+     * @var int
+     */
+    private $endTime;
+
+    /**
+     * @var LoggerInterface
+     */
+    private $logger;
+
+    public function __construct(Context $context, int $startTime, int $endTime, LoggerInterface $logger)
+    {
+        $this->context = $context;
+        $this->logger = $logger;
+        $this->startTime = $startTime;
+        $this->endTime = $endTime;
+    }
+
+    public function getContext(): Context
+    {
+        return $this->context;
+    }
+
+    public function getLogger(): LoggerInterface
+    {
+        return $this->logger;
+    }
+
+    /**
+     * In milliseconds.
+     */
+    public function getStartTime(): int
+    {
+        return $this->startTime;
+    }
+
+    /**
+     * In milliseconds.
+     */
+    public function getEndTime(): int
+    {
+        return $this->startTime;
+    }
+}
diff --git a/Consumption/EmptyExtensionTrait.php b/Consumption/EmptyExtensionTrait.php
index fec117d..ab21ee5 100644
--- a/Consumption/EmptyExtensionTrait.php
+++ b/Consumption/EmptyExtensionTrait.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\End;
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PostConsume;
@@ -45,7 +46,7 @@ public function onPostConsume(PostConsume $context): void
     {
     }
 
-    public function onInterrupted(Context $context)
+    public function onEnd(End $context): void
     {
     }
 }
diff --git a/Consumption/EndExtensionInterface.php b/Consumption/EndExtensionInterface.php
new file mode 100644
index 0000000..167779d
--- /dev/null
+++ b/Consumption/EndExtensionInterface.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Enqueue\Consumption;
+
+use Enqueue\Consumption\Context\End;
+
+interface EndExtensionInterface
+{
+    /**
+     * Executed only once just before QueueConsumer::consume returns.
+     */
+    public function onEnd(End $context): void;
+}
diff --git a/Consumption/Exception/ConsumptionInterruptedException.php b/Consumption/Exception/ConsumptionInterruptedException.php
deleted file mode 100644
index 0bfe0c1..0000000
--- a/Consumption/Exception/ConsumptionInterruptedException.php
+++ /dev/null
@@ -1,7 +0,0 @@
-<?php
-
-namespace Enqueue\Consumption\Exception;
-
-class ConsumptionInterruptedException extends \LogicException implements ExceptionInterface
-{
-}
diff --git a/Consumption/ExtensionInterface.php b/Consumption/ExtensionInterface.php
index d4c3961..b928024 100644
--- a/Consumption/ExtensionInterface.php
+++ b/Consumption/ExtensionInterface.php
@@ -2,13 +2,6 @@
 
 namespace Enqueue\Consumption;
 
-interface ExtensionInterface extends StartExtensionInterface, PreSubscribeExtensionInterface, PreConsumeExtensionInterface, MessageReceivedExtensionInterface, PostMessageReceivedExtensionInterface, MessageResultExtensionInterface, ProcessorExceptionExtensionInterface, PostConsumeExtensionInterface
+interface ExtensionInterface extends StartExtensionInterface, PreSubscribeExtensionInterface, PreConsumeExtensionInterface, MessageReceivedExtensionInterface, PostMessageReceivedExtensionInterface, MessageResultExtensionInterface, ProcessorExceptionExtensionInterface, PostConsumeExtensionInterface, EndExtensionInterface
 {
-    /**
-     * Called when the consumption was interrupted by an extension or exception
-     * In case of exception it will be present in the context.
-     *
-     * @param Context $context
-     */
-    public function onInterrupted(Context $context);
 }
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 47fe4d0..0850fb0 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Consumption;
 
+use Enqueue\Consumption\Context\End;
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PostConsume;
@@ -10,13 +11,12 @@
 use Enqueue\Consumption\Context\PreSubscribe;
 use Enqueue\Consumption\Context\ProcessorException;
 use Enqueue\Consumption\Context\Start;
-use Enqueue\Consumption\Exception\ConsumptionInterruptedException;
 use Enqueue\Consumption\Exception\InvalidArgumentException;
 use Enqueue\Consumption\Exception\LogicException;
-use Enqueue\Util\VarExport;
 use Interop\Queue\Consumer;
 use Interop\Queue\Context as InteropContext;
 use Interop\Queue\Exception\SubscriptionConsumerNotSupportedException;
+use Interop\Queue\Message;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Processor;
 use Interop\Queue\Queue as InteropQueue;
@@ -128,18 +128,6 @@ public function bindCallback($queue, callable $processor): QueueConsumerInterfac
 
     public function consume(ExtensionInterface $runtimeExtension = null): void
     {
-        /*
-         * onStart             +
-         * onPreSubscribe      +
-         * onPreConsume        +
-         * onMessageReceived   +
-         * onResult
-         * onProcessorException
-         * onPostMessageReceived
-         * onPostConsume
-         * onEnd
-         */
-
         $this->extension = $runtimeExtension ?
             new ChainExtension([$this->staticExtension, $runtimeExtension]) :
             $this->staticExtension
@@ -157,8 +145,10 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
 
         $this->extension->onStart($start);
 
-        // todo
         if ($start->isExecutionInterrupted()) {
+            $this->onEnd($startTime);
+
+            return;
         }
 
         $this->logger = $start->getLogger();
@@ -177,20 +167,6 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
             $consumers[$queue->getQueueName()] = $this->interopContext->createConsumer($queue);
         }
 
-        // todo remove
-        $context = new Context($this->interopContext);
-        $context->setLogger($this->logger);
-//        $this->extension->onStart($context);
-//
-//        if ($context->getLogger()) {
-//            $this->logger = $context->getLogger();
-//        } else {
-//            $this->logger = new NullLogger();
-//            $context->setLogger($this->logger);
-//        }
-
-//        $this->logger->info('Start consuming');
-
         try {
             $subscriptionConsumer = $this->interopContext->createSubscriptionConsumer();
         } catch (SubscriptionConsumerNotSupportedException $e) {
@@ -198,8 +174,9 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
         }
 
         $receivedMessagesCount = 0;
+        $interruptExecution = false;
 
-        $callback = function (InteropMessage $message, Consumer $consumer) use (&$context, &$receivedMessagesCount) {
+        $callback = function (InteropMessage $message, Consumer $consumer) use (&$receivedMessagesCount, &$interruptExecution) {
             ++$receivedMessagesCount;
 
             $receivedAt = (int) (microtime(true) * 1000);
@@ -210,36 +187,15 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
 
             $processor = $this->boundProcessors[$queue->getQueueName()]->getProcessor();
 
-//            $this->logger->info('Message received from the queue: '.$context->getInteropQueue()->getQueueName());
-//            $this->logger->debug('Headers: {headers}', ['headers' => new VarExport($message->getHeaders())]);
-//            $this->logger->debug('Properties: {properties}', ['properties' => new VarExport($message->getProperties())]);
-//            $this->logger->debug('Payload: {payload}', ['payload' => new VarExport($message->getBody())]);
-
-            // TODO remove
-            $context = new Context($this->interopContext);
-            $context->setLogger($this->logger);
-            $context->setInteropQueue($consumer->getQueue());
-            $context->setConsumer($consumer);
-            $context->setProcessor($processor);
-            $context->setInteropMessage($message);
-
             $messageReceived = new MessageReceived($this->interopContext, $consumer, $message, $processor, $receivedAt, $this->logger);
             $this->extension->onMessageReceived($messageReceived);
             $result = $messageReceived->getResult();
             $processor = $messageReceived->getProcessor();
             if (null === $result) {
                 try {
-                    $result = $processor->process($message, $context->getInteropContext());
-
-                    $context->setResult($result);
+                    $result = $processor->process($message, $this->interopContext);
                 } catch (\Exception $e) {
-                    $processorException = new ProcessorException($this->interopContext, $message, $e, $receivedAt, $this->logger);
-                    $this->extension->onProcessorException($processorException);
-
-                    $result = $processorException->getResult();
-                    if (null === $result) {
-                        throw $e;
-                    }
+                    $result = $this->onProcessorException($message, $e, $receivedAt);
                 }
             }
 
@@ -247,9 +203,6 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
             $this->extension->onResult($messageResult);
             $result = $messageResult->getResult();
 
-            //todo
-            $context->setResult($result);
-
             switch ($result) {
                 case Result::ACK:
                     $consumer->acknowledge($message);
@@ -266,13 +219,13 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
                     throw new \LogicException(sprintf('Status is not supported: %s', $result));
             }
 
-//            $this->logger->info(sprintf('Message processed: %s', $result));
-
             $postMessageReceived = new PostMessageReceived($this->interopContext, $message, $result, $receivedAt, $this->logger);
             $this->extension->onPostMessageReceived($postMessageReceived);
 
             if ($postMessageReceived->isExecutionInterrupted()) {
-                throw new ConsumptionInterruptedException();
+                $interruptExecution = true;
+
+                return false;
             }
 
             return true;
@@ -295,43 +248,25 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
 
         $cycle = 1;
         while (true) {
-            try {
-                $receivedMessagesCount = 0;
-
-                $preConsume = new PreConsume($this->interopContext, $subscriptionConsumer, $this->logger, $cycle, $this->receiveTimeout, $startTime);
-                $this->extension->onPreConsume($preConsume);
-
-                if ($preConsume->isExecutionInterrupted()) {
-                    throw new ConsumptionInterruptedException();
-                }
-
-                $subscriptionConsumer->consume($this->receiveTimeout);
-
-                $postConsume = new PostConsume($this->interopContext, $subscriptionConsumer, $receivedMessagesCount, $cycle, $startTime, $this->logger);
-                $this->extension->onPostConsume($postConsume);
-
-                if ($postConsume->isExecutionInterrupted()) {
-                    throw new ConsumptionInterruptedException();
-                }
-            } catch (ConsumptionInterruptedException $e) {
-                $this->logger->info(sprintf('Consuming interrupted'));
+            $receivedMessagesCount = 0;
+            $interruptExecution = false;
 
-                foreach ($consumers as $consumer) {
-                    /* @var Consumer $consumer */
+            $preConsume = new PreConsume($this->interopContext, $subscriptionConsumer, $this->logger, $cycle, $this->receiveTimeout, $startTime);
+            $this->extension->onPreConsume($preConsume);
 
-                    $subscriptionConsumer->unsubscribe($consumer);
-                }
+            if ($preConsume->isExecutionInterrupted()) {
+                $this->onEnd($startTime, $subscriptionConsumer);
 
-                $context->setExecutionInterrupted(true);
+                return;
+            }
 
-                $this->extension->onInterrupted($context);
+            $subscriptionConsumer->consume($this->receiveTimeout);
 
-                return;
-            } catch (\Throwable $exception) {
-                $context->setExecutionInterrupted(true);
-                $context->setException($exception);
+            $postConsume = new PostConsume($this->interopContext, $subscriptionConsumer, $receivedMessagesCount, $cycle, $startTime, $this->logger);
+            $this->extension->onPostConsume($postConsume);
 
-                $this->onInterruptionByException($this->extension, $context);
+            if ($interruptExecution || $postConsume->isExecutionInterrupted()) {
+                $this->onEnd($startTime, $subscriptionConsumer);
 
                 return;
             }
@@ -350,31 +285,36 @@ public function setFallbackSubscriptionConsumer(SubscriptionConsumer $fallbackSu
         $this->fallbackSubscriptionConsumer = $fallbackSubscriptionConsumer;
     }
 
+    private function onEnd(int $startTime, SubscriptionConsumer $subscriptionConsumer = null): void
+    {
+        $endTime = (int) (microtime(true) * 1000);
+
+        $this->extension->onEnd(new End($this->interopContext, $startTime, $endTime, $this->logger));
+
+        if ($subscriptionConsumer) {
+            $subscriptionConsumer->unsubscribeAll();
+        }
+    }
+
     /**
-     * @param ExtensionInterface $extension
-     * @param Context            $context
+     * The logic is similar to one in Symfony's ExceptionListener::.
      *
-     * @throws \Exception
+     * https://github.com/symfony/symfony/blob/cbe289517470eeea27162fd2d523eb29c95f775f/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php#L77
      */
-    private function onInterruptionByException(ExtensionInterface $extension, Context $context)
+    private function onProcessorException(Message $message, \Exception $exception, int $receivedAt)
     {
-//        $this->logger = $context->getLogger();
-//        $this->logger->error(sprintf('Consuming interrupted by exception'));
-
-        $exception = $context->getException();
+        $processorException = new ProcessorException($this->interopContext, $message, $exception, $receivedAt, $this->logger);
 
         try {
-            $this->extension->onInterrupted($context);
-        } catch (\Exception $e) {
-            // logic is similar to one in Symfony's ExceptionListener::onKernelException
-//            $this->logger->error(sprintf(
-//                'Exception thrown when handling an exception (%s: %s at %s line %s)',
-//                get_class($e),
-//                $e->getMessage(),
-//                $e->getFile(),
-//                $e->getLine()
-//            ));
+            $this->extension->onProcessorException($processorException);
 
+            $result = $processorException->getResult();
+            if (null === $result) {
+                throw $exception;
+            }
+
+            return $result;
+        } catch (\Exception $e) {
             $wrapper = $e;
             while ($prev = $wrapper->getPrevious()) {
                 if ($exception === $wrapper = $prev) {
diff --git a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
index 9def2e9..e6df875 100644
--- a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
@@ -4,12 +4,12 @@
 
 use Enqueue\Client\ConsumptionExtension\FlushSpoolProducerExtension;
 use Enqueue\Client\SpoolProducer;
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\End;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\Context;
 use Interop\Queue\Message;
-use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -27,7 +27,7 @@ public function testCouldBeConstructedWithSpoolProducerAsFirstArgument()
         new FlushSpoolProducerExtension($this->createSpoolProducerMock());
     }
 
-    public function testShouldFlushSpoolProducerOnInterrupted()
+    public function testShouldFlushSpoolProducerOnEnd()
     {
         $producer = $this->createSpoolProducerMock();
         $producer
@@ -35,8 +35,10 @@ public function testShouldFlushSpoolProducerOnInterrupted()
             ->method('flush')
         ;
 
+        $end = new End($this->createInteropContextMock(), 1, 2, new NullLogger());
+
         $extension = new FlushSpoolProducerExtension($producer);
-        $extension->onInterrupted($this->createContextMock());
+        $extension->onEnd($end);
     }
 
     public function testShouldFlushSpoolProducerOnPostReceived()
@@ -62,23 +64,7 @@ public function testShouldFlushSpoolProducerOnPostReceived()
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    private function createInteropContextMock(): \Interop\Queue\Context
-    {
-        return $this->createMock(\Interop\Queue\Context::class);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
-     */
-    private function createSubscriptionConsumerMock(): SubscriptionConsumer
-    {
-        return $this->createMock(SubscriptionConsumer::class);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Context
-     */
-    private function createContextMock()
+    private function createInteropContextMock(): Context
     {
         return $this->createMock(Context::class);
     }
diff --git a/Tests/Consumption/ChainExtensionTest.php b/Tests/Consumption/ChainExtensionTest.php
index 4902427..cf905d8 100644
--- a/Tests/Consumption/ChainExtensionTest.php
+++ b/Tests/Consumption/ChainExtensionTest.php
@@ -3,7 +3,7 @@
 namespace Enqueue\Tests\Consumption;
 
 use Enqueue\Consumption\ChainExtension;
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\End;
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PostConsume;
@@ -14,6 +14,7 @@
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Test\ClassExtensionTrait;
 use Interop\Queue\Consumer;
+use Interop\Queue\Context;
 use Interop\Queue\Message;
 use Interop\Queue\Processor;
 use Interop\Queue\SubscriptionConsumer;
@@ -226,26 +227,26 @@ public function testShouldProxyOnPostConsumeToAllInternalExtensions()
         $extensions->onPostConsume($postConsume);
     }
 
-    public function testShouldProxyOnInterruptedToAllInternalExtensions()
+    public function testShouldProxyOnEndToAllInternalExtensions()
     {
-        $context = $this->createContextMock();
+        $context = new End($this->createInteropContextMock(), 1, 2, new NullLogger());
 
         $fooExtension = $this->createExtension();
         $fooExtension
             ->expects($this->once())
-            ->method('onInterrupted')
+            ->method('onEnd')
             ->with($this->identicalTo($context))
         ;
         $barExtension = $this->createExtension();
         $barExtension
             ->expects($this->once())
-            ->method('onInterrupted')
+            ->method('onEnd')
             ->with($this->identicalTo($context))
         ;
 
         $extensions = new ChainExtension([$fooExtension, $barExtension]);
 
-        $extensions->onInterrupted($context);
+        $extensions->onEnd($context);
     }
 
     /**
@@ -259,33 +260,25 @@ protected function createLoggerMock(): LoggerInterface
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    protected function createInteropContextMock(): \Interop\Queue\Context
+    protected function createInteropContextMock(): Context
     {
-        return $this->createMock(\Interop\Queue\Context::class);
+        return $this->createMock(Context::class);
     }
 
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    protected function createInteropConsumerMock(): \Interop\Queue\Consumer
+    protected function createInteropConsumerMock(): Consumer
     {
-        return $this->createMock(\Interop\Queue\Consumer::class);
+        return $this->createMock(Consumer::class);
     }
 
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    protected function createInteropProcessorMock(): \Interop\Queue\Processor
+    protected function createInteropProcessorMock(): Processor
     {
-        return $this->createMock(\Interop\Queue\Processor::class);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Context
-     */
-    protected function createContextMock()
-    {
-        return $this->createMock(Context::class);
+        return $this->createMock(Processor::class);
     }
 
     /**
diff --git a/Tests/Consumption/ContextTest.php b/Tests/Consumption/ContextTest.php
deleted file mode 100644
index 41fd211..0000000
--- a/Tests/Consumption/ContextTest.php
+++ /dev/null
@@ -1,245 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Consumption;
-
-use Enqueue\Consumption\Context;
-use Enqueue\Consumption\Exception\IllegalContextModificationException;
-use Enqueue\Null\NullQueue;
-use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\Consumer;
-use Interop\Queue\Context as InteropContext;
-use Interop\Queue\Message as InteropMessage;
-use Interop\Queue\Processor;
-use PHPUnit\Framework\TestCase;
-use Psr\Log\NullLogger;
-
-class ContextTest extends TestCase
-{
-    use ClassExtensionTrait;
-
-    public function testCouldBeConstructedWithSessionAsFirstArgument()
-    {
-        new Context($this->createContext());
-    }
-
-    public function testShouldAllowGetSessionSetInConstructor()
-    {
-        $interopContext = $this->createContext();
-
-        $context = new Context($interopContext);
-
-        $this->assertSame($interopContext, $context->getInteropContext());
-    }
-
-    public function testShouldAllowGetMessageConsumerPreviouslySet()
-    {
-        $messageConsumer = $this->createConsumer();
-
-        $context = new Context($this->createContext());
-        $context->setConsumer($messageConsumer);
-
-        $this->assertSame($messageConsumer, $context->getConsumer());
-    }
-
-    public function testThrowOnTryToChangeMessageConsumerIfAlreadySet()
-    {
-        $messageConsumer = $this->createConsumer();
-        $anotherMessageConsumer = $this->createConsumer();
-
-        $context = new Context($this->createContext());
-
-        $context->setConsumer($messageConsumer);
-
-        $this->expectException(IllegalContextModificationException::class);
-
-        $context->setConsumer($anotherMessageConsumer);
-    }
-
-    public function testShouldAllowGetMessageProducerPreviouslySet()
-    {
-        $processorMock = $this->createProcessorMock();
-
-        $context = new Context($this->createContext());
-        $context->setProcessor($processorMock);
-
-        $this->assertSame($processorMock, $context->getProcessor());
-    }
-
-    public function testThrowOnTryToChangeProcessorIfAlreadySet()
-    {
-        $processor = $this->createProcessorMock();
-        $anotherProcessor = $this->createProcessorMock();
-
-        $context = new Context($this->createContext());
-
-        $context->setProcessor($processor);
-
-        $this->expectException(IllegalContextModificationException::class);
-
-        $context->setProcessor($anotherProcessor);
-    }
-
-    public function testShouldAllowGetLoggerPreviouslySet()
-    {
-        $logger = new NullLogger();
-
-        $context = new Context($this->createContext());
-        $context->setLogger($logger);
-
-        $this->assertSame($logger, $context->getLogger());
-    }
-
-    public function testShouldSetExecutionInterruptedToFalseInConstructor()
-    {
-        $context = new Context($this->createContext());
-
-        $this->assertFalse($context->isExecutionInterrupted());
-    }
-
-    public function testShouldAllowGetPreviouslySetMessage()
-    {
-        /** @var InteropMessage $message */
-        $message = $this->createMock(InteropMessage::class);
-
-        $context = new Context($this->createContext());
-
-        $context->setInteropMessage($message);
-
-        $this->assertSame($message, $context->getInteropMessage());
-    }
-
-    public function testThrowOnTryToChangeMessageIfAlreadySet()
-    {
-        /** @var InteropMessage $message */
-        $message = $this->createMock(InteropMessage::class);
-
-        $context = new Context($this->createContext());
-
-        $this->expectException(IllegalContextModificationException::class);
-        $this->expectExceptionMessage('The message could be set once');
-
-        $context->setInteropMessage($message);
-        $context->setInteropMessage($message);
-    }
-
-    public function testShouldAllowGetPreviouslySetException()
-    {
-        $exception = new \Exception();
-
-        $context = new Context($this->createContext());
-
-        $context->setException($exception);
-
-        $this->assertSame($exception, $context->getException());
-    }
-
-    public function testShouldAllowGetPreviouslySetResult()
-    {
-        $result = 'aResult';
-
-        $context = new Context($this->createContext());
-
-        $context->setResult($result);
-
-        $this->assertSame($result, $context->getResult());
-    }
-
-    public function testShouldAllowGetPreviouslySetExecutionInterrupted()
-    {
-        $context = new Context($this->createContext());
-
-        // guard
-        $this->assertFalse($context->isExecutionInterrupted());
-
-        $context->setExecutionInterrupted(true);
-
-        $this->assertTrue($context->isExecutionInterrupted());
-    }
-
-    public function testThrowOnTryToRollbackExecutionInterruptedIfAlreadySetToTrue()
-    {
-        $context = new Context($this->createContext());
-
-        $this->expectException(IllegalContextModificationException::class);
-        $this->expectExceptionMessage('The execution once interrupted could not be roll backed');
-
-        $context->setExecutionInterrupted(true);
-        $context->setExecutionInterrupted(false);
-    }
-
-    public function testNotThrowOnSettingExecutionInterruptedToTrueIfAlreadySetToTrue()
-    {
-        $context = new Context($this->createContext());
-
-        $context->setExecutionInterrupted(true);
-        $context->setExecutionInterrupted(true);
-    }
-
-    public function testShouldAllowGetPreviouslySetLogger()
-    {
-        $expectedLogger = new NullLogger();
-
-        $context = new Context($this->createContext());
-
-        $context->setLogger($expectedLogger);
-
-        $this->assertSame($expectedLogger, $context->getLogger());
-    }
-
-    public function testThrowOnSettingLoggerIfAlreadySet()
-    {
-        $context = new Context($this->createContext());
-
-        $context->setLogger(new NullLogger());
-
-        $this->expectException(IllegalContextModificationException::class);
-        $this->expectExceptionMessage('The logger modification is not allowed');
-
-        $context->setLogger(new NullLogger());
-    }
-
-    public function testShouldAllowGetPreviouslySetQueue()
-    {
-        $context = new Context($this->createContext());
-
-        $context->setInteropQueue($queue = new NullQueue(''));
-
-        $this->assertSame($queue, $context->getInteropQueue());
-    }
-
-    public function testThrowOnSettingQueueNameIfAlreadySet()
-    {
-        $context = new Context($this->createContext());
-
-        $context->setInteropQueue(new NullQueue(''));
-
-        $this->expectException(IllegalContextModificationException::class);
-        $this->expectExceptionMessage('The queue modification is not allowed');
-
-        $context->setInteropQueue(new NullQueue(''));
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
-     */
-    protected function createContext(): InteropContext
-    {
-        return $this->createMock(InteropContext::class);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Consumer
-     */
-    protected function createConsumer()
-    {
-        return $this->createMock(Consumer::class);
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Processor
-     */
-    protected function createProcessorMock()
-    {
-        return $this->createMock(Processor::class);
-    }
-}
diff --git a/Tests/Consumption/Exception/ConsumptionInterruptedExceptionTest.php b/Tests/Consumption/Exception/ConsumptionInterruptedExceptionTest.php
deleted file mode 100644
index c6f608b..0000000
--- a/Tests/Consumption/Exception/ConsumptionInterruptedExceptionTest.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Consumption\Exception;
-
-use Enqueue\Consumption\Exception\ConsumptionInterruptedException;
-use Enqueue\Consumption\Exception\ExceptionInterface;
-use Enqueue\Test\ClassExtensionTrait;
-use PHPUnit\Framework\TestCase;
-
-class ConsumptionInterruptedExceptionTest extends TestCase
-{
-    use ClassExtensionTrait;
-
-    public function testShouldImplementExceptionInterface()
-    {
-        $this->assertClassImplements(ExceptionInterface::class, ConsumptionInterruptedException::class);
-    }
-
-    public function testShouldExtendLogicException()
-    {
-        $this->assertClassExtends(\LogicException::class, ConsumptionInterruptedException::class);
-    }
-
-    public function testCouldBeConstructedWithoutAnyArguments()
-    {
-        new ConsumptionInterruptedException();
-    }
-}
diff --git a/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php b/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
index 52099ef..2f4f804 100644
--- a/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
@@ -2,14 +2,11 @@
 
 namespace Enqueue\Tests\Consumption\Extension;
 
-use Enqueue\Consumption\Context;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Extension\LimitConsumedMessagesExtension;
-use Interop\Queue\Consumer;
-use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Context;
 use Interop\Queue\Message;
-use Interop\Queue\Processor;
 use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
@@ -162,22 +159,9 @@ public function testOnPostReceivedShouldInterruptExecutionIfMessageLimitExceeded
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    protected function createInteropContextMock(): \Interop\Queue\Context
+    protected function createInteropContextMock(): Context
     {
-        return $this->createMock(\Interop\Queue\Context::class);
-    }
-
-    /**
-     * @return Context
-     */
-    protected function createContext(): Context
-    {
-        $context = new Context($this->createMock(InteropContext::class));
-        $context->setLogger($this->createMock(LoggerInterface::class));
-        $context->setConsumer($this->createMock(Consumer::class));
-        $context->setProcessor($this->createMock(Processor::class));
-
-        return $context;
+        return $this->createMock(Context::class);
     }
 
     /**
diff --git a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
index 2b4cc86..8af630c 100644
--- a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
@@ -2,15 +2,12 @@
 
 namespace Enqueue\Tests\Consumption\Extension;
 
-use Enqueue\Consumption\Context;
 use Enqueue\Consumption\Context\PostConsume;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Extension\LimitConsumerMemoryExtension;
-use Interop\Queue\Consumer;
-use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Context;
 use Interop\Queue\Message;
-use Interop\Queue\Processor;
 use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
@@ -178,22 +175,9 @@ public function testOnPostMessageReceivedShouldNotInterruptExecutionIfMemoryLimi
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    protected function createInteropContextMock(): \Interop\Queue\Context
+    protected function createInteropContextMock(): Context
     {
-        return $this->createMock(\Interop\Queue\Context::class);
-    }
-
-    /**
-     * @return Context
-     */
-    protected function createContext(): Context
-    {
-        $context = new Context($this->createMock(InteropContext::class));
-        $context->setLogger($this->createMock(LoggerInterface::class));
-        $context->setConsumer($this->createMock(Consumer::class));
-        $context->setProcessor($this->createMock(Processor::class));
-
-        return $context;
+        return $this->createMock(Context::class);
     }
 
     /**
diff --git a/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php b/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
index a824d59..fe0cb5e 100644
--- a/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
@@ -2,15 +2,12 @@
 
 namespace Enqueue\Tests\Consumption\Extension;
 
-use Enqueue\Consumption\Context;
 use Enqueue\Consumption\Context\PostConsume;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Extension\LimitConsumptionTimeExtension;
-use Interop\Queue\Consumer;
-use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Context;
 use Interop\Queue\Message;
-use Interop\Queue\Processor;
 use Interop\Queue\SubscriptionConsumer;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
@@ -156,22 +153,9 @@ public function testOnPostReceivedShouldNotInterruptExecutionIfConsumptionTimeIs
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    protected function createInteropContextMock(): \Interop\Queue\Context
+    protected function createInteropContextMock(): Context
     {
-        return $this->createMock(\Interop\Queue\Context::class);
-    }
-
-    /**
-     * @return Context
-     */
-    protected function createContext(): Context
-    {
-        $context = new Context($this->createMock(InteropContext::class));
-        $context->setLogger($this->createMock(LoggerInterface::class));
-        $context->setConsumer($this->createMock(Consumer::class));
-        $context->setProcessor($this->createMock(Processor::class));
-
-        return $context;
+        return $this->createMock(Context::class);
     }
 
     /**
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 5a5efbd..50ced5e 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -5,7 +5,7 @@
 use Enqueue\Consumption\BoundProcessor;
 use Enqueue\Consumption\CallbackProcessor;
 use Enqueue\Consumption\ChainExtension;
-use Enqueue\Consumption\Context;
+use Enqueue\Consumption\Context\End;
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PostConsume;
@@ -646,6 +646,45 @@ public function testShouldCallOnStartWithLoggerProvidedInConstructor()
         $queueConsumer->consume();
     }
 
+    public function testShouldInterruptExecutionOnStart()
+    {
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createContextStub($consumerStub);
+
+        $processorMock = $this->createProcessorMock();
+        $processorMock
+            ->expects($this->never())
+            ->method('process')
+        ;
+
+        $expectedLogger = $this->createMock(LoggerInterface::class);
+
+        $extension = $this->createExtension();
+        $extension
+            ->expects($this->once())
+            ->method('onStart')
+            ->willReturnCallback(function (Start $context) {
+                $context->interruptExecution();
+            })
+        ;
+        $extension
+            ->expects($this->once())
+            ->method('onEnd')
+        ;
+        $extension
+            ->expects($this->never())
+            ->method('onPreConsume')
+        ;
+
+        $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, [], $expectedLogger);
+        $queueConsumer->setFallbackSubscriptionConsumer(new DummySubscriptionConsumer());
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
+
+        $queueConsumer->consume();
+    }
+
     public function testShouldCallPreSubscribeExtensionMethod()
     {
         $consumerStub = $this->createConsumerStub('foo_queue');
@@ -1017,17 +1056,13 @@ public function testShouldAllowInterruptConsumingOnPostConsume()
         ;
         $extension
             ->expects($this->once())
-            ->method('onInterrupted')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use ($contextStub) {
-                $this->assertSame($contextStub, $context->getInteropContext());
+            ->method('onEnd')
+            ->with($this->isInstanceOf(End::class))
+            ->willReturnCallback(function (End $context) use ($contextStub) {
+                $this->assertSame($contextStub, $context->getContext());
                 $this->assertInstanceOf(NullLogger::class, $context->getLogger());
-                $this->assertNull($context->getConsumer());
-                $this->assertNull($context->getProcessor());
-                $this->assertNull($context->getInteropMessage());
-                $this->assertNull($context->getException());
-                $this->assertNull($context->getResult());
-                $this->assertTrue($context->isExecutionInterrupted());
+                $this->assertGreaterThan(1, $context->getStartTime());
+                $this->assertGreaterThan(1, $context->getEndTime());
             })
         ;
 
@@ -1039,45 +1074,7 @@ public function testShouldAllowInterruptConsumingOnPostConsume()
         $queueConsumer->consume();
     }
 
-    public function testShouldNotCloseContextWhenConsumptionInterruptedByException()
-    {
-        $expectedException = new \Exception();
-
-        $subscriptionConsumerMock = new DummySubscriptionConsumer();
-        $subscriptionConsumerMock->addMessage($this->createMessageMock(), 'foo_queue');
-
-        $consumerStub = $this->createConsumerStub('foo_queue');
-
-        $contextStub = $this->createContextStub($consumerStub);
-        $contextStub
-            ->expects($this->never())
-            ->method('close')
-        ;
-
-        $processorMock = $this->createProcessorMock();
-        $processorMock
-            ->expects($this->once())
-            ->method('process')
-            ->willThrowException($expectedException)
-        ;
-
-        $queueConsumer = new QueueConsumer($contextStub, new BreakCycleExtension(1));
-        $queueConsumer->setFallbackSubscriptionConsumer($subscriptionConsumerMock);
-        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
-
-        try {
-            $queueConsumer->consume();
-        } catch (\Exception $e) {
-            $this->assertSame($expectedException, $e);
-            $this->assertNull($e->getPrevious());
-
-            return;
-        }
-
-        $this->fail('Exception throw is expected.');
-    }
-
-    public function testShouldSetMainExceptionAsPreviousToExceptionThrownOnInterrupt()
+    public function testShouldSetMainExceptionAsPreviousToExceptionThrownOnProcessorException()
     {
         $mainException = new \Exception();
         $expectedException = new \Exception();
@@ -1099,7 +1096,7 @@ public function testShouldSetMainExceptionAsPreviousToExceptionThrownOnInterrupt
         $extension = $this->createExtension();
         $extension
             ->expects($this->atLeastOnce())
-            ->method('onInterrupted')
+            ->method('onProcessorException')
             ->willThrowException($expectedException)
         ;
 
@@ -1144,28 +1141,13 @@ public function testShouldAllowInterruptConsumingOnPostMessageReceived()
             ->method('onPostMessageReceived')
             ->with($this->isInstanceOf(PostMessageReceived::class))
             ->willReturnCallback(function (PostMessageReceived $context) {
-                $context->interruptExecution(true);
+                $context->interruptExecution();
             })
         ;
         $extension
             ->expects($this->atLeastOnce())
-            ->method('onInterrupted')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use (
-                $contextStub,
-                $consumerStub,
-                $processorMock,
-                $expectedMessage
-            ) {
-                $this->assertSame($contextStub, $context->getInteropContext());
-                $this->assertSame($consumerStub, $context->getConsumer());
-                $this->assertSame($processorMock, $context->getProcessor());
-                $this->assertSame($expectedMessage, $context->getInteropMessage());
-                $this->assertInstanceOf(NullLogger::class, $context->getLogger());
-                $this->assertNull($context->getException());
-                $this->assertSame(Result::ACK, $context->getResult());
-                $this->assertTrue($context->isExecutionInterrupted());
-            })
+            ->method('onEnd')
+            ->with($this->isInstanceOf(End::class))
         ;
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
@@ -1176,7 +1158,7 @@ public function testShouldAllowInterruptConsumingOnPostMessageReceived()
         $queueConsumer->consume();
     }
 
-    public function testShouldCallOnInterruptedIfExceptionThrow()
+    public function testShouldNotCallOnEndIfExceptionThrow()
     {
         $expectedException = new \Exception('Process failed');
         $expectedMessage = $this->createMessageMock();
@@ -1197,25 +1179,8 @@ public function testShouldCallOnInterruptedIfExceptionThrow()
 
         $extension = $this->createExtension();
         $extension
-            ->expects($this->atLeastOnce())
-            ->method('onInterrupted')
-            ->with($this->isInstanceOf(Context::class))
-            ->willReturnCallback(function (Context $context) use (
-                $contextStub,
-                $consumerStub,
-                $processorMock,
-                $expectedMessage,
-                $expectedException
-            ) {
-                $this->assertSame($contextStub, $context->getInteropContext());
-                $this->assertSame($consumerStub, $context->getConsumer());
-                $this->assertSame($processorMock, $context->getProcessor());
-                $this->assertSame($expectedMessage, $context->getInteropMessage());
-                $this->assertSame($expectedException, $context->getException());
-                $this->assertInstanceOf(NullLogger::class, $context->getLogger());
-                $this->assertNull($context->getResult());
-                $this->assertTrue($context->isExecutionInterrupted());
-            })
+            ->expects($this->never())
+            ->method('onEnd')
         ;
 
         $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
@@ -1458,11 +1423,6 @@ private function createContextStub(Consumer $consumer = null): InteropContext
             })
         ;
 
-        $context
-            ->expects($this->any())
-            ->method('close')
-        ;
-
         return $context;
     }
 

From 9403a28ee5b396ad1baac8cc4b99399b93baba88 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 8 Oct 2018 13:51:00 +0300
Subject: [PATCH 158/337] [consumption] Implement only required extension
 interfaces. Remvoe EmptyExtensionTrait.

---
 .../DelayRedeliveredMessageExtension.php      |   7 +-
 .../ExclusiveCommandExtension.php             |   7 +-
 .../FlushSpoolProducerExtension.php           |   8 +-
 .../SetRouterPropertiesExtension.php          |   7 +-
 .../SetupBrokerExtension.php                  |   7 +-
 Consumption/ChainExtension.php                | 119 +++++++++++++++---
 Consumption/EmptyExtensionTrait.php           |  52 --------
 .../LimitConsumedMessagesExtension.php        |   8 +-
 .../LimitConsumerMemoryExtension.php          |   9 +-
 .../LimitConsumptionTimeExtension.php         |   9 +-
 Consumption/Extension/LoggerExtension.php     |   8 +-
 Consumption/Extension/NicenessExtension.php   |   7 +-
 Consumption/Extension/ReplyExtension.php      |   7 +-
 Consumption/Extension/SignalExtension.php     |  15 +--
 .../ExclusiveCommandExtensionTest.php         |   6 +-
 .../FlushSpoolProducerExtensionTest.php       |  12 +-
 .../SetRouterPropertiesExtensionTest.php      |   6 +-
 .../SetupBrokerExtensionTest.php              |  10 +-
 Tests/Consumption/EmptyExtensionTraitTest.php |  20 ---
 .../Extension/LoggerExtensionTest.php         |  18 ++-
 .../Extension/NicenessExtensionTest.php       |   2 +-
 .../Extension/ReplyExtensionTest.php          |   6 +-
 .../Consumption/Mock/BreakCycleExtension.php  |  38 +++++-
 23 files changed, 206 insertions(+), 182 deletions(-)
 delete mode 100644 Consumption/EmptyExtensionTrait.php
 delete mode 100644 Tests/Consumption/EmptyExtensionTraitTest.php

diff --git a/Client/ConsumptionExtension/DelayRedeliveredMessageExtension.php b/Client/ConsumptionExtension/DelayRedeliveredMessageExtension.php
index 4ac26ff..77e3bc4 100644
--- a/Client/ConsumptionExtension/DelayRedeliveredMessageExtension.php
+++ b/Client/ConsumptionExtension/DelayRedeliveredMessageExtension.php
@@ -4,14 +4,11 @@
 
 use Enqueue\Client\DriverInterface;
 use Enqueue\Consumption\Context\MessageReceived;
-use Enqueue\Consumption\EmptyExtensionTrait;
-use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\MessageReceivedExtensionInterface;
 use Enqueue\Consumption\Result;
 
-class DelayRedeliveredMessageExtension implements ExtensionInterface
+class DelayRedeliveredMessageExtension implements MessageReceivedExtensionInterface
 {
-    use EmptyExtensionTrait;
-
     const PROPERTY_REDELIVER_COUNT = 'enqueue.redelivery_count';
 
     /**
diff --git a/Client/ConsumptionExtension/ExclusiveCommandExtension.php b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
index 5397456..a76e49f 100644
--- a/Client/ConsumptionExtension/ExclusiveCommandExtension.php
+++ b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
@@ -6,13 +6,10 @@
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Route;
 use Enqueue\Consumption\Context\MessageReceived;
-use Enqueue\Consumption\EmptyExtensionTrait as ConsumptionEmptyExtensionTrait;
-use Enqueue\Consumption\ExtensionInterface as ConsumptionExtensionInterface;
+use Enqueue\Consumption\MessageReceivedExtensionInterface;
 
-final class ExclusiveCommandExtension implements ConsumptionExtensionInterface
+final class ExclusiveCommandExtension implements MessageReceivedExtensionInterface
 {
-    use ConsumptionEmptyExtensionTrait;
-
     /**
      * @var DriverInterface
      */
diff --git a/Client/ConsumptionExtension/FlushSpoolProducerExtension.php b/Client/ConsumptionExtension/FlushSpoolProducerExtension.php
index 19c83d0..b7e9ca0 100644
--- a/Client/ConsumptionExtension/FlushSpoolProducerExtension.php
+++ b/Client/ConsumptionExtension/FlushSpoolProducerExtension.php
@@ -5,13 +5,11 @@
 use Enqueue\Client\SpoolProducer;
 use Enqueue\Consumption\Context\End;
 use Enqueue\Consumption\Context\PostMessageReceived;
-use Enqueue\Consumption\EmptyExtensionTrait;
-use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\EndExtensionInterface;
+use Enqueue\Consumption\PostMessageReceivedExtensionInterface;
 
-class FlushSpoolProducerExtension implements ExtensionInterface
+class FlushSpoolProducerExtension implements PostMessageReceivedExtensionInterface, EndExtensionInterface
 {
-    use EmptyExtensionTrait;
-
     /**
      * @var SpoolProducer
      */
diff --git a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
index cc3d8d3..1655886 100644
--- a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
+++ b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
@@ -5,13 +5,10 @@
 use Enqueue\Client\Config;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Consumption\Context\MessageReceived;
-use Enqueue\Consumption\EmptyExtensionTrait;
-use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\MessageReceivedExtensionInterface;
 
-class SetRouterPropertiesExtension implements ExtensionInterface
+class SetRouterPropertiesExtension implements MessageReceivedExtensionInterface
 {
-    use EmptyExtensionTrait;
-
     /**
      * @var DriverInterface
      */
diff --git a/Client/ConsumptionExtension/SetupBrokerExtension.php b/Client/ConsumptionExtension/SetupBrokerExtension.php
index d055ffd..e355807 100644
--- a/Client/ConsumptionExtension/SetupBrokerExtension.php
+++ b/Client/ConsumptionExtension/SetupBrokerExtension.php
@@ -4,13 +4,10 @@
 
 use Enqueue\Client\DriverInterface;
 use Enqueue\Consumption\Context\Start;
-use Enqueue\Consumption\EmptyExtensionTrait;
-use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\StartExtensionInterface;
 
-class SetupBrokerExtension implements ExtensionInterface
+class SetupBrokerExtension implements StartExtensionInterface
 {
-    use EmptyExtensionTrait;
-
     /**
      * @var DriverInterface
      */
diff --git a/Consumption/ChainExtension.php b/Consumption/ChainExtension.php
index 32e3f18..68c4dd1 100644
--- a/Consumption/ChainExtension.php
+++ b/Consumption/ChainExtension.php
@@ -14,83 +14,166 @@
 
 class ChainExtension implements ExtensionInterface
 {
-    use EmptyExtensionTrait;
-
-    /**
-     * @var ExtensionInterface[]
-     */
-    private $extensions;
+    private $startExtensions;
+    private $preSubscribeExtensions;
+    private $preConsumeExtensions;
+    private $messageReceivedExtensions;
+    private $messageResultExtensions;
+    private $postMessageReceivedExtensions;
+    private $processorExceptionExtensions;
+    private $postConsumeExtensions;
+    private $endExtensions;
 
     /**
      * @param ExtensionInterface[] $extensions
      */
     public function __construct(array $extensions)
     {
-        $this->extensions = [];
-        array_walk($extensions, function (ExtensionInterface $extension) {
-            $this->extensions[] = $extension;
+        $this->startExtensions = [];
+        $this->preSubscribeExtensions = [];
+        $this->preConsumeExtensions = [];
+        $this->messageReceivedExtensions = [];
+        $this->messageResultExtensions = [];
+        $this->postMessageReceivedExtensions = [];
+        $this->processorExceptionExtensions = [];
+        $this->postConsumeExtensions = [];
+        $this->endExtensions = [];
+
+        array_walk($extensions, function ($extension) {
+            if ($extension instanceof ExtensionInterface) {
+                $this->startExtensions[] = $extension;
+                $this->preSubscribeExtensions[] = $extension;
+                $this->preConsumeExtensions[] = $extension;
+                $this->messageReceivedExtensions[] = $extension;
+                $this->messageResultExtensions[] = $extension;
+                $this->postMessageReceivedExtensions[] = $extension;
+                $this->processorExceptionExtensions[] = $extension;
+                $this->postConsumeExtensions[] = $extension;
+                $this->endExtensions[] = $extension;
+
+                return;
+            }
+
+            $extensionValid = false;
+            if ($extension instanceof StartExtensionInterface) {
+                $this->startExtensions[] = $extension;
+
+                $extensionValid = true;
+            }
+
+            if ($extension instanceof PreSubscribeExtensionInterface) {
+                $this->preSubscribeExtensions[] = $extension;
+
+                $extensionValid = true;
+            }
+
+            if ($extension instanceof PreConsumeExtensionInterface) {
+                $this->preConsumeExtensions[] = $extension;
+
+                $extensionValid = true;
+            }
+
+            if ($extension instanceof MessageReceivedExtensionInterface) {
+                $this->messageReceivedExtensions[] = $extension;
+
+                $extensionValid = true;
+            }
+
+            if ($extension instanceof MessageResultExtensionInterface) {
+                $this->messageResultExtensions[] = $extension;
+
+                $extensionValid = true;
+            }
+
+            if ($extension instanceof ProcessorExceptionExtensionInterface) {
+                $this->processorExceptionExtensions[] = $extension;
+
+                $extensionValid = true;
+            }
+
+            if ($extension instanceof PostMessageReceivedExtensionInterface) {
+                $this->postMessageReceivedExtensions[] = $extension;
+
+                $extensionValid = true;
+            }
+
+            if ($extension instanceof PostConsumeExtensionInterface) {
+                $this->postConsumeExtensions[] = $extension;
+
+                $extensionValid = true;
+            }
+
+            if ($extension instanceof EndExtensionInterface) {
+                $this->endExtensions[] = $extension;
+
+                $extensionValid = true;
+            }
+
+            if (false == $extensionValid) {
+                throw new \LogicException('Invalid extension given');
+            }
         });
     }
 
     public function onStart(Start $context): void
     {
-        foreach ($this->extensions as $extension) {
+        foreach ($this->startExtensions as $extension) {
             $extension->onStart($context);
         }
     }
 
     public function onPreSubscribe(PreSubscribe $context): void
     {
-        foreach ($this->extensions as $extension) {
+        foreach ($this->preSubscribeExtensions as $extension) {
             $extension->onPreSubscribe($context);
         }
     }
 
     public function onPreConsume(PreConsume $context): void
     {
-        foreach ($this->extensions as $extension) {
+        foreach ($this->preConsumeExtensions as $extension) {
             $extension->onPreConsume($context);
         }
     }
 
     public function onMessageReceived(MessageReceived $context): void
     {
-        foreach ($this->extensions as $extension) {
+        foreach ($this->messageReceivedExtensions as $extension) {
             $extension->onMessageReceived($context);
         }
     }
 
     public function onResult(MessageResult $context): void
     {
-        foreach ($this->extensions as $extension) {
+        foreach ($this->messageResultExtensions as $extension) {
             $extension->onResult($context);
         }
     }
 
     public function onProcessorException(ProcessorException $context): void
     {
-        foreach ($this->extensions as $extension) {
+        foreach ($this->processorExceptionExtensions as $extension) {
             $extension->onProcessorException($context);
         }
     }
 
     public function onPostMessageReceived(PostMessageReceived $context): void
     {
-        foreach ($this->extensions as $extension) {
+        foreach ($this->postMessageReceivedExtensions as $extension) {
             $extension->onPostMessageReceived($context);
         }
     }
 
     public function onPostConsume(PostConsume $context): void
     {
-        foreach ($this->extensions as $extension) {
+        foreach ($this->postConsumeExtensions as $extension) {
             $extension->onPostConsume($context);
         }
     }
 
     public function onEnd(End $context): void
     {
-        foreach ($this->extensions as $extension) {
+        foreach ($this->endExtensions as $extension) {
             $extension->onEnd($context);
         }
     }
diff --git a/Consumption/EmptyExtensionTrait.php b/Consumption/EmptyExtensionTrait.php
deleted file mode 100644
index ab21ee5..0000000
--- a/Consumption/EmptyExtensionTrait.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-namespace Enqueue\Consumption;
-
-use Enqueue\Consumption\Context\End;
-use Enqueue\Consumption\Context\MessageReceived;
-use Enqueue\Consumption\Context\MessageResult;
-use Enqueue\Consumption\Context\PostConsume;
-use Enqueue\Consumption\Context\PostMessageReceived;
-use Enqueue\Consumption\Context\PreConsume;
-use Enqueue\Consumption\Context\PreSubscribe;
-use Enqueue\Consumption\Context\ProcessorException;
-use Enqueue\Consumption\Context\Start;
-
-trait EmptyExtensionTrait
-{
-    public function onStart(Start $context): void
-    {
-    }
-
-    public function onPreSubscribe(PreSubscribe $preSubscribe): void
-    {
-    }
-
-    public function onPreConsume(PreConsume $context): void
-    {
-    }
-
-    public function onMessageReceived(MessageReceived $context): void
-    {
-    }
-
-    public function onPostMessageReceived(PostMessageReceived $context): void
-    {
-    }
-
-    public function onResult(MessageResult $context): void
-    {
-    }
-
-    public function onProcessorException(ProcessorException $context): void
-    {
-    }
-
-    public function onPostConsume(PostConsume $context): void
-    {
-    }
-
-    public function onEnd(End $context): void
-    {
-    }
-}
diff --git a/Consumption/Extension/LimitConsumedMessagesExtension.php b/Consumption/Extension/LimitConsumedMessagesExtension.php
index dbd0cf2..e89957a 100644
--- a/Consumption/Extension/LimitConsumedMessagesExtension.php
+++ b/Consumption/Extension/LimitConsumedMessagesExtension.php
@@ -4,14 +4,12 @@
 
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
-use Enqueue\Consumption\EmptyExtensionTrait;
-use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\PostMessageReceivedExtensionInterface;
+use Enqueue\Consumption\PreConsumeExtensionInterface;
 use Psr\Log\LoggerInterface;
 
-class LimitConsumedMessagesExtension implements ExtensionInterface
+class LimitConsumedMessagesExtension implements PreConsumeExtensionInterface, PostMessageReceivedExtensionInterface
 {
-    use EmptyExtensionTrait;
-
     /**
      * @var int
      */
diff --git a/Consumption/Extension/LimitConsumerMemoryExtension.php b/Consumption/Extension/LimitConsumerMemoryExtension.php
index fabdfcc..13d4a28 100644
--- a/Consumption/Extension/LimitConsumerMemoryExtension.php
+++ b/Consumption/Extension/LimitConsumerMemoryExtension.php
@@ -5,14 +5,13 @@
 use Enqueue\Consumption\Context\PostConsume;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
-use Enqueue\Consumption\EmptyExtensionTrait;
-use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\PostConsumeExtensionInterface;
+use Enqueue\Consumption\PostMessageReceivedExtensionInterface;
+use Enqueue\Consumption\PreConsumeExtensionInterface;
 use Psr\Log\LoggerInterface;
 
-class LimitConsumerMemoryExtension implements ExtensionInterface
+class LimitConsumerMemoryExtension implements PreConsumeExtensionInterface, PostMessageReceivedExtensionInterface, PostConsumeExtensionInterface
 {
-    use EmptyExtensionTrait;
-
     /**
      * @var int
      */
diff --git a/Consumption/Extension/LimitConsumptionTimeExtension.php b/Consumption/Extension/LimitConsumptionTimeExtension.php
index e360e31..f0a5779 100644
--- a/Consumption/Extension/LimitConsumptionTimeExtension.php
+++ b/Consumption/Extension/LimitConsumptionTimeExtension.php
@@ -5,14 +5,13 @@
 use Enqueue\Consumption\Context\PostConsume;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
-use Enqueue\Consumption\EmptyExtensionTrait;
-use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\PostConsumeExtensionInterface;
+use Enqueue\Consumption\PostMessageReceivedExtensionInterface;
+use Enqueue\Consumption\PreConsumeExtensionInterface;
 use Psr\Log\LoggerInterface;
 
-class LimitConsumptionTimeExtension implements ExtensionInterface
+class LimitConsumptionTimeExtension implements PreConsumeExtensionInterface, PostConsumeExtensionInterface, PostMessageReceivedExtensionInterface
 {
-    use EmptyExtensionTrait;
-
     /**
      * @var \DateTime
      */
diff --git a/Consumption/Extension/LoggerExtension.php b/Consumption/Extension/LoggerExtension.php
index a3b11eb..a240a12 100644
--- a/Consumption/Extension/LoggerExtension.php
+++ b/Consumption/Extension/LoggerExtension.php
@@ -4,17 +4,15 @@
 
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\Start;
-use Enqueue\Consumption\EmptyExtensionTrait;
-use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\PostMessageReceivedExtensionInterface;
 use Enqueue\Consumption\Result;
+use Enqueue\Consumption\StartExtensionInterface;
 use Interop\Queue\Message as InteropMessage;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
-class LoggerExtension implements ExtensionInterface
+class LoggerExtension implements StartExtensionInterface, PostMessageReceivedExtensionInterface
 {
-    use EmptyExtensionTrait;
-
     /**
      * @var LoggerInterface
      */
diff --git a/Consumption/Extension/NicenessExtension.php b/Consumption/Extension/NicenessExtension.php
index 6df4f14..983e04b 100644
--- a/Consumption/Extension/NicenessExtension.php
+++ b/Consumption/Extension/NicenessExtension.php
@@ -3,13 +3,10 @@
 namespace Enqueue\Consumption\Extension;
 
 use Enqueue\Consumption\Context\Start;
-use Enqueue\Consumption\EmptyExtensionTrait;
-use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\StartExtensionInterface;
 
-class NicenessExtension implements ExtensionInterface
+class NicenessExtension implements StartExtensionInterface
 {
-    use EmptyExtensionTrait;
-
     /**
      * @var int
      */
diff --git a/Consumption/Extension/ReplyExtension.php b/Consumption/Extension/ReplyExtension.php
index d7f7dcf..c1ac19b 100644
--- a/Consumption/Extension/ReplyExtension.php
+++ b/Consumption/Extension/ReplyExtension.php
@@ -3,14 +3,11 @@
 namespace Enqueue\Consumption\Extension;
 
 use Enqueue\Consumption\Context\PostMessageReceived;
-use Enqueue\Consumption\EmptyExtensionTrait;
-use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\PostMessageReceivedExtensionInterface;
 use Enqueue\Consumption\Result;
 
-class ReplyExtension implements ExtensionInterface
+class ReplyExtension implements PostMessageReceivedExtensionInterface
 {
-    use EmptyExtensionTrait;
-
     public function onPostMessageReceived(PostMessageReceived $context): void
     {
         $replyTo = $context->getMessage()->getReplyTo();
diff --git a/Consumption/Extension/SignalExtension.php b/Consumption/Extension/SignalExtension.php
index 3abfb3c..67354b9 100644
--- a/Consumption/Extension/SignalExtension.php
+++ b/Consumption/Extension/SignalExtension.php
@@ -2,20 +2,19 @@
 
 namespace Enqueue\Consumption\Extension;
 
-use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\PostConsume;
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Context\Start;
-use Enqueue\Consumption\EmptyExtensionTrait;
 use Enqueue\Consumption\Exception\LogicException;
-use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\PostConsumeExtensionInterface;
+use Enqueue\Consumption\PostMessageReceivedExtensionInterface;
+use Enqueue\Consumption\PreConsumeExtensionInterface;
+use Enqueue\Consumption\StartExtensionInterface;
 use Psr\Log\LoggerInterface;
 
-class SignalExtension implements ExtensionInterface
+class SignalExtension implements StartExtensionInterface, PreConsumeExtensionInterface, PostMessageReceivedExtensionInterface, PostConsumeExtensionInterface
 {
-    use EmptyExtensionTrait;
-
     /**
      * @var bool
      */
@@ -51,10 +50,6 @@ public function onPreConsume(PreConsume $context): void
         }
     }
 
-    public function onMessageReceived(MessageReceived $context): void
-    {
-    }
-
     public function onPostMessageReceived(PostMessageReceived $context): void
     {
         if ($this->shouldBeStopped($context->getLogger())) {
diff --git a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
index 11e1c10..3e1ef2b 100644
--- a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
@@ -8,7 +8,7 @@
 use Enqueue\Client\Route;
 use Enqueue\Client\RouteCollection;
 use Enqueue\Consumption\Context\MessageReceived;
-use Enqueue\Consumption\ExtensionInterface as ConsumptionExtensionInterface;
+use Enqueue\Consumption\MessageReceivedExtensionInterface;
 use Enqueue\Null\NullMessage;
 use Enqueue\Null\NullQueue;
 use Enqueue\Test\ClassExtensionTrait;
@@ -22,9 +22,9 @@ class ExclusiveCommandExtensionTest extends TestCase
 {
     use ClassExtensionTrait;
 
-    public function testShouldImplementConsumptionExtensionInterface()
+    public function testShouldImplementMessageReceivedExtensionInterface()
     {
-        $this->assertClassImplements(ConsumptionExtensionInterface::class, ExclusiveCommandExtension::class);
+        $this->assertClassImplements(MessageReceivedExtensionInterface::class, ExclusiveCommandExtension::class);
     }
 
     public function testShouldBeFinal()
diff --git a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
index e6df875..4f3af65 100644
--- a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
@@ -6,7 +6,8 @@
 use Enqueue\Client\SpoolProducer;
 use Enqueue\Consumption\Context\End;
 use Enqueue\Consumption\Context\PostMessageReceived;
-use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\EndExtensionInterface;
+use Enqueue\Consumption\PostMessageReceivedExtensionInterface;
 use Enqueue\Test\ClassExtensionTrait;
 use Interop\Queue\Context;
 use Interop\Queue\Message;
@@ -17,9 +18,14 @@ class FlushSpoolProducerExtensionTest extends TestCase
 {
     use ClassExtensionTrait;
 
-    public function testShouldImplementExtensionInterface()
+    public function testShouldImplementPostMessageReceivedExtensionInterface()
     {
-        $this->assertClassImplements(ExtensionInterface::class, FlushSpoolProducerExtension::class);
+        $this->assertClassImplements(PostMessageReceivedExtensionInterface::class, FlushSpoolProducerExtension::class);
+    }
+
+    public function testShouldImplementEndExtensionInterface()
+    {
+        $this->assertClassImplements(EndExtensionInterface::class, FlushSpoolProducerExtension::class);
     }
 
     public function testCouldBeConstructedWithSpoolProducerAsFirstArgument()
diff --git a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
index dc12c22..be5609d 100644
--- a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
@@ -6,7 +6,7 @@
 use Enqueue\Client\ConsumptionExtension\SetRouterPropertiesExtension;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Consumption\Context\MessageReceived;
-use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\MessageReceivedExtensionInterface;
 use Enqueue\Null\NullMessage;
 use Enqueue\Null\NullQueue;
 use Enqueue\Test\ClassExtensionTrait;
@@ -20,9 +20,9 @@ class SetRouterPropertiesExtensionTest extends TestCase
 {
     use ClassExtensionTrait;
 
-    public function testShouldImplementExtensionInterface()
+    public function testShouldImplementMessageReceivedExtensionInterface()
     {
-        $this->assertClassImplements(ExtensionInterface::class, SetRouterPropertiesExtension::class);
+        $this->assertClassImplements(MessageReceivedExtensionInterface::class, SetRouterPropertiesExtension::class);
     }
 
     public function testCouldBeConstructedWithRequiredArguments()
diff --git a/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php b/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php
index 6ab0a9e..014c232 100644
--- a/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php
@@ -5,7 +5,7 @@
 use Enqueue\Client\ConsumptionExtension\SetupBrokerExtension;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Consumption\Context\Start;
-use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\StartExtensionInterface;
 use Enqueue\Test\ClassExtensionTrait;
 use Interop\Queue\Context as InteropContext;
 use PHPUnit\Framework\TestCase;
@@ -15,9 +15,9 @@ class SetupBrokerExtensionTest extends TestCase
 {
     use ClassExtensionTrait;
 
-    public function testShouldImplementExtensionInterface()
+    public function testShouldImplementStartExtensionInterface()
     {
-        $this->assertClassImplements(ExtensionInterface::class, SetupBrokerExtension::class);
+        $this->assertClassImplements(StartExtensionInterface::class, SetupBrokerExtension::class);
     }
 
     public function testCouldBeConstructedWithRequiredArguments()
@@ -36,7 +36,7 @@ public function testShouldSetupBroker()
             ->with($this->identicalTo($logger))
         ;
 
-        $context = new Start($this->createMock(InteropContext::class), $logger, [], 0, 0, 0);
+        $context = new Start($this->createMock(InteropContext::class), $logger, [], 0, 0);
 
         $extension = new SetupBrokerExtension($driver);
         $extension->onStart($context);
@@ -53,7 +53,7 @@ public function testShouldSetupBrokerOnlyOnce()
             ->with($this->identicalTo($logger))
         ;
 
-        $context = new Start($this->createMock(InteropContext::class), $logger, [], 0, 0, 0);
+        $context = new Start($this->createMock(InteropContext::class), $logger, [], 0, 0);
 
         $extension = new SetupBrokerExtension($driver);
         $extension->onStart($context);
diff --git a/Tests/Consumption/EmptyExtensionTraitTest.php b/Tests/Consumption/EmptyExtensionTraitTest.php
deleted file mode 100644
index 32ea861..0000000
--- a/Tests/Consumption/EmptyExtensionTraitTest.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-namespace Enqueue\Tests\Consumption;
-
-use Enqueue\Consumption\EmptyExtensionTrait;
-use Enqueue\Consumption\ExtensionInterface;
-use PHPUnit\Framework\TestCase;
-
-class EmptyExtensionTraitTest extends TestCase
-{
-    public function testTraitMustImplementOrExtensionMethods()
-    {
-        new EmptyExtension();
-    }
-}
-
-class EmptyExtension implements ExtensionInterface
-{
-    use EmptyExtensionTrait;
-}
diff --git a/Tests/Consumption/Extension/LoggerExtensionTest.php b/Tests/Consumption/Extension/LoggerExtensionTest.php
index 94cba2e..37ef696 100644
--- a/Tests/Consumption/Extension/LoggerExtensionTest.php
+++ b/Tests/Consumption/Extension/LoggerExtensionTest.php
@@ -5,8 +5,9 @@
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\Extension\LoggerExtension;
-use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\PostMessageReceivedExtensionInterface;
 use Enqueue\Consumption\Result;
+use Enqueue\Consumption\StartExtensionInterface;
 use Enqueue\Null\NullMessage;
 use Enqueue\Test\ClassExtensionTrait;
 use Interop\Queue\Consumer;
@@ -20,9 +21,14 @@ class LoggerExtensionTest extends TestCase
 {
     use ClassExtensionTrait;
 
-    public function testShouldImplementExtensionInterface()
+    public function testShouldImplementStartExtensionInterface()
     {
-        $this->assertClassImplements(ExtensionInterface::class, LoggerExtension::class);
+        $this->assertClassImplements(StartExtensionInterface::class, LoggerExtension::class);
+    }
+
+    public function testShouldImplementPostMessageReceivedExtensionInterface()
+    {
+        $this->assertClassImplements(PostMessageReceivedExtensionInterface::class, LoggerExtension::class);
     }
 
     public function testCouldBeConstructedWithLoggerAsFirstArgument()
@@ -36,7 +42,7 @@ public function testShouldSetLoggerToContextOnStart()
 
         $extension = new LoggerExtension($logger);
 
-        $context = new Start($this->createContextMock(), new NullLogger(), [], 0, 0, 0);
+        $context = new Start($this->createContextMock(), new NullLogger(), [], 0, 0);
 
         $extension->onStart($context);
 
@@ -54,7 +60,7 @@ public function testShouldAddInfoMessageOnStart()
 
         $extension = new LoggerExtension($logger);
 
-        $context = new Start($this->createContextMock(), new NullLogger(), [], 0, 0, 0);
+        $context = new Start($this->createContextMock(), new NullLogger(), [], 0, 0);
 
         $extension->onStart($context);
     }
@@ -193,7 +199,7 @@ public function testShouldNotSetLoggerIfOneHasBeenSetOnStart()
 
         $extension = new LoggerExtension($logger);
 
-        $context = new Start($this->createContextMock(), $alreadySetLogger, [], 0, 0, 0);
+        $context = new Start($this->createContextMock(), $alreadySetLogger, [], 0, 0);
 
         $extension->onStart($context);
     }
diff --git a/Tests/Consumption/Extension/NicenessExtensionTest.php b/Tests/Consumption/Extension/NicenessExtensionTest.php
index 26bbf03..5b1834a 100644
--- a/Tests/Consumption/Extension/NicenessExtensionTest.php
+++ b/Tests/Consumption/Extension/NicenessExtensionTest.php
@@ -26,7 +26,7 @@ public function testShouldThrowWarningOnInvalidArgument()
         $this->expectException(\InvalidArgumentException::class);
         $this->expectExceptionMessage('proc_nice(): Only a super user may attempt to increase the priority of a process');
 
-        $context = new Start($this->createContextMock(), new NullLogger(), [], 0, 0, 0);
+        $context = new Start($this->createContextMock(), new NullLogger(), [], 0, 0);
 
         $extension = new NicenessExtension(-1);
         $extension->onStart($context);
diff --git a/Tests/Consumption/Extension/ReplyExtensionTest.php b/Tests/Consumption/Extension/ReplyExtensionTest.php
index c027c4b..e78a745 100644
--- a/Tests/Consumption/Extension/ReplyExtensionTest.php
+++ b/Tests/Consumption/Extension/ReplyExtensionTest.php
@@ -4,7 +4,7 @@
 
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Extension\ReplyExtension;
-use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\PostMessageReceivedExtensionInterface;
 use Enqueue\Consumption\Result;
 use Enqueue\Null\NullMessage;
 use Enqueue\Null\NullQueue;
@@ -18,9 +18,9 @@ class ReplyExtensionTest extends TestCase
 {
     use ClassExtensionTrait;
 
-    public function testShouldImplementExtensionInterface()
+    public function testShouldImplementPostMessageReceivedExtensionInterface()
     {
-        $this->assertClassImplements(ExtensionInterface::class, ReplyExtension::class);
+        $this->assertClassImplements(PostMessageReceivedExtensionInterface::class, ReplyExtension::class);
     }
 
     public function testCouldBeConstructedWithoutAnyArguments()
diff --git a/Tests/Consumption/Mock/BreakCycleExtension.php b/Tests/Consumption/Mock/BreakCycleExtension.php
index 983e4e1..831c7a9 100644
--- a/Tests/Consumption/Mock/BreakCycleExtension.php
+++ b/Tests/Consumption/Mock/BreakCycleExtension.php
@@ -2,15 +2,19 @@
 
 namespace Enqueue\Tests\Consumption\Mock;
 
+use Enqueue\Consumption\Context\End;
+use Enqueue\Consumption\Context\MessageReceived;
+use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PostConsume;
 use Enqueue\Consumption\Context\PostMessageReceived;
-use Enqueue\Consumption\EmptyExtensionTrait;
+use Enqueue\Consumption\Context\PreConsume;
+use Enqueue\Consumption\Context\PreSubscribe;
+use Enqueue\Consumption\Context\ProcessorException;
+use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\ExtensionInterface;
 
 class BreakCycleExtension implements ExtensionInterface
 {
-    use EmptyExtensionTrait;
-
     protected $cycles = 1;
 
     private $limit;
@@ -29,6 +33,34 @@ public function onPostMessageReceived(PostMessageReceived $context): void
         }
     }
 
+    public function onEnd(End $context): void
+    {
+    }
+
+    public function onMessageReceived(MessageReceived $context): void
+    {
+    }
+
+    public function onResult(MessageResult $context): void
+    {
+    }
+
+    public function onPreConsume(PreConsume $context): void
+    {
+    }
+
+    public function onPreSubscribe(PreSubscribe $context): void
+    {
+    }
+
+    public function onProcessorException(ProcessorException $context): void
+    {
+    }
+
+    public function onStart(Start $context): void
+    {
+    }
+
     public function onPostConsume(PostConsume $context): void
     {
         if ($this->cycles >= $this->limit) {

From e6ccf50845db9f473e564725cd4afe744dd675c4 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 8 Oct 2018 17:12:40 +0300
Subject: [PATCH 159/337] [consumption] add InitLogger extension point. Before
 start.

---
 Consumption/ChainExtension.php                |  20 +-
 Consumption/Context/InitLogger.php            |  28 +++
 Consumption/Context/Start.php                 |   5 -
 Consumption/Extension/LoggerExtension.php     |  66 +------
 Consumption/ExtensionInterface.php            |   2 +-
 Consumption/InitLoggerExtensionInterface.php  |  14 ++
 Consumption/QueueConsumer.php                 |  47 ++---
 Consumption/QueueConsumerInterface.php        |   2 +-
 .../LimitsExtensionsCommandTrait.php          |   3 -
 Tests/Consumption/ChainExtensionTest.php      |  23 +++
 .../Extension/LoggerExtensionTest.php         | 181 ++----------------
 .../Consumption/Mock/BreakCycleExtension.php  |   5 +
 Tests/Consumption/QueueConsumerTest.php       |  46 ++++-
 13 files changed, 182 insertions(+), 260 deletions(-)
 create mode 100644 Consumption/Context/InitLogger.php
 create mode 100644 Consumption/InitLoggerExtensionInterface.php

diff --git a/Consumption/ChainExtension.php b/Consumption/ChainExtension.php
index 68c4dd1..e497bd9 100644
--- a/Consumption/ChainExtension.php
+++ b/Consumption/ChainExtension.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Consumption;
 
 use Enqueue\Consumption\Context\End;
+use Enqueue\Consumption\Context\InitLogger;
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PostConsume;
@@ -15,6 +16,7 @@
 class ChainExtension implements ExtensionInterface
 {
     private $startExtensions;
+    private $initLoggerExtensions;
     private $preSubscribeExtensions;
     private $preConsumeExtensions;
     private $messageReceivedExtensions;
@@ -24,12 +26,10 @@ class ChainExtension implements ExtensionInterface
     private $postConsumeExtensions;
     private $endExtensions;
 
-    /**
-     * @param ExtensionInterface[] $extensions
-     */
     public function __construct(array $extensions)
     {
         $this->startExtensions = [];
+        $this->initLoggerExtensions = [];
         $this->preSubscribeExtensions = [];
         $this->preConsumeExtensions = [];
         $this->messageReceivedExtensions = [];
@@ -42,6 +42,7 @@ public function __construct(array $extensions)
         array_walk($extensions, function ($extension) {
             if ($extension instanceof ExtensionInterface) {
                 $this->startExtensions[] = $extension;
+                $this->initLoggerExtensions[] = $extension;
                 $this->preSubscribeExtensions[] = $extension;
                 $this->preConsumeExtensions[] = $extension;
                 $this->messageReceivedExtensions[] = $extension;
@@ -61,6 +62,12 @@ public function __construct(array $extensions)
                 $extensionValid = true;
             }
 
+            if ($extension instanceof InitLoggerExtensionInterface) {
+                $this->initLoggerExtensions[] = $extension;
+
+                $extensionValid = true;
+            }
+
             if ($extension instanceof PreSubscribeExtensionInterface) {
                 $this->preSubscribeExtensions[] = $extension;
 
@@ -115,6 +122,13 @@ public function __construct(array $extensions)
         });
     }
 
+    public function onInitLogger(InitLogger $context): void
+    {
+        foreach ($this->endExtensions as $extension) {
+            $extension->onInitLogger($context);
+        }
+    }
+
     public function onStart(Start $context): void
     {
         foreach ($this->startExtensions as $extension) {
diff --git a/Consumption/Context/InitLogger.php b/Consumption/Context/InitLogger.php
new file mode 100644
index 0000000..c480572
--- /dev/null
+++ b/Consumption/Context/InitLogger.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Enqueue\Consumption\Context;
+
+use Psr\Log\LoggerInterface;
+
+class InitLogger
+{
+    /**
+     * @var LoggerInterface
+     */
+    private $logger;
+
+    public function __construct(LoggerInterface $logger)
+    {
+        $this->logger = $logger;
+    }
+
+    public function getLogger(): LoggerInterface
+    {
+        return $this->logger;
+    }
+
+    public function changeLogger(LoggerInterface $logger): void
+    {
+        $this->logger = $logger;
+    }
+}
diff --git a/Consumption/Context/Start.php b/Consumption/Context/Start.php
index d1a7be3..cd9f210 100644
--- a/Consumption/Context/Start.php
+++ b/Consumption/Context/Start.php
@@ -62,11 +62,6 @@ public function getLogger(): LoggerInterface
         return $this->logger;
     }
 
-    public function changeLogger(LoggerInterface $logger): void
-    {
-        $this->logger = $logger;
-    }
-
     /**
      * In milliseconds.
      */
diff --git a/Consumption/Extension/LoggerExtension.php b/Consumption/Extension/LoggerExtension.php
index a240a12..0de2739 100644
--- a/Consumption/Extension/LoggerExtension.php
+++ b/Consumption/Extension/LoggerExtension.php
@@ -2,16 +2,11 @@
 
 namespace Enqueue\Consumption\Extension;
 
-use Enqueue\Consumption\Context\PostMessageReceived;
-use Enqueue\Consumption\Context\Start;
-use Enqueue\Consumption\PostMessageReceivedExtensionInterface;
-use Enqueue\Consumption\Result;
-use Enqueue\Consumption\StartExtensionInterface;
-use Interop\Queue\Message as InteropMessage;
+use Enqueue\Consumption\Context\InitLogger;
+use Enqueue\Consumption\InitLoggerExtensionInterface;
 use Psr\Log\LoggerInterface;
-use Psr\Log\NullLogger;
 
-class LoggerExtension implements StartExtensionInterface, PostMessageReceivedExtensionInterface
+class LoggerExtension implements InitLoggerExtensionInterface
 {
     /**
      * @var LoggerInterface
@@ -26,59 +21,14 @@ public function __construct(LoggerInterface $logger)
         $this->logger = $logger;
     }
 
-    public function onStart(Start $context): void
+    public function onInitLogger(InitLogger $context): void
     {
-        if ($context->getLogger() && false == $context->getLogger() instanceof NullLogger) {
-            $context->getLogger()->debug(sprintf(
-                'Skip setting context\'s logger "%s". Another one "%s" has already been set.',
-                get_class($this->logger),
-                get_class($context->getLogger())
-            ));
-        } else {
-            $context->changeLogger($this->logger);
-            $this->logger->debug(sprintf('Set context\'s logger "%s"', get_class($this->logger)));
-        }
-    }
-
-    public function onPostMessageReceived(PostMessageReceived $context): void
-    {
-        if (false == $context->getResult() instanceof Result) {
-            return;
-        }
-
-        /** @var $result Result */
-        $result = $context->getResult();
+        $previousLogger = $context->getLogger();
 
-        switch ($result->getStatus()) {
-            case Result::REJECT:
-            case Result::REQUEUE:
-                if ($result->getReason()) {
-                    $this->logger->error($result->getReason(), $this->messageToLogContext($context->getMessage()));
-                }
-
-                break;
-            case Result::ACK:
-                if ($result->getReason()) {
-                    $this->logger->info($result->getReason(), $this->messageToLogContext($context->getMessage()));
-                }
+        if ($previousLogger !== $this->logger) {
+            $context->changeLogger($this->logger);
 
-                break;
-            default:
-                throw new \LogicException(sprintf('Got unexpected message result. "%s"', $result->getStatus()));
+            $this->logger->debug(sprintf('Change logger from "%s" to "%s"', get_class($previousLogger), get_class($this->logger)));
         }
     }
-
-    /**
-     * @param InteropMessage $message
-     *
-     * @return array
-     */
-    private function messageToLogContext(InteropMessage $message)
-    {
-        return [
-            'body' => $message->getBody(),
-            'headers' => $message->getHeaders(),
-            'properties' => $message->getProperties(),
-        ];
-    }
 }
diff --git a/Consumption/ExtensionInterface.php b/Consumption/ExtensionInterface.php
index b928024..326a98f 100644
--- a/Consumption/ExtensionInterface.php
+++ b/Consumption/ExtensionInterface.php
@@ -2,6 +2,6 @@
 
 namespace Enqueue\Consumption;
 
-interface ExtensionInterface extends StartExtensionInterface, PreSubscribeExtensionInterface, PreConsumeExtensionInterface, MessageReceivedExtensionInterface, PostMessageReceivedExtensionInterface, MessageResultExtensionInterface, ProcessorExceptionExtensionInterface, PostConsumeExtensionInterface, EndExtensionInterface
+interface ExtensionInterface extends StartExtensionInterface, PreSubscribeExtensionInterface, PreConsumeExtensionInterface, MessageReceivedExtensionInterface, PostMessageReceivedExtensionInterface, MessageResultExtensionInterface, ProcessorExceptionExtensionInterface, PostConsumeExtensionInterface, EndExtensionInterface, InitLoggerExtensionInterface
 {
 }
diff --git a/Consumption/InitLoggerExtensionInterface.php b/Consumption/InitLoggerExtensionInterface.php
new file mode 100644
index 0000000..936e32d
--- /dev/null
+++ b/Consumption/InitLoggerExtensionInterface.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace Enqueue\Consumption;
+
+use Enqueue\Consumption\Context\InitLogger;
+
+interface InitLoggerExtensionInterface
+{
+    /**
+     * Executed only once at the very beginning of the QueueConsumer::consume method call.
+     * BEFORE onStart extension method.
+     */
+    public function onInitLogger(InitLogger $context): void;
+}
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 0850fb0..31247af 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Consumption;
 
 use Enqueue\Consumption\Context\End;
+use Enqueue\Consumption\Context\InitLogger;
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PostConsume;
@@ -32,7 +33,7 @@ final class QueueConsumer implements QueueConsumerInterface
     private $interopContext;
 
     /**
-     * @var ExtensionInterface|ChainExtension
+     * @var ExtensionInterface
      */
     private $staticExtension;
 
@@ -46,11 +47,6 @@ final class QueueConsumer implements QueueConsumerInterface
      */
     private $receiveTimeout;
 
-    /**
-     * @var ExtensionInterface|ChainExtension
-     */
-    private $extension;
-
     /**
      * @var LoggerInterface
      */
@@ -128,11 +124,16 @@ public function bindCallback($queue, callable $processor): QueueConsumerInterfac
 
     public function consume(ExtensionInterface $runtimeExtension = null): void
     {
-        $this->extension = $runtimeExtension ?
+        $extension = $runtimeExtension ?
             new ChainExtension([$this->staticExtension, $runtimeExtension]) :
             $this->staticExtension
         ;
 
+        $initLogger = new InitLogger($this->logger);
+        $extension->onInitLogger($initLogger);
+
+        $this->logger = $initLogger->getLogger();
+
         $startTime = (int) (microtime(true) * 1000);
 
         $start = new Start(
@@ -143,10 +144,10 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
             $startTime
         );
 
-        $this->extension->onStart($start);
+        $extension->onStart($start);
 
         if ($start->isExecutionInterrupted()) {
-            $this->onEnd($startTime);
+            $this->onEnd($extension, $startTime);
 
             return;
         }
@@ -176,7 +177,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
         $receivedMessagesCount = 0;
         $interruptExecution = false;
 
-        $callback = function (InteropMessage $message, Consumer $consumer) use (&$receivedMessagesCount, &$interruptExecution) {
+        $callback = function (InteropMessage $message, Consumer $consumer) use (&$receivedMessagesCount, &$interruptExecution, $extension) {
             ++$receivedMessagesCount;
 
             $receivedAt = (int) (microtime(true) * 1000);
@@ -188,19 +189,19 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
             $processor = $this->boundProcessors[$queue->getQueueName()]->getProcessor();
 
             $messageReceived = new MessageReceived($this->interopContext, $consumer, $message, $processor, $receivedAt, $this->logger);
-            $this->extension->onMessageReceived($messageReceived);
+            $extension->onMessageReceived($messageReceived);
             $result = $messageReceived->getResult();
             $processor = $messageReceived->getProcessor();
             if (null === $result) {
                 try {
                     $result = $processor->process($message, $this->interopContext);
                 } catch (\Exception $e) {
-                    $result = $this->onProcessorException($message, $e, $receivedAt);
+                    $result = $this->onProcessorException($extension, $message, $e, $receivedAt);
                 }
             }
 
             $messageResult = new MessageResult($this->interopContext, $message, $result, $receivedAt, $this->logger);
-            $this->extension->onResult($messageResult);
+            $extension->onResult($messageResult);
             $result = $messageResult->getResult();
 
             switch ($result) {
@@ -220,7 +221,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
             }
 
             $postMessageReceived = new PostMessageReceived($this->interopContext, $message, $result, $receivedAt, $this->logger);
-            $this->extension->onPostMessageReceived($postMessageReceived);
+            $extension->onPostMessageReceived($postMessageReceived);
 
             if ($postMessageReceived->isExecutionInterrupted()) {
                 $interruptExecution = true;
@@ -241,7 +242,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
                 $this->logger
             );
 
-            $this->extension->onPreSubscribe($preSubscribe);
+            $extension->onPreSubscribe($preSubscribe);
 
             $subscriptionConsumer->subscribe($consumer, $callback);
         }
@@ -252,10 +253,10 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
             $interruptExecution = false;
 
             $preConsume = new PreConsume($this->interopContext, $subscriptionConsumer, $this->logger, $cycle, $this->receiveTimeout, $startTime);
-            $this->extension->onPreConsume($preConsume);
+            $extension->onPreConsume($preConsume);
 
             if ($preConsume->isExecutionInterrupted()) {
-                $this->onEnd($startTime, $subscriptionConsumer);
+                $this->onEnd($extension, $startTime, $subscriptionConsumer);
 
                 return;
             }
@@ -263,10 +264,10 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
             $subscriptionConsumer->consume($this->receiveTimeout);
 
             $postConsume = new PostConsume($this->interopContext, $subscriptionConsumer, $receivedMessagesCount, $cycle, $startTime, $this->logger);
-            $this->extension->onPostConsume($postConsume);
+            $extension->onPostConsume($postConsume);
 
             if ($interruptExecution || $postConsume->isExecutionInterrupted()) {
-                $this->onEnd($startTime, $subscriptionConsumer);
+                $this->onEnd($extension, $startTime, $subscriptionConsumer);
 
                 return;
             }
@@ -285,11 +286,11 @@ public function setFallbackSubscriptionConsumer(SubscriptionConsumer $fallbackSu
         $this->fallbackSubscriptionConsumer = $fallbackSubscriptionConsumer;
     }
 
-    private function onEnd(int $startTime, SubscriptionConsumer $subscriptionConsumer = null): void
+    private function onEnd(ExtensionInterface $extension, int $startTime, SubscriptionConsumer $subscriptionConsumer = null): void
     {
         $endTime = (int) (microtime(true) * 1000);
 
-        $this->extension->onEnd(new End($this->interopContext, $startTime, $endTime, $this->logger));
+        $extension->onEnd(new End($this->interopContext, $startTime, $endTime, $this->logger));
 
         if ($subscriptionConsumer) {
             $subscriptionConsumer->unsubscribeAll();
@@ -301,12 +302,12 @@ private function onEnd(int $startTime, SubscriptionConsumer $subscriptionConsume
      *
      * https://github.com/symfony/symfony/blob/cbe289517470eeea27162fd2d523eb29c95f775f/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php#L77
      */
-    private function onProcessorException(Message $message, \Exception $exception, int $receivedAt)
+    private function onProcessorException(ExtensionInterface $extension, Message $message, \Exception $exception, int $receivedAt)
     {
         $processorException = new ProcessorException($this->interopContext, $message, $exception, $receivedAt, $this->logger);
 
         try {
-            $this->extension->onProcessorException($processorException);
+            $extension->onProcessorException($processorException);
 
             $result = $processorException->getResult();
             if (null === $result) {
diff --git a/Consumption/QueueConsumerInterface.php b/Consumption/QueueConsumerInterface.php
index ed22938..c9c99ef 100644
--- a/Consumption/QueueConsumerInterface.php
+++ b/Consumption/QueueConsumerInterface.php
@@ -35,7 +35,7 @@ public function bindCallback($queue, callable $processor): self;
      * Runtime extension - is an extension or a collection of extensions which could be set on runtime.
      * Here's a good example: @see LimitsExtensionsCommandTrait.
      *
-     * @param ExtensionInterface|ChainExtension|null $runtimeExtension
+     * @param ExtensionInterface|null $runtimeExtension
      *
      * @throws \Exception
      */
diff --git a/Symfony/Consumption/LimitsExtensionsCommandTrait.php b/Symfony/Consumption/LimitsExtensionsCommandTrait.php
index 2a75618..b721af9 100644
--- a/Symfony/Consumption/LimitsExtensionsCommandTrait.php
+++ b/Symfony/Consumption/LimitsExtensionsCommandTrait.php
@@ -13,9 +13,6 @@
 
 trait LimitsExtensionsCommandTrait
 {
-    /**
-     * {@inheritdoc}
-     */
     protected function configureLimitsExtensions()
     {
         $this
diff --git a/Tests/Consumption/ChainExtensionTest.php b/Tests/Consumption/ChainExtensionTest.php
index cf905d8..5d469a3 100644
--- a/Tests/Consumption/ChainExtensionTest.php
+++ b/Tests/Consumption/ChainExtensionTest.php
@@ -4,6 +4,7 @@
 
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Context\End;
+use Enqueue\Consumption\Context\InitLogger;
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PostConsume;
@@ -36,6 +37,28 @@ public function testCouldBeConstructedWithExtensionsArray()
         new ChainExtension([$this->createExtension(), $this->createExtension()]);
     }
 
+    public function testShouldProxyOnInitLoggerToAllInternalExtensions()
+    {
+        $context = new InitLogger(new NullLogger());
+
+        $fooExtension = $this->createExtension();
+        $fooExtension
+            ->expects($this->once())
+            ->method('onInitLogger')
+            ->with($this->identicalTo($context))
+        ;
+        $barExtension = $this->createExtension();
+        $barExtension
+            ->expects($this->once())
+            ->method('onInitLogger')
+            ->with($this->identicalTo($context))
+        ;
+
+        $extensions = new ChainExtension([$fooExtension, $barExtension]);
+
+        $extensions->onInitLogger($context);
+    }
+
     public function testShouldProxyOnStartToAllInternalExtensions()
     {
         $context = new Start($this->createInteropContextMock(), $this->createLoggerMock(), [], 0, 0);
diff --git a/Tests/Consumption/Extension/LoggerExtensionTest.php b/Tests/Consumption/Extension/LoggerExtensionTest.php
index 37ef696..674720e 100644
--- a/Tests/Consumption/Extension/LoggerExtensionTest.php
+++ b/Tests/Consumption/Extension/LoggerExtensionTest.php
@@ -2,17 +2,10 @@
 
 namespace Enqueue\Tests\Consumption\Extension;
 
-use Enqueue\Consumption\Context\PostMessageReceived;
-use Enqueue\Consumption\Context\Start;
+use Enqueue\Consumption\Context\InitLogger;
 use Enqueue\Consumption\Extension\LoggerExtension;
-use Enqueue\Consumption\PostMessageReceivedExtensionInterface;
-use Enqueue\Consumption\Result;
-use Enqueue\Consumption\StartExtensionInterface;
-use Enqueue\Null\NullMessage;
+use Enqueue\Consumption\InitLoggerExtensionInterface;
 use Enqueue\Test\ClassExtensionTrait;
-use Interop\Queue\Consumer;
-use Interop\Queue\Context as InteropContext;
-use Interop\Queue\Message;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
@@ -21,14 +14,9 @@ class LoggerExtensionTest extends TestCase
 {
     use ClassExtensionTrait;
 
-    public function testShouldImplementStartExtensionInterface()
+    public function testShouldImplementInitLoggerExtensionInterface()
     {
-        $this->assertClassImplements(StartExtensionInterface::class, LoggerExtension::class);
-    }
-
-    public function testShouldImplementPostMessageReceivedExtensionInterface()
-    {
-        $this->assertClassImplements(PostMessageReceivedExtensionInterface::class, LoggerExtension::class);
+        $this->assertClassImplements(InitLoggerExtensionInterface::class, LoggerExtension::class);
     }
 
     public function testCouldBeConstructedWithLoggerAsFirstArgument()
@@ -36,180 +24,53 @@ public function testCouldBeConstructedWithLoggerAsFirstArgument()
         new LoggerExtension($this->createLogger());
     }
 
-    public function testShouldSetLoggerToContextOnStart()
+    public function testShouldSetLoggerToContextOnInitLogger()
     {
         $logger = $this->createLogger();
 
         $extension = new LoggerExtension($logger);
 
-        $context = new Start($this->createContextMock(), new NullLogger(), [], 0, 0);
+        $previousLogger = new NullLogger();
+        $context = new InitLogger($previousLogger);
 
-        $extension->onStart($context);
+        $extension->onInitLogger($context);
 
         $this->assertSame($logger, $context->getLogger());
     }
 
     public function testShouldAddInfoMessageOnStart()
     {
-        $logger = $this->createLogger();
-        $logger
-            ->expects($this->once())
-            ->method('debug')
-            ->with($this->stringStartsWith('Set context\'s logger'))
-        ;
-
-        $extension = new LoggerExtension($logger);
-
-        $context = new Start($this->createContextMock(), new NullLogger(), [], 0, 0);
-
-        $extension->onStart($context);
-    }
-
-    public function testShouldLogRejectMessageStatus()
-    {
-        $logger = $this->createLogger();
-        $logger
-            ->expects($this->once())
-            ->method('error')
-            ->with('reason', ['body' => 'message body', 'headers' => [], 'properties' => []])
-        ;
-
-        $extension = new LoggerExtension($logger);
-
-        $message = new NullMessage();
-        $message->setBody('message body');
-
-        $postReceivedMessage = new PostMessageReceived(
-            $this->createContextMock(),
-            $message,
-            Result::reject('reason'),
-            1,
-            $logger
-        );
-
-        $extension->onPostMessageReceived($postReceivedMessage);
-    }
-
-    public function testShouldLogRequeueMessageStatus()
-    {
-        $logger = $this->createLogger();
-        $logger
-            ->expects($this->once())
-            ->method('error')
-            ->with('reason', ['body' => 'message body', 'headers' => [], 'properties' => []])
-        ;
-
-        $extension = new LoggerExtension($logger);
-
-        $message = new NullMessage();
-        $message->setBody('message body');
-
-        $postReceivedMessage = new PostMessageReceived(
-            $this->createContextMock(),
-            $message,
-            Result::requeue('reason'),
-            1,
-            $logger
-        );
-
-        $extension->onPostMessageReceived($postReceivedMessage);
-    }
-
-    public function testShouldNotLogRequeueMessageStatusIfReasonIsEmpty()
-    {
-        $logger = $this->createLogger();
-        $logger
-            ->expects($this->never())
-            ->method('error')
-        ;
-
-        $extension = new LoggerExtension($logger);
+        $previousLogger = $this->createLogger();
 
-        $postReceivedMessage = new PostMessageReceived(
-            $this->createContextMock(),
-            $this->createMock(Message::class),
-            Result::requeue(),
-            1,
-            $logger
-        );
-
-        $extension->onPostMessageReceived($postReceivedMessage);
-    }
-
-    public function testShouldLogAckMessageStatus()
-    {
         $logger = $this->createLogger();
         $logger
             ->expects($this->once())
-            ->method('info')
-            ->with('reason', ['body' => 'message body', 'headers' => [], 'properties' => []])
+            ->method('debug')
+            ->with(sprintf('Change logger from "%s" to "%s"', get_class($logger), get_class($previousLogger)))
         ;
 
         $extension = new LoggerExtension($logger);
 
-        $message = new NullMessage();
-        $message->setBody('message body');
+        $context = new InitLogger($previousLogger);
 
-        $postReceivedMessage = new PostMessageReceived(
-            $this->createContextMock(),
-            $message,
-            Result::ack('reason'),
-            1,
-            $logger
-        );
-
-        $extension->onPostMessageReceived($postReceivedMessage);
+        $extension->onInitLogger($context);
     }
 
-    public function testShouldNotLogAckMessageStatusIfReasonIsEmpty()
+    public function testShouldDoNothingIfSameLoggerInstanceAlreadySet()
     {
         $logger = $this->createLogger();
         $logger
             ->expects($this->never())
-            ->method('info')
-        ;
-
-        $extension = new LoggerExtension($logger);
-
-        $postReceivedMessage = new PostMessageReceived(
-            $this->createContextMock(),
-            $this->createMock(Message::class),
-            Result::ack(),
-            1,
-            $logger
-        );
-
-        $extension->onPostMessageReceived($postReceivedMessage);
-    }
-
-    public function testShouldNotSetLoggerIfOneHasBeenSetOnStart()
-    {
-        $logger = $this->createLogger();
-
-        $alreadySetLogger = $this->createLogger();
-        $alreadySetLogger
-            ->expects($this->once())
             ->method('debug')
-            ->with(sprintf(
-                'Skip setting context\'s logger "%s". Another one "%s" has already been set.',
-                get_class($logger),
-                get_class($alreadySetLogger)
-            ))
         ;
 
         $extension = new LoggerExtension($logger);
 
-        $context = new Start($this->createContextMock(), $alreadySetLogger, [], 0, 0);
+        $context = new InitLogger($logger);
 
-        $extension->onStart($context);
-    }
+        $extension->onInitLogger($context);
 
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
-     */
-    protected function createContextMock(): InteropContext
-    {
-        return $this->createMock(InteropContext::class);
+        $this->assertSame($logger, $context->getLogger());
     }
 
     /**
@@ -219,12 +80,4 @@ protected function createLogger()
     {
         return $this->createMock(LoggerInterface::class);
     }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Consumer
-     */
-    protected function createConsumerMock()
-    {
-        return $this->createMock(Consumer::class);
-    }
 }
diff --git a/Tests/Consumption/Mock/BreakCycleExtension.php b/Tests/Consumption/Mock/BreakCycleExtension.php
index 831c7a9..cbc2f8b 100644
--- a/Tests/Consumption/Mock/BreakCycleExtension.php
+++ b/Tests/Consumption/Mock/BreakCycleExtension.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Tests\Consumption\Mock;
 
 use Enqueue\Consumption\Context\End;
+use Enqueue\Consumption\Context\InitLogger;
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PostConsume;
@@ -24,6 +25,10 @@ public function __construct($limit)
         $this->limit = $limit;
     }
 
+    public function onInitLogger(InitLogger $context): void
+    {
+    }
+
     public function onPostMessageReceived(PostMessageReceived $context): void
     {
         if ($this->cycles >= $this->limit) {
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 50ced5e..9b0111f 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -6,6 +6,7 @@
 use Enqueue\Consumption\CallbackProcessor;
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Context\End;
+use Enqueue\Consumption\Context\InitLogger;
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Context\MessageResult;
 use Enqueue\Consumption\Context\PostConsume;
@@ -591,6 +592,34 @@ public function testShouldNotPassMessageToProcessorIfItWasProcessedByExtension()
         $queueConsumer->consume();
     }
 
+    public function testShouldCallOnInitLoggerExtensionMethod()
+    {
+        $consumerStub = $this->createConsumerStub('foo_queue');
+
+        $contextStub = $this->createContextStub($consumerStub);
+
+        $processorMock = $this->createProcessorMock();
+
+        $logger = $this->createMock(LoggerInterface::class);
+
+        $extension = $this->createExtension();
+        $extension
+            ->expects($this->once())
+            ->method('onInitLogger')
+            ->with($this->isInstanceOf(InitLogger::class))
+            ->willReturnCallback(function (InitLogger $context) use ($logger) {
+                $this->assertSame($logger, $context->getLogger());
+            })
+        ;
+
+        $chainExtensions = new ChainExtension([$extension, new BreakCycleExtension(1)]);
+        $queueConsumer = new QueueConsumer($contextStub, $chainExtensions, [], $logger);
+        $queueConsumer->setFallbackSubscriptionConsumer(new DummySubscriptionConsumer());
+        $queueConsumer->bind(new NullQueue('foo_queue'), $processorMock);
+
+        $queueConsumer->consume();
+    }
+
     public function testShouldCallOnStartExtensionMethod()
     {
         $consumerStub = $this->createConsumerStub('foo_queue');
@@ -1212,6 +1241,11 @@ public function testShouldCallExtensionPassedOnRuntime()
         ;
 
         $runtimeExtension = $this->createExtension();
+        $runtimeExtension
+            ->expects($this->once())
+            ->method('onInitLogger')
+            ->with($this->isInstanceOf(InitLogger::class))
+        ;
         $runtimeExtension
             ->expects($this->once())
             ->method('onStart')
@@ -1250,7 +1284,7 @@ public function testShouldCallExtensionPassedOnRuntime()
         $queueConsumer->consume(new ChainExtension([$runtimeExtension]));
     }
 
-    public function testShouldChangeLoggerOnStart()
+    public function testShouldChangeLoggerOnInitLogger()
     {
         $expectedMessage = $this->createMessageMock();
 
@@ -1271,12 +1305,20 @@ public function testShouldChangeLoggerOnStart()
         $expectedLogger = new NullLogger();
 
         $extension = $this->createExtension();
+        $extension
+            ->expects($this->atLeastOnce())
+            ->method('onInitLogger')
+            ->with($this->isInstanceOf(InitLogger::class))
+            ->willReturnCallback(function (InitLogger $context) use ($expectedLogger) {
+                $context->changeLogger($expectedLogger);
+            })
+        ;
         $extension
             ->expects($this->atLeastOnce())
             ->method('onStart')
             ->with($this->isInstanceOf(Start::class))
             ->willReturnCallback(function (Start $context) use ($expectedLogger) {
-                $context->changeLogger($expectedLogger);
+                $this->assertSame($expectedLogger, $context->getLogger());
             })
         ;
         $extension

From 852f429657991b2eebb9a0615742dd39c3f142ef Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 8 Oct 2018 18:24:01 +0300
Subject: [PATCH 160/337] [consumption] Add LogExtension.

---
 Consumption/Context/PostMessageReceived.php   |  13 +
 Consumption/Extension/LogExtension.php        |  67 +++++
 Consumption/QueueConsumer.php                 |   2 +-
 .../DependencyInjection/TransportFactory.php  |   8 +-
 .../FlushSpoolProducerExtensionTest.php       |   2 +
 Tests/Consumption/ChainExtensionTest.php      |   1 +
 .../LimitConsumedMessagesExtensionTest.php    |   3 +
 .../LimitConsumerMemoryExtensionTest.php      |   3 +
 .../LimitConsumptionTimeExtensionTest.php     |   3 +
 .../Extension/LogExtensionTest.php            | 270 ++++++++++++++++++
 .../Extension/ReplyExtensionTest.php          |   5 +
 Util/Stringify.php                            |  36 +++
 12 files changed, 411 insertions(+), 2 deletions(-)
 create mode 100644 Consumption/Extension/LogExtension.php
 create mode 100644 Tests/Consumption/Extension/LogExtensionTest.php
 create mode 100644 Util/Stringify.php

diff --git a/Consumption/Context/PostMessageReceived.php b/Consumption/Context/PostMessageReceived.php
index 85ecaec..423830e 100644
--- a/Consumption/Context/PostMessageReceived.php
+++ b/Consumption/Context/PostMessageReceived.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Consumption\Context;
 
 use Enqueue\Consumption\Result;
+use Interop\Queue\Consumer;
 use Interop\Queue\Context;
 use Interop\Queue\Message;
 use Psr\Log\LoggerInterface;
@@ -14,6 +15,11 @@ final class PostMessageReceived
      */
     private $context;
 
+    /**
+     * @var Consumer
+     */
+    private $consumer;
+
     /**
      * @var Message
      */
@@ -41,12 +47,14 @@ final class PostMessageReceived
 
     public function __construct(
         Context $context,
+        Consumer $consumer,
         Message $message,
         $result,
         int $receivedAt,
         LoggerInterface $logger
     ) {
         $this->context = $context;
+        $this->consumer = $consumer;
         $this->message = $message;
         $this->result = $result;
         $this->receivedAt = $receivedAt;
@@ -60,6 +68,11 @@ public function getContext(): Context
         return $this->context;
     }
 
+    public function getConsumer(): Consumer
+    {
+        return $this->consumer;
+    }
+
     public function getMessage(): Message
     {
         return $this->message;
diff --git a/Consumption/Extension/LogExtension.php b/Consumption/Extension/LogExtension.php
new file mode 100644
index 0000000..14383c4
--- /dev/null
+++ b/Consumption/Extension/LogExtension.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace Enqueue\Consumption\Extension;
+
+use Enqueue\Consumption\Context\End;
+use Enqueue\Consumption\Context\MessageReceived;
+use Enqueue\Consumption\Context\PostMessageReceived;
+use Enqueue\Consumption\Context\Start;
+use Enqueue\Consumption\EndExtensionInterface;
+use Enqueue\Consumption\MessageReceivedExtensionInterface;
+use Enqueue\Consumption\PostMessageReceivedExtensionInterface;
+use Enqueue\Consumption\Result;
+use Enqueue\Consumption\StartExtensionInterface;
+use Enqueue\Util\Stringify;
+use Psr\Log\LogLevel;
+
+class LogExtension implements StartExtensionInterface, MessageReceivedExtensionInterface, PostMessageReceivedExtensionInterface, EndExtensionInterface
+{
+    public function onStart(Start $context): void
+    {
+        $context->getLogger()->debug('Consumption has started');
+    }
+
+    public function onEnd(End $context): void
+    {
+        $context->getLogger()->debug('Consumption has ended');
+    }
+
+    public function onMessageReceived(MessageReceived $context): void
+    {
+        $message = $context->getMessage();
+
+        $context->getLogger()->debug("Received from {queueName}\t{body}", [
+            'queueName' => $context->getConsumer()->getQueue()->getQueueName(),
+            'redelivered' => $message->isRedelivered(),
+            'body' => Stringify::that($message->getBody()),
+            'properties' => Stringify::that($message->getProperties()),
+            'headers' => Stringify::that($message->getHeaders()),
+        ]);
+    }
+
+    public function onPostMessageReceived(PostMessageReceived $context): void
+    {
+        $message = $context->getMessage();
+        $queue = $context->getConsumer()->getQueue();
+        $result = $context->getResult();
+
+        $reason = '';
+        $logMessage = "Processed from {queueName}\t{body}\t{result}";
+        if ($result instanceof Result && $result->getReason()) {
+            $reason = $result->getReason();
+            $logMessage .= ' {reason}';
+        }
+        $logContext = [
+            'result' => str_replace('enqueue.', '', $result),
+            'reason' => $reason,
+            'queueName' => $queue->getQueueName(),
+            'body' => Stringify::that($message->getBody()),
+            'properties' => Stringify::that($message->getProperties()),
+            'headers' => Stringify::that($message->getHeaders()),
+        ];
+
+        $logLevel = Result::REJECT == ((string) $result) ? LogLevel::ERROR : LogLevel::INFO;
+
+        $context->getLogger()->log($logLevel, $logMessage, $logContext);
+    }
+}
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 31247af..770f22f 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -220,7 +220,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
                     throw new \LogicException(sprintf('Status is not supported: %s', $result));
             }
 
-            $postMessageReceived = new PostMessageReceived($this->interopContext, $message, $result, $receivedAt, $this->logger);
+            $postMessageReceived = new PostMessageReceived($this->interopContext, $consumer, $message, $result, $receivedAt, $this->logger);
             $extension->onPostMessageReceived($postMessageReceived);
 
             if ($postMessageReceived->isExecutionInterrupted()) {
diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index b60420c..f2bee8d 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -5,6 +5,7 @@
 use Enqueue\ConnectionFactoryFactory;
 use Enqueue\ConnectionFactoryFactoryInterface;
 use Enqueue\Consumption\ChainExtension;
+use Enqueue\Consumption\Extension\LogExtension;
 use Enqueue\Consumption\QueueConsumer;
 use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Resources;
@@ -166,8 +167,13 @@ public function buildQueueConsumer(ContainerBuilder $container, array $config):
 
         $container->setParameter($this->format('receive_timeout'), $config['receive_timeout'] ?? 10000);
 
+        $logExtensionId = $this->format('log_extension');
+        $container->register($logExtensionId, LogExtension::class);
+
         $container->register($this->format('consumption_extensions'), ChainExtension::class)
-            ->addArgument([])
+            ->addArgument([
+                new Reference($logExtensionId),
+            ])
         ;
 
         $container->register($this->format('queue_consumer'), QueueConsumer::class)
diff --git a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
index 4f3af65..53d88aa 100644
--- a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
@@ -9,6 +9,7 @@
 use Enqueue\Consumption\EndExtensionInterface;
 use Enqueue\Consumption\PostMessageReceivedExtensionInterface;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\Consumer;
 use Interop\Queue\Context;
 use Interop\Queue\Message;
 use PHPUnit\Framework\TestCase;
@@ -57,6 +58,7 @@ public function testShouldFlushSpoolProducerOnPostReceived()
 
         $context = new PostMessageReceived(
             $this->createInteropContextMock(),
+            $this->createMock(Consumer::class),
             $this->createMock(Message::class),
             'aResult',
             1,
diff --git a/Tests/Consumption/ChainExtensionTest.php b/Tests/Consumption/ChainExtensionTest.php
index 5d469a3..dcde84d 100644
--- a/Tests/Consumption/ChainExtensionTest.php
+++ b/Tests/Consumption/ChainExtensionTest.php
@@ -197,6 +197,7 @@ public function testShouldProxyOnPostReceiveToAllInternalExtensions()
     {
         $context = new PostMessageReceived(
             $this->createInteropContextMock(),
+            $this->createMock(Consumer::class),
             $this->createMock(Message::class),
             'aResult',
             1,
diff --git a/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php b/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
index 2f4f804..37d592e 100644
--- a/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
@@ -5,6 +5,7 @@
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Extension\LimitConsumedMessagesExtension;
+use Interop\Queue\Consumer;
 use Interop\Queue\Context;
 use Interop\Queue\Message;
 use Interop\Queue\SubscriptionConsumer;
@@ -49,6 +50,7 @@ public function testOnPreConsumeShouldInterruptWhenLimitIsReached()
 
         $postReceivedMessage = new PostMessageReceived(
             $this->createInteropContextMock(),
+            $this->createMock(Consumer::class),
             $this->createMock(Message::class),
             'aResult',
             1,
@@ -135,6 +137,7 @@ public function testOnPostReceivedShouldInterruptExecutionIfMessageLimitExceeded
 
         $postReceivedMessage = new PostMessageReceived(
             $this->createInteropContextMock(),
+            $this->createMock(Consumer::class),
             $this->createMock(Message::class),
             'aResult',
             1,
diff --git a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
index 8af630c..de1531e 100644
--- a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
@@ -6,6 +6,7 @@
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Extension\LimitConsumerMemoryExtension;
+use Interop\Queue\Consumer;
 use Interop\Queue\Context;
 use Interop\Queue\Message;
 use Interop\Queue\SubscriptionConsumer;
@@ -66,6 +67,7 @@ public function testOnPostReceivedShouldInterruptExecutionIfMemoryLimitReached()
 
         $postReceivedMessage = new PostMessageReceived(
             $this->createInteropContextMock(),
+            $this->createMock(Consumer::class),
             $this->createMock(Message::class),
             'aResult',
             1,
@@ -156,6 +158,7 @@ public function testOnPostMessageReceivedShouldNotInterruptExecutionIfMemoryLimi
     {
         $postReceivedMessage = new PostMessageReceived(
             $this->createInteropContextMock(),
+            $this->createMock(Consumer::class),
             $this->createMock(Message::class),
             'aResult',
             1,
diff --git a/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php b/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
index fe0cb5e..1eaef78 100644
--- a/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
@@ -6,6 +6,7 @@
 use Enqueue\Consumption\Context\PostMessageReceived;
 use Enqueue\Consumption\Context\PreConsume;
 use Enqueue\Consumption\Extension\LimitConsumptionTimeExtension;
+use Interop\Queue\Consumer;
 use Interop\Queue\Context;
 use Interop\Queue\Message;
 use Interop\Queue\SubscriptionConsumer;
@@ -68,6 +69,7 @@ public function testOnPostReceivedShouldInterruptExecutionIfConsumptionTimeExcee
     {
         $postReceivedMessage = new PostMessageReceived(
             $this->createInteropContextMock(),
+            $this->createMock(Consumer::class),
             $this->createMock(Message::class),
             'aResult',
             1,
@@ -133,6 +135,7 @@ public function testOnPostReceivedShouldNotInterruptExecutionIfConsumptionTimeIs
     {
         $postReceivedMessage = new PostMessageReceived(
             $this->createInteropContextMock(),
+            $this->createMock(Consumer::class),
             $this->createMock(Message::class),
             'aResult',
             1,
diff --git a/Tests/Consumption/Extension/LogExtensionTest.php b/Tests/Consumption/Extension/LogExtensionTest.php
new file mode 100644
index 0000000..8038e55
--- /dev/null
+++ b/Tests/Consumption/Extension/LogExtensionTest.php
@@ -0,0 +1,270 @@
+<?php
+
+namespace Enqueue\Tests\Consumption\Extension;
+
+use Enqueue\Consumption\Context\End;
+use Enqueue\Consumption\Context\MessageReceived;
+use Enqueue\Consumption\Context\PostMessageReceived;
+use Enqueue\Consumption\Context\Start;
+use Enqueue\Consumption\EndExtensionInterface;
+use Enqueue\Consumption\Extension\LogExtension;
+use Enqueue\Consumption\MessageReceivedExtensionInterface;
+use Enqueue\Consumption\PostMessageReceivedExtensionInterface;
+use Enqueue\Consumption\Result;
+use Enqueue\Consumption\StartExtensionInterface;
+use Enqueue\Null\NullMessage;
+use Enqueue\Null\NullQueue;
+use Enqueue\Test\ClassExtensionTrait;
+use Enqueue\Util\Stringify;
+use Interop\Queue\Consumer;
+use Interop\Queue\Context;
+use Interop\Queue\Processor;
+use Interop\Queue\Queue;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+use Psr\Log\LogLevel;
+
+class LogExtensionTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementStartExtensionInterface()
+    {
+        $this->assertClassImplements(StartExtensionInterface::class, LogExtension::class);
+    }
+
+    public function testShouldImplementEndExtensionInterface()
+    {
+        $this->assertClassImplements(EndExtensionInterface::class, LogExtension::class);
+    }
+
+    public function testShouldImplementMessageReceivedExtensionInterface()
+    {
+        $this->assertClassImplements(MessageReceivedExtensionInterface::class, LogExtension::class);
+    }
+
+    public function testShouldImplementPostMessageReceivedExtensionInterface()
+    {
+        $this->assertClassImplements(PostMessageReceivedExtensionInterface::class, LogExtension::class);
+    }
+
+    public function testCouldBeConstructedWithoutAnyArguments()
+    {
+        new LogExtension();
+    }
+
+    public function testShouldLogStartOnStart()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('debug')
+            ->with('Consumption has started')
+        ;
+
+        $context = new Start($this->createContextMock(), $logger, [], 1, 1);
+
+        $extension = new LogExtension();
+        $extension->onStart($context);
+    }
+
+    public function testShouldLogEndOnEnd()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('debug')
+            ->with('Consumption has ended')
+        ;
+
+        $context = new End($this->createContextMock(), 1, 2, $logger);
+
+        $extension = new LogExtension();
+        $extension->onEnd($context);
+    }
+
+    public function testShouldLogMessageReceived()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('debug')
+            ->with('Received from {queueName}	{body}', [
+                'queueName' => 'aQueue',
+                'redelivered' => false,
+                'body' => Stringify::that('aBody'),
+                'properties' => Stringify::that(['aProp' => 'aPropVal']),
+                'headers' => Stringify::that(['aHeader' => 'aHeaderVal']),
+            ])
+        ;
+
+        $consumerMock = $this->createConsumerStub(new NullQueue('aQueue'));
+        $message = new NullMessage('aBody');
+        $message->setProperty('aProp', 'aPropVal');
+        $message->setHeader('aHeader', 'aHeaderVal');
+
+        $context = new MessageReceived($this->createContextMock(), $consumerMock, $message, $this->createProcessorMock(), 1, $logger);
+
+        $extension = new LogExtension();
+        $extension->onMessageReceived($context);
+    }
+
+    public function testShouldLogMessageProcessedWithStringResult()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('log')
+            ->with(LogLevel::INFO,
+                'Processed from {queueName}	{body}	{result}',
+                [
+                    'queueName' => 'aQueue',
+                    'body' => Stringify::that('aBody'),
+                    'properties' => Stringify::that(['aProp' => 'aPropVal']),
+                    'headers' => Stringify::that(['aHeader' => 'aHeaderVal']),
+                    'result' => 'aResult',
+                    'reason' => '',
+                ]
+            )
+        ;
+
+        $consumerMock = $this->createConsumerStub(new NullQueue('aQueue'));
+        $message = new NullMessage('aBody');
+        $message->setProperty('aProp', 'aPropVal');
+        $message->setHeader('aHeader', 'aHeaderVal');
+
+        $context = new PostMessageReceived($this->createContextMock(), $consumerMock, $message, 'aResult', 1, $logger);
+
+        $extension = new LogExtension();
+        $extension->onPostMessageReceived($context);
+    }
+
+    public function testShouldLogRejectedMessageAsError()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('log')
+            ->with(LogLevel::ERROR,
+                'Processed from {queueName}	{body}	{result}',
+                [
+                    'queueName' => 'aQueue',
+                    'body' => Stringify::that('aBody'),
+                    'properties' => Stringify::that(['aProp' => 'aPropVal']),
+                    'headers' => Stringify::that(['aHeader' => 'aHeaderVal']),
+                    'result' => 'reject',
+                    'reason' => '',
+                ]
+            )
+        ;
+
+        $consumerMock = $this->createConsumerStub(new NullQueue('aQueue'));
+        $message = new NullMessage('aBody');
+        $message->setProperty('aProp', 'aPropVal');
+        $message->setHeader('aHeader', 'aHeaderVal');
+
+        $context = new PostMessageReceived($this->createContextMock(), $consumerMock, $message, Processor::REJECT, 1, $logger);
+
+        $extension = new LogExtension();
+        $extension->onPostMessageReceived($context);
+    }
+
+    public function testShouldLogMessageProcessedWithResultObject()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('log')
+            ->with(LogLevel::INFO,
+                'Processed from {queueName}	{body}	{result}',
+                [
+                    'queueName' => 'aQueue',
+                    'body' => Stringify::that('aBody'),
+                    'properties' => Stringify::that(['aProp' => 'aPropVal']),
+                    'headers' => Stringify::that(['aHeader' => 'aHeaderVal']),
+                    'result' => 'ack',
+                    'reason' => '',
+                ]
+            )
+        ;
+
+        $consumerMock = $this->createConsumerStub(new NullQueue('aQueue'));
+        $message = new NullMessage('aBody');
+        $message->setProperty('aProp', 'aPropVal');
+        $message->setHeader('aHeader', 'aHeaderVal');
+
+        $context = new PostMessageReceived($this->createContextMock(), $consumerMock, $message, Result::ack(), 1, $logger);
+
+        $extension = new LogExtension();
+        $extension->onPostMessageReceived($context);
+    }
+
+    public function testShouldLogMessageProcessedWithReasonResultObject()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('log')
+            ->with(LogLevel::INFO,
+                'Processed from {queueName}	{body}	{result} {reason}',
+                [
+                    'queueName' => 'aQueue',
+                    'body' => Stringify::that('aBody'),
+                    'properties' => Stringify::that(['aProp' => 'aPropVal']),
+                    'headers' => Stringify::that(['aHeader' => 'aHeaderVal']),
+                    'result' => 'ack',
+                    'reason' => 'aReason',
+                ]
+            )
+        ;
+
+        $consumerMock = $this->createConsumerStub(new NullQueue('aQueue'));
+        $message = new NullMessage('aBody');
+        $message->setProperty('aProp', 'aPropVal');
+        $message->setHeader('aHeader', 'aHeaderVal');
+
+        $context = new PostMessageReceived($this->createContextMock(), $consumerMock, $message, Result::ack('aReason'), 1, $logger);
+
+        $extension = new LogExtension();
+        $extension->onPostMessageReceived($context);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createConsumerStub(Queue $queue): Consumer
+    {
+        $consumerMock = $this->createMock(Consumer::class);
+        $consumerMock
+            ->expects($this->any())
+            ->method('getQueue')
+            ->willReturn($queue)
+        ;
+
+        return $consumerMock;
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createContextMock(): Context
+    {
+        return $this->createMock(Context::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createProcessorMock(): Processor
+    {
+        return $this->createMock(Processor::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|LoggerInterface
+     */
+    private function createLogger()
+    {
+        return $this->createMock(LoggerInterface::class);
+    }
+}
diff --git a/Tests/Consumption/Extension/ReplyExtensionTest.php b/Tests/Consumption/Extension/ReplyExtensionTest.php
index e78a745..18e5727 100644
--- a/Tests/Consumption/Extension/ReplyExtensionTest.php
+++ b/Tests/Consumption/Extension/ReplyExtensionTest.php
@@ -9,6 +9,7 @@
 use Enqueue\Null\NullMessage;
 use Enqueue\Null\NullQueue;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\Consumer;
 use Interop\Queue\Context;
 use Interop\Queue\Producer as InteropProducer;
 use PHPUnit\Framework\TestCase;
@@ -34,6 +35,7 @@ public function testShouldDoNothingIfReceivedMessageNotHaveReplyToSet()
 
         $postReceivedMessage = new PostMessageReceived(
             $this->createNeverUsedContextMock(),
+            $this->createMock(Consumer::class),
             new NullMessage(),
             'aResult',
             1,
@@ -52,6 +54,7 @@ public function testShouldDoNothingIfContextResultIsNotInstanceOfResult()
 
         $postReceivedMessage = new PostMessageReceived(
             $this->createNeverUsedContextMock(),
+            $this->createMock(Consumer::class),
             $message,
             'notInstanceOfResult',
             1,
@@ -70,6 +73,7 @@ public function testShouldDoNothingIfResultInstanceOfResultButReplyMessageNotSet
 
         $postReceivedMessage = new PostMessageReceived(
             $this->createNeverUsedContextMock(),
+            $this->createMock(Consumer::class),
             $message,
             Result::ack(),
             1,
@@ -114,6 +118,7 @@ public function testShouldSendReplyMessageToReplyQueueOnPostReceived()
 
         $postReceivedMessage = new PostMessageReceived(
             $contextMock,
+            $this->createMock(Consumer::class),
             $message,
             Result::reply($replyMessage),
             1,
diff --git a/Util/Stringify.php b/Util/Stringify.php
new file mode 100644
index 0000000..39b1f13
--- /dev/null
+++ b/Util/Stringify.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Enqueue\Util;
+
+/**
+ * This is used to log message parts when in debug mode.
+ */
+class Stringify
+{
+    /**
+     * @var mixed
+     */
+    private $value;
+
+    /**
+     * @param mixed $value
+     */
+    public function __construct($value)
+    {
+        $this->value = $value;
+    }
+
+    public function __toString(): string
+    {
+        if (is_string($this->value) || is_scalar($this->value)) {
+            return $this->value;
+        }
+
+        return json_encode($this->value, JSON_UNESCAPED_SLASHES);
+    }
+
+    public static function that($value): self
+    {
+        return new static($value);
+    }
+}

From 2ebf0ab28d185c428931ac23ff69b66dc96b52c2 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 8 Oct 2018 18:55:35 +0300
Subject: [PATCH 161/337] [consumption][client] Add client aware log extension.

---
 Client/ConsumptionExtension/LogExtension.php  |  69 +++
 .../DependencyInjection/TransportFactory.php  |   8 +-
 .../ConsumptionExtension/LogExtensionTest.php | 540 ++++++++++++++++++
 3 files changed, 613 insertions(+), 4 deletions(-)
 create mode 100644 Client/ConsumptionExtension/LogExtension.php
 create mode 100644 Tests/Client/ConsumptionExtension/LogExtensionTest.php

diff --git a/Client/ConsumptionExtension/LogExtension.php b/Client/ConsumptionExtension/LogExtension.php
new file mode 100644
index 0000000..693be20
--- /dev/null
+++ b/Client/ConsumptionExtension/LogExtension.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Enqueue\Client\ConsumptionExtension;
+
+use Enqueue\Client\Config;
+use Enqueue\Consumption\Context\PostMessageReceived;
+use Enqueue\Consumption\Result;
+use Enqueue\Util\Stringify;
+use Psr\Log\LogLevel;
+
+class LogExtension extends \Enqueue\Consumption\Extension\LogExtension
+{
+    public function onPostMessageReceived(PostMessageReceived $context): void
+    {
+        $result = $context->getResult();
+        $message = $context->getMessage();
+
+        $logLevel = Result::REJECT == ((string) $result) ? LogLevel::ERROR : LogLevel::INFO;
+
+        if ($command = $message->getProperty(Config::COMMAND)) {
+            $reason = '';
+            $logMessage = "[client] Processed {command}\t{body}\t{result}";
+            if ($result instanceof Result && $result->getReason()) {
+                $reason = $result->getReason();
+
+                $logMessage .= ' {reason}';
+            }
+
+            $context->getLogger()->log($logLevel, $logMessage, [
+                'result' => str_replace('enqueue.', '', $result),
+                'reason' => $reason,
+                'command' => $command,
+                'queueName' => $context->getConsumer()->getQueue()->getQueueName(),
+                'body' => Stringify::that($message->getBody()),
+                'properties' => Stringify::that($message->getProperties()),
+                'headers' => Stringify::that($message->getHeaders()),
+            ]);
+
+            return;
+        }
+
+        $topic = $message->getProperty(Config::TOPIC);
+        $processor = $message->getProperty(Config::PROCESSOR);
+        if ($topic && $processor) {
+            $reason = '';
+            $logMessage = "[client] Processed {topic} -> {processor}\t{body}\t{result}";
+            if ($result instanceof Result && $result->getReason()) {
+                $reason = $result->getReason();
+
+                $logMessage .= ' {reason}';
+            }
+
+            $context->getLogger()->log($logLevel, $logMessage, [
+                'result' => str_replace('enqueue.', '', $result),
+                'reason' => $reason,
+                'topic' => $topic,
+                'processor' => $processor,
+                'queueName' => $context->getConsumer()->getQueue()->getQueueName(),
+                'body' => Stringify::that($message->getBody()),
+                'properties' => Stringify::that($message->getProperties()),
+                'headers' => Stringify::that($message->getHeaders()),
+            ]);
+
+            return;
+        }
+
+        parent::onPostMessageReceived($context);
+    }
+}
diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index f2bee8d..0234716 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -168,12 +168,12 @@ public function buildQueueConsumer(ContainerBuilder $container, array $config):
         $container->setParameter($this->format('receive_timeout'), $config['receive_timeout'] ?? 10000);
 
         $logExtensionId = $this->format('log_extension');
-        $container->register($logExtensionId, LogExtension::class);
+        $container->register($logExtensionId, LogExtension::class)
+            ->addTag('enqueue.transport.consumption_extension', ['transport' => $this->name, 'priority' => -100])
+        ;
 
         $container->register($this->format('consumption_extensions'), ChainExtension::class)
-            ->addArgument([
-                new Reference($logExtensionId),
-            ])
+            ->addArgument([])
         ;
 
         $container->register($this->format('queue_consumer'), QueueConsumer::class)
diff --git a/Tests/Client/ConsumptionExtension/LogExtensionTest.php b/Tests/Client/ConsumptionExtension/LogExtensionTest.php
new file mode 100644
index 0000000..0bd6514
--- /dev/null
+++ b/Tests/Client/ConsumptionExtension/LogExtensionTest.php
@@ -0,0 +1,540 @@
+<?php
+
+namespace Enqueue\Tests\Client\ConsumptionExtension;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\ConsumptionExtension\LogExtension;
+use Enqueue\Consumption\Context\End;
+use Enqueue\Consumption\Context\MessageReceived;
+use Enqueue\Consumption\Context\PostMessageReceived;
+use Enqueue\Consumption\Context\Start;
+use Enqueue\Consumption\EndExtensionInterface;
+use Enqueue\Consumption\MessageReceivedExtensionInterface;
+use Enqueue\Consumption\PostMessageReceivedExtensionInterface;
+use Enqueue\Consumption\Result;
+use Enqueue\Consumption\StartExtensionInterface;
+use Enqueue\Null\NullMessage;
+use Enqueue\Null\NullQueue;
+use Enqueue\Test\ClassExtensionTrait;
+use Enqueue\Util\Stringify;
+use Interop\Queue\Consumer;
+use Interop\Queue\Context;
+use Interop\Queue\Processor;
+use Interop\Queue\Queue;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+use Psr\Log\LogLevel;
+
+class LogExtensionTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementStartExtensionInterface()
+    {
+        $this->assertClassImplements(StartExtensionInterface::class, LogExtension::class);
+    }
+
+    public function testShouldImplementEndExtensionInterface()
+    {
+        $this->assertClassImplements(EndExtensionInterface::class, LogExtension::class);
+    }
+
+    public function testShouldImplementMessageReceivedExtensionInterface()
+    {
+        $this->assertClassImplements(MessageReceivedExtensionInterface::class, LogExtension::class);
+    }
+
+    public function testShouldImplementPostMessageReceivedExtensionInterface()
+    {
+        $this->assertClassImplements(PostMessageReceivedExtensionInterface::class, LogExtension::class);
+    }
+
+    public function testShouldSubClassOfLogExtension()
+    {
+        $this->assertClassExtends(\Enqueue\Consumption\Extension\LogExtension::class, LogExtension::class);
+    }
+
+    public function testCouldBeConstructedWithoutAnyArguments()
+    {
+        new LogExtension();
+    }
+
+    public function testShouldLogStartOnStart()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('debug')
+            ->with('Consumption has started')
+        ;
+
+        $context = new Start($this->createContextMock(), $logger, [], 1, 1);
+
+        $extension = new LogExtension();
+        $extension->onStart($context);
+    }
+
+    public function testShouldLogEndOnEnd()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('debug')
+            ->with('Consumption has ended')
+        ;
+
+        $context = new End($this->createContextMock(), 1, 2, $logger);
+
+        $extension = new LogExtension();
+        $extension->onEnd($context);
+    }
+
+    public function testShouldLogMessageReceived()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('debug')
+            ->with('Received from {queueName}	{body}', [
+                'queueName' => 'aQueue',
+                'redelivered' => false,
+                'body' => Stringify::that('aBody'),
+                'properties' => Stringify::that(['aProp' => 'aPropVal']),
+                'headers' => Stringify::that(['aHeader' => 'aHeaderVal']),
+            ])
+        ;
+
+        $consumerMock = $this->createConsumerStub(new NullQueue('aQueue'));
+        $message = new NullMessage('aBody');
+        $message->setProperty('aProp', 'aPropVal');
+        $message->setHeader('aHeader', 'aHeaderVal');
+
+        $context = new MessageReceived($this->createContextMock(), $consumerMock, $message, $this->createProcessorMock(), 1, $logger);
+
+        $extension = new LogExtension();
+        $extension->onMessageReceived($context);
+    }
+
+    public function testShouldLogMessageProcessedWithStringResult()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('log')
+            ->with(LogLevel::INFO,
+                'Processed from {queueName}	{body}	{result}',
+                [
+                    'queueName' => 'aQueue',
+                    'body' => Stringify::that('aBody'),
+                    'properties' => Stringify::that(['aProp' => 'aPropVal']),
+                    'headers' => Stringify::that(['aHeader' => 'aHeaderVal']),
+                    'result' => 'aResult',
+                    'reason' => '',
+                ]
+            )
+        ;
+
+        $consumerMock = $this->createConsumerStub(new NullQueue('aQueue'));
+        $message = new NullMessage('aBody');
+        $message->setProperty('aProp', 'aPropVal');
+        $message->setHeader('aHeader', 'aHeaderVal');
+
+        $context = new PostMessageReceived($this->createContextMock(), $consumerMock, $message, 'aResult', 1, $logger);
+
+        $extension = new LogExtension();
+        $extension->onPostMessageReceived($context);
+    }
+
+    public function testShouldLogRejectedMessageAsError()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('log')
+            ->with(LogLevel::ERROR,
+                'Processed from {queueName}	{body}	{result}',
+                [
+                    'queueName' => 'aQueue',
+                    'body' => Stringify::that('aBody'),
+                    'properties' => Stringify::that(['aProp' => 'aPropVal']),
+                    'headers' => Stringify::that(['aHeader' => 'aHeaderVal']),
+                    'result' => 'reject',
+                    'reason' => '',
+                ]
+            )
+        ;
+
+        $consumerMock = $this->createConsumerStub(new NullQueue('aQueue'));
+        $message = new NullMessage('aBody');
+        $message->setProperty('aProp', 'aPropVal');
+        $message->setHeader('aHeader', 'aHeaderVal');
+
+        $context = new PostMessageReceived($this->createContextMock(), $consumerMock, $message, Processor::REJECT, 1, $logger);
+
+        $extension = new LogExtension();
+        $extension->onPostMessageReceived($context);
+    }
+
+    public function testShouldLogMessageProcessedWithResultObject()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('log')
+            ->with(LogLevel::INFO,
+                'Processed from {queueName}	{body}	{result}',
+                [
+                    'queueName' => 'aQueue',
+                    'body' => Stringify::that('aBody'),
+                    'properties' => Stringify::that(['aProp' => 'aPropVal']),
+                    'headers' => Stringify::that(['aHeader' => 'aHeaderVal']),
+                    'result' => 'ack',
+                    'reason' => '',
+                ]
+            )
+        ;
+
+        $consumerMock = $this->createConsumerStub(new NullQueue('aQueue'));
+        $message = new NullMessage('aBody');
+        $message->setProperty('aProp', 'aPropVal');
+        $message->setHeader('aHeader', 'aHeaderVal');
+
+        $context = new PostMessageReceived($this->createContextMock(), $consumerMock, $message, Result::ack(), 1, $logger);
+
+        $extension = new LogExtension();
+        $extension->onPostMessageReceived($context);
+    }
+
+    public function testShouldLogMessageProcessedWithReasonResultObject()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('log')
+            ->with(LogLevel::INFO,
+                'Processed from {queueName}	{body}	{result} {reason}',
+                [
+                    'queueName' => 'aQueue',
+                    'body' => Stringify::that('aBody'),
+                    'properties' => Stringify::that(['aProp' => 'aPropVal']),
+                    'headers' => Stringify::that(['aHeader' => 'aHeaderVal']),
+                    'result' => 'ack',
+                    'reason' => 'aReason',
+                ]
+            )
+        ;
+
+        $consumerMock = $this->createConsumerStub(new NullQueue('aQueue'));
+        $message = new NullMessage('aBody');
+        $message->setProperty('aProp', 'aPropVal');
+        $message->setHeader('aHeader', 'aHeaderVal');
+
+        $context = new PostMessageReceived($this->createContextMock(), $consumerMock, $message, Result::ack('aReason'), 1, $logger);
+
+        $extension = new LogExtension();
+        $extension->onPostMessageReceived($context);
+    }
+
+    public function testShouldLogProcessedCommandMessageWithStringResult()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('log')
+            ->with(LogLevel::INFO,
+                '[client] Processed {command}	{body}	{result}',
+                [
+                    'queueName' => 'aQueue',
+                    'body' => Stringify::that('aBody'),
+                    'properties' => Stringify::that(['aProp' => 'aPropVal', Config::COMMAND => 'aCommand']),
+                    'headers' => Stringify::that(['aHeader' => 'aHeaderVal']),
+                    'result' => 'aResult',
+                    'reason' => '',
+                    'command' => 'aCommand',
+                ]
+            )
+        ;
+
+        $consumerMock = $this->createConsumerStub(new NullQueue('aQueue'));
+        $message = new NullMessage('aBody');
+        $message->setProperty(Config::COMMAND, 'aCommand');
+        $message->setProperty('aProp', 'aPropVal');
+        $message->setHeader('aHeader', 'aHeaderVal');
+
+        $context = new PostMessageReceived($this->createContextMock(), $consumerMock, $message, 'aResult', 1, $logger);
+
+        $extension = new LogExtension();
+        $extension->onPostMessageReceived($context);
+    }
+
+    public function testShouldLogRejectedCommandMessageAsError()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('log')
+            ->with(LogLevel::ERROR,
+                '[client] Processed {command}	{body}	{result}',
+                [
+                    'queueName' => 'aQueue',
+                    'body' => Stringify::that('aBody'),
+                    'properties' => Stringify::that(['aProp' => 'aPropVal', Config::COMMAND => 'aCommand']),
+                    'headers' => Stringify::that(['aHeader' => 'aHeaderVal']),
+                    'result' => 'reject',
+                    'reason' => '',
+                    'command' => 'aCommand',
+                ]
+            )
+        ;
+
+        $consumerMock = $this->createConsumerStub(new NullQueue('aQueue'));
+        $message = new NullMessage('aBody');
+        $message->setProperty('aProp', 'aPropVal');
+        $message->setProperty(Config::COMMAND, 'aCommand');
+        $message->setHeader('aHeader', 'aHeaderVal');
+
+        $context = new PostMessageReceived($this->createContextMock(), $consumerMock, $message, Processor::REJECT, 1, $logger);
+
+        $extension = new LogExtension();
+        $extension->onPostMessageReceived($context);
+    }
+
+    public function testShouldLogProcessedCommandMessageWithResultObject()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('log')
+            ->with(LogLevel::INFO,
+                '[client] Processed {command}	{body}	{result}',
+                [
+                    'queueName' => 'aQueue',
+                    'body' => Stringify::that('aBody'),
+                    'properties' => Stringify::that(['aProp' => 'aPropVal', Config::COMMAND => 'aCommand']),
+                    'headers' => Stringify::that(['aHeader' => 'aHeaderVal']),
+                    'result' => 'ack',
+                    'reason' => '',
+                    'command' => 'aCommand',
+                ]
+            )
+        ;
+
+        $consumerMock = $this->createConsumerStub(new NullQueue('aQueue'));
+        $message = new NullMessage('aBody');
+        $message->setProperty('aProp', 'aPropVal');
+        $message->setProperty(Config::COMMAND, 'aCommand');
+        $message->setHeader('aHeader', 'aHeaderVal');
+
+        $context = new PostMessageReceived($this->createContextMock(), $consumerMock, $message, Result::ack(), 1, $logger);
+
+        $extension = new LogExtension();
+        $extension->onPostMessageReceived($context);
+    }
+
+    public function testShouldLogProcessedCommandMessageWithReasonResultObject()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('log')
+            ->with(LogLevel::INFO,
+                '[client] Processed {command}	{body}	{result} {reason}',
+                [
+                    'queueName' => 'aQueue',
+                    'body' => Stringify::that('aBody'),
+                    'properties' => Stringify::that(['aProp' => 'aPropVal', Config::COMMAND => 'aCommand']),
+                    'headers' => Stringify::that(['aHeader' => 'aHeaderVal']),
+                    'result' => 'ack',
+                    'reason' => 'aReason',
+                    'command' => 'aCommand',
+                ]
+            )
+        ;
+
+        $consumerMock = $this->createConsumerStub(new NullQueue('aQueue'));
+        $message = new NullMessage('aBody');
+        $message->setProperty('aProp', 'aPropVal');
+        $message->setProperty(Config::COMMAND, 'aCommand');
+        $message->setHeader('aHeader', 'aHeaderVal');
+
+        $context = new PostMessageReceived($this->createContextMock(), $consumerMock, $message, Result::ack('aReason'), 1, $logger);
+
+        $extension = new LogExtension();
+        $extension->onPostMessageReceived($context);
+    }
+
+    public function testShouldLogProcessedTopicProcessorMessageWithStringResult()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('log')
+            ->with(LogLevel::INFO,
+                '[client] Processed {topic} -> {processor}	{body}	{result}',
+                [
+                    'queueName' => 'aQueue',
+                    'body' => Stringify::that('aBody'),
+                    'properties' => Stringify::that(['aProp' => 'aPropVal', Config::TOPIC => 'aTopic', Config::PROCESSOR => 'aProcessor']),
+                    'headers' => Stringify::that(['aHeader' => 'aHeaderVal']),
+                    'result' => 'aResult',
+                    'reason' => '',
+                    'topic' => 'aTopic',
+                    'processor' => 'aProcessor',
+                ]
+            )
+        ;
+
+        $consumerMock = $this->createConsumerStub(new NullQueue('aQueue'));
+        $message = new NullMessage('aBody');
+        $message->setProperty(Config::TOPIC, 'aTopic');
+        $message->setProperty(Config::PROCESSOR, 'aProcessor');
+        $message->setProperty('aProp', 'aPropVal');
+        $message->setHeader('aHeader', 'aHeaderVal');
+
+        $context = new PostMessageReceived($this->createContextMock(), $consumerMock, $message, 'aResult', 1, $logger);
+
+        $extension = new LogExtension();
+        $extension->onPostMessageReceived($context);
+    }
+
+    public function testShouldLogRejectedTopicProcessorMessageAsError()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('log')
+            ->with(LogLevel::ERROR,
+                '[client] Processed {topic} -> {processor}	{body}	{result}',
+                [
+                    'queueName' => 'aQueue',
+                    'body' => Stringify::that('aBody'),
+                    'properties' => Stringify::that(['aProp' => 'aPropVal', Config::TOPIC => 'aTopic', Config::PROCESSOR => 'aProcessor']),
+                    'headers' => Stringify::that(['aHeader' => 'aHeaderVal']),
+                    'result' => 'reject',
+                    'reason' => '',
+                    'topic' => 'aTopic',
+                    'processor' => 'aProcessor',
+                ]
+            )
+        ;
+
+        $consumerMock = $this->createConsumerStub(new NullQueue('aQueue'));
+        $message = new NullMessage('aBody');
+        $message->setProperty(Config::TOPIC, 'aTopic');
+        $message->setProperty(Config::PROCESSOR, 'aProcessor');
+        $message->setProperty('aProp', 'aPropVal');
+        $message->setHeader('aHeader', 'aHeaderVal');
+
+        $context = new PostMessageReceived($this->createContextMock(), $consumerMock, $message, Processor::REJECT, 1, $logger);
+
+        $extension = new LogExtension();
+        $extension->onPostMessageReceived($context);
+    }
+
+    public function testShouldLogProcessedTopicProcessorMessageWithResultObject()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('log')
+            ->with(LogLevel::INFO,
+                '[client] Processed {topic} -> {processor}	{body}	{result}',
+                [
+                    'queueName' => 'aQueue',
+                    'body' => Stringify::that('aBody'),
+                    'properties' => Stringify::that(['aProp' => 'aPropVal', Config::TOPIC => 'aTopic', Config::PROCESSOR => 'aProcessor']),
+                    'headers' => Stringify::that(['aHeader' => 'aHeaderVal']),
+                    'result' => 'ack',
+                    'reason' => '',
+                    'topic' => 'aTopic',
+                    'processor' => 'aProcessor',
+                ]
+            )
+        ;
+
+        $consumerMock = $this->createConsumerStub(new NullQueue('aQueue'));
+        $message = new NullMessage('aBody');
+        $message->setProperty(Config::TOPIC, 'aTopic');
+        $message->setProperty(Config::PROCESSOR, 'aProcessor');
+        $message->setProperty('aProp', 'aPropVal');
+        $message->setHeader('aHeader', 'aHeaderVal');
+
+        $context = new PostMessageReceived($this->createContextMock(), $consumerMock, $message, Result::ack(), 1, $logger);
+
+        $extension = new LogExtension();
+        $extension->onPostMessageReceived($context);
+    }
+
+    public function testShouldLogProcessedTopicProcessorMessageWithReasonResultObject()
+    {
+        $logger = $this->createLogger();
+        $logger
+            ->expects($this->once())
+            ->method('log')
+            ->with(LogLevel::INFO,
+                '[client] Processed {topic} -> {processor}	{body}	{result} {reason}',
+                [
+                    'queueName' => 'aQueue',
+                    'body' => Stringify::that('aBody'),
+                    'properties' => Stringify::that(['aProp' => 'aPropVal', Config::TOPIC => 'aTopic', Config::PROCESSOR => 'aProcessor']),
+                    'headers' => Stringify::that(['aHeader' => 'aHeaderVal']),
+                    'result' => 'ack',
+                    'reason' => 'aReason',
+                    'topic' => 'aTopic',
+                    'processor' => 'aProcessor',
+                ]
+            )
+        ;
+
+        $consumerMock = $this->createConsumerStub(new NullQueue('aQueue'));
+        $message = new NullMessage('aBody');
+        $message->setProperty(Config::TOPIC, 'aTopic');
+        $message->setProperty(Config::PROCESSOR, 'aProcessor');
+        $message->setProperty('aProp', 'aPropVal');
+        $message->setHeader('aHeader', 'aHeaderVal');
+
+        $context = new PostMessageReceived($this->createContextMock(), $consumerMock, $message, Result::ack('aReason'), 1, $logger);
+
+        $extension = new LogExtension();
+        $extension->onPostMessageReceived($context);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createConsumerStub(Queue $queue): Consumer
+    {
+        $consumerMock = $this->createMock(Consumer::class);
+        $consumerMock
+            ->expects($this->any())
+            ->method('getQueue')
+            ->willReturn($queue)
+        ;
+
+        return $consumerMock;
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createContextMock(): Context
+    {
+        return $this->createMock(Context::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createProcessorMock(): Processor
+    {
+        return $this->createMock(Processor::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|LoggerInterface
+     */
+    private function createLogger()
+    {
+        return $this->createMock(LoggerInterface::class);
+    }
+}

From eeecfe1dd74f2af7f1f4ff0e97cf917340907bc4 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 8 Oct 2018 19:02:41 +0300
Subject: [PATCH 162/337] fix tests.

---
 Client/ConsumptionExtension/FlushSpoolProducerExtension.php | 3 ---
 Consumption/ChainExtension.php                              | 2 +-
 2 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/Client/ConsumptionExtension/FlushSpoolProducerExtension.php b/Client/ConsumptionExtension/FlushSpoolProducerExtension.php
index b7e9ca0..6682cad 100644
--- a/Client/ConsumptionExtension/FlushSpoolProducerExtension.php
+++ b/Client/ConsumptionExtension/FlushSpoolProducerExtension.php
@@ -15,9 +15,6 @@ class FlushSpoolProducerExtension implements PostMessageReceivedExtensionInterfa
      */
     private $producer;
 
-    /**
-     * @param SpoolProducer $producer
-     */
     public function __construct(SpoolProducer $producer)
     {
         $this->producer = $producer;
diff --git a/Consumption/ChainExtension.php b/Consumption/ChainExtension.php
index e497bd9..6911b5b 100644
--- a/Consumption/ChainExtension.php
+++ b/Consumption/ChainExtension.php
@@ -124,7 +124,7 @@ public function __construct(array $extensions)
 
     public function onInitLogger(InitLogger $context): void
     {
-        foreach ($this->endExtensions as $extension) {
+        foreach ($this->initLoggerExtensions as $extension) {
             $extension->onInitLogger($context);
         }
     }

From c9c9fc719f438801b22e5e3710cae8bd7d47d504 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 9 Oct 2018 12:15:19 +0300
Subject: [PATCH 163/337] [client] Move services definition to ClientFactory.

---
 Symfony/DependencyInjection/ClientFactory.php | 100 ++++++++++++++++++
 .../FormatClientNameTrait.php                 |  14 +++
 .../DependencyInjection/TransportFactory.php  |   3 +-
 3 files changed, 116 insertions(+), 1 deletion(-)

diff --git a/Symfony/DependencyInjection/ClientFactory.php b/Symfony/DependencyInjection/ClientFactory.php
index a2c09af..b4d0775 100644
--- a/Symfony/DependencyInjection/ClientFactory.php
+++ b/Symfony/DependencyInjection/ClientFactory.php
@@ -2,8 +2,24 @@
 
 namespace Enqueue\Symfony\DependencyInjection;
 
+use Enqueue\Client\ChainExtension;
+use Enqueue\Client\Config;
+use Enqueue\Client\ConsumptionExtension\SetRouterPropertiesExtension;
+use Enqueue\Client\DelegateProcessor;
+use Enqueue\Client\DriverFactory;
 use Enqueue\Client\DriverInterface;
+use Enqueue\Client\Producer;
+use Enqueue\Client\ProducerInterface;
+use Enqueue\Client\RouteCollection;
+use Enqueue\Client\RouterProcessor;
+use Enqueue\Client\SpoolProducer;
+use Enqueue\Consumption\ChainExtension as ConsumptionChainExtension;
+use Enqueue\Consumption\QueueConsumer;
+use Enqueue\Rpc\RpcFactory;
+use Enqueue\Symfony\ContainerProcessorRegistry;
+use Interop\Queue\Context;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Reference;
 
 /**
@@ -11,6 +27,8 @@
  */
 final class ClientFactory
 {
+    use FormatClientNameTrait;
+
     /**
      * @var string
      */
@@ -25,6 +43,88 @@ public function __construct(string $name)
         $this->name = $name;
     }
 
+    public function build(ContainerBuilder $container, array $config): void
+    {
+        $container->register($this->format('context'), Context::class)
+            ->setFactory([$this->reference('driver'), 'getContext'])
+        ;
+
+        $container->register($this->format('driver_factory'), DriverFactory::class)
+            ->addArgument($this->reference('config'))
+            ->addArgument($this->reference('route_collection'))
+        ;
+
+        $container->register($this->format('config'), Config::class);
+
+        $container->register($this->format('route_collection'), RouteCollection::class)
+            ->addArgument([])
+            ->setFactory([RouteCollection::class, 'fromArray'])
+        ;
+
+        $container->register($this->format('producer'), Producer::class)
+            ->addArgument($this->reference('driver'))
+            ->addArgument($this->reference('rpc_factory'))
+            ->addArgument($this->reference('client_extensions'))
+        ;
+
+        $container->register($this->format('spool_producer'), SpoolProducer::class)
+            ->addArgument($this->reference('producer'))
+        ;
+
+        $container->register($this->format('client_extensions'), ChainExtension::class)
+            ->addArgument([])
+        ;
+
+        $container->register($this->format('rpc_factory'), RpcFactory::class)
+            ->addArgument($this->reference('context'))
+        ;
+
+        $container->register($this->format('router_processor'), RouterProcessor::class)
+            ->addArgument($this->reference('driver'))
+        ;
+
+        $container->register($this->format('processor_registry'), ContainerProcessorRegistry::class);
+
+        $container->register($this->format('delegate_processor'), DelegateProcessor::class)
+            ->addArgument($this->reference('processor_registry'))
+        ;
+
+        $container->register($this->format('set_router_properties_extension'), SetRouterPropertiesExtension::class)
+            ->addArgument($this->reference('driver'))
+            ->addTag('enqueue.consumption_extension', ['priority' => 100, 'client' => $this->name])
+        ;
+
+        $container->register($this->format('queue_consumer'), QueueConsumer::class)
+            ->addArgument($this->reference('context'))
+            ->addArgument($this->reference('consumption_extensions'))
+            ->addArgument([])
+            ->addArgument(null)
+            ->addArgument(null)
+            ->addArgument(null)
+        ;
+
+        $container->register($this->format('queue_consumer'), QueueConsumer::class)
+            ->addArgument($this->reference('context'))
+            ->addArgument($this->reference('consumption_extensions'))
+            ->addArgument([])
+            ->addArgument(new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE))
+        ;
+
+        $container->register($this->format('consumption_extensions'), ConsumptionChainExtension::class)
+            ->addArgument([])
+        ;
+
+        if ('default' === $this->name) {
+            $container->setAlias(ProducerInterface::class, $this->format('producer'))
+                ->setPublic(true)
+            ;
+
+            $container->setAlias(SpoolProducer::class, $this->format('spool_producer'))
+                ->setPublic(true)
+            ;
+        }
+    }
+
     public function createDriver(ContainerBuilder $container, array $config): string
     {
         $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
diff --git a/Symfony/DependencyInjection/FormatClientNameTrait.php b/Symfony/DependencyInjection/FormatClientNameTrait.php
index 8065fc3..ea2e6a0 100644
--- a/Symfony/DependencyInjection/FormatClientNameTrait.php
+++ b/Symfony/DependencyInjection/FormatClientNameTrait.php
@@ -2,10 +2,24 @@
 
 namespace Enqueue\Symfony\DependencyInjection;
 
+use Symfony\Component\DependencyInjection\Reference;
+
 trait FormatClientNameTrait
 {
     abstract protected function getName(): string;
 
+    private function reference(string $serviceName): Reference
+    {
+        return new Reference($this->format($serviceName));
+    }
+
+    private function parameter(string $serviceName): string
+    {
+        $fullName = $this->format($serviceName, false);
+
+        return "%$fullName%";
+    }
+
     private function format(string $serviceName, $parameter = false): string
     {
         $pattern = 'enqueue.client.%s.'.$serviceName;
diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index 0234716..f70b1cb 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -16,6 +16,7 @@
 use Interop\Queue\Context;
 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Reference;
 
 /**
@@ -180,7 +181,7 @@ public function buildQueueConsumer(ContainerBuilder $container, array $config):
             ->addArgument(new Reference($contextId))
             ->addArgument(new Reference($this->format('consumption_extensions')))
             ->addArgument([])
-            ->addArgument(null)
+            ->addArgument(new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE))
             ->addArgument($this->format('receive_timeout', true))
         ;
 

From c9fe1eacdced1635b9698db8677200fb2a497022 Mon Sep 17 00:00:00 2001
From: Roman Samarsky <rosamarsky@gmail.com>
Date: Fri, 12 Oct 2018 17:16:07 +0300
Subject: [PATCH 164/337] Fixed exception message in
 testThrowErrorIfServiceDoesNotImplementProcessorReturnType

---
 Tests/Symfony/ContainerProcessorRegistryTest.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/Symfony/ContainerProcessorRegistryTest.php b/Tests/Symfony/ContainerProcessorRegistryTest.php
index fe84c2e..bb3cacb 100644
--- a/Tests/Symfony/ContainerProcessorRegistryTest.php
+++ b/Tests/Symfony/ContainerProcessorRegistryTest.php
@@ -69,7 +69,7 @@ public function testThrowErrorIfServiceDoesNotImplementProcessorReturnType()
         $registry = new ContainerProcessorRegistry($containerMock);
 
         $this->expectException(\TypeError::class);
-        $this->expectExceptionMessage('Return value of Enqueue\Symfony\ContainerProcessorRegistry::get() must implement interface Interop\Queue\PsrProcessor, instance of stdClass returned');
+        $this->expectExceptionMessage('Return value of Enqueue\Symfony\ContainerProcessorRegistry::get() must implement interface Interop\Queue\Processor, instance of stdClass returned');
         $registry->get('processor-name');
     }
 

From 569209b654cd3f5e1084a9685086b3202b290dc4 Mon Sep 17 00:00:00 2001
From: Samuel NELA <hello@samnela.com>
Date: Wed, 17 Oct 2018 21:05:21 +0200
Subject: [PATCH 165/337] Remove deprecated testcase implementation

---
 Tests/Client/Driver/DbalDriverTest.php                 | 3 ++-
 Tests/Client/Driver/MongodbDriverTest.php              | 3 ++-
 Tests/Consumption/FallbackSubscriptionConsumerTest.php | 3 ++-
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/Tests/Client/Driver/DbalDriverTest.php b/Tests/Client/Driver/DbalDriverTest.php
index c9fbc6e..554a399 100644
--- a/Tests/Client/Driver/DbalDriverTest.php
+++ b/Tests/Client/Driver/DbalDriverTest.php
@@ -16,8 +16,9 @@
 use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\Queue as InteropQueue;
 use Interop\Queue\Topic as InteropTopic;
+use PHPUnit\Framework\TestCase;
 
-class DbalDriverTest extends \PHPUnit_Framework_TestCase
+class DbalDriverTest extends TestCase
 {
     use ClassExtensionTrait;
     use GenericDriverTestsTrait;
diff --git a/Tests/Client/Driver/MongodbDriverTest.php b/Tests/Client/Driver/MongodbDriverTest.php
index e74f7bd..697c757 100644
--- a/Tests/Client/Driver/MongodbDriverTest.php
+++ b/Tests/Client/Driver/MongodbDriverTest.php
@@ -16,8 +16,9 @@
 use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\Queue as InteropQueue;
 use Interop\Queue\Topic as InteropTopic;
+use PHPUnit\Framework\TestCase;
 
-class MongodbDriverTest extends \PHPUnit_Framework_TestCase
+class MongodbDriverTest extends TestCase
 {
     use ClassExtensionTrait;
     use GenericDriverTestsTrait;
diff --git a/Tests/Consumption/FallbackSubscriptionConsumerTest.php b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
index ff67b1a..588237c 100644
--- a/Tests/Consumption/FallbackSubscriptionConsumerTest.php
+++ b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
@@ -7,8 +7,9 @@
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Queue as InteropQueue;
 use Interop\Queue\SubscriptionConsumer;
+use PHPUnit\Framework\TestCase;
 
-class FallbackSubscriptionConsumerTest extends \PHPUnit_Framework_TestCase
+class FallbackSubscriptionConsumerTest extends TestCase
 {
     public function testShouldImplementSubscriptionConsumerInterface()
     {

From 7886df2cb9a3dfa3c8c2db304183bdfffb06a030 Mon Sep 17 00:00:00 2001
From: adumas37 <adumas37@outlook.com>
Date: Thu, 18 Oct 2018 14:20:46 +0200
Subject: [PATCH 166/337] Fixing composer.json to require enqueue/dsn

---
 composer.json | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index e61270c..588a8f4 100644
--- a/composer.json
+++ b/composer.json
@@ -34,7 +34,8 @@
         "enqueue/test": "0.9.x-dev",
         "enqueue/simple-client": "0.9.x-dev",
         "enqueue/mongodb": "0.9.x-dev",
-        "empi89/php-amqp-stubs": "*@dev"
+        "empi89/php-amqp-stubs": "*@dev",
+        "enqueue/dsn": "0.9.x-dev"
     },
     "suggest": {
         "symfony/console": "^2.8|^3|^4 If you want to use li commands",

From 77c254367265b171bdece6178dc359864b299b57 Mon Sep 17 00:00:00 2001
From: Roman Samarsky <rosamarsky@gmail.com>
Date: Thu, 18 Oct 2018 17:58:09 +0300
Subject: [PATCH 167/337] fixes
 https://github.com/php-enqueue/enqueue-dev/issues/557

---
 Client/Driver/RdKafkaDriver.php           | 13 ++-----------
 Client/Driver/RedisDriver.php             |  2 ++
 Tests/Client/Driver/RdKafkaDriverTest.php |  5 -----
 3 files changed, 4 insertions(+), 16 deletions(-)

diff --git a/Client/Driver/RdKafkaDriver.php b/Client/Driver/RdKafkaDriver.php
index ecb711c..37013bb 100644
--- a/Client/Driver/RdKafkaDriver.php
+++ b/Client/Driver/RdKafkaDriver.php
@@ -1,10 +1,11 @@
 <?php
 
+declare(strict_types=1);
+
 namespace Enqueue\Client\Driver;
 
 use Enqueue\RdKafka\RdKafkaContext;
 use Enqueue\RdKafka\RdKafkaTopic;
-use Interop\Queue\Destination;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
@@ -44,14 +45,4 @@ public function setupBroker(LoggerInterface $logger = null): void
             $this->getContext()->createConsumer($queue);
         }
     }
-
-    /**
-     * @return RdKafkaTopic
-     */
-    protected function createRouterTopic(): Destination
-    {
-        return $this->doCreateTopic(
-            $this->createTransportRouterTopicName($this->getConfig()->getRouterTopic(), true)
-        );
-    }
 }
diff --git a/Client/Driver/RedisDriver.php b/Client/Driver/RedisDriver.php
index 641d3bf..493cb7c 100644
--- a/Client/Driver/RedisDriver.php
+++ b/Client/Driver/RedisDriver.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace Enqueue\Client\Driver;
 
 use Enqueue\Redis\RedisContext;
diff --git a/Tests/Client/Driver/RdKafkaDriverTest.php b/Tests/Client/Driver/RdKafkaDriverTest.php
index f3474e7..659590b 100644
--- a/Tests/Client/Driver/RdKafkaDriverTest.php
+++ b/Tests/Client/Driver/RdKafkaDriverTest.php
@@ -118,11 +118,6 @@ protected function createMessage(): InteropMessage
         return new RdKafkaMessage();
     }
 
-    protected function getRouterTransportName(): string
-    {
-        return 'aprefix.router';
-    }
-
     /**
      * @return Config
      */

From 681bc4f3c983f3257bc0719134c20d914d9d144e Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 18 Oct 2018 18:36:32 +0300
Subject: [PATCH 168/337] [client][bundle] Move client configuration to
 ClientFactory.

---
 Symfony/DependencyInjection/ClientFactory.php | 76 +++++++++++++++----
 .../FormatClientNameTrait.php                 |  5 +-
 .../TransportFactoryTest.php                  |  3 +-
 3 files changed, 66 insertions(+), 18 deletions(-)

diff --git a/Symfony/DependencyInjection/ClientFactory.php b/Symfony/DependencyInjection/ClientFactory.php
index b4d0775..fd5a363 100644
--- a/Symfony/DependencyInjection/ClientFactory.php
+++ b/Symfony/DependencyInjection/ClientFactory.php
@@ -4,6 +4,9 @@
 
 use Enqueue\Client\ChainExtension;
 use Enqueue\Client\Config;
+use Enqueue\Client\ConsumptionExtension\DelayRedeliveredMessageExtension;
+use Enqueue\Client\ConsumptionExtension\ExclusiveCommandExtension;
+use Enqueue\Client\ConsumptionExtension\FlushSpoolProducerExtension;
 use Enqueue\Client\ConsumptionExtension\SetRouterPropertiesExtension;
 use Enqueue\Client\DelegateProcessor;
 use Enqueue\Client\DriverFactory;
@@ -13,6 +16,7 @@
 use Enqueue\Client\RouteCollection;
 use Enqueue\Client\RouterProcessor;
 use Enqueue\Client\SpoolProducer;
+use Enqueue\Client\TraceableProducer;
 use Enqueue\Consumption\ChainExtension as ConsumptionChainExtension;
 use Enqueue\Consumption\QueueConsumer;
 use Enqueue\Rpc\RpcFactory;
@@ -54,7 +58,21 @@ public function build(ContainerBuilder $container, array $config): void
             ->addArgument($this->reference('route_collection'))
         ;
 
-        $container->register($this->format('config'), Config::class);
+        $container->register($this->format('config'), Config::class)
+            ->setArguments([
+                $config['prefix'],
+                $config['app_name'],
+                $config['router_topic'],
+                $config['router_queue'],
+                $config['default_processor_queue'],
+                $config['router_processor'],
+                // @todo should be driver options.
+                $config['transport'],
+            ]);
+
+        $container->setParameter($this->format('router_processor'), $config['router_processor']);
+        $container->setParameter($this->format('router_queue_name'), $config['router_queue']);
+        $container->setParameter($this->format('default_queue_name'), $config['default_processor_queue']);
 
         $container->register($this->format('route_collection'), RouteCollection::class)
             ->addArgument([])
@@ -98,31 +116,59 @@ public function build(ContainerBuilder $container, array $config): void
             ->addArgument($this->reference('context'))
             ->addArgument($this->reference('consumption_extensions'))
             ->addArgument([])
-            ->addArgument(null)
-            ->addArgument(null)
-            ->addArgument(null)
+            ->addArgument($this->reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE))
+            ->addArgument($config['consumption']['receive_timeout'])
         ;
 
-        $container->register($this->format('queue_consumer'), QueueConsumer::class)
-            ->addArgument($this->reference('context'))
-            ->addArgument($this->reference('consumption_extensions'))
+        $container->register($this->format('consumption_extensions'), ConsumptionChainExtension::class)
             ->addArgument([])
-            ->addArgument(new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE))
         ;
 
-        $container->register($this->format('consumption_extensions'), ConsumptionChainExtension::class)
-            ->addArgument([])
+        $container->register($this->format('flush_spool_producer_extension'), FlushSpoolProducerExtension::class)
+            ->addArgument($this->reference('spool_producer'))
+            ->addTag('enqueue.consumption.extension', ['priority' => -100, 'client' => $this->name])
         ;
 
-        if ('default' === $this->name) {
-            $container->setAlias(ProducerInterface::class, $this->format('producer'))
-                ->setPublic(true)
+        $container->register($this->format('exclusive_command_extension'), ExclusiveCommandExtension::class)
+            ->addArgument($this->reference('driver'))
+            ->addTag('enqueue.consumption.extension', ['priority' => 100, 'client' => $this->name])
+        ;
+
+        if ($config['traceable_producer']) {
+            $container->register($this->format('traceable_producer'), TraceableProducer::class)
+                ->setDecoratedService($this->format('producer'))
+                ->addArgument($this->reference('traceable_producer.inner'))
+            ;
+        }
+
+        if ($config['redelivered_delay_time']) {
+            $container->register($this->format('delay_redelivered_message_extension'), DelayRedeliveredMessageExtension::class)
+                ->addArgument($this->reference('driver'))
+                ->addArgument($config['redelivered_delay_time'])
+                ->addTag('enqueue.consumption_extension', ['priority' => 10, 'client' => $this->name])
             ;
 
-            $container->setAlias(SpoolProducer::class, $this->format('spool_producer'))
-                ->setPublic(true)
+            $container->getDefinition('enqueue.client.default.delay_redelivered_message_extension')
+                ->replaceArgument(1, $config['redelivered_delay_time'])
             ;
         }
+
+        $locatorId = 'enqueue.locator';
+        if ($container->hasDefinition($locatorId)) {
+            $locator = $container->getDefinition($locatorId);
+            $locator->replaceArgument(0, array_replace($locator->getArgument(0), [
+                $this->format('queue_consumer') => $this->reference('queue_consumer'),
+                $this->format('driver') => $this->reference('driver'),
+                $this->format('delegate_processor') => $this->reference('delegate_processor'),
+                $this->format('producer') => $this->reference('producer'),
+            ]));
+        }
+
+        if ('default' === $this->name) {
+            $container->setAlias(ProducerInterface::class, $this->format('producer'));
+
+            $container->setAlias(SpoolProducer::class, $this->format('spool_producer'));
+        }
     }
 
     public function createDriver(ContainerBuilder $container, array $config): string
diff --git a/Symfony/DependencyInjection/FormatClientNameTrait.php b/Symfony/DependencyInjection/FormatClientNameTrait.php
index ea2e6a0..7880d12 100644
--- a/Symfony/DependencyInjection/FormatClientNameTrait.php
+++ b/Symfony/DependencyInjection/FormatClientNameTrait.php
@@ -2,15 +2,16 @@
 
 namespace Enqueue\Symfony\DependencyInjection;
 
+use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Reference;
 
 trait FormatClientNameTrait
 {
     abstract protected function getName(): string;
 
-    private function reference(string $serviceName): Reference
+    private function reference(string $serviceName, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE): Reference
     {
-        return new Reference($this->format($serviceName));
+        return new Reference($this->format($serviceName), $invalidBehavior);
     }
 
     private function parameter(string $serviceName): string
diff --git a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index 5da72c7..64afdc9 100644
--- a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -15,6 +15,7 @@
 use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
 use Symfony\Component\Config\Definition\Processor;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Reference;
 
 class TransportFactoryTest extends TestCase
@@ -388,7 +389,7 @@ public function testShouldBuildQueueConsumerWithDefaultOptions()
             new Reference('enqueue.transport.default.context'),
             new Reference('enqueue.transport.default.consumption_extensions'),
             [],
-            null,
+            new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE),
             '%enqueue.transport.default.receive_timeout%',
         ], $container->getDefinition('enqueue.transport.default.queue_consumer')->getArguments());
     }

From fb1fb31e96ce0b0cd4aac50fb7158b13371bb527 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Sun, 21 Oct 2018 12:26:26 +0300
Subject: [PATCH 169/337] [client] Move client config to the factory.

---
 .../DependencyInjection/ClientFactory.php     | 19 ++++++++++++++++++-
 .../DependencyInjection/ClientFactoryTest.php |  4 ++--
 2 files changed, 20 insertions(+), 3 deletions(-)
 rename Symfony/{ => Client}/DependencyInjection/ClientFactory.php (88%)
 rename Tests/Symfony/{ => Client}/DependencyInjection/ClientFactoryTest.php (93%)

diff --git a/Symfony/DependencyInjection/ClientFactory.php b/Symfony/Client/DependencyInjection/ClientFactory.php
similarity index 88%
rename from Symfony/DependencyInjection/ClientFactory.php
rename to Symfony/Client/DependencyInjection/ClientFactory.php
index fd5a363..e610ad8 100644
--- a/Symfony/DependencyInjection/ClientFactory.php
+++ b/Symfony/Client/DependencyInjection/ClientFactory.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Enqueue\Symfony\DependencyInjection;
+namespace Enqueue\Symfony\Client\DependencyInjection;
 
 use Enqueue\Client\ChainExtension;
 use Enqueue\Client\Config;
@@ -21,7 +21,9 @@
 use Enqueue\Consumption\QueueConsumer;
 use Enqueue\Rpc\RpcFactory;
 use Enqueue\Symfony\ContainerProcessorRegistry;
+use Enqueue\Symfony\DependencyInjection\FormatClientNameTrait;
 use Interop\Queue\Context;
+use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Reference;
@@ -47,6 +49,21 @@ public function __construct(string $name)
         $this->name = $name;
     }
 
+    public function addClientConfiguration(ArrayNodeDefinition $builder, bool $debug): void
+    {
+        $builder->children()
+            ->booleanNode('traceable_producer')->defaultValue($debug)->end()
+            ->scalarNode('prefix')->defaultValue('enqueue')->end()
+            ->scalarNode('app_name')->defaultValue('app')->end()
+            ->scalarNode('router_topic')->defaultValue('default')->cannotBeEmpty()->end()
+            ->scalarNode('router_queue')->defaultValue('default')->cannotBeEmpty()->end()
+            ->scalarNode('router_processor')->defaultValue($this->format('router_processor'))->end()
+            ->scalarNode('default_processor_queue')->defaultValue('default')->cannotBeEmpty()->end()
+            ->integerNode('redelivered_delay_time')->min(0)->defaultValue(0)->end()
+        ->end()->end()
+        ;
+    }
+
     public function build(ContainerBuilder $container, array $config): void
     {
         $container->register($this->format('context'), Context::class)
diff --git a/Tests/Symfony/DependencyInjection/ClientFactoryTest.php b/Tests/Symfony/Client/DependencyInjection/ClientFactoryTest.php
similarity index 93%
rename from Tests/Symfony/DependencyInjection/ClientFactoryTest.php
rename to Tests/Symfony/Client/DependencyInjection/ClientFactoryTest.php
index bb4cfe9..b872454 100644
--- a/Tests/Symfony/DependencyInjection/ClientFactoryTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/ClientFactoryTest.php
@@ -1,8 +1,8 @@
 <?php
 
-namespace Enqueue\Tests\Symfony\DependencyInjection;
+namespace Enqueue\Tests\Symfony\Client\DependencyInjection;
 
-use Enqueue\Symfony\DependencyInjection\ClientFactory;
+use Enqueue\Symfony\Client\DependencyInjection\ClientFactory;
 use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\DependencyInjection\ContainerBuilder;

From b019616f7f442818a98d8d6c553aae9b13d970a3 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Sun, 21 Oct 2018 13:52:25 +0300
Subject: [PATCH 170/337] [client] Rename resource key factoryClass ->
 driverClass

---
 Client/DriverFactory.php       |  2 +-
 Client/Resources.php           | 32 ++++++++++++++++----------------
 Tests/Client/ResourcesTest.php | 18 +++++++++---------
 3 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/Client/DriverFactory.php b/Client/DriverFactory.php
index 071ec0c..36534d5 100644
--- a/Client/DriverFactory.php
+++ b/Client/DriverFactory.php
@@ -33,7 +33,7 @@ public function create(ConnectionFactory $factory, string $dsn, array $config):
         $dsn = new Dsn($dsn);
 
         if ($driverInfo = $this->findDriverInfo($dsn, Resources::getAvailableDrivers())) {
-            $driverClass = $driverInfo['factoryClass'];
+            $driverClass = $driverInfo['driverClass'];
 
             if (RabbitMqDriver::class === $driverClass) {
                 if (false == $factory instanceof AmqpConnectionFactory) {
diff --git a/Client/Resources.php b/Client/Resources.php
index 56fc764..cdc8039 100644
--- a/Client/Resources.php
+++ b/Client/Resources.php
@@ -37,7 +37,7 @@ public static function getAvailableDrivers(): array
 
         $availableMap = [];
         foreach ($map as $item) {
-            if (class_exists($item['factoryClass'])) {
+            if (class_exists($item['driverClass'])) {
                 $availableMap[] = $item;
             }
         }
@@ -52,67 +52,67 @@ public static function getKnownDrivers(): array
 
             $map[] = [
                 'schemes' => ['amqp', 'amqps'],
-                'factoryClass' => AmqpDriver::class,
+                'driverClass' => AmqpDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/amqp-bunny'],
             ];
             $map[] = [
                 'schemes' => ['amqp', 'amqps'],
-                'factoryClass' => RabbitMqDriver::class,
+                'driverClass' => RabbitMqDriver::class,
                 'requiredSchemeExtensions' => ['rabbitmq'],
                 'packages' => ['enqueue/enqueue', 'enqueue/amqp-bunny'],
             ];
             $map[] = [
                 'schemes' => ['file'],
-                'factoryClass' => FsDriver::class,
+                'driverClass' => FsDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/fs'],
             ];
             $map[] = [
                 'schemes' => ['null'],
-                'factoryClass' => GenericDriver::class,
+                'driverClass' => GenericDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/null'],
             ];
             $map[] = [
                 'schemes' => ['gps'],
-                'factoryClass' => GpsDriver::class,
+                'driverClass' => GpsDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/gps'],
             ];
             $map[] = [
                 'schemes' => ['redis'],
-                'factoryClass' => RedisDriver::class,
+                'driverClass' => RedisDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/redis'],
             ];
             $map[] = [
                 'schemes' => ['sqs'],
-                'factoryClass' => SqsDriver::class,
+                'driverClass' => SqsDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/sqs'],
             ];
             $map[] = [
                 'schemes' => ['stomp'],
-                'factoryClass' => StompDriver::class,
+                'driverClass' => StompDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/stomp'],
             ];
             $map[] = [
                 'schemes' => ['stomp'],
-                'factoryClass' => RabbitMqStompDriver::class,
+                'driverClass' => RabbitMqStompDriver::class,
                 'requiredSchemeExtensions' => ['rabbitmq'],
                 'packages' => ['enqueue/enqueue', 'enqueue/stomp'],
             ];
             $map[] = [
                 'schemes' => ['kafka', 'rdkafka'],
-                'factoryClass' => RdKafkaDriver::class,
+                'driverClass' => RdKafkaDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/rdkafka'],
             ];
             $map[] = [
                 'schemes' => ['mongodb'],
-                'factoryClass' => MongodbDriver::class,
+                'driverClass' => MongodbDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/mongodb'],
             ];
@@ -132,19 +132,19 @@ public static function getKnownDrivers(): array
                     'sqlite3',
                     'sqlite',
                 ],
-                'factoryClass' => DbalDriver::class,
+                'driverClass' => DbalDriver::class,
                 'requiredSchemeExtensions' => [],
                 'package' => ['enqueue/enqueue', 'enqueue/dbal'],
             ];
             $map[] = [
                 'schemes' => ['gearman'],
-                'factoryClass' => GenericDriver::class,
+                'driverClass' => GenericDriver::class,
                 'requiredSchemeExtensions' => [],
                 'package' => ['enqueue/enqueue', 'enqueue/gearman'],
             ];
             $map[] = [
                 'schemes' => ['beanstalk'],
-                'factoryClass' => GenericDriver::class,
+                'driverClass' => GenericDriver::class,
                 'requiredSchemeExtensions' => [],
                 'package' => ['enqueue/enqueue', 'enqueue/pheanstalk'],
             ];
@@ -173,7 +173,7 @@ public static function addDriver(string $driverClass, array $schemes, array $req
         self::getKnownDrivers();
         self::$knownDrivers[] = [
             'schemes' => $schemes,
-            'factoryClass' => $driverClass,
+            'driverClass' => $driverClass,
             'requiredSchemeExtensions' => $requiredExtensions,
             'packages' => $packages,
         ];
diff --git a/Tests/Client/ResourcesTest.php b/Tests/Client/ResourcesTest.php
index 7311f31..c2d2b11 100644
--- a/Tests/Client/ResourcesTest.php
+++ b/Tests/Client/ResourcesTest.php
@@ -33,8 +33,8 @@ public function testShouldGetAvailableDriverInExpectedFormat()
 
         $driverInfo = $availableDrivers[0];
 
-        $this->assertArrayHasKey('factoryClass', $driverInfo);
-        $this->assertSame(AmqpDriver::class, $driverInfo['factoryClass']);
+        $this->assertArrayHasKey('driverClass', $driverInfo);
+        $this->assertSame(AmqpDriver::class, $driverInfo['driverClass']);
 
         $this->assertArrayHasKey('schemes', $driverInfo);
         $this->assertSame(['amqp', 'amqps'], $driverInfo['schemes']);
@@ -55,8 +55,8 @@ public function testShouldGetAvailableDriverWithRequiredExtensionInExpectedForma
 
         $driverInfo = $availableDrivers[1];
 
-        $this->assertArrayHasKey('factoryClass', $driverInfo);
-        $this->assertSame(RabbitMqDriver::class, $driverInfo['factoryClass']);
+        $this->assertArrayHasKey('driverClass', $driverInfo);
+        $this->assertSame(RabbitMqDriver::class, $driverInfo['driverClass']);
 
         $this->assertArrayHasKey('schemes', $driverInfo);
         $this->assertSame(['amqp', 'amqps'], $driverInfo['schemes']);
@@ -77,8 +77,8 @@ public function testShouldGetKnownDriversInExpectedFormat()
 
         $driverInfo = $knownDrivers[0];
 
-        $this->assertArrayHasKey('factoryClass', $driverInfo);
-        $this->assertSame(AmqpDriver::class, $driverInfo['factoryClass']);
+        $this->assertArrayHasKey('driverClass', $driverInfo);
+        $this->assertSame(AmqpDriver::class, $driverInfo['driverClass']);
 
         $this->assertArrayHasKey('schemes', $driverInfo);
         $this->assertSame(['amqp', 'amqps'], $driverInfo['schemes']);
@@ -126,7 +126,7 @@ public function testShouldAllowRegisterDriverThatIsNotInstalled()
 
         $driverInfo = end($availableDrivers);
 
-        $this->assertSame('theDriverClass', $driverInfo['factoryClass']);
+        $this->assertSame('theDriverClass', $driverInfo['driverClass']);
     }
 
     public function testShouldAllowGetPreviouslyRegisteredDriver()
@@ -144,8 +144,8 @@ public function testShouldAllowGetPreviouslyRegisteredDriver()
 
         $driverInfo = end($availableDrivers);
 
-        $this->assertArrayHasKey('factoryClass', $driverInfo);
-        $this->assertSame($driverClass, $driverInfo['factoryClass']);
+        $this->assertArrayHasKey('driverClass', $driverInfo);
+        $this->assertSame($driverClass, $driverInfo['driverClass']);
 
         $this->assertArrayHasKey('schemes', $driverInfo);
         $this->assertSame(['fooscheme', 'barscheme'], $driverInfo['schemes']);

From 6dfefd156fa253748eb9569fc34fc1c4a1b06f2b Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Sun, 21 Oct 2018 21:52:00 +0300
Subject: [PATCH 171/337] Make build processor registyry pass support multi
 transports.

---
 .../DependencyInjection/ClientFactory.php     |  1 -
 .../FormatClientNameTrait.php                 |  2 +-
 .../BuildConsumptionExtensionsPass.php        | 75 ++++++++++---------
 .../BuildProcessorRegistryPass.php            | 59 ++++++++-------
 .../FormatTransportNameTrait.php              | 15 ++++
 .../BuildConsumptionExtensionsPassTest.php    | 46 +++++++-----
 .../BuildProcessorRegistryPassTest.php        | 74 ++++++++++++++----
 7 files changed, 174 insertions(+), 98 deletions(-)
 rename Symfony/{ => Client}/DependencyInjection/FormatClientNameTrait.php (94%)

diff --git a/Symfony/Client/DependencyInjection/ClientFactory.php b/Symfony/Client/DependencyInjection/ClientFactory.php
index e610ad8..f78c1ab 100644
--- a/Symfony/Client/DependencyInjection/ClientFactory.php
+++ b/Symfony/Client/DependencyInjection/ClientFactory.php
@@ -21,7 +21,6 @@
 use Enqueue\Consumption\QueueConsumer;
 use Enqueue\Rpc\RpcFactory;
 use Enqueue\Symfony\ContainerProcessorRegistry;
-use Enqueue\Symfony\DependencyInjection\FormatClientNameTrait;
 use Interop\Queue\Context;
 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
diff --git a/Symfony/DependencyInjection/FormatClientNameTrait.php b/Symfony/Client/DependencyInjection/FormatClientNameTrait.php
similarity index 94%
rename from Symfony/DependencyInjection/FormatClientNameTrait.php
rename to Symfony/Client/DependencyInjection/FormatClientNameTrait.php
index 7880d12..6f76d2b 100644
--- a/Symfony/DependencyInjection/FormatClientNameTrait.php
+++ b/Symfony/Client/DependencyInjection/FormatClientNameTrait.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Enqueue\Symfony\DependencyInjection;
+namespace Enqueue\Symfony\Client\DependencyInjection;
 
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Reference;
diff --git a/Symfony/DependencyInjection/BuildConsumptionExtensionsPass.php b/Symfony/DependencyInjection/BuildConsumptionExtensionsPass.php
index f9a6a47..857ddf2 100644
--- a/Symfony/DependencyInjection/BuildConsumptionExtensionsPass.php
+++ b/Symfony/DependencyInjection/BuildConsumptionExtensionsPass.php
@@ -8,55 +8,60 @@
 
 final class BuildConsumptionExtensionsPass implements CompilerPassInterface
 {
-    /**
-     * @var string
-     */
-    private $name;
+    use FormatTransportNameTrait;
 
-    public function __construct(string $transportName)
-    {
-        if (empty($transportName)) {
-            throw new \InvalidArgumentException('The name could not be empty.');
-        }
-
-        $this->name = $transportName;
-    }
+    protected $name;
 
     public function process(ContainerBuilder $container): void
     {
-        $extensionsId = sprintf('enqueue.transport.%s.consumption_extensions', $this->name);
-        if (false == $container->hasDefinition($extensionsId)) {
-            return;
+        if (false == $container->hasParameter('enqueue.transports')) {
+            throw new \LogicException('The "enqueue.transports" parameter must be set.');
         }
 
-        $tags = $container->findTaggedServiceIds('enqueue.transport.consumption_extension');
+        $names = $container->getParameter('enqueue.transports');
 
-        $groupByPriority = [];
-        foreach ($tags as $serviceId => $tagAttributes) {
-            foreach ($tagAttributes as $tagAttribute) {
-                $transport = $tagAttribute['transport'] ?? 'default';
+        foreach ($names as $name) {
+            $this->name = $name;
 
-                if ($transport !== $this->name && 'all' !== $transport) {
-                    continue;
-                }
+            $extensionsId = $this->format('consumption_extensions');
+            if (false == $container->hasDefinition($extensionsId)) {
+                throw new \LogicException(sprintf('Service "%s" not found', $extensionsId));
+            }
+
+            $tags = $container->findTaggedServiceIds('enqueue.transport.consumption_extension');
+
+            $groupByPriority = [];
+            foreach ($tags as $serviceId => $tagAttributes) {
+                foreach ($tagAttributes as $tagAttribute) {
+                    $transport = $tagAttribute['transport'] ?? 'default';
 
-                $priority = (int) ($tagAttribute['priority'] ?? 0);
+                    if ($transport !== $this->name && 'all' !== $transport) {
+                        continue;
+                    }
 
-                $groupByPriority[$priority][] = new Reference($serviceId);
+                    $priority = (int) ($tagAttribute['priority'] ?? 0);
+
+                    $groupByPriority[$priority][] = new Reference($serviceId);
+                }
             }
-        }
 
-        krsort($groupByPriority, SORT_NUMERIC);
+            krsort($groupByPriority, SORT_NUMERIC);
 
-        $flatExtensions = [];
-        foreach ($groupByPriority as $extension) {
-            $flatExtensions = array_merge($flatExtensions, $extension);
+            $flatExtensions = [];
+            foreach ($groupByPriority as $extension) {
+                $flatExtensions = array_merge($flatExtensions, $extension);
+            }
+
+            $extensionsService = $container->getDefinition($extensionsId);
+            $extensionsService->replaceArgument(0, array_merge(
+                $extensionsService->getArgument(0),
+                $flatExtensions
+            ));
         }
+    }
 
-        $extensionsService = $container->getDefinition($extensionsId);
-        $extensionsService->replaceArgument(0, array_merge(
-            $extensionsService->getArgument(0),
-            $flatExtensions
-        ));
+    protected function getName(): string
+    {
+        return $this->name;
     }
 }
diff --git a/Symfony/DependencyInjection/BuildProcessorRegistryPass.php b/Symfony/DependencyInjection/BuildProcessorRegistryPass.php
index 8493dc7..c9b7f8b 100644
--- a/Symfony/DependencyInjection/BuildProcessorRegistryPass.php
+++ b/Symfony/DependencyInjection/BuildProcessorRegistryPass.php
@@ -9,44 +9,49 @@
 
 final class BuildProcessorRegistryPass implements CompilerPassInterface
 {
-    /**
-     * @var string
-     */
-    private $name;
+    use FormatTransportNameTrait;
 
-    public function __construct(string $transportName)
-    {
-        if (empty($transportName)) {
-            throw new \InvalidArgumentException('The name could not be empty.');
-        }
-
-        $this->name = $transportName;
-    }
+    protected $name;
 
     public function process(ContainerBuilder $container): void
     {
-        $processorRegistryId = sprintf('enqueue.transport.%s.processor_registry', $this->name);
-        if (false == $container->hasDefinition($processorRegistryId)) {
-            return;
+        if (false == $container->hasParameter('enqueue.transports')) {
+            throw new \LogicException('The "enqueue.transports" parameter must be set.');
         }
 
-        $tag = 'enqueue.transport.processor';
-        $map = [];
-        foreach ($container->findTaggedServiceIds($tag) as $serviceId => $tagAttributes) {
-            foreach ($tagAttributes as $tagAttribute) {
-                $transport = $tagAttribute['transport'] ?? 'default';
+        $names = $container->getParameter('enqueue.transports');
 
-                if ($transport !== $this->name && 'all' !== $transport) {
-                    continue;
-                }
+        foreach ($names as $name) {
+            $this->name = $name;
+
+            $processorRegistryId = $this->format('processor_registry');
+            if (false == $container->hasDefinition($processorRegistryId)) {
+                throw new \LogicException(sprintf('Service "%s" not found', $processorRegistryId));
+            }
 
-                $processor = $tagAttribute['processor'] ?? $serviceId;
+            $tag = 'enqueue.transport.processor';
+            $map = [];
+            foreach ($container->findTaggedServiceIds($tag) as $serviceId => $tagAttributes) {
+                foreach ($tagAttributes as $tagAttribute) {
+                    $transport = $tagAttribute['transport'] ?? 'default';
 
-                $map[$processor] = new Reference($serviceId);
+                    if ($transport !== $this->name && 'all' !== $transport) {
+                        continue;
+                    }
+
+                    $processor = $tagAttribute['processor'] ?? $serviceId;
+
+                    $map[$processor] = new Reference($serviceId);
+                }
             }
+
+            $registry = $container->getDefinition($processorRegistryId);
+            $registry->setArgument(0, ServiceLocatorTagPass::register($container, $map, $processorRegistryId));
         }
+    }
 
-        $registry = $container->getDefinition($processorRegistryId);
-        $registry->setArgument(0, ServiceLocatorTagPass::register($container, $map, $processorRegistryId));
+    protected function getName(): string
+    {
+        return $this->name;
     }
 }
diff --git a/Symfony/DependencyInjection/FormatTransportNameTrait.php b/Symfony/DependencyInjection/FormatTransportNameTrait.php
index 17a41d0..e5531b5 100644
--- a/Symfony/DependencyInjection/FormatTransportNameTrait.php
+++ b/Symfony/DependencyInjection/FormatTransportNameTrait.php
@@ -2,10 +2,25 @@
 
 namespace Enqueue\Symfony\DependencyInjection;
 
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\Reference;
+
 trait FormatTransportNameTrait
 {
     abstract protected function getName(): string;
 
+    private function reference(string $serviceName, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE): Reference
+    {
+        return new Reference($this->format($serviceName), $invalidBehavior);
+    }
+
+    private function parameter(string $serviceName): string
+    {
+        $fullName = $this->format($serviceName, false);
+
+        return "%$fullName%";
+    }
+
     private function format(string $serviceName, $parameter = false): string
     {
         $pattern = 'enqueue.transport.%s.'.$serviceName;
diff --git a/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php b/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
index 6a7e83f..5401b7e 100644
--- a/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
+++ b/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
@@ -25,28 +25,29 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildConsumptionExtensionsPass::class);
     }
 
-    public function testCouldBeConstructedWithName()
+    public function testCouldBeConstructedWithoutArguments()
     {
-        $pass = new BuildConsumptionExtensionsPass('aName');
-
-        $this->assertAttributeSame('aName', 'name', $pass);
+        new BuildConsumptionExtensionsPass();
     }
 
-    public function testThrowIfNameEmptyOnConstruct()
+    public function testThrowIfEnqueueTransportsParameterNotSet()
     {
-        $this->expectException(\InvalidArgumentException::class);
-        $this->expectExceptionMessage('The name could not be empty.');
-        new BuildConsumptionExtensionsPass('');
+        $pass = new BuildConsumptionExtensionsPass();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The "enqueue.transports" parameter must be set.');
+        $pass->process(new ContainerBuilder());
     }
 
-    public function testShouldDoNothingIfExtensionsServiceIsNotRegistered()
+    public function testThrowsIfNoConsumptionExtensionsServiceFoundForConfiguredTransport()
     {
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.transports', ['foo', 'bar']);
 
-        //guard
-        $this->assertFalse($container->hasDefinition('enqueue.transport.aName.consumption_extensions'));
+        $pass = new BuildConsumptionExtensionsPass();
 
-        $pass = new BuildConsumptionExtensionsPass('aName');
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Service "enqueue.transport.foo.consumption_extensions" not found');
         $pass->process($container);
     }
 
@@ -56,6 +57,7 @@ public function testShouldRegisterTransportExtension()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.transports', ['aName']);
         $container->setDefinition('enqueue.transport.aName.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
@@ -65,7 +67,7 @@ public function testShouldRegisterTransportExtension()
             ->addTag('enqueue.transport.consumption_extension', ['transport' => 'aName'])
         ;
 
-        $pass = new BuildConsumptionExtensionsPass('aName');
+        $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $extensions->getArgument(0));
@@ -81,6 +83,7 @@ public function testShouldIgnoreOtherTransportExtensions()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.transports', ['aName']);
         $container->setDefinition('enqueue.transport.aName.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
@@ -90,7 +93,7 @@ public function testShouldIgnoreOtherTransportExtensions()
             ->addTag('enqueue.transport.consumption_extension', ['transport' => 'anotherName'])
         ;
 
-        $pass = new BuildConsumptionExtensionsPass('aName');
+        $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $extensions->getArgument(0));
@@ -105,6 +108,7 @@ public function testShouldAddExtensionIfTransportAll()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.transports', ['aName']);
         $container->setDefinition('enqueue.transport.aName.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
@@ -114,7 +118,7 @@ public function testShouldAddExtensionIfTransportAll()
             ->addTag('enqueue.transport.consumption_extension', ['transport' => 'anotherName'])
         ;
 
-        $pass = new BuildConsumptionExtensionsPass('aName');
+        $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $extensions->getArgument(0));
@@ -129,6 +133,7 @@ public function testShouldTreatTagsWithoutTransportAsDefaultTransport()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.transports', ['default']);
         $container->setDefinition('enqueue.transport.default.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
@@ -138,7 +143,7 @@ public function testShouldTreatTagsWithoutTransportAsDefaultTransport()
             ->addTag('enqueue.transport.consumption_extension')
         ;
 
-        $pass = new BuildConsumptionExtensionsPass('default');
+        $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $extensions->getArgument(0));
@@ -151,6 +156,7 @@ public function testShouldTreatTagsWithoutTransportAsDefaultTransport()
     public function testShouldOrderExtensionsByPriority()
     {
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.transports', ['default']);
 
         $extensions = new Definition();
         $extensions->addArgument([]);
@@ -168,7 +174,7 @@ public function testShouldOrderExtensionsByPriority()
         $extension->addTag('enqueue.transport.consumption_extension', ['priority' => 2]);
         $container->setDefinition('baz_extension', $extension);
 
-        $pass = new BuildConsumptionExtensionsPass('default');
+        $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
         $orderedExtensions = $extensions->getArgument(0);
@@ -182,6 +188,7 @@ public function testShouldOrderExtensionsByPriority()
     public function testShouldAssumePriorityZeroIfPriorityIsNotSet()
     {
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.transports', ['default']);
 
         $extensions = new Definition();
         $extensions->addArgument([]);
@@ -199,7 +206,7 @@ public function testShouldAssumePriorityZeroIfPriorityIsNotSet()
         $extension->addTag('enqueue.transport.consumption_extension', ['priority' => -1]);
         $container->setDefinition('baz_extension', $extension);
 
-        $pass = new BuildConsumptionExtensionsPass('default');
+        $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
         $orderedExtensions = $extensions->getArgument(0);
@@ -219,6 +226,7 @@ public function testShouldMergeWithAddedPreviously()
         ]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.transports', ['aName']);
         $container->setDefinition('enqueue.transport.aName.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
@@ -228,7 +236,7 @@ public function testShouldMergeWithAddedPreviously()
             ->addTag('enqueue.transport.consumption_extension')
         ;
 
-        $pass = new BuildConsumptionExtensionsPass('aName');
+        $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $extensions->getArgument(0));
diff --git a/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php b/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
index 039024f..e9b9c7d 100644
--- a/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
+++ b/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
@@ -26,24 +26,30 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildProcessorRegistryPass::class);
     }
 
-    public function testCouldBeConstructedWithName()
+    public function testCouldBeConstructedWithoutArguments()
     {
-        $pass = new BuildProcessorRegistryPass('aName');
-
-        $this->assertAttributeSame('aName', 'name', $pass);
+        new BuildProcessorRegistryPass();
     }
 
-    public function testThrowIfNameEmptyOnConstruct()
+    public function testThrowIfEnqueueTransportsParameterNotSet()
     {
-        $this->expectException(\InvalidArgumentException::class);
-        $this->expectExceptionMessage('The name could not be empty.');
-        new BuildProcessorRegistryPass('');
+        $pass = new BuildProcessorRegistryPass();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The "enqueue.transports" parameter must be set.');
+        $pass->process(new ContainerBuilder());
     }
 
-    public function testShouldDoNothingIfProcessorRegistryServiceIsNotRegistered()
+    public function testThrowsIfNoRegistryServiceFoundForConfiguredTransport()
     {
-        $pass = new BuildProcessorRegistryPass('aName');
-        $pass->process(new ContainerBuilder());
+        $container = new ContainerBuilder();
+        $container->setParameter('enqueue.transports', ['foo', 'bar']);
+
+        $pass = new BuildProcessorRegistryPass();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Service "enqueue.transport.foo.processor_registry" not found');
+        $pass->process($container);
     }
 
     public function testShouldRegisterProcessorWithMatchedName()
@@ -52,6 +58,7 @@ public function testShouldRegisterProcessorWithMatchedName()
         $registry->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.transports', ['foo']);
         $container->setDefinition('enqueue.transport.foo.processor_registry', $registry);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.transport.processor', ['transport' => 'foo'])
@@ -60,7 +67,7 @@ public function testShouldRegisterProcessorWithMatchedName()
             ->addTag('enqueue.transport.processor', ['transport' => 'bar'])
         ;
 
-        $pass = new BuildProcessorRegistryPass('foo');
+        $pass = new BuildProcessorRegistryPass();
 
         $pass->process($container);
 
@@ -71,12 +78,47 @@ public function testShouldRegisterProcessorWithMatchedName()
         ]);
     }
 
+    public function testShouldRegisterProcessorWithMatchedNameToCorrespondingRegistries()
+    {
+        $fooRegistry = new Definition(ProcessorRegistryInterface::class);
+        $fooRegistry->addArgument([]);
+
+        $barRegistry = new Definition(ProcessorRegistryInterface::class);
+        $barRegistry->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setParameter('enqueue.transports', ['foo', 'bar']);
+        $container->setDefinition('enqueue.transport.foo.processor_registry', $fooRegistry);
+        $container->setDefinition('enqueue.transport.bar.processor_registry', $barRegistry);
+        $container->register('aFooProcessor', 'aProcessorClass')
+            ->addTag('enqueue.transport.processor', ['transport' => 'foo'])
+        ;
+        $container->register('aBarProcessor', 'aProcessorClass')
+            ->addTag('enqueue.transport.processor', ['transport' => 'bar'])
+        ;
+
+        $pass = new BuildProcessorRegistryPass();
+
+        $pass->process($container);
+
+        $this->assertInstanceOf(Reference::class, $fooRegistry->getArgument(0));
+        $this->assertLocatorServices($container, $fooRegistry->getArgument(0), [
+            'aFooProcessor' => 'aFooProcessor',
+        ]);
+
+        $this->assertInstanceOf(Reference::class, $barRegistry->getArgument(0));
+        $this->assertLocatorServices($container, $barRegistry->getArgument(0), [
+            'aBarProcessor' => 'aBarProcessor',
+        ]);
+    }
+
     public function testShouldRegisterProcessorWithoutNameToDefaultTransport()
     {
         $registry = new Definition(ProcessorRegistryInterface::class);
         $registry->addArgument(null);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.transports', ['default']);
         $container->setDefinition('enqueue.transport.default.processor_registry', $registry);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.transport.processor', [])
@@ -85,7 +127,7 @@ public function testShouldRegisterProcessorWithoutNameToDefaultTransport()
             ->addTag('enqueue.transport.processor', ['transport' => 'bar'])
         ;
 
-        $pass = new BuildProcessorRegistryPass('default');
+        $pass = new BuildProcessorRegistryPass();
 
         $pass->process($container);
 
@@ -102,6 +144,7 @@ public function testShouldRegisterProcessorIfTransportNameEqualsAll()
         $registry->addArgument(null);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.transports', ['default']);
         $container->setDefinition('enqueue.transport.default.processor_registry', $registry);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.transport.processor', ['transport' => 'all'])
@@ -110,7 +153,7 @@ public function testShouldRegisterProcessorIfTransportNameEqualsAll()
             ->addTag('enqueue.transport.processor', ['transport' => 'bar'])
         ;
 
-        $pass = new BuildProcessorRegistryPass('default');
+        $pass = new BuildProcessorRegistryPass();
 
         $pass->process($container);
 
@@ -127,12 +170,13 @@ public function testShouldRegisterWithCustomProcessorName()
         $registry->addArgument(null);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.transports', ['default']);
         $container->setDefinition('enqueue.transport.default.processor_registry', $registry);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.transport.processor', ['processor' => 'customProcessorName'])
         ;
 
-        $pass = new BuildProcessorRegistryPass('default');
+        $pass = new BuildProcessorRegistryPass();
 
         $pass->process($container);
 

From ea7e7f23a31ac31be4e500a7618409d7bd74aae0 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 22 Oct 2018 15:38:21 +0300
Subject: [PATCH 172/337] [client] migrate passes.

---
 .../BuildClientExtensionsPass.php             | 80 +++++++++---------
 .../BuildConsumptionExtensionsPass.php        | 81 ++++++++++---------
 .../FormatClientNameTrait.php                 |  8 +-
 .../FormatTransportNameTrait.php              |  8 +-
 .../BuildClientExtensionsPassTest.php         | 80 +++++++++++++-----
 .../BuildConsumptionExtensionsPassTest.php    | 80 +++++++++++++-----
 .../BuildConsumptionExtensionsPassTest.php    | 34 ++++++++
 composer.json                                 |  1 +
 8 files changed, 248 insertions(+), 124 deletions(-)

diff --git a/Symfony/Client/DependencyInjection/BuildClientExtensionsPass.php b/Symfony/Client/DependencyInjection/BuildClientExtensionsPass.php
index 5cb516d..3f97093 100644
--- a/Symfony/Client/DependencyInjection/BuildClientExtensionsPass.php
+++ b/Symfony/Client/DependencyInjection/BuildClientExtensionsPass.php
@@ -8,58 +8,62 @@
 
 final class BuildClientExtensionsPass implements CompilerPassInterface
 {
-    /**
-     * @var string
-     */
-    private $name;
+    use FormatClientNameTrait;
 
-    public function __construct(string $clientName)
-    {
-        if (empty($clientName)) {
-            throw new \InvalidArgumentException('The name could not be empty.');
-        }
-
-        $this->name = $clientName;
-    }
+    protected $name;
 
     public function process(ContainerBuilder $container): void
     {
-        $extensionsId = sprintf('enqueue.client.%s.client_extensions', $this->name);
-        if (false == $container->hasDefinition($extensionsId)) {
-            return;
+        if (false == $container->hasParameter('enqueue.clients')) {
+            throw new \LogicException('The "enqueue.clients" parameter must be set.');
         }
 
-        $tags = array_merge(
-            $container->findTaggedServiceIds('enqueue.client_extension'),
-            $container->findTaggedServiceIds('enqueue.client.extension') // TODO BC
-        );
+        $names = $container->getParameter('enqueue.clients');
 
-        $groupByPriority = [];
-        foreach ($tags as $serviceId => $tagAttributes) {
-            foreach ($tagAttributes as $tagAttribute) {
-                $client = $tagAttribute['client'] ?? 'default';
+        foreach ($names as $name) {
+            $this->name = $name;
+            $extensionsId = $this->format('client_extensions');
+            if (false == $container->hasDefinition($extensionsId)) {
+                throw new \LogicException(sprintf('Service "%s" not found', $extensionsId));
+            }
 
-                if ($client !== $this->name && 'all' !== $client) {
-                    continue;
-                }
+            $tags = array_merge(
+                $container->findTaggedServiceIds('enqueue.client_extension'),
+                $container->findTaggedServiceIds('enqueue.client.extension') // TODO BC
+            );
+
+            $groupByPriority = [];
+            foreach ($tags as $serviceId => $tagAttributes) {
+                foreach ($tagAttributes as $tagAttribute) {
+                    $client = $tagAttribute['client'] ?? 'default';
 
-                $priority = (int) ($tagAttribute['priority'] ?? 0);
+                    if ($client !== $this->name && 'all' !== $client) {
+                        continue;
+                    }
 
-                $groupByPriority[$priority][] = new Reference($serviceId);
+                    $priority = (int) ($tagAttribute['priority'] ?? 0);
+
+                    $groupByPriority[$priority][] = new Reference($serviceId);
+                }
             }
-        }
 
-        krsort($groupByPriority, SORT_NUMERIC);
+            krsort($groupByPriority, SORT_NUMERIC);
 
-        $flatExtensions = [];
-        foreach ($groupByPriority as $extension) {
-            $flatExtensions = array_merge($flatExtensions, $extension);
+            $flatExtensions = [];
+            foreach ($groupByPriority as $extension) {
+                $flatExtensions = array_merge($flatExtensions, $extension);
+            }
+
+            $extensionsService = $container->getDefinition($extensionsId);
+            $extensionsService->replaceArgument(0, array_merge(
+                $extensionsService->getArgument(0),
+                $flatExtensions
+            ));
         }
+    }
 
-        $extensionsService = $container->getDefinition($extensionsId);
-        $extensionsService->replaceArgument(0, array_merge(
-            $extensionsService->getArgument(0),
-            $flatExtensions
-        ));
+    protected function getName(): string
+    {
+        return $this->name;
     }
 }
diff --git a/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php b/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php
index 8482225..d05f6f0 100644
--- a/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php
+++ b/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php
@@ -8,58 +8,63 @@
 
 final class BuildConsumptionExtensionsPass implements CompilerPassInterface
 {
-    /**
-     * @var string
-     */
-    private $name;
+    use FormatClientNameTrait;
 
-    public function __construct(string $clientName)
-    {
-        if (empty($clientName)) {
-            throw new \InvalidArgumentException('The name could not be empty.');
-        }
-
-        $this->name = $clientName;
-    }
+    protected $name;
 
     public function process(ContainerBuilder $container): void
     {
-        $extensionsId = sprintf('enqueue.client.%s.consumption_extensions', $this->name);
-        if (false == $container->hasDefinition($extensionsId)) {
-            return;
+        if (false == $container->hasParameter('enqueue.clients')) {
+            throw new \LogicException('The "enqueue.clients" parameter must be set.');
         }
 
-        $tags = array_merge(
-            $container->findTaggedServiceIds('enqueue.consumption_extension'),
-            $container->findTaggedServiceIds('enqueue.consumption.extension') // TODO BC
-        );
+        $names = $container->getParameter('enqueue.clients');
 
-        $groupByPriority = [];
-        foreach ($tags as $serviceId => $tagAttributes) {
-            foreach ($tagAttributes as $tagAttribute) {
-                $client = $tagAttribute['client'] ?? 'default';
+        foreach ($names as $name) {
+            $this->name = $name;
 
-                if ($client !== $this->name && 'all' !== $client) {
-                    continue;
-                }
+            $extensionsId = $this->format('consumption_extensions');
+            if (false == $container->hasDefinition($extensionsId)) {
+                throw new \LogicException(sprintf('Service "%s" not found', $extensionsId));
+            }
+
+            $tags = array_merge(
+                $container->findTaggedServiceIds('enqueue.consumption_extension'),
+                $container->findTaggedServiceIds('enqueue.consumption.extension') // TODO BC
+            );
+
+            $groupByPriority = [];
+            foreach ($tags as $serviceId => $tagAttributes) {
+                foreach ($tagAttributes as $tagAttribute) {
+                    $client = $tagAttribute['client'] ?? 'default';
 
-                $priority = (int) ($tagAttribute['priority'] ?? 0);
+                    if ($client !== $this->name && 'all' !== $client) {
+                        continue;
+                    }
 
-                $groupByPriority[$priority][] = new Reference($serviceId);
+                    $priority = (int) ($tagAttribute['priority'] ?? 0);
+
+                    $groupByPriority[$priority][] = new Reference($serviceId);
+                }
             }
-        }
 
-        krsort($groupByPriority, SORT_NUMERIC);
+            krsort($groupByPriority, SORT_NUMERIC);
 
-        $flatExtensions = [];
-        foreach ($groupByPriority as $extension) {
-            $flatExtensions = array_merge($flatExtensions, $extension);
+            $flatExtensions = [];
+            foreach ($groupByPriority as $extension) {
+                $flatExtensions = array_merge($flatExtensions, $extension);
+            }
+
+            $extensionsService = $container->getDefinition($extensionsId);
+            $extensionsService->replaceArgument(0, array_merge(
+                $extensionsService->getArgument(0),
+                $flatExtensions
+            ));
         }
+    }
 
-        $extensionsService = $container->getDefinition($extensionsId);
-        $extensionsService->replaceArgument(0, array_merge(
-            $extensionsService->getArgument(0),
-            $flatExtensions
-        ));
+    protected function getName(): string
+    {
+        return $this->name;
     }
 }
diff --git a/Symfony/Client/DependencyInjection/FormatClientNameTrait.php b/Symfony/Client/DependencyInjection/FormatClientNameTrait.php
index 6f76d2b..41dace8 100644
--- a/Symfony/Client/DependencyInjection/FormatClientNameTrait.php
+++ b/Symfony/Client/DependencyInjection/FormatClientNameTrait.php
@@ -16,17 +16,15 @@ private function reference(string $serviceName, $invalidBehavior = ContainerInte
 
     private function parameter(string $serviceName): string
     {
-        $fullName = $this->format($serviceName, false);
+        $fullName = $this->format($serviceName);
 
         return "%$fullName%";
     }
 
-    private function format(string $serviceName, $parameter = false): string
+    private function format(string $serviceName): string
     {
         $pattern = 'enqueue.client.%s.'.$serviceName;
 
-        $fullName = sprintf($pattern, $this->getName());
-
-        return $parameter ? "%$fullName%" : $fullName;
+        return sprintf($pattern, $this->getName());
     }
 }
diff --git a/Symfony/DependencyInjection/FormatTransportNameTrait.php b/Symfony/DependencyInjection/FormatTransportNameTrait.php
index e5531b5..dd0fdd7 100644
--- a/Symfony/DependencyInjection/FormatTransportNameTrait.php
+++ b/Symfony/DependencyInjection/FormatTransportNameTrait.php
@@ -16,17 +16,15 @@ private function reference(string $serviceName, $invalidBehavior = ContainerInte
 
     private function parameter(string $serviceName): string
     {
-        $fullName = $this->format($serviceName, false);
+        $fullName = $this->format($serviceName);
 
         return "%$fullName%";
     }
 
-    private function format(string $serviceName, $parameter = false): string
+    private function format(string $serviceName): string
     {
         $pattern = 'enqueue.transport.%s.'.$serviceName;
 
-        $fullName = sprintf($pattern, $this->getName());
-
-        return $parameter ? "%$fullName%" : $fullName;
+        return sprintf($pattern, $this->getName());
     }
 }
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php
index 46f5491..e1c8bdc 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php
@@ -25,28 +25,29 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildClientExtensionsPass::class);
     }
 
-    public function testCouldBeConstructedWithName()
+    public function testCouldBeConstructedWithoutArguments()
     {
-        $pass = new BuildClientExtensionsPass('aName');
-
-        $this->assertAttributeSame('aName', 'name', $pass);
+        new BuildClientExtensionsPass();
     }
 
-    public function testThrowIfNameEmptyOnConstruct()
+    public function testThrowIfEnqueueClientsParameterNotSet()
     {
-        $this->expectException(\InvalidArgumentException::class);
-        $this->expectExceptionMessage('The name could not be empty.');
-        new BuildClientExtensionsPass('');
+        $pass = new BuildClientExtensionsPass();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The "enqueue.clients" parameter must be set.');
+        $pass->process(new ContainerBuilder());
     }
 
-    public function testShouldDoNothingIfExtensionsServiceIsNotRegistered()
+    public function testThrowsIfNoClientExtensionsServiceFoundForConfiguredTransport()
     {
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['foo', 'bar']);
 
-        //guard
-        $this->assertFalse($container->hasDefinition('enqueue.client.aName.client_extensions'));
+        $pass = new BuildClientExtensionsPass();
 
-        $pass = new BuildClientExtensionsPass('aName');
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Service "enqueue.client.foo.client_extensions" not found');
         $pass->process($container);
     }
 
@@ -56,6 +57,7 @@ public function testShouldRegisterClientExtension()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['aName']);
         $container->setDefinition('enqueue.client.aName.client_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
@@ -65,7 +67,7 @@ public function testShouldRegisterClientExtension()
             ->addTag('enqueue.client_extension', ['client' => 'aName'])
         ;
 
-        $pass = new BuildClientExtensionsPass('aName');
+        $pass = new BuildClientExtensionsPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $extensions->getArgument(0));
@@ -81,6 +83,7 @@ public function testShouldIgnoreOtherClientExtensions()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['aName']);
         $container->setDefinition('enqueue.client.aName.client_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
@@ -90,7 +93,7 @@ public function testShouldIgnoreOtherClientExtensions()
             ->addTag('enqueue.client_extension', ['client' => 'anotherName'])
         ;
 
-        $pass = new BuildClientExtensionsPass('aName');
+        $pass = new BuildClientExtensionsPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $extensions->getArgument(0));
@@ -105,6 +108,7 @@ public function testShouldAddExtensionIfClientAll()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['aName']);
         $container->setDefinition('enqueue.client.aName.client_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
@@ -114,7 +118,7 @@ public function testShouldAddExtensionIfClientAll()
             ->addTag('enqueue.client_extension', ['client' => 'anotherName'])
         ;
 
-        $pass = new BuildClientExtensionsPass('aName');
+        $pass = new BuildClientExtensionsPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $extensions->getArgument(0));
@@ -129,6 +133,7 @@ public function testShouldTreatTagsWithoutClientAsDefaultClient()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.client_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
@@ -138,7 +143,7 @@ public function testShouldTreatTagsWithoutClientAsDefaultClient()
             ->addTag('enqueue.client_extension')
         ;
 
-        $pass = new BuildClientExtensionsPass('default');
+        $pass = new BuildClientExtensionsPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $extensions->getArgument(0));
@@ -151,6 +156,7 @@ public function testShouldTreatTagsWithoutClientAsDefaultClient()
     public function testShouldOrderExtensionsByPriority()
     {
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
 
         $extensions = new Definition();
         $extensions->addArgument([]);
@@ -168,7 +174,7 @@ public function testShouldOrderExtensionsByPriority()
         $extension->addTag('enqueue.client_extension', ['priority' => 2]);
         $container->setDefinition('baz_extension', $extension);
 
-        $pass = new BuildClientExtensionsPass('default');
+        $pass = new BuildClientExtensionsPass();
         $pass->process($container);
 
         $orderedExtensions = $extensions->getArgument(0);
@@ -182,6 +188,7 @@ public function testShouldOrderExtensionsByPriority()
     public function testShouldAssumePriorityZeroIfPriorityIsNotSet()
     {
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
 
         $extensions = new Definition();
         $extensions->addArgument([]);
@@ -199,7 +206,7 @@ public function testShouldAssumePriorityZeroIfPriorityIsNotSet()
         $extension->addTag('enqueue.client_extension', ['priority' => -1]);
         $container->setDefinition('baz_extension', $extension);
 
-        $pass = new BuildClientExtensionsPass('default');
+        $pass = new BuildClientExtensionsPass();
         $pass->process($container);
 
         $orderedExtensions = $extensions->getArgument(0);
@@ -219,6 +226,7 @@ public function testShouldMergeWithAddedPreviously()
         ]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['aName']);
         $container->setDefinition('enqueue.client.aName.client_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
@@ -228,7 +236,7 @@ public function testShouldMergeWithAddedPreviously()
             ->addTag('enqueue.client_extension')
         ;
 
-        $pass = new BuildClientExtensionsPass('aName');
+        $pass = new BuildClientExtensionsPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $extensions->getArgument(0));
@@ -237,4 +245,38 @@ public function testShouldMergeWithAddedPreviously()
             'aOloloExtension' => 'aOloloServiceIdAddedPreviously',
         ], $extensions->getArgument(0));
     }
+
+    public function testShouldRegisterProcessorWithMatchedNameToCorrespondingExtensions()
+    {
+        $fooExtensions = new Definition();
+        $fooExtensions->addArgument([]);
+
+        $barExtensions = new Definition();
+        $barExtensions->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['foo', 'bar']);
+        $container->setDefinition('enqueue.client.foo.client_extensions', $fooExtensions);
+        $container->setDefinition('enqueue.client.bar.client_extensions', $barExtensions);
+
+        $container->register('aFooExtension', ExtensionInterface::class)
+            ->addTag('enqueue.client_extension', ['client' => 'foo'])
+        ;
+        $container->register('aBarExtension', ExtensionInterface::class)
+            ->addTag('enqueue.client_extension', ['client' => 'bar'])
+        ;
+
+        $pass = new BuildClientExtensionsPass();
+        $pass->process($container);
+
+        $this->assertInternalType('array', $fooExtensions->getArgument(0));
+        $this->assertEquals([
+            new Reference('aFooExtension'),
+        ], $fooExtensions->getArgument(0));
+
+        $this->assertInternalType('array', $barExtensions->getArgument(0));
+        $this->assertEquals([
+            new Reference('aBarExtension'),
+        ], $barExtensions->getArgument(0));
+    }
 }
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php
index 68ad2ab..d3936fe 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php
@@ -25,28 +25,29 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildConsumptionExtensionsPass::class);
     }
 
-    public function testCouldBeConstructedWithName()
+    public function testCouldBeConstructedWithoutArguments()
     {
-        $pass = new BuildConsumptionExtensionsPass('aName');
-
-        $this->assertAttributeSame('aName', 'name', $pass);
+        new BuildConsumptionExtensionsPass();
     }
 
-    public function testThrowIfNameEmptyOnConstruct()
+    public function testThrowIfEnqueueClientsParameterNotSet()
     {
-        $this->expectException(\InvalidArgumentException::class);
-        $this->expectExceptionMessage('The name could not be empty.');
-        new BuildConsumptionExtensionsPass('');
+        $pass = new BuildConsumptionExtensionsPass();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The "enqueue.clients" parameter must be set.');
+        $pass->process(new ContainerBuilder());
     }
 
-    public function testShouldDoNothingIfExtensionsServiceIsNotRegistered()
+    public function testThrowsIfNoConsumptionExtensionsServiceFoundForConfiguredTransport()
     {
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['foo', 'bar']);
 
-        //guard
-        $this->assertFalse($container->hasDefinition('enqueue.client.aName.consumption_extensions'));
+        $pass = new BuildConsumptionExtensionsPass();
 
-        $pass = new BuildConsumptionExtensionsPass('aName');
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Service "enqueue.client.foo.consumption_extensions" not found');
         $pass->process($container);
     }
 
@@ -56,6 +57,7 @@ public function testShouldRegisterClientExtension()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['aName']);
         $container->setDefinition('enqueue.client.aName.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
@@ -65,7 +67,7 @@ public function testShouldRegisterClientExtension()
             ->addTag('enqueue.consumption_extension', ['client' => 'aName'])
         ;
 
-        $pass = new BuildConsumptionExtensionsPass('aName');
+        $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $extensions->getArgument(0));
@@ -81,6 +83,7 @@ public function testShouldIgnoreOtherClientExtensions()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['aName']);
         $container->setDefinition('enqueue.client.aName.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
@@ -90,7 +93,7 @@ public function testShouldIgnoreOtherClientExtensions()
             ->addTag('enqueue.consumption_extension', ['client' => 'anotherName'])
         ;
 
-        $pass = new BuildConsumptionExtensionsPass('aName');
+        $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $extensions->getArgument(0));
@@ -105,6 +108,7 @@ public function testShouldAddExtensionIfClientAll()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['aName']);
         $container->setDefinition('enqueue.client.aName.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
@@ -114,7 +118,7 @@ public function testShouldAddExtensionIfClientAll()
             ->addTag('enqueue.consumption_extension', ['client' => 'anotherName'])
         ;
 
-        $pass = new BuildConsumptionExtensionsPass('aName');
+        $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $extensions->getArgument(0));
@@ -129,6 +133,7 @@ public function testShouldTreatTagsWithoutClientAsDefaultClient()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
@@ -138,7 +143,7 @@ public function testShouldTreatTagsWithoutClientAsDefaultClient()
             ->addTag('enqueue.consumption_extension')
         ;
 
-        $pass = new BuildConsumptionExtensionsPass('default');
+        $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $extensions->getArgument(0));
@@ -151,6 +156,7 @@ public function testShouldTreatTagsWithoutClientAsDefaultClient()
     public function testShouldOrderExtensionsByPriority()
     {
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
 
         $extensions = new Definition();
         $extensions->addArgument([]);
@@ -168,7 +174,7 @@ public function testShouldOrderExtensionsByPriority()
         $extension->addTag('enqueue.consumption_extension', ['priority' => 2]);
         $container->setDefinition('baz_extension', $extension);
 
-        $pass = new BuildConsumptionExtensionsPass('default');
+        $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
         $orderedExtensions = $extensions->getArgument(0);
@@ -182,6 +188,7 @@ public function testShouldOrderExtensionsByPriority()
     public function testShouldAssumePriorityZeroIfPriorityIsNotSet()
     {
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
 
         $extensions = new Definition();
         $extensions->addArgument([]);
@@ -199,7 +206,7 @@ public function testShouldAssumePriorityZeroIfPriorityIsNotSet()
         $extension->addTag('enqueue.consumption_extension', ['priority' => -1]);
         $container->setDefinition('baz_extension', $extension);
 
-        $pass = new BuildConsumptionExtensionsPass('default');
+        $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
         $orderedExtensions = $extensions->getArgument(0);
@@ -219,6 +226,7 @@ public function testShouldMergeWithAddedPreviously()
         ]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['aName']);
         $container->setDefinition('enqueue.client.aName.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
@@ -228,7 +236,7 @@ public function testShouldMergeWithAddedPreviously()
             ->addTag('enqueue.consumption_extension')
         ;
 
-        $pass = new BuildConsumptionExtensionsPass('aName');
+        $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $extensions->getArgument(0));
@@ -237,4 +245,38 @@ public function testShouldMergeWithAddedPreviously()
             'aOloloExtension' => 'aOloloServiceIdAddedPreviously',
         ], $extensions->getArgument(0));
     }
+
+    public function testShouldRegisterProcessorWithMatchedNameToCorrespondingExtensions()
+    {
+        $fooExtensions = new Definition();
+        $fooExtensions->addArgument([]);
+
+        $barExtensions = new Definition();
+        $barExtensions->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['foo', 'bar']);
+        $container->setDefinition('enqueue.client.foo.consumption_extensions', $fooExtensions);
+        $container->setDefinition('enqueue.client.bar.consumption_extensions', $barExtensions);
+
+        $container->register('aFooExtension', ExtensionInterface::class)
+            ->addTag('enqueue.consumption_extension', ['client' => 'foo'])
+        ;
+        $container->register('aBarExtension', ExtensionInterface::class)
+            ->addTag('enqueue.consumption_extension', ['client' => 'bar'])
+        ;
+
+        $pass = new BuildConsumptionExtensionsPass();
+        $pass->process($container);
+
+        $this->assertInternalType('array', $fooExtensions->getArgument(0));
+        $this->assertEquals([
+            new Reference('aFooExtension'),
+        ], $fooExtensions->getArgument(0));
+
+        $this->assertInternalType('array', $barExtensions->getArgument(0));
+        $this->assertEquals([
+            new Reference('aBarExtension'),
+        ], $barExtensions->getArgument(0));
+    }
 }
diff --git a/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php b/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
index 5401b7e..e44272d 100644
--- a/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
+++ b/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
@@ -245,4 +245,38 @@ public function testShouldMergeWithAddedPreviously()
             'aOloloExtension' => 'aOloloServiceIdAddedPreviously',
         ], $extensions->getArgument(0));
     }
+
+    public function testShouldRegisterProcessorWithMatchedNameToCorrespondingRegistries()
+    {
+        $fooExtensions = new Definition();
+        $fooExtensions->addArgument([]);
+
+        $barExtensions = new Definition();
+        $barExtensions->addArgument([]);
+
+        $container = new ContainerBuilder();
+        $container->setParameter('enqueue.transports', ['foo', 'bar']);
+        $container->setDefinition('enqueue.transport.foo.consumption_extensions', $fooExtensions);
+        $container->setDefinition('enqueue.transport.bar.consumption_extensions', $barExtensions);
+
+        $container->register('aFooExtension', ExtensionInterface::class)
+            ->addTag('enqueue.transport.consumption_extension', ['transport' => 'foo'])
+        ;
+        $container->register('aBarExtension', ExtensionInterface::class)
+            ->addTag('enqueue.transport.consumption_extension', ['transport' => 'bar'])
+        ;
+
+        $pass = new BuildConsumptionExtensionsPass();
+        $pass->process($container);
+
+        $this->assertInternalType('array', $fooExtensions->getArgument(0));
+        $this->assertEquals([
+            new Reference('aFooExtension'),
+        ], $fooExtensions->getArgument(0));
+
+        $this->assertInternalType('array', $barExtensions->getArgument(0));
+        $this->assertEquals([
+            new Reference('aBarExtension'),
+        ], $barExtensions->getArgument(0));
+    }
 }
diff --git a/composer.json b/composer.json
index e42f5e9..fc42d77 100644
--- a/composer.json
+++ b/composer.json
@@ -10,6 +10,7 @@
         "queue-interop/amqp-interop": "0.8.x-dev",
         "queue-interop/queue-interop": "0.7.x-dev",
         "enqueue/null": "0.9.x-dev",
+        "enqueue/dsn": "0.9.x-dev",
         "ramsey/uuid": "^2|^3.5",
         "psr/log": "^1",
         "psr/container": "^1"

From d467ab16e4cb8a2fbd21533de0bab60e721cc254 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 22 Oct 2018 15:43:27 +0300
Subject: [PATCH 173/337] [client] migrate passes.

---
 .../BuildTopicSubscriberRoutesPass.php        | 138 +++++++++---------
 .../BuildTopicSubscriberRoutesPassTest.php    |  58 +++++---
 2 files changed, 108 insertions(+), 88 deletions(-)

diff --git a/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php b/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php
index 73f3a01..f1b9ecc 100644
--- a/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php
+++ b/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php
@@ -10,94 +10,98 @@
 
 final class BuildTopicSubscriberRoutesPass implements CompilerPassInterface
 {
-    /**
-     * @var string
-     */
-    private $name;
+    use FormatClientNameTrait;
 
-    public function __construct(string $clientName)
-    {
-        if (empty($clientName)) {
-            throw new \InvalidArgumentException('The name could not be empty.');
-        }
-
-        $this->name = $clientName;
-    }
+    protected $name;
 
     public function process(ContainerBuilder $container): void
     {
-        $routeCollectionId = sprintf('enqueue.client.%s.route_collection', $this->name);
-        if (false == $container->hasDefinition($routeCollectionId)) {
-            return;
+        if (false == $container->hasParameter('enqueue.clients')) {
+            throw new \LogicException('The "enqueue.clients" parameter must be set.');
         }
 
-        $tag = 'enqueue.topic_subscriber';
-        $routeCollection = new RouteCollection([]);
-        foreach ($container->findTaggedServiceIds($tag) as $serviceId => $tagAttributes) {
-            $processorDefinition = $container->getDefinition($serviceId);
-            if ($processorDefinition->getFactory()) {
-                throw new \LogicException('The topic subscriber tag could not be applied to a service created by factory.');
-            }
+        $names = $container->getParameter('enqueue.clients');
 
-            $processorClass = $processorDefinition->getClass();
-            if (false == class_exists($processorClass)) {
-                throw new \LogicException(sprintf('The processor class "%s" could not be found.', $processorClass));
+        foreach ($names as $name) {
+            $this->name = $name;
+            $routeCollectionId = $this->format('route_collection');
+            if (false == $container->hasDefinition($routeCollectionId)) {
+                throw new \LogicException(sprintf('Service "%s" not found', $routeCollectionId));
             }
 
-            if (false == is_subclass_of($processorClass, TopicSubscriberInterface::class)) {
-                throw new \LogicException(sprintf('The processor must implement "%s" interface to be used with the tag "%s"', TopicSubscriberInterface::class, $tag));
-            }
+            $tag = 'enqueue.topic_subscriber';
+            $routeCollection = new RouteCollection([]);
+            foreach ($container->findTaggedServiceIds($tag) as $serviceId => $tagAttributes) {
+                $processorDefinition = $container->getDefinition($serviceId);
+                if ($processorDefinition->getFactory()) {
+                    throw new \LogicException('The topic subscriber tag could not be applied to a service created by factory.');
+                }
 
-            foreach ($tagAttributes as $tagAttribute) {
-                $client = $tagAttribute['client'] ?? 'default';
+                $processorClass = $processorDefinition->getClass();
+                if (false == class_exists($processorClass)) {
+                    throw new \LogicException(sprintf('The processor class "%s" could not be found.', $processorClass));
+                }
 
-                if ($client !== $this->name && 'all' !== $client) {
-                    continue;
+                if (false == is_subclass_of($processorClass, TopicSubscriberInterface::class)) {
+                    throw new \LogicException(sprintf('The processor must implement "%s" interface to be used with the tag "%s"', TopicSubscriberInterface::class, $tag));
                 }
 
-                /** @var TopicSubscriberInterface $processorClass */
-                $topics = $processorClass::getSubscribedTopics();
+                foreach ($tagAttributes as $tagAttribute) {
+                    $client = $tagAttribute['client'] ?? 'default';
 
-                if (empty($topics)) {
-                    throw new \LogicException('Topic subscriber must return something.');
-                }
+                    if ($client !== $this->name && 'all' !== $client) {
+                        continue;
+                    }
 
-                if (is_string($topics)) {
-                    $topics = [$topics];
-                }
+                    /** @var TopicSubscriberInterface $processorClass */
+                    $topics = $processorClass::getSubscribedTopics();
 
-                if (!is_array($topics)) {
-                    throw new \LogicException('Topic subscriber configuration is invalid. Should be an array or string.');
-                }
+                    if (empty($topics)) {
+                        throw new \LogicException('Topic subscriber must return something.');
+                    }
+
+                    if (is_string($topics)) {
+                        $topics = [$topics];
+                    }
+
+                    if (!is_array($topics)) {
+                        throw new \LogicException('Topic subscriber configuration is invalid. Should be an array or string.');
+                    }
 
-                foreach ($topics as $key => $params) {
-                    if (is_string($params)) {
-                        $routeCollection->add(new Route($params, Route::TOPIC, $serviceId, ['processor_service_id' => $serviceId]));
-                    } elseif (is_array($params)) {
-                        $source = $params['topic'] ?? null;
-                        $processor = $params['processor'] ?? $serviceId;
-                        unset($params['topic'], $params['source'], $params['source_type'], $params['processor'], $params['options']);
-                        $options = $params;
-                        $options['processor_service_id'] = $serviceId;
-
-                        $routeCollection->add(new Route($source, Route::TOPIC, $processor, $options));
-                    } else {
-                        throw new \LogicException(sprintf(
-                            'Topic subscriber configuration is invalid for "%s::getSubscribedTopics()". Got "%s"',
-                            $processorClass,
-                            json_encode($processorClass::getSubscribedTopics())
-                        ));
+                    foreach ($topics as $key => $params) {
+                        if (is_string($params)) {
+                            $routeCollection->add(new Route($params, Route::TOPIC, $serviceId, ['processor_service_id' => $serviceId]));
+                        } elseif (is_array($params)) {
+                            $source = $params['topic'] ?? null;
+                            $processor = $params['processor'] ?? $serviceId;
+                            unset($params['topic'], $params['source'], $params['source_type'], $params['processor'], $params['options']);
+                            $options = $params;
+                            $options['processor_service_id'] = $serviceId;
+
+                            $routeCollection->add(new Route($source, Route::TOPIC, $processor, $options));
+                        } else {
+                            throw new \LogicException(sprintf(
+                                'Topic subscriber configuration is invalid for "%s::getSubscribedTopics()". Got "%s"',
+                                $processorClass,
+                                json_encode($processorClass::getSubscribedTopics())
+                            ));
+                        }
                     }
                 }
             }
-        }
 
-        $rawRoutes = $routeCollection->toArray();
+            $rawRoutes = $routeCollection->toArray();
 
-        $routeCollectionService = $container->getDefinition($routeCollectionId);
-        $routeCollectionService->replaceArgument(0, array_merge(
-            $routeCollectionService->getArgument(0),
-            $rawRoutes
-        ));
+            $routeCollectionService = $container->getDefinition($routeCollectionId);
+            $routeCollectionService->replaceArgument(0, array_merge(
+                $routeCollectionService->getArgument(0),
+                $rawRoutes
+            ));
+        }
+    }
+
+    protected function getName(): string
+    {
+        return $this->name;
     }
 }
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
index 895cfb1..25033f0 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
@@ -29,29 +29,36 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildTopicSubscriberRoutesPass::class);
     }
 
-    public function testCouldBeConstructedWithName()
+    public function testCouldBeConstructedWithoutArguments()
     {
-        $pass = new BuildTopicSubscriberRoutesPass('aName');
-
-        $this->assertAttributeSame('aName', 'name', $pass);
+        new BuildTopicSubscriberRoutesPass();
     }
 
-    public function testThrowIfNameEmptyOnConstruct()
+    public function testThrowIfEnqueueClientsParameterNotSet()
     {
-        $this->expectException(\InvalidArgumentException::class);
-        $this->expectExceptionMessage('The name could not be empty.');
-        new BuildTopicSubscriberRoutesPass('');
+        $pass = new BuildTopicSubscriberRoutesPass();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The "enqueue.clients" parameter must be set.');
+        $pass->process(new ContainerBuilder());
     }
 
-    public function testShouldDoNothingIfRouteCollectionServiceIsNotRegistered()
+    public function testThrowsIfNoRouteCollectionServiceFoundForConfiguredTransport()
     {
-        $pass = new BuildTopicSubscriberRoutesPass('aName');
-        $pass->process(new ContainerBuilder());
+        $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['foo', 'bar']);
+
+        $pass = new BuildTopicSubscriberRoutesPass();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Service "enqueue.client.foo.route_collection" not found');
+        $pass->process($container);
     }
 
     public function testThrowIfTaggedProcessorIsBuiltByFactory()
     {
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['aName']);
         $container->register('enqueue.client.aName.route_collection', RouteCollection::class)
             ->addArgument([])
         ;
@@ -60,7 +67,7 @@ public function testThrowIfTaggedProcessorIsBuiltByFactory()
             ->addTag('enqueue.topic_subscriber')
         ;
 
-        $pass = new BuildTopicSubscriberRoutesPass('aName');
+        $pass = new BuildTopicSubscriberRoutesPass();
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('The topic subscriber tag could not be applied to a service created by factory.');
@@ -73,6 +80,7 @@ public function testShouldRegisterProcessorWithMatchedName()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['foo']);
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($this->createTopicSubscriberProcessor()))
             ->addTag('enqueue.topic_subscriber', ['client' => 'foo'])
@@ -81,7 +89,7 @@ public function testShouldRegisterProcessorWithMatchedName()
             ->addTag('enqueue.topic_subscriber', ['client' => 'bar'])
         ;
 
-        $pass = new BuildTopicSubscriberRoutesPass('foo');
+        $pass = new BuildTopicSubscriberRoutesPass();
 
         $pass->process($container);
 
@@ -95,6 +103,7 @@ public function testShouldRegisterProcessorWithoutNameToDefaultClient()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($this->createTopicSubscriberProcessor()))
             ->addTag('enqueue.topic_subscriber')
@@ -103,7 +112,7 @@ public function testShouldRegisterProcessorWithoutNameToDefaultClient()
             ->addTag('enqueue.topic_subscriber', ['client' => 'bar'])
         ;
 
-        $pass = new BuildTopicSubscriberRoutesPass('default');
+        $pass = new BuildTopicSubscriberRoutesPass();
 
         $pass->process($container);
 
@@ -117,6 +126,7 @@ public function testShouldRegisterProcessorIfClientNameEqualsAll()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($this->createTopicSubscriberProcessor()))
             ->addTag('enqueue.topic_subscriber', ['client' => 'all'])
@@ -125,7 +135,7 @@ public function testShouldRegisterProcessorIfClientNameEqualsAll()
             ->addTag('enqueue.topic_subscriber', ['client' => 'bar'])
         ;
 
-        $pass = new BuildTopicSubscriberRoutesPass('default');
+        $pass = new BuildTopicSubscriberRoutesPass();
 
         $pass->process($container);
 
@@ -141,12 +151,13 @@ public function testShouldRegisterProcessorIfTopicsIsString()
         $processor = $this->createTopicSubscriberProcessor('fooTopic');
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.topic_subscriber')
         ;
 
-        $pass = new BuildTopicSubscriberRoutesPass('default');
+        $pass = new BuildTopicSubscriberRoutesPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $routeCollection->getArgument(0));
@@ -173,12 +184,13 @@ public function testThrowIfTopicSubscriberReturnsNothing()
         $processor = $this->createTopicSubscriberProcessor(null);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.topic_subscriber')
         ;
 
-        $pass = new BuildTopicSubscriberRoutesPass('default');
+        $pass = new BuildTopicSubscriberRoutesPass();
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('Topic subscriber must return something.');
@@ -193,12 +205,13 @@ public function testShouldRegisterProcessorIfTopicsAreStrings()
         $processor = $this->createTopicSubscriberProcessor(['fooTopic', 'barTopic']);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.topic_subscriber')
         ;
 
-        $pass = new BuildTopicSubscriberRoutesPass('default');
+        $pass = new BuildTopicSubscriberRoutesPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $routeCollection->getArgument(0));
@@ -234,12 +247,13 @@ public function testShouldRegisterProcessorIfTopicsAreParamArrays()
         ]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.topic_subscriber')
         ;
 
-        $pass = new BuildTopicSubscriberRoutesPass('default');
+        $pass = new BuildTopicSubscriberRoutesPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $routeCollection->getArgument(0));
@@ -274,12 +288,13 @@ public function testThrowIfTopicSubscriberParamsInvalid()
         $processor = $this->createTopicSubscriberProcessor(['fooBar', true]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.topic_subscriber')
         ;
 
-        $pass = new BuildTopicSubscriberRoutesPass('default');
+        $pass = new BuildTopicSubscriberRoutesPass();
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('Topic subscriber configuration is invalid');
@@ -297,12 +312,13 @@ public function testShouldMergeExtractedRoutesWithAlreadySetInCollection()
         $processor = $this->createTopicSubscriberProcessor(['fooTopic']);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.topic_subscriber')
         ;
 
-        $pass = new BuildTopicSubscriberRoutesPass('default');
+        $pass = new BuildTopicSubscriberRoutesPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $routeCollection->getArgument(0));

From b5628db2bb8913503e3602db872dc56a055ad995 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 22 Oct 2018 15:48:47 +0300
Subject: [PATCH 174/337] [client] migrate passes.

---
 .../BuildCommandSubscriberRoutesPass.php      | 144 +++++++++---------
 .../BuildProcessorRegistryPass.php            |   5 +
 .../BuildProcessorRoutesPass.php              | 112 +++++++-------
 .../BuildCommandSubscriberRoutesPassTest.php  |  61 +++++---
 .../BuildProcessorRoutesPassTest.php          |  55 ++++---
 5 files changed, 211 insertions(+), 166 deletions(-)

diff --git a/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php b/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php
index 3e8791a..6cc9934 100644
--- a/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php
+++ b/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php
@@ -10,98 +10,102 @@
 
 final class BuildCommandSubscriberRoutesPass implements CompilerPassInterface
 {
-    /**
-     * @var string
-     */
-    private $name;
+    use FormatClientNameTrait;
 
-    public function __construct(string $clientName)
-    {
-        if (empty($clientName)) {
-            throw new \InvalidArgumentException('The name could not be empty.');
-        }
-
-        $this->name = $clientName;
-    }
+    protected $name;
 
     public function process(ContainerBuilder $container): void
     {
-        $routeCollectionId = sprintf('enqueue.client.%s.route_collection', $this->name);
-        if (false == $container->hasDefinition($routeCollectionId)) {
-            return;
+        if (false == $container->hasParameter('enqueue.clients')) {
+            throw new \LogicException('The "enqueue.clients" parameter must be set.');
         }
 
-        $tag = 'enqueue.command_subscriber';
-        $routeCollection = new RouteCollection([]);
-        foreach ($container->findTaggedServiceIds($tag) as $serviceId => $tagAttributes) {
-            $processorDefinition = $container->getDefinition($serviceId);
-            if ($processorDefinition->getFactory()) {
-                throw new \LogicException('The command subscriber tag could not be applied to a service created by factory.');
-            }
+        $names = $container->getParameter('enqueue.clients');
 
-            $processorClass = $processorDefinition->getClass();
-            if (false == class_exists($processorClass)) {
-                throw new \LogicException(sprintf('The processor class "%s" could not be found.', $processorClass));
+        foreach ($names as $name) {
+            $this->name = $name;
+            $routeCollectionId = sprintf('enqueue.client.%s.route_collection', $this->name);
+            if (false == $container->hasDefinition($routeCollectionId)) {
+                throw new \LogicException(sprintf('Service "%s" not found', $routeCollectionId));
             }
 
-            if (false == is_subclass_of($processorClass, CommandSubscriberInterface::class)) {
-                throw new \LogicException(sprintf('The processor must implement "%s" interface to be used with the tag "%s"', CommandSubscriberInterface::class, $tag));
-            }
+            $tag = 'enqueue.command_subscriber';
+            $routeCollection = new RouteCollection([]);
+            foreach ($container->findTaggedServiceIds($tag) as $serviceId => $tagAttributes) {
+                $processorDefinition = $container->getDefinition($serviceId);
+                if ($processorDefinition->getFactory()) {
+                    throw new \LogicException('The command subscriber tag could not be applied to a service created by factory.');
+                }
 
-            foreach ($tagAttributes as $tagAttribute) {
-                $client = $tagAttribute['client'] ?? 'default';
+                $processorClass = $processorDefinition->getClass();
+                if (false == class_exists($processorClass)) {
+                    throw new \LogicException(sprintf('The processor class "%s" could not be found.', $processorClass));
+                }
 
-                if ($client !== $this->name && 'all' !== $client) {
-                    continue;
+                if (false == is_subclass_of($processorClass, CommandSubscriberInterface::class)) {
+                    throw new \LogicException(sprintf('The processor must implement "%s" interface to be used with the tag "%s"', CommandSubscriberInterface::class, $tag));
                 }
 
-                /** @var CommandSubscriberInterface $processorClass */
-                $commands = $processorClass::getSubscribedCommand();
+                foreach ($tagAttributes as $tagAttribute) {
+                    $client = $tagAttribute['client'] ?? 'default';
 
-                if (empty($commands)) {
-                    throw new \LogicException('Command subscriber must return something.');
-                }
+                    if ($client !== $this->name && 'all' !== $client) {
+                        continue;
+                    }
 
-                if (is_string($commands)) {
-                    $commands = [$commands];
-                }
+                    /** @var CommandSubscriberInterface $processorClass */
+                    $commands = $processorClass::getSubscribedCommand();
 
-                if (!is_array($commands)) {
-                    throw new \LogicException('Command subscriber configuration is invalid. Should be an array or string.');
-                }
+                    if (empty($commands)) {
+                        throw new \LogicException('Command subscriber must return something.');
+                    }
 
-                if (isset($commands['command'])) {
-                    $commands = [$commands];
-                }
+                    if (is_string($commands)) {
+                        $commands = [$commands];
+                    }
+
+                    if (!is_array($commands)) {
+                        throw new \LogicException('Command subscriber configuration is invalid. Should be an array or string.');
+                    }
+
+                    if (isset($commands['command'])) {
+                        $commands = [$commands];
+                    }
 
-                foreach ($commands as $key => $params) {
-                    if (is_string($params)) {
-                        $routeCollection->add(new Route($params, Route::COMMAND, $serviceId, ['processor_service_id' => $serviceId]));
-                    } elseif (is_array($params)) {
-                        $source = $params['command'] ?? null;
-                        $processor = $params['processor'] ?? $serviceId;
-                        unset($params['command'], $params['source'], $params['source_type'], $params['processor'], $params['options']);
-                        $options = $params;
-                        $options['processor_service_id'] = $serviceId;
-
-                        $routeCollection->add(new Route($source, Route::COMMAND, $processor, $options));
-                    } else {
-                        throw new \LogicException(sprintf(
-                            'Command subscriber configuration is invalid for "%s::getSubscribedCommand()". "%s"',
-                            $processorClass,
-                            json_encode($processorClass::getSubscribedCommand())
-                        ));
+                    foreach ($commands as $key => $params) {
+                        if (is_string($params)) {
+                            $routeCollection->add(new Route($params, Route::COMMAND, $serviceId, ['processor_service_id' => $serviceId]));
+                        } elseif (is_array($params)) {
+                            $source = $params['command'] ?? null;
+                            $processor = $params['processor'] ?? $serviceId;
+                            unset($params['command'], $params['source'], $params['source_type'], $params['processor'], $params['options']);
+                            $options = $params;
+                            $options['processor_service_id'] = $serviceId;
+
+                            $routeCollection->add(new Route($source, Route::COMMAND, $processor, $options));
+                        } else {
+                            throw new \LogicException(sprintf(
+                                'Command subscriber configuration is invalid for "%s::getSubscribedCommand()". "%s"',
+                                $processorClass,
+                                json_encode($processorClass::getSubscribedCommand())
+                            ));
+                        }
                     }
                 }
             }
-        }
 
-        $rawRoutes = $routeCollection->toArray();
+            $rawRoutes = $routeCollection->toArray();
+
+            $routeCollectionService = $container->getDefinition($routeCollectionId);
+            $routeCollectionService->replaceArgument(0, array_merge(
+                $routeCollectionService->getArgument(0),
+                $rawRoutes
+            ));
+        }
+    }
 
-        $routeCollectionService = $container->getDefinition($routeCollectionId);
-        $routeCollectionService->replaceArgument(0, array_merge(
-            $routeCollectionService->getArgument(0),
-            $rawRoutes
-        ));
+    protected function getName(): string
+    {
+        return $this->name;
     }
 }
diff --git a/Symfony/Client/DependencyInjection/BuildProcessorRegistryPass.php b/Symfony/Client/DependencyInjection/BuildProcessorRegistryPass.php
index d69935d..0d5f97c 100644
--- a/Symfony/Client/DependencyInjection/BuildProcessorRegistryPass.php
+++ b/Symfony/Client/DependencyInjection/BuildProcessorRegistryPass.php
@@ -57,4 +57,9 @@ public function process(ContainerBuilder $container): void
         $registry = $container->getDefinition($processorRegistryId);
         $registry->setArgument(0, ServiceLocatorTagPass::register($container, $map, $processorRegistryId));
     }
+
+    private function getName(): string
+    {
+        return $this->name;
+    }
 }
diff --git a/Symfony/Client/DependencyInjection/BuildProcessorRoutesPass.php b/Symfony/Client/DependencyInjection/BuildProcessorRoutesPass.php
index 415b87a..c89f374 100644
--- a/Symfony/Client/DependencyInjection/BuildProcessorRoutesPass.php
+++ b/Symfony/Client/DependencyInjection/BuildProcessorRoutesPass.php
@@ -9,72 +9,76 @@
 
 final class BuildProcessorRoutesPass implements CompilerPassInterface
 {
-    /**
-     * @var string
-     */
-    private $name;
+    use FormatClientNameTrait;
 
-    public function __construct(string $clientName)
-    {
-        if (empty($clientName)) {
-            throw new \InvalidArgumentException('The name could not be empty.');
-        }
-
-        $this->name = $clientName;
-    }
+    protected $name;
 
     public function process(ContainerBuilder $container): void
     {
-        $routeCollectionId = sprintf('enqueue.client.%s.route_collection', $this->name);
-        if (false == $container->hasDefinition($routeCollectionId)) {
-            return;
+        if (false == $container->hasParameter('enqueue.clients')) {
+            throw new \LogicException('The "enqueue.clients" parameter must be set.');
         }
 
-        $tag = 'enqueue.processor';
-        $routeCollection = new RouteCollection([]);
-        foreach ($container->findTaggedServiceIds($tag) as $serviceId => $tagAttributes) {
-            foreach ($tagAttributes as $tagAttribute) {
-                $client = $tagAttribute['client'] ?? 'default';
+        $names = $container->getParameter('enqueue.clients');
 
-                if ($client !== $this->name && 'all' !== $client) {
-                    continue;
-                }
+        foreach ($names as $name) {
+            $this->name = $name;
+            $routeCollectionId = $this->format('route_collection');
+            if (false == $container->hasDefinition($routeCollectionId)) {
+                throw new \LogicException(sprintf('Service "%s" not found', $routeCollectionId));
+            }
 
-                $topic = $tagAttribute['topic'] ?? null;
-                $command = $tagAttribute['command'] ?? null;
+            $tag = 'enqueue.processor';
+            $routeCollection = new RouteCollection([]);
+            foreach ($container->findTaggedServiceIds($tag) as $serviceId => $tagAttributes) {
+                foreach ($tagAttributes as $tagAttribute) {
+                    $client = $tagAttribute['client'] ?? 'default';
 
-                if (false == $topic && false == $command) {
-                    throw new \LogicException(sprintf('Either "topic" or "command" tag attribute must be set on service "%s". None is set.', $serviceId));
-                }
-                if ($topic && $command) {
-                    throw new \LogicException(sprintf('Either "topic" or "command" tag attribute must be set on service "%s". Both are set.', $serviceId));
-                }
+                    if ($client !== $this->name && 'all' !== $client) {
+                        continue;
+                    }
+
+                    $topic = $tagAttribute['topic'] ?? null;
+                    $command = $tagAttribute['command'] ?? null;
 
-                $source = $command ?: $topic;
-                $sourceType = $command ? Route::COMMAND : Route::TOPIC;
-                $processor = $tagAttribute['processor'] ?? $serviceId;
-
-                unset(
-                    $tagAttribute['topic'],
-                    $tagAttribute['command'],
-                    $tagAttribute['source'],
-                    $tagAttribute['source_type'],
-                    $tagAttribute['processor'],
-                    $tagAttribute['options']
-                );
-                $options = $tagAttribute;
-                $options['processor_service_id'] = $serviceId;
-
-                $routeCollection->add(new Route($source, $sourceType, $processor, $options));
+                    if (false == $topic && false == $command) {
+                        throw new \LogicException(sprintf('Either "topic" or "command" tag attribute must be set on service "%s". None is set.', $serviceId));
+                    }
+                    if ($topic && $command) {
+                        throw new \LogicException(sprintf('Either "topic" or "command" tag attribute must be set on service "%s". Both are set.', $serviceId));
+                    }
+
+                    $source = $command ?: $topic;
+                    $sourceType = $command ? Route::COMMAND : Route::TOPIC;
+                    $processor = $tagAttribute['processor'] ?? $serviceId;
+
+                    unset(
+                        $tagAttribute['topic'],
+                        $tagAttribute['command'],
+                        $tagAttribute['source'],
+                        $tagAttribute['source_type'],
+                        $tagAttribute['processor'],
+                        $tagAttribute['options']
+                    );
+                    $options = $tagAttribute;
+                    $options['processor_service_id'] = $serviceId;
+
+                    $routeCollection->add(new Route($source, $sourceType, $processor, $options));
+                }
             }
-        }
 
-        $rawRoutes = $routeCollection->toArray();
+            $rawRoutes = $routeCollection->toArray();
 
-        $routeCollectionService = $container->getDefinition($routeCollectionId);
-        $routeCollectionService->replaceArgument(0, array_merge(
-            $routeCollectionService->getArgument(0),
-            $rawRoutes
-        ));
+            $routeCollectionService = $container->getDefinition($routeCollectionId);
+            $routeCollectionService->replaceArgument(0, array_merge(
+                $routeCollectionService->getArgument(0),
+                $rawRoutes
+            ));
+        }
+    }
+
+    protected function getName(): string
+    {
+        return $this->name;
     }
 }
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
index 9d37522..e077a76 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
@@ -29,29 +29,36 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildCommandSubscriberRoutesPass::class);
     }
 
-    public function testCouldBeConstructedWithName()
+    public function testCouldBeConstructedWithoutArguments()
     {
-        $pass = new BuildCommandSubscriberRoutesPass('aName');
-
-        $this->assertAttributeSame('aName', 'name', $pass);
+        new BuildCommandSubscriberRoutesPass();
     }
 
-    public function testThrowIfNameEmptyOnConstruct()
+    public function testThrowIfEnqueueClientsParameterNotSet()
     {
-        $this->expectException(\InvalidArgumentException::class);
-        $this->expectExceptionMessage('The name could not be empty.');
-        new BuildCommandSubscriberRoutesPass('');
+        $pass = new BuildCommandSubscriberRoutesPass();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The "enqueue.clients" parameter must be set.');
+        $pass->process(new ContainerBuilder());
     }
 
-    public function testShouldDoNothingIfRouteCollectionServiceIsNotRegistered()
+    public function testThrowsIfNoRouteCollectionServiceFoundForConfiguredTransport()
     {
-        $pass = new BuildCommandSubscriberRoutesPass('aName');
-        $pass->process(new ContainerBuilder());
+        $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['foo', 'bar']);
+
+        $pass = new BuildCommandSubscriberRoutesPass();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Service "enqueue.client.foo.route_collection" not found');
+        $pass->process($container);
     }
 
     public function testThrowIfTaggedProcessorIsBuiltByFactory()
     {
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['aName']);
         $container->register('enqueue.client.aName.route_collection', RouteCollection::class)
             ->addArgument([])
         ;
@@ -60,7 +67,7 @@ public function testThrowIfTaggedProcessorIsBuiltByFactory()
             ->addTag('enqueue.command_subscriber')
         ;
 
-        $pass = new BuildCommandSubscriberRoutesPass('aName');
+        $pass = new BuildCommandSubscriberRoutesPass();
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('The command subscriber tag could not be applied to a service created by factory.');
@@ -73,6 +80,7 @@ public function testShouldRegisterProcessorWithMatchedName()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['foo']);
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($this->createCommandSubscriberProcessor()))
             ->addTag('enqueue.command_subscriber', ['client' => 'foo'])
@@ -81,7 +89,7 @@ public function testShouldRegisterProcessorWithMatchedName()
             ->addTag('enqueue.command_subscriber', ['client' => 'bar'])
         ;
 
-        $pass = new BuildCommandSubscriberRoutesPass('foo');
+        $pass = new BuildCommandSubscriberRoutesPass();
 
         $pass->process($container);
 
@@ -95,6 +103,7 @@ public function testShouldRegisterProcessorWithoutNameToDefaultClient()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($this->createCommandSubscriberProcessor()))
             ->addTag('enqueue.command_subscriber')
@@ -103,7 +112,7 @@ public function testShouldRegisterProcessorWithoutNameToDefaultClient()
             ->addTag('enqueue.command_subscriber', ['client' => 'bar'])
         ;
 
-        $pass = new BuildCommandSubscriberRoutesPass('default');
+        $pass = new BuildCommandSubscriberRoutesPass();
 
         $pass->process($container);
 
@@ -117,6 +126,7 @@ public function testShouldRegisterProcessorIfClientNameEqualsAll()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($this->createCommandSubscriberProcessor()))
             ->addTag('enqueue.command_subscriber', ['client' => 'all'])
@@ -125,7 +135,7 @@ public function testShouldRegisterProcessorIfClientNameEqualsAll()
             ->addTag('enqueue.command_subscriber', ['client' => 'bar'])
         ;
 
-        $pass = new BuildCommandSubscriberRoutesPass('default');
+        $pass = new BuildCommandSubscriberRoutesPass();
 
         $pass->process($container);
 
@@ -141,12 +151,13 @@ public function testShouldRegisterProcessorIfCommandsIsString()
         $processor = $this->createCommandSubscriberProcessor('fooCommand');
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.command_subscriber')
         ;
 
-        $pass = new BuildCommandSubscriberRoutesPass('default');
+        $pass = new BuildCommandSubscriberRoutesPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $routeCollection->getArgument(0));
@@ -173,12 +184,13 @@ public function testThrowIfCommandSubscriberReturnsNothing()
         $processor = $this->createCommandSubscriberProcessor(null);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.command_subscriber')
         ;
 
-        $pass = new BuildCommandSubscriberRoutesPass('default');
+        $pass = new BuildCommandSubscriberRoutesPass();
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('Command subscriber must return something.');
@@ -193,12 +205,13 @@ public function testShouldRegisterProcessorIfCommandsAreStrings()
         $processor = $this->createCommandSubscriberProcessor(['fooCommand', 'barCommand']);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.command_subscriber')
         ;
 
-        $pass = new BuildCommandSubscriberRoutesPass('default');
+        $pass = new BuildCommandSubscriberRoutesPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $routeCollection->getArgument(0));
@@ -235,12 +248,13 @@ public function testShouldRegisterProcessorIfParamSingleCommandArray()
         ]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.command_subscriber')
         ;
 
-        $pass = new BuildCommandSubscriberRoutesPass('default');
+        $pass = new BuildCommandSubscriberRoutesPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $routeCollection->getArgument(0));
@@ -272,12 +286,13 @@ public function testShouldRegisterProcessorIfCommandsAreParamArrays()
         ]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.command_subscriber')
         ;
 
-        $pass = new BuildCommandSubscriberRoutesPass('default');
+        $pass = new BuildCommandSubscriberRoutesPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $routeCollection->getArgument(0));
@@ -312,12 +327,13 @@ public function testThrowIfCommandSubscriberParamsInvalid()
         $processor = $this->createCommandSubscriberProcessor(['fooBar', true]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.command_subscriber')
         ;
 
-        $pass = new BuildCommandSubscriberRoutesPass('default');
+        $pass = new BuildCommandSubscriberRoutesPass();
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('Command subscriber configuration is invalid');
@@ -335,12 +351,13 @@ public function testShouldMergeExtractedRoutesWithAlreadySetInCollection()
         $processor = $this->createCommandSubscriberProcessor(['fooCommand']);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.command_subscriber')
         ;
 
-        $pass = new BuildCommandSubscriberRoutesPass('default');
+        $pass = new BuildCommandSubscriberRoutesPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $routeCollection->getArgument(0));
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php
index b0d7af5..e671f7a 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php
@@ -25,24 +25,30 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildProcessorRoutesPass::class);
     }
 
-    public function testCouldBeConstructedWithName()
+    public function testCouldBeConstructedWithoutArguments()
     {
-        $pass = new BuildProcessorRoutesPass('aName');
-
-        $this->assertAttributeSame('aName', 'name', $pass);
+        new BuildProcessorRoutesPass();
     }
 
-    public function testThrowIfNameEmptyOnConstruct()
+    public function testThrowIfEnqueueClientsParameterNotSet()
     {
-        $this->expectException(\InvalidArgumentException::class);
-        $this->expectExceptionMessage('The name could not be empty.');
-        new BuildProcessorRoutesPass('');
+        $pass = new BuildProcessorRoutesPass();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The "enqueue.clients" parameter must be set.');
+        $pass->process(new ContainerBuilder());
     }
 
-    public function testShouldDoNothingIfRouteCollectionServiceIsNotRegistered()
+    public function testThrowsIfNoRouteCollectionServiceFoundForConfiguredTransport()
     {
-        $pass = new BuildProcessorRoutesPass('aName');
-        $pass->process(new ContainerBuilder());
+        $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['foo', 'bar']);
+
+        $pass = new BuildProcessorRoutesPass();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Service "enqueue.client.foo.route_collection" not found');
+        $pass->process($container);
     }
 
     public function testThrowIfBothTopicAndCommandAttributesAreSet()
@@ -51,12 +57,13 @@ public function testThrowIfBothTopicAndCommandAttributesAreSet()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.processor', ['topic' => 'foo', 'command' => 'bar'])
         ;
 
-        $pass = new BuildProcessorRoutesPass('default');
+        $pass = new BuildProcessorRoutesPass();
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('Either "topic" or "command" tag attribute must be set on service "aFooProcessor". Both are set.');
@@ -69,12 +76,13 @@ public function testThrowIfNeitherTopicNorCommandAttributesAreSet()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.processor', [])
         ;
 
-        $pass = new BuildProcessorRoutesPass('default');
+        $pass = new BuildProcessorRoutesPass();
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('Either "topic" or "command" tag attribute must be set on service "aFooProcessor". None is set.');
@@ -87,6 +95,7 @@ public function testShouldRegisterProcessorWithMatchedName()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['foo']);
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.processor', ['client' => 'foo', 'topic' => 'foo'])
@@ -95,7 +104,7 @@ public function testShouldRegisterProcessorWithMatchedName()
             ->addTag('enqueue.processor', ['client' => 'bar', 'command' => 'foo'])
         ;
 
-        $pass = new BuildProcessorRoutesPass('foo');
+        $pass = new BuildProcessorRoutesPass();
 
         $pass->process($container);
 
@@ -109,6 +118,7 @@ public function testShouldRegisterProcessorWithoutNameToDefaultClient()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.processor', ['topic' => 'foo'])
@@ -117,7 +127,7 @@ public function testShouldRegisterProcessorWithoutNameToDefaultClient()
             ->addTag('enqueue.processor', ['client' => 'bar', 'command' => 'foo'])
         ;
 
-        $pass = new BuildProcessorRoutesPass('default');
+        $pass = new BuildProcessorRoutesPass();
 
         $pass->process($container);
 
@@ -131,6 +141,7 @@ public function testShouldRegisterProcessorIfClientNameEqualsAll()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.processor', ['client' => 'all', 'topic' => 'foo'])
@@ -139,7 +150,7 @@ public function testShouldRegisterProcessorIfClientNameEqualsAll()
             ->addTag('enqueue.processor', ['client' => 'bar', 'command' => 'foo'])
         ;
 
-        $pass = new BuildProcessorRoutesPass('default');
+        $pass = new BuildProcessorRoutesPass();
 
         $pass->process($container);
 
@@ -153,12 +164,13 @@ public function testShouldRegisterAsTopicProcessor()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.processor', ['topic' => 'aTopic'])
         ;
 
-        $pass = new BuildProcessorRoutesPass('default');
+        $pass = new BuildProcessorRoutesPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $routeCollection->getArgument(0));
@@ -183,12 +195,13 @@ public function testShouldRegisterAsCommandProcessor()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.processor', ['command' => 'aCommand'])
         ;
 
-        $pass = new BuildProcessorRoutesPass('default');
+        $pass = new BuildProcessorRoutesPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $routeCollection->getArgument(0));
@@ -213,12 +226,13 @@ public function testShouldRegisterWithCustomProcessorName()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.processor', ['command' => 'aCommand', 'processor' => 'customProcessorName'])
         ;
 
-        $pass = new BuildProcessorRoutesPass('default');
+        $pass = new BuildProcessorRoutesPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $routeCollection->getArgument(0));
@@ -246,12 +260,13 @@ public function testShouldMergeExtractedRoutesWithAlreadySetInCollection()
         ]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.processor', ['command' => 'fooCommand'])
         ;
 
-        $pass = new BuildProcessorRoutesPass('default');
+        $pass = new BuildProcessorRoutesPass();
         $pass->process($container);
 
         $this->assertInternalType('array', $routeCollection->getArgument(0));

From 03d2b5b06df911b2ce3ff828bb12c3c5618fd7f8 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 22 Oct 2018 16:21:21 +0300
Subject: [PATCH 175/337] [client] migrate passes.

---
 .../BuildProcessorRegistryPass.php            | 66 +++++++-------
 .../BuildProcessorRegistryPassTest.php        | 91 ++++++++++++-------
 2 files changed, 93 insertions(+), 64 deletions(-)

diff --git a/Symfony/Client/DependencyInjection/BuildProcessorRegistryPass.php b/Symfony/Client/DependencyInjection/BuildProcessorRegistryPass.php
index 0d5f97c..cdf40a8 100644
--- a/Symfony/Client/DependencyInjection/BuildProcessorRegistryPass.php
+++ b/Symfony/Client/DependencyInjection/BuildProcessorRegistryPass.php
@@ -10,52 +10,52 @@
 
 final class BuildProcessorRegistryPass implements CompilerPassInterface
 {
-    /**
-     * @var string
-     */
-    private $name;
+    use FormatClientNameTrait;
 
-    public function __construct(string $clientName)
-    {
-        if (empty($clientName)) {
-            throw new \InvalidArgumentException('The name could not be empty.');
-        }
-
-        $this->name = $clientName;
-    }
+    protected $name;
 
     public function process(ContainerBuilder $container): void
     {
-        $processorRegistryId = sprintf('enqueue.client.%s.processor_registry', $this->name);
-        if (false == $container->hasDefinition($processorRegistryId)) {
-            return;
+        if (false == $container->hasParameter('enqueue.clients')) {
+            throw new \LogicException('The "enqueue.clients" parameter must be set.');
         }
 
-        $routeCollectionId = sprintf('enqueue.client.%s.route_collection', $this->name);
-        if (false == $container->hasDefinition($routeCollectionId)) {
-            return;
-        }
+        $names = $container->getParameter('enqueue.clients');
 
-        $routerProcessorId = sprintf('enqueue.client.%s.router_processor', $this->name);
-        if (false == $container->hasDefinition($routerProcessorId)) {
-            return;
-        }
+        foreach ($names as $name) {
+            $this->name = $name;
+
+            $processorRegistryId = $this->format('processor_registry');
+            if (false == $container->hasDefinition($processorRegistryId)) {
+                throw new \LogicException(sprintf('Service "%s" not found', $processorRegistryId));
+            }
 
-        $routeCollection = RouteCollection::fromArray($container->getDefinition($routeCollectionId)->getArgument(0));
+            $routeCollectionId = $this->format('route_collection');
+            if (false == $container->hasDefinition($routeCollectionId)) {
+                throw new \LogicException(sprintf('Service "%s" not found', $routeCollectionId));
+            }
 
-        $map = [];
-        foreach ($routeCollection->all() as $route) {
-            if (false == $processorServiceId = $route->getOption('processor_service_id')) {
-                throw new \LogicException('The route option "processor_service_id" is required');
+            $routerProcessorId = $this->format('router_processor');
+            if (false == $container->hasDefinition($routerProcessorId)) {
+                throw new \LogicException(sprintf('Service "%s" not found', $routerProcessorId));
             }
 
-            $map[$route->getProcessor()] = new Reference($processorServiceId);
-        }
+            $routeCollection = RouteCollection::fromArray($container->getDefinition($routeCollectionId)->getArgument(0));
 
-        $map["%enqueue.client.{$this->name}.router_processor%"] = new Reference($routerProcessorId);
+            $map = [];
+            foreach ($routeCollection->all() as $route) {
+                if (false == $processorServiceId = $route->getOption('processor_service_id')) {
+                    throw new \LogicException('The route option "processor_service_id" is required');
+                }
 
-        $registry = $container->getDefinition($processorRegistryId);
-        $registry->setArgument(0, ServiceLocatorTagPass::register($container, $map, $processorRegistryId));
+                $map[$route->getProcessor()] = new Reference($processorServiceId);
+            }
+
+            $map[$this->parameter('router_processor')] = new Reference($routerProcessorId);
+
+            $registry = $container->getDefinition($processorRegistryId);
+            $registry->setArgument(0, ServiceLocatorTagPass::register($container, $map, $processorRegistryId));
+        }
     }
 
     private function getName(): string
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php
index 5979652..c0142f0 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php
@@ -6,6 +6,7 @@
 use Enqueue\Symfony\Client\DependencyInjection\BuildProcessorRegistryPass;
 use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Definition;
@@ -25,70 +26,70 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildProcessorRegistryPass::class);
     }
 
-    public function testCouldBeConstructedWithName()
+    public function testCouldBeConstructedWithoutArguments()
     {
-        $pass = new BuildProcessorRegistryPass('aName');
-
-        $this->assertAttributeSame('aName', 'name', $pass);
+        new BuildProcessorRegistryPass();
     }
 
-    public function testThrowIfNameEmptyOnConstruct()
+    public function testThrowIfEnqueueClientsParameterNotSet()
     {
-        $this->expectException(\InvalidArgumentException::class);
-        $this->expectExceptionMessage('The name could not be empty.');
-        new BuildProcessorRegistryPass('');
+        $pass = new BuildProcessorRegistryPass();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The "enqueue.clients" parameter must be set.');
+        $pass->process(new ContainerBuilder());
     }
 
-    public function testShouldDoNothingIfRouteCollectionServiceIsNotRegistered()
+    public function testThrowsIfNoProcessorRegistryServiceFoundForConfiguredTransport()
     {
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['foo', 'bar']);
 
-        //guard
-        $this->assertFalse($container->hasDefinition('enqueue.client.aName.route_collection'));
+        $pass = new BuildProcessorRegistryPass();
 
-        $pass = new BuildProcessorRegistryPass('aName');
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Service "enqueue.client.foo.processor_registry" not found');
         $pass->process($container);
     }
 
-    public function testShouldDoNothingIfProcessorRegistryCollectionServiceIsNotRegistered()
+    public function testThrowsIfNoRouteCollectionServiceFoundForConfiguredTransport()
     {
         $container = new ContainerBuilder();
-        $container->register('enqueue.client.aName.route_collection');
+        $container->setParameter('enqueue.clients', ['foo', 'bar']);
+        $container->register('enqueue.client.foo.processor_registry');
 
-        //guard
-        $this->assertFalse($container->hasDefinition('enqueue.client.aName.processor_registry'));
+        $pass = new BuildProcessorRegistryPass();
 
-        $pass = new BuildProcessorRegistryPass('aName');
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Service "enqueue.client.foo.route_collection" not found');
         $pass->process($container);
     }
 
-    public function testShouldDoNothingIfRouterProcessorServiceIsNotRegistered()
+    public function testThrowsIfNoRouteProcessorServiceFoundForConfiguredTransport()
     {
         $container = new ContainerBuilder();
-        $container->register('enqueue.client.aName.route_collection');
-        $container->register('enqueue.client.aName.processor_registry')
-            ->addArgument([])
-        ;
+        $container->setParameter('enqueue.clients', ['foo', 'bar']);
+        $container->register('enqueue.client.foo.processor_registry');
+        $container->register('enqueue.client.foo.route_collection');
 
-        //guard
-        $this->assertFalse($container->hasDefinition('enqueue.client.aName.router_processor'));
+        $pass = new BuildProcessorRegistryPass();
 
-        $pass = new BuildProcessorRegistryPass('aName');
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Service "enqueue.client.foo.router_processor" not found');
         $pass->process($container);
-
-        $this->assertSame([], $container->getDefinition('enqueue.client.aName.processor_registry')->getArgument(0));
     }
 
     public function testThrowIfProcessorServiceIdOptionNotSet()
     {
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['aName']);
         $container->register('enqueue.client.aName.route_collection')->addArgument([
             (new Route('aCommand', Route::COMMAND, 'aProcessor'))->toArray(),
         ]);
         $container->register('enqueue.client.aName.processor_registry')->addArgument([]);
         $container->register('enqueue.client.aName.router_processor');
 
-        $pass = new BuildProcessorRegistryPass('aName');
+        $pass = new BuildProcessorRegistryPass();
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('The route option "processor_service_id" is required');
@@ -101,6 +102,7 @@ public function testShouldPassLocatorAsFirstArgument()
         $registry->addArgument([]);
 
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['aName']);
         $container->register('enqueue.client.aName.route_collection')->addArgument([
             (new Route(
                 'aCommand',
@@ -118,10 +120,37 @@ public function testShouldPassLocatorAsFirstArgument()
         $container->setDefinition('enqueue.client.aName.processor_registry', $registry);
         $container->register('enqueue.client.aName.router_processor');
 
-        $pass = new BuildProcessorRegistryPass('aName');
+        $pass = new BuildProcessorRegistryPass();
         $pass->process($container);
 
-        $this->assertInstanceOf(Reference::class, $registry->getArgument(0));
-        $this->assertRegExp('/service_locator\..*?\.enqueue\.client\.aName\.processor_registry/', (string) $registry->getArgument(0));
+        $this->assertLocatorServices($container, $registry->getArgument(0), [
+            '%enqueue.client.aName.router_processor%' => 'enqueue.client.aName.router_processor',
+            'aBarProcessor' => 'aBarServiceId',
+            'aFooProcessor' => 'aFooServiceId',
+        ]);
+    }
+
+    private function assertLocatorServices(ContainerBuilder $container, $locatorId, array $locatorServices)
+    {
+        $this->assertInstanceOf(Reference::class, $locatorId);
+        $locatorId = (string) $locatorId;
+
+        $this->assertTrue($container->hasDefinition($locatorId));
+        $this->assertRegExp('/service_locator\..*?\.enqueue\./', $locatorId);
+
+        $match = [];
+        if (false == preg_match('/(service_locator\..*?)\.enqueue\./', $locatorId, $match)) {
+            $this->fail('preg_match should not failed');
+        }
+
+        $this->assertTrue($container->hasDefinition($match[1]));
+        $locator = $container->getDefinition($match[1]);
+
+        $this->assertContainsOnly(ServiceClosureArgument::class, $locator->getArgument(0));
+        $actualServices = array_map(function (ServiceClosureArgument $value) {
+            return (string) $value->getValues()[0];
+        }, $locator->getArgument(0));
+
+        $this->assertEquals($locatorServices, $actualServices);
     }
 }

From 2e81a602e4d630179fb1f395390ca2df6599b7b3 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 22 Oct 2018 18:39:13 +0300
Subject: [PATCH 176/337] [client] migrate passes.

---
 .../AnalyzeRouteCollectionPass.php            | 40 ++++++++++---------
 .../DependencyInjection/TransportFactory.php  |  2 +-
 .../AnalyzeRouteCollectionPassTest.php        | 40 ++++++++++++-------
 3 files changed, 48 insertions(+), 34 deletions(-)

diff --git a/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php b/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php
index c8b140c..234c7b4 100644
--- a/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php
+++ b/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php
@@ -8,31 +8,35 @@
 
 final class AnalyzeRouteCollectionPass implements CompilerPassInterface
 {
-    /**
-     * @var string
-     */
-    private $name;
+    use FormatClientNameTrait;
 
-    public function __construct(string $clientName)
-    {
-        if (empty($clientName)) {
-            throw new \InvalidArgumentException('The name could not be empty.');
-        }
-
-        $this->name = $clientName;
-    }
+    protected $name;
 
     public function process(ContainerBuilder $container): void
     {
-        $routeCollectionId = sprintf('enqueue.client.%s.route_collection', $this->name);
-        if (false == $container->hasDefinition($routeCollectionId)) {
-            return;
+        if (false == $container->hasParameter('enqueue.clients')) {
+            throw new \LogicException('The "enqueue.clients" parameter must be set.');
         }
 
-        $collection = RouteCollection::fromArray($container->getDefinition($routeCollectionId)->getArgument(0));
+        $names = $container->getParameter('enqueue.clients');
+
+        foreach ($names as $name) {
+            $this->name = $name;
+            $routeCollectionId = $this->format('route_collection');
+            if (false == $container->hasDefinition($routeCollectionId)) {
+                throw new \LogicException(sprintf('Service "%s" not found', $routeCollectionId));
+            }
 
-        $this->exclusiveCommandsCouldNotBeRunOnDefaultQueue($collection);
-        $this->exclusiveCommandProcessorMustBeSingleOnGivenQueue($collection);
+            $collection = RouteCollection::fromArray($container->getDefinition($routeCollectionId)->getArgument(0));
+
+            $this->exclusiveCommandsCouldNotBeRunOnDefaultQueue($collection);
+            $this->exclusiveCommandProcessorMustBeSingleOnGivenQueue($collection);
+        }
+    }
+
+    protected function getName(): string
+    {
+        return $this->name;
     }
 
     private function exclusiveCommandsCouldNotBeRunOnDefaultQueue(RouteCollection $collection)
diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index f70b1cb..19712ff 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -182,7 +182,7 @@ public function buildQueueConsumer(ContainerBuilder $container, array $config):
             ->addArgument(new Reference($this->format('consumption_extensions')))
             ->addArgument([])
             ->addArgument(new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE))
-            ->addArgument($this->format('receive_timeout', true))
+            ->addArgument($this->parameter('receive_timeout'))
         ;
 
         $container->register($this->format('processor_registry'), ContainerProcessorRegistry::class);
diff --git a/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php b/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php
index c2f7386..9e4dfde 100644
--- a/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php
@@ -23,29 +23,36 @@ public function testShouldBeFinal()
         $this->assertClassFinal(AnalyzeRouteCollectionPass::class);
     }
 
-    public function testCouldBeConstructedWithName()
+    public function testCouldBeConstructedWithoutArguments()
     {
-        $pass = new AnalyzeRouteCollectionPass('aName');
-
-        $this->assertAttributeSame('aName', 'name', $pass);
+        new AnalyzeRouteCollectionPass();
     }
 
-    public function testThrowIfNameEmptyOnConstruct()
+    public function testThrowIfEnqueueClientsParameterNotSet()
     {
-        $this->expectException(\InvalidArgumentException::class);
-        $this->expectExceptionMessage('The name could not be empty.');
-        new AnalyzeRouteCollectionPass('');
+        $pass = new AnalyzeRouteCollectionPass();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The "enqueue.clients" parameter must be set.');
+        $pass->process(new ContainerBuilder());
     }
 
-    public function testShouldDoNothingIfRouteCollectionServiceIsNotRegistered()
+    public function testThrowsIfNoRouteCollectionServiceFoundForConfiguredTransport()
     {
-        $pass = new AnalyzeRouteCollectionPass('aName');
-        $pass->process(new ContainerBuilder());
+        $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['foo', 'bar']);
+
+        $pass = new AnalyzeRouteCollectionPass();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Service "enqueue.client.foo.route_collection" not found');
+        $pass->process($container);
     }
 
     public function testThrowIfExclusiveCommandProcessorOnDefaultQueue()
     {
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['aName']);
         $container->register('enqueue.client.aName.route_collection')->addArgument([
             (new Route(
                 'aCommand',
@@ -57,7 +64,7 @@ public function testThrowIfExclusiveCommandProcessorOnDefaultQueue()
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('The command "aCommand" processor "aBarProcessor" is exclusive but queue is not specified. Exclusive processors could not be run on a default queue.');
-        $pass = new AnalyzeRouteCollectionPass('aName');
+        $pass = new AnalyzeRouteCollectionPass();
 
         $pass->process($container);
     }
@@ -65,6 +72,7 @@ public function testThrowIfExclusiveCommandProcessorOnDefaultQueue()
     public function testThrowIfTwoExclusiveCommandProcessorsWorkOnSamePrefixedQueue()
     {
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['aName']);
         $container->register('enqueue.client.aName.route_collection')->addArgument([
             (new Route(
                 'aFooCommand',
@@ -83,7 +91,7 @@ public function testThrowIfTwoExclusiveCommandProcessorsWorkOnSamePrefixedQueue(
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('The command "aBarCommand" processor "aBarProcessor" is exclusive. The queue "aQueue" already has another exclusive command processor "aFooProcessor" bound to it.');
-        $pass = new AnalyzeRouteCollectionPass('aName');
+        $pass = new AnalyzeRouteCollectionPass();
 
         $pass->process($container);
     }
@@ -91,6 +99,7 @@ public function testThrowIfTwoExclusiveCommandProcessorsWorkOnSamePrefixedQueue(
     public function testThrowIfTwoExclusiveCommandProcessorsWorkOnSameQueue()
     {
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['aName']);
         $container->register('enqueue.client.aName.route_collection')->addArgument([
             (new Route(
                 'aFooCommand',
@@ -109,7 +118,7 @@ public function testThrowIfTwoExclusiveCommandProcessorsWorkOnSameQueue()
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('The command "aBarCommand" processor "aBarProcessor" is exclusive. The queue "aQueue" already has another exclusive command processor "aFooProcessor" bound to it.');
-        $pass = new AnalyzeRouteCollectionPass('aName');
+        $pass = new AnalyzeRouteCollectionPass();
 
         $pass->process($container);
     }
@@ -117,6 +126,7 @@ public function testThrowIfTwoExclusiveCommandProcessorsWorkOnSameQueue()
     public function testShouldNotThrowIfTwoExclusiveCommandProcessorsWorkOnQueueWithSameNameButOnePrefixed()
     {
         $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['aName']);
         $container->register('enqueue.client.aName.route_collection')->addArgument([
             (new Route(
                 'aFooCommand',
@@ -133,7 +143,7 @@ public function testShouldNotThrowIfTwoExclusiveCommandProcessorsWorkOnQueueWith
             ))->toArray(),
         ]);
 
-        $pass = new AnalyzeRouteCollectionPass('aName');
+        $pass = new AnalyzeRouteCollectionPass();
 
         $pass->process($container);
     }

From f723b99b8e2e4d13750d21d638e4692e230a011b Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Tue, 23 Oct 2018 10:37:11 +0300
Subject: [PATCH 177/337] wamp

---
 Resources.php | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Resources.php b/Resources.php
index 21172c6..3232ba0 100644
--- a/Resources.php
+++ b/Resources.php
@@ -16,6 +16,7 @@
 use Enqueue\Redis\RedisConnectionFactory;
 use Enqueue\Sqs\SqsConnectionFactory;
 use Enqueue\Stomp\StompConnectionFactory;
+use Enqueue\Wamp\WampConnectionFactory;
 use Interop\Queue\ConnectionFactory;
 
 final class Resources
@@ -163,6 +164,11 @@ public static function getKnownConnections(): array
                 'supportedSchemeExtensions' => [],
                 'package' => 'enqueue/mongodb',
             ];
+            $map[WampConnectionFactory::class] = [
+                'schemes' => ['wamp'],
+                'supportedSchemeExtensions' => [],
+                'package' => 'enqueue/wamp',
+            ];
 
             self::$knownConnections = $map;
         }

From be04a58adf86b4a5f4a9f54a45175ddec0a6712d Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Tue, 23 Oct 2018 13:33:41 +0300
Subject: [PATCH 178/337] wamp

---
 Resources.php           |  2 +-
 Tests/ResourcesTest.php | 19 +++++++++++++++++++
 2 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/Resources.php b/Resources.php
index 3232ba0..508dc26 100644
--- a/Resources.php
+++ b/Resources.php
@@ -165,7 +165,7 @@ public static function getKnownConnections(): array
                 'package' => 'enqueue/mongodb',
             ];
             $map[WampConnectionFactory::class] = [
-                'schemes' => ['wamp'],
+                'schemes' => ['wamp', 'ws'],
                 'supportedSchemeExtensions' => [],
                 'package' => 'enqueue/wamp',
             ];
diff --git a/Tests/ResourcesTest.php b/Tests/ResourcesTest.php
index 52bd13f..ed36236 100644
--- a/Tests/ResourcesTest.php
+++ b/Tests/ResourcesTest.php
@@ -4,6 +4,7 @@
 
 use Enqueue\Redis\RedisConnectionFactory;
 use Enqueue\Resources;
+use Enqueue\Wamp\WampConnectionFactory;
 use Interop\Queue\ConnectionFactory;
 use PHPUnit\Framework\TestCase;
 
@@ -127,4 +128,22 @@ public function testShouldAllowGetPreviouslyRegisteredConnection()
         $this->assertArrayHasKey('package', $connectionInfo);
         $this->assertSame('foo/bar', $connectionInfo['package']);
     }
+
+    public function testShouldHaveRegisteredWampConfiguration()
+    {
+        $availableConnections = Resources::getKnownConnections();
+
+        $this->assertInternalType('array', $availableConnections);
+        $this->assertArrayHasKey(WampConnectionFactory::class, $availableConnections);
+
+        $connectionInfo = $availableConnections[WampConnectionFactory::class];
+        $this->assertArrayHasKey('schemes', $connectionInfo);
+        $this->assertSame(['wamp', 'ws'], $connectionInfo['schemes']);
+
+        $this->assertArrayHasKey('supportedSchemeExtensions', $connectionInfo);
+        $this->assertSame([], $connectionInfo['supportedSchemeExtensions']);
+
+        $this->assertArrayHasKey('package', $connectionInfo);
+        $this->assertSame('enqueue/wamp', $connectionInfo['package']);
+    }
 }

From 03b6ddff45c8bea119d57de74056fa08e8cb61b3 Mon Sep 17 00:00:00 2001
From: webmake <webmake@users.noreply.github.com>
Date: Sat, 27 Oct 2018 10:56:55 +0000
Subject: [PATCH 179/337] Do not export non source files

---
 .gitattributes | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100644 .gitattributes

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..bdf2dcb
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,5 @@
+/Tests export-ignore
+.gitattributes export-ignore
+.gitignore export-ignore
+.travis.yml export-ignore
+phpunit.xml.dist export-ignore

From be4df1f66f28aa5ced47904a246cc35bf1512965 Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Thu, 1 Nov 2018 15:08:01 +0200
Subject: [PATCH 180/337] metrics

---
 Consumption/Context/MessageResult.php      | 14 +++++++++++++-
 Consumption/Context/ProcessorException.php | 14 +++++++++++++-
 Consumption/QueueConsumer.php              | 12 ++++--------
 3 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/Consumption/Context/MessageResult.php b/Consumption/Context/MessageResult.php
index fccda66..302899c 100644
--- a/Consumption/Context/MessageResult.php
+++ b/Consumption/Context/MessageResult.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Consumption\Context;
 
 use Enqueue\Consumption\Result;
+use Interop\Queue\Consumer;
 use Interop\Queue\Context;
 use Interop\Queue\Message;
 use Psr\Log\LoggerInterface;
@@ -14,6 +15,11 @@ final class MessageResult
      */
     private $context;
 
+    /**
+     * @var Consumer
+     */
+    private $consumer;
+
     /**
      * @var Message
      */
@@ -34,9 +40,10 @@ final class MessageResult
      */
     private $logger;
 
-    public function __construct(Context $context, Message $message, $result, int $receivedAt, LoggerInterface $logger)
+    public function __construct(Context $context, Consumer $consumer, Message $message, $result, int $receivedAt, LoggerInterface $logger)
     {
         $this->context = $context;
+        $this->consumer = $consumer;
         $this->message = $message;
         $this->logger = $logger;
         $this->result = $result;
@@ -48,6 +55,11 @@ public function getContext(): Context
         return $this->context;
     }
 
+    public function getConsumer(): Consumer
+    {
+        return $this->consumer;
+    }
+
     public function getMessage(): Message
     {
         return $this->message;
diff --git a/Consumption/Context/ProcessorException.php b/Consumption/Context/ProcessorException.php
index 139bd67..f41f232 100644
--- a/Consumption/Context/ProcessorException.php
+++ b/Consumption/Context/ProcessorException.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Consumption\Context;
 
 use Enqueue\Consumption\Result;
+use Interop\Queue\Consumer;
 use Interop\Queue\Context;
 use Interop\Queue\Message;
 use Psr\Log\LoggerInterface;
@@ -14,6 +15,11 @@ final class ProcessorException
      */
     private $context;
 
+    /**
+     * @var Consumer
+     */
+    private $consumer;
+
     /**
      * @var Message
      */
@@ -38,9 +44,10 @@ final class ProcessorException
      */
     private $logger;
 
-    public function __construct(Context $context, Message $message, \Exception $exception, int $receivedAt, LoggerInterface $logger)
+    public function __construct(Context $context, Consumer $consumer, Message $message, \Exception $exception, int $receivedAt, LoggerInterface $logger)
     {
         $this->context = $context;
+        $this->consumer = $consumer;
         $this->message = $message;
         $this->exception = $exception;
         $this->logger = $logger;
@@ -52,6 +59,11 @@ public function getContext(): Context
         return $this->context;
     }
 
+    public function getConsumer(): Consumer
+    {
+        return $this->consumer;
+    }
+
     public function getMessage(): Message
     {
         return $this->message;
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index ca858ac..7ba9276 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -196,11 +196,11 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
                 try {
                     $result = $processor->process($message, $this->interopContext);
                 } catch (\Exception $e) {
-                    $result = $this->onProcessorException($extension, $message, $e, $receivedAt);
+                    $result = $this->onProcessorException($extension, $consumer, $message, $e, $receivedAt);
                 }
             }
 
-            $messageResult = new MessageResult($this->interopContext, $message, $result, $receivedAt, $this->logger);
+            $messageResult = new MessageResult($this->interopContext, $consumer, $message, $result, $receivedAt, $this->logger);
             $extension->onResult($messageResult);
             $result = $messageResult->getResult();
 
@@ -274,8 +274,6 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
 
             ++$cycle;
         }
-
-        $this->enableSubscriptionConsumer = $enableSubscriptionConsumer;
     }
 
     /**
@@ -304,9 +302,9 @@ private function onEnd(ExtensionInterface $extension, int $startTime, Subscripti
      *
      * https://github.com/symfony/symfony/blob/cbe289517470eeea27162fd2d523eb29c95f775f/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php#L77
      */
-    private function onProcessorException(ExtensionInterface $extension, Message $message, \Exception $exception, int $receivedAt)
+    private function onProcessorException(ExtensionInterface $extension, Consumer $consumer, Message $message, \Exception $exception, int $receivedAt)
     {
-        $processorException = new ProcessorException($this->interopContext, $message, $exception, $receivedAt, $this->logger);
+        $processorException = new ProcessorException($this->interopContext, $consumer, $message, $exception, $receivedAt, $this->logger);
 
         try {
             $extension->onProcessorException($processorException);
@@ -331,7 +329,5 @@ private function onProcessorException(ExtensionInterface $extension, Message $me
 
             throw $e;
         }
-
-        throw $exception;
     }
 }

From 2a20027e8104123aa91257b44fe695ab45de42ad Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Sat, 3 Nov 2018 10:28:45 +0200
Subject: [PATCH 181/337] [client][bundle] Take queue prefix into account while
 queue binding.

---
 Symfony/Client/ConsumeCommand.php             |  45 +++++---
 .../AnalyzeRouteCollectionPass.php            |  40 ++++++-
 Tests/Symfony/Client/ConsumeCommandTest.php   | 106 ++++++++++++++++--
 .../AnalyzeRouteCollectionPassTest.php        |  28 ++++-
 4 files changed, 189 insertions(+), 30 deletions(-)

diff --git a/Symfony/Client/ConsumeCommand.php b/Symfony/Client/ConsumeCommand.php
index d8b4efb..cc1e46c 100644
--- a/Symfony/Client/ConsumeCommand.php
+++ b/Symfony/Client/ConsumeCommand.php
@@ -96,26 +96,43 @@ protected function execute(InputInterface $input, OutputInterface $output): ?int
 
         $this->setQueueConsumerOptions($consumer, $input);
 
-        $clientQueueNames = $input->getArgument('client-queue-names');
-        if (empty($clientQueueNames)) {
-            $clientQueueNames[$driver->getConfig()->getDefaultQueue()] = true;
-            $clientQueueNames[$driver->getConfig()->getRouterQueue()] = true;
-
-            foreach ($driver->getRouteCollection()->all() as $route) {
-                if ($route->getQueue()) {
-                    $clientQueueNames[$route->getQueue()] = true;
-                }
+        $allQueues[$driver->getConfig()->getDefaultQueue()] = true;
+        $allQueues[$driver->getConfig()->getRouterQueue()] = true;
+        foreach ($driver->getRouteCollection()->all() as $route) {
+            if (false == $route->getQueue()) {
+                continue;
+            }
+            if ($route->isProcessorExternal()) {
+                continue;
             }
 
-            foreach ($input->getOption('skip') as $skipClientQueueName) {
-                unset($clientQueueNames[$skipClientQueueName]);
+            $allQueues[$route->getQueue()] = $route->isPrefixQueue();
+        }
+
+        $selectedQueues = $input->getArgument('client-queue-names');
+        if (empty($selectedQueues)) {
+            $queues = $allQueues;
+        } else {
+            $queues = [];
+            foreach ($selectedQueues as $queue) {
+                if (false == array_key_exists($queue, $allQueues)) {
+                    throw new \LogicException(sprintf(
+                        'There is no such queue "%s". Available are "%s"',
+                        $queue,
+                        implode('", "', array_keys($allQueues))
+                    ));
+                }
+
+                $queues[$queue] = $allQueues[$queue];
             }
+        }
 
-            $clientQueueNames = array_keys($clientQueueNames);
+        foreach ($input->getOption('skip') as $skipQueue) {
+            unset($queues[$skipQueue]);
         }
 
-        foreach ($clientQueueNames as $clientQueueName) {
-            $queue = $driver->createQueue($clientQueueName);
+        foreach ($queues as $queue => $prefix) {
+            $queue = $driver->createQueue($queue, $prefix);
             $consumer->bind($queue, $processor);
         }
 
diff --git a/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php b/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php
index 234c7b4..2613bcc 100644
--- a/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php
+++ b/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php
@@ -31,6 +31,8 @@ public function process(ContainerBuilder $container): void
 
             $this->exclusiveCommandsCouldNotBeRunOnDefaultQueue($collection);
             $this->exclusiveCommandProcessorMustBeSingleOnGivenQueue($collection);
+            $this->customQueueNamesUnique($collection);
+            $this->defaultQueueMustBePrefixed($collection);
         }
     }
 
@@ -39,7 +41,7 @@ protected function getName(): string
         return $this->name;
     }
 
-    private function exclusiveCommandsCouldNotBeRunOnDefaultQueue(RouteCollection $collection)
+    private function exclusiveCommandsCouldNotBeRunOnDefaultQueue(RouteCollection $collection): void
     {
         foreach ($collection->all() as $route) {
             if ($route->isCommand() && $route->isProcessorExclusive() && false == $route->getQueue()) {
@@ -52,7 +54,7 @@ private function exclusiveCommandsCouldNotBeRunOnDefaultQueue(RouteCollection $c
         }
     }
 
-    private function exclusiveCommandProcessorMustBeSingleOnGivenQueue(RouteCollection $collection)
+    private function exclusiveCommandProcessorMustBeSingleOnGivenQueue(RouteCollection $collection): void
     {
         $prefixedQueues = [];
         $queues = [];
@@ -91,4 +93,38 @@ private function exclusiveCommandProcessorMustBeSingleOnGivenQueue(RouteCollecti
             }
         }
     }
+
+    private function defaultQueueMustBePrefixed(RouteCollection $collection): void
+    {
+        foreach ($collection->all() as $route) {
+            if (false == $route->getQueue() && false == $route->isPrefixQueue()) {
+                throw new \LogicException('The default queue must be prefixed.');
+            }
+        }
+    }
+
+    private function customQueueNamesUnique(RouteCollection $collection): void
+    {
+        $prefixedQueues = [];
+        $notPrefixedQueues = [];
+
+        foreach ($collection->all() as $route) {
+            //default queue
+            $queueName = $route->getQueue();
+            if (false == $queueName) {
+                return;
+            }
+
+            $route->isPrefixQueue() ?
+                $prefixedQueues[$queueName] = $queueName :
+                $notPrefixedQueues[$queueName] = $queueName
+            ;
+        }
+
+        foreach ($notPrefixedQueues as $queueName) {
+            if (array_key_exists($queueName, $prefixedQueues)) {
+                throw new \LogicException(sprintf('There are prefixed and not prefixed queue with the same name "%s". This is not allowed.', $queueName));
+            }
+        }
+    }
 }
diff --git a/Tests/Symfony/Client/ConsumeCommandTest.php b/Tests/Symfony/Client/ConsumeCommandTest.php
index e57696a..3b6feb6 100644
--- a/Tests/Symfony/Client/ConsumeCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeCommandTest.php
@@ -96,7 +96,7 @@ public function testShouldBindDefaultQueueOnly()
         $driver
             ->expects($this->once())
             ->method('createQueue')
-            ->with('default')
+            ->with('default', true)
             ->willReturn($queue)
         ;
 
@@ -153,7 +153,7 @@ public function testShouldUseRequestedClient()
         $fooDriver
             ->expects($this->once())
             ->method('createQueue')
-            ->with('default')
+            ->with('default', true)
             ->willReturn($queue)
         ;
 
@@ -235,7 +235,7 @@ public function testShouldBindDefaultQueueIfRouteUseDifferentQueue()
         $driver
             ->expects($this->once())
             ->method('createQueue')
-            ->with('default')
+            ->with('default', true)
             ->willReturn($queue)
         ;
 
@@ -264,13 +264,13 @@ public function testShouldBindCustomExecuteConsumptionAndUseCustomClientDestinat
         $driver
             ->expects($this->at(3))
             ->method('createQueue')
-            ->with('default')
+            ->with('default', true)
             ->willReturn($defaultQueue)
         ;
         $driver
             ->expects($this->at(4))
             ->method('createQueue')
-            ->with('custom')
+            ->with('custom', true)
             ->willReturn($customQueue)
         ;
 
@@ -307,6 +307,7 @@ public function testShouldBindUserProvidedQueues()
 
         $routeCollection = new RouteCollection([
             new Route('topic', Route::TOPIC, 'processor', ['queue' => 'custom']),
+            new Route('topic', Route::TOPIC, 'processor', ['queue' => 'non-default-queue']),
         ]);
 
         $processor = $this->createDelegateProcessorMock();
@@ -315,7 +316,7 @@ public function testShouldBindUserProvidedQueues()
         $driver
             ->expects($this->once())
             ->method('createQueue')
-            ->with('non-default-queue')
+            ->with('non-default-queue', true)
             ->willReturn($queue)
         ;
 
@@ -343,6 +344,48 @@ public function testShouldBindUserProvidedQueues()
         ]);
     }
 
+    public function testShouldBindNotPrefixedQueue()
+    {
+        $queue = new NullQueue('');
+
+        $routeCollection = new RouteCollection([
+            new Route('topic', Route::TOPIC, 'processor', ['queue' => 'non-prefixed-queue', 'prefix_queue' => false]),
+        ]);
+
+        $processor = $this->createDelegateProcessorMock();
+
+        $driver = $this->createDriverStub($routeCollection);
+        $driver
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('non-prefixed-queue', false)
+            ->willReturn($queue)
+        ;
+
+        $consumer = $this->createQueueConsumerMock();
+        $consumer
+            ->expects($this->once())
+            ->method('bind')
+            ->with($this->identicalTo($queue), $this->identicalTo($processor))
+        ;
+        $consumer
+            ->expects($this->once())
+            ->method('consume')
+            ->with($this->isInstanceOf(ChainExtension::class))
+        ;
+
+        $command = new ConsumeCommand(new Container([
+            'enqueue.client.default.queue_consumer' => $consumer,
+            'enqueue.client.default.driver' => $driver,
+            'enqueue.client.default.delegate_processor' => $processor,
+        ]));
+
+        $tester = new CommandTester($command);
+        $tester->execute([
+            'client-queue-names' => ['non-prefixed-queue'],
+        ]);
+    }
+
     public function testShouldBindQueuesOnlyOnce()
     {
         $defaultQueue = new NullQueue('');
@@ -360,12 +403,12 @@ public function testShouldBindQueuesOnlyOnce()
         $driver
             ->expects($this->at(3))
             ->method('createQueue')
-            ->with('default')
+            ->with('default', true)
             ->willReturn($defaultQueue)
         ;
         $driver
             ->expects($this->at(4))
-            ->method('createQueue')
+            ->method('createQueue', true)
             ->with('custom')
             ->willReturn($customQueue)
         ;
@@ -397,6 +440,47 @@ public function testShouldBindQueuesOnlyOnce()
         $tester->execute([]);
     }
 
+    public function testShouldNotBindExternalRoutes()
+    {
+        $defaultQueue = new NullQueue('');
+
+        $routeCollection = new RouteCollection([
+            new Route('barTopic', Route::TOPIC, 'processor', ['queue' => null]),
+            new Route('fooTopic', Route::TOPIC, 'processor', ['queue' => 'external_queue', 'external' => true]),
+        ]);
+
+        $processor = $this->createDelegateProcessorMock();
+
+        $driver = $this->createDriverStub($routeCollection);
+        $driver
+            ->expects($this->exactly(1))
+            ->method('createQueue')
+            ->with('default', true)
+            ->willReturn($defaultQueue)
+        ;
+
+        $consumer = $this->createQueueConsumerMock();
+        $consumer
+            ->expects($this->exactly(1))
+            ->method('bind')
+            ->with($this->identicalTo($defaultQueue), $this->identicalTo($processor))
+        ;
+        $consumer
+            ->expects($this->at(1))
+            ->method('consume')
+            ->with($this->isInstanceOf(ChainExtension::class))
+        ;
+
+        $command = new ConsumeCommand(new Container([
+            'enqueue.client.default.queue_consumer' => $consumer,
+            'enqueue.client.default.driver' => $driver,
+            'enqueue.client.default.delegate_processor' => $processor,
+        ]));
+
+        $tester = new CommandTester($command);
+        $tester->execute([]);
+    }
+
     public function testShouldSkipQueueConsumptionAndUseCustomClientDestinationName()
     {
         $queue = new NullQueue('');
@@ -423,19 +507,19 @@ public function testShouldSkipQueueConsumptionAndUseCustomClientDestinationName(
         $driver = $this->createDriverStub($routeCollection);
         $driver
             ->expects($this->at(3))
-            ->method('createQueue')
+            ->method('createQueue', true)
             ->with('default')
             ->willReturn($queue)
         ;
         $driver
             ->expects($this->at(4))
-            ->method('createQueue')
+            ->method('createQueue', true)
             ->with('fooQueue')
             ->willReturn($queue)
         ;
         $driver
             ->expects($this->at(5))
-            ->method('createQueue')
+            ->method('createQueue', true)
             ->with('ololoQueue')
             ->willReturn($queue)
         ;
diff --git a/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php b/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php
index 9e4dfde..8dcffe4 100644
--- a/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php
@@ -123,7 +123,7 @@ public function testThrowIfTwoExclusiveCommandProcessorsWorkOnSameQueue()
         $pass->process($container);
     }
 
-    public function testShouldNotThrowIfTwoExclusiveCommandProcessorsWorkOnQueueWithSameNameButOnePrefixed()
+    public function testThrowIfThereAreTwoQueuesWithSameNameAndOneNotPrefixed()
     {
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.clients', ['aName']);
@@ -132,19 +132,41 @@ public function testShouldNotThrowIfTwoExclusiveCommandProcessorsWorkOnQueueWith
                 'aFooCommand',
                 Route::COMMAND,
                 'aFooProcessor',
-                ['exclusive' => true, 'queue' => 'aQueue', 'prefix_queue' => false]
+                ['queue' => 'foo', 'prefix_queue' => false]
             ))->toArray(),
 
             (new Route(
                 'aBarCommand',
                 Route::COMMAND,
                 'aBarProcessor',
-                ['exclusive' => true, 'queue' => 'aQueue', 'prefix_queue' => true]
+                ['queue' => 'foo', 'prefix_queue' => true]
+            ))->toArray(),
+        ]);
+
+        $pass = new AnalyzeRouteCollectionPass();
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('There are prefixed and not prefixed queue with the same name "foo". This is not allowed.');
+        $pass->process($container);
+    }
+
+    public function testThrowIfDefaultQueueNotPrefixed()
+    {
+        $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['aName']);
+        $container->register('enqueue.client.aName.route_collection')->addArgument([
+            (new Route(
+                'aFooCommand',
+                Route::COMMAND,
+                'aFooProcessor',
+                ['queue' => null, 'prefix_queue' => false]
             ))->toArray(),
         ]);
 
         $pass = new AnalyzeRouteCollectionPass();
 
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('The default queue must be prefixed.');
         $pass->process($container);
     }
 }

From 5d2b86c9cfbdc2d581c441672c1b3d270e34b0cc Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Mon, 5 Nov 2018 10:04:51 +0200
Subject: [PATCH 182/337] compatibility with 0.9x

---
 Client/Producer.php | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Client/Producer.php b/Client/Producer.php
index 8fda47f..5e0f168 100644
--- a/Client/Producer.php
+++ b/Client/Producer.php
@@ -8,6 +8,12 @@
 
 class Producer implements ProducerInterface
 {
+    /**
+     * compatibility with 0.9x
+     */
+    const TOPIC_09X = 'enqueue.topic';
+    const COMMAND_09X = 'enqueue.command';
+
     /**
      * @var DriverInterface
      */
@@ -54,6 +60,7 @@ public function sendEvent($topic, $message)
         $this->prepareBody($message);
 
         $message->setProperty(Config::PARAMETER_TOPIC_NAME, $topic);
+        $message->setProperty(self::TOPIC_09X, $topic);
 
         if (!$message->getMessageId()) {
             $message->setMessageId(UUID::generate());
@@ -119,6 +126,7 @@ public function sendCommand($command, $message, $needReply = false)
 
         $message->setProperty(Config::PARAMETER_TOPIC_NAME, Config::COMMAND_TOPIC);
         $message->setProperty(Config::PARAMETER_COMMAND_NAME, $command);
+        $message->setProperty(self::COMMAND_09X, $command);
         $message->setScope(Message::SCOPE_APP);
 
         $this->sendEvent(Config::COMMAND_TOPIC, $message);

From a5d4ccc85755236c25a2fbae861187f05d65c44b Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Mon, 5 Nov 2018 10:24:49 +0200
Subject: [PATCH 183/337] compatibility with 0.9x

---
 Tests/Client/ProducerTest.php | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Tests/Client/ProducerTest.php b/Tests/Client/ProducerTest.php
index c9a30fc..0716dc5 100644
--- a/Tests/Client/ProducerTest.php
+++ b/Tests/Client/ProducerTest.php
@@ -44,6 +44,7 @@ public function testShouldSendMessageToRouter()
 
         $expectedProperties = [
             'enqueue.topic_name' => 'topic',
+            'enqueue.topic' => 'topic',
         ];
 
         self::assertEquals($expectedProperties, $message->getProperties());

From 52314132f35e6a6e8068580b229b69e2b9da38fa Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Mon, 5 Nov 2018 11:06:24 +0200
Subject: [PATCH 184/337] compatibility with 0.8x

---
 Client/RouterProcessor.php | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/Client/RouterProcessor.php b/Client/RouterProcessor.php
index c4832fd..82c882e 100644
--- a/Client/RouterProcessor.php
+++ b/Client/RouterProcessor.php
@@ -9,6 +9,11 @@
 
 final class RouterProcessor implements Processor
 {
+    /**
+     * compatibility with 0.8x
+     */
+    private const COMMAND_TOPIC_08X = '__command__';
+
     /**
      * @var DriverInterface
      */
@@ -21,6 +26,17 @@ public function __construct(DriverInterface $driver)
 
     public function process(InteropMessage $message, Context $context): Result
     {
+        // compatibility with 0.8x
+        if (self::COMMAND_TOPIC_08X === $message->getProperty(Config::TOPIC)) {
+            $clientMessage = $this->driver->createClientMessage($message);
+            $clientMessage->setProperty(Config::TOPIC, null);
+
+            $this->driver->sendToProcessor($clientMessage);
+
+            return Result::ack('Legacy 0.8x message routed to processor');
+        }
+        // compatibility with 0.8x
+
         if ($message->getProperty(Config::COMMAND)) {
             return Result::reject(sprintf(
                 'Unexpected command "%s" got. Command must not go to the router.',

From 8d4d7ce9cc79a8047d7c773b1d90b17ef536264a Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Mon, 5 Nov 2018 12:42:54 +0200
Subject: [PATCH 185/337] fix amqp ssl certs

---
 Client/Producer.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Client/Producer.php b/Client/Producer.php
index 5e0f168..ea385ef 100644
--- a/Client/Producer.php
+++ b/Client/Producer.php
@@ -9,7 +9,7 @@
 class Producer implements ProducerInterface
 {
     /**
-     * compatibility with 0.9x
+     * compatibility with 0.9x.
      */
     const TOPIC_09X = 'enqueue.topic';
     const COMMAND_09X = 'enqueue.command';

From 55597f6e4130c41661f195bb74ed5b8a916ee946 Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Mon, 5 Nov 2018 14:32:03 +0200
Subject: [PATCH 186/337] compatibility with 0.8x

---
 Client/RouterProcessor.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Client/RouterProcessor.php b/Client/RouterProcessor.php
index 82c882e..c441ceb 100644
--- a/Client/RouterProcessor.php
+++ b/Client/RouterProcessor.php
@@ -10,7 +10,7 @@
 final class RouterProcessor implements Processor
 {
     /**
-     * compatibility with 0.8x
+     * compatibility with 0.8x.
      */
     private const COMMAND_TOPIC_08X = '__command__';
 

From 23971efd3d6b18c70c956a8cf06ee80c21fd953d Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Tue, 6 Nov 2018 17:42:24 +0200
Subject: [PATCH 187/337] monitoring

---
 Tests/Consumption/ChainExtensionTest.php | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Tests/Consumption/ChainExtensionTest.php b/Tests/Consumption/ChainExtensionTest.php
index dcde84d..c558424 100644
--- a/Tests/Consumption/ChainExtensionTest.php
+++ b/Tests/Consumption/ChainExtensionTest.php
@@ -169,6 +169,7 @@ public function testShouldProxyOnResultToAllInternalExtensions()
     {
         $context = new MessageResult(
             $this->createInteropContextMock(),
+            $this->createInteropConsumerMock(),
             $this->createMock(Message::class),
             'aResult',
             1,

From 616ed150aa3d9c219e041347e87b2d0dfc749556 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 6 Nov 2018 18:06:13 +0200
Subject: [PATCH 188/337] [doc][skip ci] Add sponsoring section.

---
 README.md | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/README.md b/README.md
index b8cb04c..a8db6e3 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,12 @@
+<h2 align="center">Supporting Enqueue</h2>
+
+Enqueue is an MIT-licensed open source project with its ongoing development made possible entirely by the support of community and our customers. If you'd like to join them, please consider:
+
+- [Become a sponsor](https://www.patreon.com/makasim).
+- [Become our client](http://forma-pro.com/)
+
+---
+
 # Message Queue.
 
 [![Gitter](https://badges.gitter.im/php-enqueue/Lobby.svg)](https://gitter.im/php-enqueue/Lobby)

From 095f9390360928deba9a323fd62972cdff476202 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 6 Nov 2018 18:12:08 +0200
Subject: [PATCH 189/337] [docs][skip ci] Add sponsoring to docs.

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index a8db6e3..0b7fb8a 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
 
 Enqueue is an MIT-licensed open source project with its ongoing development made possible entirely by the support of community and our customers. If you'd like to join them, please consider:
 
-- [Become a sponsor](https://www.patreon.com/makasim).
+- [Become a sponsor](https://www.patreon.com/makasim)
 - [Become our client](http://forma-pro.com/)
 
 ---

From 19f83ca5a89fc8e6383924ff9f73d2fa6e269fc0 Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Wed, 7 Nov 2018 11:23:01 +0200
Subject: [PATCH 190/337] add driver send result

---
 Client/Driver/GenericDriver.php |  9 +++++++--
 Client/DriverInterface.php      |  4 ++--
 Client/DriverSendResult.php     | 35 +++++++++++++++++++++++++++++++++
 Client/PostSend.php             | 28 ++++++++++++++++++++++++--
 Client/Producer.php             |  6 +++---
 5 files changed, 73 insertions(+), 9 deletions(-)
 create mode 100644 Client/DriverSendResult.php

diff --git a/Client/Driver/GenericDriver.php b/Client/Driver/GenericDriver.php
index 740bbaf..63a1f12 100644
--- a/Client/Driver/GenericDriver.php
+++ b/Client/Driver/GenericDriver.php
@@ -6,6 +6,7 @@
 
 use Enqueue\Client\Config;
 use Enqueue\Client\DriverInterface;
+use Enqueue\Client\DriverSendResult;
 use Enqueue\Client\Message;
 use Enqueue\Client\MessagePriority;
 use Enqueue\Client\Route;
@@ -45,7 +46,7 @@ public function __construct(
         $this->routeCollection = $routeCollection;
     }
 
-    public function sendToRouter(Message $message): void
+    public function sendToRouter(Message $message): DriverSendResult
     {
         if ($message->getProperty(Config::COMMAND)) {
             throw new \LogicException('Command must not be send to router but go directly to its processor.');
@@ -59,9 +60,11 @@ public function sendToRouter(Message $message): void
         $producer = $this->getContext()->createProducer();
 
         $this->doSendToRouter($producer, $topic, $transportMessage);
+
+        return new DriverSendResult($topic, $transportMessage);
     }
 
-    public function sendToProcessor(Message $message): void
+    public function sendToProcessor(Message $message): DriverSendResult
     {
         $topic = $message->getProperty(Config::TOPIC);
         $command = $message->getProperty(Config::COMMAND);
@@ -111,6 +114,8 @@ public function sendToProcessor(Message $message): void
         }
 
         $this->doSendToProcessor($producer, $queue, $transportMessage);
+
+        return new DriverSendResult($queue, $transportMessage);
     }
 
     public function setupBroker(LoggerInterface $logger = null): void
diff --git a/Client/DriverInterface.php b/Client/DriverInterface.php
index f8b13d0..1832e0f 100644
--- a/Client/DriverInterface.php
+++ b/Client/DriverInterface.php
@@ -15,9 +15,9 @@ public function createTransportMessage(Message $message): InteropMessage;
 
     public function createClientMessage(InteropMessage $message): Message;
 
-    public function sendToRouter(Message $message): void;
+    public function sendToRouter(Message $message): DriverSendResult;
 
-    public function sendToProcessor(Message $message): void;
+    public function sendToProcessor(Message $message): DriverSendResult;
 
     public function createQueue(string $queueName, bool $prefix = true): InteropQueue;
 
diff --git a/Client/DriverSendResult.php b/Client/DriverSendResult.php
new file mode 100644
index 0000000..d92e28d
--- /dev/null
+++ b/Client/DriverSendResult.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Enqueue\Client;
+
+use Interop\Queue\Destination;
+use Interop\Queue\Message as TransportMessage;
+
+final class DriverSendResult
+{
+    /**
+     * @var Destination
+     */
+    private $transportDestination;
+
+    /**
+     * @var TransportMessage
+     */
+    private $transportMessage;
+
+    public function __construct(Destination $transportDestination, TransportMessage $transportMessage)
+    {
+        $this->transportDestination = $transportDestination;
+        $this->transportMessage = $transportMessage;
+    }
+
+    public function getTransportDestination(): Destination
+    {
+        return $this->transportDestination;
+    }
+
+    public function getTransportMessage(): TransportMessage
+    {
+        return $this->transportMessage;
+    }
+}
diff --git a/Client/PostSend.php b/Client/PostSend.php
index 1c5ab9d..7bce741 100644
--- a/Client/PostSend.php
+++ b/Client/PostSend.php
@@ -2,6 +2,9 @@
 
 namespace Enqueue\Client;
 
+use Interop\Queue\Destination;
+use Interop\Queue\Message as TransportMessage;
+
 final class PostSend
 {
     private $message;
@@ -10,11 +13,22 @@ final class PostSend
 
     private $driver;
 
-    public function __construct(Message $message, ProducerInterface $producer, DriverInterface $driver)
-    {
+    private $transportDestination;
+
+    private $transportMessage;
+
+    public function __construct(
+        Message $message,
+        ProducerInterface $producer,
+        DriverInterface $driver,
+        Destination $transportDestination,
+        TransportMessage $transportMessage
+    ) {
         $this->message = $message;
         $this->producer = $producer;
         $this->driver = $driver;
+        $this->transportDestination = $transportDestination;
+        $this->transportMessage = $transportMessage;
     }
 
     public function getMessage(): Message
@@ -32,6 +46,16 @@ public function getDriver(): DriverInterface
         return $this->driver;
     }
 
+    public function getTransportDestination(): Destination
+    {
+        return $this->transportDestination;
+    }
+
+    public function getTransportMessage(): TransportMessage
+    {
+        return $this->transportMessage;
+    }
+
     public function isEvent(): bool
     {
         return (bool) $this->message->getProperty(Config::TOPIC);
diff --git a/Client/Producer.php b/Client/Producer.php
index 702ec38..6ea612b 100644
--- a/Client/Producer.php
+++ b/Client/Producer.php
@@ -118,13 +118,13 @@ private function doSend(Message $message): void
         $this->extension->onDriverPreSend(new DriverPreSend($message, $this, $this->driver));
 
         if (Message::SCOPE_MESSAGE_BUS == $message->getScope()) {
-            $this->driver->sendToRouter($message);
+            $result = $this->driver->sendToRouter($message);
         } elseif (Message::SCOPE_APP == $message->getScope()) {
-            $this->driver->sendToProcessor($message);
+            $result = $this->driver->sendToProcessor($message);
         } else {
             throw new \LogicException(sprintf('The message scope "%s" is not supported.', $message->getScope()));
         }
 
-        $this->extension->onPostSend(new PostSend($message, $this, $this->driver));
+        $this->extension->onPostSend(new PostSend($message, $this, $this->driver, $result->getTransportDestination(), $result->getTransportMessage()));
     }
 }

From 2d4575232b6e5d95fffbdb0334b379a1d2c9852b Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Wed, 7 Nov 2018 12:27:40 +0200
Subject: [PATCH 191/337] add driver send result

---
 Tests/Client/ChainExtensionTest.php           |  6 ++-
 .../DelayRedeliveredMessageExtensionTest.php  | 12 ++++++
 Tests/Client/PostSendTest.php                 | 38 +++++++++++++++++--
 Tests/Client/ProducerSendCommandTest.php      | 35 +++++++++++++++++
 Tests/Client/ProducerSendEventTest.php        | 36 ++++++++++++++++++
 Tests/Client/RouterProcessorTest.php          | 15 ++++++++
 6 files changed, 137 insertions(+), 5 deletions(-)

diff --git a/Tests/Client/ChainExtensionTest.php b/Tests/Client/ChainExtensionTest.php
index a48da5f..7d2d6fc 100644
--- a/Tests/Client/ChainExtensionTest.php
+++ b/Tests/Client/ChainExtensionTest.php
@@ -11,6 +11,8 @@
 use Enqueue\Client\PreSend;
 use Enqueue\Client\ProducerInterface;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\Destination;
+use Interop\Queue\Message as TransportMessage;
 use PHPUnit\Framework\TestCase;
 
 class ChainExtensionTest extends TestCase
@@ -132,7 +134,9 @@ public function testShouldProxyOnPostSentToAllInternalExtensions()
         $postSend = new PostSend(
             new Message(),
             $this->createMock(ProducerInterface::class),
-            $this->createMock(DriverInterface::class)
+            $this->createMock(DriverInterface::class),
+            $this->createMock(Destination::class),
+            $this->createMock(TransportMessage::class)
         );
 
         $fooExtension = $this->createExtension();
diff --git a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
index 8b6019e..9c8c244 100644
--- a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
@@ -4,6 +4,7 @@
 
 use Enqueue\Client\ConsumptionExtension\DelayRedeliveredMessageExtension;
 use Enqueue\Client\DriverInterface;
+use Enqueue\Client\DriverSendResult;
 use Enqueue\Client\Message;
 use Enqueue\Consumption\Context\MessageReceived;
 use Enqueue\Consumption\Result;
@@ -11,6 +12,8 @@
 use Enqueue\Null\NullQueue;
 use Interop\Queue\Consumer;
 use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Destination;
+use Interop\Queue\Message as TransportMessage;
 use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
@@ -41,6 +44,7 @@ public function testShouldSendDelayedMessageAndRejectOriginalMessage()
             ->expects(self::once())
             ->method('sendToProcessor')
             ->with(self::isInstanceOf(Message::class))
+            ->willReturn($this->createDriverSendResult())
         ;
         $driver
             ->expects(self::once())
@@ -187,4 +191,12 @@ private function createLoggerMock(): LoggerInterface
     {
         return $this->createMock(LoggerInterface::class);
     }
+
+    private function createDriverSendResult(): DriverSendResult
+    {
+        return new DriverSendResult(
+            $this->createMock(Destination::class),
+            $this->createMock(TransportMessage::class)
+        );
+    }
 }
diff --git a/Tests/Client/PostSendTest.php b/Tests/Client/PostSendTest.php
index 6586076..7694073 100644
--- a/Tests/Client/PostSendTest.php
+++ b/Tests/Client/PostSendTest.php
@@ -8,6 +8,8 @@
 use Enqueue\Client\PostSend;
 use Enqueue\Client\ProducerInterface;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\Destination;
+use Interop\Queue\Message as TransportMessage;
 use PHPUnit\Framework\TestCase;
 
 class PostSendTest extends TestCase
@@ -24,7 +26,9 @@ public function testCouldBeConstructedWithExpectedArguments()
         new PostSend(
             new Message(),
             $this->createProducerMock(),
-            $this->createDriverMock()
+            $this->createDriverMock(),
+            $this->createDestinationMock(),
+            $this->createTransportMessageMock()
         );
     }
 
@@ -33,16 +37,22 @@ public function testShouldAllowGetArgumentSetInConstructor()
         $expectedMessage = new Message();
         $expectedProducer = $this->createProducerMock();
         $expectedDriver = $this->createDriverMock();
+        $expectedDestination = $this->createDestinationMock();
+        $expectedTransportMessage = $this->createTransportMessageMock();
 
         $context = new PostSend(
             $expectedMessage,
             $expectedProducer,
-            $expectedDriver
+            $expectedDriver,
+            $expectedDestination,
+            $expectedTransportMessage
         );
 
         $this->assertSame($expectedMessage, $context->getMessage());
         $this->assertSame($expectedProducer, $context->getProducer());
         $this->assertSame($expectedDriver, $context->getDriver());
+        $this->assertSame($expectedDestination, $context->getTransportDestination());
+        $this->assertSame($expectedTransportMessage, $context->getTransportMessage());
     }
 
     public function testShouldAllowGetCommand()
@@ -53,7 +63,9 @@ public function testShouldAllowGetCommand()
         $context = new PostSend(
             $message,
             $this->createProducerMock(),
-            $this->createDriverMock()
+            $this->createDriverMock(),
+            $this->createDestinationMock(),
+            $this->createTransportMessageMock()
         );
 
         $this->assertFalse($context->isEvent());
@@ -68,7 +80,9 @@ public function testShouldAllowGetTopic()
         $context = new PostSend(
             $message,
             $this->createProducerMock(),
-            $this->createDriverMock()
+            $this->createDriverMock(),
+            $this->createDestinationMock(),
+            $this->createTransportMessageMock()
         );
 
         $this->assertTrue($context->isEvent());
@@ -90,4 +104,20 @@ private function createProducerMock(): ProducerInterface
     {
         return $this->createMock(ProducerInterface::class);
     }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|Destination
+     */
+    private function createDestinationMock(): Destination
+    {
+        return $this->createMock(Destination::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|TransportMessage
+     */
+    private function createTransportMessageMock(): TransportMessage
+    {
+        return $this->createMock(TransportMessage::class);
+    }
 }
diff --git a/Tests/Client/ProducerSendCommandTest.php b/Tests/Client/ProducerSendCommandTest.php
index 92885aa..06db964 100644
--- a/Tests/Client/ProducerSendCommandTest.php
+++ b/Tests/Client/ProducerSendCommandTest.php
@@ -5,6 +5,7 @@
 use Enqueue\Client\Config;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\DriverPreSend;
+use Enqueue\Client\DriverSendResult;
 use Enqueue\Client\ExtensionInterface;
 use Enqueue\Client\Message;
 use Enqueue\Client\MessagePriority;
@@ -15,6 +16,8 @@
 use Enqueue\Rpc\RpcFactory;
 use Enqueue\Test\ClassExtensionTrait;
 use Enqueue\Tests\Mocks\CustomPrepareBodyClientExtension;
+use Interop\Queue\Destination;
+use Interop\Queue\Message as TransportMessage;
 use PHPUnit\Framework\TestCase;
 
 class ProducerSendCommandTest extends TestCase
@@ -30,6 +33,7 @@ public function testShouldSendCommandToProcessor()
             ->expects($this->once())
             ->method('sendToProcessor')
             ->with(self::identicalTo($message))
+            ->willReturn($this->createDriverSendResult())
         ;
         $driver
             ->expects($this->never())
@@ -55,6 +59,7 @@ public function testShouldSendCommandWithReply()
             ->expects($this->once())
             ->method('sendToProcessor')
             ->with(self::identicalTo($message))
+            ->willReturn($this->createDriverSendResult())
         ;
         $driver
             ->expects($this->never())
@@ -100,6 +105,7 @@ public function testShouldSendCommandWithReplyAndCustomReplyQueueAndCorrelationI
             ->expects($this->once())
             ->method('sendToProcessor')
             ->with(self::identicalTo($message))
+            ->willReturn($this->createDriverSendResult())
         ;
         $driver
             ->expects($this->never())
@@ -140,6 +146,11 @@ public function testShouldOverwriteExpectedMessageProperties()
         $message->setScope('scopeShouldBeOverwritten');
 
         $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToProcessor')
+            ->willReturn($this->createDriverSendResult())
+        ;
 
         $producer = new Producer($driver, $this->createRpcFactoryMock());
         $producer->sendCommand('expectedCommand', $message);
@@ -161,6 +172,7 @@ public function testShouldSendCommandWithoutPriorityByDefault()
             ->expects($this->once())
             ->method('sendToProcessor')
             ->with(self::identicalTo($message))
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $producer = new Producer($driver, $this->createRpcFactoryMock());
@@ -179,6 +191,7 @@ public function testShouldSendCommandWithCustomPriority()
             ->expects($this->once())
             ->method('sendToProcessor')
             ->with(self::identicalTo($message))
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $producer = new Producer($driver, $this->createRpcFactoryMock());
@@ -196,6 +209,7 @@ public function testShouldSendCommandWithGeneratedMessageId()
             ->expects($this->once())
             ->method('sendToProcessor')
             ->with(self::identicalTo($message))
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $producer = new Producer($driver, $this->createRpcFactoryMock());
@@ -214,6 +228,7 @@ public function testShouldSendCommandWithCustomMessageId()
             ->expects($this->once())
             ->method('sendToProcessor')
             ->with(self::identicalTo($message))
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $producer = new Producer($driver, $this->createRpcFactoryMock());
@@ -231,6 +246,7 @@ public function testShouldSendCommandWithGeneratedTimestamp()
             ->expects($this->once())
             ->method('sendToProcessor')
             ->with(self::identicalTo($message))
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $producer = new Producer($driver, $this->createRpcFactoryMock());
@@ -249,6 +265,7 @@ public function testShouldSendCommandWithCustomTimestamp()
             ->expects($this->once())
             ->method('sendToProcessor')
             ->with(self::identicalTo($message))
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $producer = new Producer($driver, $this->createRpcFactoryMock());
@@ -265,6 +282,8 @@ public function testShouldSerializeMessageToJsonByDefault()
             ->method('sendToProcessor')
             ->willReturnCallback(function (Message $message) {
                 $this->assertSame('{"foo":"fooVal"}', $message->getBody());
+
+                return $this->createDriverSendResult();
             })
         ;
 
@@ -280,6 +299,8 @@ public function testShouldSerializeMessageByCustomExtension()
             ->method('sendToProcessor')
             ->willReturnCallback(function (Message $message) {
                 $this->assertSame('theCommandBodySerializedByCustomExtension', $message->getBody());
+
+                return $this->createDriverSendResult();
             })
         ;
 
@@ -301,6 +322,8 @@ public function testShouldSendCommandToApplicationRouter()
                 self::assertSame('aBody', $message->getBody());
                 self::assertNull($message->getProperty(Config::PROCESSOR));
                 self::assertSame('command', $message->getProperty(Config::COMMAND));
+
+                return $this->createDriverSendResult();
             })
         ;
 
@@ -338,6 +361,7 @@ public function testShouldCallPreSendCommandExtensionMethodWhenSendToBus()
         $driver
             ->expects($this->once())
             ->method('sendToProcessor')
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $extension = $this->createMock(ExtensionInterface::class);
@@ -375,6 +399,7 @@ public function testShouldCallPreSendCommandExtensionMethodWhenSendToApplication
         $driver
             ->expects($this->once())
             ->method('sendToProcessor')
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $extension = $this->createMock(ExtensionInterface::class);
@@ -412,6 +437,7 @@ public function testShouldCallPreDriverSendExtensionMethod()
         $driver
             ->expects($this->once())
             ->method('sendToProcessor')
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $extension = $this->createMock(ExtensionInterface::class);
@@ -443,6 +469,7 @@ public function testShouldCallPostSendExtensionMethod()
         $driver
             ->expects($this->once())
             ->method('sendToProcessor')
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $extension = $this->createMock(ExtensionInterface::class);
@@ -495,4 +522,12 @@ private function createDriverStub(): DriverInterface
 
         return $driverMock;
     }
+
+    private function createDriverSendResult(): DriverSendResult
+    {
+        return new DriverSendResult(
+            $this->createMock(Destination::class),
+            $this->createMock(TransportMessage::class)
+        );
+    }
 }
diff --git a/Tests/Client/ProducerSendEventTest.php b/Tests/Client/ProducerSendEventTest.php
index 0fdd780..1cc5cb4 100644
--- a/Tests/Client/ProducerSendEventTest.php
+++ b/Tests/Client/ProducerSendEventTest.php
@@ -5,6 +5,7 @@
 use Enqueue\Client\Config;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\DriverPreSend;
+use Enqueue\Client\DriverSendResult;
 use Enqueue\Client\ExtensionInterface;
 use Enqueue\Client\Message;
 use Enqueue\Client\MessagePriority;
@@ -13,6 +14,8 @@
 use Enqueue\Client\Producer;
 use Enqueue\Rpc\RpcFactory;
 use Enqueue\Tests\Mocks\CustomPrepareBodyClientExtension;
+use Interop\Queue\Destination;
+use Interop\Queue\Message as TransportMessage;
 use PHPUnit\Framework\TestCase;
 
 class ProducerSendEventTest extends TestCase
@@ -26,6 +29,7 @@ public function testShouldSendEventToRouter()
             ->expects($this->once())
             ->method('sendToRouter')
             ->with(self::identicalTo($message))
+            ->willReturn($this->createDriverSendResult())
         ;
         $driver
             ->expects($this->never())
@@ -48,6 +52,12 @@ public function testShouldOverwriteTopicProperty()
         $message->setProperty(Config::TOPIC, 'topicShouldBeOverwritten');
 
         $driver = $this->createDriverStub();
+        $driver
+            ->expects($this->once())
+            ->method('sendToRouter')
+            ->with(self::identicalTo($message))
+            ->willReturn($this->createDriverSendResult())
+        ;
 
         $producer = new Producer($driver, $this->createRpcFactoryMock());
         $producer->sendEvent('expectedTopic', $message);
@@ -68,6 +78,7 @@ public function testShouldSendEventWithoutPriorityByDefault()
             ->expects($this->once())
             ->method('sendToRouter')
             ->with(self::identicalTo($message))
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $producer = new Producer($driver, $this->createRpcFactoryMock());
@@ -86,6 +97,7 @@ public function testShouldSendEventWithCustomPriority()
             ->expects($this->once())
             ->method('sendToRouter')
             ->with(self::identicalTo($message))
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $producer = new Producer($driver, $this->createRpcFactoryMock());
@@ -103,6 +115,7 @@ public function testShouldSendEventWithGeneratedMessageId()
             ->expects($this->once())
             ->method('sendToRouter')
             ->with(self::identicalTo($message))
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $producer = new Producer($driver, $this->createRpcFactoryMock());
@@ -121,6 +134,7 @@ public function testShouldSendEventWithCustomMessageId()
             ->expects($this->once())
             ->method('sendToRouter')
             ->with(self::identicalTo($message))
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $producer = new Producer($driver, $this->createRpcFactoryMock());
@@ -138,6 +152,7 @@ public function testShouldSendEventWithGeneratedTimestamp()
             ->expects($this->once())
             ->method('sendToRouter')
             ->with(self::identicalTo($message))
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $producer = new Producer($driver, $this->createRpcFactoryMock());
@@ -156,6 +171,7 @@ public function testShouldSendEventWithCustomTimestamp()
             ->expects($this->once())
             ->method('sendToRouter')
             ->with(self::identicalTo($message))
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $producer = new Producer($driver, $this->createRpcFactoryMock());
@@ -172,6 +188,8 @@ public function testShouldSerializeMessageToJsonByDefault()
             ->method('sendToRouter')
             ->willReturnCallback(function (Message $message) {
                 $this->assertSame('{"foo":"fooVal"}', $message->getBody());
+
+                return $this->createDriverSendResult();
             })
         ;
 
@@ -187,6 +205,8 @@ public function testShouldSerializeMessageByCustomExtension()
             ->method('sendToRouter')
             ->willReturnCallback(function (Message $message) {
                 $this->assertSame('theEventBodySerializedByCustomExtension', $message->getBody());
+
+                return $this->createDriverSendResult();
             })
         ;
 
@@ -236,6 +256,8 @@ public function testShouldSendEventToApplicationRouter()
 
                 // null means a driver sends a message to router processor.
                 self::assertNull($message->getProperty(Config::PROCESSOR));
+
+                return $this->createDriverSendResult();
             })
         ;
 
@@ -295,6 +317,7 @@ public function testShouldCallPreSendEventExtensionMethodWhenSendToBus()
         $driver
             ->expects($this->once())
             ->method('sendToRouter')
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $extension = $this->createMock(ExtensionInterface::class);
@@ -332,6 +355,7 @@ public function testShouldCallPreSendEventExtensionMethodWhenSendToApplicationRo
         $driver
             ->expects($this->once())
             ->method('sendToProcessor')
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $extension = $this->createMock(ExtensionInterface::class);
@@ -369,6 +393,7 @@ public function testShouldCallPreDriverSendExtensionMethodWhenSendToMessageBus()
         $driver
             ->expects($this->once())
             ->method('sendToRouter')
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $extension = $this->createMock(ExtensionInterface::class);
@@ -400,6 +425,7 @@ public function testShouldCallPreDriverSendExtensionMethodWhenSendToApplicationR
         $driver
             ->expects($this->once())
             ->method('sendToProcessor')
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $extension = $this->createMock(ExtensionInterface::class);
@@ -431,6 +457,7 @@ public function testShouldCallPostSendExtensionMethodWhenSendToMessageBus()
         $driver
             ->expects($this->once())
             ->method('sendToRouter')
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $extension = $this->createMock(ExtensionInterface::class);
@@ -462,6 +489,7 @@ public function testShouldCallPostSendExtensionMethodWhenSendToApplicationRouter
         $driver
             ->expects($this->once())
             ->method('sendToProcessor')
+            ->willReturn($this->createDriverSendResult())
         ;
 
         $extension = $this->createMock(ExtensionInterface::class);
@@ -514,4 +542,12 @@ private function createDriverStub(): DriverInterface
 
         return $driverMock;
     }
+
+    private function createDriverSendResult(): DriverSendResult
+    {
+        return new DriverSendResult(
+            $this->createMock(Destination::class),
+            $this->createMock(TransportMessage::class)
+        );
+    }
 }
diff --git a/Tests/Client/RouterProcessorTest.php b/Tests/Client/RouterProcessorTest.php
index 3a4596b..d3f5039 100644
--- a/Tests/Client/RouterProcessorTest.php
+++ b/Tests/Client/RouterProcessorTest.php
@@ -4,6 +4,7 @@
 
 use Enqueue\Client\Config;
 use Enqueue\Client\DriverInterface;
+use Enqueue\Client\DriverSendResult;
 use Enqueue\Client\Message;
 use Enqueue\Client\Route;
 use Enqueue\Client\RouteCollection;
@@ -12,6 +13,8 @@
 use Enqueue\Null\NullContext;
 use Enqueue\Null\NullMessage;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\Destination;
+use Interop\Queue\Message as TransportMessage;
 use Interop\Queue\Processor;
 use PHPUnit\Framework\TestCase;
 
@@ -83,6 +86,8 @@ public function testShouldRouteOriginalMessageToAllRecipients()
             ->method('sendToProcessor')
             ->willReturnCallback(function (Message $message) use ($routedMessages) {
                 $routedMessages->append($message);
+
+                return $this->createDriverSendResult();
             })
         ;
         $driver
@@ -167,6 +172,8 @@ public function testShouldDoNotModifyOriginalMessage()
             ->method('sendToProcessor')
             ->willReturnCallback(function (Message $message) use ($routedMessages) {
                 $routedMessages->append($message);
+
+                return $this->createDriverSendResult();
             });
         $driver
             ->expects($this->atLeastOnce())
@@ -201,4 +208,12 @@ private function createDriverStub(RouteCollection $routeCollection = null): Driv
 
         return $driver;
     }
+
+    private function createDriverSendResult(): DriverSendResult
+    {
+        return new DriverSendResult(
+            $this->createMock(Destination::class),
+            $this->createMock(TransportMessage::class)
+        );
+    }
 }

From c3a6b1135483d75b93185ab2b70c56d994d75a1f Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Wed, 7 Nov 2018 14:21:11 +0200
Subject: [PATCH 192/337] enqueue client extension interface segregation

---
 Client/ChainExtension.php                     | 84 +++++++++++++++----
 Client/DriverPreSendExtensionInterface.php    |  8 ++
 Client/EmptyExtensionTrait.php                | 22 -----
 Client/Extension/PrepareBodyExtension.php     |  8 +-
 Client/ExtensionInterface.php                 |  9 +-
 Client/PostSendExtensionInterface.php         |  8 ++
 Client/PreSendCommandExtensionInterface.php   |  8 ++
 Client/PreSendEventExtensionInterface.php     |  8 ++
 Tests/Client/ChainExtensionTest.php           | 11 +--
 .../CustomPrepareBodyClientExtension.php      |  8 +-
 10 files changed, 109 insertions(+), 65 deletions(-)
 create mode 100644 Client/DriverPreSendExtensionInterface.php
 delete mode 100644 Client/EmptyExtensionTrait.php
 create mode 100644 Client/PostSendExtensionInterface.php
 create mode 100644 Client/PreSendCommandExtensionInterface.php
 create mode 100644 Client/PreSendEventExtensionInterface.php

diff --git a/Client/ChainExtension.php b/Client/ChainExtension.php
index ba0fc88..c5606ce 100644
--- a/Client/ChainExtension.php
+++ b/Client/ChainExtension.php
@@ -5,46 +5,98 @@
 final class ChainExtension implements ExtensionInterface
 {
     /**
-     * @var ExtensionInterface[]
+     * @var PreSendEventExtensionInterface[]
      */
-    private $extensions;
+    private $preSendEventExtensions;
 
     /**
-     * @param ExtensionInterface[] $extensions
+     * @var PreSendCommandExtensionInterface[]
      */
+    private $preSendCommandExtensions;
+
+    /**
+     * @var DriverPreSendExtensionInterface[]
+     */
+    private $driverPreSendExtensions;
+
+    /**
+     * @var PostSendExtensionInterface[]
+     */
+    private $postSendExtensions;
+
     public function __construct(array $extensions)
     {
-        $this->extensions = [];
-        array_walk($extensions, function (ExtensionInterface $extension) {
-            $this->extensions[] = $extension;
+        $this->preSendEventExtensions = [];
+        $this->preSendCommandExtensions = [];
+        $this->driverPreSendExtensions = [];
+        $this->postSendExtensions = [];
+
+        array_walk($extensions, function ($extension) {
+            if ($extension instanceof ExtensionInterface) {
+                $this->preSendEventExtensions[] = $extension;
+                $this->preSendCommandExtensions[] = $extension;
+                $this->driverPreSendExtensions[] = $extension;
+                $this->postSendExtensions[] = $extension;
+
+                return;
+            }
+
+            $extensionValid = false;
+            if ($extension instanceof PreSendEventExtensionInterface) {
+                $this->preSendEventExtensions[] = $extension;
+
+                $extensionValid = true;
+            }
+
+            if ($extension instanceof PreSendCommandExtensionInterface) {
+                $this->preSendCommandExtensions[] = $extension;
+
+                $extensionValid = true;
+            }
+
+            if ($extension instanceof DriverPreSendExtensionInterface) {
+                $this->driverPreSendExtensions[] = $extension;
+
+                $extensionValid = true;
+            }
+
+            if ($extension instanceof PostSendExtensionInterface) {
+                $this->postSendExtensions[] = $extension;
+
+                $extensionValid = true;
+            }
+
+            if (false == $extensionValid) {
+                throw new \LogicException('Invalid extension given');
+            }
         });
     }
 
-    public function onPreSendEvent(PreSend $event): void
+    public function onPreSendEvent(PreSend $context): void
     {
-        foreach ($this->extensions as $extension) {
-            $extension->onPreSendEvent($event);
+        foreach ($this->preSendEventExtensions as $extension) {
+            $extension->onPreSendEvent($context);
         }
     }
 
-    public function onPreSendCommand(PreSend $event): void
+    public function onPreSendCommand(PreSend $context): void
     {
-        foreach ($this->extensions as $extension) {
-            $extension->onPreSendCommand($event);
+        foreach ($this->preSendCommandExtensions as $extension) {
+            $extension->onPreSendCommand($context);
         }
     }
 
     public function onDriverPreSend(DriverPreSend $context): void
     {
-        foreach ($this->extensions as $extension) {
+        foreach ($this->driverPreSendExtensions as $extension) {
             $extension->onDriverPreSend($context);
         }
     }
 
-    public function onPostSend(PostSend $event): void
+    public function onPostSend(PostSend $context): void
     {
-        foreach ($this->extensions as $extension) {
-            $extension->onPostSend($event);
+        foreach ($this->postSendExtensions as $extension) {
+            $extension->onPostSend($context);
         }
     }
 }
diff --git a/Client/DriverPreSendExtensionInterface.php b/Client/DriverPreSendExtensionInterface.php
new file mode 100644
index 0000000..fd95c93
--- /dev/null
+++ b/Client/DriverPreSendExtensionInterface.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Enqueue\Client;
+
+interface DriverPreSendExtensionInterface
+{
+    public function onDriverPreSend(DriverPreSend $context): void;
+}
diff --git a/Client/EmptyExtensionTrait.php b/Client/EmptyExtensionTrait.php
deleted file mode 100644
index 2b4f976..0000000
--- a/Client/EmptyExtensionTrait.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-namespace Enqueue\Client;
-
-trait EmptyExtensionTrait
-{
-    public function onPreSendEvent(PreSend $context): void
-    {
-    }
-
-    public function onPreSendCommand(PreSend $context): void
-    {
-    }
-
-    public function onDriverPreSend(DriverPreSend $context): void
-    {
-    }
-
-    public function onPostSend(PostSend $context): void
-    {
-    }
-}
diff --git a/Client/Extension/PrepareBodyExtension.php b/Client/Extension/PrepareBodyExtension.php
index 47111d2..eeb0ded 100644
--- a/Client/Extension/PrepareBodyExtension.php
+++ b/Client/Extension/PrepareBodyExtension.php
@@ -2,16 +2,14 @@
 
 namespace Enqueue\Client\Extension;
 
-use Enqueue\Client\EmptyExtensionTrait;
-use Enqueue\Client\ExtensionInterface;
 use Enqueue\Client\Message;
 use Enqueue\Client\PreSend;
+use Enqueue\Client\PreSendCommandExtensionInterface;
+use Enqueue\Client\PreSendEventExtensionInterface;
 use Enqueue\Util\JSON;
 
-class PrepareBodyExtension implements ExtensionInterface
+class PrepareBodyExtension implements PreSendEventExtensionInterface, PreSendCommandExtensionInterface
 {
-    use EmptyExtensionTrait;
-
     public function onPreSendEvent(PreSend $context): void
     {
         $this->prepareBody($context->getMessage());
diff --git a/Client/ExtensionInterface.php b/Client/ExtensionInterface.php
index a0b413d..596b1b9 100644
--- a/Client/ExtensionInterface.php
+++ b/Client/ExtensionInterface.php
@@ -2,13 +2,6 @@
 
 namespace Enqueue\Client;
 
-interface ExtensionInterface
+interface ExtensionInterface extends PreSendEventExtensionInterface, PreSendCommandExtensionInterface, DriverPreSendExtensionInterface, PostSendExtensionInterface
 {
-    public function onPreSendEvent(PreSend $context): void;
-
-    public function onPreSendCommand(PreSend $context): void;
-
-    public function onDriverPreSend(DriverPreSend $context): void;
-
-    public function onPostSend(PostSend $context): void;
 }
diff --git a/Client/PostSendExtensionInterface.php b/Client/PostSendExtensionInterface.php
new file mode 100644
index 0000000..dd3ca8b
--- /dev/null
+++ b/Client/PostSendExtensionInterface.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Enqueue\Client;
+
+interface PostSendExtensionInterface
+{
+    public function onPostSend(PostSend $context): void;
+}
diff --git a/Client/PreSendCommandExtensionInterface.php b/Client/PreSendCommandExtensionInterface.php
new file mode 100644
index 0000000..b2f6c7a
--- /dev/null
+++ b/Client/PreSendCommandExtensionInterface.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Enqueue\Client;
+
+interface PreSendCommandExtensionInterface
+{
+    public function onPreSendCommand(PreSend $context): void;
+}
diff --git a/Client/PreSendEventExtensionInterface.php b/Client/PreSendEventExtensionInterface.php
new file mode 100644
index 0000000..1eaaae5
--- /dev/null
+++ b/Client/PreSendEventExtensionInterface.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Enqueue\Client;
+
+interface PreSendEventExtensionInterface
+{
+    public function onPreSendEvent(PreSend $context): void;
+}
diff --git a/Tests/Client/ChainExtensionTest.php b/Tests/Client/ChainExtensionTest.php
index 7d2d6fc..95f3144 100644
--- a/Tests/Client/ChainExtensionTest.php
+++ b/Tests/Client/ChainExtensionTest.php
@@ -29,13 +29,6 @@ public function testShouldBeFinal()
         $this->assertClassFinal(ChainExtension::class);
     }
 
-    public function testShouldInitEmptyExtensionsArrayOnConstruct()
-    {
-        $extension = new ChainExtension([]);
-
-        $this->assertAttributeSame([], 'extensions', $extension);
-    }
-
     public function testCouldBeConstructedWithExtensionsArray()
     {
         new ChainExtension([$this->createExtension(), $this->createExtension()]);
@@ -43,8 +36,8 @@ public function testCouldBeConstructedWithExtensionsArray()
 
     public function testThrowIfArrayContainsNotExtension()
     {
-        $this->expectException(\TypeError::class);
-        $this->expectExceptionMessage('Argument 1 passed to');
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Invalid extension given');
 
         new ChainExtension([$this->createExtension(), new \stdClass()]);
     }
diff --git a/Tests/Mocks/CustomPrepareBodyClientExtension.php b/Tests/Mocks/CustomPrepareBodyClientExtension.php
index 4d68fa1..dd0e1a6 100644
--- a/Tests/Mocks/CustomPrepareBodyClientExtension.php
+++ b/Tests/Mocks/CustomPrepareBodyClientExtension.php
@@ -2,14 +2,12 @@
 
 namespace Enqueue\Tests\Mocks;
 
-use Enqueue\Client\EmptyExtensionTrait;
-use Enqueue\Client\ExtensionInterface;
 use Enqueue\Client\PreSend;
+use Enqueue\Client\PreSendCommandExtensionInterface;
+use Enqueue\Client\PreSendEventExtensionInterface;
 
-class CustomPrepareBodyClientExtension implements ExtensionInterface
+class CustomPrepareBodyClientExtension implements PreSendEventExtensionInterface, PreSendCommandExtensionInterface
 {
-    use EmptyExtensionTrait;
-
     public function onPreSendCommand(PreSend $context): void
     {
         $context->getMessage()->setBody('theCommandBodySerializedByCustomExtension');

From 69b3c0002e21ba8b688045eb5a2af73b98d43dc9 Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Wed, 7 Nov 2018 15:15:35 +0200
Subject: [PATCH 193/337] enqueue client extension interface segregation

---
 Tests/Client/Extension/PrepareBodyExtensionTest.php | 6 ++++--
 Tests/Client/ProducerSendCommandTest.php            | 3 ++-
 Tests/Client/ProducerSendEventTest.php              | 3 ++-
 3 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/Tests/Client/Extension/PrepareBodyExtensionTest.php b/Tests/Client/Extension/PrepareBodyExtensionTest.php
index b1fd985..1b385c1 100644
--- a/Tests/Client/Extension/PrepareBodyExtensionTest.php
+++ b/Tests/Client/Extension/PrepareBodyExtensionTest.php
@@ -4,9 +4,10 @@
 
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Extension\PrepareBodyExtension;
-use Enqueue\Client\ExtensionInterface;
 use Enqueue\Client\Message;
 use Enqueue\Client\PreSend;
+use Enqueue\Client\PreSendCommandExtensionInterface;
+use Enqueue\Client\PreSendEventExtensionInterface;
 use Enqueue\Client\ProducerInterface;
 use Enqueue\Tests\Mocks\JsonSerializableObject;
 use PHPUnit\Framework\TestCase;
@@ -17,7 +18,8 @@ public function testShouldImplementExtensionInterface()
     {
         $rc = new \ReflectionClass(PrepareBodyExtension::class);
 
-        $this->assertTrue($rc->implementsInterface(ExtensionInterface::class));
+        $this->assertTrue($rc->implementsInterface(PreSendEventExtensionInterface::class));
+        $this->assertTrue($rc->implementsInterface(PreSendCommandExtensionInterface::class));
     }
 
     public function testCouldConstructedWithoutAnyArguments()
diff --git a/Tests/Client/ProducerSendCommandTest.php b/Tests/Client/ProducerSendCommandTest.php
index 06db964..0fecbdf 100644
--- a/Tests/Client/ProducerSendCommandTest.php
+++ b/Tests/Client/ProducerSendCommandTest.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Tests\Client;
 
+use Enqueue\Client\ChainExtension;
 use Enqueue\Client\Config;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\DriverPreSend;
@@ -304,7 +305,7 @@ public function testShouldSerializeMessageByCustomExtension()
             })
         ;
 
-        $producer = new Producer($driver, $this->createRpcFactoryMock(), new CustomPrepareBodyClientExtension());
+        $producer = new Producer($driver, $this->createRpcFactoryMock(), new ChainExtension([new CustomPrepareBodyClientExtension()]));
         $producer->sendCommand('command', ['foo' => 'fooVal']);
     }
 
diff --git a/Tests/Client/ProducerSendEventTest.php b/Tests/Client/ProducerSendEventTest.php
index 1cc5cb4..de07d55 100644
--- a/Tests/Client/ProducerSendEventTest.php
+++ b/Tests/Client/ProducerSendEventTest.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Tests\Client;
 
+use Enqueue\Client\ChainExtension;
 use Enqueue\Client\Config;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\DriverPreSend;
@@ -210,7 +211,7 @@ public function testShouldSerializeMessageByCustomExtension()
             })
         ;
 
-        $producer = new Producer($driver, $this->createRpcFactoryMock(), new CustomPrepareBodyClientExtension());
+        $producer = new Producer($driver, $this->createRpcFactoryMock(), new ChainExtension([new CustomPrepareBodyClientExtension()]));
         $producer->sendEvent('topic', ['foo' => 'fooVal']);
     }
 

From 579a45722daa13d8293e2628e432967e97369051 Mon Sep 17 00:00:00 2001
From: Thomas Prelot <tprelot@gmail.com>
Date: Wed, 7 Nov 2018 15:51:48 +0100
Subject: [PATCH 194/337] Add context services to locator

---
 .../DependencyInjection/TransportFactory.php  | 26 +++++++++++++------
 1 file changed, 18 insertions(+), 8 deletions(-)

diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index 19712ff..da360d7 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -156,6 +156,8 @@ public function buildContext(ContainerBuilder $container, array $config): void
             ->setFactory([new Reference($factoryId), 'createContext'])
         ;
 
+        $this->addServiceToLocator($container, 'context');
+
         if ('default' === $this->name) {
             $container->setAlias(Context::class, $this->format('context'));
         }
@@ -187,14 +189,8 @@ public function buildQueueConsumer(ContainerBuilder $container, array $config):
 
         $container->register($this->format('processor_registry'), ContainerProcessorRegistry::class);
 
-        $locatorId = 'enqueue.locator';
-        if ($container->hasDefinition($locatorId)) {
-            $locator = $container->getDefinition($locatorId);
-            $locator->replaceArgument(0, array_replace($locator->getArgument(0), [
-                $this->format('queue_consumer') => new Reference($this->format('queue_consumer')),
-                $this->format('processor_registry') => new Reference($this->format('processor_registry')),
-            ]));
-        }
+        $this->addServiceToLocator($container, 'queue_consumer');
+        $this->addServiceToLocator($container, 'processor_registry');
 
         if ('default' === $this->name) {
             $container->setAlias(QueueConsumerInterface::class, $this->format('queue_consumer'));
@@ -226,4 +222,18 @@ private function assertServiceExists(ContainerBuilder $container, string $servic
             throw new \InvalidArgumentException(sprintf('The service "%s" does not exist.', $serviceId));
         }
     }
+
+    private function addServiceToLocator(ContainerBuilder $container, string $serviceName): void
+    {
+        $locatorId = 'enqueue.locator';
+
+        if ($container->hasDefinition($locatorId)) {
+            $locator = $container->getDefinition($locatorId);
+
+            $map = $locator->getArgument(0);
+            $map[$this->format($serviceName)] = $this->reference($serviceName);
+
+            $locator->replaceArgument(0, $map);
+        }
+    }
 }

From e4e25dabcd21a7d6333054b8c8872a0aabb6dc1e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20de=20Guillebon?=
 <fx@gensdeconfiance.fr>
Date: Thu, 8 Nov 2018 11:52:49 +0100
Subject: [PATCH 195/337] Avoid receiveNoWait when only one subscriber

---
 Consumption/FallbackSubscriptionConsumer.php | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/Consumption/FallbackSubscriptionConsumer.php b/Consumption/FallbackSubscriptionConsumer.php
index cdd6fb1..15e2f27 100644
--- a/Consumption/FallbackSubscriptionConsumer.php
+++ b/Consumption/FallbackSubscriptionConsumer.php
@@ -25,13 +25,13 @@ public function __construct()
         $this->subscribers = [];
     }
 
-    public function consume(int $timeout = 0): void
+    public function consume(int $timeoutMs = 0): void
     {
-        if (empty($this->subscribers)) {
+        if (!$subscriberCount = \count($this->subscribers)) {
             throw new \LogicException('No subscribers');
         }
 
-        $timeout /= 1000;
+        $timeout = $timeoutMs / 1000;
         $endAt = microtime(true) + $timeout;
 
         while (true) {
@@ -41,13 +41,13 @@ public function consume(int $timeout = 0): void
              * @var callable $processor
              */
             foreach ($this->subscribers as $queueName => list($consumer, $callback)) {
-                $message = $consumer->receiveNoWait();
+                $message = 1 === $subscriberCount ? $consumer->receive($timeoutMs) : $consumer->receiveNoWait();
 
                 if ($message) {
                     if (false === call_user_func($callback, $message, $consumer)) {
                         return;
                     }
-                } else {
+                } elseif (1 !== $subscriberCount) {
                     if ($timeout && microtime(true) >= $endAt) {
                         return;
                     }

From 2bd79cb187eb637d0a3121519e9410ef98b06e68 Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Fri, 9 Nov 2018 14:31:59 +0200
Subject: [PATCH 196/337] multi client configuration

---
 .../DependencyInjection/ClientFactory.php     | 25 ++++++++++++++-----
 .../DependencyInjection/TransportFactory.php  | 12 +++++++--
 2 files changed, 29 insertions(+), 8 deletions(-)

diff --git a/Symfony/Client/DependencyInjection/ClientFactory.php b/Symfony/Client/DependencyInjection/ClientFactory.php
index f78c1ab..9873b6c 100644
--- a/Symfony/Client/DependencyInjection/ClientFactory.php
+++ b/Symfony/Client/DependencyInjection/ClientFactory.php
@@ -20,9 +20,11 @@
 use Enqueue\Consumption\ChainExtension as ConsumptionChainExtension;
 use Enqueue\Consumption\QueueConsumer;
 use Enqueue\Rpc\RpcFactory;
+use Enqueue\Symfony\Client\FlushSpoolProducerListener;
 use Enqueue\Symfony\ContainerProcessorRegistry;
 use Interop\Queue\Context;
 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
+use Symfony\Component\Config\Definition\Builder\NodeDefinition;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Reference;
@@ -48,22 +50,26 @@ public function __construct(string $name)
         $this->name = $name;
     }
 
-    public function addClientConfiguration(ArrayNodeDefinition $builder, bool $debug): void
+    public function getConfiguration(string $name, bool $debug): NodeDefinition
     {
+        $builder = new ArrayNodeDefinition($name);
+
         $builder->children()
             ->booleanNode('traceable_producer')->defaultValue($debug)->end()
             ->scalarNode('prefix')->defaultValue('enqueue')->end()
             ->scalarNode('app_name')->defaultValue('app')->end()
             ->scalarNode('router_topic')->defaultValue('default')->cannotBeEmpty()->end()
             ->scalarNode('router_queue')->defaultValue('default')->cannotBeEmpty()->end()
-            ->scalarNode('router_processor')->defaultValue($this->format('router_processor'))->end()
+            ->scalarNode('router_processor')->defaultNull()->end()
             ->scalarNode('default_processor_queue')->defaultValue('default')->cannotBeEmpty()->end()
             ->integerNode('redelivered_delay_time')->min(0)->defaultValue(0)->end()
         ->end()->end()
         ;
+
+        return $builder;
     }
 
-    public function build(ContainerBuilder $container, array $config): void
+    public function build(ContainerBuilder $container, array $config, bool $default = false): void
     {
         $container->register($this->format('context'), Context::class)
             ->setFactory([$this->reference('driver'), 'getContext'])
@@ -81,7 +87,7 @@ public function build(ContainerBuilder $container, array $config): void
                 $config['router_topic'],
                 $config['router_queue'],
                 $config['default_processor_queue'],
-                $config['router_processor'],
+                isset($config['router_processor']) ? $config['router_processor'] : $this->format('router_processor'),
                 // @todo should be driver options.
                 $config['transport'],
             ]);
@@ -180,9 +186,8 @@ public function build(ContainerBuilder $container, array $config): void
             ]));
         }
 
-        if ('default' === $this->name) {
+        if ($default) {
             $container->setAlias(ProducerInterface::class, $this->format('producer'));
-
             $container->setAlias(SpoolProducer::class, $this->format('spool_producer'));
         }
     }
@@ -203,6 +208,14 @@ public function createDriver(ContainerBuilder $container, array $config): string
         return $driverId;
     }
 
+    public function createFlushSpoolProducerListener(ContainerBuilder $container): void
+    {
+        $container->register($this->format('flush_spool_producer_listener'), FlushSpoolProducerListener::class)
+            ->addArgument($this->reference('spool_producer'))
+            ->addTag('kernel.event_subscriber')
+        ;
+    }
+
     public function getName(): string
     {
         return $this->name;
diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index da360d7..70b2a84 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -15,6 +15,7 @@
 use Interop\Queue\ConnectionFactory;
 use Interop\Queue\Context;
 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
+use Symfony\Component\Config\Definition\Builder\NodeDefinition;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Reference;
@@ -40,11 +41,12 @@ public function __construct(string $name)
         $this->name = $name;
     }
 
-    public function addTransportConfiguration(ArrayNodeDefinition $builder): void
+    public function getConfiguration(string $name): NodeDefinition
     {
         $knownSchemes = array_keys(Resources::getKnownSchemes());
         $availableSchemes = array_keys(Resources::getAvailableSchemes());
 
+        $builder = new ArrayNodeDefinition($name);
         $builder
             ->info('The transport option could accept a string DSN, an array with DSN key, or null. It accept extra options. To find out what option you can set, look at connection factory constructor docblock.')
             ->beforeNormalization()
@@ -94,10 +96,14 @@ public function addTransportConfiguration(ArrayNodeDefinition $builder): void
             ->end()
         ->end()
         ;
+
+        return $builder;
     }
 
-    public function addQueueConsumerConfiguration(ArrayNodeDefinition $builder): void
+    public function getQueueConsumerConfiguration(string $name): ArrayNodeDefinition
     {
+        $builder = new ArrayNodeDefinition($name);
+
         $builder
             ->addDefaultsIfNotSet()->children()
                 ->integerNode('receive_timeout')
@@ -106,6 +112,8 @@ public function addQueueConsumerConfiguration(ArrayNodeDefinition $builder): voi
                     ->info('the time in milliseconds queue consumer waits for a message (100 ms by default)')
                 ->end()
         ;
+
+        return $builder;
     }
 
     public function getName(): string

From 602a5d16fcebb1bd1d08cf1059262a1ccf98ea11 Mon Sep 17 00:00:00 2001
From: Oskar Stark <oskarstark@googlemail.com>
Date: Mon, 12 Nov 2018 12:24:34 +0100
Subject: [PATCH 197/337] Fixed headline

---
 Symfony/Client/Meta/QueuesCommand.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Symfony/Client/Meta/QueuesCommand.php b/Symfony/Client/Meta/QueuesCommand.php
index 02d263d..a1f9f54 100644
--- a/Symfony/Client/Meta/QueuesCommand.php
+++ b/Symfony/Client/Meta/QueuesCommand.php
@@ -47,7 +47,7 @@ protected function configure()
     protected function execute(InputInterface $input, OutputInterface $output)
     {
         $table = new Table($output);
-        $table->setHeaders(['Client Name', 'Transport Name', 'processors']);
+        $table->setHeaders(['Client Name', 'Transport Name', 'Processors']);
 
         $count = 0;
         $firstRow = true;

From ba33593f712db7d128a9f160be89870062986b58 Mon Sep 17 00:00:00 2001
From: Oskar Stark <oskarstark@googlemail.com>
Date: Mon, 12 Nov 2018 12:25:29 +0100
Subject: [PATCH 198/337] fixed headline

---
 Symfony/Client/Meta/TopicsCommand.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Symfony/Client/Meta/TopicsCommand.php b/Symfony/Client/Meta/TopicsCommand.php
index 8314fb9..51019a7 100644
--- a/Symfony/Client/Meta/TopicsCommand.php
+++ b/Symfony/Client/Meta/TopicsCommand.php
@@ -47,7 +47,7 @@ protected function configure()
     protected function execute(InputInterface $input, OutputInterface $output)
     {
         $table = new Table($output);
-        $table->setHeaders(['Topic', 'Description', 'processors']);
+        $table->setHeaders(['Topic', 'Description', 'Processors']);
 
         $count = 0;
         $firstRow = true;

From c898e35874e35da2ead16c463e92ae1be2fad001 Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Mon, 12 Nov 2018 13:50:57 +0200
Subject: [PATCH 199/337] multi client configuration

---
 Symfony/DiUtils.php | 57 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)
 create mode 100644 Symfony/DiUtils.php

diff --git a/Symfony/DiUtils.php b/Symfony/DiUtils.php
new file mode 100644
index 0000000..6781043
--- /dev/null
+++ b/Symfony/DiUtils.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Enqueue\Symfony;
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\Reference;
+
+class DiUtils
+{
+    /**
+     * @var string
+     */
+    private $moduleName;
+
+    /**
+     * @var string
+     */
+    private $configName;
+
+    public function __construct(string $moduleName, string $configName)
+    {
+        $this->moduleName = $moduleName;
+        $this->configName = $configName;
+    }
+
+    public static function create(string $moduleName, string $configName): self
+    {
+        return new static($moduleName, $configName);
+    }
+
+    public function getModuleName(): string
+    {
+        return $this->moduleName;
+    }
+
+    public function getConfigName(): string
+    {
+        return $this->configName;
+    }
+
+    public function reference(string $serviceName, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE): Reference
+    {
+        return new Reference($this->format($serviceName), $invalidBehavior);
+    }
+
+    public function parameter(string $serviceName): string
+    {
+        $fullName = $this->format($serviceName);
+
+        return "%$fullName%";
+    }
+
+    public function format(string $serviceName): string
+    {
+        return sprintf('enqueue.%s.%s.%s', $this->moduleName, $this->configName, $serviceName);
+    }
+}

From 4fb5aa1e4b36bb3196a7975fab3b3a0552e4b02f Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Tue, 13 Nov 2018 10:56:13 +0200
Subject: [PATCH 200/337] multi client configuration

---
 Symfony/Client/DependencyInjection/ClientFactory.php | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/Symfony/Client/DependencyInjection/ClientFactory.php b/Symfony/Client/DependencyInjection/ClientFactory.php
index 9873b6c..afa5c9b 100644
--- a/Symfony/Client/DependencyInjection/ClientFactory.php
+++ b/Symfony/Client/DependencyInjection/ClientFactory.php
@@ -80,6 +80,11 @@ public function build(ContainerBuilder $container, array $config, bool $default
             ->addArgument($this->reference('route_collection'))
         ;
 
+        $routerProcessor = empty($config['router_processor'])
+            ? $this->format('router_processor')
+            : $config['router_processor']
+        ;
+
         $container->register($this->format('config'), Config::class)
             ->setArguments([
                 $config['prefix'],
@@ -87,12 +92,12 @@ public function build(ContainerBuilder $container, array $config, bool $default
                 $config['router_topic'],
                 $config['router_queue'],
                 $config['default_processor_queue'],
-                isset($config['router_processor']) ? $config['router_processor'] : $this->format('router_processor'),
+                $routerProcessor,
                 // @todo should be driver options.
                 $config['transport'],
             ]);
 
-        $container->setParameter($this->format('router_processor'), $config['router_processor']);
+        $container->setParameter($this->format('router_processor'), $routerProcessor);
         $container->setParameter($this->format('router_queue_name'), $config['router_queue']);
         $container->setParameter($this->format('default_queue_name'), $config['default_processor_queue']);
 

From 9a5b2350266086c48d78b30a7842d52ecf657983 Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Tue, 13 Nov 2018 12:25:16 +0200
Subject: [PATCH 201/337] multi client configuration

---
 .../DependencyInjection/ClientFactory.php     |   2 +-
 .../DependencyInjection/TransportFactory.php  |   5 +-
 .../TransportFactoryTest.php                  | 162 ++++++++++--------
 3 files changed, 96 insertions(+), 73 deletions(-)

diff --git a/Symfony/Client/DependencyInjection/ClientFactory.php b/Symfony/Client/DependencyInjection/ClientFactory.php
index afa5c9b..b50efa5 100644
--- a/Symfony/Client/DependencyInjection/ClientFactory.php
+++ b/Symfony/Client/DependencyInjection/ClientFactory.php
@@ -50,7 +50,7 @@ public function __construct(string $name)
         $this->name = $name;
     }
 
-    public function getConfiguration(string $name, bool $debug): NodeDefinition
+    public static function getConfiguration(bool $debug, string $name = 'client'): NodeDefinition
     {
         $builder = new ArrayNodeDefinition($name);
 
diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index 70b2a84..5d98d3f 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -41,7 +41,7 @@ public function __construct(string $name)
         $this->name = $name;
     }
 
-    public function getConfiguration(string $name): NodeDefinition
+    public static function getConfiguration(string $name = 'transport'): NodeDefinition
     {
         $knownSchemes = array_keys(Resources::getKnownSchemes());
         $availableSchemes = array_keys(Resources::getAvailableSchemes());
@@ -74,6 +74,7 @@ public function getConfiguration(string $name): NodeDefinition
                     throw new \LogicException(sprintf('The value must be array, null or string. Got "%s"', gettype($v)));
                 })
         ->end()
+        ->isRequired()
         ->ignoreExtraKeys(false)
         ->children()
             ->scalarNode('dsn')
@@ -100,7 +101,7 @@ public function getConfiguration(string $name): NodeDefinition
         return $builder;
     }
 
-    public function getQueueConsumerConfiguration(string $name): ArrayNodeDefinition
+    public static function getQueueConsumerConfiguration(string $name = 'consumption'): ArrayNodeDefinition
     {
         $builder = new ArrayNodeDefinition($name);
 
diff --git a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index 64afdc9..05a54cc 100644
--- a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -44,15 +44,19 @@ public function testThrowIfEmptyNameGivenOnConstruction()
 
     public function testShouldAllowAddConfigurationAsStringDsn()
     {
-        $transport = new TransportFactory('default');
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addTransportConfiguration($rootNode);
+        $rootNode->append(TransportFactory::getConfiguration());
+
         $processor = new Processor();
-        $config = $processor->process($tb->buildTree(), ['dsn://']);
+        $config = $processor->process($tb->buildTree(), [['transport' => 'dsn://']]);
 
-        $this->assertEquals(['dsn' => 'dsn://'], $config);
+        $this->assertEquals([
+            'transport' => [
+                'dsn' => 'dsn://',
+            ],
+        ], $config);
     }
 
     /**
@@ -62,191 +66,209 @@ public function testShouldAllowAddConfigurationAsStringDsn()
      */
     public function testShouldAllowAddConfigurationAsDsnWithoutSlashes()
     {
-        $transport = new TransportFactory('default');
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addTransportConfiguration($rootNode);
+        $rootNode->append(TransportFactory::getConfiguration());
+        
         $processor = new Processor();
-        $config = $processor->process($tb->buildTree(), ['dsn:']);
+        $config = $processor->process($tb->buildTree(), [['transport' => 'dsn:']]);
 
-        $this->assertEquals(['dsn' => 'dsn:'], $config);
+        $this->assertEquals([
+            'transport' => [
+                'dsn' => 'dsn:',
+            ],
+        ], $config);
     }
 
     public function testShouldSetNullTransportIfNullGiven()
     {
-        $transport = new TransportFactory('default');
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addTransportConfiguration($rootNode);
+        $rootNode->append(TransportFactory::getConfiguration());
+
         $processor = new Processor();
+        $config = $processor->process($tb->buildTree(), [['transport' => null]]);
 
-        $config = $processor->process($tb->buildTree(), [null]);
-        $this->assertEquals(['dsn' => 'null:'], $config);
+        $this->assertEquals([
+            'transport' => [
+                'dsn' => 'null:',
+            ],
+        ], $config);
     }
 
     public function testShouldSetNullTransportIfEmptyStringGiven()
     {
-        $transport = new TransportFactory('default');
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addTransportConfiguration($rootNode);
+        $rootNode->append(TransportFactory::getConfiguration());
+
         $processor = new Processor();
+        $config = $processor->process($tb->buildTree(), [['transport' => '']]);
 
-        $config = $processor->process($tb->buildTree(), ['']);
-        $this->assertEquals(['dsn' => 'null:'], $config);
+        $this->assertEquals([
+            'transport' => [
+                'dsn' => 'null:',
+            ],
+        ], $config);
     }
 
     public function testShouldSetNullTransportIfEmptyArrayGiven()
     {
-        $transport = new TransportFactory('default');
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addTransportConfiguration($rootNode);
+        $rootNode->append(TransportFactory::getConfiguration());
+
         $processor = new Processor();
+        $config = $processor->process($tb->buildTree(), [['transport' => []]]);
 
-        $config = $processor->process($tb->buildTree(), [[]]);
-        $this->assertEquals(['dsn' => 'null:'], $config);
+        $this->assertEquals([
+            'transport' => [
+                'dsn' => 'null:',
+            ],
+        ], $config);
     }
 
     public function testThrowIfEmptyDsnGiven()
     {
-        $transport = new TransportFactory('default');
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addTransportConfiguration($rootNode);
-        $processor = new Processor();
+        $rootNode->append(TransportFactory::getConfiguration());
 
+        $processor = new Processor();
         $this->expectException(InvalidConfigurationException::class);
-        $this->expectExceptionMessage('The path "foo.dsn" cannot contain an empty value, but got "".');
-        $processor->process($tb->buildTree(), [['dsn' => '']]);
+        $this->expectExceptionMessage('The path "foo.transport.dsn" cannot contain an empty value, but got "".');
+        $processor->process($tb->buildTree(), [['transport' => ['dsn' => '']]]);
     }
 
     public function testThrowIfFactoryClassAndFactoryServiceSetAtTheSameTime()
     {
-        $transport = new TransportFactory('default');
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addTransportConfiguration($rootNode);
+        $rootNode->append(TransportFactory::getConfiguration());
+
         $processor = new Processor();
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('Both options factory_class and factory_service are set. Please choose one.');
         $processor->process($tb->buildTree(), [[
-            'dsn' => 'foo:',
-            'factory_class' => 'aFactoryClass',
-            'factory_service' => 'aFactoryService',
-        ]]);
+            'transport' => [
+                'dsn' => 'foo:',
+                'factory_class' => 'aFactoryClass',
+                'factory_service' => 'aFactoryService',
+        ]]]);
     }
 
     public function testThrowIfConnectionFactoryClassUsedWithFactoryClassAtTheSameTime()
     {
-        $transport = new TransportFactory('default');
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addTransportConfiguration($rootNode);
+        $rootNode->append(TransportFactory::getConfiguration());
+
         $processor = new Processor();
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('The option connection_factory_class must not be used with factory_class or factory_service at the same time. Please choose one.');
         $processor->process($tb->buildTree(), [[
-            'dsn' => 'foo:',
-            'connection_factory_class' => 'aFactoryClass',
-            'factory_service' => 'aFactoryService',
-        ]]);
+            'transport' => [
+                'dsn' => 'foo:',
+                'connection_factory_class' => 'aFactoryClass',
+                'factory_service' => 'aFactoryService',
+        ]]]);
     }
 
     public function testThrowIfConnectionFactoryClassUsedWithFactoryServiceAtTheSameTime()
     {
-        $transport = new TransportFactory('default');
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addTransportConfiguration($rootNode);
+        $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('The option connection_factory_class must not be used with factory_class or factory_service at the same time. Please choose one.');
         $processor->process($tb->buildTree(), [[
-            'dsn' => 'foo:',
-            'connection_factory_class' => 'aFactoryClass',
-            'factory_service' => 'aFactoryService',
-        ]]);
+            'transport' => [
+                'dsn' => 'foo:',
+                'connection_factory_class' => 'aFactoryClass',
+                'factory_service' => 'aFactoryService',
+        ]]]);
     }
 
     public function testShouldAllowSetFactoryClass()
     {
-        $transport = new TransportFactory('default');
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addTransportConfiguration($rootNode);
+        $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
 
         $config = $processor->process($tb->buildTree(), [[
-            'dsn' => 'foo:',
-            'factory_class' => 'theFactoryClass',
-        ]]);
+            'transport' => [
+                'dsn' => 'foo:',
+                'factory_class' => 'theFactoryClass',
+        ]]]);
 
-        $this->assertArrayHasKey('factory_class', $config);
-        $this->assertSame('theFactoryClass', $config['factory_class']);
+        $this->assertArrayHasKey('factory_class', $config['transport']);
+        $this->assertSame('theFactoryClass', $config['transport']['factory_class']);
     }
 
     public function testShouldAllowSetFactoryService()
     {
-        $transport = new TransportFactory('default');
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addTransportConfiguration($rootNode);
+        $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
 
         $config = $processor->process($tb->buildTree(), [[
-            'dsn' => 'foo:',
-            'factory_service' => 'theFactoryService',
-        ]]);
+            'transport' => [
+                'dsn' => 'foo:',
+                'factory_service' => 'theFactoryService',
+        ]]]);
 
-        $this->assertArrayHasKey('factory_service', $config);
-        $this->assertSame('theFactoryService', $config['factory_service']);
+        $this->assertArrayHasKey('factory_service', $config['transport']);
+        $this->assertSame('theFactoryService', $config['transport']['factory_service']);
     }
 
     public function testShouldAllowSetConnectionFactoryClass()
     {
-        $transport = new TransportFactory('default');
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addTransportConfiguration($rootNode);
+        $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
 
         $config = $processor->process($tb->buildTree(), [[
-            'dsn' => 'foo:',
-            'connection_factory_class' => 'theFactoryClass',
-        ]]);
+            'transport' => [
+                'dsn' => 'foo:',
+                'connection_factory_class' => 'theFactoryClass',
+        ]]]);
 
-        $this->assertArrayHasKey('connection_factory_class', $config);
-        $this->assertSame('theFactoryClass', $config['connection_factory_class']);
+        $this->assertArrayHasKey('connection_factory_class', $config['transport']);
+        $this->assertSame('theFactoryClass', $config['transport']['connection_factory_class']);
     }
 
     public function testThrowIfExtraOptionGiven()
     {
-        $transport = new TransportFactory('default');
         $tb = new TreeBuilder();
         $rootNode = $tb->root('foo');
 
-        $transport->addTransportConfiguration($rootNode);
+        $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
 
-        $config = $processor->process($tb->buildTree(), [['dsn' => 'foo:', 'extraOption' => 'aVal']]);
-        $this->assertEquals(
-            ['dsn' => 'foo:', 'extraOption' => 'aVal'],
-            $config
+        $config = $processor->process($tb->buildTree(), [['transport' => ['dsn' => 'foo:', 'extraOption' => 'aVal']]]);
+        $this->assertEquals([
+            'transport' => [
+                'dsn' => 'foo:',
+                'extraOption' => 'aVal',
+            ]], $config
         );
     }
 

From 8f3eefcdd1a034743e34d9a20d302320cd41c655 Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Tue, 13 Nov 2018 12:29:29 +0200
Subject: [PATCH 202/337] multi client configuration

---
 .../DependencyInjection/TransportFactoryTest.php | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index 05a54cc..7e021ca 100644
--- a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -70,7 +70,7 @@ public function testShouldAllowAddConfigurationAsDsnWithoutSlashes()
         $rootNode = $tb->root('foo');
 
         $rootNode->append(TransportFactory::getConfiguration());
-        
+
         $processor = new Processor();
         $config = $processor->process($tb->buildTree(), [['transport' => 'dsn:']]);
 
@@ -161,7 +161,7 @@ public function testThrowIfFactoryClassAndFactoryServiceSetAtTheSameTime()
                 'dsn' => 'foo:',
                 'factory_class' => 'aFactoryClass',
                 'factory_service' => 'aFactoryService',
-        ]]]);
+        ], ]]);
     }
 
     public function testThrowIfConnectionFactoryClassUsedWithFactoryClassAtTheSameTime()
@@ -180,7 +180,7 @@ public function testThrowIfConnectionFactoryClassUsedWithFactoryClassAtTheSameTi
                 'dsn' => 'foo:',
                 'connection_factory_class' => 'aFactoryClass',
                 'factory_service' => 'aFactoryService',
-        ]]]);
+        ], ]]);
     }
 
     public function testThrowIfConnectionFactoryClassUsedWithFactoryServiceAtTheSameTime()
@@ -198,7 +198,7 @@ public function testThrowIfConnectionFactoryClassUsedWithFactoryServiceAtTheSame
                 'dsn' => 'foo:',
                 'connection_factory_class' => 'aFactoryClass',
                 'factory_service' => 'aFactoryService',
-        ]]]);
+        ], ]]);
     }
 
     public function testShouldAllowSetFactoryClass()
@@ -213,7 +213,7 @@ public function testShouldAllowSetFactoryClass()
             'transport' => [
                 'dsn' => 'foo:',
                 'factory_class' => 'theFactoryClass',
-        ]]]);
+        ], ]]);
 
         $this->assertArrayHasKey('factory_class', $config['transport']);
         $this->assertSame('theFactoryClass', $config['transport']['factory_class']);
@@ -231,7 +231,7 @@ public function testShouldAllowSetFactoryService()
             'transport' => [
                 'dsn' => 'foo:',
                 'factory_service' => 'theFactoryService',
-        ]]]);
+        ], ]]);
 
         $this->assertArrayHasKey('factory_service', $config['transport']);
         $this->assertSame('theFactoryService', $config['transport']['factory_service']);
@@ -249,7 +249,7 @@ public function testShouldAllowSetConnectionFactoryClass()
             'transport' => [
                 'dsn' => 'foo:',
                 'connection_factory_class' => 'theFactoryClass',
-        ]]]);
+        ], ]]);
 
         $this->assertArrayHasKey('connection_factory_class', $config['transport']);
         $this->assertSame('theFactoryClass', $config['transport']['connection_factory_class']);
@@ -268,7 +268,7 @@ public function testThrowIfExtraOptionGiven()
             'transport' => [
                 'dsn' => 'foo:',
                 'extraOption' => 'aVal',
-            ]], $config
+            ], ], $config
         );
     }
 

From deb721f96c067d96aaced62623931e6ad759469e Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Wed, 14 Nov 2018 14:06:25 +0200
Subject: [PATCH 203/337] multi client configuration

---
 Client/TraceableProducer.php                  |  1 +
 .../DependencyInjection/ClientFactory.php     |  1 +
 Symfony/MissingComponentFactory.php           | 42 +++++++++++++++++++
 3 files changed, 44 insertions(+)
 create mode 100644 Symfony/MissingComponentFactory.php

diff --git a/Client/TraceableProducer.php b/Client/TraceableProducer.php
index 86ff9f8..59b0c7b 100644
--- a/Client/TraceableProducer.php
+++ b/Client/TraceableProducer.php
@@ -85,6 +85,7 @@ private function collectTrace(string $topic = null, string $command = null, $mes
             'timestamp' => null,
             'contentType' => null,
             'messageId' => null,
+            'sentAt' => (new \DateTime())->format('Y-m-d H:i:s.u'),
         ];
 
         if ($message instanceof Message) {
diff --git a/Symfony/Client/DependencyInjection/ClientFactory.php b/Symfony/Client/DependencyInjection/ClientFactory.php
index b50efa5..9a2392d 100644
--- a/Symfony/Client/DependencyInjection/ClientFactory.php
+++ b/Symfony/Client/DependencyInjection/ClientFactory.php
@@ -107,6 +107,7 @@ public function build(ContainerBuilder $container, array $config, bool $default
         ;
 
         $container->register($this->format('producer'), Producer::class)
+            ->setPublic(true)
             ->addArgument($this->reference('driver'))
             ->addArgument($this->reference('rpc_factory'))
             ->addArgument($this->reference('client_extensions'))
diff --git a/Symfony/MissingComponentFactory.php b/Symfony/MissingComponentFactory.php
new file mode 100644
index 0000000..94fcb83
--- /dev/null
+++ b/Symfony/MissingComponentFactory.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace Enqueue\Symfony;
+
+use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
+
+class MissingComponentFactory
+{
+    public static function getConfiguration(string $componentName, array $packages): ArrayNodeDefinition
+    {
+        if (1 == count($packages)) {
+            $message = sprintf(
+                'In order to use the component "%s" install a package "%s"',
+                $componentName,
+                implode('", "', $packages)
+            );
+        } else {
+            $message = sprintf(
+                'In order to use the component "%s" install one of the packages "%s"',
+                $componentName,
+                implode('", "', $packages)
+            );
+        }
+
+        $node = new ArrayNodeDefinition($componentName);
+        $node
+            ->info($message)
+            ->beforeNormalization()
+                ->always(function () {
+                    return [];
+                })
+            ->end()
+            ->validate()
+                ->always(function () use ($message) {
+                    throw new \InvalidArgumentException($message);
+                })
+            ->end()
+        ;
+
+        return $node;
+    }
+}

From 14180864e55ef8ef5167e58910af463b8823bdf1 Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Wed, 14 Nov 2018 15:16:24 +0200
Subject: [PATCH 204/337] multi client configuration

---
 Tests/Client/TraceableProducerTest.php | 24 ++++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)

diff --git a/Tests/Client/TraceableProducerTest.php b/Tests/Client/TraceableProducerTest.php
index 82364ca..f58413d 100644
--- a/Tests/Client/TraceableProducerTest.php
+++ b/Tests/Client/TraceableProducerTest.php
@@ -45,7 +45,7 @@ public function testShouldCollectInfoIfStringGivenAsEventMessage()
 
         $producer->sendEvent('aFooTopic', 'aFooBody');
 
-        $this->assertSame([
+        $this->assertArraySubset([
             [
                 'topic' => 'aFooTopic',
                 'command' => null,
@@ -60,6 +60,8 @@ public function testShouldCollectInfoIfStringGivenAsEventMessage()
                 'messageId' => null,
             ],
         ], $producer->getTraces());
+        
+        $this->assertArrayHasKey('sentAt', $producer->getTraces()[0]);
     }
 
     public function testShouldCollectInfoIfArrayGivenAsEventMessage()
@@ -68,7 +70,7 @@ public function testShouldCollectInfoIfArrayGivenAsEventMessage()
 
         $producer->sendEvent('aFooTopic', ['foo' => 'fooVal', 'bar' => 'barVal']);
 
-        $this->assertSame([
+        $this->assertArraySubset([
             [
                 'topic' => 'aFooTopic',
                 'command' => null,
@@ -83,6 +85,8 @@ public function testShouldCollectInfoIfArrayGivenAsEventMessage()
                 'messageId' => null,
             ],
         ], $producer->getTraces());
+
+        $this->assertArrayHasKey('sentAt', $producer->getTraces()[0]);
     }
 
     public function testShouldCollectInfoIfEventMessageObjectGivenAsMessage()
@@ -102,7 +106,7 @@ public function testShouldCollectInfoIfEventMessageObjectGivenAsMessage()
 
         $producer->sendEvent('aFooTopic', $message);
 
-        $this->assertSame([
+        $this->assertArraySubset([
             [
                 'topic' => 'aFooTopic',
                 'command' => null,
@@ -117,6 +121,8 @@ public function testShouldCollectInfoIfEventMessageObjectGivenAsMessage()
                 'messageId' => 'theMessageId',
             ],
         ], $producer->getTraces());
+
+        $this->assertArrayHasKey('sentAt', $producer->getTraces()[0]);
     }
 
     public function testShouldNotStoreAnythingIfInternalEventMessageProducerThrowsException()
@@ -162,7 +168,7 @@ public function testShouldCollectInfoIfStringGivenAsCommandMessage()
 
         $producer->sendCommand('aFooCommand', 'aFooBody');
 
-        $this->assertSame([
+        $this->assertArraySubset([
             [
                 'topic' => null,
                 'command' => 'aFooCommand',
@@ -177,6 +183,8 @@ public function testShouldCollectInfoIfStringGivenAsCommandMessage()
                 'messageId' => null,
             ],
         ], $producer->getTraces());
+
+        $this->assertArrayHasKey('sentAt', $producer->getTraces()[0]);
     }
 
     public function testShouldCollectInfoIfArrayGivenAsCommandMessage()
@@ -185,7 +193,7 @@ public function testShouldCollectInfoIfArrayGivenAsCommandMessage()
 
         $producer->sendCommand('aFooCommand', ['foo' => 'fooVal', 'bar' => 'barVal']);
 
-        $this->assertSame([
+        $this->assertArraySubset([
             [
                 'topic' => null,
                 'command' => 'aFooCommand',
@@ -200,6 +208,8 @@ public function testShouldCollectInfoIfArrayGivenAsCommandMessage()
                 'messageId' => null,
             ],
         ], $producer->getTraces());
+
+        $this->assertArrayHasKey('sentAt', $producer->getTraces()[0]);
     }
 
     public function testShouldCollectInfoIfCommandMessageObjectGivenAsMessage()
@@ -219,7 +229,7 @@ public function testShouldCollectInfoIfCommandMessageObjectGivenAsMessage()
 
         $producer->sendCommand('aFooCommand', $message);
 
-        $this->assertSame([
+        $this->assertArraySubset([
             [
                 'topic' => null,
                 'command' => 'aFooCommand',
@@ -234,6 +244,8 @@ public function testShouldCollectInfoIfCommandMessageObjectGivenAsMessage()
                 'messageId' => 'theMessageId',
             ],
         ], $producer->getTraces());
+
+        $this->assertArrayHasKey('sentAt', $producer->getTraces()[0]);
     }
 
     public function testShouldNotStoreAnythingIfInternalCommandMessageProducerThrowsException()

From 56453f4020c42f0272e28bc926e72013246c6225 Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Thu, 15 Nov 2018 09:49:48 +0200
Subject: [PATCH 205/337] multi client configuration

---
 Symfony/Client/ConsumeCommand.php             | 13 +++++++--
 Symfony/Client/ProduceCommand.php             | 14 +++++++---
 Symfony/Client/RoutesCommand.php              | 14 +++++++---
 Symfony/Client/SetupBrokerCommand.php         | 14 +++++++---
 .../ConfigurableConsumeCommand.php            | 13 +++++++--
 Tests/Client/TraceableProducerTest.php        |  2 +-
 Tests/Symfony/Client/ConsumeCommandTest.php   | 28 +++++++++----------
 Tests/Symfony/Client/ProduceCommandTest.php   | 22 +++++++--------
 Tests/Symfony/Client/RoutesCommandTest.php    | 28 +++++++++----------
 .../Symfony/Client/SetupBrokerCommandTest.php | 16 +++++------
 .../ConfigurableConsumeCommandTest.php        | 20 ++++++-------
 11 files changed, 108 insertions(+), 76 deletions(-)

diff --git a/Symfony/Client/ConsumeCommand.php b/Symfony/Client/ConsumeCommand.php
index cc1e46c..e6b25fc 100644
--- a/Symfony/Client/ConsumeCommand.php
+++ b/Symfony/Client/ConsumeCommand.php
@@ -34,6 +34,11 @@ class ConsumeCommand extends Command
      */
     private $container;
 
+    /**
+     * @var string
+     */
+    private $defaultClient;
+
     /**
      * @var string
      */
@@ -51,16 +56,18 @@ class ConsumeCommand extends Command
 
     public function __construct(
         ContainerInterface $container,
+        string $defaultClient,
         string $queueConsumerIdPattern = 'enqueue.client.%s.queue_consumer',
         string $driverIdPattern = 'enqueue.client.%s.driver',
         string $processorIdPatter = 'enqueue.client.%s.delegate_processor'
     ) {
-        parent::__construct(self::$defaultName);
-
         $this->container = $container;
+        $this->defaultClient = $defaultClient;
         $this->queueConsumerIdPattern = $queueConsumerIdPattern;
         $this->driverIdPattern = $driverIdPattern;
         $this->processorIdPattern = $processorIdPatter;
+
+        parent::__construct(self::$defaultName);
     }
 
     protected function configure(): void
@@ -77,7 +84,7 @@ protected function configure(): void
                 'It select an appropriate message processor based on a message headers')
             ->addArgument('client-queue-names', InputArgument::IS_ARRAY, 'Queues to consume messages from')
             ->addOption('skip', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Queues to skip consumption of messages from', [])
-            ->addOption('client', 'c', InputOption::VALUE_OPTIONAL, 'The client to consume messages from.', 'default')
+            ->addOption('client', 'c', InputOption::VALUE_OPTIONAL, 'The client to consume messages from.', $this->defaultClient)
         ;
     }
 
diff --git a/Symfony/Client/ProduceCommand.php b/Symfony/Client/ProduceCommand.php
index 302b344..61be6a8 100644
--- a/Symfony/Client/ProduceCommand.php
+++ b/Symfony/Client/ProduceCommand.php
@@ -20,17 +20,23 @@ class ProduceCommand extends Command
      */
     private $container;
 
+    /**
+     * @var string
+     */
+    private $defaultClient;
+
     /**
      * @var string
      */
     private $producerIdPattern;
 
-    public function __construct(ContainerInterface $container, string $producerIdPattern = 'enqueue.client.%s.producer')
+    public function __construct(ContainerInterface $container, string $defaultClient, string $producerIdPattern = 'enqueue.client.%s.producer')
     {
-        parent::__construct(static::$defaultName);
-
         $this->container = $container;
+        $this->defaultClient = $defaultClient;
         $this->producerIdPattern = $producerIdPattern;
+
+        parent::__construct(static::$defaultName);
     }
 
     protected function configure(): void
@@ -38,7 +44,7 @@ protected function configure(): void
         $this
             ->setDescription('Sends an event to the topic')
             ->addArgument('message', InputArgument::REQUIRED, 'A message')
-            ->addOption('client', 'c', InputOption::VALUE_OPTIONAL, 'The client to consume messages from.', 'default')
+            ->addOption('client', 'c', InputOption::VALUE_OPTIONAL, 'The client to consume messages from.', $this->defaultClient)
             ->addOption('topic', null, InputOption::VALUE_OPTIONAL, 'The topic to send a message to')
             ->addOption('command', null, InputOption::VALUE_OPTIONAL, 'The command to send a message to')
         ;
diff --git a/Symfony/Client/RoutesCommand.php b/Symfony/Client/RoutesCommand.php
index 82bcacf..04d404d 100644
--- a/Symfony/Client/RoutesCommand.php
+++ b/Symfony/Client/RoutesCommand.php
@@ -22,6 +22,11 @@ class RoutesCommand extends Command
      */
     private $container;
 
+    /**
+     * @var string
+     */
+    private $defaultClient;
+
     /**
      * @var string
      */
@@ -32,12 +37,13 @@ class RoutesCommand extends Command
      */
     private $driver;
 
-    public function __construct(ContainerInterface $container, string $driverIdPatter = 'enqueue.client.%s.driver')
+    public function __construct(ContainerInterface $container, string $defaultClient, string $driverIdPatter = 'enqueue.client.%s.driver')
     {
-        parent::__construct(static::$defaultName);
-
         $this->container = $container;
+        $this->defaultClient = $defaultClient;
         $this->driverIdPatter = $driverIdPatter;
+
+        parent::__construct(static::$defaultName);
     }
 
     protected function configure(): void
@@ -46,7 +52,7 @@ protected function configure(): void
             ->setAliases(['debug:enqueue:routes'])
             ->setDescription('A command lists all registered routes.')
             ->addOption('show-route-options', null, InputOption::VALUE_NONE, 'Adds ability to hide options.')
-            ->addOption('client', 'c', InputOption::VALUE_OPTIONAL, 'The client to consume messages from.', 'default')
+            ->addOption('client', 'c', InputOption::VALUE_OPTIONAL, 'The client to consume messages from.', $this->defaultClient)
         ;
 
         $this->driver = null;
diff --git a/Symfony/Client/SetupBrokerCommand.php b/Symfony/Client/SetupBrokerCommand.php
index 4a4f322..72215a8 100644
--- a/Symfony/Client/SetupBrokerCommand.php
+++ b/Symfony/Client/SetupBrokerCommand.php
@@ -20,17 +20,23 @@ class SetupBrokerCommand extends Command
      */
     private $container;
 
+    /**
+     * @var string
+     */
+    private $defaultClient;
+
     /**
      * @var string
      */
     private $driverIdPattern;
 
-    public function __construct(ContainerInterface $container, string $driverIdPattern = 'enqueue.client.%s.driver')
+    public function __construct(ContainerInterface $container, string $defaultClient, string $driverIdPattern = 'enqueue.client.%s.driver')
     {
-        parent::__construct(static::$defaultName);
-
         $this->container = $container;
+        $this->defaultClient = $defaultClient;
         $this->driverIdPattern = $driverIdPattern;
+
+        parent::__construct(static::$defaultName);
     }
 
     protected function configure(): void
@@ -38,7 +44,7 @@ protected function configure(): void
         $this
             ->setAliases(['enq:sb'])
             ->setDescription('Setup broker. Configure the broker, creates queues, topics and so on.')
-            ->addOption('client', 'c', InputOption::VALUE_OPTIONAL, 'The client to consume messages from.', 'default')
+            ->addOption('client', 'c', InputOption::VALUE_OPTIONAL, 'The client to consume messages from.', $this->defaultClient)
         ;
     }
 
diff --git a/Symfony/Consumption/ConfigurableConsumeCommand.php b/Symfony/Consumption/ConfigurableConsumeCommand.php
index 68ae6da..9c47134 100644
--- a/Symfony/Consumption/ConfigurableConsumeCommand.php
+++ b/Symfony/Consumption/ConfigurableConsumeCommand.php
@@ -27,6 +27,11 @@ class ConfigurableConsumeCommand extends Command
      */
     private $container;
 
+    /**
+     * @var string
+     */
+    private $defaultTransport;
+
     /**
      * @var string
      */
@@ -39,14 +44,16 @@ class ConfigurableConsumeCommand extends Command
 
     public function __construct(
         ContainerInterface $container,
+        string $defaultTransport,
         string $queueConsumerIdPattern = 'enqueue.transport.%s.queue_consumer',
         string $processorRegistryIdPattern = 'enqueue.transport.%s.processor_registry'
     ) {
-        parent::__construct(static::$defaultName);
-
         $this->container = $container;
+        $this->defaultTransport = $defaultTransport;
         $this->queueConsumerIdPattern = $queueConsumerIdPattern;
         $this->processorRegistryIdPattern = $processorRegistryIdPattern;
+
+        parent::__construct(static::$defaultName);
     }
 
     protected function configure(): void
@@ -61,7 +68,7 @@ protected function configure(): void
                 'and a message processor service')
             ->addArgument('processor', InputArgument::REQUIRED, 'A message processor.')
             ->addArgument('queues', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'A queue to consume from', [])
-            ->addOption('transport', 't', InputOption::VALUE_OPTIONAL, 'The transport to consume messages from.', 'default')
+            ->addOption('transport', 't', InputOption::VALUE_OPTIONAL, 'The transport to consume messages from.', $this->defaultTransport)
         ;
     }
 
diff --git a/Tests/Client/TraceableProducerTest.php b/Tests/Client/TraceableProducerTest.php
index f58413d..b25b7c1 100644
--- a/Tests/Client/TraceableProducerTest.php
+++ b/Tests/Client/TraceableProducerTest.php
@@ -60,7 +60,7 @@ public function testShouldCollectInfoIfStringGivenAsEventMessage()
                 'messageId' => null,
             ],
         ], $producer->getTraces());
-        
+
         $this->assertArrayHasKey('sentAt', $producer->getTraces()[0]);
     }
 
diff --git a/Tests/Symfony/Client/ConsumeCommandTest.php b/Tests/Symfony/Client/ConsumeCommandTest.php
index 3b6feb6..9a45d42 100644
--- a/Tests/Symfony/Client/ConsumeCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeCommandTest.php
@@ -34,19 +34,19 @@ public function testShouldNotBeFinal()
 
     public function testCouldBeConstructedWithRequiredAttributes()
     {
-        new ConsumeCommand($this->createMock(ContainerInterface::class));
+        new ConsumeCommand($this->createMock(ContainerInterface::class), 'default');
     }
 
     public function testShouldHaveCommandName()
     {
-        $command = new ConsumeCommand($this->createMock(ContainerInterface::class));
+        $command = new ConsumeCommand($this->createMock(ContainerInterface::class), 'default');
 
         $this->assertEquals('enqueue:consume', $command->getName());
     }
 
     public function testShouldHaveExpectedOptions()
     {
-        $command = new ConsumeCommand($this->createMock(ContainerInterface::class));
+        $command = new ConsumeCommand($this->createMock(ContainerInterface::class), 'default');
 
         $options = $command->getDefinition()->getOptions();
 
@@ -64,7 +64,7 @@ public function testShouldHaveExpectedOptions()
 
     public function testShouldHaveExpectedAttributes()
     {
-        $command = new ConsumeCommand($this->createMock(ContainerInterface::class));
+        $command = new ConsumeCommand($this->createMock(ContainerInterface::class), 'default');
 
         $arguments = $command->getDefinition()->getArguments();
 
@@ -104,7 +104,7 @@ public function testShouldBindDefaultQueueOnly()
             'enqueue.client.default.queue_consumer' => $consumer,
             'enqueue.client.default.driver' => $driver,
             'enqueue.client.default.delegate_processor' => $processor,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([]);
@@ -164,7 +164,7 @@ public function testShouldUseRequestedClient()
             'enqueue.client.foo.queue_consumer' => $fooConsumer,
             'enqueue.client.foo.driver' => $fooDriver,
             'enqueue.client.foo.delegate_processor' => $fooProcessor,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([
@@ -198,7 +198,7 @@ public function testThrowIfNotDefinedClientRequested()
             'enqueue.client.default.queue_consumer' => $consumer,
             'enqueue.client.default.driver' => $driver,
             'enqueue.client.default.delegate_processor' => $processor,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
 
@@ -243,7 +243,7 @@ public function testShouldBindDefaultQueueIfRouteUseDifferentQueue()
             'enqueue.client.default.queue_consumer' => $consumer,
             'enqueue.client.default.driver' => $driver,
             'enqueue.client.default.delegate_processor' => $processor,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([]);
@@ -295,7 +295,7 @@ public function testShouldBindCustomExecuteConsumptionAndUseCustomClientDestinat
             'enqueue.client.default.queue_consumer' => $consumer,
             'enqueue.client.default.driver' => $driver,
             'enqueue.client.default.delegate_processor' => $processor,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([]);
@@ -336,7 +336,7 @@ public function testShouldBindUserProvidedQueues()
             'enqueue.client.default.queue_consumer' => $consumer,
             'enqueue.client.default.driver' => $driver,
             'enqueue.client.default.delegate_processor' => $processor,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([
@@ -378,7 +378,7 @@ public function testShouldBindNotPrefixedQueue()
             'enqueue.client.default.queue_consumer' => $consumer,
             'enqueue.client.default.driver' => $driver,
             'enqueue.client.default.delegate_processor' => $processor,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([
@@ -434,7 +434,7 @@ public function testShouldBindQueuesOnlyOnce()
             'enqueue.client.default.queue_consumer' => $consumer,
             'enqueue.client.default.driver' => $driver,
             'enqueue.client.default.delegate_processor' => $processor,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([]);
@@ -475,7 +475,7 @@ public function testShouldNotBindExternalRoutes()
             'enqueue.client.default.queue_consumer' => $consumer,
             'enqueue.client.default.driver' => $driver,
             'enqueue.client.default.delegate_processor' => $processor,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([]);
@@ -528,7 +528,7 @@ public function testShouldSkipQueueConsumptionAndUseCustomClientDestinationName(
             'enqueue.client.default.queue_consumer' => $consumer,
             'enqueue.client.default.driver' => $driver,
             'enqueue.client.default.delegate_processor' => $processor,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([
diff --git a/Tests/Symfony/Client/ProduceCommandTest.php b/Tests/Symfony/Client/ProduceCommandTest.php
index 321b4ee..8e9b750 100644
--- a/Tests/Symfony/Client/ProduceCommandTest.php
+++ b/Tests/Symfony/Client/ProduceCommandTest.php
@@ -27,19 +27,19 @@ public function testShouldNotBeFinal()
 
     public function testCouldBeConstructedWithContainerAsFirstArgument()
     {
-        new ProduceCommand($this->createMock(ContainerInterface::class));
+        new ProduceCommand($this->createMock(ContainerInterface::class), 'default');
     }
 
     public function testShouldHaveCommandName()
     {
-        $command = new ProduceCommand($this->createMock(ContainerInterface::class));
+        $command = new ProduceCommand($this->createMock(ContainerInterface::class), 'default');
 
         $this->assertEquals('enqueue:produce', $command->getName());
     }
 
     public function testShouldHaveExpectedOptions()
     {
-        $command = new ProduceCommand($this->createMock(ContainerInterface::class));
+        $command = new ProduceCommand($this->createMock(ContainerInterface::class), 'default');
 
         $options = $command->getDefinition()->getOptions();
         $this->assertCount(3, $options);
@@ -50,7 +50,7 @@ public function testShouldHaveExpectedOptions()
 
     public function testShouldHaveExpectedAttributes()
     {
-        $command = new ProduceCommand($this->createMock(ContainerInterface::class));
+        $command = new ProduceCommand($this->createMock(ContainerInterface::class), 'default');
 
         $arguments = $command->getDefinition()->getArguments();
         $this->assertCount(1, $arguments);
@@ -72,7 +72,7 @@ public function testThrowIfNeitherTopicNorCommandOptionsAreSet()
 
         $command = new ProduceCommand(new Container([
             'enqueue.client.default.producer' => $producerMock,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
 
@@ -97,7 +97,7 @@ public function testThrowIfBothTopicAndCommandOptionsAreSet()
 
         $command = new ProduceCommand(new Container([
             'enqueue.client.default.producer' => $producerMock,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
 
@@ -125,7 +125,7 @@ public function testShouldSendEventToDefaultTransport()
 
         $command = new ProduceCommand(new Container([
             'enqueue.client.default.producer' => $producerMock,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([
@@ -149,7 +149,7 @@ public function testShouldSendCommandToDefaultTransport()
 
         $command = new ProduceCommand(new Container([
             'enqueue.client.default.producer' => $producerMock,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([
@@ -184,7 +184,7 @@ public function testShouldSendEventToFooTransport()
         $command = new ProduceCommand(new Container([
             'enqueue.client.default.producer' => $defaultProducerMock,
             'enqueue.client.foo.producer' => $fooProducerMock,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([
@@ -220,7 +220,7 @@ public function testShouldSendCommandToFooTransport()
         $command = new ProduceCommand(new Container([
             'enqueue.client.default.producer' => $defaultProducerMock,
             'enqueue.client.foo.producer' => $fooProducerMock,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([
@@ -244,7 +244,7 @@ public function testThrowIfClientNotFound()
 
         $command = new ProduceCommand(new Container([
             'enqueue.client.default.producer' => $defaultProducerMock,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
 
diff --git a/Tests/Symfony/Client/RoutesCommandTest.php b/Tests/Symfony/Client/RoutesCommandTest.php
index daab37e..0ed73ef 100644
--- a/Tests/Symfony/Client/RoutesCommandTest.php
+++ b/Tests/Symfony/Client/RoutesCommandTest.php
@@ -30,26 +30,26 @@ public function testShouldNotBeFinal()
 
     public function testCouldBeConstructedWithConfigAndRouteCollectionAsArguments()
     {
-        new RoutesCommand($this->createMock(ContainerInterface::class));
+        new RoutesCommand($this->createMock(ContainerInterface::class), 'default');
     }
 
     public function testShouldHaveCommandName()
     {
-        $command = new RoutesCommand($this->createMock(ContainerInterface::class));
+        $command = new RoutesCommand($this->createMock(ContainerInterface::class), 'default');
 
         $this->assertEquals('enqueue:routes', $command->getName());
     }
 
     public function testShouldHaveCommandAliases()
     {
-        $command = new RoutesCommand($this->createMock(ContainerInterface::class));
+        $command = new RoutesCommand($this->createMock(ContainerInterface::class), 'default');
 
         $this->assertEquals(['debug:enqueue:routes'], $command->getAliases());
     }
 
     public function testShouldHaveExpectedOptions()
     {
-        $command = new RoutesCommand($this->createMock(ContainerInterface::class));
+        $command = new RoutesCommand($this->createMock(ContainerInterface::class), 'default');
 
         $options = $command->getDefinition()->getOptions();
         $this->assertCount(2, $options);
@@ -60,7 +60,7 @@ public function testShouldHaveExpectedOptions()
 
     public function testShouldHaveExpectedAttributes()
     {
-        $command = new RoutesCommand($this->createMock(ContainerInterface::class));
+        $command = new RoutesCommand($this->createMock(ContainerInterface::class), 'default');
 
         $arguments = $command->getDefinition()->getArguments();
         $this->assertCount(0, $arguments);
@@ -72,7 +72,7 @@ public function testShouldOutputEmptyRouteCollection()
 
         $command = new RoutesCommand(new Container([
             'enqueue.client.default.driver' => $this->createDriverStub(Config::create(), $routeCollection),
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
 
@@ -109,7 +109,7 @@ public function testShouldUseFooDriver()
         $command = new RoutesCommand(new Container([
             'enqueue.client.default.driver' => $defaultDriverMock,
             'enqueue.client.foo.driver' => $fooDriverMock,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([
@@ -134,7 +134,7 @@ public function testThrowIfClientNotFound()
 
         $command = new RoutesCommand(new Container([
             'enqueue.client.default.driver' => $defaultDriverMock,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
 
@@ -154,7 +154,7 @@ public function testShouldOutputTopicRouteInfo()
 
         $command = new RoutesCommand(new Container([
             'enqueue.client.default.driver' => $this->createDriverStub(Config::create(), $routeCollection),
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
 
@@ -184,7 +184,7 @@ public function testShouldOutputCommandRouteInfo()
 
         $command = new RoutesCommand(new Container([
             'enqueue.client.default.driver' => $this->createDriverStub(Config::create(), $routeCollection),
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
 
@@ -214,7 +214,7 @@ public function testShouldCorrectlyOutputPrefixedCustomQueue()
 
         $command = new RoutesCommand(new Container([
             'enqueue.client.default.driver' => $this->createDriverStub(Config::create(), $routeCollection),
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
 
@@ -245,7 +245,7 @@ public function testShouldCorrectlyOutputNotPrefixedCustomQueue()
 
         $command = new RoutesCommand(new Container([
             'enqueue.client.default.driver' => $this->createDriverStub(Config::create(), $routeCollection),
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
 
@@ -275,7 +275,7 @@ public function testShouldCorrectlyOutputExternalRoute()
 
         $command = new RoutesCommand(new Container([
             'enqueue.client.default.driver' => $this->createDriverStub(Config::create(), $routeCollection),
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
 
@@ -305,7 +305,7 @@ public function testShouldOutputRouteOptions()
 
         $command = new RoutesCommand(new Container([
             'enqueue.client.default.driver' => $this->createDriverStub(Config::create(), $routeCollection),
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
 
diff --git a/Tests/Symfony/Client/SetupBrokerCommandTest.php b/Tests/Symfony/Client/SetupBrokerCommandTest.php
index b81daf2..4bbaf55 100644
--- a/Tests/Symfony/Client/SetupBrokerCommandTest.php
+++ b/Tests/Symfony/Client/SetupBrokerCommandTest.php
@@ -27,26 +27,26 @@ public function testShouldNotBeFinal()
 
     public function testCouldBeConstructedWithContainerAsFirstArgument()
     {
-        new SetupBrokerCommand($this->createMock(ContainerInterface::class));
+        new SetupBrokerCommand($this->createMock(ContainerInterface::class), 'default');
     }
 
     public function testShouldHaveCommandName()
     {
-        $command = new SetupBrokerCommand($this->createMock(ContainerInterface::class));
+        $command = new SetupBrokerCommand($this->createMock(ContainerInterface::class), 'default');
 
         $this->assertEquals('enqueue:setup-broker', $command->getName());
     }
 
     public function testShouldHaveCommandAliases()
     {
-        $command = new SetupBrokerCommand($this->createMock(ContainerInterface::class));
+        $command = new SetupBrokerCommand($this->createMock(ContainerInterface::class), 'default');
 
         $this->assertEquals(['enq:sb'], $command->getAliases());
     }
 
     public function testShouldHaveExpectedOptions()
     {
-        $command = new SetupBrokerCommand($this->createMock(ContainerInterface::class));
+        $command = new SetupBrokerCommand($this->createMock(ContainerInterface::class), 'default');
 
         $options = $command->getDefinition()->getOptions();
 
@@ -56,7 +56,7 @@ public function testShouldHaveExpectedOptions()
 
     public function testShouldHaveExpectedAttributes()
     {
-        $command = new SetupBrokerCommand($this->createMock(ContainerInterface::class));
+        $command = new SetupBrokerCommand($this->createMock(ContainerInterface::class), 'default');
 
         $arguments = $command->getDefinition()->getArguments();
 
@@ -73,7 +73,7 @@ public function testShouldCallDriverSetupBrokerMethod()
 
         $command = new SetupBrokerCommand(new Container([
             'enqueue.client.default.driver' => $driver,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([]);
@@ -98,7 +98,7 @@ public function testShouldCallRequestedClientDriverSetupBrokerMethod()
         $command = new SetupBrokerCommand(new Container([
             'enqueue.client.default.driver' => $defaultDriver,
             'enqueue.client.foo.driver' => $fooDriver,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([
@@ -118,7 +118,7 @@ public function testShouldThrowIfClientNotFound()
 
         $command = new SetupBrokerCommand(new Container([
             'enqueue.client.default.driver' => $defaultDriver,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
 
diff --git a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
index b402960..4586849 100644
--- a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
@@ -34,19 +34,19 @@ public function testShouldNotBeFinal()
 
     public function testCouldBeConstructedWithRequiredAttributes()
     {
-        new ConfigurableConsumeCommand($this->createMock(ContainerInterface::class));
+        new ConfigurableConsumeCommand($this->createMock(ContainerInterface::class), 'default');
     }
 
     public function testShouldHaveCommandName()
     {
-        $command = new ConfigurableConsumeCommand($this->createMock(ContainerInterface::class));
+        $command = new ConfigurableConsumeCommand($this->createMock(ContainerInterface::class), 'default');
 
         $this->assertEquals('enqueue:transport:consume', $command->getName());
     }
 
     public function testShouldHaveExpectedOptions()
     {
-        $command = new ConfigurableConsumeCommand($this->createMock(ContainerInterface::class));
+        $command = new ConfigurableConsumeCommand($this->createMock(ContainerInterface::class), 'default');
 
         $options = $command->getDefinition()->getOptions();
 
@@ -62,7 +62,7 @@ public function testShouldHaveExpectedOptions()
 
     public function testShouldHaveExpectedAttributes()
     {
-        $command = new ConfigurableConsumeCommand($this->createMock(ContainerInterface::class));
+        $command = new ConfigurableConsumeCommand($this->createMock(ContainerInterface::class), 'default');
 
         $arguments = $command->getDefinition()->getArguments();
 
@@ -88,7 +88,7 @@ public function testThrowIfNeitherQueueOptionNorProcessorImplementsQueueSubscrib
         $command = new ConfigurableConsumeCommand(new Container([
             'enqueue.transport.default.queue_consumer' => $consumer,
             'enqueue.transport.default.processor_registry' => new ArrayProcessorRegistry(['aProcessor' => $processor]),
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
 
@@ -118,7 +118,7 @@ public function testShouldExecuteConsumptionWithExplicitlySetQueue()
         $command = new ConfigurableConsumeCommand(new Container([
             'enqueue.transport.default.queue_consumer' => $consumer,
             'enqueue.transport.default.processor_registry' => new ArrayProcessorRegistry(['processor-service' => $processor]),
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([
@@ -144,7 +144,7 @@ public function testThrowIfTransportNotDefined()
         $command = new ConfigurableConsumeCommand(new Container([
             'enqueue.transport.default.queue_consumer' => $consumer,
             'enqueue.transport.default.processor_registry' => new ArrayProcessorRegistry(['processor-service' => $processor]),
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
 
@@ -181,7 +181,7 @@ public function testShouldExecuteConsumptionWithSeveralCustomQueues()
         $command = new ConfigurableConsumeCommand(new Container([
             'enqueue.transport.default.queue_consumer' => $consumer,
             'enqueue.transport.default.processor_registry' => new ArrayProcessorRegistry(['processor-service' => $processor]),
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([
@@ -223,7 +223,7 @@ public static function getSubscribedQueues()
         $command = new ConfigurableConsumeCommand(new Container([
             'enqueue.transport.default.queue_consumer' => $consumer,
             'enqueue.transport.default.processor_registry' => new ArrayProcessorRegistry(['processor-service' => $processor]),
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([
@@ -263,7 +263,7 @@ public function testShouldExecuteConsumptionWithCustomTransportExplicitlySetQueu
             'enqueue.transport.foo.processor_registry' => new ArrayProcessorRegistry(['processor-service' => $processor]),
             'enqueue.transport.bar.queue_consumer' => $barConsumer,
             'enqueue.transport.bar.processor_registry' => new ArrayProcessorRegistry(['processor-service' => $processor]),
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([

From 91ce04b1a93ac1344ecba829f4fb575e52207ffd Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Thu, 15 Nov 2018 09:59:55 +0200
Subject: [PATCH 206/337] multi client configuration

---
 Symfony/Consumption/ConsumeCommand.php          | 17 +++++++++--------
 .../Symfony/Consumption/ConsumeCommandTest.php  | 14 +++++++-------
 2 files changed, 16 insertions(+), 15 deletions(-)

diff --git a/Symfony/Consumption/ConsumeCommand.php b/Symfony/Consumption/ConsumeCommand.php
index 632a7de..e65acc3 100644
--- a/Symfony/Consumption/ConsumeCommand.php
+++ b/Symfony/Consumption/ConsumeCommand.php
@@ -27,19 +27,20 @@ class ConsumeCommand extends Command
     /**
      * @var string
      */
-    private $queueConsumerIdPattern;
+    private $defaultTransport;
 
     /**
-     * [name => QueueConsumerInterface].
-     *
-     * @param QueueConsumerInterface[]
+     * @var string
      */
-    public function __construct(ContainerInterface $container, string $queueConsumerIdPattern = 'enqueue.transport.%s.queue_consumer')
-    {
-        parent::__construct(static::$defaultName);
+    private $queueConsumerIdPattern;
 
+    public function __construct(ContainerInterface $container, string $defaultTransport, string $queueConsumerIdPattern = 'enqueue.transport.%s.queue_consumer')
+    {
         $this->container = $container;
+        $this->defaultTransport = $defaultTransport;
         $this->queueConsumerIdPattern = $queueConsumerIdPattern;
+
+        parent::__construct(static::$defaultName);
     }
 
     protected function configure(): void
@@ -49,7 +50,7 @@ protected function configure(): void
         $this->configureLoggerExtension();
 
         $this
-            ->addOption('transport', 't', InputOption::VALUE_OPTIONAL, 'The transport to consume messages from.', 'default')
+            ->addOption('transport', 't', InputOption::VALUE_OPTIONAL, 'The transport to consume messages from.', $this->defaultTransport)
             ->setDescription('A worker that consumes message from a broker. '.
                 'To use this broker you have to configure queue consumer before adding to the command')
         ;
diff --git a/Tests/Symfony/Consumption/ConsumeCommandTest.php b/Tests/Symfony/Consumption/ConsumeCommandTest.php
index a8ead2d..6396568 100644
--- a/Tests/Symfony/Consumption/ConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConsumeCommandTest.php
@@ -28,19 +28,19 @@ public function testShouldNotBeFinal()
 
     public function testCouldBeConstructedWithRequiredAttributes()
     {
-        new ConsumeCommand($this->createMock(ContainerInterface::class));
+        new ConsumeCommand($this->createMock(ContainerInterface::class), 'default');
     }
 
     public function testShouldHaveCommandName()
     {
-        $command = new ConsumeCommand($this->createMock(ContainerInterface::class));
+        $command = new ConsumeCommand($this->createMock(ContainerInterface::class), 'default');
 
         $this->assertEquals('enqueue:transport:consume', $command->getName());
     }
 
     public function testShouldHaveExpectedOptions()
     {
-        $command = new ConsumeCommand($this->createMock(ContainerInterface::class));
+        $command = new ConsumeCommand($this->createMock(ContainerInterface::class), 'default');
 
         $options = $command->getDefinition()->getOptions();
 
@@ -56,7 +56,7 @@ public function testShouldHaveExpectedOptions()
 
     public function testShouldHaveExpectedAttributes()
     {
-        $command = new ConsumeCommand($this->createMock(ContainerInterface::class));
+        $command = new ConsumeCommand($this->createMock(ContainerInterface::class), 'default');
 
         $arguments = $command->getDefinition()->getArguments();
 
@@ -74,7 +74,7 @@ public function testShouldExecuteDefaultConsumption()
 
         $command = new ConsumeCommand(new Container([
             'enqueue.transport.default.queue_consumer' => $consumer,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute([]);
@@ -98,7 +98,7 @@ public function testShouldExecuteCustomConsumption()
         $command = new ConsumeCommand(new Container([
             'enqueue.transport.default.queue_consumer' => $defaultConsumer,
             'enqueue.transport.custom.queue_consumer' => $customConsumer,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
         $tester->execute(['--transport' => 'custom']);
@@ -114,7 +114,7 @@ public function testThrowIfNotDefinedTransportRequested()
 
         $command = new ConsumeCommand(new Container([
             'enqueue.transport.default.queue_consumer' => $defaultConsumer,
-        ]));
+        ]), 'default');
 
         $tester = new CommandTester($command);
 

From ee51e672ecdbc5cb4a75c828c87a7781c486eef9 Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Thu, 15 Nov 2018 15:54:49 +0200
Subject: [PATCH 207/337] multi client configuration

---
 .../Client/DependencyInjection/ClientFactory.php | 14 ++++++++++----
 Symfony/DependencyInjection/TransportFactory.php | 16 +++++++++++-----
 2 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/Symfony/Client/DependencyInjection/ClientFactory.php b/Symfony/Client/DependencyInjection/ClientFactory.php
index 9a2392d..47d8e8e 100644
--- a/Symfony/Client/DependencyInjection/ClientFactory.php
+++ b/Symfony/Client/DependencyInjection/ClientFactory.php
@@ -41,13 +41,19 @@ final class ClientFactory
      */
     private $name;
 
-    public function __construct(string $name)
+    /**
+     * @var bool
+     */
+    private $default;
+
+    public function __construct(string $name, bool $default = false)
     {
         if (empty($name)) {
             throw new \InvalidArgumentException('The name could not be empty.');
         }
 
         $this->name = $name;
+        $this->default = $default;
     }
 
     public static function getConfiguration(bool $debug, string $name = 'client'): NodeDefinition
@@ -69,7 +75,7 @@ public static function getConfiguration(bool $debug, string $name = 'client'): N
         return $builder;
     }
 
-    public function build(ContainerBuilder $container, array $config, bool $default = false): void
+    public function build(ContainerBuilder $container, array $config): void
     {
         $container->register($this->format('context'), Context::class)
             ->setFactory([$this->reference('driver'), 'getContext'])
@@ -176,7 +182,7 @@ public function build(ContainerBuilder $container, array $config, bool $default
                 ->addTag('enqueue.consumption_extension', ['priority' => 10, 'client' => $this->name])
             ;
 
-            $container->getDefinition('enqueue.client.default.delay_redelivered_message_extension')
+            $container->getDefinition($this->format('delay_redelivered_message_extension'))
                 ->replaceArgument(1, $config['redelivered_delay_time'])
             ;
         }
@@ -192,7 +198,7 @@ public function build(ContainerBuilder $container, array $config, bool $default
             ]));
         }
 
-        if ($default) {
+        if ($this->default) {
             $container->setAlias(ProducerInterface::class, $this->format('producer'));
             $container->setAlias(SpoolProducer::class, $this->format('spool_producer'));
         }
diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index 5d98d3f..eb9454b 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -32,13 +32,19 @@ final class TransportFactory
      */
     private $name;
 
-    public function __construct(string $name)
+    /**
+     * @var bool
+     */
+    private $default;
+
+    public function __construct(string $name, bool $default = false)
     {
         if (empty($name)) {
             throw new \InvalidArgumentException('The name could not be empty.');
         }
 
         $this->name = $name;
+        $this->default = $default;
     }
 
     public static function getConfiguration(string $name = 'transport'): NodeDefinition
@@ -149,7 +155,7 @@ public function buildConnectionFactory(ContainerBuilder $container, array $confi
             ;
         }
 
-        if ('default' === $this->name) {
+        if ($this->default) {
             $container->setAlias(ConnectionFactory::class, $this->format('connection_factory'));
         }
     }
@@ -167,7 +173,7 @@ public function buildContext(ContainerBuilder $container, array $config): void
 
         $this->addServiceToLocator($container, 'context');
 
-        if ('default' === $this->name) {
+        if ($this->default) {
             $container->setAlias(Context::class, $this->format('context'));
         }
     }
@@ -201,7 +207,7 @@ public function buildQueueConsumer(ContainerBuilder $container, array $config):
         $this->addServiceToLocator($container, 'queue_consumer');
         $this->addServiceToLocator($container, 'processor_registry');
 
-        if ('default' === $this->name) {
+        if ($this->default) {
             $container->setAlias(QueueConsumerInterface::class, $this->format('queue_consumer'));
         }
     }
@@ -220,7 +226,7 @@ public function buildRpcClient(ContainerBuilder $container, array $config): void
             ->addArgument(new Reference($this->format('rpc_factory')))
         ;
 
-        if ('default' === $this->name) {
+        if ($this->default) {
             $container->setAlias(RpcClient::class, $this->format('rpc_client'));
         }
     }

From 4943c2dd30010ad053fb00d1894e546ef848d90b Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Fri, 16 Nov 2018 13:52:04 +0200
Subject: [PATCH 208/337] multi client configuration

---
 .../AnalyzeRouteCollectionPass.php            |  15 +-
 .../BuildClientExtensionsPass.php             |  20 +--
 .../BuildCommandSubscriberRoutesPass.php      |  19 +--
 .../BuildConsumptionExtensionsPass.php        |  19 +--
 .../BuildProcessorRegistryPass.php            |  20 +--
 .../BuildProcessorRoutesPass.php              |  19 +--
 .../BuildTopicSubscriberRoutesPass.php        |  19 +--
 .../DependencyInjection/ClientFactory.php     | 146 ++++++++++--------
 .../FormatClientNameTrait.php                 |  30 ----
 .../BuildConsumptionExtensionsPass.php        |  19 +--
 .../BuildProcessorRegistryPass.php            |  19 +--
 .../FormatTransportNameTrait.php              |  30 ----
 .../DependencyInjection/TransportFactory.php  |  78 ++++++----
 Symfony/DiUtils.php                           |  26 +++-
 composer.json                                 |   4 +-
 15 files changed, 203 insertions(+), 280 deletions(-)
 delete mode 100644 Symfony/Client/DependencyInjection/FormatClientNameTrait.php
 delete mode 100644 Symfony/DependencyInjection/FormatTransportNameTrait.php

diff --git a/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php b/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php
index 2613bcc..c541f41 100644
--- a/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php
+++ b/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php
@@ -3,15 +3,12 @@
 namespace Enqueue\Symfony\Client\DependencyInjection;
 
 use Enqueue\Client\RouteCollection;
+use Enqueue\Symfony\DiUtils;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 
 final class AnalyzeRouteCollectionPass implements CompilerPassInterface
 {
-    use FormatClientNameTrait;
-
-    protected $name;
-
     public function process(ContainerBuilder $container): void
     {
         if (false == $container->hasParameter('enqueue.clients')) {
@@ -21,8 +18,9 @@ public function process(ContainerBuilder $container): void
         $names = $container->getParameter('enqueue.clients');
 
         foreach ($names as $name) {
-            $this->name = $name;
-            $routeCollectionId = $this->format('route_collection');
+            $diUtils = DiUtils::create(ClientFactory::MODULE, $name);
+
+            $routeCollectionId = $diUtils->format('route_collection');
             if (false == $container->hasDefinition($routeCollectionId)) {
                 throw new \LogicException(sprintf('Service "%s" not found', $routeCollectionId));
             }
@@ -36,11 +34,6 @@ public function process(ContainerBuilder $container): void
         }
     }
 
-    protected function getName(): string
-    {
-        return $this->name;
-    }
-
     private function exclusiveCommandsCouldNotBeRunOnDefaultQueue(RouteCollection $collection): void
     {
         foreach ($collection->all() as $route) {
diff --git a/Symfony/Client/DependencyInjection/BuildClientExtensionsPass.php b/Symfony/Client/DependencyInjection/BuildClientExtensionsPass.php
index 3f97093..5ad45fd 100644
--- a/Symfony/Client/DependencyInjection/BuildClientExtensionsPass.php
+++ b/Symfony/Client/DependencyInjection/BuildClientExtensionsPass.php
@@ -2,16 +2,13 @@
 
 namespace Enqueue\Symfony\Client\DependencyInjection;
 
+use Enqueue\Symfony\DiUtils;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Reference;
 
 final class BuildClientExtensionsPass implements CompilerPassInterface
 {
-    use FormatClientNameTrait;
-
-    protected $name;
-
     public function process(ContainerBuilder $container): void
     {
         if (false == $container->hasParameter('enqueue.clients')) {
@@ -19,10 +16,12 @@ public function process(ContainerBuilder $container): void
         }
 
         $names = $container->getParameter('enqueue.clients');
+        $defaultName = $container->getParameter('enqueue.default_client');
 
         foreach ($names as $name) {
-            $this->name = $name;
-            $extensionsId = $this->format('client_extensions');
+            $diUtils = DiUtils::create(ClientFactory::MODULE, $name);
+
+            $extensionsId = $diUtils->format('client_extensions');
             if (false == $container->hasDefinition($extensionsId)) {
                 throw new \LogicException(sprintf('Service "%s" not found', $extensionsId));
             }
@@ -35,9 +34,9 @@ public function process(ContainerBuilder $container): void
             $groupByPriority = [];
             foreach ($tags as $serviceId => $tagAttributes) {
                 foreach ($tagAttributes as $tagAttribute) {
-                    $client = $tagAttribute['client'] ?? 'default';
+                    $client = $tagAttribute['client'] ?? $defaultName;
 
-                    if ($client !== $this->name && 'all' !== $client) {
+                    if ($client !== $name && 'all' !== $client) {
                         continue;
                     }
 
@@ -61,9 +60,4 @@ public function process(ContainerBuilder $container): void
             ));
         }
     }
-
-    protected function getName(): string
-    {
-        return $this->name;
-    }
 }
diff --git a/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php b/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php
index 6cc9934..cee1343 100644
--- a/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php
+++ b/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php
@@ -5,15 +5,12 @@
 use Enqueue\Client\CommandSubscriberInterface;
 use Enqueue\Client\Route;
 use Enqueue\Client\RouteCollection;
+use Enqueue\Symfony\DiUtils;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 
 final class BuildCommandSubscriberRoutesPass implements CompilerPassInterface
 {
-    use FormatClientNameTrait;
-
-    protected $name;
-
     public function process(ContainerBuilder $container): void
     {
         if (false == $container->hasParameter('enqueue.clients')) {
@@ -21,10 +18,11 @@ public function process(ContainerBuilder $container): void
         }
 
         $names = $container->getParameter('enqueue.clients');
+        $defaultName = $container->getParameter('enqueue.default_client');
 
         foreach ($names as $name) {
-            $this->name = $name;
-            $routeCollectionId = sprintf('enqueue.client.%s.route_collection', $this->name);
+            $diUtils = DiUtils::create(ClientFactory::MODULE, $name);
+            $routeCollectionId = $diUtils->format('route_collection');
             if (false == $container->hasDefinition($routeCollectionId)) {
                 throw new \LogicException(sprintf('Service "%s" not found', $routeCollectionId));
             }
@@ -47,9 +45,9 @@ public function process(ContainerBuilder $container): void
                 }
 
                 foreach ($tagAttributes as $tagAttribute) {
-                    $client = $tagAttribute['client'] ?? 'default';
+                    $client = $tagAttribute['client'] ?? $defaultName;
 
-                    if ($client !== $this->name && 'all' !== $client) {
+                    if ($client !== $name && 'all' !== $client) {
                         continue;
                     }
 
@@ -103,9 +101,4 @@ public function process(ContainerBuilder $container): void
             ));
         }
     }
-
-    protected function getName(): string
-    {
-        return $this->name;
-    }
 }
diff --git a/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php b/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php
index d05f6f0..c1f1ce9 100644
--- a/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php
+++ b/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php
@@ -2,16 +2,13 @@
 
 namespace Enqueue\Symfony\Client\DependencyInjection;
 
+use Enqueue\Symfony\DiUtils;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Reference;
 
 final class BuildConsumptionExtensionsPass implements CompilerPassInterface
 {
-    use FormatClientNameTrait;
-
-    protected $name;
-
     public function process(ContainerBuilder $container): void
     {
         if (false == $container->hasParameter('enqueue.clients')) {
@@ -19,11 +16,12 @@ public function process(ContainerBuilder $container): void
         }
 
         $names = $container->getParameter('enqueue.clients');
+        $defaultName = $container->getParameter('enqueue.default_client');
 
         foreach ($names as $name) {
-            $this->name = $name;
+            $diUtils = DiUtils::create(ClientFactory::MODULE, $name);
 
-            $extensionsId = $this->format('consumption_extensions');
+            $extensionsId = $diUtils->format('consumption_extensions');
             if (false == $container->hasDefinition($extensionsId)) {
                 throw new \LogicException(sprintf('Service "%s" not found', $extensionsId));
             }
@@ -36,9 +34,9 @@ public function process(ContainerBuilder $container): void
             $groupByPriority = [];
             foreach ($tags as $serviceId => $tagAttributes) {
                 foreach ($tagAttributes as $tagAttribute) {
-                    $client = $tagAttribute['client'] ?? 'default';
+                    $client = $tagAttribute['client'] ?? $defaultName;
 
-                    if ($client !== $this->name && 'all' !== $client) {
+                    if ($client !== $name && 'all' !== $client) {
                         continue;
                     }
 
@@ -62,9 +60,4 @@ public function process(ContainerBuilder $container): void
             ));
         }
     }
-
-    protected function getName(): string
-    {
-        return $this->name;
-    }
 }
diff --git a/Symfony/Client/DependencyInjection/BuildProcessorRegistryPass.php b/Symfony/Client/DependencyInjection/BuildProcessorRegistryPass.php
index cdf40a8..3759dd2 100644
--- a/Symfony/Client/DependencyInjection/BuildProcessorRegistryPass.php
+++ b/Symfony/Client/DependencyInjection/BuildProcessorRegistryPass.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Symfony\Client\DependencyInjection;
 
 use Enqueue\Client\RouteCollection;
+use Enqueue\Symfony\DiUtils;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -10,10 +11,6 @@
 
 final class BuildProcessorRegistryPass implements CompilerPassInterface
 {
-    use FormatClientNameTrait;
-
-    protected $name;
-
     public function process(ContainerBuilder $container): void
     {
         if (false == $container->hasParameter('enqueue.clients')) {
@@ -23,19 +20,19 @@ public function process(ContainerBuilder $container): void
         $names = $container->getParameter('enqueue.clients');
 
         foreach ($names as $name) {
-            $this->name = $name;
+            $diUtils = DiUtils::create(ClientFactory::MODULE, $name);
 
-            $processorRegistryId = $this->format('processor_registry');
+            $processorRegistryId = $diUtils->format('processor_registry');
             if (false == $container->hasDefinition($processorRegistryId)) {
                 throw new \LogicException(sprintf('Service "%s" not found', $processorRegistryId));
             }
 
-            $routeCollectionId = $this->format('route_collection');
+            $routeCollectionId = $diUtils->format('route_collection');
             if (false == $container->hasDefinition($routeCollectionId)) {
                 throw new \LogicException(sprintf('Service "%s" not found', $routeCollectionId));
             }
 
-            $routerProcessorId = $this->format('router_processor');
+            $routerProcessorId = $diUtils->format('router_processor');
             if (false == $container->hasDefinition($routerProcessorId)) {
                 throw new \LogicException(sprintf('Service "%s" not found', $routerProcessorId));
             }
@@ -51,15 +48,10 @@ public function process(ContainerBuilder $container): void
                 $map[$route->getProcessor()] = new Reference($processorServiceId);
             }
 
-            $map[$this->parameter('router_processor')] = new Reference($routerProcessorId);
+            $map[$diUtils->parameter('router_processor')] = new Reference($routerProcessorId);
 
             $registry = $container->getDefinition($processorRegistryId);
             $registry->setArgument(0, ServiceLocatorTagPass::register($container, $map, $processorRegistryId));
         }
     }
-
-    private function getName(): string
-    {
-        return $this->name;
-    }
 }
diff --git a/Symfony/Client/DependencyInjection/BuildProcessorRoutesPass.php b/Symfony/Client/DependencyInjection/BuildProcessorRoutesPass.php
index c89f374..e88cb1f 100644
--- a/Symfony/Client/DependencyInjection/BuildProcessorRoutesPass.php
+++ b/Symfony/Client/DependencyInjection/BuildProcessorRoutesPass.php
@@ -4,15 +4,12 @@
 
 use Enqueue\Client\Route;
 use Enqueue\Client\RouteCollection;
+use Enqueue\Symfony\DiUtils;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 
 final class BuildProcessorRoutesPass implements CompilerPassInterface
 {
-    use FormatClientNameTrait;
-
-    protected $name;
-
     public function process(ContainerBuilder $container): void
     {
         if (false == $container->hasParameter('enqueue.clients')) {
@@ -20,10 +17,11 @@ public function process(ContainerBuilder $container): void
         }
 
         $names = $container->getParameter('enqueue.clients');
+        $defaultName = $container->getParameter('enqueue.default_client');
 
         foreach ($names as $name) {
-            $this->name = $name;
-            $routeCollectionId = $this->format('route_collection');
+            $diUtils = DiUtils::create(ClientFactory::MODULE, $name);
+            $routeCollectionId = $diUtils->format('route_collection');
             if (false == $container->hasDefinition($routeCollectionId)) {
                 throw new \LogicException(sprintf('Service "%s" not found', $routeCollectionId));
             }
@@ -32,9 +30,9 @@ public function process(ContainerBuilder $container): void
             $routeCollection = new RouteCollection([]);
             foreach ($container->findTaggedServiceIds($tag) as $serviceId => $tagAttributes) {
                 foreach ($tagAttributes as $tagAttribute) {
-                    $client = $tagAttribute['client'] ?? 'default';
+                    $client = $tagAttribute['client'] ?? $defaultName;
 
-                    if ($client !== $this->name && 'all' !== $client) {
+                    if ($client !== $name && 'all' !== $client) {
                         continue;
                     }
 
@@ -76,9 +74,4 @@ public function process(ContainerBuilder $container): void
             ));
         }
     }
-
-    protected function getName(): string
-    {
-        return $this->name;
-    }
 }
diff --git a/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php b/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php
index f1b9ecc..3414d49 100644
--- a/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php
+++ b/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php
@@ -5,15 +5,12 @@
 use Enqueue\Client\Route;
 use Enqueue\Client\RouteCollection;
 use Enqueue\Client\TopicSubscriberInterface;
+use Enqueue\Symfony\DiUtils;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 
 final class BuildTopicSubscriberRoutesPass implements CompilerPassInterface
 {
-    use FormatClientNameTrait;
-
-    protected $name;
-
     public function process(ContainerBuilder $container): void
     {
         if (false == $container->hasParameter('enqueue.clients')) {
@@ -21,10 +18,11 @@ public function process(ContainerBuilder $container): void
         }
 
         $names = $container->getParameter('enqueue.clients');
+        $defaultName = $container->getParameter('enqueue.default_client');
 
         foreach ($names as $name) {
-            $this->name = $name;
-            $routeCollectionId = $this->format('route_collection');
+            $diUtils = DiUtils::create(ClientFactory::MODULE, $name);
+            $routeCollectionId = $diUtils->format('route_collection');
             if (false == $container->hasDefinition($routeCollectionId)) {
                 throw new \LogicException(sprintf('Service "%s" not found', $routeCollectionId));
             }
@@ -47,9 +45,9 @@ public function process(ContainerBuilder $container): void
                 }
 
                 foreach ($tagAttributes as $tagAttribute) {
-                    $client = $tagAttribute['client'] ?? 'default';
+                    $client = $tagAttribute['client'] ?? $defaultName;
 
-                    if ($client !== $this->name && 'all' !== $client) {
+                    if ($client !== $name && 'all' !== $client) {
                         continue;
                     }
 
@@ -99,9 +97,4 @@ public function process(ContainerBuilder $container): void
             ));
         }
     }
-
-    protected function getName(): string
-    {
-        return $this->name;
-    }
 }
diff --git a/Symfony/Client/DependencyInjection/ClientFactory.php b/Symfony/Client/DependencyInjection/ClientFactory.php
index 47d8e8e..0acf659 100644
--- a/Symfony/Client/DependencyInjection/ClientFactory.php
+++ b/Symfony/Client/DependencyInjection/ClientFactory.php
@@ -22,6 +22,8 @@
 use Enqueue\Rpc\RpcFactory;
 use Enqueue\Symfony\Client\FlushSpoolProducerListener;
 use Enqueue\Symfony\ContainerProcessorRegistry;
+use Enqueue\Symfony\DependencyInjection\TransportFactory;
+use Enqueue\Symfony\DiUtils;
 use Interop\Queue\Context;
 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
 use Symfony\Component\Config\Definition\Builder\NodeDefinition;
@@ -34,17 +36,17 @@
  */
 final class ClientFactory
 {
-    use FormatClientNameTrait;
+    public const MODULE = 'client';
 
     /**
-     * @var string
+     * @var bool
      */
-    private $name;
+    private $default;
 
     /**
-     * @var bool
+     * @var DiUtils
      */
-    private $default;
+    private $diUtils;
 
     public function __construct(string $name, bool $default = false)
     {
@@ -52,8 +54,8 @@ public function __construct(string $name, bool $default = false)
             throw new \InvalidArgumentException('The name could not be empty.');
         }
 
-        $this->name = $name;
         $this->default = $default;
+        $this->diUtils = DiUtils::create(self::MODULE, $name);
     }
 
     public static function getConfiguration(bool $debug, string $name = 'client'): NodeDefinition
@@ -77,21 +79,21 @@ public static function getConfiguration(bool $debug, string $name = 'client'): N
 
     public function build(ContainerBuilder $container, array $config): void
     {
-        $container->register($this->format('context'), Context::class)
-            ->setFactory([$this->reference('driver'), 'getContext'])
+        $container->register($this->diUtils->format('context'), Context::class)
+            ->setFactory([$this->diUtils->reference('driver'), 'getContext'])
         ;
 
-        $container->register($this->format('driver_factory'), DriverFactory::class)
-            ->addArgument($this->reference('config'))
-            ->addArgument($this->reference('route_collection'))
+        $container->register($this->diUtils->format('driver_factory'), DriverFactory::class)
+            ->addArgument($this->diUtils->reference('config'))
+            ->addArgument($this->diUtils->reference('route_collection'))
         ;
 
         $routerProcessor = empty($config['router_processor'])
-            ? $this->format('router_processor')
+            ? $this->diUtils->format('router_processor')
             : $config['router_processor']
         ;
 
-        $container->register($this->format('config'), Config::class)
+        $container->register($this->diUtils->format('config'), Config::class)
             ->setArguments([
                 $config['prefix'],
                 $config['app_name'],
@@ -103,86 +105,86 @@ public function build(ContainerBuilder $container, array $config): void
                 $config['transport'],
             ]);
 
-        $container->setParameter($this->format('router_processor'), $routerProcessor);
-        $container->setParameter($this->format('router_queue_name'), $config['router_queue']);
-        $container->setParameter($this->format('default_queue_name'), $config['default_processor_queue']);
+        $container->setParameter($this->diUtils->format('router_processor'), $routerProcessor);
+        $container->setParameter($this->diUtils->format('router_queue_name'), $config['router_queue']);
+        $container->setParameter($this->diUtils->format('default_queue_name'), $config['default_processor_queue']);
 
-        $container->register($this->format('route_collection'), RouteCollection::class)
+        $container->register($this->diUtils->format('route_collection'), RouteCollection::class)
             ->addArgument([])
             ->setFactory([RouteCollection::class, 'fromArray'])
         ;
 
-        $container->register($this->format('producer'), Producer::class)
+        $container->register($this->diUtils->format('producer'), Producer::class)
             ->setPublic(true)
-            ->addArgument($this->reference('driver'))
-            ->addArgument($this->reference('rpc_factory'))
-            ->addArgument($this->reference('client_extensions'))
+            ->addArgument($this->diUtils->reference('driver'))
+            ->addArgument($this->diUtils->reference('rpc_factory'))
+            ->addArgument($this->diUtils->reference('client_extensions'))
         ;
 
-        $container->register($this->format('spool_producer'), SpoolProducer::class)
-            ->addArgument($this->reference('producer'))
+        $container->register($this->diUtils->format('spool_producer'), SpoolProducer::class)
+            ->addArgument($this->diUtils->reference('producer'))
         ;
 
-        $container->register($this->format('client_extensions'), ChainExtension::class)
+        $container->register($this->diUtils->format('client_extensions'), ChainExtension::class)
             ->addArgument([])
         ;
 
-        $container->register($this->format('rpc_factory'), RpcFactory::class)
-            ->addArgument($this->reference('context'))
+        $container->register($this->diUtils->format('rpc_factory'), RpcFactory::class)
+            ->addArgument($this->diUtils->reference('context'))
         ;
 
-        $container->register($this->format('router_processor'), RouterProcessor::class)
-            ->addArgument($this->reference('driver'))
+        $container->register($this->diUtils->format('router_processor'), RouterProcessor::class)
+            ->addArgument($this->diUtils->reference('driver'))
         ;
 
-        $container->register($this->format('processor_registry'), ContainerProcessorRegistry::class);
+        $container->register($this->diUtils->format('processor_registry'), ContainerProcessorRegistry::class);
 
-        $container->register($this->format('delegate_processor'), DelegateProcessor::class)
-            ->addArgument($this->reference('processor_registry'))
+        $container->register($this->diUtils->format('delegate_processor'), DelegateProcessor::class)
+            ->addArgument($this->diUtils->reference('processor_registry'))
         ;
 
-        $container->register($this->format('set_router_properties_extension'), SetRouterPropertiesExtension::class)
-            ->addArgument($this->reference('driver'))
-            ->addTag('enqueue.consumption_extension', ['priority' => 100, 'client' => $this->name])
+        $container->register($this->diUtils->format('set_router_properties_extension'), SetRouterPropertiesExtension::class)
+            ->addArgument($this->diUtils->reference('driver'))
+            ->addTag('enqueue.consumption_extension', ['priority' => 100, 'client' => $this->diUtils->getConfigName()])
         ;
 
-        $container->register($this->format('queue_consumer'), QueueConsumer::class)
-            ->addArgument($this->reference('context'))
-            ->addArgument($this->reference('consumption_extensions'))
+        $container->register($this->diUtils->format('queue_consumer'), QueueConsumer::class)
+            ->addArgument($this->diUtils->reference('context'))
+            ->addArgument($this->diUtils->reference('consumption_extensions'))
             ->addArgument([])
-            ->addArgument($this->reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE))
+            ->addArgument($this->diUtils->reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE))
             ->addArgument($config['consumption']['receive_timeout'])
         ;
 
-        $container->register($this->format('consumption_extensions'), ConsumptionChainExtension::class)
+        $container->register($this->diUtils->format('consumption_extensions'), ConsumptionChainExtension::class)
             ->addArgument([])
         ;
 
-        $container->register($this->format('flush_spool_producer_extension'), FlushSpoolProducerExtension::class)
-            ->addArgument($this->reference('spool_producer'))
-            ->addTag('enqueue.consumption.extension', ['priority' => -100, 'client' => $this->name])
+        $container->register($this->diUtils->format('flush_spool_producer_extension'), FlushSpoolProducerExtension::class)
+            ->addArgument($this->diUtils->reference('spool_producer'))
+            ->addTag('enqueue.consumption.extension', ['priority' => -100, 'client' => $this->diUtils->getConfigName()])
         ;
 
-        $container->register($this->format('exclusive_command_extension'), ExclusiveCommandExtension::class)
-            ->addArgument($this->reference('driver'))
-            ->addTag('enqueue.consumption.extension', ['priority' => 100, 'client' => $this->name])
+        $container->register($this->diUtils->format('exclusive_command_extension'), ExclusiveCommandExtension::class)
+            ->addArgument($this->diUtils->reference('driver'))
+            ->addTag('enqueue.consumption.extension', ['priority' => 100, 'client' => $this->diUtils->getConfigName()])
         ;
 
         if ($config['traceable_producer']) {
-            $container->register($this->format('traceable_producer'), TraceableProducer::class)
-                ->setDecoratedService($this->format('producer'))
-                ->addArgument($this->reference('traceable_producer.inner'))
+            $container->register($this->diUtils->format('traceable_producer'), TraceableProducer::class)
+                ->setDecoratedService($this->diUtils->format('producer'))
+                ->addArgument($this->diUtils->reference('traceable_producer.inner'))
             ;
         }
 
         if ($config['redelivered_delay_time']) {
-            $container->register($this->format('delay_redelivered_message_extension'), DelayRedeliveredMessageExtension::class)
-                ->addArgument($this->reference('driver'))
+            $container->register($this->diUtils->format('delay_redelivered_message_extension'), DelayRedeliveredMessageExtension::class)
+                ->addArgument($this->diUtils->reference('driver'))
                 ->addArgument($config['redelivered_delay_time'])
-                ->addTag('enqueue.consumption_extension', ['priority' => 10, 'client' => $this->name])
+                ->addTag('enqueue.consumption_extension', ['priority' => 10, 'client' => $this->diUtils->getConfigName()])
             ;
 
-            $container->getDefinition($this->format('delay_redelivered_message_extension'))
+            $container->getDefinition($this->diUtils->format('delay_redelivered_message_extension'))
                 ->replaceArgument(1, $config['redelivered_delay_time'])
             ;
         }
@@ -191,24 +193,29 @@ public function build(ContainerBuilder $container, array $config): void
         if ($container->hasDefinition($locatorId)) {
             $locator = $container->getDefinition($locatorId);
             $locator->replaceArgument(0, array_replace($locator->getArgument(0), [
-                $this->format('queue_consumer') => $this->reference('queue_consumer'),
-                $this->format('driver') => $this->reference('driver'),
-                $this->format('delegate_processor') => $this->reference('delegate_processor'),
-                $this->format('producer') => $this->reference('producer'),
+                $this->diUtils->format('queue_consumer') => $this->diUtils->reference('queue_consumer'),
+                $this->diUtils->format('driver') => $this->diUtils->reference('driver'),
+                $this->diUtils->format('delegate_processor') => $this->diUtils->reference('delegate_processor'),
+                $this->diUtils->format('producer') => $this->diUtils->reference('producer'),
             ]));
         }
 
         if ($this->default) {
-            $container->setAlias(ProducerInterface::class, $this->format('producer'));
-            $container->setAlias(SpoolProducer::class, $this->format('spool_producer'));
+            $container->setAlias(ProducerInterface::class, $this->diUtils->format('producer'));
+            $container->setAlias(SpoolProducer::class, $this->diUtils->format('spool_producer'));
+
+            if (DiUtils::DEFAULT_CONFIG !== $this->diUtils->getConfigName()) {
+                $container->setAlias($this->diUtils->formatDefault('producer'), $this->diUtils->format('producer'));
+                $container->setAlias($this->diUtils->formatDefault('spool_producer'), $this->diUtils->format('spool_producer'));
+            }
         }
     }
 
     public function createDriver(ContainerBuilder $container, array $config): string
     {
-        $factoryId = sprintf('enqueue.transport.%s.connection_factory', $this->getName());
-        $driverId = sprintf('enqueue.client.%s.driver', $this->getName());
-        $driverFactoryId = sprintf('enqueue.client.%s.driver_factory', $this->getName());
+        $factoryId = DiUtils::create(TransportFactory::MODULE, $this->diUtils->getConfigName())->format('connection_factory');
+        $driverId = $this->diUtils->format('driver');
+        $driverFactoryId = $this->diUtils->format('driver_factory');
 
         $container->register($driverId, DriverInterface::class)
             ->setFactory([new Reference($driverFactoryId), 'create'])
@@ -217,19 +224,22 @@ public function createDriver(ContainerBuilder $container, array $config): string
             ->addArgument($config)
         ;
 
+        if ($this->default) {
+            $container->setAlias(DriverInterface::class, $driverId);
+
+            if (DiUtils::DEFAULT_CONFIG !== $this->diUtils->getConfigName()) {
+                $container->setAlias($this->diUtils->formatDefault('driver'), $driverId);
+            }
+        }
+
         return $driverId;
     }
 
     public function createFlushSpoolProducerListener(ContainerBuilder $container): void
     {
-        $container->register($this->format('flush_spool_producer_listener'), FlushSpoolProducerListener::class)
-            ->addArgument($this->reference('spool_producer'))
+        $container->register($this->diUtils->format('flush_spool_producer_listener'), FlushSpoolProducerListener::class)
+            ->addArgument($this->diUtils->reference('spool_producer'))
             ->addTag('kernel.event_subscriber')
         ;
     }
-
-    public function getName(): string
-    {
-        return $this->name;
-    }
 }
diff --git a/Symfony/Client/DependencyInjection/FormatClientNameTrait.php b/Symfony/Client/DependencyInjection/FormatClientNameTrait.php
deleted file mode 100644
index 41dace8..0000000
--- a/Symfony/Client/DependencyInjection/FormatClientNameTrait.php
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-namespace Enqueue\Symfony\Client\DependencyInjection;
-
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\DependencyInjection\Reference;
-
-trait FormatClientNameTrait
-{
-    abstract protected function getName(): string;
-
-    private function reference(string $serviceName, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE): Reference
-    {
-        return new Reference($this->format($serviceName), $invalidBehavior);
-    }
-
-    private function parameter(string $serviceName): string
-    {
-        $fullName = $this->format($serviceName);
-
-        return "%$fullName%";
-    }
-
-    private function format(string $serviceName): string
-    {
-        $pattern = 'enqueue.client.%s.'.$serviceName;
-
-        return sprintf($pattern, $this->getName());
-    }
-}
diff --git a/Symfony/DependencyInjection/BuildConsumptionExtensionsPass.php b/Symfony/DependencyInjection/BuildConsumptionExtensionsPass.php
index 857ddf2..352682b 100644
--- a/Symfony/DependencyInjection/BuildConsumptionExtensionsPass.php
+++ b/Symfony/DependencyInjection/BuildConsumptionExtensionsPass.php
@@ -2,16 +2,13 @@
 
 namespace Enqueue\Symfony\DependencyInjection;
 
+use Enqueue\Symfony\DiUtils;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Reference;
 
 final class BuildConsumptionExtensionsPass implements CompilerPassInterface
 {
-    use FormatTransportNameTrait;
-
-    protected $name;
-
     public function process(ContainerBuilder $container): void
     {
         if (false == $container->hasParameter('enqueue.transports')) {
@@ -19,11 +16,12 @@ public function process(ContainerBuilder $container): void
         }
 
         $names = $container->getParameter('enqueue.transports');
+        $defaultName = $container->getParameter('enqueue.default_transport');
 
         foreach ($names as $name) {
-            $this->name = $name;
+            $diUtils = DiUtils::create(TransportFactory::MODULE, $name);
 
-            $extensionsId = $this->format('consumption_extensions');
+            $extensionsId = $diUtils->format('consumption_extensions');
             if (false == $container->hasDefinition($extensionsId)) {
                 throw new \LogicException(sprintf('Service "%s" not found', $extensionsId));
             }
@@ -33,9 +31,9 @@ public function process(ContainerBuilder $container): void
             $groupByPriority = [];
             foreach ($tags as $serviceId => $tagAttributes) {
                 foreach ($tagAttributes as $tagAttribute) {
-                    $transport = $tagAttribute['transport'] ?? 'default';
+                    $transport = $tagAttribute['transport'] ?? $defaultName;
 
-                    if ($transport !== $this->name && 'all' !== $transport) {
+                    if ($transport !== $name && 'all' !== $transport) {
                         continue;
                     }
 
@@ -59,9 +57,4 @@ public function process(ContainerBuilder $container): void
             ));
         }
     }
-
-    protected function getName(): string
-    {
-        return $this->name;
-    }
 }
diff --git a/Symfony/DependencyInjection/BuildProcessorRegistryPass.php b/Symfony/DependencyInjection/BuildProcessorRegistryPass.php
index c9b7f8b..cc6e042 100644
--- a/Symfony/DependencyInjection/BuildProcessorRegistryPass.php
+++ b/Symfony/DependencyInjection/BuildProcessorRegistryPass.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Symfony\DependencyInjection;
 
+use Enqueue\Symfony\DiUtils;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -9,10 +10,6 @@
 
 final class BuildProcessorRegistryPass implements CompilerPassInterface
 {
-    use FormatTransportNameTrait;
-
-    protected $name;
-
     public function process(ContainerBuilder $container): void
     {
         if (false == $container->hasParameter('enqueue.transports')) {
@@ -20,11 +17,12 @@ public function process(ContainerBuilder $container): void
         }
 
         $names = $container->getParameter('enqueue.transports');
+        $defaultName = $container->getParameter('enqueue.default_transport');
 
         foreach ($names as $name) {
-            $this->name = $name;
+            $diUtils = DiUtils::create(TransportFactory::MODULE, $name);
 
-            $processorRegistryId = $this->format('processor_registry');
+            $processorRegistryId = $diUtils->format('processor_registry');
             if (false == $container->hasDefinition($processorRegistryId)) {
                 throw new \LogicException(sprintf('Service "%s" not found', $processorRegistryId));
             }
@@ -33,9 +31,9 @@ public function process(ContainerBuilder $container): void
             $map = [];
             foreach ($container->findTaggedServiceIds($tag) as $serviceId => $tagAttributes) {
                 foreach ($tagAttributes as $tagAttribute) {
-                    $transport = $tagAttribute['transport'] ?? 'default';
+                    $transport = $tagAttribute['transport'] ?? $defaultName;
 
-                    if ($transport !== $this->name && 'all' !== $transport) {
+                    if ($transport !== $name && 'all' !== $transport) {
                         continue;
                     }
 
@@ -49,9 +47,4 @@ public function process(ContainerBuilder $container): void
             $registry->setArgument(0, ServiceLocatorTagPass::register($container, $map, $processorRegistryId));
         }
     }
-
-    protected function getName(): string
-    {
-        return $this->name;
-    }
 }
diff --git a/Symfony/DependencyInjection/FormatTransportNameTrait.php b/Symfony/DependencyInjection/FormatTransportNameTrait.php
deleted file mode 100644
index dd0fdd7..0000000
--- a/Symfony/DependencyInjection/FormatTransportNameTrait.php
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-namespace Enqueue\Symfony\DependencyInjection;
-
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\DependencyInjection\Reference;
-
-trait FormatTransportNameTrait
-{
-    abstract protected function getName(): string;
-
-    private function reference(string $serviceName, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE): Reference
-    {
-        return new Reference($this->format($serviceName), $invalidBehavior);
-    }
-
-    private function parameter(string $serviceName): string
-    {
-        $fullName = $this->format($serviceName);
-
-        return "%$fullName%";
-    }
-
-    private function format(string $serviceName): string
-    {
-        $pattern = 'enqueue.transport.%s.'.$serviceName;
-
-        return sprintf($pattern, $this->getName());
-    }
-}
diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index eb9454b..5cc1a65 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -12,6 +12,7 @@
 use Enqueue\Rpc\RpcClient;
 use Enqueue\Rpc\RpcFactory;
 use Enqueue\Symfony\ContainerProcessorRegistry;
+use Enqueue\Symfony\DiUtils;
 use Interop\Queue\ConnectionFactory;
 use Interop\Queue\Context;
 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
@@ -25,17 +26,17 @@
  */
 final class TransportFactory
 {
-    use FormatTransportNameTrait;
+    public const MODULE = 'transport';
 
     /**
-     * @var string
+     * @var bool
      */
-    private $name;
+    private $default;
 
     /**
-     * @var bool
+     * @var DiUtils
      */
-    private $default;
+    private $diUtils;
 
     public function __construct(string $name, bool $default = false)
     {
@@ -43,8 +44,8 @@ public function __construct(string $name, bool $default = false)
             throw new \InvalidArgumentException('The name could not be empty.');
         }
 
-        $this->name = $name;
         $this->default = $default;
+        $this->diUtils = DiUtils::create(self::MODULE, $name);
     }
 
     public static function getConfiguration(string $name = 'transport'): NodeDefinition
@@ -123,16 +124,11 @@ public static function getQueueConsumerConfiguration(string $name = 'consumption
         return $builder;
     }
 
-    public function getName(): string
-    {
-        return $this->name;
-    }
-
     public function buildConnectionFactory(ContainerBuilder $container, array $config): void
     {
-        $factoryId = $this->format('connection_factory');
+        $factoryId = $this->diUtils->format('connection_factory');
 
-        $factoryFactoryId = $this->format('connection_factory_factory');
+        $factoryFactoryId = $this->diUtils->format('connection_factory_factory');
         $container->register($factoryFactoryId, $config['factory_class'] ?? ConnectionFactoryFactory::class);
 
         $factoryFactoryService = new Reference(
@@ -156,16 +152,20 @@ public function buildConnectionFactory(ContainerBuilder $container, array $confi
         }
 
         if ($this->default) {
-            $container->setAlias(ConnectionFactory::class, $this->format('connection_factory'));
+            $container->setAlias(ConnectionFactory::class, $factoryId);
+
+            if (DiUtils::DEFAULT_CONFIG !== $this->diUtils->getConfigName()) {
+                $container->setAlias($this->diUtils->formatDefault('connection_factory'), $factoryId);
+            }
         }
     }
 
     public function buildContext(ContainerBuilder $container, array $config): void
     {
-        $factoryId = $this->format('connection_factory');
+        $factoryId = $this->diUtils->format('connection_factory');
         $this->assertServiceExists($container, $factoryId);
 
-        $contextId = $this->format('context');
+        $contextId = $this->diUtils->format('context');
 
         $container->register($contextId, Context::class)
             ->setFactory([new Reference($factoryId), 'createContext'])
@@ -174,60 +174,72 @@ public function buildContext(ContainerBuilder $container, array $config): void
         $this->addServiceToLocator($container, 'context');
 
         if ($this->default) {
-            $container->setAlias(Context::class, $this->format('context'));
+            $container->setAlias(Context::class, $contextId);
+
+            if (DiUtils::DEFAULT_CONFIG !== $this->diUtils->getConfigName()) {
+                $container->setAlias($this->diUtils->formatDefault('context'), $contextId);
+            }
         }
     }
 
     public function buildQueueConsumer(ContainerBuilder $container, array $config): void
     {
-        $contextId = $this->format('context');
+        $contextId = $this->diUtils->format('context');
         $this->assertServiceExists($container, $contextId);
 
-        $container->setParameter($this->format('receive_timeout'), $config['receive_timeout'] ?? 10000);
+        $container->setParameter($this->diUtils->format('receive_timeout'), $config['receive_timeout'] ?? 10000);
 
-        $logExtensionId = $this->format('log_extension');
+        $logExtensionId = $this->diUtils->format('log_extension');
         $container->register($logExtensionId, LogExtension::class)
-            ->addTag('enqueue.transport.consumption_extension', ['transport' => $this->name, 'priority' => -100])
+            ->addTag('enqueue.transport.consumption_extension', ['transport' => $this->diUtils->getConfigName(), 'priority' => -100])
         ;
 
-        $container->register($this->format('consumption_extensions'), ChainExtension::class)
+        $container->register($this->diUtils->format('consumption_extensions'), ChainExtension::class)
             ->addArgument([])
         ;
 
-        $container->register($this->format('queue_consumer'), QueueConsumer::class)
+        $container->register($this->diUtils->format('queue_consumer'), QueueConsumer::class)
             ->addArgument(new Reference($contextId))
-            ->addArgument(new Reference($this->format('consumption_extensions')))
+            ->addArgument(new Reference($this->diUtils->format('consumption_extensions')))
             ->addArgument([])
             ->addArgument(new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE))
-            ->addArgument($this->parameter('receive_timeout'))
+            ->addArgument($this->diUtils->parameter('receive_timeout'))
         ;
 
-        $container->register($this->format('processor_registry'), ContainerProcessorRegistry::class);
+        $container->register($this->diUtils->format('processor_registry'), ContainerProcessorRegistry::class);
 
         $this->addServiceToLocator($container, 'queue_consumer');
         $this->addServiceToLocator($container, 'processor_registry');
 
         if ($this->default) {
-            $container->setAlias(QueueConsumerInterface::class, $this->format('queue_consumer'));
+            $container->setAlias(QueueConsumerInterface::class, $this->diUtils->format('queue_consumer'));
+
+            if (DiUtils::DEFAULT_CONFIG !== $this->diUtils->getConfigName()) {
+                $container->setAlias($this->diUtils->formatDefault('queue_consumer'), $this->diUtils->format('queue_consumer'));
+            }
         }
     }
 
     public function buildRpcClient(ContainerBuilder $container, array $config): void
     {
-        $contextId = $this->format('context');
+        $contextId = $this->diUtils->format('context');
         $this->assertServiceExists($container, $contextId);
 
-        $container->register($this->format('rpc_factory'), RpcFactory::class)
+        $container->register($this->diUtils->format('rpc_factory'), RpcFactory::class)
             ->addArgument(new Reference($contextId))
         ;
 
-        $container->register($this->format('rpc_client'), RpcClient::class)
+        $container->register($this->diUtils->format('rpc_client'), RpcClient::class)
             ->addArgument(new Reference($contextId))
-            ->addArgument(new Reference($this->format('rpc_factory')))
+            ->addArgument(new Reference($this->diUtils->format('rpc_factory')))
         ;
 
         if ($this->default) {
-            $container->setAlias(RpcClient::class, $this->format('rpc_client'));
+            $container->setAlias(RpcClient::class, $this->diUtils->format('rpc_client'));
+
+            if (DiUtils::DEFAULT_CONFIG !== $this->diUtils->getConfigName()) {
+                $container->setAlias($this->diUtils->formatDefault('rpc_client'), $this->diUtils->format('rpc_client'));
+            }
         }
     }
 
@@ -246,7 +258,7 @@ private function addServiceToLocator(ContainerBuilder $container, string $servic
             $locator = $container->getDefinition($locatorId);
 
             $map = $locator->getArgument(0);
-            $map[$this->format($serviceName)] = $this->reference($serviceName);
+            $map[$this->diUtils->format($serviceName)] = $this->diUtils->reference($serviceName);
 
             $locator->replaceArgument(0, $map);
         }
diff --git a/Symfony/DiUtils.php b/Symfony/DiUtils.php
index 6781043..335ff04 100644
--- a/Symfony/DiUtils.php
+++ b/Symfony/DiUtils.php
@@ -7,6 +7,8 @@
 
 class DiUtils
 {
+    public const DEFAULT_CONFIG = 'default';
+
     /**
      * @var string
      */
@@ -43,6 +45,11 @@ public function reference(string $serviceName, $invalidBehavior = ContainerInter
         return new Reference($this->format($serviceName), $invalidBehavior);
     }
 
+    public function referenceDefault(string $serviceName, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE): Reference
+    {
+        return new Reference($this->formatDefault($serviceName), $invalidBehavior);
+    }
+
     public function parameter(string $serviceName): string
     {
         $fullName = $this->format($serviceName);
@@ -50,8 +57,25 @@ public function parameter(string $serviceName): string
         return "%$fullName%";
     }
 
+    public function parameterDefault(string $serviceName): string
+    {
+        $fullName = $this->formatDefault($serviceName);
+
+        return "%$fullName%";
+    }
+
     public function format(string $serviceName): string
     {
-        return sprintf('enqueue.%s.%s.%s', $this->moduleName, $this->configName, $serviceName);
+        return $this->doFormat($this->moduleName, $this->configName, $serviceName);
+    }
+
+    public function formatDefault(string $serviceName): string
+    {
+        return $this->doFormat($this->moduleName, self::DEFAULT_CONFIG, $serviceName);
+    }
+
+    private function doFormat(string $moduleName, string $configName, string $serviceName): string
+    {
+        return sprintf('enqueue.%s.%s.%s', $moduleName, $configName, $serviceName);
     }
 }
diff --git a/composer.json b/composer.json
index fc42d77..31b2159 100644
--- a/composer.json
+++ b/composer.json
@@ -7,8 +7,8 @@
     "license": "MIT",
     "require": {
         "php": "^7.1.3",
-        "queue-interop/amqp-interop": "0.8.x-dev",
-        "queue-interop/queue-interop": "0.7.x-dev",
+        "queue-interop/amqp-interop": "^0.8",
+        "queue-interop/queue-interop": "^0.7",
         "enqueue/null": "0.9.x-dev",
         "enqueue/dsn": "0.9.x-dev",
         "ramsey/uuid": "^2|^3.5",

From 657a48de1956d5ac09600641ca72dd66d6ef8ece Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Fri, 16 Nov 2018 14:59:34 +0200
Subject: [PATCH 209/337] multi client configuration

---
 .../BuildClientExtensionsPassTest.php         | 30 +++++++-----
 .../BuildCommandSubscriberRoutesPassTest.php  | 48 ++++++++++++-------
 .../BuildConsumptionExtensionsPassTest.php    | 48 +++++++++++--------
 .../BuildProcessorRoutesPassTest.php          | 42 +++++++++-------
 .../BuildTopicSubscriberRoutesPassTest.php    | 47 +++++++++++-------
 .../DependencyInjection/ClientFactoryTest.php |  7 ---
 .../BuildConsumptionExtensionsPassTest.php    | 48 +++++++++++--------
 .../BuildProcessorRegistryPassTest.php        | 19 +++++---
 .../TransportFactoryTest.php                  |  7 ---
 9 files changed, 170 insertions(+), 126 deletions(-)

diff --git a/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php
index e1c8bdc..af69293 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php
@@ -43,6 +43,7 @@ public function testThrowsIfNoClientExtensionsServiceFoundForConfiguredTransport
     {
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.clients', ['foo', 'bar']);
+        $container->setParameter('enqueue.default_client', 'foo');
 
         $pass = new BuildClientExtensionsPass();
 
@@ -58,6 +59,7 @@ public function testShouldRegisterClientExtension()
 
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.clients', ['aName']);
+        $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.aName.client_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
@@ -84,6 +86,7 @@ public function testShouldIgnoreOtherClientExtensions()
 
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.clients', ['aName']);
+        $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.aName.client_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
@@ -109,6 +112,7 @@ public function testShouldAddExtensionIfClientAll()
 
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.clients', ['aName']);
+        $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.aName.client_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
@@ -133,8 +137,9 @@ public function testShouldTreatTagsWithoutClientAsDefaultClient()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.client_extensions', $extensions);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.client_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
             ->addTag('enqueue.client_extension')
@@ -156,11 +161,12 @@ public function testShouldTreatTagsWithoutClientAsDefaultClient()
     public function testShouldOrderExtensionsByPriority()
     {
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
 
         $extensions = new Definition();
         $extensions->addArgument([]);
-        $container->setDefinition('enqueue.client.default.client_extensions', $extensions);
+        $container->setDefinition('enqueue.client.foo.client_extensions', $extensions);
 
         $extension = new Definition();
         $extension->addTag('enqueue.client_extension', ['priority' => 6]);
@@ -188,11 +194,12 @@ public function testShouldOrderExtensionsByPriority()
     public function testShouldAssumePriorityZeroIfPriorityIsNotSet()
     {
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
 
         $extensions = new Definition();
         $extensions->addArgument([]);
-        $container->setDefinition('enqueue.client.default.client_extensions', $extensions);
+        $container->setDefinition('enqueue.client.foo.client_extensions', $extensions);
 
         $extension = new Definition();
         $extension->addTag('enqueue.client_extension');
@@ -226,8 +233,9 @@ public function testShouldMergeWithAddedPreviously()
         ]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['aName']);
-        $container->setDefinition('enqueue.client.aName.client_extensions', $extensions);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.client_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
             ->addTag('enqueue.client_extension')
@@ -240,10 +248,7 @@ public function testShouldMergeWithAddedPreviously()
         $pass->process($container);
 
         $this->assertInternalType('array', $extensions->getArgument(0));
-        $this->assertEquals([
-            'aBarExtension' => 'aBarServiceIdAddedPreviously',
-            'aOloloExtension' => 'aOloloServiceIdAddedPreviously',
-        ], $extensions->getArgument(0));
+        $this->assertCount(4, $extensions->getArgument(0));
     }
 
     public function testShouldRegisterProcessorWithMatchedNameToCorrespondingExtensions()
@@ -256,6 +261,7 @@ public function testShouldRegisterProcessorWithMatchedNameToCorrespondingExtensi
 
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.clients', ['foo', 'bar']);
+        $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.foo.client_extensions', $fooExtensions);
         $container->setDefinition('enqueue.client.bar.client_extensions', $barExtensions);
 
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
index e077a76..603f0fb 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
@@ -47,6 +47,7 @@ public function testThrowsIfNoRouteCollectionServiceFoundForConfiguredTransport(
     {
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.clients', ['foo', 'bar']);
+        $container->setParameter('enqueue.default_client', 'baz');
 
         $pass = new BuildCommandSubscriberRoutesPass();
 
@@ -59,6 +60,7 @@ public function testThrowIfTaggedProcessorIsBuiltByFactory()
     {
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.clients', ['aName']);
+        $container->setParameter('enqueue.default_client', 'foo');
         $container->register('enqueue.client.aName.route_collection', RouteCollection::class)
             ->addArgument([])
         ;
@@ -81,6 +83,7 @@ public function testShouldRegisterProcessorWithMatchedName()
 
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($this->createCommandSubscriberProcessor()))
             ->addTag('enqueue.command_subscriber', ['client' => 'foo'])
@@ -103,8 +106,9 @@ public function testShouldRegisterProcessorWithoutNameToDefaultClient()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($this->createCommandSubscriberProcessor()))
             ->addTag('enqueue.command_subscriber')
         ;
@@ -126,8 +130,9 @@ public function testShouldRegisterProcessorIfClientNameEqualsAll()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($this->createCommandSubscriberProcessor()))
             ->addTag('enqueue.command_subscriber', ['client' => 'all'])
         ;
@@ -151,8 +156,9 @@ public function testShouldRegisterProcessorIfCommandsIsString()
         $processor = $this->createCommandSubscriberProcessor('fooCommand');
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.command_subscriber')
         ;
@@ -184,8 +190,9 @@ public function testThrowIfCommandSubscriberReturnsNothing()
         $processor = $this->createCommandSubscriberProcessor(null);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.command_subscriber')
         ;
@@ -205,8 +212,9 @@ public function testShouldRegisterProcessorIfCommandsAreStrings()
         $processor = $this->createCommandSubscriberProcessor(['fooCommand', 'barCommand']);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.command_subscriber')
         ;
@@ -248,8 +256,9 @@ public function testShouldRegisterProcessorIfParamSingleCommandArray()
         ]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.command_subscriber')
         ;
@@ -286,8 +295,9 @@ public function testShouldRegisterProcessorIfCommandsAreParamArrays()
         ]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.command_subscriber')
         ;
@@ -327,8 +337,9 @@ public function testThrowIfCommandSubscriberParamsInvalid()
         $processor = $this->createCommandSubscriberProcessor(['fooBar', true]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.command_subscriber')
         ;
@@ -351,8 +362,9 @@ public function testShouldMergeExtractedRoutesWithAlreadySetInCollection()
         $processor = $this->createCommandSubscriberProcessor(['fooCommand']);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.command_subscriber')
         ;
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php
index d3936fe..db991e9 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php
@@ -43,6 +43,7 @@ public function testThrowsIfNoConsumptionExtensionsServiceFoundForConfiguredTran
     {
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.clients', ['foo', 'bar']);
+        $container->setParameter('enqueue.default_client', 'baz');
 
         $pass = new BuildConsumptionExtensionsPass();
 
@@ -57,14 +58,15 @@ public function testShouldRegisterClientExtension()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['aName']);
-        $container->setDefinition('enqueue.client.aName.consumption_extensions', $extensions);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
-            ->addTag('enqueue.consumption_extension', ['client' => 'aName'])
+            ->addTag('enqueue.consumption_extension', ['client' => 'foo'])
         ;
         $container->register('aBarExtension', ExtensionInterface::class)
-            ->addTag('enqueue.consumption_extension', ['client' => 'aName'])
+            ->addTag('enqueue.consumption_extension', ['client' => 'foo'])
         ;
 
         $pass = new BuildConsumptionExtensionsPass();
@@ -83,11 +85,12 @@ public function testShouldIgnoreOtherClientExtensions()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['aName']);
-        $container->setDefinition('enqueue.client.aName.consumption_extensions', $extensions);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
-            ->addTag('enqueue.consumption_extension', ['client' => 'aName'])
+            ->addTag('enqueue.consumption_extension', ['client' => 'foo'])
         ;
         $container->register('aBarExtension', ExtensionInterface::class)
             ->addTag('enqueue.consumption_extension', ['client' => 'anotherName'])
@@ -108,8 +111,9 @@ public function testShouldAddExtensionIfClientAll()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['aName']);
-        $container->setDefinition('enqueue.client.aName.consumption_extensions', $extensions);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
             ->addTag('enqueue.consumption_extension', ['client' => 'all'])
@@ -133,8 +137,9 @@ public function testShouldTreatTagsWithoutClientAsDefaultClient()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.consumption_extensions', $extensions);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
             ->addTag('enqueue.consumption_extension')
@@ -156,11 +161,12 @@ public function testShouldTreatTagsWithoutClientAsDefaultClient()
     public function testShouldOrderExtensionsByPriority()
     {
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
 
         $extensions = new Definition();
         $extensions->addArgument([]);
-        $container->setDefinition('enqueue.client.default.consumption_extensions', $extensions);
+        $container->setDefinition('enqueue.client.foo.consumption_extensions', $extensions);
 
         $extension = new Definition();
         $extension->addTag('enqueue.consumption_extension', ['priority' => 6]);
@@ -188,11 +194,12 @@ public function testShouldOrderExtensionsByPriority()
     public function testShouldAssumePriorityZeroIfPriorityIsNotSet()
     {
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
 
         $extensions = new Definition();
         $extensions->addArgument([]);
-        $container->setDefinition('enqueue.client.default.consumption_extensions', $extensions);
+        $container->setDefinition('enqueue.client.foo.consumption_extensions', $extensions);
 
         $extension = new Definition();
         $extension->addTag('enqueue.consumption_extension');
@@ -226,8 +233,9 @@ public function testShouldMergeWithAddedPreviously()
         ]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['aName']);
-        $container->setDefinition('enqueue.client.aName.consumption_extensions', $extensions);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
             ->addTag('enqueue.consumption_extension')
@@ -240,10 +248,7 @@ public function testShouldMergeWithAddedPreviously()
         $pass->process($container);
 
         $this->assertInternalType('array', $extensions->getArgument(0));
-        $this->assertEquals([
-            'aBarExtension' => 'aBarServiceIdAddedPreviously',
-            'aOloloExtension' => 'aOloloServiceIdAddedPreviously',
-        ], $extensions->getArgument(0));
+        $this->assertCount(4, $extensions->getArgument(0));
     }
 
     public function testShouldRegisterProcessorWithMatchedNameToCorrespondingExtensions()
@@ -256,6 +261,7 @@ public function testShouldRegisterProcessorWithMatchedNameToCorrespondingExtensi
 
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.clients', ['foo', 'bar']);
+        $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.foo.consumption_extensions', $fooExtensions);
         $container->setDefinition('enqueue.client.bar.consumption_extensions', $barExtensions);
 
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php
index e671f7a..74553e6 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php
@@ -43,6 +43,7 @@ public function testThrowsIfNoRouteCollectionServiceFoundForConfiguredTransport(
     {
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.clients', ['foo', 'bar']);
+        $container->setParameter('enqueue.default_client', 'baz');
 
         $pass = new BuildProcessorRoutesPass();
 
@@ -57,8 +58,9 @@ public function testThrowIfBothTopicAndCommandAttributesAreSet()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.processor', ['topic' => 'foo', 'command' => 'bar'])
         ;
@@ -76,8 +78,9 @@ public function testThrowIfNeitherTopicNorCommandAttributesAreSet()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.processor', [])
         ;
@@ -96,6 +99,7 @@ public function testShouldRegisterProcessorWithMatchedName()
 
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'bar');
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.processor', ['client' => 'foo', 'topic' => 'foo'])
@@ -118,8 +122,9 @@ public function testShouldRegisterProcessorWithoutNameToDefaultClient()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.processor', ['topic' => 'foo'])
         ;
@@ -141,8 +146,9 @@ public function testShouldRegisterProcessorIfClientNameEqualsAll()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.processor', ['client' => 'all', 'topic' => 'foo'])
         ;
@@ -164,8 +170,9 @@ public function testShouldRegisterAsTopicProcessor()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.processor', ['topic' => 'aTopic'])
         ;
@@ -195,8 +202,9 @@ public function testShouldRegisterAsCommandProcessor()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.processor', ['command' => 'aCommand'])
         ;
@@ -226,8 +234,9 @@ public function testShouldRegisterWithCustomProcessorName()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.processor', ['command' => 'aCommand', 'processor' => 'customProcessorName'])
         ;
@@ -260,8 +269,9 @@ public function testShouldMergeExtractedRoutesWithAlreadySetInCollection()
         ]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.processor', ['command' => 'fooCommand'])
         ;
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
index 25033f0..72f5b21 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
@@ -47,6 +47,7 @@ public function testThrowsIfNoRouteCollectionServiceFoundForConfiguredTransport(
     {
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.clients', ['foo', 'bar']);
+        $container->setParameter('enqueue.default_client', 'baz');
 
         $pass = new BuildTopicSubscriberRoutesPass();
 
@@ -58,8 +59,9 @@ public function testThrowsIfNoRouteCollectionServiceFoundForConfiguredTransport(
     public function testThrowIfTaggedProcessorIsBuiltByFactory()
     {
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['aName']);
-        $container->register('enqueue.client.aName.route_collection', RouteCollection::class)
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->register('enqueue.client.foo.route_collection', RouteCollection::class)
             ->addArgument([])
         ;
         $container->register('aProcessor', Processor::class)
@@ -81,6 +83,7 @@ public function testShouldRegisterProcessorWithMatchedName()
 
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'bar');
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($this->createTopicSubscriberProcessor()))
             ->addTag('enqueue.topic_subscriber', ['client' => 'foo'])
@@ -103,8 +106,9 @@ public function testShouldRegisterProcessorWithoutNameToDefaultClient()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($this->createTopicSubscriberProcessor()))
             ->addTag('enqueue.topic_subscriber')
         ;
@@ -126,8 +130,9 @@ public function testShouldRegisterProcessorIfClientNameEqualsAll()
         $routeCollection->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($this->createTopicSubscriberProcessor()))
             ->addTag('enqueue.topic_subscriber', ['client' => 'all'])
         ;
@@ -151,8 +156,9 @@ public function testShouldRegisterProcessorIfTopicsIsString()
         $processor = $this->createTopicSubscriberProcessor('fooTopic');
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.topic_subscriber')
         ;
@@ -184,8 +190,9 @@ public function testThrowIfTopicSubscriberReturnsNothing()
         $processor = $this->createTopicSubscriberProcessor(null);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.topic_subscriber')
         ;
@@ -205,8 +212,9 @@ public function testShouldRegisterProcessorIfTopicsAreStrings()
         $processor = $this->createTopicSubscriberProcessor(['fooTopic', 'barTopic']);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.topic_subscriber')
         ;
@@ -247,8 +255,9 @@ public function testShouldRegisterProcessorIfTopicsAreParamArrays()
         ]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.topic_subscriber')
         ;
@@ -288,8 +297,9 @@ public function testThrowIfTopicSubscriberParamsInvalid()
         $processor = $this->createTopicSubscriberProcessor(['fooBar', true]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.topic_subscriber')
         ;
@@ -312,8 +322,9 @@ public function testShouldMergeExtractedRoutesWithAlreadySetInCollection()
         $processor = $this->createTopicSubscriberProcessor(['fooTopic']);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.clients', ['default']);
-        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->setParameter('enqueue.clients', ['foo']);
+        $container->setParameter('enqueue.default_client', 'foo');
+        $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.topic_subscriber')
         ;
diff --git a/Tests/Symfony/Client/DependencyInjection/ClientFactoryTest.php b/Tests/Symfony/Client/DependencyInjection/ClientFactoryTest.php
index b872454..cac1805 100644
--- a/Tests/Symfony/Client/DependencyInjection/ClientFactoryTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/ClientFactoryTest.php
@@ -17,13 +17,6 @@ public function testShouldBeFinal()
         $this->assertClassFinal(ClientFactory::class);
     }
 
-    public function testShouldAllowGetNameSetInConstructor()
-    {
-        $transport = new ClientFactory('aName');
-
-        $this->assertEquals('aName', $transport->getName());
-    }
-
     public function testThrowIfEmptyNameGivenOnConstruction()
     {
         $this->expectException(\InvalidArgumentException::class);
diff --git a/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php b/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
index e44272d..b26b448 100644
--- a/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
+++ b/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
@@ -43,6 +43,7 @@ public function testThrowsIfNoConsumptionExtensionsServiceFoundForConfiguredTran
     {
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.transports', ['foo', 'bar']);
+        $container->setParameter('enqueue.default_transport', 'foo');
 
         $pass = new BuildConsumptionExtensionsPass();
 
@@ -57,14 +58,15 @@ public function testShouldRegisterTransportExtension()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.transports', ['aName']);
-        $container->setDefinition('enqueue.transport.aName.consumption_extensions', $extensions);
+        $container->setParameter('enqueue.transports', ['foo']);
+        $container->setParameter('enqueue.default_transport', 'foo');
+        $container->setDefinition('enqueue.transport.foo.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
-            ->addTag('enqueue.transport.consumption_extension', ['transport' => 'aName'])
+            ->addTag('enqueue.transport.consumption_extension', ['transport' => 'foo'])
         ;
         $container->register('aBarExtension', ExtensionInterface::class)
-            ->addTag('enqueue.transport.consumption_extension', ['transport' => 'aName'])
+            ->addTag('enqueue.transport.consumption_extension', ['transport' => 'foo'])
         ;
 
         $pass = new BuildConsumptionExtensionsPass();
@@ -83,11 +85,12 @@ public function testShouldIgnoreOtherTransportExtensions()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.transports', ['aName']);
-        $container->setDefinition('enqueue.transport.aName.consumption_extensions', $extensions);
+        $container->setParameter('enqueue.transports', ['foo']);
+        $container->setParameter('enqueue.default_transport', 'foo');
+        $container->setDefinition('enqueue.transport.foo.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
-            ->addTag('enqueue.transport.consumption_extension', ['transport' => 'aName'])
+            ->addTag('enqueue.transport.consumption_extension', ['transport' => 'foo'])
         ;
         $container->register('aBarExtension', ExtensionInterface::class)
             ->addTag('enqueue.transport.consumption_extension', ['transport' => 'anotherName'])
@@ -108,8 +111,9 @@ public function testShouldAddExtensionIfTransportAll()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.transports', ['aName']);
-        $container->setDefinition('enqueue.transport.aName.consumption_extensions', $extensions);
+        $container->setParameter('enqueue.transports', ['foo']);
+        $container->setParameter('enqueue.default_transport', 'foo');
+        $container->setDefinition('enqueue.transport.foo.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
             ->addTag('enqueue.transport.consumption_extension', ['transport' => 'all'])
@@ -133,8 +137,9 @@ public function testShouldTreatTagsWithoutTransportAsDefaultTransport()
         $extensions->addArgument([]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.transports', ['default']);
-        $container->setDefinition('enqueue.transport.default.consumption_extensions', $extensions);
+        $container->setParameter('enqueue.transports', ['foo']);
+        $container->setParameter('enqueue.default_transport', 'foo');
+        $container->setDefinition('enqueue.transport.foo.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
             ->addTag('enqueue.transport.consumption_extension')
@@ -156,11 +161,12 @@ public function testShouldTreatTagsWithoutTransportAsDefaultTransport()
     public function testShouldOrderExtensionsByPriority()
     {
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.transports', ['default']);
+        $container->setParameter('enqueue.transports', ['foo']);
+        $container->setParameter('enqueue.default_transport', 'foo');
 
         $extensions = new Definition();
         $extensions->addArgument([]);
-        $container->setDefinition('enqueue.transport.default.consumption_extensions', $extensions);
+        $container->setDefinition('enqueue.transport.foo.consumption_extensions', $extensions);
 
         $extension = new Definition();
         $extension->addTag('enqueue.transport.consumption_extension', ['priority' => 6]);
@@ -188,11 +194,12 @@ public function testShouldOrderExtensionsByPriority()
     public function testShouldAssumePriorityZeroIfPriorityIsNotSet()
     {
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.transports', ['default']);
+        $container->setParameter('enqueue.transports', ['foo']);
+        $container->setParameter('enqueue.default_transport', 'foo');
 
         $extensions = new Definition();
         $extensions->addArgument([]);
-        $container->setDefinition('enqueue.transport.default.consumption_extensions', $extensions);
+        $container->setDefinition('enqueue.transport.foo.consumption_extensions', $extensions);
 
         $extension = new Definition();
         $extension->addTag('enqueue.transport.consumption_extension');
@@ -226,8 +233,9 @@ public function testShouldMergeWithAddedPreviously()
         ]);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.transports', ['aName']);
-        $container->setDefinition('enqueue.transport.aName.consumption_extensions', $extensions);
+        $container->setParameter('enqueue.transports', ['foo']);
+        $container->setParameter('enqueue.default_transport', 'foo');
+        $container->setDefinition('enqueue.transport.foo.consumption_extensions', $extensions);
 
         $container->register('aFooExtension', ExtensionInterface::class)
             ->addTag('enqueue.transport.consumption_extension')
@@ -240,10 +248,7 @@ public function testShouldMergeWithAddedPreviously()
         $pass->process($container);
 
         $this->assertInternalType('array', $extensions->getArgument(0));
-        $this->assertEquals([
-            'aBarExtension' => 'aBarServiceIdAddedPreviously',
-            'aOloloExtension' => 'aOloloServiceIdAddedPreviously',
-        ], $extensions->getArgument(0));
+        $this->assertCount(4, $extensions->getArgument(0));
     }
 
     public function testShouldRegisterProcessorWithMatchedNameToCorrespondingRegistries()
@@ -256,6 +261,7 @@ public function testShouldRegisterProcessorWithMatchedNameToCorrespondingRegistr
 
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.transports', ['foo', 'bar']);
+        $container->setParameter('enqueue.default_transport', 'foo');
         $container->setDefinition('enqueue.transport.foo.consumption_extensions', $fooExtensions);
         $container->setDefinition('enqueue.transport.bar.consumption_extensions', $barExtensions);
 
diff --git a/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php b/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
index e9b9c7d..bb6eae2 100644
--- a/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
+++ b/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
@@ -44,6 +44,8 @@ public function testThrowsIfNoRegistryServiceFoundForConfiguredTransport()
     {
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.transports', ['foo', 'bar']);
+        $container->setParameter('enqueue.default_transport', 'baz');
+
 
         $pass = new BuildProcessorRegistryPass();
 
@@ -59,6 +61,7 @@ public function testShouldRegisterProcessorWithMatchedName()
 
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.transports', ['foo']);
+        $container->setParameter('enqueue.default_transport', 'foo');
         $container->setDefinition('enqueue.transport.foo.processor_registry', $registry);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.transport.processor', ['transport' => 'foo'])
@@ -88,6 +91,7 @@ public function testShouldRegisterProcessorWithMatchedNameToCorrespondingRegistr
 
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.transports', ['foo', 'bar']);
+        $container->setParameter('enqueue.default_transport', 'foo');
         $container->setDefinition('enqueue.transport.foo.processor_registry', $fooRegistry);
         $container->setDefinition('enqueue.transport.bar.processor_registry', $barRegistry);
         $container->register('aFooProcessor', 'aProcessorClass')
@@ -118,8 +122,9 @@ public function testShouldRegisterProcessorWithoutNameToDefaultTransport()
         $registry->addArgument(null);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.transports', ['default']);
-        $container->setDefinition('enqueue.transport.default.processor_registry', $registry);
+        $container->setParameter('enqueue.transports', ['foo']);
+        $container->setParameter('enqueue.default_transport', 'foo');
+        $container->setDefinition('enqueue.transport.foo.processor_registry', $registry);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.transport.processor', [])
         ;
@@ -144,8 +149,9 @@ public function testShouldRegisterProcessorIfTransportNameEqualsAll()
         $registry->addArgument(null);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.transports', ['default']);
-        $container->setDefinition('enqueue.transport.default.processor_registry', $registry);
+        $container->setParameter('enqueue.transports', ['foo']);
+        $container->setParameter('enqueue.default_transport', 'foo');
+        $container->setDefinition('enqueue.transport.foo.processor_registry', $registry);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.transport.processor', ['transport' => 'all'])
         ;
@@ -170,8 +176,9 @@ public function testShouldRegisterWithCustomProcessorName()
         $registry->addArgument(null);
 
         $container = new ContainerBuilder();
-        $container->setParameter('enqueue.transports', ['default']);
-        $container->setDefinition('enqueue.transport.default.processor_registry', $registry);
+        $container->setParameter('enqueue.transports', ['foo']);
+        $container->setParameter('enqueue.default_transport', 'foo');
+        $container->setDefinition('enqueue.transport.foo.processor_registry', $registry);
         $container->register('aFooProcessor', 'aProcessorClass')
             ->addTag('enqueue.transport.processor', ['processor' => 'customProcessorName'])
         ;
diff --git a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index 7e021ca..6cc02b6 100644
--- a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -27,13 +27,6 @@ public function testShouldBeFinal()
         $this->assertClassFinal(TransportFactory::class);
     }
 
-    public function testShouldAllowGetNameSetInConstructor()
-    {
-        $transport = new TransportFactory('aName');
-
-        $this->assertEquals('aName', $transport->getName());
-    }
-
     public function testThrowIfEmptyNameGivenOnConstruction()
     {
         $this->expectException(\InvalidArgumentException::class);

From 227df79e33c2531341baf19473daea7c34b924d0 Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Fri, 16 Nov 2018 15:08:54 +0200
Subject: [PATCH 210/337] multi client configuration

---
 .../DependencyInjection/BuildProcessorRegistryPassTest.php       | 1 -
 1 file changed, 1 deletion(-)

diff --git a/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php b/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
index bb6eae2..d122e04 100644
--- a/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
+++ b/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
@@ -46,7 +46,6 @@ public function testThrowsIfNoRegistryServiceFoundForConfiguredTransport()
         $container->setParameter('enqueue.transports', ['foo', 'bar']);
         $container->setParameter('enqueue.default_transport', 'baz');
 
-
         $pass = new BuildProcessorRegistryPass();
 
         $this->expectException(\LogicException::class);

From f8a2d93e67120eb52b883e717284c7441cd85a4a Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Fri, 16 Nov 2018 16:22:12 +0200
Subject: [PATCH 211/337] multi client configuration

---
 Tests/Symfony/ContainerProcessorRegistryTest.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/Symfony/ContainerProcessorRegistryTest.php b/Tests/Symfony/ContainerProcessorRegistryTest.php
index bb3cacb..fe84c2e 100644
--- a/Tests/Symfony/ContainerProcessorRegistryTest.php
+++ b/Tests/Symfony/ContainerProcessorRegistryTest.php
@@ -69,7 +69,7 @@ public function testThrowErrorIfServiceDoesNotImplementProcessorReturnType()
         $registry = new ContainerProcessorRegistry($containerMock);
 
         $this->expectException(\TypeError::class);
-        $this->expectExceptionMessage('Return value of Enqueue\Symfony\ContainerProcessorRegistry::get() must implement interface Interop\Queue\Processor, instance of stdClass returned');
+        $this->expectExceptionMessage('Return value of Enqueue\Symfony\ContainerProcessorRegistry::get() must implement interface Interop\Queue\PsrProcessor, instance of stdClass returned');
         $registry->get('processor-name');
     }
 

From 05453e871da733249bc9bc0bded48bf0f4d9b4cd Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 16 Nov 2018 17:40:03 +0200
Subject: [PATCH 212/337] [bundle] Add BC for topic\command subscribers.

---
 Client/TopicSubscriberInterface.php           | 18 ++++++--
 .../BuildCommandSubscriberRoutesPass.php      | 35 ++++++++++++++
 .../BuildTopicSubscriberRoutesPass.php        | 31 +++++++++++++
 .../BuildCommandSubscriberRoutesPassTest.php  | 42 +++++++++++++++++
 .../BuildTopicSubscriberRoutesPassTest.php    | 46 +++++++++++++++++++
 5 files changed, 169 insertions(+), 3 deletions(-)

diff --git a/Client/TopicSubscriberInterface.php b/Client/TopicSubscriberInterface.php
index 634a7ac..849a782 100644
--- a/Client/TopicSubscriberInterface.php
+++ b/Client/TopicSubscriberInterface.php
@@ -15,10 +15,22 @@ interface TopicSubscriberInterface
      *
      * or
      *
-     * ['aTopicName' => [
-     *     'processor' => 'processor',
+     * [
+     *   [
+     *     'topic' => 'aTopicName',
+     *     'processor' => 'fooProcessor',
      *     'queue' => 'a_client_queue_name',
-     * ]]
+     *
+     *     'aCustomOption' => 'aVal',
+     *   ],
+     *   [
+     *     'topic' => 'anotherTopicName',
+     *     'processor' => 'barProcessor',
+     *     'queue' => 'a_client_queue_name',
+     *
+     *     'aCustomOption' => 'aVal',
+     *   ],
+     * ]
      *
      * Note: If you set prefix_queue to true then the queue is used as is and therefor the driver is not used to prepare a transport queue name.
      * It is possible to pass other options, they could be accessible on a route instance through options.
diff --git a/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php b/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php
index cee1343..1527e51 100644
--- a/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php
+++ b/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php
@@ -66,6 +66,41 @@ public function process(ContainerBuilder $container): void
                         throw new \LogicException('Command subscriber configuration is invalid. Should be an array or string.');
                     }
 
+                    // 0.8 command subscriber
+                    if (isset($commands['processorName'])) {
+                        @trigger_error('The command subscriber 0.8 syntax is deprecated since Enqueue 0.9.', E_USER_DEPRECATED);
+
+                        $source = $commands['processorName'];
+                        $processor = $params['processorName'] ?? $serviceId;
+
+                        $options = $commands;
+                        unset(
+                            $options['processorName'],
+                            $options['queueName'],
+                            $options['queueNameHardcoded'],
+                            $options['exclusive'],
+                            $options['topic'],
+                            $options['source'],
+                            $options['source_type'],
+                            $options['processor'],
+                            $options['options']
+                        );
+
+                        $options['processor_service_id'] = $serviceId;
+
+                        if (isset($commands['queueName'])) {
+                            $options['queue'] = $commands['queueName'];
+                        }
+
+                        if (isset($commands['queueNameHardcoded']) && $commands['queueNameHardcoded']) {
+                            $options['prefix_queue'] = false;
+                        }
+
+                        $routeCollection->add(new Route($source, Route::COMMAND, $processor, $options));
+
+                        continue;
+                    }
+
                     if (isset($commands['command'])) {
                         $commands = [$commands];
                     }
diff --git a/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php b/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php
index 3414d49..5188511 100644
--- a/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php
+++ b/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php
@@ -69,6 +69,37 @@ public function process(ContainerBuilder $container): void
                     foreach ($topics as $key => $params) {
                         if (is_string($params)) {
                             $routeCollection->add(new Route($params, Route::TOPIC, $serviceId, ['processor_service_id' => $serviceId]));
+
+                        // 0.8 topic subscriber
+                        } elseif (is_array($params) && is_string($key)) {
+                            @trigger_error('The topic subscriber 0.8 syntax is deprecated since Enqueue 0.9.', E_USER_DEPRECATED);
+
+                            $source = $key;
+                            $processor = $params['processorName'] ?? $serviceId;
+
+                            $options = $params;
+                            unset(
+                                $options['processorName'],
+                                $options['queueName'],
+                                $options['queueNameHardcoded'],
+                                $options['topic'],
+                                $options['source'],
+                                $options['source_type'],
+                                $options['processor'],
+                                $options['options']
+                            );
+
+                            $options['processor_service_id'] = $serviceId;
+
+                            if (isset($params['queueName'])) {
+                                $options['queue'] = $params['queueName'];
+                            }
+
+                            if (isset($params['queueNameHardcoded']) && $params['queueNameHardcoded']) {
+                                $options['prefix_queue'] = false;
+                            }
+
+                            $routeCollection->add(new Route($source, Route::TOPIC, $processor, $options));
                         } elseif (is_array($params)) {
                             $source = $params['topic'] ?? null;
                             $processor = $params['processor'] ?? $serviceId;
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
index 603f0fb..5d53c7b 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
@@ -398,6 +398,48 @@ public function testShouldMergeExtractedRoutesWithAlreadySetInCollection()
         );
     }
 
+    public function testShouldRegister08CommandProcessor()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $processor = $this->createCommandSubscriberProcessor([
+            'processorName' => 'fooCommand',
+            'queueName' => 'a_client_queue_name',
+            'queueNameHardcoded' => true,
+            'exclusive' => true,
+            'anOption' => 'aFooVal',
+        ]);
+
+        $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($processor))
+            ->addTag('enqueue.command_subscriber')
+        ;
+
+        $pass = new BuildCommandSubscriberRoutesPass();
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(1, $routeCollection->getArgument(0));
+
+        $this->assertEquals(
+            [
+                [
+                    'source' => 'fooCommand',
+                    'source_type' => 'enqueue.client.command_route',
+                    'processor' => 'aFooProcessor',
+                    'processor_service_id' => 'aFooProcessor',
+                    'anOption' => 'aFooVal',
+                    'queue' => 'a_client_queue_name',
+                    'prefix_queue' => false,
+                ],
+            ],
+            $routeCollection->getArgument(0)
+        );
+    }
+
     private function createCommandSubscriberProcessor($commandSubscriberReturns = ['aCommand'])
     {
         $processor = new class() implements Processor, CommandSubscriberInterface {
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
index 72f5b21..ecdab5f 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
@@ -358,6 +358,52 @@ public function testShouldMergeExtractedRoutesWithAlreadySetInCollection()
         );
     }
 
+    public function testShouldRegister08TopicSubscriber()
+    {
+        $routeCollection = new Definition(RouteCollection::class);
+        $routeCollection->addArgument([]);
+
+        $processor = $this->createTopicSubscriberProcessor([
+            'fooTopic' => ['processorName' => 'aCustomFooProcessorName', 'queueName' => 'fooQueue', 'queueNameHardcoded' => true, 'anOption' => 'aFooVal'],
+            'barTopic' => ['processorName' => 'aCustomBarProcessorName', 'anOption' => 'aBarVal'],
+        ]);
+
+        $container = new ContainerBuilder();
+        $container->setParameter('enqueue.clients', ['default']);
+        $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
+        $container->register('aFooProcessor', get_class($processor))
+            ->addTag('enqueue.topic_subscriber')
+        ;
+
+        $pass = new BuildTopicSubscriberRoutesPass();
+        $pass->process($container);
+
+        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        $this->assertCount(2, $routeCollection->getArgument(0));
+
+        $this->assertEquals(
+            [
+                [
+                    'source' => 'fooTopic',
+                    'source_type' => 'enqueue.client.topic_route',
+                    'processor' => 'aCustomFooProcessorName',
+                    'processor_service_id' => 'aFooProcessor',
+                    'anOption' => 'aFooVal',
+                    'queue' => 'fooQueue',
+                    'prefix_queue' => false,
+                ],
+                [
+                    'source' => 'barTopic',
+                    'source_type' => 'enqueue.client.topic_route',
+                    'processor' => 'aCustomBarProcessorName',
+                    'processor_service_id' => 'aFooProcessor',
+                    'anOption' => 'aBarVal',
+                ],
+            ],
+            $routeCollection->getArgument(0)
+        );
+    }
+
     private function createTopicSubscriberProcessor($topicSubscriberReturns = ['aTopic'])
     {
         $processor = new class() implements Processor, TopicSubscriberInterface {

From 071586568cc53c84a6e3c1d833b32acddecc1328 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 16 Nov 2018 20:00:31 +0200
Subject: [PATCH 213/337] fix tests.

---
 .../DependencyInjection/BuildCommandSubscriberRoutesPassTest.php | 1 +
 .../DependencyInjection/BuildTopicSubscriberRoutesPassTest.php   | 1 +
 2 files changed, 2 insertions(+)

diff --git a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
index 5d53c7b..346c0cb 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
@@ -413,6 +413,7 @@ public function testShouldRegister08CommandProcessor()
 
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.clients', ['default']);
+        $container->setParameter('enqueue.default_client', 'default');
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.command_subscriber')
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
index ecdab5f..65b64dc 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
@@ -370,6 +370,7 @@ public function testShouldRegister08TopicSubscriber()
 
         $container = new ContainerBuilder();
         $container->setParameter('enqueue.clients', ['default']);
+        $container->setParameter('enqueue.default_client', 'default');
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
         $container->register('aFooProcessor', get_class($processor))
             ->addTag('enqueue.topic_subscriber')

From 443a6af35e904265b73f64f607b0fb0ace530735 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Sat, 17 Nov 2018 16:22:43 +0200
Subject: [PATCH 214/337] [dsn] Cluster DSN

---
 Client/DriverFactory.php     | 2 +-
 ConnectionFactoryFactory.php | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Client/DriverFactory.php b/Client/DriverFactory.php
index 36534d5..60fda51 100644
--- a/Client/DriverFactory.php
+++ b/Client/DriverFactory.php
@@ -30,7 +30,7 @@ public function __construct(Config $config, RouteCollection $routeCollection)
 
     public function create(ConnectionFactory $factory, string $dsn, array $config): DriverInterface
     {
-        $dsn = new Dsn($dsn);
+        $dsn = Dsn::parseFirst($dsn);
 
         if ($driverInfo = $this->findDriverInfo($dsn, Resources::getAvailableDrivers())) {
             $driverClass = $driverInfo['driverClass'];
diff --git a/ConnectionFactoryFactory.php b/ConnectionFactoryFactory.php
index 48c7f3d..d89c671 100644
--- a/ConnectionFactoryFactory.php
+++ b/ConnectionFactoryFactory.php
@@ -21,7 +21,7 @@ public function create($config): ConnectionFactory
             throw new \InvalidArgumentException('The config must have dsn key set.');
         }
 
-        $dsn = new Dsn($config['dsn']);
+        $dsn = Dsn::parseFirst($config['dsn']);
 
         if ($factoryClass = $this->findFactoryClass($dsn, Resources::getAvailableConnections())) {
             return new $factoryClass(1 === count($config) ? $config['dsn'] : $config);

From 5d01b3ef86ef6e0845c25dcf07657183fe7848cf Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Mon, 19 Nov 2018 11:45:42 +0200
Subject: [PATCH 215/337] remove unrelated changes.

---
 Client/Producer.php | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/Client/Producer.php b/Client/Producer.php
index 3d73cd8..6ea612b 100644
--- a/Client/Producer.php
+++ b/Client/Producer.php
@@ -9,12 +9,6 @@
 
 final class Producer implements ProducerInterface
 {
-    /**
-     * compatibility with 0.9x.
-     */
-    const TOPIC_09X = 'enqueue.topic';
-    const COMMAND_09X = 'enqueue.command';
-
     /**
      * @var DriverInterface
      */

From 2b2da144bcdd8c828d84cd9c3daabcfe3d1a8bca Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Wed, 21 Nov 2018 13:55:20 +0200
Subject: [PATCH 216/337] Rework DriverFactory, add separator option to Client
 Config.

---
 Client/Config.php                             | 49 +++++++++++----
 Client/DriverFactory.php                      | 30 ++++------
 Client/DriverFactoryInterface.php             |  2 +-
 .../DependencyInjection/ClientFactory.php     | 26 ++++----
 Tests/Client/ConfigTest.php                   | 60 +++++++++++++++----
 .../SetRouterPropertiesExtensionTest.php      |  2 +-
 .../Client/Driver/GenericDriverTestsTrait.php |  8 +++
 .../Client/Driver/RabbitMqStompDriverTest.php |  7 +++
 Tests/Client/DriverFactoryTest.php            | 35 +++++++----
 Tests/Client/ProducerSendCommandTest.php      |  5 +-
 Tests/Client/ProducerSendEventTest.php        |  5 +-
 .../DependencyInjection/ClientFactoryTest.php |  4 +-
 12 files changed, 164 insertions(+), 69 deletions(-)

diff --git a/Client/Config.php b/Client/Config.php
index ef68fb6..8883842 100644
--- a/Client/Config.php
+++ b/Client/Config.php
@@ -52,8 +52,22 @@ class Config
      */
     private $transportConfig;
 
-    public function __construct(string $prefix, string $app, string $routerTopic, string $routerQueue, string $defaultQueue, string $routerProcessor, array $transportConfig = [])
-    {
+    /**
+     * @var array
+     */
+    private $driverConfig;
+
+    public function __construct(
+        string $prefix,
+        string $separator,
+        string $app,
+        string $routerTopic,
+        string $routerQueue,
+        string $defaultQueue,
+        string $routerProcessor,
+        array $transportConfig,
+        array $driverConfig
+    ) {
         $this->prefix = trim($prefix);
         $this->app = trim($app);
 
@@ -78,8 +92,9 @@ public function __construct(string $prefix, string $app, string $routerTopic, st
         }
 
         $this->transportConfig = $transportConfig;
+        $this->driverConfig = $driverConfig;
 
-        $this->separator = '.';
+        $this->separator = $separator;
     }
 
     public function getPrefix(): string
@@ -117,33 +132,47 @@ public function getRouterProcessor(): string
         return $this->routerProcessor;
     }
 
-    /**
-     * @deprecated
-     *
-     * @param null|mixed $default
-     */
     public function getTransportOption(string $name, $default = null)
     {
         return array_key_exists($name, $this->transportConfig) ? $this->transportConfig[$name] : $default;
     }
 
+    public function getTransportOptions(): array
+    {
+        return $this->transportConfig;
+    }
+
+    public function getDriverOption(string $name, $default = null)
+    {
+        return array_key_exists($name, $this->driverConfig) ? $this->driverConfig[$name] : $default;
+    }
+
+    public function getDriverOptions(): array
+    {
+        return $this->driverConfig;
+    }
+
     public static function create(
         string $prefix = null,
+        string $separator = null,
         string $app = null,
         string $routerTopic = null,
         string $routerQueue = null,
         string $defaultQueue = null,
         string $routerProcessor = null,
-        array $transportConfig = []
+        array $transportConfig = [],
+        array $driverConfig = []
     ): self {
         return new static(
             $prefix ?: '',
+            $separator ?: '.',
             $app ?: '',
             $routerTopic ?: 'router',
             $routerQueue ?: 'default',
             $defaultQueue ?: 'default',
             $routerProcessor ?: 'router',
-            $transportConfig
+            $transportConfig,
+            $driverConfig
         );
     }
 }
diff --git a/Client/DriverFactory.php b/Client/DriverFactory.php
index 60fda51..48433fb 100644
--- a/Client/DriverFactory.php
+++ b/Client/DriverFactory.php
@@ -12,24 +12,14 @@
 
 final class DriverFactory implements DriverFactoryInterface
 {
-    /**
-     * @var Config
-     */
-    private $config;
-
-    /**
-     * @var RouteCollection
-     */
-    private $routeCollection;
-
-    public function __construct(Config $config, RouteCollection $routeCollection)
+    public function create(ConnectionFactory $factory, Config $config, RouteCollection $collection): DriverInterface
     {
-        $this->config = $config;
-        $this->routeCollection = $routeCollection;
-    }
+        $dsn = $config->getTransportOption('dsn');
+
+        if (empty($dsn)) {
+            throw new \LogicException('This driver factory relies on dsn option from transport config. The option is empty or not set.');
+        }
 
-    public function create(ConnectionFactory $factory, string $dsn, array $config): DriverInterface
-    {
         $dsn = Dsn::parseFirst($dsn);
 
         if ($driverInfo = $this->findDriverInfo($dsn, Resources::getAvailableDrivers())) {
@@ -44,7 +34,7 @@ public function create(ConnectionFactory $factory, string $dsn, array $config):
                     ));
                 }
 
-                return new RabbitMqDriver($factory->createContext(), $this->config, $this->routeCollection);
+                return new RabbitMqDriver($factory->createContext(), $config, $collection);
             }
 
             if (RabbitMqStompDriver::class === $driverClass) {
@@ -59,15 +49,15 @@ public function create(ConnectionFactory $factory, string $dsn, array $config):
                 $managementClient = StompManagementClient::create(
                     ltrim($dsn->getPath(), '/'),
                     $dsn->getHost() ?: 'localhost',
-                    $config['management_plugin_port'] ?? 15672,
+                    $config->getDriverOption('management_plugin_port', 15672),
                     (string) $dsn->getUser(),
                     (string) $dsn->getPassword()
                 );
 
-                return new RabbitMqStompDriver($factory->createContext(), $this->config, $this->routeCollection, $managementClient);
+                return new RabbitMqStompDriver($factory->createContext(), $config, $collection, $managementClient);
             }
 
-            return new $driverClass($factory->createContext(), $this->config, $this->routeCollection);
+            return new $driverClass($factory->createContext(), $config, $collection);
         }
 
         $knownDrivers = Resources::getKnownDrivers();
diff --git a/Client/DriverFactoryInterface.php b/Client/DriverFactoryInterface.php
index 6a558b7..698ad05 100644
--- a/Client/DriverFactoryInterface.php
+++ b/Client/DriverFactoryInterface.php
@@ -6,5 +6,5 @@
 
 interface DriverFactoryInterface
 {
-    public function create(ConnectionFactory $factory, string $dsn, array $config): DriverInterface;
+    public function create(ConnectionFactory $factory, Config $config, RouteCollection $collection): DriverInterface;
 }
diff --git a/Symfony/Client/DependencyInjection/ClientFactory.php b/Symfony/Client/DependencyInjection/ClientFactory.php
index 0acf659..e69311e 100644
--- a/Symfony/Client/DependencyInjection/ClientFactory.php
+++ b/Symfony/Client/DependencyInjection/ClientFactory.php
@@ -65,13 +65,19 @@ public static function getConfiguration(bool $debug, string $name = 'client'): N
         $builder->children()
             ->booleanNode('traceable_producer')->defaultValue($debug)->end()
             ->scalarNode('prefix')->defaultValue('enqueue')->end()
+            ->scalarNode('separator')->defaultValue('.')->end()
             ->scalarNode('app_name')->defaultValue('app')->end()
             ->scalarNode('router_topic')->defaultValue('default')->cannotBeEmpty()->end()
             ->scalarNode('router_queue')->defaultValue('default')->cannotBeEmpty()->end()
             ->scalarNode('router_processor')->defaultNull()->end()
-            ->scalarNode('default_processor_queue')->defaultValue('default')->cannotBeEmpty()->end()
             ->integerNode('redelivered_delay_time')->min(0)->defaultValue(0)->end()
-        ->end()->end()
+            ->scalarNode('default_queue')->defaultValue('default')->cannotBeEmpty()->end()
+            ->arrayNode('driver_options')
+                ->addDefaultsIfNotSet()
+                ->info('The array contains driver specific options')
+                ->ignoreExtraKeys(false)
+            ->end()
+            ->end()->end()
         ;
 
         return $builder;
@@ -83,10 +89,7 @@ public function build(ContainerBuilder $container, array $config): void
             ->setFactory([$this->diUtils->reference('driver'), 'getContext'])
         ;
 
-        $container->register($this->diUtils->format('driver_factory'), DriverFactory::class)
-            ->addArgument($this->diUtils->reference('config'))
-            ->addArgument($this->diUtils->reference('route_collection'))
-        ;
+        $container->register($this->diUtils->format('driver_factory'), DriverFactory::class);
 
         $routerProcessor = empty($config['router_processor'])
             ? $this->diUtils->format('router_processor')
@@ -96,18 +99,19 @@ public function build(ContainerBuilder $container, array $config): void
         $container->register($this->diUtils->format('config'), Config::class)
             ->setArguments([
                 $config['prefix'],
+                $config['separator'],
                 $config['app_name'],
                 $config['router_topic'],
                 $config['router_queue'],
-                $config['default_processor_queue'],
+                $config['default_queue'],
                 $routerProcessor,
-                // @todo should be driver options.
                 $config['transport'],
+                $config['driver_options'] ?? [],
             ]);
 
         $container->setParameter($this->diUtils->format('router_processor'), $routerProcessor);
         $container->setParameter($this->diUtils->format('router_queue_name'), $config['router_queue']);
-        $container->setParameter($this->diUtils->format('default_queue_name'), $config['default_processor_queue']);
+        $container->setParameter($this->diUtils->format('default_queue_name'), $config['default_queue']);
 
         $container->register($this->diUtils->format('route_collection'), RouteCollection::class)
             ->addArgument([])
@@ -220,8 +224,8 @@ public function createDriver(ContainerBuilder $container, array $config): string
         $container->register($driverId, DriverInterface::class)
             ->setFactory([new Reference($driverFactoryId), 'create'])
             ->addArgument(new Reference($factoryId))
-            ->addArgument($config['dsn'])
-            ->addArgument($config)
+            ->addArgument($this->diUtils->reference('config'))
+            ->addArgument($this->diUtils->reference('route_collection'))
         ;
 
         if ($this->default) {
diff --git a/Tests/Client/ConfigTest.php b/Tests/Client/ConfigTest.php
index 8e54508..09b80e2 100644
--- a/Tests/Client/ConfigTest.php
+++ b/Tests/Client/ConfigTest.php
@@ -11,11 +11,14 @@ public function testShouldReturnPrefixSetInConstructor()
     {
         $config = new Config(
             'thePrefix',
+            'theSeparator',
             'aApp',
             'aRouterTopicName',
             'aRouterQueueName',
             'aDefaultQueueName',
-            'aRouterProcessorName'
+            'aRouterProcessorName',
+            [],
+            []
         );
 
         $this->assertEquals('thePrefix', $config->getPrefix());
@@ -29,10 +32,13 @@ public function testShouldTrimReturnPrefixSetInConstructor(string $empty)
         $config = new Config(
             $empty,
             'aApp',
+            'theSeparator',
             'aRouterTopicName',
             'aRouterQueueName',
             'aDefaultQueueName',
-            'aRouterProcessorName'
+            'aRouterProcessorName',
+            [],
+            []
         );
 
         $this->assertSame('', $config->getPrefix());
@@ -42,11 +48,14 @@ public function testShouldReturnAppNameSetInConstructor()
     {
         $config = new Config(
             'aPrefix',
+            'theSeparator',
             'theApp',
             'aRouterTopicName',
             'aRouterQueueName',
             'aDefaultQueueName',
-            'aRouterProcessorName'
+            'aRouterProcessorName',
+            [],
+            []
         );
 
         $this->assertEquals('theApp', $config->getApp());
@@ -59,11 +68,14 @@ public function testShouldTrimReturnAppNameSetInConstructor(string $empty)
     {
         $config = new Config(
             'aPrefix',
+            'theSeparator',
             $empty,
             'aRouterTopicName',
             'aRouterQueueName',
             'aDefaultQueueName',
-            'aRouterProcessorName'
+            'aRouterProcessorName',
+            [],
+            []
         );
 
         $this->assertSame('', $config->getApp());
@@ -73,11 +85,14 @@ public function testShouldReturnRouterProcessorNameSetInConstructor()
     {
         $config = new Config(
             'aPrefix',
+            'theSeparator',
             'aApp',
             'aRouterTopicName',
             'aRouterQueueName',
             'aDefaultQueueName',
-            'aRouterProcessorName'
+            'aRouterProcessorName',
+            [],
+            []
         );
 
         $this->assertEquals('aRouterProcessorName', $config->getRouterProcessor());
@@ -87,11 +102,14 @@ public function testShouldReturnRouterTopicNameSetInConstructor()
     {
         $config = new Config(
             'aPrefix',
+            'theSeparator',
             'aApp',
             'aRouterTopicName',
             'aRouterQueueName',
             'aDefaultQueueName',
-            'aRouterProcessorName'
+            'aRouterProcessorName',
+            [],
+            []
         );
 
         $this->assertEquals('aRouterTopicName', $config->getRouterTopic());
@@ -101,11 +119,14 @@ public function testShouldReturnRouterQueueNameSetInConstructor()
     {
         $config = new Config(
             'aPrefix',
+            'theSeparator',
             'aApp',
             'aRouterTopicName',
             'aRouterQueueName',
             'aDefaultQueueName',
-            'aRouterProcessorName'
+            'aRouterProcessorName',
+            [],
+            []
         );
 
         $this->assertEquals('aRouterQueueName', $config->getRouterQueue());
@@ -115,11 +136,14 @@ public function testShouldReturnDefaultQueueNameSetInConstructor()
     {
         $config = new Config(
             'aPrefix',
+            'theSeparator',
             'aApp',
             'aRouterTopicName',
             'aRouterQueueName',
             'aDefaultQueueName',
-            'aRouterProcessorName'
+            'aRouterProcessorName',
+            [],
+            []
         );
 
         $this->assertEquals('aDefaultQueueName', $config->getDefaultQueue());
@@ -143,12 +167,15 @@ public function testThrowIfRouterTopicNameIsEmpty(string $empty)
         $this->expectException(\InvalidArgumentException::class);
         $this->expectExceptionMessage('Router topic is empty.');
         new Config(
+            '',
             '',
             '',
             $empty,
             'aRouterQueueName',
             'aDefaultQueueName',
-            'aRouterProcessorName'
+            'aRouterProcessorName',
+            [],
+            []
         );
     }
 
@@ -160,12 +187,15 @@ public function testThrowIfRouterQueueNameIsEmpty(string $empty)
         $this->expectException(\InvalidArgumentException::class);
         $this->expectExceptionMessage('Router queue is empty.');
         new Config(
+            '',
             '',
             '',
             'aRouterTopicName',
             $empty,
             'aDefaultQueueName',
-            'aRouterProcessorName'
+            'aRouterProcessorName',
+            [],
+            []
         );
     }
 
@@ -177,12 +207,15 @@ public function testThrowIfDefaultQueueNameIsEmpty(string $empty)
         $this->expectException(\InvalidArgumentException::class);
         $this->expectExceptionMessage('Default processor queue name is empty.');
         new Config(
+            '',
             '',
             '',
             'aRouterTopicName',
             'aRouterQueueName',
             $empty,
-            'aRouterProcessorName'
+            'aRouterProcessorName',
+            [],
+            []
         );
     }
 
@@ -194,12 +227,15 @@ public function testThrowIfRouterProcessorNameIsEmpty(string $empty)
         $this->expectException(\InvalidArgumentException::class);
         $this->expectExceptionMessage('Router processor name is empty.');
         new Config(
+            '',
             '',
             '',
             'aRouterTopicName',
             'aRouterQueueName',
             'aDefaultQueueName',
-            $empty
+            $empty,
+            [],
+            []
         );
     }
 
diff --git a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
index be5609d..4cd214e 100644
--- a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
@@ -32,7 +32,7 @@ public function testCouldBeConstructedWithRequiredArguments()
 
     public function testShouldSetRouterProcessorPropertyIfNotSetAndOnRouterQueue()
     {
-        $config = Config::create('test', '', '', 'router-queue', '', 'router-processor-name');
+        $config = Config::create('test', '.', '', '', 'router-queue', '', 'router-processor-name');
         $queue = new NullQueue('test.router-queue');
 
         $driver = $this->createDriverMock();
diff --git a/Tests/Client/Driver/GenericDriverTestsTrait.php b/Tests/Client/Driver/GenericDriverTestsTrait.php
index 2d39018..b4c23dd 100644
--- a/Tests/Client/Driver/GenericDriverTestsTrait.php
+++ b/Tests/Client/Driver/GenericDriverTestsTrait.php
@@ -70,11 +70,13 @@ public function testShouldCreateAndReturnQueueInstanceWithPrefixAndAppName()
 
         $config = new Config(
             'aPrefix',
+            '.',
             'anAppName',
             'aRouterTopicName',
             'aRouterQueueName',
             'aDefaultQueue',
             'aRouterProcessor',
+            [],
             []
         );
 
@@ -99,11 +101,13 @@ public function testShouldCreateAndReturnQueueInstanceWithPrefixWithoutAppName()
 
         $config = new Config(
             'aPrefix',
+            '.',
             '',
             'aRouterTopicName',
             'aRouterQueueName',
             'aDefaultQueue',
             'aRouterProcessor',
+            [],
             []
         );
 
@@ -128,11 +132,13 @@ public function testShouldCreateAndReturnQueueInstanceWithAppNameAndWithoutPrefi
 
         $config = new Config(
             '',
+            '.',
             'anAppName',
             'aRouterTopicName',
             'aRouterQueueName',
             'aDefaultQueue',
             'aRouterProcessor',
+            [],
             []
         );
 
@@ -157,11 +163,13 @@ public function testShouldCreateAndReturnQueueInstanceWithoutPrefixAndAppName()
 
         $config = new Config(
             '',
+            '.',
             '',
             'aRouterTopicName',
             'aRouterQueueName',
             'aDefaultQueue',
             'aRouterProcessor',
+            [],
             []
         );
 
diff --git a/Tests/Client/Driver/RabbitMqStompDriverTest.php b/Tests/Client/Driver/RabbitMqStompDriverTest.php
index 5e6dc24..1378dd3 100644
--- a/Tests/Client/Driver/RabbitMqStompDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqStompDriverTest.php
@@ -121,6 +121,7 @@ public function testThrowIfDelayIsSetButDelayPluginInstalledOptionIsFalse()
 
         $config = Config::create(
             'aPrefix',
+            '.',
             '',
             null,
             null,
@@ -156,6 +157,7 @@ public function testShouldSetXDelayHeaderIfDelayPluginInstalledOptionIsTrue()
 
         $config = Config::create(
             'aPrefix',
+            '.',
             '',
             null,
             null,
@@ -232,6 +234,7 @@ public function shouldSendMessageToDelayExchangeIfDelaySet()
 
         $config = Config::create(
             'aPrefix',
+            '.',
             '',
             null,
             null,
@@ -261,6 +264,7 @@ public function testShouldNotSetupBrokerIfManagementPluginInstalledOptionIsNotEn
     {
         $config = Config::create(
             'aPrefix',
+            '.',
             '',
             null,
             null,
@@ -345,6 +349,7 @@ public function testShouldSetupBroker()
 
         $config = Config::create(
             'aPrefix',
+            '.',
             '',
             null,
             null,
@@ -412,6 +417,7 @@ public function testSetupBrokerShouldCreateDelayExchangeIfEnabled()
 
         $config = Config::create(
             'aPrefix',
+            '.',
             '',
             null,
             null,
@@ -555,6 +561,7 @@ protected function createDummyConfig(): Config
     {
         return Config::create(
             'aPrefix',
+            '.',
             '',
             null,
             null,
diff --git a/Tests/Client/DriverFactoryTest.php b/Tests/Client/DriverFactoryTest.php
index c4f1ffb..cce6311 100644
--- a/Tests/Client/DriverFactoryTest.php
+++ b/Tests/Client/DriverFactoryTest.php
@@ -61,9 +61,9 @@ public function testShouldBeFinal()
         $this->assertTrue($rc->isFinal());
     }
 
-    public function testCouldBeConstructedWithConfigAndRouteCollectionAsArguments()
+    public function testCouldBeConstructedWithoutAnyArguments()
     {
-        new DriverFactory($this->createConfigMock(), new RouteCollection([]));
+        new DriverFactory();
     }
 
     public function testThrowIfPackageThatSupportSchemeNotInstalled()
@@ -75,9 +75,9 @@ public function testThrowIfPackageThatSupportSchemeNotInstalled()
 
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('To use given scheme "scheme5b7aa7d7cd213" a package has to be installed. Run "composer req thePackage theOtherPackage" to add it.');
-        $factory = new DriverFactory($this->createConfigMock(), new RouteCollection([]));
+        $factory = new DriverFactory();
 
-        $factory->create($this->createConnectionFactoryMock(), $scheme.'://foo', []);
+        $factory->create($this->createConnectionFactoryMock(), $this->createDummyConfig($scheme.'://foo'), new RouteCollection([]));
     }
 
     public function testThrowIfSchemeIsNotKnown()
@@ -87,9 +87,9 @@ public function testThrowIfSchemeIsNotKnown()
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('A given scheme "scheme5b7aa862e70a5" is not supported. Maybe it is a custom driver, make sure you registered it with "Enqueue\Client\Resources::addDriver".');
 
-        $factory = new DriverFactory($this->createConfigMock(), new RouteCollection([]));
+        $factory = new DriverFactory();
 
-        $factory->create($this->createConnectionFactoryMock(), $scheme.'://foo', []);
+        $factory->create($this->createConnectionFactoryMock(), $this->createDummyConfig($scheme.'://foo'), new RouteCollection([]));
     }
 
     public function testThrowIfDsnInvalid()
@@ -97,9 +97,9 @@ public function testThrowIfDsnInvalid()
         $this->expectException(\LogicException::class);
         $this->expectExceptionMessage('The DSN is invalid. It does not have scheme separator ":".');
 
-        $factory = new DriverFactory($this->createConfigMock(), new RouteCollection([]));
+        $factory = new DriverFactory();
 
-        $factory->create($this->createConnectionFactoryMock(), 'invalidDsn', []);
+        $factory->create($this->createConnectionFactoryMock(), $this->createDummyConfig('invalidDsn'), new RouteCollection([]));
     }
 
     /**
@@ -119,9 +119,9 @@ public function testReturnsExpectedFactories(
             ->willReturn($this->createMock($contextClass))
         ;
 
-        $driverFactory = new DriverFactory($this->createConfigMock(), new RouteCollection([]));
+        $driverFactory = new DriverFactory();
 
-        $driver = $driverFactory->create($connectionFactoryMock, $dsn, $conifg);
+        $driver = $driverFactory->create($connectionFactoryMock, $this->createDummyConfig($dsn), new RouteCollection([]));
 
         $this->assertInstanceOf($expectedDriverClass, $driver);
     }
@@ -164,6 +164,21 @@ public static function provideDSN()
         yield ['beanstalk:', PheanstalkConnectionFactory::class, PheanstalkContext::class, [], GenericDriver::class];
     }
 
+    private function createDummyConfig(string $dsn): Config
+    {
+        return Config::create(
+            null,
+            null,
+            null,
+            null,
+            null,
+            null,
+            null,
+            ['dsn' => $dsn],
+            []
+        );
+    }
+
     private function createConnectionFactoryMock(): ConnectionFactory
     {
         return $this->createMock(ConnectionFactory::class);
diff --git a/Tests/Client/ProducerSendCommandTest.php b/Tests/Client/ProducerSendCommandTest.php
index 0fecbdf..3469c38 100644
--- a/Tests/Client/ProducerSendCommandTest.php
+++ b/Tests/Client/ProducerSendCommandTest.php
@@ -507,11 +507,14 @@ private function createDriverStub(): DriverInterface
     {
         $config = new Config(
             'a_prefix',
+            '.',
             'an_app',
             'a_router_topic',
             'a_router_queue',
             'a_default_processor_queue',
-            'a_router_processor_name'
+            'a_router_processor_name',
+            [],
+            []
         );
 
         $driverMock = $this->createMock(DriverInterface::class);
diff --git a/Tests/Client/ProducerSendEventTest.php b/Tests/Client/ProducerSendEventTest.php
index de07d55..1346afe 100644
--- a/Tests/Client/ProducerSendEventTest.php
+++ b/Tests/Client/ProducerSendEventTest.php
@@ -527,11 +527,14 @@ private function createDriverStub(): DriverInterface
     {
         $config = new Config(
             'a_prefix',
+            '.',
             'an_app',
             'a_router_topic',
             'a_router_queue',
             'a_default_processor_queue',
-            'a_router_processor_name'
+            'a_router_processor_name',
+            [],
+            []
         );
 
         $driverMock = $this->createMock(DriverInterface::class);
diff --git a/Tests/Symfony/Client/DependencyInjection/ClientFactoryTest.php b/Tests/Symfony/Client/DependencyInjection/ClientFactoryTest.php
index cac1805..9f37dff 100644
--- a/Tests/Symfony/Client/DependencyInjection/ClientFactoryTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/ClientFactoryTest.php
@@ -45,8 +45,8 @@ public function testShouldCreateDriverFromDsn()
         $this->assertEquals(
             [
                 new Reference('enqueue.transport.default.connection_factory'),
-                'foo://bar/baz',
-                ['dsn' => 'foo://bar/baz', 'foo' => 'fooVal'],
+                new Reference('enqueue.client.default.config'),
+                new Reference('enqueue.client.default.route_collection'),
             ],
             $container->getDefinition('enqueue.client.default.driver')->getArguments())
         ;

From a5083ee2c05d72544d3a93bf8eae34b4c72f1079 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Wed, 21 Nov 2018 16:41:53 +0200
Subject: [PATCH 217/337] [client] update rabbitmq stomp driver configuration

---
 Client/DriverFactory.php | 49 ++++++++++++++--------------------------
 1 file changed, 17 insertions(+), 32 deletions(-)

diff --git a/Client/DriverFactory.php b/Client/DriverFactory.php
index 48433fb..0dd9412 100644
--- a/Client/DriverFactory.php
+++ b/Client/DriverFactory.php
@@ -2,12 +2,9 @@
 
 namespace Enqueue\Client;
 
-use Enqueue\Client\Driver\RabbitMqDriver;
 use Enqueue\Client\Driver\RabbitMqStompDriver;
 use Enqueue\Client\Driver\StompManagementClient;
 use Enqueue\Dsn\Dsn;
-use Enqueue\Stomp\StompConnectionFactory;
-use Interop\Amqp\AmqpConnectionFactory;
 use Interop\Queue\ConnectionFactory;
 
 final class DriverFactory implements DriverFactoryInterface
@@ -25,36 +22,8 @@ public function create(ConnectionFactory $factory, Config $config, RouteCollecti
         if ($driverInfo = $this->findDriverInfo($dsn, Resources::getAvailableDrivers())) {
             $driverClass = $driverInfo['driverClass'];
 
-            if (RabbitMqDriver::class === $driverClass) {
-                if (false == $factory instanceof AmqpConnectionFactory) {
-                    throw new \LogicException(sprintf(
-                        'The factory must be instance of "%s", got "%s"',
-                        AmqpConnectionFactory::class,
-                        get_class($factory)
-                    ));
-                }
-
-                return new RabbitMqDriver($factory->createContext(), $config, $collection);
-            }
-
             if (RabbitMqStompDriver::class === $driverClass) {
-                if (false == $factory instanceof StompConnectionFactory) {
-                    throw new \LogicException(sprintf(
-                        'The factory must be instance of "%s", got "%s"',
-                        StompConnectionFactory::class,
-                        get_class($factory)
-                    ));
-                }
-
-                $managementClient = StompManagementClient::create(
-                    ltrim($dsn->getPath(), '/'),
-                    $dsn->getHost() ?: 'localhost',
-                    $config->getDriverOption('management_plugin_port', 15672),
-                    (string) $dsn->getUser(),
-                    (string) $dsn->getPassword()
-                );
-
-                return new RabbitMqStompDriver($factory->createContext(), $config, $collection, $managementClient);
+                return $this->createRabbitMqStompDriver($factory, $dsn, $config, $collection);
             }
 
             return new $driverClass($factory->createContext(), $config, $collection);
@@ -111,4 +80,20 @@ private function findDriverInfo(Dsn $dsn, array $factories): ?array
 
         return null;
     }
+
+    private function createRabbitMqStompDriver(ConnectionFactory $factory, Dsn $dsn, Config $config, RouteCollection $collection): RabbitMqStompDriver
+    {
+        $defaultManagementHost = $dsn->getHost() ?: $config->getTransportOption('host', 'localhost');
+        $managementVast = ltrim($dsn->getPath(), '/') ?: $config->getTransportOption('vhost', '/');
+
+        $managementClient = StompManagementClient::create(
+            urldecode($managementVast),
+            $config->getDriverOption('rabbitmq_management_host', $defaultManagementHost),
+            $config->getDriverOption('rabbitmq_management_port', 15672),
+            (string) $dsn->getUser() ?: $config->getTransportOption('user', 'guest'),
+            (string) $dsn->getPassword() ?: $config->getTransportOption('pass', 'guest')
+        );
+
+        return new RabbitMqStompDriver($factory->createContext(), $config, $collection, $managementClient);
+    }
 }

From af9c5d09462a0b26310561f807eb64a2ed61ab1f Mon Sep 17 00:00:00 2001
From: webmake <nyrkao@gmail.com>
Date: Sat, 27 Oct 2018 13:56:55 +0300
Subject: [PATCH 218/337] Do not export non source files

(cherry picked from commit 404fae1)
---
 .gitattributes | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100644 .gitattributes

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..bdf2dcb
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,5 @@
+/Tests export-ignore
+.gitattributes export-ignore
+.gitignore export-ignore
+.travis.yml export-ignore
+phpunit.xml.dist export-ignore

From f3602e7088d7269c5abd2a812338240a9ed4932d Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 23 Nov 2018 10:00:43 +0200
Subject: [PATCH 219/337] [redis][client] Add rediss scheme

fixes https://github.com/php-enqueue/enqueue-dev/issues/648
---
 Client/Resources.php | 2 +-
 Resources.php        | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Client/Resources.php b/Client/Resources.php
index cdc8039..b88b822 100644
--- a/Client/Resources.php
+++ b/Client/Resources.php
@@ -81,7 +81,7 @@ public static function getKnownDrivers(): array
                 'packages' => ['enqueue/enqueue', 'enqueue/gps'],
             ];
             $map[] = [
-                'schemes' => ['redis'],
+                'schemes' => ['redis', 'rediss'],
                 'driverClass' => RedisDriver::class,
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/redis'],
diff --git a/Resources.php b/Resources.php
index 508dc26..a3d94da 100644
--- a/Resources.php
+++ b/Resources.php
@@ -143,7 +143,7 @@ public static function getKnownConnections(): array
                 'package' => 'enqueue/rdkafka',
             ];
             $map[RedisConnectionFactory::class] = [
-                'schemes' => ['redis'],
+                'schemes' => ['redis', 'rediss'],
                 'supportedSchemeExtensions' => ['predis', 'phpredis'],
                 'package' => 'enqueue/redis',
             ];

From 4b6a350c40f4b5af9fbc892d873a1d6827996737 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 23 Nov 2018 10:40:55 +0200
Subject: [PATCH 220/337] fix tests.

---
 Tests/ResourcesTest.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Tests/ResourcesTest.php b/Tests/ResourcesTest.php
index ed36236..214a083 100644
--- a/Tests/ResourcesTest.php
+++ b/Tests/ResourcesTest.php
@@ -33,7 +33,7 @@ public function testShouldGetAvailableConnectionsInExpectedFormat()
 
         $connectionInfo = $availableConnections[RedisConnectionFactory::class];
         $this->assertArrayHasKey('schemes', $connectionInfo);
-        $this->assertSame(['redis'], $connectionInfo['schemes']);
+        $this->assertSame(['redis', 'rediss'], $connectionInfo['schemes']);
 
         $this->assertArrayHasKey('supportedSchemeExtensions', $connectionInfo);
         $this->assertSame(['predis', 'phpredis'], $connectionInfo['supportedSchemeExtensions']);
@@ -51,7 +51,7 @@ public function testShouldGetKnownConnectionsInExpectedFormat()
 
         $connectionInfo = $availableConnections[RedisConnectionFactory::class];
         $this->assertArrayHasKey('schemes', $connectionInfo);
-        $this->assertSame(['redis'], $connectionInfo['schemes']);
+        $this->assertSame(['redis', 'rediss'], $connectionInfo['schemes']);
 
         $this->assertArrayHasKey('supportedSchemeExtensions', $connectionInfo);
         $this->assertSame(['predis', 'phpredis'], $connectionInfo['supportedSchemeExtensions']);

From fd8e0c8afa3ebc5c367d884d8b408eb34fc6aad1 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 27 Nov 2018 19:54:18 +0200
Subject: [PATCH 221/337] Allow installing stable dependencies.

---
 composer.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/composer.json b/composer.json
index 31b2159..26d1477 100644
--- a/composer.json
+++ b/composer.json
@@ -9,8 +9,8 @@
         "php": "^7.1.3",
         "queue-interop/amqp-interop": "^0.8",
         "queue-interop/queue-interop": "^0.7",
-        "enqueue/null": "0.9.x-dev",
-        "enqueue/dsn": "0.9.x-dev",
+        "enqueue/null": "^0.9",
+        "enqueue/dsn": "^0.9",
         "ramsey/uuid": "^2|^3.5",
         "psr/log": "^1",
         "psr/container": "^1"

From e2caf8f421158f255940bde63c0da8a1d221ebc0 Mon Sep 17 00:00:00 2001
From: Kotlyar Maksim <kotlyar.maksim@gmail.com>
Date: Thu, 13 Dec 2018 17:26:05 +0200
Subject: [PATCH 222/337] Allow Queue Interop 0.8 version (wihtout Psr prefixed
 interfaces)

---
 Tests/Symfony/ContainerProcessorRegistryTest.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/Symfony/ContainerProcessorRegistryTest.php b/Tests/Symfony/ContainerProcessorRegistryTest.php
index fe84c2e..bb3cacb 100644
--- a/Tests/Symfony/ContainerProcessorRegistryTest.php
+++ b/Tests/Symfony/ContainerProcessorRegistryTest.php
@@ -69,7 +69,7 @@ public function testThrowErrorIfServiceDoesNotImplementProcessorReturnType()
         $registry = new ContainerProcessorRegistry($containerMock);
 
         $this->expectException(\TypeError::class);
-        $this->expectExceptionMessage('Return value of Enqueue\Symfony\ContainerProcessorRegistry::get() must implement interface Interop\Queue\PsrProcessor, instance of stdClass returned');
+        $this->expectExceptionMessage('Return value of Enqueue\Symfony\ContainerProcessorRegistry::get() must implement interface Interop\Queue\Processor, instance of stdClass returned');
         $registry->get('processor-name');
     }
 

From b552e850c4b6248484b24e4453b4365c1d2332e0 Mon Sep 17 00:00:00 2001
From: Kotlyar Maksim <kotlyar.maksim@gmail.com>
Date: Thu, 13 Dec 2018 17:31:01 +0200
Subject: [PATCH 223/337] Allow queue interop 0.8 in packages

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 26d1477..05dd1df 100644
--- a/composer.json
+++ b/composer.json
@@ -8,7 +8,7 @@
     "require": {
         "php": "^7.1.3",
         "queue-interop/amqp-interop": "^0.8",
-        "queue-interop/queue-interop": "^0.7",
+        "queue-interop/queue-interop": "^0.7|^0.8",
         "enqueue/null": "^0.9",
         "enqueue/dsn": "^0.9",
         "ramsey/uuid": "^2|^3.5",

From 3806007d96ace05a18f6b2db2a671e05a045cebe Mon Sep 17 00:00:00 2001
From: Oskar Stark <oskarstark@googlemail.com>
Date: Fri, 14 Dec 2018 10:13:35 +0100
Subject: [PATCH 224/337] updated docs

---
 Client/CommandSubscriberInterface.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Client/CommandSubscriberInterface.php b/Client/CommandSubscriberInterface.php
index 5bbe78e..9ee09d0 100644
--- a/Client/CommandSubscriberInterface.php
+++ b/Client/CommandSubscriberInterface.php
@@ -41,7 +41,7 @@ interface CommandSubscriberInterface
      * queue, processor, prefix_queue, and exclusive are optional.
      * It is possible to pass other options, they could be accessible on a route instance through options.
      *
-     * Note: If you set queueNameHardcoded to true then the queueName is used as is and therefor the driver is not used to create a transport queue name.
+     * Note: If you set "prefix_queue" to true then the "queue" is used as is and therefor the driver is not used to create a transport queue name.
      *
      * @return string|array
      */

From 8522e2361b3ffc7f8b5faa45b1d6f392cfd953b7 Mon Sep 17 00:00:00 2001
From: Jo Carter <github@jocarter.co.uk>
Date: Fri, 14 Dec 2018 12:15:30 +0000
Subject: [PATCH 225/337] Fix TreeBuilder in Symfony 4.2

A tree builder without a root node is deprecated since Symfony 4.2 and will not be supported anymore in 5.0.
---
 .../TransportFactoryTest.php                  | 52 +++++++++----------
 1 file changed, 26 insertions(+), 26 deletions(-)

diff --git a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index 6cc02b6..5f96c72 100644
--- a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -37,8 +37,8 @@ public function testThrowIfEmptyNameGivenOnConstruction()
 
     public function testShouldAllowAddConfigurationAsStringDsn()
     {
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
+        $tb = new TreeBuilder('foo');
+        $rootNode = $tb->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -59,8 +59,8 @@ public function testShouldAllowAddConfigurationAsStringDsn()
      */
     public function testShouldAllowAddConfigurationAsDsnWithoutSlashes()
     {
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
+        $tb = new TreeBuilder('foo');
+        $rootNode = $tb->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -76,8 +76,8 @@ public function testShouldAllowAddConfigurationAsDsnWithoutSlashes()
 
     public function testShouldSetNullTransportIfNullGiven()
     {
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
+        $tb = new TreeBuilder('foo');
+        $rootNode = $tb->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -93,8 +93,8 @@ public function testShouldSetNullTransportIfNullGiven()
 
     public function testShouldSetNullTransportIfEmptyStringGiven()
     {
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
+        $tb = new TreeBuilder('foo');
+        $rootNode = $tb->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -110,8 +110,8 @@ public function testShouldSetNullTransportIfEmptyStringGiven()
 
     public function testShouldSetNullTransportIfEmptyArrayGiven()
     {
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
+        $tb = new TreeBuilder('foo');
+        $rootNode = $tb->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -127,8 +127,8 @@ public function testShouldSetNullTransportIfEmptyArrayGiven()
 
     public function testThrowIfEmptyDsnGiven()
     {
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
+        $tb = new TreeBuilder('foo');
+        $rootNode = $tb->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -140,8 +140,8 @@ public function testThrowIfEmptyDsnGiven()
 
     public function testThrowIfFactoryClassAndFactoryServiceSetAtTheSameTime()
     {
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
+        $tb = new TreeBuilder('foo');
+        $rootNode = $tb->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -159,8 +159,8 @@ public function testThrowIfFactoryClassAndFactoryServiceSetAtTheSameTime()
 
     public function testThrowIfConnectionFactoryClassUsedWithFactoryClassAtTheSameTime()
     {
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
+        $tb = new TreeBuilder('foo');
+        $rootNode = $tb->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -178,8 +178,8 @@ public function testThrowIfConnectionFactoryClassUsedWithFactoryClassAtTheSameTi
 
     public function testThrowIfConnectionFactoryClassUsedWithFactoryServiceAtTheSameTime()
     {
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
+        $tb = new TreeBuilder('foo');
+        $rootNode = $tb->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
@@ -196,8 +196,8 @@ public function testThrowIfConnectionFactoryClassUsedWithFactoryServiceAtTheSame
 
     public function testShouldAllowSetFactoryClass()
     {
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
+        $tb = new TreeBuilder('foo');
+        $rootNode = $tb->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
@@ -214,8 +214,8 @@ public function testShouldAllowSetFactoryClass()
 
     public function testShouldAllowSetFactoryService()
     {
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
+        $tb = new TreeBuilder('foo');
+        $rootNode = $tb->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
@@ -232,8 +232,8 @@ public function testShouldAllowSetFactoryService()
 
     public function testShouldAllowSetConnectionFactoryClass()
     {
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
+        $tb = new TreeBuilder('foo');
+        $rootNode = $tb->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
@@ -250,8 +250,8 @@ public function testShouldAllowSetConnectionFactoryClass()
 
     public function testThrowIfExtraOptionGiven()
     {
-        $tb = new TreeBuilder();
-        $rootNode = $tb->root('foo');
+        $tb = new TreeBuilder('foo');
+        $rootNode = $tb->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();

From ccec14ea9278364407aee5569288bef76aca0133 Mon Sep 17 00:00:00 2001
From: Jo Carter <github@jocarter.co.uk>
Date: Fri, 14 Dec 2018 12:27:16 +0000
Subject: [PATCH 226/337] BC fix.

---
 .../TransportFactoryTest.php                  | 91 ++++++++++++++++---
 1 file changed, 78 insertions(+), 13 deletions(-)

diff --git a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index 5f96c72..7b76eef 100644
--- a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -38,7 +38,12 @@ public function testThrowIfEmptyNameGivenOnConstruction()
     public function testShouldAllowAddConfigurationAsStringDsn()
     {
         $tb = new TreeBuilder('foo');
-        $rootNode = $tb->getRootNode();
+
+        if (method_exists($tb, 'getRootNode')) {
+            $rootNode = $tb->getRootNode();
+        } else {
+            $rootNode = $tb->root('foo');
+        }
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -60,7 +65,12 @@ public function testShouldAllowAddConfigurationAsStringDsn()
     public function testShouldAllowAddConfigurationAsDsnWithoutSlashes()
     {
         $tb = new TreeBuilder('foo');
-        $rootNode = $tb->getRootNode();
+
+        if (method_exists($tb, 'getRootNode')) {
+            $rootNode = $tb->getRootNode();
+        } else {
+            $rootNode = $tb->root('foo');
+        }
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -77,7 +87,12 @@ public function testShouldAllowAddConfigurationAsDsnWithoutSlashes()
     public function testShouldSetNullTransportIfNullGiven()
     {
         $tb = new TreeBuilder('foo');
-        $rootNode = $tb->getRootNode();
+
+        if (method_exists($tb, 'getRootNode')) {
+            $rootNode = $tb->getRootNode();
+        } else {
+            $rootNode = $tb->root('foo');
+        }
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -94,7 +109,12 @@ public function testShouldSetNullTransportIfNullGiven()
     public function testShouldSetNullTransportIfEmptyStringGiven()
     {
         $tb = new TreeBuilder('foo');
-        $rootNode = $tb->getRootNode();
+
+        if (method_exists($tb, 'getRootNode')) {
+            $rootNode = $tb->getRootNode();
+        } else {
+            $rootNode = $tb->root('foo');
+        }
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -111,7 +131,12 @@ public function testShouldSetNullTransportIfEmptyStringGiven()
     public function testShouldSetNullTransportIfEmptyArrayGiven()
     {
         $tb = new TreeBuilder('foo');
-        $rootNode = $tb->getRootNode();
+
+        if (method_exists($tb, 'getRootNode')) {
+            $rootNode = $tb->getRootNode();
+        } else {
+            $rootNode = $tb->root('foo');
+        }
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -128,7 +153,12 @@ public function testShouldSetNullTransportIfEmptyArrayGiven()
     public function testThrowIfEmptyDsnGiven()
     {
         $tb = new TreeBuilder('foo');
-        $rootNode = $tb->getRootNode();
+
+        if (method_exists($tb, 'getRootNode')) {
+            $rootNode = $tb->getRootNode();
+        } else {
+            $rootNode = $tb->root('foo');
+        }
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -141,7 +171,12 @@ public function testThrowIfEmptyDsnGiven()
     public function testThrowIfFactoryClassAndFactoryServiceSetAtTheSameTime()
     {
         $tb = new TreeBuilder('foo');
-        $rootNode = $tb->getRootNode();
+
+        if (method_exists($tb, 'getRootNode')) {
+            $rootNode = $tb->getRootNode();
+        } else {
+            $rootNode = $tb->root('foo');
+        }
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -160,7 +195,12 @@ public function testThrowIfFactoryClassAndFactoryServiceSetAtTheSameTime()
     public function testThrowIfConnectionFactoryClassUsedWithFactoryClassAtTheSameTime()
     {
         $tb = new TreeBuilder('foo');
-        $rootNode = $tb->getRootNode();
+
+        if (method_exists($tb, 'getRootNode')) {
+            $rootNode = $tb->getRootNode();
+        } else {
+            $rootNode = $tb->root('foo');
+        }
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -179,7 +219,12 @@ public function testThrowIfConnectionFactoryClassUsedWithFactoryClassAtTheSameTi
     public function testThrowIfConnectionFactoryClassUsedWithFactoryServiceAtTheSameTime()
     {
         $tb = new TreeBuilder('foo');
-        $rootNode = $tb->getRootNode();
+
+        if (method_exists($tb, 'getRootNode')) {
+            $rootNode = $tb->getRootNode();
+        } else {
+            $rootNode = $tb->root('foo');
+        }
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
@@ -197,7 +242,12 @@ public function testThrowIfConnectionFactoryClassUsedWithFactoryServiceAtTheSame
     public function testShouldAllowSetFactoryClass()
     {
         $tb = new TreeBuilder('foo');
-        $rootNode = $tb->getRootNode();
+
+        if (method_exists($tb, 'getRootNode')) {
+            $rootNode = $tb->getRootNode();
+        } else {
+            $rootNode = $tb->root('foo');
+        }
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
@@ -215,7 +265,12 @@ public function testShouldAllowSetFactoryClass()
     public function testShouldAllowSetFactoryService()
     {
         $tb = new TreeBuilder('foo');
-        $rootNode = $tb->getRootNode();
+
+        if (method_exists($tb, 'getRootNode')) {
+            $rootNode = $tb->getRootNode();
+        } else {
+            $rootNode = $tb->root('foo');
+        }
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
@@ -233,7 +288,12 @@ public function testShouldAllowSetFactoryService()
     public function testShouldAllowSetConnectionFactoryClass()
     {
         $tb = new TreeBuilder('foo');
-        $rootNode = $tb->getRootNode();
+
+        if (method_exists($tb, 'getRootNode')) {
+            $rootNode = $tb->getRootNode();
+        } else {
+            $rootNode = $tb->root('foo');
+        }
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
@@ -251,7 +311,12 @@ public function testShouldAllowSetConnectionFactoryClass()
     public function testThrowIfExtraOptionGiven()
     {
         $tb = new TreeBuilder('foo');
-        $rootNode = $tb->getRootNode();
+
+        if (method_exists($tb, 'getRootNode')) {
+            $rootNode = $tb->getRootNode();
+        } else {
+            $rootNode = $tb->root('foo');
+        }
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();

From dd82c4278bc25a39504477f0c0e299d61872296d Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 14 Dec 2018 14:39:47 +0200
Subject: [PATCH 227/337] Add commands for single transport\client with typed
 arguments.

---
 Symfony/Client/SimpleConsumeCommand.php       |  26 ++++
 Symfony/Client/SimpleProduceCommand.php       |  18 +++
 Symfony/Client/SimpleRoutesCommand.php        |  18 +++
 Symfony/Client/SimpleSetupBrokerCommand.php   |  18 +++
 Symfony/Consumption/SimpleConsumeCommand.php  |  18 +++
 .../Client/SimpleConsumeCommandTest.php       | 142 ++++++++++++++++++
 .../Client/SimpleProduceCommandTest.php       |  89 +++++++++++
 .../Client/SimpleRoutesCommandTest.php        | 119 +++++++++++++++
 .../Client/SimpleSetupBrokerCommandTest.php   |  87 +++++++++++
 .../Consumption/SimpleConsumeCommandTest.php  |  86 +++++++++++
 10 files changed, 621 insertions(+)
 create mode 100644 Symfony/Client/SimpleConsumeCommand.php
 create mode 100644 Symfony/Client/SimpleProduceCommand.php
 create mode 100644 Symfony/Client/SimpleRoutesCommand.php
 create mode 100644 Symfony/Client/SimpleSetupBrokerCommand.php
 create mode 100644 Symfony/Consumption/SimpleConsumeCommand.php
 create mode 100644 Tests/Symfony/Client/SimpleConsumeCommandTest.php
 create mode 100644 Tests/Symfony/Client/SimpleProduceCommandTest.php
 create mode 100644 Tests/Symfony/Client/SimpleRoutesCommandTest.php
 create mode 100644 Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php
 create mode 100644 Tests/Symfony/Consumption/SimpleConsumeCommandTest.php

diff --git a/Symfony/Client/SimpleConsumeCommand.php b/Symfony/Client/SimpleConsumeCommand.php
new file mode 100644
index 0000000..fafc35d
--- /dev/null
+++ b/Symfony/Client/SimpleConsumeCommand.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Enqueue\Symfony\Client;
+
+use Enqueue\Client\DriverInterface;
+use Enqueue\Consumption\QueueConsumerInterface;
+use Enqueue\Container\Container;
+use Interop\Queue\Processor;
+
+class SimpleConsumeCommand extends ConsumeCommand
+{
+    public function __construct(QueueConsumerInterface $queueConsumer, DriverInterface $driver, Processor $processor)
+    {
+        parent::__construct(
+            new Container([
+                'queue_consumer' => $queueConsumer,
+                'driver' => $driver,
+                'processor' => $processor,
+            ]),
+            'default',
+            'queue_consumer',
+            'driver',
+            'processor'
+        );
+    }
+}
diff --git a/Symfony/Client/SimpleProduceCommand.php b/Symfony/Client/SimpleProduceCommand.php
new file mode 100644
index 0000000..5d7f765
--- /dev/null
+++ b/Symfony/Client/SimpleProduceCommand.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Enqueue\Symfony\Client;
+
+use Enqueue\Client\ProducerInterface;
+use Enqueue\Container\Container;
+
+class SimpleProduceCommand extends ProduceCommand
+{
+    public function __construct(ProducerInterface $producer)
+    {
+        parent::__construct(
+            new Container(['producer' => $producer]),
+            'default',
+            'producer'
+        );
+    }
+}
diff --git a/Symfony/Client/SimpleRoutesCommand.php b/Symfony/Client/SimpleRoutesCommand.php
new file mode 100644
index 0000000..0023f14
--- /dev/null
+++ b/Symfony/Client/SimpleRoutesCommand.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Enqueue\Symfony\Client;
+
+use Enqueue\Client\DriverInterface;
+use Enqueue\Container\Container;
+
+class SimpleRoutesCommand extends RoutesCommand
+{
+    public function __construct(DriverInterface $driver)
+    {
+        parent::__construct(
+            new Container(['driver' => $driver]),
+            'default',
+            'driver'
+        );
+    }
+}
diff --git a/Symfony/Client/SimpleSetupBrokerCommand.php b/Symfony/Client/SimpleSetupBrokerCommand.php
new file mode 100644
index 0000000..aae19f8
--- /dev/null
+++ b/Symfony/Client/SimpleSetupBrokerCommand.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Enqueue\Symfony\Client;
+
+use Enqueue\Client\DriverInterface;
+use Enqueue\Container\Container;
+
+class SimpleSetupBrokerCommand extends SetupBrokerCommand
+{
+    public function __construct(DriverInterface $driver)
+    {
+        parent::__construct(
+            new Container(['driver' => $driver]),
+            'default',
+            'driver'
+        );
+    }
+}
diff --git a/Symfony/Consumption/SimpleConsumeCommand.php b/Symfony/Consumption/SimpleConsumeCommand.php
new file mode 100644
index 0000000..90d0e36
--- /dev/null
+++ b/Symfony/Consumption/SimpleConsumeCommand.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Enqueue\Symfony\Consumption;
+
+use Enqueue\Consumption\QueueConsumerInterface;
+use Enqueue\Container\Container;
+
+class SimpleConsumeCommand extends ConsumeCommand
+{
+    public function __construct(QueueConsumerInterface $consumer)
+    {
+        parent::__construct(
+            new Container(['queue_consumer' => $consumer]),
+            'default',
+            'queue_consumer'
+        );
+    }
+}
diff --git a/Tests/Symfony/Client/SimpleConsumeCommandTest.php b/Tests/Symfony/Client/SimpleConsumeCommandTest.php
new file mode 100644
index 0000000..cb12fe5
--- /dev/null
+++ b/Tests/Symfony/Client/SimpleConsumeCommandTest.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\Client;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DelegateProcessor;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\RouteCollection;
+use Enqueue\Consumption\ChainExtension;
+use Enqueue\Consumption\QueueConsumerInterface;
+use Enqueue\Null\NullQueue;
+use Enqueue\Symfony\Client\ConsumeCommand;
+use Enqueue\Symfony\Client\SimpleConsumeCommand;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Tester\CommandTester;
+
+class SimpleConsumeCommandTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldBeSubClassOfConsumeCommand()
+    {
+        $this->assertClassExtends(ConsumeCommand::class, SimpleConsumeCommand::class);
+    }
+
+    public function testShouldNotBeFinal()
+    {
+        $this->assertClassNotFinal(SimpleConsumeCommand::class);
+    }
+
+    public function testCouldBeConstructedWithRequiredAttributes()
+    {
+        new SimpleConsumeCommand($this->createQueueConsumerMock(), $this->createDriverStub(), $this->createDelegateProcessorMock());
+    }
+
+    public function testShouldHaveCommandName()
+    {
+        $command = new SimpleConsumeCommand($this->createQueueConsumerMock(), $this->createDriverStub(), $this->createDelegateProcessorMock());
+
+        $this->assertEquals('enqueue:consume', $command->getName());
+    }
+
+    public function testShouldHaveExpectedOptions()
+    {
+        $command = new SimpleConsumeCommand($this->createQueueConsumerMock(), $this->createDriverStub(), $this->createDelegateProcessorMock());
+
+        $options = $command->getDefinition()->getOptions();
+
+        $this->assertCount(9, $options);
+        $this->assertArrayHasKey('memory-limit', $options);
+        $this->assertArrayHasKey('message-limit', $options);
+        $this->assertArrayHasKey('time-limit', $options);
+        $this->assertArrayHasKey('receive-timeout', $options);
+        $this->assertArrayHasKey('niceness', $options);
+        $this->assertArrayHasKey('client', $options);
+        $this->assertArrayHasKey('logger', $options);
+        $this->assertArrayHasKey('skip', $options);
+        $this->assertArrayHasKey('setup-broker', $options);
+    }
+
+    public function testShouldHaveExpectedAttributes()
+    {
+        $command = new SimpleConsumeCommand($this->createQueueConsumerMock(), $this->createDriverStub(), $this->createDelegateProcessorMock());
+
+        $arguments = $command->getDefinition()->getArguments();
+
+        $this->assertCount(1, $arguments);
+        $this->assertArrayHasKey('client-queue-names', $arguments);
+    }
+
+    public function testShouldBindDefaultQueueOnly()
+    {
+        $queue = new NullQueue('');
+
+        $routeCollection = new RouteCollection([]);
+
+        $processor = $this->createDelegateProcessorMock();
+
+        $consumer = $this->createQueueConsumerMock();
+        $consumer
+            ->expects($this->once())
+            ->method('bind')
+            ->with($this->identicalTo($queue), $this->identicalTo($processor))
+        ;
+        $consumer
+            ->expects($this->once())
+            ->method('consume')
+            ->with($this->isInstanceOf(ChainExtension::class))
+        ;
+
+        $driver = $this->createDriverStub($routeCollection);
+        $driver
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with('default', true)
+            ->willReturn($queue)
+        ;
+
+        $command = new SimpleConsumeCommand($consumer, $driver, $processor);
+
+        $tester = new CommandTester($command);
+        $tester->execute([]);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|DelegateProcessor
+     */
+    private function createDelegateProcessorMock()
+    {
+        return $this->createMock(DelegateProcessor::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumerInterface
+     */
+    private function createQueueConsumerMock()
+    {
+        return $this->createMock(QueueConsumerInterface::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|DriverInterface
+     */
+    private function createDriverStub(RouteCollection $routeCollection = null): DriverInterface
+    {
+        $driverMock = $this->createMock(DriverInterface::class);
+        $driverMock
+            ->expects($this->any())
+            ->method('getRouteCollection')
+            ->willReturn($routeCollection)
+        ;
+
+        $driverMock
+            ->expects($this->any())
+            ->method('getConfig')
+            ->willReturn(Config::create('aPrefix', 'anApp'))
+        ;
+
+        return $driverMock;
+    }
+}
diff --git a/Tests/Symfony/Client/SimpleProduceCommandTest.php b/Tests/Symfony/Client/SimpleProduceCommandTest.php
new file mode 100644
index 0000000..82a7e00
--- /dev/null
+++ b/Tests/Symfony/Client/SimpleProduceCommandTest.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\Client;
+
+use Enqueue\Client\ProducerInterface;
+use Enqueue\Symfony\Client\ProduceCommand;
+use Enqueue\Symfony\Client\SimpleProduceCommand;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Tester\CommandTester;
+
+class SimpleProduceCommandTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldBeSubClassOfProduceCommand()
+    {
+        $this->assertClassExtends(ProduceCommand::class, SimpleProduceCommand::class);
+    }
+
+    public function testShouldNotBeFinal()
+    {
+        $this->assertClassNotFinal(SimpleProduceCommand::class);
+    }
+
+    public function testCouldBeConstructedWithContainerAsFirstArgument()
+    {
+        new SimpleProduceCommand($this->createProducerMock());
+    }
+
+    public function testShouldHaveCommandName()
+    {
+        $command = new SimpleProduceCommand($this->createProducerMock());
+
+        $this->assertEquals('enqueue:produce', $command->getName());
+    }
+
+    public function testShouldHaveExpectedOptions()
+    {
+        $command = new SimpleProduceCommand($this->createProducerMock());
+
+        $options = $command->getDefinition()->getOptions();
+        $this->assertCount(3, $options);
+        $this->assertArrayHasKey('client', $options);
+        $this->assertArrayHasKey('topic', $options);
+        $this->assertArrayHasKey('command', $options);
+    }
+
+    public function testShouldHaveExpectedAttributes()
+    {
+        $command = new SimpleProduceCommand($this->createProducerMock());
+
+        $arguments = $command->getDefinition()->getArguments();
+        $this->assertCount(1, $arguments);
+
+        $this->assertArrayHasKey('message', $arguments);
+    }
+
+    public function testThrowIfNeitherTopicNorCommandOptionsAreSet()
+    {
+        $producerMock = $this->createProducerMock();
+        $producerMock
+            ->expects($this->never())
+            ->method('sendEvent')
+        ;
+        $producerMock
+            ->expects($this->never())
+            ->method('sendCommand')
+        ;
+
+        $command = new SimpleProduceCommand($producerMock);
+
+        $tester = new CommandTester($command);
+
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Either topic or command option should be set, none is set.');
+        $tester->execute([
+            'message' => 'theMessage',
+        ]);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|ProducerInterface
+     */
+    private function createProducerMock()
+    {
+        return $this->createMock(ProducerInterface::class);
+    }
+}
diff --git a/Tests/Symfony/Client/SimpleRoutesCommandTest.php b/Tests/Symfony/Client/SimpleRoutesCommandTest.php
new file mode 100644
index 0000000..ce97be1
--- /dev/null
+++ b/Tests/Symfony/Client/SimpleRoutesCommandTest.php
@@ -0,0 +1,119 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\Client;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\RouteCollection;
+use Enqueue\Symfony\Client\RoutesCommand;
+use Enqueue\Symfony\Client\SimpleRoutesCommand;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Tester\CommandTester;
+
+class SimpleRoutesCommandTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldBeSubClassOfRoutesCommand()
+    {
+        $this->assertClassExtends(RoutesCommand::class, SimpleRoutesCommand::class);
+    }
+
+    public function testShouldNotBeFinal()
+    {
+        $this->assertClassNotFinal(SimpleRoutesCommand::class);
+    }
+
+    public function testCouldBeConstructedWithConfigAndRouteCollectionAsArguments()
+    {
+        new SimpleRoutesCommand($this->createDriverMock());
+    }
+
+    public function testShouldHaveCommandName()
+    {
+        $command = new SimpleRoutesCommand($this->createDriverMock());
+
+        $this->assertEquals('enqueue:routes', $command->getName());
+    }
+
+    public function testShouldHaveCommandAliases()
+    {
+        $command = new SimpleRoutesCommand($this->createDriverMock());
+
+        $this->assertEquals(['debug:enqueue:routes'], $command->getAliases());
+    }
+
+    public function testShouldHaveExpectedOptions()
+    {
+        $command = new SimpleRoutesCommand($this->createDriverMock());
+
+        $options = $command->getDefinition()->getOptions();
+        $this->assertCount(2, $options);
+
+        $this->assertArrayHasKey('show-route-options', $options);
+        $this->assertArrayHasKey('client', $options);
+    }
+
+    public function testShouldHaveExpectedAttributes()
+    {
+        $command = new SimpleRoutesCommand($this->createDriverMock());
+
+        $arguments = $command->getDefinition()->getArguments();
+        $this->assertCount(0, $arguments);
+    }
+
+    public function testShouldOutputEmptyRouteCollection()
+    {
+        $routeCollection = new RouteCollection([]);
+
+        $command = new SimpleRoutesCommand($this->createDriverStub(Config::create(), $routeCollection));
+
+        $tester = new CommandTester($command);
+
+        $tester->execute([]);
+
+        $expectedOutput = <<<'OUTPUT'
+Found 0 routes
+
+
+OUTPUT;
+
+        $this->assertCommandOutput($expectedOutput, $tester);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createDriverMock(): DriverInterface
+    {
+        return $this->createMock(DriverInterface::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createDriverStub(Config $config, RouteCollection $routeCollection): DriverInterface
+    {
+        $driverMock = $this->createDriverMock();
+        $driverMock
+            ->expects($this->any())
+            ->method('getConfig')
+            ->willReturn($config)
+        ;
+
+        $driverMock
+            ->expects($this->any())
+            ->method('getRouteCollection')
+            ->willReturn($routeCollection)
+        ;
+
+        return $driverMock;
+    }
+
+    private function assertCommandOutput(string $expected, CommandTester $tester): void
+    {
+        $this->assertSame(0, $tester->getStatusCode());
+        $this->assertSame($expected, $tester->getDisplay());
+    }
+}
diff --git a/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php b/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php
new file mode 100644
index 0000000..240cffa
--- /dev/null
+++ b/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\Client;
+
+use Enqueue\Client\DriverInterface;
+use Enqueue\Symfony\Client\SetupBrokerCommand;
+use Enqueue\Symfony\Client\SimpleSetupBrokerCommand;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Tester\CommandTester;
+
+class SimpleSetupBrokerCommandTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldBeSubClassOfSetupBrokerCommand()
+    {
+        $this->assertClassExtends(SetupBrokerCommand::class, SimpleSetupBrokerCommand::class);
+    }
+
+    public function testShouldNotBeFinal()
+    {
+        $this->assertClassNotFinal(SimpleSetupBrokerCommand::class);
+    }
+
+    public function testCouldBeConstructedWithContainerAsFirstArgument()
+    {
+        new SimpleSetupBrokerCommand($this->createClientDriverMock());
+    }
+
+    public function testShouldHaveCommandName()
+    {
+        $command = new SimpleSetupBrokerCommand($this->createClientDriverMock());
+
+        $this->assertEquals('enqueue:setup-broker', $command->getName());
+    }
+
+    public function testShouldHaveCommandAliases()
+    {
+        $command = new SimpleSetupBrokerCommand($this->createClientDriverMock());
+
+        $this->assertEquals(['enq:sb'], $command->getAliases());
+    }
+
+    public function testShouldHaveExpectedOptions()
+    {
+        $command = new SimpleSetupBrokerCommand($this->createClientDriverMock());
+
+        $options = $command->getDefinition()->getOptions();
+
+        $this->assertCount(1, $options);
+        $this->assertArrayHasKey('client', $options);
+    }
+
+    public function testShouldHaveExpectedAttributes()
+    {
+        $command = new SimpleSetupBrokerCommand($this->createClientDriverMock());
+
+        $arguments = $command->getDefinition()->getArguments();
+
+        $this->assertCount(0, $arguments);
+    }
+
+    public function testShouldCallDriverSetupBrokerMethod()
+    {
+        $driver = $this->createClientDriverMock();
+        $driver
+            ->expects($this->once())
+            ->method('setupBroker')
+        ;
+
+        $command = new SimpleSetupBrokerCommand($driver);
+
+        $tester = new CommandTester($command);
+        $tester->execute([]);
+
+        $this->assertContains('Broker set up', $tester->getDisplay());
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|DriverInterface
+     */
+    private function createClientDriverMock()
+    {
+        return $this->createMock(DriverInterface::class);
+    }
+}
diff --git a/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php b/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php
new file mode 100644
index 0000000..34d4161
--- /dev/null
+++ b/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Enqueue\Tests\Symfony\Consumption;
+
+use Enqueue\Consumption\ChainExtension;
+use Enqueue\Consumption\QueueConsumerInterface;
+use Enqueue\Symfony\Consumption\ConsumeCommand;
+use Enqueue\Symfony\Consumption\SimpleConsumeCommand;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Tester\CommandTester;
+
+class SimpleConsumeCommandTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldBeSubClassOfConsumeCommand()
+    {
+        $this->assertClassExtends(ConsumeCommand::class, SimpleConsumeCommand::class);
+    }
+
+    public function testShouldNotBeFinal()
+    {
+        $this->assertClassNotFinal(SimpleConsumeCommand::class);
+    }
+
+    public function testCouldBeConstructedWithRequiredAttributes()
+    {
+        new SimpleConsumeCommand($this->createQueueConsumerMock());
+    }
+
+    public function testShouldHaveCommandName()
+    {
+        $command = new SimpleConsumeCommand($this->createQueueConsumerMock());
+
+        $this->assertEquals('enqueue:transport:consume', $command->getName());
+    }
+
+    public function testShouldHaveExpectedOptions()
+    {
+        $command = new SimpleConsumeCommand($this->createQueueConsumerMock());
+
+        $options = $command->getDefinition()->getOptions();
+
+        $this->assertCount(7, $options);
+        $this->assertArrayHasKey('memory-limit', $options);
+        $this->assertArrayHasKey('message-limit', $options);
+        $this->assertArrayHasKey('time-limit', $options);
+        $this->assertArrayHasKey('receive-timeout', $options);
+        $this->assertArrayHasKey('niceness', $options);
+        $this->assertArrayHasKey('transport', $options);
+        $this->assertArrayHasKey('logger', $options);
+    }
+
+    public function testShouldHaveExpectedAttributes()
+    {
+        $command = new SimpleConsumeCommand($this->createQueueConsumerMock());
+
+        $arguments = $command->getDefinition()->getArguments();
+
+        $this->assertCount(0, $arguments);
+    }
+
+    public function testShouldExecuteDefaultConsumption()
+    {
+        $consumer = $this->createQueueConsumerMock();
+        $consumer
+            ->expects($this->once())
+            ->method('consume')
+            ->with($this->isInstanceOf(ChainExtension::class))
+        ;
+
+        $command = new SimpleConsumeCommand($consumer);
+
+        $tester = new CommandTester($command);
+        $tester->execute([]);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumerInterface
+     */
+    private function createQueueConsumerMock()
+    {
+        return $this->createMock(QueueConsumerInterface::class);
+    }
+}

From e9fcc34c20fc2fa904ae0389165f90c8a2ad69eb Mon Sep 17 00:00:00 2001
From: Jo Carter <github@jocarter.co.uk>
Date: Fri, 14 Dec 2018 13:03:59 +0000
Subject: [PATCH 228/337] Further BC fix

---
 .../TransportFactoryTest.php                  | 116 ++++--------------
 1 file changed, 25 insertions(+), 91 deletions(-)

diff --git a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index 7b76eef..92d89dd 100644
--- a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -11,6 +11,7 @@
 use Interop\Queue\ConnectionFactory;
 use Interop\Queue\Context;
 use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Definition\Builder\NodeDefinition;
 use Symfony\Component\Config\Definition\Builder\TreeBuilder;
 use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
 use Symfony\Component\Config\Definition\Processor;
@@ -37,13 +38,7 @@ public function testThrowIfEmptyNameGivenOnConstruction()
 
     public function testShouldAllowAddConfigurationAsStringDsn()
     {
-        $tb = new TreeBuilder('foo');
-
-        if (method_exists($tb, 'getRootNode')) {
-            $rootNode = $tb->getRootNode();
-        } else {
-            $rootNode = $tb->root('foo');
-        }
+        $rootNode = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -64,13 +59,7 @@ public function testShouldAllowAddConfigurationAsStringDsn()
      */
     public function testShouldAllowAddConfigurationAsDsnWithoutSlashes()
     {
-        $tb = new TreeBuilder('foo');
-
-        if (method_exists($tb, 'getRootNode')) {
-            $rootNode = $tb->getRootNode();
-        } else {
-            $rootNode = $tb->root('foo');
-        }
+        $rootNode = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -86,13 +75,7 @@ public function testShouldAllowAddConfigurationAsDsnWithoutSlashes()
 
     public function testShouldSetNullTransportIfNullGiven()
     {
-        $tb = new TreeBuilder('foo');
-
-        if (method_exists($tb, 'getRootNode')) {
-            $rootNode = $tb->getRootNode();
-        } else {
-            $rootNode = $tb->root('foo');
-        }
+        $rootNode = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -108,13 +91,7 @@ public function testShouldSetNullTransportIfNullGiven()
 
     public function testShouldSetNullTransportIfEmptyStringGiven()
     {
-        $tb = new TreeBuilder('foo');
-
-        if (method_exists($tb, 'getRootNode')) {
-            $rootNode = $tb->getRootNode();
-        } else {
-            $rootNode = $tb->root('foo');
-        }
+        $rootNode = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -130,13 +107,7 @@ public function testShouldSetNullTransportIfEmptyStringGiven()
 
     public function testShouldSetNullTransportIfEmptyArrayGiven()
     {
-        $tb = new TreeBuilder('foo');
-
-        if (method_exists($tb, 'getRootNode')) {
-            $rootNode = $tb->getRootNode();
-        } else {
-            $rootNode = $tb->root('foo');
-        }
+        $rootNode = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -152,13 +123,7 @@ public function testShouldSetNullTransportIfEmptyArrayGiven()
 
     public function testThrowIfEmptyDsnGiven()
     {
-        $tb = new TreeBuilder('foo');
-
-        if (method_exists($tb, 'getRootNode')) {
-            $rootNode = $tb->getRootNode();
-        } else {
-            $rootNode = $tb->root('foo');
-        }
+        $rootNode = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -170,13 +135,7 @@ public function testThrowIfEmptyDsnGiven()
 
     public function testThrowIfFactoryClassAndFactoryServiceSetAtTheSameTime()
     {
-        $tb = new TreeBuilder('foo');
-
-        if (method_exists($tb, 'getRootNode')) {
-            $rootNode = $tb->getRootNode();
-        } else {
-            $rootNode = $tb->root('foo');
-        }
+        $rootNode = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -194,13 +153,7 @@ public function testThrowIfFactoryClassAndFactoryServiceSetAtTheSameTime()
 
     public function testThrowIfConnectionFactoryClassUsedWithFactoryClassAtTheSameTime()
     {
-        $tb = new TreeBuilder('foo');
-
-        if (method_exists($tb, 'getRootNode')) {
-            $rootNode = $tb->getRootNode();
-        } else {
-            $rootNode = $tb->root('foo');
-        }
+        $rootNode = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -218,13 +171,7 @@ public function testThrowIfConnectionFactoryClassUsedWithFactoryClassAtTheSameTi
 
     public function testThrowIfConnectionFactoryClassUsedWithFactoryServiceAtTheSameTime()
     {
-        $tb = new TreeBuilder('foo');
-
-        if (method_exists($tb, 'getRootNode')) {
-            $rootNode = $tb->getRootNode();
-        } else {
-            $rootNode = $tb->root('foo');
-        }
+        $rootNode = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
@@ -241,13 +188,7 @@ public function testThrowIfConnectionFactoryClassUsedWithFactoryServiceAtTheSame
 
     public function testShouldAllowSetFactoryClass()
     {
-        $tb = new TreeBuilder('foo');
-
-        if (method_exists($tb, 'getRootNode')) {
-            $rootNode = $tb->getRootNode();
-        } else {
-            $rootNode = $tb->root('foo');
-        }
+        $rootNode = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
@@ -264,13 +205,7 @@ public function testShouldAllowSetFactoryClass()
 
     public function testShouldAllowSetFactoryService()
     {
-        $tb = new TreeBuilder('foo');
-
-        if (method_exists($tb, 'getRootNode')) {
-            $rootNode = $tb->getRootNode();
-        } else {
-            $rootNode = $tb->root('foo');
-        }
+        $rootNode = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
@@ -287,13 +222,7 @@ public function testShouldAllowSetFactoryService()
 
     public function testShouldAllowSetConnectionFactoryClass()
     {
-        $tb = new TreeBuilder('foo');
-
-        if (method_exists($tb, 'getRootNode')) {
-            $rootNode = $tb->getRootNode();
-        } else {
-            $rootNode = $tb->root('foo');
-        }
+        $rootNode = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
@@ -310,13 +239,7 @@ public function testShouldAllowSetConnectionFactoryClass()
 
     public function testThrowIfExtraOptionGiven()
     {
-        $tb = new TreeBuilder('foo');
-
-        if (method_exists($tb, 'getRootNode')) {
-            $rootNode = $tb->getRootNode();
-        } else {
-            $rootNode = $tb->root('foo');
-        }
+        $rootNode = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
@@ -529,4 +452,15 @@ public function testThrowIfBuildRpcClientCalledButContextServiceDoesNotExist()
         $this->expectExceptionMessage('The service "enqueue.transport.default.context" does not exist.');
         $transport->buildRpcClient($container, []);
     }
+
+    private function getRootNode(): NodeDefinition
+    {
+        if (method_exists(TreeBuilder::class, 'getRootNode')) {
+            $tb = new TreeBuilder('foo');
+            return $tb->getRootNode();
+        }
+
+        $tb = new TreeBuilder();
+        return $tb->root('foo');
+    }
 }

From 55458d87a09bb2fe3fc3defed7f7dd186298abae Mon Sep 17 00:00:00 2001
From: Jo Carter <github@jocarter.co.uk>
Date: Fri, 14 Dec 2018 13:14:51 +0000
Subject: [PATCH 229/337] Fix wonky var return

---
 .../TransportFactoryTest.php                  | 37 +++++++++++--------
 1 file changed, 21 insertions(+), 16 deletions(-)

diff --git a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index 92d89dd..6886871 100644
--- a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -38,7 +38,7 @@ public function testThrowIfEmptyNameGivenOnConstruction()
 
     public function testShouldAllowAddConfigurationAsStringDsn()
     {
-        $rootNode = $this->getRootNode();
+        list($tb, $rootNode) = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -59,7 +59,7 @@ public function testShouldAllowAddConfigurationAsStringDsn()
      */
     public function testShouldAllowAddConfigurationAsDsnWithoutSlashes()
     {
-        $rootNode = $this->getRootNode();
+        list($tb, $rootNode) = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -75,7 +75,7 @@ public function testShouldAllowAddConfigurationAsDsnWithoutSlashes()
 
     public function testShouldSetNullTransportIfNullGiven()
     {
-        $rootNode = $this->getRootNode();
+        list($tb, $rootNode) = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -91,7 +91,7 @@ public function testShouldSetNullTransportIfNullGiven()
 
     public function testShouldSetNullTransportIfEmptyStringGiven()
     {
-        $rootNode = $this->getRootNode();
+        list($tb, $rootNode) = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -107,7 +107,7 @@ public function testShouldSetNullTransportIfEmptyStringGiven()
 
     public function testShouldSetNullTransportIfEmptyArrayGiven()
     {
-        $rootNode = $this->getRootNode();
+        list($tb, $rootNode) = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -123,7 +123,7 @@ public function testShouldSetNullTransportIfEmptyArrayGiven()
 
     public function testThrowIfEmptyDsnGiven()
     {
-        $rootNode = $this->getRootNode();
+        list($tb, $rootNode) = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -135,7 +135,7 @@ public function testThrowIfEmptyDsnGiven()
 
     public function testThrowIfFactoryClassAndFactoryServiceSetAtTheSameTime()
     {
-        $rootNode = $this->getRootNode();
+        list($tb, $rootNode) = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -153,7 +153,7 @@ public function testThrowIfFactoryClassAndFactoryServiceSetAtTheSameTime()
 
     public function testThrowIfConnectionFactoryClassUsedWithFactoryClassAtTheSameTime()
     {
-        $rootNode = $this->getRootNode();
+        list($tb, $rootNode) = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
 
@@ -171,7 +171,7 @@ public function testThrowIfConnectionFactoryClassUsedWithFactoryClassAtTheSameTi
 
     public function testThrowIfConnectionFactoryClassUsedWithFactoryServiceAtTheSameTime()
     {
-        $rootNode = $this->getRootNode();
+        list($tb, $rootNode) = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
@@ -188,7 +188,7 @@ public function testThrowIfConnectionFactoryClassUsedWithFactoryServiceAtTheSame
 
     public function testShouldAllowSetFactoryClass()
     {
-        $rootNode = $this->getRootNode();
+        list($tb, $rootNode) = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
@@ -205,7 +205,7 @@ public function testShouldAllowSetFactoryClass()
 
     public function testShouldAllowSetFactoryService()
     {
-        $rootNode = $this->getRootNode();
+        list($tb, $rootNode) = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
@@ -222,7 +222,7 @@ public function testShouldAllowSetFactoryService()
 
     public function testShouldAllowSetConnectionFactoryClass()
     {
-        $rootNode = $this->getRootNode();
+        list($tb, $rootNode) = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
@@ -239,7 +239,7 @@ public function testShouldAllowSetConnectionFactoryClass()
 
     public function testThrowIfExtraOptionGiven()
     {
-        $rootNode = $this->getRootNode();
+        list($tb, $rootNode) = $this->getRootNode();
 
         $rootNode->append(TransportFactory::getConfiguration());
         $processor = new Processor();
@@ -453,14 +453,19 @@ public function testThrowIfBuildRpcClientCalledButContextServiceDoesNotExist()
         $transport->buildRpcClient($container, []);
     }
 
-    private function getRootNode(): NodeDefinition
+    /**
+     * @return [TreeBuilder, NodeDefinition]
+     */
+    private function getRootNode(): array
     {
         if (method_exists(TreeBuilder::class, 'getRootNode')) {
             $tb = new TreeBuilder('foo');
-            return $tb->getRootNode();
+
+            return [$tb, $tb->getRootNode()];
         }
 
         $tb = new TreeBuilder();
-        return $tb->root('foo');
+
+        return [$tb, $tb->root('foo')];
     }
 }

From 07cb89dfb446ad4b8fd5841e3d698ae88eb23bf9 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 14 Dec 2018 21:01:02 +0200
Subject: [PATCH 230/337] fix extensions version in tests.

---
 Tests/fix_composer_json.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Tests/fix_composer_json.php b/Tests/fix_composer_json.php
index bce1ebb..dfbec62 100644
--- a/Tests/fix_composer_json.php
+++ b/Tests/fix_composer_json.php
@@ -4,8 +4,8 @@
 
 $composerJson = json_decode(file_get_contents(__DIR__.'/../composer.json'), true);
 
-$composerJson['config']['platform']['ext-amqp'] = '1.7';
+$composerJson['config']['platform']['ext-amqp'] = '1.9.3';
 $composerJson['config']['platform']['ext-rdkafka'] = '3.3';
-$composerJson['config']['platform']['ext-gearman'] = '1.1';
+$composerJson['config']['platform']['ext-gearman'] = '2';
 
 file_put_contents(__DIR__.'/../composer.json', json_encode($composerJson, JSON_PRETTY_PRINT));

From 22a116dff909a20fac4a0cd11f247e4337a33723 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 20 Dec 2018 14:07:24 +0200
Subject: [PATCH 231/337] [client] Fix Exclusive Command Extension ignores
 route queue preifx option.

---
 Client/ConsumptionExtension/ExclusiveCommandExtension.php  | 2 +-
 .../ConsumptionExtension/ExclusiveCommandExtensionTest.php | 7 ++++---
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/Client/ConsumptionExtension/ExclusiveCommandExtension.php b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
index a76e49f..7ab88ae 100644
--- a/Client/ConsumptionExtension/ExclusiveCommandExtension.php
+++ b/Client/ConsumptionExtension/ExclusiveCommandExtension.php
@@ -64,7 +64,7 @@ private function buildMap(): array
                 continue;
             }
 
-            $queueName = $this->driver->createQueue($route->getQueue())->getQueueName();
+            $queueName = $this->driver->createRouteQueue($route)->getQueueName();
             if (array_key_exists($queueName, $map)) {
                 throw new \LogicException('The queue name has been already bound by another exclusive command processor');
             }
diff --git a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
index 3e1ef2b..8778367 100644
--- a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
@@ -181,9 +181,10 @@ public function testShouldSetCommandPropertiesIfCurrentQueueHasExclusiveCommandP
         $driver = $this->createDriverStub($routeCollection);
         $driver
             ->expects($this->any())
-            ->method('createQueue')
-            ->willReturnCallback(function (string $queueName) {
-                return new NullQueue($queueName);
+            ->method('createRouteQueue')
+            ->with($this->isInstanceOf(Route::class))
+            ->willReturnCallback(function (Route $route) {
+                return new NullQueue($route->getQueue());
             })
         ;
 

From 7901916c47d80e7b4ccb47bfc5ac64f14c813832 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 20 Dec 2018 14:13:19 +0200
Subject: [PATCH 232/337] [client] Fix SetRouterProeprtiesExtension should skip
 no topic messages.

---
 .../SetRouterPropertiesExtension.php          |  3 ++
 .../SetRouterPropertiesExtensionTest.php      | 32 ++++++++++++++++++-
 2 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
index 1655886..c7682f6 100644
--- a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
+++ b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
@@ -25,6 +25,9 @@ public function __construct(DriverInterface $driver)
     public function onMessageReceived(MessageReceived $context): void
     {
         $message = $context->getMessage();
+        if (false == $message->getProperty(Config::TOPIC)) {
+            return;
+        }
         if ($message->getProperty(Config::PROCESSOR)) {
             return;
         }
diff --git a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
index 4cd214e..3c12de4 100644
--- a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
@@ -49,6 +49,7 @@ public function testShouldSetRouterProcessorPropertyIfNotSetAndOnRouterQueue()
         ;
 
         $message = new NullMessage();
+        $message->setProperty(Config::TOPIC, 'aTopic');
 
         $messageReceived = new MessageReceived(
             $this->createContextMock(),
@@ -64,6 +65,7 @@ public function testShouldSetRouterProcessorPropertyIfNotSetAndOnRouterQueue()
 
         $this->assertEquals([
             Config::PROCESSOR => 'router-processor-name',
+            Config::TOPIC => 'aTopic',
         ], $message->getProperties());
     }
 
@@ -86,6 +88,7 @@ public function testShouldNotSetRouterProcessorPropertyIfNotSetAndNotOnRouterQue
         ;
 
         $message = new NullMessage();
+        $message->setProperty(Config::TOPIC, 'aTopic');
 
         $messageReceived = new MessageReceived(
             $this->createContextMock(),
@@ -99,7 +102,9 @@ public function testShouldNotSetRouterProcessorPropertyIfNotSetAndNotOnRouterQue
         $extension = new SetRouterPropertiesExtension($driver);
         $extension->onMessageReceived($messageReceived);
 
-        $this->assertEquals([], $message->getProperties());
+        $this->assertEquals([
+            Config::TOPIC => 'aTopic',
+        ], $message->getProperties());
     }
 
     public function testShouldNotSetAnyPropertyIfProcessorNamePropertyAlreadySet()
@@ -130,6 +135,31 @@ public function testShouldNotSetAnyPropertyIfProcessorNamePropertyAlreadySet()
         ], $message->getProperties());
     }
 
+    public function testShouldSkipMessagesWithoutTopicPropertySet()
+    {
+        $driver = $this->createDriverMock();
+        $driver
+            ->expects($this->never())
+            ->method('getConfig')
+        ;
+
+        $message = new NullMessage();
+
+        $messageReceived = new MessageReceived(
+            $this->createContextMock(),
+            $this->createConsumerStub(null),
+            $message,
+            $this->createProcessorMock(),
+            1,
+            new NullLogger()
+        );
+
+        $extension = new SetRouterPropertiesExtension($driver);
+        $extension->onMessageReceived($messageReceived);
+
+        $this->assertEquals([], $message->getProperties());
+    }
+
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
      */

From 76e6b4281dcf94b13840e46b2c13a8d83b6cff0c Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 20 Dec 2018 14:20:43 +0200
Subject: [PATCH 233/337] [client] sendToProcessor should able to send message
 to router processor.

---
 Client/Driver/GenericDriver.php               |  8 ++--
 .../Client/Driver/GenericDriverTestsTrait.php | 48 +++++++++++++++++++
 2 files changed, 53 insertions(+), 3 deletions(-)

diff --git a/Client/Driver/GenericDriver.php b/Client/Driver/GenericDriver.php
index 63a1f12..529f632 100644
--- a/Client/Driver/GenericDriver.php
+++ b/Client/Driver/GenericDriver.php
@@ -71,7 +71,9 @@ public function sendToProcessor(Message $message): DriverSendResult
 
         /** @var InteropQueue $queue */
         $queue = null;
-        if ($topic && $processor = $message->getProperty(Config::PROCESSOR)) {
+        $routerProcessor = $this->config->getRouterProcessor();
+        $processor = $message->getProperty(Config::PROCESSOR);
+        if ($topic && $processor && $processor !== $routerProcessor) {
             $route = $this->routeCollection->topicAndProcessor($topic, $processor);
             if (false == $route) {
                 throw new \LogicException(sprintf('There is no route for topic "%s" and processor "%s"', $topic, $processor));
@@ -79,8 +81,8 @@ public function sendToProcessor(Message $message): DriverSendResult
 
             $message->setProperty(Config::PROCESSOR, $route->getProcessor());
             $queue = $this->createRouteQueue($route);
-        } elseif ($topic && false == $message->getProperty(Config::PROCESSOR)) {
-            $message->setProperty(Config::PROCESSOR, $this->config->getRouterProcessor());
+        } elseif ($topic && (false == $processor || $processor === $routerProcessor)) {
+            $message->setProperty(Config::PROCESSOR, $routerProcessor);
 
             $queue = $this->createQueue($this->config->getRouterQueue());
         } elseif ($command) {
diff --git a/Tests/Client/Driver/GenericDriverTestsTrait.php b/Tests/Client/Driver/GenericDriverTestsTrait.php
index b4c23dd..ec38dfd 100644
--- a/Tests/Client/Driver/GenericDriverTestsTrait.php
+++ b/Tests/Client/Driver/GenericDriverTestsTrait.php
@@ -445,6 +445,54 @@ public function testThrowIfCommandSetOnSendToRouter()
         $driver->sendToRouter($message);
     }
 
+    public function testShouldSendMessageToRouterProcessor()
+    {
+        $queue = $this->createQueue('');
+        $transportMessage = $this->createMessage();
+
+        $producer = $this->createProducerMock();
+        $producer
+            ->expects($this->once())
+            ->method('send')
+            ->with($this->identicalTo($queue), $this->identicalTo($transportMessage))
+        ;
+        $context = $this->createContextMock();
+        $context
+            ->expects($this->once())
+            ->method('createQueue')
+            ->with($this->getDefaultQueueTransportName())
+            ->willReturn($queue)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createProducer')
+            ->willReturn($producer)
+        ;
+        $context
+            ->expects($this->once())
+            ->method('createMessage')
+            ->willReturn($transportMessage)
+        ;
+
+        $config = $this->createDummyConfig();
+
+        $driver = $this->createDriver(
+            $context,
+            $config,
+            new RouteCollection([
+                new Route('topic', Route::TOPIC, 'processor', [
+                    'queue' => 'custom'
+                ]),
+            ])
+        );
+
+        $message = new Message();
+        $message->setProperty(Config::TOPIC, 'topic');
+        $message->setProperty(Config::PROCESSOR, $config->getRouterProcessor());
+
+        $driver->sendToProcessor($message);
+    }
+
     public function testShouldSendTopicMessageToProcessorToDefaultQueue()
     {
         $queue = $this->createQueue('');

From c70b6e0b5d3bd6779935c7462dce08c982675468 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 20 Dec 2018 15:04:59 +0200
Subject: [PATCH 234/337] fix cs

---
 Tests/Client/Driver/GenericDriverTestsTrait.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/Client/Driver/GenericDriverTestsTrait.php b/Tests/Client/Driver/GenericDriverTestsTrait.php
index ec38dfd..cb14137 100644
--- a/Tests/Client/Driver/GenericDriverTestsTrait.php
+++ b/Tests/Client/Driver/GenericDriverTestsTrait.php
@@ -481,7 +481,7 @@ public function testShouldSendMessageToRouterProcessor()
             $config,
             new RouteCollection([
                 new Route('topic', Route::TOPIC, 'processor', [
-                    'queue' => 'custom'
+                    'queue' => 'custom',
                 ]),
             ])
         );

From b375509bd6066f59b3fd0dc2042426b2f294d98e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ram=C5=ABnas=20Dronga?= <ieskok@ramuno.lt>
Date: Fri, 28 Dec 2018 00:11:00 +0200
Subject: [PATCH 235/337] [consumption] fix wrong niceness extension param

---
 .../LimitsExtensionsCommandTrait.php          |  4 +--
 .../LimitsExtensionsCommandTraitTest.php      | 28 ++++++++++++++++---
 2 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/Symfony/Consumption/LimitsExtensionsCommandTrait.php b/Symfony/Consumption/LimitsExtensionsCommandTrait.php
index b721af9..d7363db 100644
--- a/Symfony/Consumption/LimitsExtensionsCommandTrait.php
+++ b/Symfony/Consumption/LimitsExtensionsCommandTrait.php
@@ -58,8 +58,8 @@ protected function getLimitsExtensions(InputInterface $input, OutputInterface $o
         }
 
         $niceness = $input->getOption('niceness');
-        if ($niceness) {
-            $extensions[] = new NicenessExtension($niceness);
+        if (!empty($niceness) && is_numeric($niceness)) {
+            $extensions[] = new NicenessExtension((int) $niceness);
         }
 
         return $extensions;
diff --git a/Tests/Symfony/Consumption/LimitsExtensionsCommandTraitTest.php b/Tests/Symfony/Consumption/LimitsExtensionsCommandTraitTest.php
index 5495274..e929123 100644
--- a/Tests/Symfony/Consumption/LimitsExtensionsCommandTraitTest.php
+++ b/Tests/Symfony/Consumption/LimitsExtensionsCommandTraitTest.php
@@ -108,17 +108,37 @@ public function testShouldAddThreeLimitExtensions()
         $this->assertInstanceOf(LimitConsumerMemoryExtension::class, $result[2]);
     }
 
-    public function testShouldAddNicenessExtension()
+    /**
+     * @param mixed $inputValue
+     * @param bool  $enabled
+     * @dataProvider provideNicenessValues
+     */
+    public function testShouldAddNicenessExtension($inputValue, bool $enabled)
     {
         $command = new LimitsExtensionsCommand('name');
         $tester = new CommandTester($command);
         $tester->execute([
-            '--niceness' => 1,
+            '--niceness' => $inputValue,
         ]);
 
         $result = $command->getExtensions();
-        $this->assertCount(1, $result);
 
-        $this->assertInstanceOf(NicenessExtension::class, $result[0]);
+        if ($enabled) {
+            $this->assertCount(1, $result);
+            $this->assertInstanceOf(NicenessExtension::class, $result[0]);
+        } else {
+            $this->assertEmpty($result);
+        }
+    }
+
+    public function provideNicenessValues(): \Generator
+    {
+        yield [1, true];
+        yield ['1', true];
+        yield [-1.0, true];
+        yield ['100', true];
+        yield ['', false];
+        yield ['0', false];
+        yield [0.0, false];
     }
 }

From cfb4149b46fc60631a352a259388d5599ec43fe6 Mon Sep 17 00:00:00 2001
From: Damien Harper <damien.harper@gmail.com>
Date: Tue, 15 Jan 2019 15:23:41 +0100
Subject: [PATCH 236/337] Avoid OutOfMemory exception

`previous` attribute should not reference itself
---
 Consumption/QueueConsumer.php | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 7ba9276..3454b30 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -323,9 +323,11 @@ private function onProcessorException(ExtensionInterface $extension, Consumer $c
                 }
             }
 
-            $prev = new \ReflectionProperty('Exception', 'previous');
-            $prev->setAccessible(true);
-            $prev->setValue($wrapper, $exception);
+            if ($exception !== $wrapper) {
+                $prev = new \ReflectionProperty('Exception', 'previous');
+                $prev->setAccessible(true);
+                $prev->setValue($wrapper, $exception);
+            }
 
             throw $e;
         }

From f39b2d058879f2691c9b523ee5800c2275ad76b0 Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Mon, 21 Jan 2019 12:03:01 +0200
Subject: [PATCH 237/337] doctrine schema

---
 Doctrine/DoctrineConnectionFactoryFactory.php | 54 +++++++++++++++++++
 Doctrine/DoctrineDriverFactory.php            | 41 ++++++++++++++
 Doctrine/DoctrineSchemaCompilerPass.php       | 39 ++++++++++++++
 3 files changed, 134 insertions(+)
 create mode 100644 Doctrine/DoctrineConnectionFactoryFactory.php
 create mode 100644 Doctrine/DoctrineDriverFactory.php
 create mode 100644 Doctrine/DoctrineSchemaCompilerPass.php

diff --git a/Doctrine/DoctrineConnectionFactoryFactory.php b/Doctrine/DoctrineConnectionFactoryFactory.php
new file mode 100644
index 0000000..14f39f9
--- /dev/null
+++ b/Doctrine/DoctrineConnectionFactoryFactory.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Enqueue\Doctrine;
+
+use Enqueue\ConnectionFactoryFactoryInterface;
+use Enqueue\Dbal\ManagerRegistryConnectionFactory;
+use Enqueue\Dsn\Dsn;
+use Interop\Queue\ConnectionFactory;
+use Symfony\Bridge\Doctrine\RegistryInterface;
+
+class DoctrineConnectionFactoryFactory implements ConnectionFactoryFactoryInterface
+{
+    /**
+     * @var RegistryInterface
+     */
+    private $doctrine;
+
+    /**
+     * @var ConnectionFactoryFactoryInterface
+     */
+    private $fallbackFactory;
+
+    public function __construct(RegistryInterface $doctrine, ConnectionFactoryFactoryInterface $fallbackFactory)
+    {
+        $this->doctrine = $doctrine;
+        $this->fallbackFactory = $fallbackFactory;
+    }
+
+    public function create($config): ConnectionFactory
+    {
+        if (is_string($config)) {
+            $config = ['dsn' => $config];
+        }
+
+        if (false == is_array($config)) {
+            throw new \InvalidArgumentException('The config must be either array or DSN string.');
+        }
+
+        if (false == array_key_exists('dsn', $config)) {
+            throw new \InvalidArgumentException('The config must have dsn key set.');
+        }
+
+        $dsn = Dsn::parseFirst($config['dsn']);
+
+        if ($dsn->getScheme() === 'doctrine') {
+            $config = $dsn->getQuery();
+            $config['connection_name'] = $dsn->getHost();
+
+            return new ManagerRegistryConnectionFactory($this->doctrine, $config);
+        }
+
+        return $this->fallbackFactory->create($config);
+    }
+}
diff --git a/Doctrine/DoctrineDriverFactory.php b/Doctrine/DoctrineDriverFactory.php
new file mode 100644
index 0000000..a1c977c
--- /dev/null
+++ b/Doctrine/DoctrineDriverFactory.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Enqueue\Doctrine;
+
+use Enqueue\Client\Config;
+use Enqueue\Client\Driver\DbalDriver;
+use Enqueue\Client\DriverFactoryInterface;
+use Enqueue\Client\DriverInterface;
+use Enqueue\Client\RouteCollection;
+use Enqueue\Dsn\Dsn;
+use Interop\Queue\ConnectionFactory;
+
+class DoctrineDriverFactory implements DriverFactoryInterface
+{
+    /**
+     * @var DriverFactoryInterface
+     */
+    private $fallbackFactory;
+
+    public function __construct(DriverFactoryInterface $fallbackFactory)
+    {
+        $this->fallbackFactory = $fallbackFactory;
+    }
+
+    public function create(ConnectionFactory $factory, Config $config, RouteCollection $collection): DriverInterface
+    {
+        $dsn = $config->getTransportOption('dsn');
+
+        if (empty($dsn)) {
+            throw new \LogicException('This driver factory relies on dsn option from transport config. The option is empty or not set.');
+        }
+
+        $dsn = Dsn::parseFirst($dsn);
+
+        if ($dsn->getScheme() === 'doctrine') {
+            return new DbalDriver($factory->createContext(), $config, $collection);
+        }
+
+        return $this->fallbackFactory->create($factory, $config, $collection);
+    }
+}
diff --git a/Doctrine/DoctrineSchemaCompilerPass.php b/Doctrine/DoctrineSchemaCompilerPass.php
new file mode 100644
index 0000000..25016a7
--- /dev/null
+++ b/Doctrine/DoctrineSchemaCompilerPass.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Enqueue\Doctrine;
+
+use Enqueue\Symfony\Client\DependencyInjection\ClientFactory;
+use Enqueue\Symfony\DependencyInjection\TransportFactory;
+use Enqueue\Symfony\DiUtils;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+class DoctrineSchemaCompilerPass implements CompilerPassInterface
+{
+    public function process(ContainerBuilder $container)
+    {
+        if (false === $container->hasDefinition('doctrine')) {
+            return;
+        }
+
+        foreach ($container->getParameter('enqueue.transports') as $name) {
+            $diUtils = DiUtils::create(TransportFactory::MODULE, $name);
+
+            $container->register($diUtils->format('connection_factory_factory.outer'), DoctrineConnectionFactoryFactory::class)
+                ->setDecoratedService($diUtils->format('connection_factory_factory'), $diUtils->format('connection_factory_factory.inner'))
+                ->addArgument(new Reference('doctrine'))
+                ->addArgument(new Reference($diUtils->format('connection_factory_factory.inner')))
+            ;
+        }
+
+        foreach ($container->getParameter('enqueue.clients') as $name) {
+            $diUtils = DiUtils::create(ClientFactory::MODULE, $name);
+
+            $container->register($diUtils->format('driver_factory.outer'), DoctrineDriverFactory::class)
+                ->setDecoratedService($diUtils->format('driver_factory'), $diUtils->format('driver_factory.inner'))
+                ->addArgument(new Reference($diUtils->format('driver_factory.inner')))
+            ;
+        }
+    }
+}

From 3a5d48585199a2f863bff5da6e71f6cb2445f8ce Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Mon, 21 Jan 2019 12:15:40 +0200
Subject: [PATCH 238/337] doctrine schema

---
 Doctrine/DoctrineConnectionFactoryFactory.php | 2 +-
 Doctrine/DoctrineDriverFactory.php            | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Doctrine/DoctrineConnectionFactoryFactory.php b/Doctrine/DoctrineConnectionFactoryFactory.php
index 14f39f9..db8f0dd 100644
--- a/Doctrine/DoctrineConnectionFactoryFactory.php
+++ b/Doctrine/DoctrineConnectionFactoryFactory.php
@@ -42,7 +42,7 @@ public function create($config): ConnectionFactory
 
         $dsn = Dsn::parseFirst($config['dsn']);
 
-        if ($dsn->getScheme() === 'doctrine') {
+        if ('doctrine' === $dsn->getScheme()) {
             $config = $dsn->getQuery();
             $config['connection_name'] = $dsn->getHost();
 
diff --git a/Doctrine/DoctrineDriverFactory.php b/Doctrine/DoctrineDriverFactory.php
index a1c977c..aab6489 100644
--- a/Doctrine/DoctrineDriverFactory.php
+++ b/Doctrine/DoctrineDriverFactory.php
@@ -32,7 +32,7 @@ public function create(ConnectionFactory $factory, Config $config, RouteCollecti
 
         $dsn = Dsn::parseFirst($dsn);
 
-        if ($dsn->getScheme() === 'doctrine') {
+        if ('doctrine' === $dsn->getScheme()) {
             return new DbalDriver($factory->createContext(), $config, $collection);
         }
 

From 2990169a4dd636a38d7fc181e8e6247eb6b69e53 Mon Sep 17 00:00:00 2001
From: Andriy Volberg <andriy.volberg@gmail.com>
Date: Wed, 13 Feb 2019 18:20:17 +0200
Subject: [PATCH 239/337] add exit status flow

---
 Consumption/Context/End.php                 | 20 ++++++++++++++++++--
 Consumption/Context/PostConsume.php         | 13 ++++++++++++-
 Consumption/Context/PostMessageReceived.php | 13 ++++++++++++-
 Consumption/Context/PreConsume.php          | 13 ++++++++++++-
 Consumption/Context/Start.php               | 13 ++++++++++++-
 Consumption/QueueConsumer.php               | 11 ++++++-----
 6 files changed, 72 insertions(+), 11 deletions(-)

diff --git a/Consumption/Context/End.php b/Consumption/Context/End.php
index 467f6d3..ec8c1f7 100644
--- a/Consumption/Context/End.php
+++ b/Consumption/Context/End.php
@@ -27,12 +27,23 @@ final class End
      */
     private $logger;
 
-    public function __construct(Context $context, int $startTime, int $endTime, LoggerInterface $logger)
-    {
+    /**
+     * @var int
+     */
+    private $exitStatus;
+
+    public function __construct(
+        Context $context,
+        int $startTime,
+        int $endTime,
+        LoggerInterface $logger,
+        ?int $exitStatus = null
+    ) {
         $this->context = $context;
         $this->logger = $logger;
         $this->startTime = $startTime;
         $this->endTime = $endTime;
+        $this->exitStatus = $exitStatus;
     }
 
     public function getContext(): Context
@@ -60,4 +71,9 @@ public function getEndTime(): int
     {
         return $this->startTime;
     }
+
+    public function getExitStatus(): ?int
+    {
+        return $this->exitStatus;
+    }
 }
diff --git a/Consumption/Context/PostConsume.php b/Consumption/Context/PostConsume.php
index 0add003..a6f1d83 100644
--- a/Consumption/Context/PostConsume.php
+++ b/Consumption/Context/PostConsume.php
@@ -43,6 +43,11 @@ final class PostConsume
      */
     private $executionInterrupted;
 
+    /**
+     * @var int
+     */
+    private $exitStatus;
+
     public function __construct(Context $context, SubscriptionConsumer $subscriptionConsumer, int $receivedMessagesCount, int $cycle, int $startTime, LoggerInterface $logger)
     {
         $this->context = $context;
@@ -85,13 +90,19 @@ public function getLogger(): LoggerInterface
         return $this->logger;
     }
 
+    public function getExitStatus(): ?int
+    {
+        return $this->exitStatus;
+    }
+
     public function isExecutionInterrupted(): bool
     {
         return $this->executionInterrupted;
     }
 
-    public function interruptExecution(): void
+    public function interruptExecution(?int $exitStatus = null): void
     {
+        $this->exitStatus = $exitStatus;
         $this->executionInterrupted = true;
     }
 }
diff --git a/Consumption/Context/PostMessageReceived.php b/Consumption/Context/PostMessageReceived.php
index 423830e..1e312da 100644
--- a/Consumption/Context/PostMessageReceived.php
+++ b/Consumption/Context/PostMessageReceived.php
@@ -45,6 +45,11 @@ final class PostMessageReceived
      */
     private $executionInterrupted;
 
+    /**
+     * @var int
+     */
+    private $exitStatus;
+
     public function __construct(
         Context $context,
         Consumer $consumer,
@@ -96,13 +101,19 @@ public function getResult()
         return $this->result;
     }
 
+    public function getExitStatus(): ?int
+    {
+        return $this->exitStatus;
+    }
+
     public function isExecutionInterrupted(): bool
     {
         return $this->executionInterrupted;
     }
 
-    public function interruptExecution(): void
+    public function interruptExecution(?int $exitStatus = null): void
     {
+        $this->exitStatus = $exitStatus;
         $this->executionInterrupted = true;
     }
 }
diff --git a/Consumption/Context/PreConsume.php b/Consumption/Context/PreConsume.php
index a6d83d5..77cc7d0 100644
--- a/Consumption/Context/PreConsume.php
+++ b/Consumption/Context/PreConsume.php
@@ -43,6 +43,11 @@ final class PreConsume
      */
     private $executionInterrupted;
 
+    /**
+     * @var int
+     */
+    private $exitStatus;
+
     public function __construct(Context $context, SubscriptionConsumer $subscriptionConsumer, LoggerInterface $logger, int $cycle, int $receiveTimeout, int $startTime)
     {
         $this->context = $context;
@@ -85,13 +90,19 @@ public function getStartTime(): int
         return $this->startTime;
     }
 
+    public function getExitStatus(): ?int
+    {
+        return $this->exitStatus;
+    }
+
     public function isExecutionInterrupted(): bool
     {
         return $this->executionInterrupted;
     }
 
-    public function interruptExecution(): void
+    public function interruptExecution(?int $exitStatus = null): void
     {
+        $this->exitStatus = $exitStatus;
         $this->executionInterrupted = true;
     }
 }
diff --git a/Consumption/Context/Start.php b/Consumption/Context/Start.php
index cd9f210..84db29c 100644
--- a/Consumption/Context/Start.php
+++ b/Consumption/Context/Start.php
@@ -38,6 +38,11 @@ final class Start
      */
     private $executionInterrupted;
 
+    /**
+     * @var int
+     */
+    private $exitStatus;
+
     /**
      * @param BoundProcessor[] $processors
      */
@@ -105,13 +110,19 @@ public function changeBoundProcessors(array $processors): void
         });
     }
 
+    public function getExitStatus(): ?int
+    {
+        return $this->exitStatus;
+    }
+
     public function isExecutionInterrupted(): bool
     {
         return $this->executionInterrupted;
     }
 
-    public function interruptExecution(): void
+    public function interruptExecution(?int $exitStatus = null): void
     {
+        $this->exitStatus = $exitStatus;
         $this->executionInterrupted = true;
     }
 }
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 3454b30..58b8fa0 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -147,7 +147,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
         $extension->onStart($start);
 
         if ($start->isExecutionInterrupted()) {
-            $this->onEnd($extension, $startTime);
+            $this->onEnd($extension, $startTime, $start->getExitStatus());
 
             return;
         }
@@ -256,7 +256,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
             $extension->onPreConsume($preConsume);
 
             if ($preConsume->isExecutionInterrupted()) {
-                $this->onEnd($extension, $startTime, $subscriptionConsumer);
+                $this->onEnd($extension, $startTime, $preConsume->getExitStatus(), $subscriptionConsumer);
 
                 return;
             }
@@ -267,7 +267,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
             $extension->onPostConsume($postConsume);
 
             if ($interruptExecution || $postConsume->isExecutionInterrupted()) {
-                $this->onEnd($extension, $startTime, $subscriptionConsumer);
+                $this->onEnd($extension, $startTime, $postConsume->getExitStatus(), $subscriptionConsumer);
 
                 return;
             }
@@ -286,11 +286,12 @@ public function setFallbackSubscriptionConsumer(SubscriptionConsumer $fallbackSu
         $this->fallbackSubscriptionConsumer = $fallbackSubscriptionConsumer;
     }
 
-    private function onEnd(ExtensionInterface $extension, int $startTime, SubscriptionConsumer $subscriptionConsumer = null): void
+    private function onEnd(ExtensionInterface $extension, int $startTime, ?int $exitStatus = null, SubscriptionConsumer $subscriptionConsumer = null): void
     {
         $endTime = (int) (microtime(true) * 1000);
 
-        $extension->onEnd(new End($this->interopContext, $startTime, $endTime, $this->logger));
+        $endContext = new End($this->interopContext, $startTime, $endTime, $this->logger, $exitStatus);
+        $extension->onEnd($endContext);
 
         if ($subscriptionConsumer) {
             $subscriptionConsumer->unsubscribeAll();

From a61224766eefbb200b6c13d04da409e36d67278b Mon Sep 17 00:00:00 2001
From: Andriy Volberg <andriy.volberg@gmail.com>
Date: Wed, 13 Feb 2019 19:11:00 +0200
Subject: [PATCH 240/337] capture exit status extension

---
 .../Extension/CaptureExitStatusExtension.php  | 35 +++++++++++++++++++
 Symfony/Consumption/ConsumeCommand.php        |  6 +++-
 2 files changed, 40 insertions(+), 1 deletion(-)
 create mode 100644 Consumption/Extension/CaptureExitStatusExtension.php

diff --git a/Consumption/Extension/CaptureExitStatusExtension.php b/Consumption/Extension/CaptureExitStatusExtension.php
new file mode 100644
index 0000000..49d8981
--- /dev/null
+++ b/Consumption/Extension/CaptureExitStatusExtension.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Enqueue\Consumption\Extension;
+
+use Enqueue\Consumption\Context\End;
+use Enqueue\Consumption\EndExtensionInterface;
+
+class CaptureExitStatusExtension implements EndExtensionInterface
+{
+    /**
+     * @var int
+     */
+    private $exitStatus;
+
+    /**
+     * @var bool
+     */
+    private $isExitStatusCaptured = false;
+
+    public function onEnd(End $context): void
+    {
+        $this->exitStatus = $context->getExitStatus();
+        $this->isExitStatusCaptured = true;
+    }
+
+    public function getExitStatus(): ?int
+    {
+        return $this->exitStatus;
+    }
+
+    public function isExitStatusCaptured(): bool
+    {
+        return $this->isExitStatusCaptured;
+    }
+}
diff --git a/Symfony/Consumption/ConsumeCommand.php b/Symfony/Consumption/ConsumeCommand.php
index e65acc3..22187a1 100644
--- a/Symfony/Consumption/ConsumeCommand.php
+++ b/Symfony/Consumption/ConsumeCommand.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Symfony\Consumption;
 
 use Enqueue\Consumption\ChainExtension;
+use Enqueue\Consumption\Extension\CaptureExitStatusExtension;
 use Enqueue\Consumption\QueueConsumerInterface;
 use Psr\Container\ContainerInterface;
 use Psr\Container\NotFoundExceptionInterface;
@@ -75,9 +76,12 @@ protected function execute(InputInterface $input, OutputInterface $output): ?int
             array_unshift($extensions, $loggerExtension);
         }
 
+        $captureExitStatusExtension = new CaptureExitStatusExtension();
+        array_unshift($extensions, $captureExitStatusExtension);
+
         $consumer->consume(new ChainExtension($extensions));
 
-        return null;
+        return $captureExitStatusExtension->getExitStatus();
     }
 
     private function getQueueConsumer(string $name): QueueConsumerInterface

From 19a61bb276ad0baa5a8a69c8ee6e660f8fb1d1d1 Mon Sep 17 00:00:00 2001
From: Andriy Volberg <andriy.volberg@gmail.com>
Date: Thu, 14 Feb 2019 11:24:18 +0200
Subject: [PATCH 241/337] test capture exit status

---
 .../Extension/CaptureExitStatusExtension.php  | 40 ++++++++++++++++++-
 Tests/Consumption/QueueConsumerTest.php       | 25 ++++++++++++
 2 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/Consumption/Extension/CaptureExitStatusExtension.php b/Consumption/Extension/CaptureExitStatusExtension.php
index 49d8981..f4d967e 100644
--- a/Consumption/Extension/CaptureExitStatusExtension.php
+++ b/Consumption/Extension/CaptureExitStatusExtension.php
@@ -3,9 +3,9 @@
 namespace Enqueue\Consumption\Extension;
 
 use Enqueue\Consumption\Context\End;
-use Enqueue\Consumption\EndExtensionInterface;
+use Enqueue\Consumption\ExtensionInterface;
 
-class CaptureExitStatusExtension implements EndExtensionInterface
+class CaptureExitStatusExtension implements ExtensionInterface
 {
     /**
      * @var int
@@ -32,4 +32,40 @@ public function isExitStatusCaptured(): bool
     {
         return $this->isExitStatusCaptured;
     }
+
+    public function onInitLogger(\Enqueue\Consumption\Context\InitLogger $context): void
+    {
+    }
+
+    public function onMessageReceived(\Enqueue\Consumption\Context\MessageReceived $context): void
+    {
+    }
+
+    public function onPostConsume(\Enqueue\Consumption\Context\PostConsume $context): void
+    {
+    }
+
+    public function onPostMessageReceived(\Enqueue\Consumption\Context\PostMessageReceived $context): void
+    {
+    }
+
+    public function onPreConsume(\Enqueue\Consumption\Context\PreConsume $context): void
+    {
+    }
+
+    public function onPreSubscribe(\Enqueue\Consumption\Context\PreSubscribe $context): void
+    {
+    }
+
+    public function onProcessorException(\Enqueue\Consumption\Context\ProcessorException $context): void
+    {
+    }
+
+    public function onResult(\Enqueue\Consumption\Context\MessageResult $context): void
+    {
+    }
+
+    public function onStart(\Enqueue\Consumption\Context\Start $context): void
+    {
+    }
 }
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 9b0111f..94361b5 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -16,6 +16,7 @@
 use Enqueue\Consumption\Context\ProcessorException;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\Exception\InvalidArgumentException;
+use Enqueue\Consumption\Extension\CaptureExitStatusExtension;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Consumption\QueueConsumer;
 use Enqueue\Consumption\Result;
@@ -1429,6 +1430,30 @@ public function testShouldCallProcessorAsMessageComeAlong()
         $this->assertSame($fooConsumerStub, $actualContexts[2]->getConsumer());
     }
 
+    public function testCaptureExitStatus()
+    {
+        $testExitCode = 5;
+
+        $stubExtension = $this->createExtension();
+
+        $stubExtension
+            ->expects($this->once())
+            ->method('onStart')
+            ->with($this->isInstanceOf(Start::class))
+            ->willReturnCallback(function (Start $context) use ($testExitCode) {
+                $context->interruptExecution($testExitCode);
+            })
+        ;
+
+        $exitExtension = new CaptureExitStatusExtension();
+
+        $consumer = new QueueConsumer($this->createContextStub(), $stubExtension);
+        $consumer->consume($exitExtension);
+
+        $this->assertEquals($testExitCode, $exitExtension->getExitStatus());
+        $this->assertTrue($exitExtension->isExitStatusCaptured());
+    }
+
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject
      */

From 24e9415287908ed574476ab3bd941172be7212e6 Mon Sep 17 00:00:00 2001
From: Andriy Volberg <andriy.volberg@gmail.com>
Date: Thu, 14 Feb 2019 11:38:51 +0200
Subject: [PATCH 242/337] revert to end extension

---
 .../Extension/CaptureExitStatusExtension.php  | 40 +------------------
 Tests/Consumption/QueueConsumerTest.php       |  2 +-
 2 files changed, 3 insertions(+), 39 deletions(-)

diff --git a/Consumption/Extension/CaptureExitStatusExtension.php b/Consumption/Extension/CaptureExitStatusExtension.php
index f4d967e..49d8981 100644
--- a/Consumption/Extension/CaptureExitStatusExtension.php
+++ b/Consumption/Extension/CaptureExitStatusExtension.php
@@ -3,9 +3,9 @@
 namespace Enqueue\Consumption\Extension;
 
 use Enqueue\Consumption\Context\End;
-use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\EndExtensionInterface;
 
-class CaptureExitStatusExtension implements ExtensionInterface
+class CaptureExitStatusExtension implements EndExtensionInterface
 {
     /**
      * @var int
@@ -32,40 +32,4 @@ public function isExitStatusCaptured(): bool
     {
         return $this->isExitStatusCaptured;
     }
-
-    public function onInitLogger(\Enqueue\Consumption\Context\InitLogger $context): void
-    {
-    }
-
-    public function onMessageReceived(\Enqueue\Consumption\Context\MessageReceived $context): void
-    {
-    }
-
-    public function onPostConsume(\Enqueue\Consumption\Context\PostConsume $context): void
-    {
-    }
-
-    public function onPostMessageReceived(\Enqueue\Consumption\Context\PostMessageReceived $context): void
-    {
-    }
-
-    public function onPreConsume(\Enqueue\Consumption\Context\PreConsume $context): void
-    {
-    }
-
-    public function onPreSubscribe(\Enqueue\Consumption\Context\PreSubscribe $context): void
-    {
-    }
-
-    public function onProcessorException(\Enqueue\Consumption\Context\ProcessorException $context): void
-    {
-    }
-
-    public function onResult(\Enqueue\Consumption\Context\MessageResult $context): void
-    {
-    }
-
-    public function onStart(\Enqueue\Consumption\Context\Start $context): void
-    {
-    }
 }
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 94361b5..5e45644 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -1448,7 +1448,7 @@ public function testCaptureExitStatus()
         $exitExtension = new CaptureExitStatusExtension();
 
         $consumer = new QueueConsumer($this->createContextStub(), $stubExtension);
-        $consumer->consume($exitExtension);
+        $consumer->consume(new ChainExtension([$exitExtension]));
 
         $this->assertEquals($testExitCode, $exitExtension->getExitStatus());
         $this->assertTrue($exitExtension->isExitStatusCaptured());

From df939e3c3ededae0fe1e9d19af50555838de8663 Mon Sep 17 00:00:00 2001
From: Andriy Volberg <andriy.volberg@gmail.com>
Date: Thu, 14 Feb 2019 14:40:44 +0200
Subject: [PATCH 243/337] refactoring and consume command tests

---
 ...sExtension.php => ExitStatusExtension.php} |  13 +-
 Symfony/Client/ConsumeCommand.php             |   8 +-
 Symfony/Consumption/ConsumeCommand.php        |   8 +-
 Tests/Consumption/QueueConsumerTest.php       |   5 +-
 Tests/Symfony/Client/ConsumeCommandTest.php   | 118 ++++++++++++++++++
 .../Consumption/ConsumeCommandTest.php        | 104 +++++++++++++++
 6 files changed, 235 insertions(+), 21 deletions(-)
 rename Consumption/Extension/{CaptureExitStatusExtension.php => ExitStatusExtension.php} (57%)

diff --git a/Consumption/Extension/CaptureExitStatusExtension.php b/Consumption/Extension/ExitStatusExtension.php
similarity index 57%
rename from Consumption/Extension/CaptureExitStatusExtension.php
rename to Consumption/Extension/ExitStatusExtension.php
index 49d8981..a430712 100644
--- a/Consumption/Extension/CaptureExitStatusExtension.php
+++ b/Consumption/Extension/ExitStatusExtension.php
@@ -5,31 +5,20 @@
 use Enqueue\Consumption\Context\End;
 use Enqueue\Consumption\EndExtensionInterface;
 
-class CaptureExitStatusExtension implements EndExtensionInterface
+class ExitStatusExtension implements EndExtensionInterface
 {
     /**
      * @var int
      */
     private $exitStatus;
 
-    /**
-     * @var bool
-     */
-    private $isExitStatusCaptured = false;
-
     public function onEnd(End $context): void
     {
         $this->exitStatus = $context->getExitStatus();
-        $this->isExitStatusCaptured = true;
     }
 
     public function getExitStatus(): ?int
     {
         return $this->exitStatus;
     }
-
-    public function isExitStatusCaptured(): bool
-    {
-        return $this->isExitStatusCaptured;
-    }
 }
diff --git a/Symfony/Client/ConsumeCommand.php b/Symfony/Client/ConsumeCommand.php
index e6b25fc..ee8b394 100644
--- a/Symfony/Client/ConsumeCommand.php
+++ b/Symfony/Client/ConsumeCommand.php
@@ -4,6 +4,7 @@
 
 use Enqueue\Client\DriverInterface;
 use Enqueue\Consumption\ChainExtension;
+use Enqueue\Consumption\Extension\ExitStatusExtension;
 use Enqueue\Consumption\Extension\LoggerExtension;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Consumption\QueueConsumerInterface;
@@ -143,9 +144,12 @@ protected function execute(InputInterface $input, OutputInterface $output): ?int
             $consumer->bind($queue, $processor);
         }
 
-        $consumer->consume($this->getRuntimeExtensions($input, $output));
+        $runtimeExtensionChain = $this->getRuntimeExtensions($input, $output);
+        $exitStatusExtension = new ExitStatusExtension();
 
-        return null;
+        $consumer->consume(new ChainExtension([$runtimeExtensionChain, $exitStatusExtension]));
+
+        return $exitStatusExtension->getExitStatus();
     }
 
     protected function getRuntimeExtensions(InputInterface $input, OutputInterface $output): ExtensionInterface
diff --git a/Symfony/Consumption/ConsumeCommand.php b/Symfony/Consumption/ConsumeCommand.php
index 22187a1..b10d19f 100644
--- a/Symfony/Consumption/ConsumeCommand.php
+++ b/Symfony/Consumption/ConsumeCommand.php
@@ -3,7 +3,7 @@
 namespace Enqueue\Symfony\Consumption;
 
 use Enqueue\Consumption\ChainExtension;
-use Enqueue\Consumption\Extension\CaptureExitStatusExtension;
+use Enqueue\Consumption\Extension\ExitStatusExtension;
 use Enqueue\Consumption\QueueConsumerInterface;
 use Psr\Container\ContainerInterface;
 use Psr\Container\NotFoundExceptionInterface;
@@ -76,12 +76,12 @@ protected function execute(InputInterface $input, OutputInterface $output): ?int
             array_unshift($extensions, $loggerExtension);
         }
 
-        $captureExitStatusExtension = new CaptureExitStatusExtension();
-        array_unshift($extensions, $captureExitStatusExtension);
+        $exitStatusExtension = new ExitStatusExtension();
+        array_unshift($extensions, $exitStatusExtension);
 
         $consumer->consume(new ChainExtension($extensions));
 
-        return $captureExitStatusExtension->getExitStatus();
+        return $exitStatusExtension->getExitStatus();
     }
 
     private function getQueueConsumer(string $name): QueueConsumerInterface
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 5e45644..f466c19 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -16,7 +16,7 @@
 use Enqueue\Consumption\Context\ProcessorException;
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\Exception\InvalidArgumentException;
-use Enqueue\Consumption\Extension\CaptureExitStatusExtension;
+use Enqueue\Consumption\Extension\ExitStatusExtension;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Consumption\QueueConsumer;
 use Enqueue\Consumption\Result;
@@ -1445,13 +1445,12 @@ public function testCaptureExitStatus()
             })
         ;
 
-        $exitExtension = new CaptureExitStatusExtension();
+        $exitExtension = new ExitStatusExtension();
 
         $consumer = new QueueConsumer($this->createContextStub(), $stubExtension);
         $consumer->consume(new ChainExtension([$exitExtension]));
 
         $this->assertEquals($testExitCode, $exitExtension->getExitStatus());
-        $this->assertTrue($exitExtension->isExitStatusCaptured());
     }
 
     /**
diff --git a/Tests/Symfony/Client/ConsumeCommandTest.php b/Tests/Symfony/Client/ConsumeCommandTest.php
index 9a45d42..6f50844 100644
--- a/Tests/Symfony/Client/ConsumeCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeCommandTest.php
@@ -8,11 +8,18 @@
 use Enqueue\Client\Route;
 use Enqueue\Client\RouteCollection;
 use Enqueue\Consumption\ChainExtension;
+use Enqueue\Consumption\Context\Start;
+use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\QueueConsumer;
 use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Container\Container;
 use Enqueue\Null\NullQueue;
 use Enqueue\Symfony\Client\ConsumeCommand;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\Consumer;
+use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Exception\SubscriptionConsumerNotSupportedException;
+use Interop\Queue\Queue;
 use PHPUnit\Framework\TestCase;
 use Psr\Container\ContainerInterface;
 use Symfony\Component\Console\Command\Command;
@@ -536,6 +543,49 @@ public function testShouldSkipQueueConsumptionAndUseCustomClientDestinationName(
         ]);
     }
 
+    public function testShouldReturnExitStatusIfSet()
+    {
+        $testExitCode = 678;
+
+        $stubExtension = $this->createExtension();
+
+        $stubExtension
+            ->expects($this->once())
+            ->method('onStart')
+            ->with($this->isInstanceOf(Start::class))
+            ->willReturnCallback(function (Start $context) use ($testExitCode) {
+                $context->interruptExecution($testExitCode);
+            })
+        ;
+
+        $defaultQueue = new NullQueue('default');
+
+        $routeCollection = new RouteCollection([]);
+
+        $processor = $this->createDelegateProcessorMock();
+
+        $driver = $this->createDriverStub($routeCollection);
+        $driver
+            ->expects($this->exactly(1))
+            ->method('createQueue')
+            ->with('default', true)
+            ->willReturn($defaultQueue)
+        ;
+
+        $consumer = new QueueConsumer($this->createContextStub(), $stubExtension);
+
+        $command = new ConsumeCommand(new Container([
+            'enqueue.client.default.queue_consumer' => $consumer,
+            'enqueue.client.default.driver' => $driver,
+            'enqueue.client.default.delegate_processor' => $processor,
+        ]), 'default');
+
+        $tester = new CommandTester($command);
+        $tester->execute([]);
+
+        $this->assertEquals($testExitCode, $tester->getStatusCode());
+    }
+
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|DelegateProcessor
      */
@@ -572,4 +622,72 @@ private function createDriverStub(RouteCollection $routeCollection = null): Driv
 
         return $driverMock;
     }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createContextWithoutSubscriptionConsumerMock(): InteropContext
+    {
+        $contextMock = $this->createMock(InteropContext::class);
+        $contextMock
+            ->expects($this->any())
+            ->method('createSubscriptionConsumer')
+            ->willThrowException(SubscriptionConsumerNotSupportedException::providerDoestNotSupportIt())
+        ;
+
+        return $contextMock;
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
+     */
+    private function createContextStub(Consumer $consumer = null): InteropContext
+    {
+        $context = $this->createContextWithoutSubscriptionConsumerMock();
+        $context
+            ->expects($this->any())
+            ->method('createQueue')
+            ->willReturnCallback(function (string $queueName) {
+                return new NullQueue($queueName);
+            })
+        ;
+        $context
+            ->expects($this->any())
+            ->method('createConsumer')
+            ->willReturnCallback(function (Queue $queue) use ($consumer) {
+                return $consumer ?: $this->createConsumerStub($queue);
+            })
+        ;
+
+        return $context;
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|ExtensionInterface
+     */
+    private function createExtension()
+    {
+        return $this->createMock(ExtensionInterface::class);
+    }
+
+    /**
+     * @param null|mixed $queue
+     *
+     * @return \PHPUnit_Framework_MockObject_MockObject|Consumer
+     */
+    private function createConsumerStub($queue = null): Consumer
+    {
+        if (is_string($queue)) {
+            $queue = new NullQueue($queue);
+        }
+
+        $consumerMock = $this->createMock(Consumer::class);
+        $consumerMock
+            ->expects($this->any())
+            ->method('getQueue')
+            ->willReturn($queue)
+        ;
+
+        return $consumerMock;
+    }
 }
diff --git a/Tests/Symfony/Consumption/ConsumeCommandTest.php b/Tests/Symfony/Consumption/ConsumeCommandTest.php
index 6396568..37ca32f 100644
--- a/Tests/Symfony/Consumption/ConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConsumeCommandTest.php
@@ -3,10 +3,18 @@
 namespace Enqueue\Tests\Symfony\Consumption;
 
 use Enqueue\Consumption\ChainExtension;
+use Enqueue\Consumption\Context\Start;
+use Enqueue\Consumption\ExtensionInterface;
+use Enqueue\Consumption\QueueConsumer;
 use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Container\Container;
+use Enqueue\Null\NullQueue;
 use Enqueue\Symfony\Consumption\ConsumeCommand;
 use Enqueue\Test\ClassExtensionTrait;
+use Interop\Queue\Consumer;
+use Interop\Queue\Context as InteropContext;
+use Interop\Queue\Exception\SubscriptionConsumerNotSupportedException;
+use Interop\Queue\Queue;
 use PHPUnit\Framework\TestCase;
 use Psr\Container\ContainerInterface;
 use Symfony\Component\Console\Command\Command;
@@ -123,6 +131,34 @@ public function testThrowIfNotDefinedTransportRequested()
         $tester->execute(['--transport' => 'not-defined']);
     }
 
+    public function testShouldReturnExitStatusIfSet()
+    {
+        $testExitCode = 678;
+
+        $stubExtension = $this->createExtension();
+
+        $stubExtension
+            ->expects($this->once())
+            ->method('onStart')
+            ->with($this->isInstanceOf(Start::class))
+            ->willReturnCallback(function (Start $context) use ($testExitCode) {
+                $context->interruptExecution($testExitCode);
+            })
+        ;
+
+        $consumer = new QueueConsumer($this->createContextStub(), $stubExtension);
+
+        $command = new ConsumeCommand(new Container([
+            'enqueue.transport.default.queue_consumer' => $consumer,
+        ]), 'default');
+
+        $tester = new CommandTester($command);
+
+        $tester->execute([]);
+
+        $this->assertEquals($testExitCode, $tester->getStatusCode());
+    }
+
     /**
      * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumerInterface
      */
@@ -130,4 +166,72 @@ private function createQueueConsumerMock()
     {
         return $this->createMock(QueueConsumerInterface::class);
     }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function createContextWithoutSubscriptionConsumerMock(): InteropContext
+    {
+        $contextMock = $this->createMock(InteropContext::class);
+        $contextMock
+            ->expects($this->any())
+            ->method('createSubscriptionConsumer')
+            ->willThrowException(SubscriptionConsumerNotSupportedException::providerDoestNotSupportIt())
+        ;
+
+        return $contextMock;
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
+     */
+    private function createContextStub(Consumer $consumer = null): InteropContext
+    {
+        $context = $this->createContextWithoutSubscriptionConsumerMock();
+        $context
+            ->expects($this->any())
+            ->method('createQueue')
+            ->willReturnCallback(function (string $queueName) {
+                return new NullQueue($queueName);
+            })
+        ;
+        $context
+            ->expects($this->any())
+            ->method('createConsumer')
+            ->willReturnCallback(function (Queue $queue) use ($consumer) {
+                return $consumer ?: $this->createConsumerStub($queue);
+            })
+        ;
+
+        return $context;
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|ExtensionInterface
+     */
+    private function createExtension()
+    {
+        return $this->createMock(ExtensionInterface::class);
+    }
+
+    /**
+     * @param null|mixed $queue
+     *
+     * @return \PHPUnit_Framework_MockObject_MockObject|Consumer
+     */
+    private function createConsumerStub($queue = null): Consumer
+    {
+        if (is_string($queue)) {
+            $queue = new NullQueue($queue);
+        }
+
+        $consumerMock = $this->createMock(Consumer::class);
+        $consumerMock
+            ->expects($this->any())
+            ->method('getQueue')
+            ->willReturn($queue)
+        ;
+
+        return $consumerMock;
+    }
 }

From 7b105431670510bbcf66e67feb426590ef8319d7 Mon Sep 17 00:00:00 2001
From: Andriy Volberg <andriy.volberg@gmail.com>
Date: Thu, 14 Feb 2019 15:05:46 +0200
Subject: [PATCH 244/337] manual cs fix

---
 Consumption/Context/PostMessageReceived.php      | 2 +-
 Tests/Consumption/QueueConsumerTest.php          | 2 +-
 Tests/Symfony/Client/ConsumeCommandTest.php      | 2 +-
 Tests/Symfony/Consumption/ConsumeCommandTest.php | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/Consumption/Context/PostMessageReceived.php b/Consumption/Context/PostMessageReceived.php
index 1e312da..91b5326 100644
--- a/Consumption/Context/PostMessageReceived.php
+++ b/Consumption/Context/PostMessageReceived.php
@@ -94,7 +94,7 @@ public function getReceivedAt(): int
     }
 
     /**
-     * @return Result|null|object|string
+     * @return Result|object|string|null
      */
     public function getResult()
     {
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index f466c19..b103393 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -1532,7 +1532,7 @@ private function createExtension()
     }
 
     /**
-     * @param null|mixed $queue
+     * @param mixed|null $queue
      *
      * @return \PHPUnit_Framework_MockObject_MockObject|Consumer
      */
diff --git a/Tests/Symfony/Client/ConsumeCommandTest.php b/Tests/Symfony/Client/ConsumeCommandTest.php
index 6f50844..52bcd9a 100644
--- a/Tests/Symfony/Client/ConsumeCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeCommandTest.php
@@ -671,7 +671,7 @@ private function createExtension()
     }
 
     /**
-     * @param null|mixed $queue
+     * @param mixed|null $queue
      *
      * @return \PHPUnit_Framework_MockObject_MockObject|Consumer
      */
diff --git a/Tests/Symfony/Consumption/ConsumeCommandTest.php b/Tests/Symfony/Consumption/ConsumeCommandTest.php
index 37ca32f..88b8267 100644
--- a/Tests/Symfony/Consumption/ConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConsumeCommandTest.php
@@ -215,7 +215,7 @@ private function createExtension()
     }
 
     /**
-     * @param null|mixed $queue
+     * @param mixed|null $queue
      *
      * @return \PHPUnit_Framework_MockObject_MockObject|Consumer
      */

From 8ec2a24c0316ec7f3a05a2b18e763d9cac9a8262 Mon Sep 17 00:00:00 2001
From: Andriy Volberg <andriy.volberg@gmail.com>
Date: Fri, 15 Feb 2019 10:27:16 +0200
Subject: [PATCH 245/337] correct phrase

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 05dd1df..29cd7a8 100644
--- a/composer.json
+++ b/composer.json
@@ -41,7 +41,7 @@
         "enqueue/dsn": "0.9.x-dev"
     },
     "suggest": {
-        "symfony/console": "^2.8|^3|^4 If you want to use li commands",
+        "symfony/console": "^2.8|^3|^4 If you want to use cli commands",
         "symfony/dependency-injection": "^3.4|^4",
         "symfony/config": "^3.4|^4",
         "enqueue/amqp-ext": "AMQP transport (based on php extension)",

From a752818f9a3d17b46664ba1fe1702b838a94e70c Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Tue, 19 Feb 2019 15:35:48 +0200
Subject: [PATCH 246/337] snsqs client driver

---
 Client/Driver/SnsQsDriver.php | 90 +++++++++++++++++++++++++++++++++++
 Client/Resources.php          |  7 +++
 Resources.php                 |  4 ++
 3 files changed, 101 insertions(+)
 create mode 100644 Client/Driver/SnsQsDriver.php

diff --git a/Client/Driver/SnsQsDriver.php b/Client/Driver/SnsQsDriver.php
new file mode 100644
index 0000000..0720699
--- /dev/null
+++ b/Client/Driver/SnsQsDriver.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace Enqueue\Client\Driver;
+
+use Enqueue\SnsQs\SnsQsContext;
+use Enqueue\SnsQs\SnsQsTopic;
+use Interop\Queue\Destination;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
+/**
+ * @method SnsQsContext getContext()
+ * @method SnsQsTopic createRouterTopic()
+ */
+class SnsQsDriver extends GenericDriver
+{
+    public function __construct(SnsQsContext $context, ...$args)
+    {
+        parent::__construct($context, ...$args);
+    }
+
+    public function setupBroker(LoggerInterface $logger = null): void
+    {
+        $logger = $logger ?: new NullLogger();
+        $log = function ($text, ...$args) use ($logger) {
+            $logger->debug(sprintf('[SqsQsDriver] '.$text, ...$args));
+        };
+
+        // setup router
+        $routerTopic = $this->createRouterTopic();
+        $log('Declare router topic: %s', $routerTopic->getTopicName());
+        $this->getContext()->declareTopic($routerTopic);
+
+        $routerQueue = $this->createQueue($this->getConfig()->getRouterQueue());
+        $log('Declare router queue: %s', $routerQueue->getQueueName());
+        $this->getContext()->declareQueue($routerQueue);
+
+        $log('Bind router queue to topic: %s -> %s', $routerQueue->getQueueName(), $routerTopic->getTopicName());
+        $this->getContext()->bind($routerTopic, $routerQueue);
+
+        // setup queues
+        $declaredQueues = [];
+        $declaredTopics = [];
+        foreach ($this->getRouteCollection()->all() as $route) {
+            $queue = $this->createRouteQueue($route);
+            if (false === array_key_exists($queue->getQueueName(), $declaredQueues)) {
+                $log('Declare processor queue: %s', $queue->getQueueName());
+                $this->getContext()->declareQueue($queue);
+
+                $declaredQueues[$queue->getQueueName()] = true;
+            }
+
+            if ($route->isCommand()) {
+                continue;
+            }
+
+            $topic = $this->doCreateTopic($this->createTransportQueueName($route->getSource(), true));
+            if (false === array_key_exists($topic->getTopicName(), $declaredTopics)) {
+                $log('Declare processor topic: %s', $topic->getTopicName());
+                $this->getContext()->declareTopic($topic);
+
+                $declaredTopics[$topic->getTopicName()] = true;
+            }
+
+            $log('Bind processor queue to topic: %s -> %s', $queue->getQueueName(), $topic->getTopicName());
+            $this->getContext()->bind($topic, $queue);
+        }
+    }
+
+    protected function createRouterTopic(): Destination
+    {
+        return $this->doCreateTopic(
+            $this->createTransportRouterTopicName($this->getConfig()->getRouterTopic(), true)
+        );
+    }
+
+    protected function createTransportRouterTopicName(string $name, bool $prefix): string
+    {
+        $name = parent::createTransportRouterTopicName($name, $prefix);
+
+        return str_replace('.', '_dot_', $name);
+    }
+
+    protected function createTransportQueueName(string $name, bool $prefix): string
+    {
+        $name = parent::createTransportQueueName($name, $prefix);
+
+        return str_replace('.', '_dot_', $name);
+    }
+}
diff --git a/Client/Resources.php b/Client/Resources.php
index b88b822..1207b64 100644
--- a/Client/Resources.php
+++ b/Client/Resources.php
@@ -12,6 +12,7 @@
 use Enqueue\Client\Driver\RabbitMqStompDriver;
 use Enqueue\Client\Driver\RdKafkaDriver;
 use Enqueue\Client\Driver\RedisDriver;
+use Enqueue\Client\Driver\SnsQsDriver;
 use Enqueue\Client\Driver\SqsDriver;
 use Enqueue\Client\Driver\StompDriver;
 
@@ -92,6 +93,12 @@ public static function getKnownDrivers(): array
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/sqs'],
             ];
+            $map[] = [
+                'schemes' => ['snsqs'],
+                'driverClass' => SnsQsDriver::class,
+                'requiredSchemeExtensions' => [],
+                'packages' => ['enqueue/enqueue', 'enqueue/sqs', 'enqueue/sns', 'enqueue/snsqs'],
+            ];
             $map[] = [
                 'schemes' => ['stomp'],
                 'driverClass' => StompDriver::class,
diff --git a/Resources.php b/Resources.php
index a3d94da..0c804d9 100644
--- a/Resources.php
+++ b/Resources.php
@@ -155,6 +155,10 @@ public static function getKnownConnections(): array
                 'schemes' => ['sqs'],
                 'supportedSchemeExtensions' => [],
                 'package' => 'enqueue/sqs', ];
+            $map[SqsConnectionFactory::class] = [
+                'schemes' => ['snsqs'],
+                'supportedSchemeExtensions' => [],
+                'package' => 'enqueue/snsqs', ];
             $map[GpsConnectionFactory::class] = [
                 'schemes' => ['gps'],
                 'supportedSchemeExtensions' => [],

From 4c9efd0a854b186cd296fa4bc7e1d30ab5ab2ecc Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Tue, 19 Feb 2019 18:15:00 +0200
Subject: [PATCH 247/337] snsqs client driver

---
 Resources.php | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Resources.php b/Resources.php
index 0c804d9..75884ba 100644
--- a/Resources.php
+++ b/Resources.php
@@ -14,6 +14,7 @@
 use Enqueue\Pheanstalk\PheanstalkConnectionFactory;
 use Enqueue\RdKafka\RdKafkaConnectionFactory;
 use Enqueue\Redis\RedisConnectionFactory;
+use Enqueue\SnsQs\SnsQsConnectionFactory;
 use Enqueue\Sqs\SqsConnectionFactory;
 use Enqueue\Stomp\StompConnectionFactory;
 use Enqueue\Wamp\WampConnectionFactory;
@@ -155,7 +156,7 @@ public static function getKnownConnections(): array
                 'schemes' => ['sqs'],
                 'supportedSchemeExtensions' => [],
                 'package' => 'enqueue/sqs', ];
-            $map[SqsConnectionFactory::class] = [
+            $map[SnsQsConnectionFactory::class] = [
                 'schemes' => ['snsqs'],
                 'supportedSchemeExtensions' => [],
                 'package' => 'enqueue/snsqs', ];

From fb23dca34d40f5c149d280cb8ab2e0bd62a49c02 Mon Sep 17 00:00:00 2001
From: Alexander Kozienko <a.s.kozienko@gmail.com>
Date: Wed, 20 Feb 2019 09:56:06 +0200
Subject: [PATCH 248/337] snsqs fix typo

---
 Client/Driver/SnsQsDriver.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Client/Driver/SnsQsDriver.php b/Client/Driver/SnsQsDriver.php
index 0720699..2b1d4f2 100644
--- a/Client/Driver/SnsQsDriver.php
+++ b/Client/Driver/SnsQsDriver.php
@@ -10,7 +10,7 @@
 
 /**
  * @method SnsQsContext getContext()
- * @method SnsQsTopic createRouterTopic()
+ * @method SnsQsTopic   createRouterTopic()
  */
 class SnsQsDriver extends GenericDriver
 {

From bcbdf40239edb6fe0f507a5b8437b036985192e6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A1chym=20Tou=C5=A1ek?= <enumag@gmail.com>
Date: Thu, 21 Feb 2019 12:55:04 +0100
Subject: [PATCH 249/337] Fix exception loop in QueueConsumer

---
 Consumption/QueueConsumer.php | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 58b8fa0..1987159 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -317,18 +317,16 @@ private function onProcessorException(ExtensionInterface $extension, Consumer $c
 
             return $result;
         } catch (\Exception $e) {
-            $wrapper = $e;
-            while ($prev = $wrapper->getPrevious()) {
+            $prev = $e;
+            do {
                 if ($exception === $wrapper = $prev) {
                     throw $e;
                 }
-            }
+            } while ($prev = $wrapper->getPrevious());
 
-            if ($exception !== $wrapper) {
-                $prev = new \ReflectionProperty('Exception', 'previous');
-                $prev->setAccessible(true);
-                $prev->setValue($wrapper, $exception);
-            }
+            $prev = new \ReflectionProperty($wrapper instanceof \Exception ? \Exception::class : \Error::class, 'previous');
+            $prev->setAccessible(true);
+            $prev->setValue($wrapper, $exception);
 
             throw $e;
         }

From 32b9c4a4929a5d5be6e65fae6c1b062e1451660e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A1chym=20Tou=C5=A1ek?= <enumag@gmail.com>
Date: Thu, 21 Feb 2019 13:06:01 +0100
Subject: [PATCH 250/337] Improve comment

---
 Consumption/QueueConsumer.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 1987159..30d438d 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -299,7 +299,7 @@ private function onEnd(ExtensionInterface $extension, int $startTime, ?int $exit
     }
 
     /**
-     * The logic is similar to one in Symfony's ExceptionListener::.
+     * The logic is similar to one in Symfony's ExceptionListener::onKernelException().
      *
      * https://github.com/symfony/symfony/blob/cbe289517470eeea27162fd2d523eb29c95f775f/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php#L77
      */

From 63f3c4b810a2bf197bc0fb26c64170a7c1c614ce Mon Sep 17 00:00:00 2001
From: Marko Kaznovac <kaznovac@users.noreply.github.com>
Date: Wed, 6 Mar 2019 21:23:12 +0100
Subject: [PATCH 251/337] fix comment on QueueConsumer constructor

default timeout is 10000 milliseconds and this constructor will not accept float
---
 Consumption/QueueConsumer.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 30d438d..6cb9029 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -43,7 +43,7 @@ final class QueueConsumer implements QueueConsumerInterface
     private $boundProcessors;
 
     /**
-     * @var int|float in milliseconds
+     * @var int in milliseconds
      */
     private $receiveTimeout;
 
@@ -59,7 +59,7 @@ final class QueueConsumer implements QueueConsumerInterface
 
     /**
      * @param BoundProcessor[] $boundProcessors
-     * @param int|float        $receiveTimeout  the time in milliseconds queue consumer waits for a message (10 ms by default)
+     * @param int              $receiveTimeout  the time in milliseconds queue consumer waits for a message (10000 ms by default)
      */
     public function __construct(
         InteropContext $interopContext,

From 88df6be34fb9e9b98dcdacfdda88058517213927 Mon Sep 17 00:00:00 2001
From: gpenverne <gpenverne@gmail.com>
Date: Thu, 21 Mar 2019 16:54:44 +0100
Subject: [PATCH 252/337] Minor typo fix in docblock

---
 Consumption/Result.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Consumption/Result.php b/Consumption/Result.php
index b94b744..c5659bc 100644
--- a/Consumption/Result.php
+++ b/Consumption/Result.php
@@ -13,12 +13,12 @@ class Result
     const ACK = Processor::ACK;
 
     /**
-     * @see Processor::ACK for more details
+     * @see Processor::REJECT for more details
      */
     const REJECT = Processor::REJECT;
 
     /**
-     * @see Processor::ACK for more details
+     * @see Processor::REQUEUE for more details
      */
     const REQUEUE = Processor::REQUEUE;
 

From 96dae58da437402ef6e076e915cda19cc1fc1a55 Mon Sep 17 00:00:00 2001
From: Yanick Witschi <yanick.witschi@terminal42.ch>
Date: Wed, 10 Apr 2019 16:56:18 +0200
Subject: [PATCH 253/337] Fixed docs

---
 Symfony/DependencyInjection/TransportFactory.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index 5cc1a65..54dba4b 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -117,7 +117,7 @@ public static function getQueueConsumerConfiguration(string $name = 'consumption
                 ->integerNode('receive_timeout')
                     ->min(0)
                     ->defaultValue(10000)
-                    ->info('the time in milliseconds queue consumer waits for a message (100 ms by default)')
+                    ->info('the time in milliseconds queue consumer waits for a message (10000 ms by default)')
                 ->end()
         ;
 

From e3ed1d5908a613fdaa74004502c1a505839c4335 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Tue, 14 May 2019 16:03:21 +0300
Subject: [PATCH 254/337] [client] Lazy producer.

---
 .../DependencyInjection/ClientFactory.php     |  15 +-
 Symfony/Client/LazyProducer.php               |  37 +++++
 .../BuildProcessorRegistryPassTest.php        |   4 +-
 .../BuildProcessorRegistryPassTest.php        |   4 +-
 Tests/Symfony/LazyProducerTest.php            | 133 ++++++++++++++++++
 5 files changed, 186 insertions(+), 7 deletions(-)
 create mode 100644 Symfony/Client/LazyProducer.php
 create mode 100644 Tests/Symfony/LazyProducerTest.php

diff --git a/Symfony/Client/DependencyInjection/ClientFactory.php b/Symfony/Client/DependencyInjection/ClientFactory.php
index e69311e..bdae6f3 100644
--- a/Symfony/Client/DependencyInjection/ClientFactory.php
+++ b/Symfony/Client/DependencyInjection/ClientFactory.php
@@ -21,12 +21,14 @@
 use Enqueue\Consumption\QueueConsumer;
 use Enqueue\Rpc\RpcFactory;
 use Enqueue\Symfony\Client\FlushSpoolProducerListener;
+use Enqueue\Symfony\Client\LazyProducer;
 use Enqueue\Symfony\ContainerProcessorRegistry;
 use Enqueue\Symfony\DependencyInjection\TransportFactory;
 use Enqueue\Symfony\DiUtils;
 use Interop\Queue\Context;
 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
 use Symfony\Component\Config\Definition\Builder\NodeDefinition;
+use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Reference;
@@ -119,14 +121,21 @@ public function build(ContainerBuilder $container, array $config): void
         ;
 
         $container->register($this->diUtils->format('producer'), Producer::class)
+            // @deprecated
             ->setPublic(true)
             ->addArgument($this->diUtils->reference('driver'))
             ->addArgument($this->diUtils->reference('rpc_factory'))
             ->addArgument($this->diUtils->reference('client_extensions'))
         ;
 
+        $lazyProducer = $container->register($this->diUtils->format('lazy_producer'), LazyProducer::class);
+        $lazyProducer->addArgument(ServiceLocatorTagPass::register($container, [
+            $this->diUtils->format('producer') => new Reference($this->diUtils->format('producer')),
+        ]));
+        $lazyProducer->addArgument($this->diUtils->format('producer'));
+
         $container->register($this->diUtils->format('spool_producer'), SpoolProducer::class)
-            ->addArgument($this->diUtils->reference('producer'))
+            ->addArgument($this->diUtils->reference('lazy_producer'))
         ;
 
         $container->register($this->diUtils->format('client_extensions'), ChainExtension::class)
@@ -200,12 +209,12 @@ public function build(ContainerBuilder $container, array $config): void
                 $this->diUtils->format('queue_consumer') => $this->diUtils->reference('queue_consumer'),
                 $this->diUtils->format('driver') => $this->diUtils->reference('driver'),
                 $this->diUtils->format('delegate_processor') => $this->diUtils->reference('delegate_processor'),
-                $this->diUtils->format('producer') => $this->diUtils->reference('producer'),
+                $this->diUtils->format('producer') => $this->diUtils->reference('lazy_producer'),
             ]));
         }
 
         if ($this->default) {
-            $container->setAlias(ProducerInterface::class, $this->diUtils->format('producer'));
+            $container->setAlias(ProducerInterface::class, $this->diUtils->format('lazy_producer'));
             $container->setAlias(SpoolProducer::class, $this->diUtils->format('spool_producer'));
 
             if (DiUtils::DEFAULT_CONFIG !== $this->diUtils->getConfigName()) {
diff --git a/Symfony/Client/LazyProducer.php b/Symfony/Client/LazyProducer.php
new file mode 100644
index 0000000..8dd3aad
--- /dev/null
+++ b/Symfony/Client/LazyProducer.php
@@ -0,0 +1,37 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Enqueue\Symfony\Client;
+
+use Enqueue\Client\ProducerInterface;
+use Enqueue\Rpc\Promise;
+use Psr\Container\ContainerInterface;
+
+class LazyProducer implements ProducerInterface
+{
+    private $container;
+
+    private $producerId;
+
+    public function __construct(ContainerInterface $container, string $producerId)
+    {
+        $this->container = $container;
+        $this->producerId = $producerId;
+    }
+
+    public function sendEvent(string $topic, $message): void
+    {
+        $this->getRealProducer()->sendEvent($topic, $message);
+    }
+
+    public function sendCommand(string $command, $message, bool $needReply = false): ?Promise
+    {
+        return $this->getRealProducer()->sendCommand($command, $message, $needReply);
+    }
+
+    private function getRealProducer(): ProducerInterface
+    {
+        return $this->container->get($this->producerId);
+    }
+}
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php
index c0142f0..4d95ca0 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php
@@ -136,10 +136,10 @@ private function assertLocatorServices(ContainerBuilder $container, $locatorId,
         $locatorId = (string) $locatorId;
 
         $this->assertTrue($container->hasDefinition($locatorId));
-        $this->assertRegExp('/service_locator\..*?\.enqueue\./', $locatorId);
+        $this->assertRegExp('/\.?service_locator\..*?\.enqueue\./', $locatorId);
 
         $match = [];
-        if (false == preg_match('/(service_locator\..*?)\.enqueue\./', $locatorId, $match)) {
+        if (false == preg_match('/(\.?service_locator\..*?)\.enqueue\./', $locatorId, $match)) {
             $this->fail('preg_match should not failed');
         }
 
diff --git a/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php b/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
index d122e04..989d048 100644
--- a/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
+++ b/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
@@ -199,10 +199,10 @@ private function assertLocatorServices(ContainerBuilder $container, $locatorId,
         $locatorId = (string) $locatorId;
 
         $this->assertTrue($container->hasDefinition($locatorId));
-        $this->assertRegExp('/service_locator\..*?\.enqueue\./', $locatorId);
+        $this->assertRegExp('/\.?service_locator\..*?\.enqueue\./', $locatorId);
 
         $match = [];
-        if (false == preg_match('/(service_locator\..*?)\.enqueue\./', $locatorId, $match)) {
+        if (false == preg_match('/(\.?service_locator\..*?)\.enqueue\./', $locatorId, $match)) {
             $this->fail('preg_match should not failed');
         }
 
diff --git a/Tests/Symfony/LazyProducerTest.php b/Tests/Symfony/LazyProducerTest.php
new file mode 100644
index 0000000..b574414
--- /dev/null
+++ b/Tests/Symfony/LazyProducerTest.php
@@ -0,0 +1,133 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Enqueue\Tests\Symfony;
+
+use Enqueue\Client\ProducerInterface;
+use Enqueue\Rpc\Promise;
+use Enqueue\Symfony\Client\LazyProducer;
+use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\TestCase;
+use Psr\Container\ContainerInterface;
+
+class LazyProducerTest extends TestCase
+{
+    use ClassExtensionTrait;
+
+    public function testShouldImplementProducerInterface()
+    {
+        $this->assertClassImplements(ProducerInterface::class, LazyProducer::class);
+    }
+
+    public function testCouldBeConstructedWithContainerAndServiceId()
+    {
+        new LazyProducer($this->createContainerMock(), 'realProducerId');
+    }
+
+    public function testShouldNotCallRealProducerInConstructor()
+    {
+        $containerMock = $this->createContainerMock();
+        $containerMock
+            ->expects($this->never())
+            ->method('get')
+        ;
+
+        new LazyProducer($containerMock, 'realProducerId');
+    }
+
+    public function testShouldProxyAllArgumentOnSendEvent()
+    {
+        $topic = 'theTopic';
+        $message = 'theMessage';
+
+        $realProducerMock = $this->createProducerMock();
+        $realProducerMock
+            ->expects($this->once())
+            ->method('sendEvent')
+            ->with($topic, $message)
+        ;
+
+        $containerMock = $this->createContainerMock();
+        $containerMock
+            ->expects($this->once())
+            ->method('get')
+            ->with('realProducerId')
+            ->willReturn($realProducerMock)
+        ;
+
+        $lazyProducer = new LazyProducer($containerMock, 'realProducerId');
+
+        $lazyProducer->sendEvent($topic, $message);
+    }
+
+    public function testShouldProxyAllArgumentOnSendCommand()
+    {
+        $command = 'theCommand';
+        $message = 'theMessage';
+        $needReply = false;
+
+        $realProducerMock = $this->createProducerMock();
+        $realProducerMock
+            ->expects($this->once())
+            ->method('sendCommand')
+            ->with($command, $message, $needReply)
+        ;
+
+        $containerMock = $this->createContainerMock();
+        $containerMock
+            ->expects($this->once())
+            ->method('get')
+            ->with('realProducerId')
+            ->willReturn($realProducerMock)
+        ;
+
+        $lazyProducer = new LazyProducer($containerMock, 'realProducerId');
+
+        $result = $lazyProducer->sendCommand($command, $message, $needReply);
+
+        $this->assertNull($result);
+    }
+
+    public function testShouldProxyReturnedPromiseBackOnSendCommand()
+    {
+        $expectedPromise = $this->createMock(Promise::class);
+
+        $realProducerMock = $this->createProducerMock();
+        $realProducerMock
+            ->expects($this->once())
+            ->method('sendCommand')
+            ->willReturn($expectedPromise)
+        ;
+
+        $containerMock = $this->createContainerMock();
+        $containerMock
+            ->expects($this->once())
+            ->method('get')
+            ->with('realProducerId')
+            ->willReturn($realProducerMock)
+        ;
+
+        $lazyProducer = new LazyProducer($containerMock, 'realProducerId');
+
+        $actualPromise = $lazyProducer->sendCommand('aCommand', 'aMessage', true);
+
+        $this->assertSame($expectedPromise, $actualPromise);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|ProducerInterface
+     */
+    private function createProducerMock(): ProducerInterface
+    {
+        return $this->createMock(ProducerInterface::class);
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|ContainerInterface
+     */
+    private function createContainerMock(): ContainerInterface
+    {
+        return $this->createMock(ContainerInterface::class);
+    }
+}

From 414e88c77ab712c3228c3f96f844396d3852a87c Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 24 May 2019 16:42:09 +0300
Subject: [PATCH 255/337] Fix deprecation notice: The method bindCallback will
 require a new argument in next major version

back_consumer_1   | consumer1 | [2019-05-24 12:57:13] php.INFO: User
Deprecated: The "Enqueue\Consumption\QueueConsumer::bindCallback()"
method will require a new "string|InteropQueue $queueName" argument in
the next major version of its parent class
"Enqueue\Consumption\QueueConsumerInterface", not defining it is
deprecated. {"exception":"[object] (ErrorException(code: 0): User
Deprecated: The \"Enqueue\\Consumption\\QueueConsumer::bindCallback()\"
method will require a new \"string|InteropQueue $queueName\" argument in
the next major version of its parent class
\"Enqueue\\Consumption\\QueueConsumerInterface\", not defining it is
deprecated. at /app/vendor/symfony/debug/DebugClassLoader.php:199)"} []
---
 Consumption/QueueConsumerInterface.php | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/Consumption/QueueConsumerInterface.php b/Consumption/QueueConsumerInterface.php
index c9c99ef..430aa87 100644
--- a/Consumption/QueueConsumerInterface.php
+++ b/Consumption/QueueConsumerInterface.php
@@ -27,9 +27,8 @@ public function bind($queueName, Processor $processor): self;
 
     /**
      * @param string|InteropQueue $queueName
-     * @param mixed               $queue
      */
-    public function bindCallback($queue, callable $processor): self;
+    public function bindCallback($queueName, callable $processor): self;
 
     /**
      * Runtime extension - is an extension or a collection of extensions which could be set on runtime.

From b0cb2cf3547975b332a8428414650a37c4cf643a Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 24 May 2019 16:49:05 +0300
Subject: [PATCH 256/337] [client] Fix reference to logger service.

---
 Symfony/Client/DependencyInjection/ClientFactory.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Symfony/Client/DependencyInjection/ClientFactory.php b/Symfony/Client/DependencyInjection/ClientFactory.php
index bdae6f3..2eede31 100644
--- a/Symfony/Client/DependencyInjection/ClientFactory.php
+++ b/Symfony/Client/DependencyInjection/ClientFactory.php
@@ -165,7 +165,7 @@ public function build(ContainerBuilder $container, array $config): void
             ->addArgument($this->diUtils->reference('context'))
             ->addArgument($this->diUtils->reference('consumption_extensions'))
             ->addArgument([])
-            ->addArgument($this->diUtils->reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE))
+            ->addArgument(new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE))
             ->addArgument($config['consumption']['receive_timeout'])
         ;
 

From 28e42f6d85bb6a4a77353620487bff0ddacfd960 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Fri, 24 May 2019 17:07:00 +0300
Subject: [PATCH 257/337] [client] Fix --logger option. Removed unintentionally
 set console logger.

---
 Symfony/Client/ConsumeCommand.php | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/Symfony/Client/ConsumeCommand.php b/Symfony/Client/ConsumeCommand.php
index ee8b394..fee54e1 100644
--- a/Symfony/Client/ConsumeCommand.php
+++ b/Symfony/Client/ConsumeCommand.php
@@ -5,7 +5,6 @@
 use Enqueue\Client\DriverInterface;
 use Enqueue\Consumption\ChainExtension;
 use Enqueue\Consumption\Extension\ExitStatusExtension;
-use Enqueue\Consumption\Extension\LoggerExtension;
 use Enqueue\Consumption\ExtensionInterface;
 use Enqueue\Consumption\QueueConsumerInterface;
 use Enqueue\Symfony\Consumption\ChooseLoggerCommandTrait;
@@ -18,7 +17,6 @@
 use Symfony\Component\Console\Input\InputArgument;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputOption;
-use Symfony\Component\Console\Logger\ConsoleLogger;
 use Symfony\Component\Console\Output\OutputInterface;
 
 class ConsumeCommand extends Command
@@ -154,7 +152,7 @@ protected function execute(InputInterface $input, OutputInterface $output): ?int
 
     protected function getRuntimeExtensions(InputInterface $input, OutputInterface $output): ExtensionInterface
     {
-        $extensions = [new LoggerExtension(new ConsoleLogger($output))];
+        $extensions = [];
         $extensions = array_merge($extensions, $this->getLimitsExtensions($input, $output));
 
         $driver = $this->getDriver($input->getOption('client'));

From 0afdc6cde5214ee36aaadd45235194020805b3fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= <pawel@niedzielski.biz.pl>
Date: Mon, 27 May 2019 15:12:22 +0200
Subject: [PATCH 258/337] Prefer github pages in packages' readme files

---
 README.md | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/README.md b/README.md
index 0b7fb8a..a1e076e 100644
--- a/README.md
+++ b/README.md
@@ -13,26 +13,26 @@ Enqueue is an MIT-licensed open source project with its ongoing development made
 [![Build Status](https://travis-ci.org/php-enqueue/enqueue.png?branch=master)](https://travis-ci.org/php-enqueue/enqueue)
 [![Total Downloads](https://poser.pugx.org/enqueue/enqueue/d/total.png)](https://packagist.org/packages/enqueue/enqueue)
 [![Latest Stable Version](https://poser.pugx.org/enqueue/enqueue/version.png)](https://packagist.org/packages/enqueue/enqueue)
- 
-It contains advanced features build on top of a transport component. 
+
+It contains advanced features build on top of a transport component.
 Client component kind of plug and play things or consumption component that simplify message processing a lot.
-Read more about it in documentation. 
+Read more about it in documentation.
 
 ## Resources
 
 * [Site](https://enqueue.forma-pro.com/)
-* [Documentation](https://github.com/php-enqueue/enqueue-dev/blob/master/docs/index.md)
+* [Documentation](https://php-enqueue.github.com/)
 * [Questions](https://gitter.im/php-enqueue/Lobby)
 * [Issue Tracker](https://github.com/php-enqueue/enqueue-dev/issues)
 
 ## Developed by Forma-Pro
 
-Forma-Pro is a full stack development company which interests also spread to open source development. 
-Being a team of strong professionals we have an aim an ability to help community by developing cutting edge solutions in the areas of e-commerce, docker & microservice oriented architecture where we have accumulated a huge many-years experience. 
+Forma-Pro is a full stack development company which interests also spread to open source development.
+Being a team of strong professionals we have an aim an ability to help community by developing cutting edge solutions in the areas of e-commerce, docker & microservice oriented architecture where we have accumulated a huge many-years experience.
 Our main specialization is Symfony framework based solution, but we are always looking to the technologies that allow us to do our job the best way. We are committed to creating solutions that revolutionize the way how things are developed in aspects of architecture & scalability.
 
 If you have any questions and inquires about our open source development, this product particularly or any other matter feel free to contact at opensource@forma-pro.com
 
 ## License
 
-It is released under the [MIT License](LICENSE).
\ No newline at end of file
+It is released under the [MIT License](LICENSE).

From 711a7033515cd8b3fd9a9e4346ad9eb9e53a6793 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= <pawel@niedzielski.biz.pl>
Date: Wed, 29 May 2019 17:08:39 +0200
Subject: [PATCH 259/337] Fix documentation links

Whoops.
---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index a1e076e..2e5aa5a 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ Read more about it in documentation.
 ## Resources
 
 * [Site](https://enqueue.forma-pro.com/)
-* [Documentation](https://php-enqueue.github.com/)
+* [Documentation](https://php-enqueue.github.io/)
 * [Questions](https://gitter.im/php-enqueue/Lobby)
 * [Issue Tracker](https://github.com/php-enqueue/enqueue-dev/issues)
 

From 40df73c0f01d1566b9508659c59cb1e3d8f37cb8 Mon Sep 17 00:00:00 2001
From: Max Kotliar <kotlyar.maksim@gmail.com>
Date: Mon, 3 Jun 2019 14:38:16 +0300
Subject: [PATCH 260/337] Update ChainExtension.php

---
 Consumption/ChainExtension.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Consumption/ChainExtension.php b/Consumption/ChainExtension.php
index 6911b5b..e2c11a1 100644
--- a/Consumption/ChainExtension.php
+++ b/Consumption/ChainExtension.php
@@ -117,7 +117,7 @@ public function __construct(array $extensions)
             }
 
             if (false == $extensionValid) {
-                throw new \LogicException('Invalid extension given');
+                throw new \LogicException(sprintf('Invalid extension given %s', get_class($extension)));
             }
         });
     }

From 4694032dc5e5dafbe02b990a3c6b7c9be97f1680 Mon Sep 17 00:00:00 2001
From: Max Kotliar <kotlyar.maksim@gmail.com>
Date: Mon, 3 Jun 2019 14:38:39 +0300
Subject: [PATCH 261/337] Update ChainExtension.php

---
 Client/ChainExtension.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Client/ChainExtension.php b/Client/ChainExtension.php
index c5606ce..d01da37 100644
--- a/Client/ChainExtension.php
+++ b/Client/ChainExtension.php
@@ -67,7 +67,7 @@ public function __construct(array $extensions)
             }
 
             if (false == $extensionValid) {
-                throw new \LogicException('Invalid extension given');
+                throw new \LogicException(sprintf('Invalid extension given %s', get_class($extension)));
             }
         });
     }

From 99e724b6319f0333ad7b08dd9c051b9e2310be1c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?=
 <pawel.tadeusz.niedzielski@gmail.com>
Date: Fri, 7 Jun 2019 02:32:02 +0200
Subject: [PATCH 262/337] Fix Code Style in all files

---
 Client/Message.php                                     | 4 ++--
 Consumption/Context/MessageResult.php                  | 2 +-
 Tests/Client/DelegateProcessorTest.php                 | 4 ++--
 Tests/Client/Extension/PrepareBodyExtensionTest.php    | 2 +-
 Tests/Consumption/FallbackSubscriptionConsumerTest.php | 4 ++--
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/Client/Message.php b/Client/Message.php
index 6f1186d..b1cc9c2 100644
--- a/Client/Message.php
+++ b/Client/Message.php
@@ -88,7 +88,7 @@ public function __construct($body = '', array $properties = [], array $headers =
     }
 
     /**
-     * @return null|string
+     * @return string|null
      */
     public function getBody()
     {
@@ -96,7 +96,7 @@ public function getBody()
     }
 
     /**
-     * @param null|string|int|float|array|\JsonSerializable $body
+     * @param string|int|float|array|\JsonSerializable|null $body
      */
     public function setBody($body)
     {
diff --git a/Consumption/Context/MessageResult.php b/Consumption/Context/MessageResult.php
index 302899c..4fa8f7d 100644
--- a/Consumption/Context/MessageResult.php
+++ b/Consumption/Context/MessageResult.php
@@ -76,7 +76,7 @@ public function getReceivedAt(): int
     }
 
     /**
-     * @return Result|null|object|string
+     * @return Result|object|string|null
      */
     public function getResult()
     {
diff --git a/Tests/Client/DelegateProcessorTest.php b/Tests/Client/DelegateProcessorTest.php
index 5ed49d8..4fe6bd5 100644
--- a/Tests/Client/DelegateProcessorTest.php
+++ b/Tests/Client/DelegateProcessorTest.php
@@ -39,7 +39,7 @@ public function testShouldProcessMessage()
             ->expects($this->once())
             ->method('process')
             ->with($this->identicalTo($message), $this->identicalTo($session))
-            ->will($this->returnValue('return-value'))
+            ->willReturn('return-value')
         ;
 
         $processorRegistry = $this->createProcessorRegistryMock();
@@ -47,7 +47,7 @@ public function testShouldProcessMessage()
             ->expects($this->once())
             ->method('get')
             ->with('processor-name')
-            ->will($this->returnValue($processor))
+            ->willReturn($processor)
         ;
 
         $processor = new DelegateProcessor($processorRegistry);
diff --git a/Tests/Client/Extension/PrepareBodyExtensionTest.php b/Tests/Client/Extension/PrepareBodyExtensionTest.php
index 1b385c1..454034a 100644
--- a/Tests/Client/Extension/PrepareBodyExtensionTest.php
+++ b/Tests/Client/Extension/PrepareBodyExtensionTest.php
@@ -31,7 +31,7 @@ public function testCouldConstructedWithoutAnyArguments()
      * @dataProvider provideMessages
      *
      * @param mixed      $body
-     * @param null|mixed $contentType
+     * @param mixed|null $contentType
      */
     public function testShouldSendStringUnchangedAndAddPlainTextContentTypeIfEmpty(
         $body,
diff --git a/Tests/Consumption/FallbackSubscriptionConsumerTest.php b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
index 588237c..dce6081 100644
--- a/Tests/Consumption/FallbackSubscriptionConsumerTest.php
+++ b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
@@ -227,7 +227,7 @@ public function testShouldConsumeTillTimeoutIsReached()
     }
 
     /**
-     * @param null|mixed $body
+     * @param mixed|null $body
      *
      * @return InteropMessage|\PHPUnit_Framework_MockObject_MockObject
      */
@@ -244,7 +244,7 @@ private function createMessageStub($body = null)
     }
 
     /**
-     * @param null|mixed $queueName
+     * @param mixed|null $queueName
      *
      * @return Consumer|\PHPUnit_Framework_MockObject_MockObject
      */

From db086cbba69362ec4750ba4ffa17ab23952fa9e4 Mon Sep 17 00:00:00 2001
From: Tobias Nyholm <tobias.nyholm@gmail.com>
Date: Fri, 21 Jun 2019 08:27:36 +0200
Subject: [PATCH 263/337] Adding generic driver for schema SNS

---
 Client/Resources.php | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Client/Resources.php b/Client/Resources.php
index 1207b64..6ea5988 100644
--- a/Client/Resources.php
+++ b/Client/Resources.php
@@ -93,6 +93,12 @@ public static function getKnownDrivers(): array
                 'requiredSchemeExtensions' => [],
                 'packages' => ['enqueue/enqueue', 'enqueue/sqs'],
             ];
+            $map[] = [
+                'schemes' => ['sns'],
+                'driverClass' => GenericDriver::class,
+                'requiredSchemeExtensions' => [],
+                'packages' => ['enqueue/enqueue', 'enqueue/sns'],
+            ];
             $map[] = [
                 'schemes' => ['snsqs'],
                 'driverClass' => SnsQsDriver::class,

From 5b024f380bade6a3a7e7f5822d80ad1ccd044c7e Mon Sep 17 00:00:00 2001
From: Nicolas Guilloux <nguilloux@richcongress.com>
Date: Fri, 4 Oct 2019 14:47:10 +0200
Subject: [PATCH 264/337] Fix empty class for autowired services (Fix #957)

---
 .../DependencyInjection/BuildCommandSubscriberRoutesPass.php    | 2 +-
 .../DependencyInjection/BuildTopicSubscriberRoutesPass.php      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php b/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php
index 1527e51..5eee3ef 100644
--- a/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php
+++ b/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php
@@ -35,7 +35,7 @@ public function process(ContainerBuilder $container): void
                     throw new \LogicException('The command subscriber tag could not be applied to a service created by factory.');
                 }
 
-                $processorClass = $processorDefinition->getClass();
+                $processorClass = $processorDefinition->getClass() ?? $serviceId;
                 if (false == class_exists($processorClass)) {
                     throw new \LogicException(sprintf('The processor class "%s" could not be found.', $processorClass));
                 }
diff --git a/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php b/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php
index 5188511..4999921 100644
--- a/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php
+++ b/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php
@@ -35,7 +35,7 @@ public function process(ContainerBuilder $container): void
                     throw new \LogicException('The topic subscriber tag could not be applied to a service created by factory.');
                 }
 
-                $processorClass = $processorDefinition->getClass();
+                $processorClass = $processorDefinition->getClass() ?? $serviceId;
                 if (false == class_exists($processorClass)) {
                     throw new \LogicException(sprintf('The processor class "%s" could not be found.', $processorClass));
                 }

From ea9738c758024e1488dfe6c5c1243c6e2d41730f Mon Sep 17 00:00:00 2001
From: TiMESPLiNTER <github@timesplinter.ch>
Date: Thu, 10 Oct 2019 08:47:47 +0200
Subject: [PATCH 265/337] Add header support

---
 Symfony/Client/ProduceCommand.php | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/Symfony/Client/ProduceCommand.php b/Symfony/Client/ProduceCommand.php
index 61be6a8..e96735e 100644
--- a/Symfony/Client/ProduceCommand.php
+++ b/Symfony/Client/ProduceCommand.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Symfony\Client;
 
+use Enqueue\Client\Message;
 use Enqueue\Client\ProducerInterface;
 use Psr\Container\ContainerInterface;
 use Psr\Container\NotFoundExceptionInterface;
@@ -44,6 +45,7 @@ protected function configure(): void
         $this
             ->setDescription('Sends an event to the topic')
             ->addArgument('message', InputArgument::REQUIRED, 'A message')
+            ->addOption('header', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'The message headers')
             ->addOption('client', 'c', InputOption::VALUE_OPTIONAL, 'The client to consume messages from.', $this->defaultClient)
             ->addOption('topic', null, InputOption::VALUE_OPTIONAL, 'The topic to send a message to')
             ->addOption('command', null, InputOption::VALUE_OPTIONAL, 'The command to send a message to')
@@ -55,6 +57,7 @@ protected function execute(InputInterface $input, OutputInterface $output): ?int
         $topic = $input->getOption('topic');
         $command = $input->getOption('command');
         $message = $input->getArgument('message');
+        $headers = $input->getOption('header');
         $client = $input->getOption('client');
 
         if ($topic && $command) {
@@ -68,7 +71,7 @@ protected function execute(InputInterface $input, OutputInterface $output): ?int
         }
 
         if ($topic) {
-            $producer->sendEvent($topic, $message);
+            $producer->sendEvent($topic, new Message($message, [], $headers));
 
             $output->writeln('An event is sent');
         } elseif ($command) {

From 9250414ee372f341ae789dfe633ff3ae9e1bdc8b Mon Sep 17 00:00:00 2001
From: TiMESPLiNTER <github@timesplinter.ch>
Date: Thu, 10 Oct 2019 15:42:26 +0200
Subject: [PATCH 266/337] Add tests

---
 Tests/Symfony/Client/ProduceCommandTest.php | 25 ++++++++++++++++-----
 1 file changed, 19 insertions(+), 6 deletions(-)

diff --git a/Tests/Symfony/Client/ProduceCommandTest.php b/Tests/Symfony/Client/ProduceCommandTest.php
index 8e9b750..f6aea36 100644
--- a/Tests/Symfony/Client/ProduceCommandTest.php
+++ b/Tests/Symfony/Client/ProduceCommandTest.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Tests\Symfony\Client;
 
+use Enqueue\Client\Message;
 use Enqueue\Client\ProducerInterface;
 use Enqueue\Container\Container;
 use Enqueue\Symfony\Client\ProduceCommand;
@@ -112,11 +113,14 @@ public function testThrowIfBothTopicAndCommandOptionsAreSet()
 
     public function testShouldSendEventToDefaultTransport()
     {
+        $header = 'Content-Type: text/plain';
+        $payload = 'theMessage';
+
         $producerMock = $this->createProducerMock();
         $producerMock
             ->expects($this->once())
             ->method('sendEvent')
-            ->with('theTopic', 'theMessage')
+            ->with('theTopic', new Message($payload, [], [$header]))
         ;
         $producerMock
             ->expects($this->never())
@@ -129,18 +133,22 @@ public function testShouldSendEventToDefaultTransport()
 
         $tester = new CommandTester($command);
         $tester->execute([
-            'message' => 'theMessage',
+            'message' => $payload,
+            '--header' => $header,
             '--topic' => 'theTopic',
         ]);
     }
 
     public function testShouldSendCommandToDefaultTransport()
     {
+        $header = 'Content-Type: text/plain';
+        $payload = 'theMessage';
+
         $producerMock = $this->createProducerMock();
         $producerMock
             ->expects($this->once())
             ->method('sendCommand')
-            ->with('theCommand', 'theMessage')
+            ->with('theCommand', new Message($payload, [], [$header]))
         ;
         $producerMock
             ->expects($this->never())
@@ -153,13 +161,17 @@ public function testShouldSendCommandToDefaultTransport()
 
         $tester = new CommandTester($command);
         $tester->execute([
-            'message' => 'theMessage',
+            'message' => $payload,
+            '--header' => $header,
             '--command' => 'theCommand',
         ]);
     }
 
     public function testShouldSendEventToFooTransport()
     {
+        $header = 'Content-Type: text/plain';
+        $payload = 'theMessage';
+
         $defaultProducerMock = $this->createProducerMock();
         $defaultProducerMock
             ->expects($this->never())
@@ -174,7 +186,7 @@ public function testShouldSendEventToFooTransport()
         $fooProducerMock
             ->expects($this->once())
             ->method('sendEvent')
-            ->with('theTopic', 'theMessage')
+            ->with('theTopic', new Message($payload, [], [$header]))
         ;
         $fooProducerMock
             ->expects($this->never())
@@ -188,7 +200,8 @@ public function testShouldSendEventToFooTransport()
 
         $tester = new CommandTester($command);
         $tester->execute([
-            'message' => 'theMessage',
+            'message' => $payload,
+            '--header' => $header,
             '--topic' => 'theTopic',
             '--client' => 'foo',
         ]);

From 67e37926e023df5896c87db22174367119c5f1f6 Mon Sep 17 00:00:00 2001
From: TiMESPLiNTER <github@timesplinter.ch>
Date: Thu, 10 Oct 2019 15:59:29 +0200
Subject: [PATCH 267/337] Fix tests

---
 Symfony/Client/ProduceCommand.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Symfony/Client/ProduceCommand.php b/Symfony/Client/ProduceCommand.php
index e96735e..51a3659 100644
--- a/Symfony/Client/ProduceCommand.php
+++ b/Symfony/Client/ProduceCommand.php
@@ -57,7 +57,7 @@ protected function execute(InputInterface $input, OutputInterface $output): ?int
         $topic = $input->getOption('topic');
         $command = $input->getOption('command');
         $message = $input->getArgument('message');
-        $headers = $input->getOption('header');
+        $headers = (array) $input->getOption('header');
         $client = $input->getOption('client');
 
         if ($topic && $command) {

From e8f9fdb6d9ec52fb40d54c7ff03a008cfd06f799 Mon Sep 17 00:00:00 2001
From: TiMESPLiNTER <github@timesplinter.ch>
Date: Thu, 10 Oct 2019 16:11:14 +0200
Subject: [PATCH 268/337] Fix tests

---
 Tests/Symfony/Client/ProduceCommandTest.php | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/Tests/Symfony/Client/ProduceCommandTest.php b/Tests/Symfony/Client/ProduceCommandTest.php
index f6aea36..7c50ae0 100644
--- a/Tests/Symfony/Client/ProduceCommandTest.php
+++ b/Tests/Symfony/Client/ProduceCommandTest.php
@@ -43,10 +43,11 @@ public function testShouldHaveExpectedOptions()
         $command = new ProduceCommand($this->createMock(ContainerInterface::class), 'default');
 
         $options = $command->getDefinition()->getOptions();
-        $this->assertCount(3, $options);
+        $this->assertCount(4, $options);
         $this->assertArrayHasKey('client', $options);
         $this->assertArrayHasKey('topic', $options);
         $this->assertArrayHasKey('command', $options);
+        $this->assertArrayHasKey('header', $options);
     }
 
     public function testShouldHaveExpectedAttributes()
@@ -141,14 +142,11 @@ public function testShouldSendEventToDefaultTransport()
 
     public function testShouldSendCommandToDefaultTransport()
     {
-        $header = 'Content-Type: text/plain';
-        $payload = 'theMessage';
-
         $producerMock = $this->createProducerMock();
         $producerMock
             ->expects($this->once())
             ->method('sendCommand')
-            ->with('theCommand', new Message($payload, [], [$header]))
+            ->with('theCommand', 'theMessage')
         ;
         $producerMock
             ->expects($this->never())
@@ -161,8 +159,7 @@ public function testShouldSendCommandToDefaultTransport()
 
         $tester = new CommandTester($command);
         $tester->execute([
-            'message' => $payload,
-            '--header' => $header,
+            'message' => 'theMessage',
             '--command' => 'theCommand',
         ]);
     }

From 2eb27b3d6fcaca993efef82e8b4fa2b84e783e1f Mon Sep 17 00:00:00 2001
From: TiMESPLiNTER <github@timesplinter.ch>
Date: Thu, 10 Oct 2019 16:13:20 +0200
Subject: [PATCH 269/337] Fix tests

---
 Tests/Symfony/Client/SimpleProduceCommandTest.php | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Tests/Symfony/Client/SimpleProduceCommandTest.php b/Tests/Symfony/Client/SimpleProduceCommandTest.php
index 82a7e00..84391cd 100644
--- a/Tests/Symfony/Client/SimpleProduceCommandTest.php
+++ b/Tests/Symfony/Client/SimpleProduceCommandTest.php
@@ -40,10 +40,11 @@ public function testShouldHaveExpectedOptions()
         $command = new SimpleProduceCommand($this->createProducerMock());
 
         $options = $command->getDefinition()->getOptions();
-        $this->assertCount(3, $options);
+        $this->assertCount(4, $options);
         $this->assertArrayHasKey('client', $options);
         $this->assertArrayHasKey('topic', $options);
         $this->assertArrayHasKey('command', $options);
+        $this->assertArrayHasKey('header', $options);
     }
 
     public function testShouldHaveExpectedAttributes()

From 70de3ac98680c902777e16bb7a6d5066e949c56d Mon Sep 17 00:00:00 2001
From: Nicky De Maeyer <nicky.demaeyer@gmail.com>
Date: Fri, 22 Nov 2019 14:53:10 +0100
Subject: [PATCH 270/337] #984 Symfony 4.4 compatibility

---
 Doctrine/DoctrineConnectionFactoryFactory.php | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Doctrine/DoctrineConnectionFactoryFactory.php b/Doctrine/DoctrineConnectionFactoryFactory.php
index db8f0dd..06150e9 100644
--- a/Doctrine/DoctrineConnectionFactoryFactory.php
+++ b/Doctrine/DoctrineConnectionFactoryFactory.php
@@ -2,16 +2,16 @@
 
 namespace Enqueue\Doctrine;
 
+use Doctrine\Bundle\DoctrineBundle\Registry;
 use Enqueue\ConnectionFactoryFactoryInterface;
 use Enqueue\Dbal\ManagerRegistryConnectionFactory;
 use Enqueue\Dsn\Dsn;
 use Interop\Queue\ConnectionFactory;
-use Symfony\Bridge\Doctrine\RegistryInterface;
 
 class DoctrineConnectionFactoryFactory implements ConnectionFactoryFactoryInterface
 {
     /**
-     * @var RegistryInterface
+     * @var Registry
      */
     private $doctrine;
 
@@ -20,7 +20,7 @@ class DoctrineConnectionFactoryFactory implements ConnectionFactoryFactoryInterf
      */
     private $fallbackFactory;
 
-    public function __construct(RegistryInterface $doctrine, ConnectionFactoryFactoryInterface $fallbackFactory)
+    public function __construct(Registry $doctrine, ConnectionFactoryFactoryInterface $fallbackFactory)
     {
         $this->doctrine = $doctrine;
         $this->fallbackFactory = $fallbackFactory;

From 7e8a366bb635c5caaf4c71d761511e7de07dca9d Mon Sep 17 00:00:00 2001
From: Jeroeny <spee.jeroen@gmail.com>
Date: Sat, 23 Nov 2019 11:31:39 +0100
Subject: [PATCH 271/337] allow symfony 5

---
 composer.json | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/composer.json b/composer.json
index 29cd7a8..a18b0a9 100644
--- a/composer.json
+++ b/composer.json
@@ -17,11 +17,11 @@
     },
     "require-dev": {
         "phpunit/phpunit": "~5.5",
-        "symfony/console": "^3.4|^4",
-        "symfony/dependency-injection": "^3.4|^4",
-        "symfony/config": "^3.4|^4",
-        "symfony/event-dispatcher": "^3.4|^4",
-        "symfony/http-kernel": "^3.4|^4",
+        "symfony/console": "^3.4|^4|^5",
+        "symfony/dependency-injection": "^3.4|^4|^5",
+        "symfony/config": "^3.4|^4|^5",
+        "symfony/event-dispatcher": "^3.4|^4|^5",
+        "symfony/http-kernel": "^3.4|^4|^5",
         "enqueue/amqp-ext": "0.9.x-dev",
         "enqueue/amqp-lib": "0.9.x-dev",
         "enqueue/amqp-bunny": "0.9.x-dev",
@@ -41,9 +41,9 @@
         "enqueue/dsn": "0.9.x-dev"
     },
     "suggest": {
-        "symfony/console": "^2.8|^3|^4 If you want to use cli commands",
-        "symfony/dependency-injection": "^3.4|^4",
-        "symfony/config": "^3.4|^4",
+        "symfony/console": "^2.8|^3|^4|^5 If you want to use cli commands",
+        "symfony/dependency-injection": "^3.4|^4|^5",
+        "symfony/config": "^3.4|^4|^5",
         "enqueue/amqp-ext": "AMQP transport (based on php extension)",
         "enqueue/stomp": "STOMP transport",
         "enqueue/fs": "Filesystem transport",

From 44edc1a22e1dd6d3d68e75683c73135809e5949f Mon Sep 17 00:00:00 2001
From: Markus Bachmann <markus.bachmann@bachi.biz>
Date: Sat, 23 Nov 2019 15:57:53 +0100
Subject: [PATCH 272/337] Fixes #984

---
 Doctrine/DoctrineConnectionFactoryFactory.php |  6 +-
 .../DoctrineConnectionFactoryFactoryTest.php  | 67 +++++++++++++++++++
 2 files changed, 70 insertions(+), 3 deletions(-)
 create mode 100644 Tests/DoctrineConnectionFactoryFactoryTest.php

diff --git a/Doctrine/DoctrineConnectionFactoryFactory.php b/Doctrine/DoctrineConnectionFactoryFactory.php
index 06150e9..b6e684c 100644
--- a/Doctrine/DoctrineConnectionFactoryFactory.php
+++ b/Doctrine/DoctrineConnectionFactoryFactory.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Doctrine;
 
-use Doctrine\Bundle\DoctrineBundle\Registry;
+use Doctrine\Common\Persistence\ManagerRegistry;
 use Enqueue\ConnectionFactoryFactoryInterface;
 use Enqueue\Dbal\ManagerRegistryConnectionFactory;
 use Enqueue\Dsn\Dsn;
@@ -11,7 +11,7 @@
 class DoctrineConnectionFactoryFactory implements ConnectionFactoryFactoryInterface
 {
     /**
-     * @var Registry
+     * @var ManagerRegistry
      */
     private $doctrine;
 
@@ -20,7 +20,7 @@ class DoctrineConnectionFactoryFactory implements ConnectionFactoryFactoryInterf
      */
     private $fallbackFactory;
 
-    public function __construct(Registry $doctrine, ConnectionFactoryFactoryInterface $fallbackFactory)
+    public function __construct(ManagerRegistry $doctrine, ConnectionFactoryFactoryInterface $fallbackFactory)
     {
         $this->doctrine = $doctrine;
         $this->fallbackFactory = $fallbackFactory;
diff --git a/Tests/DoctrineConnectionFactoryFactoryTest.php b/Tests/DoctrineConnectionFactoryFactoryTest.php
new file mode 100644
index 0000000..5aca26d
--- /dev/null
+++ b/Tests/DoctrineConnectionFactoryFactoryTest.php
@@ -0,0 +1,67 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Enqueue\Tests;
+
+use Doctrine\Common\Persistence\ManagerRegistry;
+use Enqueue\ConnectionFactoryFactoryInterface;
+use Enqueue\Dbal\ManagerRegistryConnectionFactory;
+use Enqueue\Doctrine\DoctrineConnectionFactoryFactory;
+
+class DoctrineConnectionFactoryFactoryTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var ManagerRegistry|\Prophecy\Prophecy\ObjectProphecy
+     */
+    private $registry;
+    /**
+     * @var ConnectionFactoryFactoryInterface|\Prophecy\Prophecy\ObjectProphecy
+     */
+    private $fallbackFactory;
+    /**
+     * @var DoctrineConnectionFactoryFactory
+     */
+    private $factory;
+
+    protected function setUp()
+    {
+        $this->registry = $this->prophesize(ManagerRegistry::class);
+        $this->fallbackFactory = $this->prophesize(ConnectionFactoryFactoryInterface::class);
+
+        $this->factory = new DoctrineConnectionFactoryFactory($this->registry->reveal(), $this->fallbackFactory->reveal());
+    }
+
+    public function testCreateWithoutArray()
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The config must be either array or DSN string.');
+
+        $this->factory->create(true);
+    }
+
+    public function testCreateWithoutDsn()
+    {
+        $this->expectExceptionMessage(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The config must have dsn key set.');
+
+        $this->factory->create(['foo' => 'bar']);
+    }
+
+    public function testCreateWithDoctrineSchema()
+    {
+        $this->assertInstanceOf(
+            ManagerRegistryConnectionFactory::class,
+            $this->factory->create('doctrine://localhost:3306')
+        );
+    }
+
+    public function testCreateFallback()
+    {
+        $this->fallbackFactory
+            ->create(['dsn' => 'fallback://'])
+            ->shouldBeCalled();
+
+        $this->factory->create(['dsn' => 'fallback://']);
+    }
+}

From f69b8963d051ad497579ebdf2c949fc9941151b3 Mon Sep 17 00:00:00 2001
From: Mathieu Lemoine <mathieu@doctoome.com>
Date: Wed, 11 Dec 2019 16:32:12 +0100
Subject: [PATCH 273/337] Reduced dependency to voryx/Thruway

---
 Doctrine/DoctrineConnectionFactoryFactory.php    |  6 +++---
 Symfony/Client/ConsumeCommand.php                |  4 ++--
 Symfony/Client/ProduceCommand.php                |  4 ++--
 Symfony/Client/RoutesCommand.php                 |  4 ++--
 Symfony/Client/SetupBrokerCommand.php            |  4 ++--
 .../Consumption/ConfigurableConsumeCommand.php   |  4 ++--
 Symfony/Consumption/ConsumeCommand.php           |  4 ++--
 Tests/ArrayProcessorRegistryTest.php             |  3 ++-
 Tests/Client/ChainExtensionTest.php              |  2 +-
 .../DelayRedeliveredMessageExtensionTest.php     | 11 ++++++-----
 .../ExclusiveCommandExtensionTest.php            |  9 +++++----
 .../FlushSpoolProducerExtensionTest.php          |  5 +++--
 .../ConsumptionExtension/LogExtensionTest.php    |  9 +++++----
 .../SetRouterPropertiesExtensionTest.php         |  9 +++++----
 .../SetupBrokerExtensionTest.php                 |  3 ++-
 Tests/Client/DelegateProcessorTest.php           |  7 ++++---
 Tests/Client/Driver/GenericDriverTestsTrait.php  |  6 +++---
 Tests/Client/Driver/RabbitMqStompDriverTest.php  |  4 ++--
 .../Client/Driver/StompManagementClientTest.php  |  8 ++++----
 Tests/Client/DriverPreSendTest.php               |  4 ++--
 Tests/Client/PostSendTest.php                    |  8 ++++----
 Tests/Client/PreSendTest.php                     |  4 ++--
 Tests/Client/ProducerSendCommandTest.php         |  4 ++--
 Tests/Client/ProducerSendEventTest.php           |  4 ++--
 Tests/Client/ProducerTest.php                    |  4 ++--
 Tests/Client/RouterProcessorTest.php             |  2 +-
 Tests/Client/SpoolProducerTest.php               |  2 +-
 Tests/Client/TraceableProducerTest.php           |  2 +-
 Tests/Consumption/ChainExtensionTest.php         | 13 +++++++------
 .../LimitConsumedMessagesExtensionTest.php       |  7 ++++---
 .../LimitConsumerMemoryExtensionTest.php         | 10 ++++++----
 .../LimitConsumptionTimeExtensionTest.php        |  7 ++++---
 Tests/Consumption/Extension/LogExtensionTest.php |  9 +++++----
 .../Extension/LoggerExtensionTest.php            |  3 ++-
 .../Extension/NicenessExtensionTest.php          |  3 ++-
 .../Consumption/Extension/ReplyExtensionTest.php |  7 ++++---
 .../FallbackSubscriptionConsumerTest.php         |  4 ++--
 Tests/Consumption/QueueConsumerTest.php          | 16 ++++++++--------
 Tests/Router/RouteRecipientListProcessorTest.php |  6 +++---
 Tests/Rpc/RpcClientTest.php                      |  7 ++++---
 Tests/Symfony/Client/ConsumeCommandTest.php      | 14 +++++++-------
 .../Client/FlushSpoolProducerListenerTest.php    |  2 +-
 .../Client/Mock/SetupBrokerExtensionCommand.php  |  4 +++-
 Tests/Symfony/Client/ProduceCommandTest.php      |  2 +-
 Tests/Symfony/Client/RoutesCommandTest.php       |  2 +-
 Tests/Symfony/Client/SetupBrokerCommandTest.php  |  2 +-
 .../Symfony/Client/SimpleConsumeCommandTest.php  |  6 +++---
 .../Symfony/Client/SimpleProduceCommandTest.php  |  2 +-
 Tests/Symfony/Client/SimpleRoutesCommandTest.php |  4 ++--
 .../Client/SimpleSetupBrokerCommandTest.php      |  2 +-
 .../ConfigurableConsumeCommandTest.php           |  6 +++---
 Tests/Symfony/Consumption/ConsumeCommandTest.php | 10 +++++-----
 .../Consumption/Mock/LimitsExtensionsCommand.php |  4 +++-
 .../Mock/QueueConsumerOptionsCommand.php         |  4 +++-
 .../QueueConsumerOptionsCommandTraitTest.php     |  2 +-
 .../Consumption/SimpleConsumeCommandTest.php     |  2 +-
 Tests/Symfony/ContainerProcessorRegistryTest.php |  4 ++--
 Tests/Symfony/LazyProducerTest.php               |  4 ++--
 Tests/Util/JSONTest.php                          | 15 ++++++---------
 composer.json                                    |  3 ++-
 phpunit.xml.dist                                 |  1 -
 61 files changed, 174 insertions(+), 153 deletions(-)

diff --git a/Doctrine/DoctrineConnectionFactoryFactory.php b/Doctrine/DoctrineConnectionFactoryFactory.php
index db8f0dd..b6e684c 100644
--- a/Doctrine/DoctrineConnectionFactoryFactory.php
+++ b/Doctrine/DoctrineConnectionFactoryFactory.php
@@ -2,16 +2,16 @@
 
 namespace Enqueue\Doctrine;
 
+use Doctrine\Common\Persistence\ManagerRegistry;
 use Enqueue\ConnectionFactoryFactoryInterface;
 use Enqueue\Dbal\ManagerRegistryConnectionFactory;
 use Enqueue\Dsn\Dsn;
 use Interop\Queue\ConnectionFactory;
-use Symfony\Bridge\Doctrine\RegistryInterface;
 
 class DoctrineConnectionFactoryFactory implements ConnectionFactoryFactoryInterface
 {
     /**
-     * @var RegistryInterface
+     * @var ManagerRegistry
      */
     private $doctrine;
 
@@ -20,7 +20,7 @@ class DoctrineConnectionFactoryFactory implements ConnectionFactoryFactoryInterf
      */
     private $fallbackFactory;
 
-    public function __construct(RegistryInterface $doctrine, ConnectionFactoryFactoryInterface $fallbackFactory)
+    public function __construct(ManagerRegistry $doctrine, ConnectionFactoryFactoryInterface $fallbackFactory)
     {
         $this->doctrine = $doctrine;
         $this->fallbackFactory = $fallbackFactory;
diff --git a/Symfony/Client/ConsumeCommand.php b/Symfony/Client/ConsumeCommand.php
index fee54e1..4f6e5ed 100644
--- a/Symfony/Client/ConsumeCommand.php
+++ b/Symfony/Client/ConsumeCommand.php
@@ -87,7 +87,7 @@ protected function configure(): void
         ;
     }
 
-    protected function execute(InputInterface $input, OutputInterface $output): ?int
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $client = $input->getOption('client');
 
@@ -147,7 +147,7 @@ protected function execute(InputInterface $input, OutputInterface $output): ?int
 
         $consumer->consume(new ChainExtension([$runtimeExtensionChain, $exitStatusExtension]));
 
-        return $exitStatusExtension->getExitStatus();
+        return $exitStatusExtension->getExitStatus() ?? 0;
     }
 
     protected function getRuntimeExtensions(InputInterface $input, OutputInterface $output): ExtensionInterface
diff --git a/Symfony/Client/ProduceCommand.php b/Symfony/Client/ProduceCommand.php
index 51a3659..667871c 100644
--- a/Symfony/Client/ProduceCommand.php
+++ b/Symfony/Client/ProduceCommand.php
@@ -52,7 +52,7 @@ protected function configure(): void
         ;
     }
 
-    protected function execute(InputInterface $input, OutputInterface $output): ?int
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $topic = $input->getOption('topic');
         $command = $input->getOption('command');
@@ -82,7 +82,7 @@ protected function execute(InputInterface $input, OutputInterface $output): ?int
             throw new \LogicException('Either topic or command option should be set, none is set.');
         }
 
-        return null;
+        return 0;
     }
 
     private function getProducer(string $client): ProducerInterface
diff --git a/Symfony/Client/RoutesCommand.php b/Symfony/Client/RoutesCommand.php
index 04d404d..5419f3b 100644
--- a/Symfony/Client/RoutesCommand.php
+++ b/Symfony/Client/RoutesCommand.php
@@ -58,7 +58,7 @@ protected function configure(): void
         $this->driver = null;
     }
 
-    protected function execute(InputInterface $input, OutputInterface $output): ?int
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         try {
             $this->driver = $this->getDriver($input->getOption('client'));
@@ -112,7 +112,7 @@ protected function execute(InputInterface $input, OutputInterface $output): ?int
             $table->render();
         }
 
-        return null;
+        return 0;
     }
 
     private function formatSourceType(Route $route): string
diff --git a/Symfony/Client/SetupBrokerCommand.php b/Symfony/Client/SetupBrokerCommand.php
index 72215a8..65d52ee 100644
--- a/Symfony/Client/SetupBrokerCommand.php
+++ b/Symfony/Client/SetupBrokerCommand.php
@@ -48,7 +48,7 @@ protected function configure(): void
         ;
     }
 
-    protected function execute(InputInterface $input, OutputInterface $output): ?int
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $client = $input->getOption('client');
 
@@ -60,7 +60,7 @@ protected function execute(InputInterface $input, OutputInterface $output): ?int
 
         $output->writeln('Broker set up');
 
-        return null;
+        return 0;
     }
 
     private function getDriver(string $client): DriverInterface
diff --git a/Symfony/Consumption/ConfigurableConsumeCommand.php b/Symfony/Consumption/ConfigurableConsumeCommand.php
index 9c47134..9439f76 100644
--- a/Symfony/Consumption/ConfigurableConsumeCommand.php
+++ b/Symfony/Consumption/ConfigurableConsumeCommand.php
@@ -72,7 +72,7 @@ protected function configure(): void
         ;
     }
 
-    protected function execute(InputInterface $input, OutputInterface $output): ?int
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $transport = $input->getOption('transport');
 
@@ -110,7 +110,7 @@ protected function execute(InputInterface $input, OutputInterface $output): ?int
 
         $consumer->consume(new ChainExtension($extensions));
 
-        return null;
+        return 0;
     }
 
     private function getQueueConsumer(string $name): QueueConsumerInterface
diff --git a/Symfony/Consumption/ConsumeCommand.php b/Symfony/Consumption/ConsumeCommand.php
index b10d19f..b1a54c1 100644
--- a/Symfony/Consumption/ConsumeCommand.php
+++ b/Symfony/Consumption/ConsumeCommand.php
@@ -57,7 +57,7 @@ protected function configure(): void
         ;
     }
 
-    protected function execute(InputInterface $input, OutputInterface $output): ?int
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $transport = $input->getOption('transport');
 
@@ -81,7 +81,7 @@ protected function execute(InputInterface $input, OutputInterface $output): ?int
 
         $consumer->consume(new ChainExtension($extensions));
 
-        return $exitStatusExtension->getExitStatus();
+        return $exitStatusExtension->getExitStatus() ?? 0;
     }
 
     private function getQueueConsumer(string $name): QueueConsumerInterface
diff --git a/Tests/ArrayProcessorRegistryTest.php b/Tests/ArrayProcessorRegistryTest.php
index 50c8025..897276d 100644
--- a/Tests/ArrayProcessorRegistryTest.php
+++ b/Tests/ArrayProcessorRegistryTest.php
@@ -6,6 +6,7 @@
 use Enqueue\ProcessorRegistryInterface;
 use Enqueue\Test\ClassExtensionTrait;
 use Interop\Queue\Processor;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 
 class ArrayProcessorRegistryTest extends TestCase
@@ -51,7 +52,7 @@ public function testShouldAllowGetProcessorAddedViaAddMethod()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Processor
+     * @return MockObject|Processor
      */
     protected function createProcessorMock()
     {
diff --git a/Tests/Client/ChainExtensionTest.php b/Tests/Client/ChainExtensionTest.php
index 95f3144..a59005b 100644
--- a/Tests/Client/ChainExtensionTest.php
+++ b/Tests/Client/ChainExtensionTest.php
@@ -151,7 +151,7 @@ public function testShouldProxyOnPostSentToAllInternalExtensions()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|ExtensionInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|ExtensionInterface
      */
     protected function createExtension()
     {
diff --git a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
index 9c8c244..050b4f2 100644
--- a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
@@ -15,6 +15,7 @@
 use Interop\Queue\Destination;
 use Interop\Queue\Message as TransportMessage;
 use Interop\Queue\Processor;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
@@ -144,7 +145,7 @@ public function testShouldDoNothingIfMessageIsRedeliveredButResultWasAlreadySetO
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createDriverMock(): DriverInterface
     {
@@ -152,7 +153,7 @@ private function createDriverMock(): DriverInterface
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createContextMock(): InteropContext
     {
@@ -160,7 +161,7 @@ private function createContextMock(): InteropContext
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createProcessorMock(): Processor
     {
@@ -170,7 +171,7 @@ private function createProcessorMock(): Processor
     /**
      * @param mixed $queue
      *
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createConsumerStub($queue): Consumer
     {
@@ -185,7 +186,7 @@ private function createConsumerStub($queue): Consumer
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createLoggerMock(): LoggerInterface
     {
diff --git a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
index 8778367..878778b 100644
--- a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
@@ -15,6 +15,7 @@
 use Interop\Queue\Consumer;
 use Interop\Queue\Context as InteropContext;
 use Interop\Queue\Processor;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -242,7 +243,7 @@ public function testShouldDoNothingIfAnotherQueue()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createDriverStub(RouteCollection $routeCollection = null): DriverInterface
     {
@@ -257,7 +258,7 @@ private function createDriverStub(RouteCollection $routeCollection = null): Driv
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createContextMock(): InteropContext
     {
@@ -265,7 +266,7 @@ private function createContextMock(): InteropContext
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createProcessorMock(): Processor
     {
@@ -275,7 +276,7 @@ private function createProcessorMock(): Processor
     /**
      * @param mixed $queue
      *
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createConsumerStub($queue): Consumer
     {
diff --git a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
index 53d88aa..63fb50c 100644
--- a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
@@ -12,6 +12,7 @@
 use Interop\Queue\Consumer;
 use Interop\Queue\Context;
 use Interop\Queue\Message;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -70,7 +71,7 @@ public function testShouldFlushSpoolProducerOnPostReceived()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createInteropContextMock(): Context
     {
@@ -78,7 +79,7 @@ private function createInteropContextMock(): Context
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|SpoolProducer
+     * @return MockObject|SpoolProducer
      */
     private function createSpoolProducerMock()
     {
diff --git a/Tests/Client/ConsumptionExtension/LogExtensionTest.php b/Tests/Client/ConsumptionExtension/LogExtensionTest.php
index 0bd6514..063ea23 100644
--- a/Tests/Client/ConsumptionExtension/LogExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/LogExtensionTest.php
@@ -21,6 +21,7 @@
 use Interop\Queue\Context;
 use Interop\Queue\Processor;
 use Interop\Queue\Queue;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 use Psr\Log\LogLevel;
@@ -500,7 +501,7 @@ public function testShouldLogProcessedTopicProcessorMessageWithReasonResultObjec
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createConsumerStub(Queue $queue): Consumer
     {
@@ -515,7 +516,7 @@ private function createConsumerStub(Queue $queue): Consumer
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createContextMock(): Context
     {
@@ -523,7 +524,7 @@ private function createContextMock(): Context
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createProcessorMock(): Processor
     {
@@ -531,7 +532,7 @@ private function createProcessorMock(): Processor
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|LoggerInterface
+     * @return MockObject|LoggerInterface
      */
     private function createLogger()
     {
diff --git a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
index 3c12de4..18760e9 100644
--- a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
@@ -13,6 +13,7 @@
 use Interop\Queue\Consumer;
 use Interop\Queue\Context as InteropContext;
 use Interop\Queue\Processor;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -161,7 +162,7 @@ public function testShouldSkipMessagesWithoutTopicPropertySet()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
+     * @return MockObject|InteropContext
      */
     protected function createContextMock(): InteropContext
     {
@@ -169,7 +170,7 @@ protected function createContextMock(): InteropContext
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|DriverInterface
+     * @return MockObject|DriverInterface
      */
     protected function createDriverMock(): DriverInterface
     {
@@ -177,7 +178,7 @@ protected function createDriverMock(): DriverInterface
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createProcessorMock(): Processor
     {
@@ -187,7 +188,7 @@ private function createProcessorMock(): Processor
     /**
      * @param mixed $queue
      *
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createConsumerStub($queue): Consumer
     {
diff --git a/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php b/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php
index 014c232..4b241aa 100644
--- a/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php
@@ -8,6 +8,7 @@
 use Enqueue\Consumption\StartExtensionInterface;
 use Enqueue\Test\ClassExtensionTrait;
 use Interop\Queue\Context as InteropContext;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -61,7 +62,7 @@ public function testShouldSetupBrokerOnlyOnce()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|DriverInterface
+     * @return MockObject|DriverInterface
      */
     private function createDriverMock()
     {
diff --git a/Tests/Client/DelegateProcessorTest.php b/Tests/Client/DelegateProcessorTest.php
index 4fe6bd5..40ef179 100644
--- a/Tests/Client/DelegateProcessorTest.php
+++ b/Tests/Client/DelegateProcessorTest.php
@@ -8,6 +8,7 @@
 use Enqueue\ProcessorRegistryInterface;
 use Interop\Queue\Context;
 use Interop\Queue\Processor;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 
 class DelegateProcessorTest extends TestCase
@@ -57,7 +58,7 @@ public function testShouldProcessMessage()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|ProcessorRegistryInterface
+     * @return MockObject|ProcessorRegistryInterface
      */
     protected function createProcessorRegistryMock()
     {
@@ -65,7 +66,7 @@ protected function createProcessorRegistryMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Context
+     * @return MockObject|Context
      */
     protected function createContextMock()
     {
@@ -73,7 +74,7 @@ protected function createContextMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Processor
+     * @return MockObject|Processor
      */
     protected function createProcessorMock()
     {
diff --git a/Tests/Client/Driver/GenericDriverTestsTrait.php b/Tests/Client/Driver/GenericDriverTestsTrait.php
index cb14137..4f1c815 100644
--- a/Tests/Client/Driver/GenericDriverTestsTrait.php
+++ b/Tests/Client/Driver/GenericDriverTestsTrait.php
@@ -1124,12 +1124,12 @@ public function testThrowIfNeitherTopicNorCommandAreSentOnSendToProcessor()
     abstract protected function createDriver(...$args): DriverInterface;
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     abstract protected function createContextMock(): Context;
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     abstract protected function createProducerMock(): InteropProducer;
 
@@ -1140,7 +1140,7 @@ abstract protected function createTopic(string $name): InteropTopic;
     abstract protected function createMessage(): InteropMessage;
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     protected function createContextStub(): Context
     {
diff --git a/Tests/Client/Driver/RabbitMqStompDriverTest.php b/Tests/Client/Driver/RabbitMqStompDriverTest.php
index 1378dd3..a55fd91 100644
--- a/Tests/Client/Driver/RabbitMqStompDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqStompDriverTest.php
@@ -577,7 +577,7 @@ protected function getRouterTransportName(): string
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createManagementClientMock(): StompManagementClient
     {
@@ -585,7 +585,7 @@ private function createManagementClientMock(): StompManagementClient
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|LoggerInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|LoggerInterface
      */
     private function createLoggerMock()
     {
diff --git a/Tests/Client/Driver/StompManagementClientTest.php b/Tests/Client/Driver/StompManagementClientTest.php
index 325fe2c..081a62c 100644
--- a/Tests/Client/Driver/StompManagementClientTest.php
+++ b/Tests/Client/Driver/StompManagementClientTest.php
@@ -81,7 +81,7 @@ public function testCouldCreateNewInstanceUsingFactory()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Client
+     * @return \PHPUnit\Framework\MockObject\MockObject|Client
      */
     private function createClientMock()
     {
@@ -89,7 +89,7 @@ private function createClientMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Exchange
+     * @return \PHPUnit\Framework\MockObject\MockObject|Exchange
      */
     private function createExchangeMock()
     {
@@ -97,7 +97,7 @@ private function createExchangeMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Queue
+     * @return \PHPUnit\Framework\MockObject\MockObject|Queue
      */
     private function createQueueMock()
     {
@@ -105,7 +105,7 @@ private function createQueueMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Binding
+     * @return \PHPUnit\Framework\MockObject\MockObject|Binding
      */
     private function createBindingMock()
     {
diff --git a/Tests/Client/DriverPreSendTest.php b/Tests/Client/DriverPreSendTest.php
index d31ac17..85686b4 100644
--- a/Tests/Client/DriverPreSendTest.php
+++ b/Tests/Client/DriverPreSendTest.php
@@ -76,7 +76,7 @@ public function testShouldAllowGetTopic()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createDriverMock(): DriverInterface
     {
@@ -84,7 +84,7 @@ private function createDriverMock(): DriverInterface
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createProducerMock(): ProducerInterface
     {
diff --git a/Tests/Client/PostSendTest.php b/Tests/Client/PostSendTest.php
index 7694073..0492770 100644
--- a/Tests/Client/PostSendTest.php
+++ b/Tests/Client/PostSendTest.php
@@ -90,7 +90,7 @@ public function testShouldAllowGetTopic()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createDriverMock(): DriverInterface
     {
@@ -98,7 +98,7 @@ private function createDriverMock(): DriverInterface
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createProducerMock(): ProducerInterface
     {
@@ -106,7 +106,7 @@ private function createProducerMock(): ProducerInterface
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Destination
+     * @return \PHPUnit\Framework\MockObject\MockObject|Destination
      */
     private function createDestinationMock(): Destination
     {
@@ -114,7 +114,7 @@ private function createDestinationMock(): Destination
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|TransportMessage
+     * @return \PHPUnit\Framework\MockObject\MockObject|TransportMessage
      */
     private function createTransportMessageMock(): TransportMessage
     {
diff --git a/Tests/Client/PreSendTest.php b/Tests/Client/PreSendTest.php
index d1e5595..408be1c 100644
--- a/Tests/Client/PreSendTest.php
+++ b/Tests/Client/PreSendTest.php
@@ -109,7 +109,7 @@ public function testCouldChangeBody()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createDriverMock(): DriverInterface
     {
@@ -117,7 +117,7 @@ private function createDriverMock(): DriverInterface
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createProducerMock(): ProducerInterface
     {
diff --git a/Tests/Client/ProducerSendCommandTest.php b/Tests/Client/ProducerSendCommandTest.php
index 3469c38..8ac0704 100644
--- a/Tests/Client/ProducerSendCommandTest.php
+++ b/Tests/Client/ProducerSendCommandTest.php
@@ -493,7 +493,7 @@ public function testShouldCallPostSendExtensionMethod()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createRpcFactoryMock(): RpcFactory
     {
@@ -501,7 +501,7 @@ private function createRpcFactoryMock(): RpcFactory
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createDriverStub(): DriverInterface
     {
diff --git a/Tests/Client/ProducerSendEventTest.php b/Tests/Client/ProducerSendEventTest.php
index 1346afe..29a14b7 100644
--- a/Tests/Client/ProducerSendEventTest.php
+++ b/Tests/Client/ProducerSendEventTest.php
@@ -513,7 +513,7 @@ public function testShouldCallPostSendExtensionMethodWhenSendToApplicationRouter
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createRpcFactoryMock(): RpcFactory
     {
@@ -521,7 +521,7 @@ private function createRpcFactoryMock(): RpcFactory
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createDriverStub(): DriverInterface
     {
diff --git a/Tests/Client/ProducerTest.php b/Tests/Client/ProducerTest.php
index 4fbf4ba..558e70e 100644
--- a/Tests/Client/ProducerTest.php
+++ b/Tests/Client/ProducerTest.php
@@ -39,7 +39,7 @@ public function testCouldBeConstructedWithOptionalArguments()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createRpcFactoryMock(): RpcFactory
     {
@@ -47,7 +47,7 @@ private function createRpcFactoryMock(): RpcFactory
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createDriverMock(): DriverInterface
     {
diff --git a/Tests/Client/RouterProcessorTest.php b/Tests/Client/RouterProcessorTest.php
index d3f5039..cdd44df 100644
--- a/Tests/Client/RouterProcessorTest.php
+++ b/Tests/Client/RouterProcessorTest.php
@@ -195,7 +195,7 @@ public function testShouldDoNotModifyOriginalMessage()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createDriverStub(RouteCollection $routeCollection = null): DriverInterface
     {
diff --git a/Tests/Client/SpoolProducerTest.php b/Tests/Client/SpoolProducerTest.php
index 8c00ded..024fd6d 100644
--- a/Tests/Client/SpoolProducerTest.php
+++ b/Tests/Client/SpoolProducerTest.php
@@ -154,7 +154,7 @@ public function testShouldSendImmediatelyCommandMessageWithNeedReplyTrue()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|ProducerInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|ProducerInterface
      */
     protected function createProducerMock()
     {
diff --git a/Tests/Client/TraceableProducerTest.php b/Tests/Client/TraceableProducerTest.php
index b25b7c1..ff5ee83 100644
--- a/Tests/Client/TraceableProducerTest.php
+++ b/Tests/Client/TraceableProducerTest.php
@@ -366,7 +366,7 @@ public function testShouldAllowClearStoredTraces()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|ProducerInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|ProducerInterface
      */
     protected function createProducerMock()
     {
diff --git a/Tests/Consumption/ChainExtensionTest.php b/Tests/Consumption/ChainExtensionTest.php
index c558424..23bd4f9 100644
--- a/Tests/Consumption/ChainExtensionTest.php
+++ b/Tests/Consumption/ChainExtensionTest.php
@@ -19,6 +19,7 @@
 use Interop\Queue\Message;
 use Interop\Queue\Processor;
 use Interop\Queue\SubscriptionConsumer;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
@@ -275,7 +276,7 @@ public function testShouldProxyOnEndToAllInternalExtensions()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     protected function createLoggerMock(): LoggerInterface
     {
@@ -283,7 +284,7 @@ protected function createLoggerMock(): LoggerInterface
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     protected function createInteropContextMock(): Context
     {
@@ -291,7 +292,7 @@ protected function createInteropContextMock(): Context
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     protected function createInteropConsumerMock(): Consumer
     {
@@ -299,7 +300,7 @@ protected function createInteropConsumerMock(): Consumer
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     protected function createInteropProcessorMock(): Processor
     {
@@ -307,7 +308,7 @@ protected function createInteropProcessorMock(): Processor
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|ExtensionInterface
+     * @return MockObject|ExtensionInterface
      */
     protected function createExtension()
     {
@@ -315,7 +316,7 @@ protected function createExtension()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createSubscriptionConsumerMock(): SubscriptionConsumer
     {
diff --git a/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php b/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
index 37d592e..e1d4c50 100644
--- a/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
@@ -9,6 +9,7 @@
 use Interop\Queue\Context;
 use Interop\Queue\Message;
 use Interop\Queue\SubscriptionConsumer;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
@@ -160,7 +161,7 @@ public function testOnPostReceivedShouldInterruptExecutionIfMessageLimitExceeded
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     protected function createInteropContextMock(): Context
     {
@@ -168,7 +169,7 @@ protected function createInteropContextMock(): Context
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createSubscriptionConsumerMock(): SubscriptionConsumer
     {
@@ -176,7 +177,7 @@ private function createSubscriptionConsumerMock(): SubscriptionConsumer
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createLoggerMock(): LoggerInterface
     {
diff --git a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
index de1531e..5a95486 100644
--- a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
@@ -10,6 +10,7 @@
 use Interop\Queue\Context;
 use Interop\Queue\Message;
 use Interop\Queue\SubscriptionConsumer;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
@@ -23,7 +24,8 @@ public function testCouldBeConstructedWithRequiredArguments()
 
     public function testShouldThrowExceptionIfMemoryLimitIsNotInt()
     {
-        $this->setExpectedException(\InvalidArgumentException::class, 'Expected memory limit is int but got: "double"');
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Expected memory limit is int but got: "double"');
 
         new LimitConsumerMemoryExtension(0.0);
     }
@@ -176,7 +178,7 @@ public function testOnPostMessageReceivedShouldNotInterruptExecutionIfMemoryLimi
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     protected function createInteropContextMock(): Context
     {
@@ -184,7 +186,7 @@ protected function createInteropContextMock(): Context
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createSubscriptionConsumerMock(): SubscriptionConsumer
     {
@@ -192,7 +194,7 @@ private function createSubscriptionConsumerMock(): SubscriptionConsumer
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createLoggerMock(): LoggerInterface
     {
diff --git a/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php b/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
index 1eaef78..fa730e3 100644
--- a/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
@@ -10,6 +10,7 @@
 use Interop\Queue\Context;
 use Interop\Queue\Message;
 use Interop\Queue\SubscriptionConsumer;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
@@ -154,7 +155,7 @@ public function testOnPostReceivedShouldNotInterruptExecutionIfConsumptionTimeIs
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     protected function createInteropContextMock(): Context
     {
@@ -162,7 +163,7 @@ protected function createInteropContextMock(): Context
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createSubscriptionConsumerMock(): SubscriptionConsumer
     {
@@ -170,7 +171,7 @@ private function createSubscriptionConsumerMock(): SubscriptionConsumer
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createLoggerMock(): LoggerInterface
     {
diff --git a/Tests/Consumption/Extension/LogExtensionTest.php b/Tests/Consumption/Extension/LogExtensionTest.php
index 8038e55..4bc4ae9 100644
--- a/Tests/Consumption/Extension/LogExtensionTest.php
+++ b/Tests/Consumption/Extension/LogExtensionTest.php
@@ -20,6 +20,7 @@
 use Interop\Queue\Context;
 use Interop\Queue\Processor;
 use Interop\Queue\Queue;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 use Psr\Log\LogLevel;
@@ -230,7 +231,7 @@ public function testShouldLogMessageProcessedWithReasonResultObject()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createConsumerStub(Queue $queue): Consumer
     {
@@ -245,7 +246,7 @@ private function createConsumerStub(Queue $queue): Consumer
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createContextMock(): Context
     {
@@ -253,7 +254,7 @@ private function createContextMock(): Context
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createProcessorMock(): Processor
     {
@@ -261,7 +262,7 @@ private function createProcessorMock(): Processor
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|LoggerInterface
+     * @return MockObject|LoggerInterface
      */
     private function createLogger()
     {
diff --git a/Tests/Consumption/Extension/LoggerExtensionTest.php b/Tests/Consumption/Extension/LoggerExtensionTest.php
index 674720e..f3641ad 100644
--- a/Tests/Consumption/Extension/LoggerExtensionTest.php
+++ b/Tests/Consumption/Extension/LoggerExtensionTest.php
@@ -6,6 +6,7 @@
 use Enqueue\Consumption\Extension\LoggerExtension;
 use Enqueue\Consumption\InitLoggerExtensionInterface;
 use Enqueue\Test\ClassExtensionTrait;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
@@ -74,7 +75,7 @@ public function testShouldDoNothingIfSameLoggerInstanceAlreadySet()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|LoggerInterface
+     * @return MockObject|LoggerInterface
      */
     protected function createLogger()
     {
diff --git a/Tests/Consumption/Extension/NicenessExtensionTest.php b/Tests/Consumption/Extension/NicenessExtensionTest.php
index 5b1834a..a2937d0 100644
--- a/Tests/Consumption/Extension/NicenessExtensionTest.php
+++ b/Tests/Consumption/Extension/NicenessExtensionTest.php
@@ -5,6 +5,7 @@
 use Enqueue\Consumption\Context\Start;
 use Enqueue\Consumption\Extension\NicenessExtension;
 use Interop\Queue\Context as InteropContext;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -33,7 +34,7 @@ public function testShouldThrowWarningOnInvalidArgument()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
+     * @return MockObject|InteropContext
      */
     protected function createContextMock(): InteropContext
     {
diff --git a/Tests/Consumption/Extension/ReplyExtensionTest.php b/Tests/Consumption/Extension/ReplyExtensionTest.php
index 18e5727..4088956 100644
--- a/Tests/Consumption/Extension/ReplyExtensionTest.php
+++ b/Tests/Consumption/Extension/ReplyExtensionTest.php
@@ -12,6 +12,7 @@
 use Interop\Queue\Consumer;
 use Interop\Queue\Context;
 use Interop\Queue\Producer as InteropProducer;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
 
@@ -103,7 +104,7 @@ public function testShouldSendReplyMessageToReplyQueueOnPostReceived()
             ->with($replyQueue, $replyMessage)
         ;
 
-        /** @var \PHPUnit_Framework_MockObject_MockObject|Context $contextMock */
+        /** @var MockObject|Context $contextMock */
         $contextMock = $this->createMock(Context::class);
         $contextMock
             ->expects($this->once())
@@ -129,7 +130,7 @@ public function testShouldSendReplyMessageToReplyQueueOnPostReceived()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     protected function createInteropContextMock(): Context
     {
@@ -137,7 +138,7 @@ protected function createInteropContextMock(): Context
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return MockObject
      */
     private function createNeverUsedContextMock(): Context
     {
diff --git a/Tests/Consumption/FallbackSubscriptionConsumerTest.php b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
index dce6081..6206673 100644
--- a/Tests/Consumption/FallbackSubscriptionConsumerTest.php
+++ b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
@@ -229,7 +229,7 @@ public function testShouldConsumeTillTimeoutIsReached()
     /**
      * @param mixed|null $body
      *
-     * @return InteropMessage|\PHPUnit_Framework_MockObject_MockObject
+     * @return InteropMessage|\PHPUnit\Framework\MockObject\MockObject
      */
     private function createMessageStub($body = null)
     {
@@ -246,7 +246,7 @@ private function createMessageStub($body = null)
     /**
      * @param mixed|null $queueName
      *
-     * @return Consumer|\PHPUnit_Framework_MockObject_MockObject
+     * @return Consumer|\PHPUnit\Framework\MockObject\MockObject
      */
     private function createConsumerStub($queueName = null)
     {
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index b103393..9e25ab9 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -1454,7 +1454,7 @@ public function testCaptureExitStatus()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createContextWithoutSubscriptionConsumerMock(): InteropContext
     {
@@ -1469,7 +1469,7 @@ private function createContextWithoutSubscriptionConsumerMock(): InteropContext
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
+     * @return \PHPUnit\Framework\MockObject\MockObject|InteropContext
      */
     private function createContextStub(Consumer $consumer = null): InteropContext
     {
@@ -1493,7 +1493,7 @@ private function createContextStub(Consumer $consumer = null): InteropContext
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Processor
+     * @return \PHPUnit\Framework\MockObject\MockObject|Processor
      */
     private function createProcessorMock()
     {
@@ -1501,7 +1501,7 @@ private function createProcessorMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Processor
+     * @return \PHPUnit\Framework\MockObject\MockObject|Processor
      */
     private function createProcessorStub()
     {
@@ -1516,7 +1516,7 @@ private function createProcessorStub()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Message
+     * @return \PHPUnit\Framework\MockObject\MockObject|Message
      */
     private function createMessageMock(): Message
     {
@@ -1524,7 +1524,7 @@ private function createMessageMock(): Message
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|ExtensionInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|ExtensionInterface
      */
     private function createExtension()
     {
@@ -1534,7 +1534,7 @@ private function createExtension()
     /**
      * @param mixed|null $queue
      *
-     * @return \PHPUnit_Framework_MockObject_MockObject|Consumer
+     * @return \PHPUnit\Framework\MockObject\MockObject|Consumer
      */
     private function createConsumerStub($queue = null): Consumer
     {
@@ -1553,7 +1553,7 @@ private function createConsumerStub($queue = null): Consumer
     }
 
     /**
-     * @return SubscriptionConsumer|\PHPUnit_Framework_MockObject_MockObject
+     * @return SubscriptionConsumer|\PHPUnit\Framework\MockObject\MockObject
      */
     private function createSubscriptionConsumerMock(): SubscriptionConsumer
     {
diff --git a/Tests/Router/RouteRecipientListProcessorTest.php b/Tests/Router/RouteRecipientListProcessorTest.php
index 38e0580..8a2e089 100644
--- a/Tests/Router/RouteRecipientListProcessorTest.php
+++ b/Tests/Router/RouteRecipientListProcessorTest.php
@@ -70,7 +70,7 @@ public function testShouldProduceRecipientsMessagesAndAckOriginalMessage()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|InteropProducer
+     * @return \PHPUnit\Framework\MockObject\MockObject|InteropProducer
      */
     protected function createProducerMock()
     {
@@ -78,7 +78,7 @@ protected function createProducerMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Context
+     * @return \PHPUnit\Framework\MockObject\MockObject|Context
      */
     protected function createContextMock()
     {
@@ -86,7 +86,7 @@ protected function createContextMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|RecipientListRouterInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|RecipientListRouterInterface
      */
     protected function createRecipientListRouterMock()
     {
diff --git a/Tests/Rpc/RpcClientTest.php b/Tests/Rpc/RpcClientTest.php
index 78f72bb..e2035c4 100644
--- a/Tests/Rpc/RpcClientTest.php
+++ b/Tests/Rpc/RpcClientTest.php
@@ -10,6 +10,7 @@
 use Interop\Queue\Consumer;
 use Interop\Queue\Context;
 use Interop\Queue\Producer as InteropProducer;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 
 class RpcClientTest extends TestCase
@@ -329,7 +330,7 @@ public function testShouldDoSyncCall()
     }
 
     /**
-     * @return Context|\PHPUnit_Framework_MockObject_MockObject|InteropProducer
+     * @return Context|MockObject|InteropProducer
      */
     private function createInteropProducerMock()
     {
@@ -337,7 +338,7 @@ private function createInteropProducerMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Consumer
+     * @return MockObject|Consumer
      */
     private function createConsumerMock()
     {
@@ -345,7 +346,7 @@ private function createConsumerMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Context
+     * @return MockObject|Context
      */
     private function createContextMock()
     {
diff --git a/Tests/Symfony/Client/ConsumeCommandTest.php b/Tests/Symfony/Client/ConsumeCommandTest.php
index 52bcd9a..be7de22 100644
--- a/Tests/Symfony/Client/ConsumeCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeCommandTest.php
@@ -587,7 +587,7 @@ public function testShouldReturnExitStatusIfSet()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|DelegateProcessor
+     * @return \PHPUnit\Framework\MockObject\MockObject|DelegateProcessor
      */
     private function createDelegateProcessorMock()
     {
@@ -595,7 +595,7 @@ private function createDelegateProcessorMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumerInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|QueueConsumerInterface
      */
     private function createQueueConsumerMock()
     {
@@ -603,7 +603,7 @@ private function createQueueConsumerMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|DriverInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|DriverInterface
      */
     private function createDriverStub(RouteCollection $routeCollection = null): DriverInterface
     {
@@ -624,7 +624,7 @@ private function createDriverStub(RouteCollection $routeCollection = null): Driv
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createContextWithoutSubscriptionConsumerMock(): InteropContext
     {
@@ -639,7 +639,7 @@ private function createContextWithoutSubscriptionConsumerMock(): InteropContext
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
+     * @return \PHPUnit\Framework\MockObject\MockObject|InteropContext
      */
     private function createContextStub(Consumer $consumer = null): InteropContext
     {
@@ -663,7 +663,7 @@ private function createContextStub(Consumer $consumer = null): InteropContext
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|ExtensionInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|ExtensionInterface
      */
     private function createExtension()
     {
@@ -673,7 +673,7 @@ private function createExtension()
     /**
      * @param mixed|null $queue
      *
-     * @return \PHPUnit_Framework_MockObject_MockObject|Consumer
+     * @return \PHPUnit\Framework\MockObject\MockObject|Consumer
      */
     private function createConsumerStub($queue = null): Consumer
     {
diff --git a/Tests/Symfony/Client/FlushSpoolProducerListenerTest.php b/Tests/Symfony/Client/FlushSpoolProducerListenerTest.php
index a1fe06e..e7ca3ff 100644
--- a/Tests/Symfony/Client/FlushSpoolProducerListenerTest.php
+++ b/Tests/Symfony/Client/FlushSpoolProducerListenerTest.php
@@ -58,7 +58,7 @@ public function testShouldFlushSpoolProducerOnFlushMessagesCall()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|SpoolProducer
+     * @return \PHPUnit\Framework\MockObject\MockObject|SpoolProducer
      */
     private function createSpoolProducerMock()
     {
diff --git a/Tests/Symfony/Client/Mock/SetupBrokerExtensionCommand.php b/Tests/Symfony/Client/Mock/SetupBrokerExtensionCommand.php
index aa0c812..c217505 100644
--- a/Tests/Symfony/Client/Mock/SetupBrokerExtensionCommand.php
+++ b/Tests/Symfony/Client/Mock/SetupBrokerExtensionCommand.php
@@ -29,12 +29,14 @@ protected function configure()
         $this->configureSetupBrokerExtension();
     }
 
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $this->extension = $this->getSetupBrokerExtension($input, new GenericDriver(
             new NullContext(),
             Config::create(),
             new RouteCollection([])
         ));
+
+        return 0;
     }
 }
diff --git a/Tests/Symfony/Client/ProduceCommandTest.php b/Tests/Symfony/Client/ProduceCommandTest.php
index 7c50ae0..0dffc3d 100644
--- a/Tests/Symfony/Client/ProduceCommandTest.php
+++ b/Tests/Symfony/Client/ProduceCommandTest.php
@@ -268,7 +268,7 @@ public function testThrowIfClientNotFound()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|ProducerInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|ProducerInterface
      */
     private function createProducerMock()
     {
diff --git a/Tests/Symfony/Client/RoutesCommandTest.php b/Tests/Symfony/Client/RoutesCommandTest.php
index 0ed73ef..d9935bd 100644
--- a/Tests/Symfony/Client/RoutesCommandTest.php
+++ b/Tests/Symfony/Client/RoutesCommandTest.php
@@ -331,7 +331,7 @@ public function testShouldOutputRouteOptions()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createDriverStub(Config $config, RouteCollection $routeCollection): DriverInterface
     {
diff --git a/Tests/Symfony/Client/SetupBrokerCommandTest.php b/Tests/Symfony/Client/SetupBrokerCommandTest.php
index 4bbaf55..358206d 100644
--- a/Tests/Symfony/Client/SetupBrokerCommandTest.php
+++ b/Tests/Symfony/Client/SetupBrokerCommandTest.php
@@ -130,7 +130,7 @@ public function testShouldThrowIfClientNotFound()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|DriverInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|DriverInterface
      */
     private function createClientDriverMock()
     {
diff --git a/Tests/Symfony/Client/SimpleConsumeCommandTest.php b/Tests/Symfony/Client/SimpleConsumeCommandTest.php
index cb12fe5..8f23acd 100644
--- a/Tests/Symfony/Client/SimpleConsumeCommandTest.php
+++ b/Tests/Symfony/Client/SimpleConsumeCommandTest.php
@@ -104,7 +104,7 @@ public function testShouldBindDefaultQueueOnly()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|DelegateProcessor
+     * @return \PHPUnit\Framework\MockObject\MockObject|DelegateProcessor
      */
     private function createDelegateProcessorMock()
     {
@@ -112,7 +112,7 @@ private function createDelegateProcessorMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumerInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|QueueConsumerInterface
      */
     private function createQueueConsumerMock()
     {
@@ -120,7 +120,7 @@ private function createQueueConsumerMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|DriverInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|DriverInterface
      */
     private function createDriverStub(RouteCollection $routeCollection = null): DriverInterface
     {
diff --git a/Tests/Symfony/Client/SimpleProduceCommandTest.php b/Tests/Symfony/Client/SimpleProduceCommandTest.php
index 84391cd..71ac20f 100644
--- a/Tests/Symfony/Client/SimpleProduceCommandTest.php
+++ b/Tests/Symfony/Client/SimpleProduceCommandTest.php
@@ -81,7 +81,7 @@ public function testThrowIfNeitherTopicNorCommandOptionsAreSet()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|ProducerInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|ProducerInterface
      */
     private function createProducerMock()
     {
diff --git a/Tests/Symfony/Client/SimpleRoutesCommandTest.php b/Tests/Symfony/Client/SimpleRoutesCommandTest.php
index ce97be1..469334b 100644
--- a/Tests/Symfony/Client/SimpleRoutesCommandTest.php
+++ b/Tests/Symfony/Client/SimpleRoutesCommandTest.php
@@ -83,7 +83,7 @@ public function testShouldOutputEmptyRouteCollection()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createDriverMock(): DriverInterface
     {
@@ -91,7 +91,7 @@ private function createDriverMock(): DriverInterface
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createDriverStub(Config $config, RouteCollection $routeCollection): DriverInterface
     {
diff --git a/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php b/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php
index 240cffa..0d30582 100644
--- a/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php
+++ b/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php
@@ -78,7 +78,7 @@ public function testShouldCallDriverSetupBrokerMethod()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|DriverInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|DriverInterface
      */
     private function createClientDriverMock()
     {
diff --git a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
index 4586849..c788d5e 100644
--- a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
@@ -274,7 +274,7 @@ public function testShouldExecuteConsumptionWithCustomTransportExplicitlySetQueu
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|InteropQueue
+     * @return \PHPUnit\Framework\MockObject\MockObject|InteropQueue
      */
     protected function createQueueMock()
     {
@@ -282,7 +282,7 @@ protected function createQueueMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Processor
+     * @return \PHPUnit\Framework\MockObject\MockObject|Processor
      */
     protected function createProcessor()
     {
@@ -290,7 +290,7 @@ protected function createProcessor()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumerInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|QueueConsumerInterface
      */
     protected function createQueueConsumerMock()
     {
diff --git a/Tests/Symfony/Consumption/ConsumeCommandTest.php b/Tests/Symfony/Consumption/ConsumeCommandTest.php
index 88b8267..c2f2257 100644
--- a/Tests/Symfony/Consumption/ConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConsumeCommandTest.php
@@ -160,7 +160,7 @@ public function testShouldReturnExitStatusIfSet()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumerInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|QueueConsumerInterface
      */
     private function createQueueConsumerMock()
     {
@@ -168,7 +168,7 @@ private function createQueueConsumerMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createContextWithoutSubscriptionConsumerMock(): InteropContext
     {
@@ -183,7 +183,7 @@ private function createContextWithoutSubscriptionConsumerMock(): InteropContext
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|InteropContext
+     * @return \PHPUnit\Framework\MockObject\MockObject|InteropContext
      */
     private function createContextStub(Consumer $consumer = null): InteropContext
     {
@@ -207,7 +207,7 @@ private function createContextStub(Consumer $consumer = null): InteropContext
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|ExtensionInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|ExtensionInterface
      */
     private function createExtension()
     {
@@ -217,7 +217,7 @@ private function createExtension()
     /**
      * @param mixed|null $queue
      *
-     * @return \PHPUnit_Framework_MockObject_MockObject|Consumer
+     * @return \PHPUnit\Framework\MockObject\MockObject|Consumer
      */
     private function createConsumerStub($queue = null): Consumer
     {
diff --git a/Tests/Symfony/Consumption/Mock/LimitsExtensionsCommand.php b/Tests/Symfony/Consumption/Mock/LimitsExtensionsCommand.php
index 7b67223..05e0c56 100644
--- a/Tests/Symfony/Consumption/Mock/LimitsExtensionsCommand.php
+++ b/Tests/Symfony/Consumption/Mock/LimitsExtensionsCommand.php
@@ -25,8 +25,10 @@ protected function configure()
         $this->configureLimitsExtensions();
     }
 
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $this->extensions = $this->getLimitsExtensions($input, $output);
+
+        return 0;
     }
 }
diff --git a/Tests/Symfony/Consumption/Mock/QueueConsumerOptionsCommand.php b/Tests/Symfony/Consumption/Mock/QueueConsumerOptionsCommand.php
index 289a5e7..147a3b9 100644
--- a/Tests/Symfony/Consumption/Mock/QueueConsumerOptionsCommand.php
+++ b/Tests/Symfony/Consumption/Mock/QueueConsumerOptionsCommand.php
@@ -31,8 +31,10 @@ protected function configure()
         $this->configureQueueConsumerOptions();
     }
 
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $this->setQueueConsumerOptions($this->consumer, $input);
+
+        return 0;
     }
 }
diff --git a/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php b/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php
index c221f02..b44c89a 100644
--- a/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php
+++ b/Tests/Symfony/Consumption/QueueConsumerOptionsCommandTraitTest.php
@@ -37,7 +37,7 @@ public function testShouldSetQueueConsumerOptions()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumerInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|QueueConsumerInterface
      */
     private function createQueueConsumer()
     {
diff --git a/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php b/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php
index 34d4161..19b9d76 100644
--- a/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php
@@ -77,7 +77,7 @@ public function testShouldExecuteDefaultConsumption()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|QueueConsumerInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|QueueConsumerInterface
      */
     private function createQueueConsumerMock()
     {
diff --git a/Tests/Symfony/ContainerProcessorRegistryTest.php b/Tests/Symfony/ContainerProcessorRegistryTest.php
index bb3cacb..bf6e3cd 100644
--- a/Tests/Symfony/ContainerProcessorRegistryTest.php
+++ b/Tests/Symfony/ContainerProcessorRegistryTest.php
@@ -91,7 +91,7 @@ public function testShouldThrowExceptionIfProcessorIsNotSet()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createProcessorMock(): Processor
     {
@@ -99,7 +99,7 @@ private function createProcessorMock(): Processor
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject
      */
     private function createContainerMock(): ContainerInterface
     {
diff --git a/Tests/Symfony/LazyProducerTest.php b/Tests/Symfony/LazyProducerTest.php
index b574414..1b15ce2 100644
--- a/Tests/Symfony/LazyProducerTest.php
+++ b/Tests/Symfony/LazyProducerTest.php
@@ -116,7 +116,7 @@ public function testShouldProxyReturnedPromiseBackOnSendCommand()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|ProducerInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|ProducerInterface
      */
     private function createProducerMock(): ProducerInterface
     {
@@ -124,7 +124,7 @@ private function createProducerMock(): ProducerInterface
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|ContainerInterface
+     * @return \PHPUnit\Framework\MockObject\MockObject|ContainerInterface
      */
     private function createContainerMock(): ContainerInterface
     {
diff --git a/Tests/Util/JSONTest.php b/Tests/Util/JSONTest.php
index c37862e..a6f5833 100644
--- a/Tests/Util/JSONTest.php
+++ b/Tests/Util/JSONTest.php
@@ -16,7 +16,8 @@ public function testShouldDecodeString()
 
     public function testThrowIfMalformedJson()
     {
-        $this->setExpectedException(\InvalidArgumentException::class, 'The malformed json given. ');
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('The malformed json given. ');
         $this->assertSame(['foo' => 'fooVal'], JSON::decode('{]'));
     }
 
@@ -43,10 +44,8 @@ public function nonStringDataProvider()
      */
     public function testShouldThrowExceptionIfInputIsNotString($value)
     {
-        $this->setExpectedException(
-            \InvalidArgumentException::class,
-            'Accept only string argument but got:'
-        );
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Accept only string argument but got:');
 
         $this->assertSame(0, JSON::decode($value));
     }
@@ -96,10 +95,8 @@ public function testShouldEncodeObjectOfJsonSerializableClass()
 
     public function testThrowIfValueIsResource()
     {
-        $this->setExpectedException(
-            \InvalidArgumentException::class,
-            'Could not encode value into json. Error 8 and message Type is not supported'
-        );
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Could not encode value into json. Error 8 and message Type is not supported');
 
         $resource = fopen('php://memory', 'r');
         fclose($resource);
diff --git a/composer.json b/composer.json
index a18b0a9..759d7cc 100644
--- a/composer.json
+++ b/composer.json
@@ -16,12 +16,13 @@
         "psr/container": "^1"
     },
     "require-dev": {
-        "phpunit/phpunit": "~5.5",
+        "phpunit/phpunit": "~7.5",
         "symfony/console": "^3.4|^4|^5",
         "symfony/dependency-injection": "^3.4|^4|^5",
         "symfony/config": "^3.4|^4|^5",
         "symfony/event-dispatcher": "^3.4|^4|^5",
         "symfony/http-kernel": "^3.4|^4|^5",
+        "symfony/yaml": "^3.4|^4|^5",
         "enqueue/amqp-ext": "0.9.x-dev",
         "enqueue/amqp-lib": "0.9.x-dev",
         "enqueue/amqp-bunny": "0.9.x-dev",
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 156ab49..2b99dda 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -8,7 +8,6 @@
          convertWarningsToExceptions="true"
          processIsolation="false"
          stopOnFailure="false"
-         syntaxCheck="false"
          bootstrap="./vendor/autoload.php"
         >
 

From aa8228beea12713216312b195b93419f6083b24c Mon Sep 17 00:00:00 2001
From: Mathieu Lemoine <mathieu@doctoome.com>
Date: Thu, 12 Dec 2019 09:10:53 +0100
Subject: [PATCH 274/337] apply new cs-fixer and phpstan rules

---
 Symfony/Client/ConsumeCommand.php                  | 6 +-----
 Symfony/Consumption/ConfigurableConsumeCommand.php | 5 +----
 2 files changed, 2 insertions(+), 9 deletions(-)

diff --git a/Symfony/Client/ConsumeCommand.php b/Symfony/Client/ConsumeCommand.php
index 4f6e5ed..4b65f62 100644
--- a/Symfony/Client/ConsumeCommand.php
+++ b/Symfony/Client/ConsumeCommand.php
@@ -122,11 +122,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
             $queues = [];
             foreach ($selectedQueues as $queue) {
                 if (false == array_key_exists($queue, $allQueues)) {
-                    throw new \LogicException(sprintf(
-                        'There is no such queue "%s". Available are "%s"',
-                        $queue,
-                        implode('", "', array_keys($allQueues))
-                    ));
+                    throw new \LogicException(sprintf('There is no such queue "%s". Available are "%s"', $queue, implode('", "', array_keys($allQueues))));
                 }
 
                 $queues[$queue] = $allQueues[$queue];
diff --git a/Symfony/Consumption/ConfigurableConsumeCommand.php b/Symfony/Consumption/ConfigurableConsumeCommand.php
index 9439f76..9ec8a36 100644
--- a/Symfony/Consumption/ConfigurableConsumeCommand.php
+++ b/Symfony/Consumption/ConfigurableConsumeCommand.php
@@ -92,10 +92,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
         }
 
         if (empty($queues)) {
-            throw new \LogicException(sprintf(
-                'The queue is not provided. The processor must implement "%s" interface and it must return not empty array of queues or a queue set using as a second argument.',
-                QueueSubscriberInterface::class
-            ));
+            throw new \LogicException(sprintf('The queue is not provided. The processor must implement "%s" interface and it must return not empty array of queues or a queue set using as a second argument.', QueueSubscriberInterface::class));
         }
 
         $extensions = $this->getLimitsExtensions($input, $output);

From e8a54605baaa629f548c90e9a26882ccbd2ac2b9 Mon Sep 17 00:00:00 2001
From: Mathieu Lemoine <mathieu@doctoome.com>
Date: Thu, 12 Dec 2019 11:56:38 +0100
Subject: [PATCH 275/337] drop support for Symfony < 4.3

---
 composer.json | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/composer.json b/composer.json
index 759d7cc..84a41b0 100644
--- a/composer.json
+++ b/composer.json
@@ -17,12 +17,12 @@
     },
     "require-dev": {
         "phpunit/phpunit": "~7.5",
-        "symfony/console": "^3.4|^4|^5",
-        "symfony/dependency-injection": "^3.4|^4|^5",
-        "symfony/config": "^3.4|^4|^5",
-        "symfony/event-dispatcher": "^3.4|^4|^5",
-        "symfony/http-kernel": "^3.4|^4|^5",
-        "symfony/yaml": "^3.4|^4|^5",
+        "symfony/console": "^4.3|^5",
+        "symfony/dependency-injection": "^4.3|^5",
+        "symfony/config": "^4.3|^5",
+        "symfony/event-dispatcher": "^4.3|^5",
+        "symfony/http-kernel": "^4.3|^5",
+        "symfony/yaml": "^4.3|^5",
         "enqueue/amqp-ext": "0.9.x-dev",
         "enqueue/amqp-lib": "0.9.x-dev",
         "enqueue/amqp-bunny": "0.9.x-dev",
@@ -43,8 +43,8 @@
     },
     "suggest": {
         "symfony/console": "^2.8|^3|^4|^5 If you want to use cli commands",
-        "symfony/dependency-injection": "^3.4|^4|^5",
-        "symfony/config": "^3.4|^4|^5",
+        "symfony/dependency-injection": "^4.3|^5",
+        "symfony/config": "^4.3|^5",
         "enqueue/amqp-ext": "AMQP transport (based on php extension)",
         "enqueue/stomp": "STOMP transport",
         "enqueue/fs": "Filesystem transport",

From e63dab2510ae3e31f5967a91e96c9cfafb5c4d07 Mon Sep 17 00:00:00 2001
From: Maksim Kotlyar <kotlyar.maksim@gmail.com>
Date: Thu, 19 Dec 2019 09:01:09 +0200
Subject: [PATCH 276/337] master is 0.10

---
 .../DoctrineConnectionFactoryFactoryTest.php  |  3 +-
 composer.json                                 | 40 +++++++++----------
 2 files changed, 22 insertions(+), 21 deletions(-)

diff --git a/Tests/DoctrineConnectionFactoryFactoryTest.php b/Tests/DoctrineConnectionFactoryFactoryTest.php
index 5aca26d..cf1e591 100644
--- a/Tests/DoctrineConnectionFactoryFactoryTest.php
+++ b/Tests/DoctrineConnectionFactoryFactoryTest.php
@@ -8,8 +8,9 @@
 use Enqueue\ConnectionFactoryFactoryInterface;
 use Enqueue\Dbal\ManagerRegistryConnectionFactory;
 use Enqueue\Doctrine\DoctrineConnectionFactoryFactory;
+use PHPUnit\Framework\TestCase;
 
-class DoctrineConnectionFactoryFactoryTest extends \PHPUnit_Framework_TestCase
+class DoctrineConnectionFactoryFactoryTest extends TestCase
 {
     /**
      * @var ManagerRegistry|\Prophecy\Prophecy\ObjectProphecy
diff --git a/composer.json b/composer.json
index 84a41b0..d95bb23 100644
--- a/composer.json
+++ b/composer.json
@@ -8,9 +8,9 @@
     "require": {
         "php": "^7.1.3",
         "queue-interop/amqp-interop": "^0.8",
-        "queue-interop/queue-interop": "^0.7|^0.8",
-        "enqueue/null": "^0.9",
-        "enqueue/dsn": "^0.9",
+        "queue-interop/queue-interop": "^0.8",
+        "enqueue/null": "^0.10",
+        "enqueue/dsn": "^0.10",
         "ramsey/uuid": "^2|^3.5",
         "psr/log": "^1",
         "psr/container": "^1"
@@ -23,23 +23,23 @@
         "symfony/event-dispatcher": "^4.3|^5",
         "symfony/http-kernel": "^4.3|^5",
         "symfony/yaml": "^4.3|^5",
-        "enqueue/amqp-ext": "0.9.x-dev",
-        "enqueue/amqp-lib": "0.9.x-dev",
-        "enqueue/amqp-bunny": "0.9.x-dev",
-        "enqueue/pheanstalk": "0.9.x-dev",
-        "enqueue/gearman": "0.9.x-dev",
-        "enqueue/rdkafka": "0.9.x-dev",
-        "enqueue/dbal": "0.9.x-dev",
-        "enqueue/fs": "0.9.x-dev",
-        "enqueue/gps": "0.9.x-dev",
-        "enqueue/redis": "0.9.x-dev",
-        "enqueue/sqs": "0.9.x-dev",
-        "enqueue/stomp": "0.9.x-dev",
-        "enqueue/test": "0.9.x-dev",
-        "enqueue/simple-client": "0.9.x-dev",
-        "enqueue/mongodb": "0.9.x-dev",
+        "enqueue/amqp-ext": "0.10.x-dev",
+        "enqueue/amqp-lib": "0.10.x-dev",
+        "enqueue/amqp-bunny": "0.10.x-dev",
+        "enqueue/pheanstalk": "0.10.x-dev",
+        "enqueue/gearman": "0.10.x-dev",
+        "enqueue/rdkafka": "0.10.x-dev",
+        "enqueue/dbal": "0.10.x-dev",
+        "enqueue/fs": "0.10.x-dev",
+        "enqueue/gps": "0.10.x-dev",
+        "enqueue/redis": "0.10.x-dev",
+        "enqueue/sqs": "0.10.x-dev",
+        "enqueue/stomp": "0.10.x-dev",
+        "enqueue/test": "0.10.x-dev",
+        "enqueue/simple-client": "0.10.x-dev",
+        "enqueue/mongodb": "0.10.x-dev",
         "empi89/php-amqp-stubs": "*@dev",
-        "enqueue/dsn": "0.9.x-dev"
+        "enqueue/dsn": "0.10.x-dev"
     },
     "suggest": {
         "symfony/console": "^2.8|^3|^4|^5 If you want to use cli commands",
@@ -73,7 +73,7 @@
     "minimum-stability": "dev",
     "extra": {
         "branch-alias": {
-            "dev-master": "0.9.x-dev"
+            "dev-master": "0.10.x-dev"
         }
     }
 }

From c2a32569a6df81c060d9b497b63b6a96b1968ee1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?=
 <pawel.tadeusz.niedzielski@gmail.com>
Date: Mon, 27 Jan 2020 00:45:10 +0100
Subject: [PATCH 277/337] Add schema declaration to phpunit files. Remove
 parameters set to phpunit defaults.

---
 phpunit.xml.dist | 16 ++++++----------
 1 file changed, 6 insertions(+), 10 deletions(-)

diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 2b99dda..69c12ca 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,15 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
-<phpunit backupGlobals="false"
-         backupStaticAttributes="false"
-         colors="true"
-         convertErrorsToExceptions="true"
-         convertNoticesToExceptions="true"
-         convertWarningsToExceptions="true"
-         processIsolation="false"
-         stopOnFailure="false"
-         bootstrap="./vendor/autoload.php"
-        >
+<phpunit
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/7.5/phpunit.xsd"
+    colors="true"
+    bootstrap="./vendor/autoload.php"
+>
 
     <testsuites>
         <testsuite name="Message Queue Test Suite">

From 9a23c784285f66be6e482605d06b8d30effa295e Mon Sep 17 00:00:00 2001
From: Pierre Grimaud <grimaud.pierre@gmail.com>
Date: Sun, 19 Apr 2020 02:35:35 +0200
Subject: [PATCH 278/337] Fix typos

---
 Tests/Rpc/PromiseTest.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Tests/Rpc/PromiseTest.php b/Tests/Rpc/PromiseTest.php
index 2755ef4..6762149 100644
--- a/Tests/Rpc/PromiseTest.php
+++ b/Tests/Rpc/PromiseTest.php
@@ -50,10 +50,10 @@ public function testOnReceiveShouldCallReceiveCallBackWithTimeout()
         $receiveInvoked = false;
         $receivePromise = null;
         $receiveTimeout = null;
-        $receivecb = function ($promise, $timout) use (&$receiveInvoked, &$receivePromise, &$receiveTimeout) {
+        $receivecb = function ($promise, $timeout) use (&$receiveInvoked, &$receivePromise, &$receiveTimeout) {
             $receiveInvoked = true;
             $receivePromise = $promise;
-            $receiveTimeout = $timout;
+            $receiveTimeout = $timeout;
         };
 
         $promise = new Promise($receivecb, function () {}, function () {});

From 49d22d26fc57bd1f17ccd7c22a8b0554c8c1e34c Mon Sep 17 00:00:00 2001
From: Witold Wasiczko <witold@wasiczko.pl>
Date: Thu, 30 Jul 2020 17:40:15 +0200
Subject: [PATCH 279/337] Allow to install ramsey/uuid:^4

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index d95bb23..ed96030 100644
--- a/composer.json
+++ b/composer.json
@@ -11,7 +11,7 @@
         "queue-interop/queue-interop": "^0.8",
         "enqueue/null": "^0.10",
         "enqueue/dsn": "^0.10",
-        "ramsey/uuid": "^2|^3.5",
+        "ramsey/uuid": "^2|^3.5|^4",
         "psr/log": "^1",
         "psr/container": "^1"
     },

From 2ea68b5cd567fdfe6e2f2386dda7afe99e8e8b6c Mon Sep 17 00:00:00 2001
From: Kevin Defives <kevin.defives@decathlon.com>
Date: Fri, 4 Sep 2020 11:28:49 +0200
Subject: [PATCH 280/337] Fix #1085

Fix #1085 - Fix to be compatible with doctrine v3 . Since Doctrine v3, seems some changes with doctrine/common
---
 Doctrine/DoctrineConnectionFactoryFactory.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Doctrine/DoctrineConnectionFactoryFactory.php b/Doctrine/DoctrineConnectionFactoryFactory.php
index b6e684c..1fd336c 100644
--- a/Doctrine/DoctrineConnectionFactoryFactory.php
+++ b/Doctrine/DoctrineConnectionFactoryFactory.php
@@ -2,7 +2,7 @@
 
 namespace Enqueue\Doctrine;
 
-use Doctrine\Common\Persistence\ManagerRegistry;
+use Doctrine\Persistence\ManagerRegistry;
 use Enqueue\ConnectionFactoryFactoryInterface;
 use Enqueue\Dbal\ManagerRegistryConnectionFactory;
 use Enqueue\Dsn\Dsn;

From e5fd5e92ba1ff622ce01da3dd04cb87e49e7b68e Mon Sep 17 00:00:00 2001
From: Kevin <kevin.defives@decathlon.com>
Date: Fri, 4 Sep 2020 20:48:09 +0200
Subject: [PATCH 281/337] Change doctine\common by doctrine

---
 Tests/DoctrineConnectionFactoryFactoryTest.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/DoctrineConnectionFactoryFactoryTest.php b/Tests/DoctrineConnectionFactoryFactoryTest.php
index cf1e591..7282aa4 100644
--- a/Tests/DoctrineConnectionFactoryFactoryTest.php
+++ b/Tests/DoctrineConnectionFactoryFactoryTest.php
@@ -4,7 +4,7 @@
 
 namespace Enqueue\Tests;
 
-use Doctrine\Common\Persistence\ManagerRegistry;
+use Doctrine\Persistence\ManagerRegistry;
 use Enqueue\ConnectionFactoryFactoryInterface;
 use Enqueue\Dbal\ManagerRegistryConnectionFactory;
 use Enqueue\Doctrine\DoctrineConnectionFactoryFactory;

From 1c4c6ab922049248a7e5f9418817473ded110e52 Mon Sep 17 00:00:00 2001
From: Alexander Trauzzi <acj@trauzzi.me>
Date: Thu, 24 Sep 2020 09:24:16 -0500
Subject: [PATCH 282/337] Test fixes.

---
 Tests/Client/Driver/RabbitMqStompDriverTest.php | 17 +++++++++--------
 Tests/Client/Driver/StompDriverTest.php         |  5 +++--
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/Tests/Client/Driver/RabbitMqStompDriverTest.php b/Tests/Client/Driver/RabbitMqStompDriverTest.php
index a55fd91..4152f93 100644
--- a/Tests/Client/Driver/RabbitMqStompDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqStompDriverTest.php
@@ -12,6 +12,7 @@
 use Enqueue\Client\MessagePriority;
 use Enqueue\Client\Route;
 use Enqueue\Client\RouteCollection;
+use Enqueue\Stomp\ExtensionType;
 use Enqueue\Stomp\StompContext;
 use Enqueue\Stomp\StompDestination;
 use Enqueue\Stomp\StompMessage;
@@ -47,7 +48,7 @@ public function testShouldBeSubClassOfStompDriver()
 
     public function testShouldCreateAndReturnStompQueueInstance()
     {
-        $expectedQueue = new StompDestination();
+        $expectedQueue = new StompDestination(ExtensionType::RABBITMQ);
 
         $context = $this->createContextMock();
         $context
@@ -185,10 +186,10 @@ public function testShouldInitDeliveryDelayIfDelayPropertyOnSendToProcessor()
 
     public function shouldSendMessageToDelayExchangeIfDelaySet()
     {
-        $queue = new StompDestination();
+        $queue = new StompDestination(ExtensionType::RABBITMQ);
         $queue->setStompName('queueName');
 
-        $delayTopic = new StompDestination();
+        $delayTopic = new StompDestination(ExtensionType::RABBITMQ);
         $delayTopic->setStompName('delayTopic');
 
         $transportMessage = new StompMessage();
@@ -339,7 +340,7 @@ public function testShouldSetupBroker()
             ->expects($this->any())
             ->method('createQueue')
             ->willReturnCallback(function (string $name) {
-                $destination = new StompDestination();
+                $destination = new StompDestination(ExtensionType::RABBITMQ);
                 $destination->setType(StompDestination::TYPE_QUEUE);
                 $destination->setStompName($name);
 
@@ -431,7 +432,7 @@ public function testSetupBrokerShouldCreateDelayExchangeIfEnabled()
             ->expects($this->any())
             ->method('createQueue')
             ->willReturnCallback(function (string $name) {
-                $destination = new StompDestination();
+                $destination = new StompDestination(ExtensionType::RABBITMQ);
                 $destination->setType(StompDestination::TYPE_QUEUE);
                 $destination->setStompName($name);
 
@@ -442,7 +443,7 @@ public function testSetupBrokerShouldCreateDelayExchangeIfEnabled()
             ->expects($this->any())
             ->method('createTopic')
             ->willReturnCallback(function (string $name) {
-                $destination = new StompDestination();
+                $destination = new StompDestination(ExtensionType::RABBITMQ);
                 $destination->setType(StompDestination::TYPE_TOPIC);
                 $destination->setStompName($name);
 
@@ -503,7 +504,7 @@ protected function createProducerMock(): InteropProducer
      */
     protected function createQueue(string $name): InteropQueue
     {
-        $destination = new StompDestination();
+        $destination = new StompDestination(ExtensionType::RABBITMQ);
         $destination->setType(StompDestination::TYPE_QUEUE);
         $destination->setStompName($name);
 
@@ -515,7 +516,7 @@ protected function createQueue(string $name): InteropQueue
      */
     protected function createTopic(string $name): InteropTopic
     {
-        $destination = new StompDestination();
+        $destination = new StompDestination(ExtensionType::RABBITMQ);
         $destination->setType(StompDestination::TYPE_TOPIC);
         $destination->setStompName($name);
 
diff --git a/Tests/Client/Driver/StompDriverTest.php b/Tests/Client/Driver/StompDriverTest.php
index 67764f3..8f777fd 100644
--- a/Tests/Client/Driver/StompDriverTest.php
+++ b/Tests/Client/Driver/StompDriverTest.php
@@ -9,6 +9,7 @@
 use Enqueue\Client\Message;
 use Enqueue\Client\MessagePriority;
 use Enqueue\Client\RouteCollection;
+use Enqueue\Stomp\ExtensionType;
 use Enqueue\Stomp\StompContext;
 use Enqueue\Stomp\StompDestination;
 use Enqueue\Stomp\StompMessage;
@@ -127,7 +128,7 @@ protected function createProducerMock(): InteropProducer
      */
     protected function createQueue(string $name): InteropQueue
     {
-        $destination = new StompDestination();
+        $destination = new StompDestination(ExtensionType::RABBITMQ);
         $destination->setType(StompDestination::TYPE_QUEUE);
         $destination->setStompName($name);
 
@@ -139,7 +140,7 @@ protected function createQueue(string $name): InteropQueue
      */
     protected function createTopic(string $name): InteropTopic
     {
-        $destination = new StompDestination();
+        $destination = new StompDestination(ExtensionType::RABBITMQ);
         $destination->setType(StompDestination::TYPE_TOPIC);
         $destination->setStompName($name);
 

From 8bae6fb81ad8dcece3a6b14caaf2ba89f6e8b247 Mon Sep 17 00:00:00 2001
From: Patrick Mac Gregor <patrick.macgregor@basilicom.de>
Date: Mon, 9 Nov 2020 13:40:09 +0100
Subject: [PATCH 283/337] [fix] queue consumption: catch throwable for
 processing errors

cf. issue #1113
---
 Consumption/Context/ProcessorException.php | 6 +++---
 Consumption/QueueConsumer.php              | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/Consumption/Context/ProcessorException.php b/Consumption/Context/ProcessorException.php
index f41f232..329b13d 100644
--- a/Consumption/Context/ProcessorException.php
+++ b/Consumption/Context/ProcessorException.php
@@ -26,7 +26,7 @@ final class ProcessorException
     private $message;
 
     /**
-     * @var \Exception
+     * @var \Throwable
      */
     private $exception;
 
@@ -44,7 +44,7 @@ final class ProcessorException
      */
     private $logger;
 
-    public function __construct(Context $context, Consumer $consumer, Message $message, \Exception $exception, int $receivedAt, LoggerInterface $logger)
+    public function __construct(Context $context, Consumer $consumer, Message $message, \Throwable $exception, int $receivedAt, LoggerInterface $logger)
     {
         $this->context = $context;
         $this->consumer = $consumer;
@@ -69,7 +69,7 @@ public function getMessage(): Message
         return $this->message;
     }
 
-    public function getException(): \Exception
+    public function getException(): \Throwable
     {
         return $this->exception;
     }
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 6cb9029..80936bf 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -195,7 +195,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
             if (null === $result) {
                 try {
                     $result = $processor->process($message, $this->interopContext);
-                } catch (\Exception $e) {
+                } catch (\Exception | \Throwable $e) {
                     $result = $this->onProcessorException($extension, $consumer, $message, $e, $receivedAt);
                 }
             }
@@ -303,7 +303,7 @@ private function onEnd(ExtensionInterface $extension, int $startTime, ?int $exit
      *
      * https://github.com/symfony/symfony/blob/cbe289517470eeea27162fd2d523eb29c95f775f/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php#L77
      */
-    private function onProcessorException(ExtensionInterface $extension, Consumer $consumer, Message $message, \Exception $exception, int $receivedAt)
+    private function onProcessorException(ExtensionInterface $extension, Consumer $consumer, Message $message, \Throwable $exception, int $receivedAt)
     {
         $processorException = new ProcessorException($this->interopContext, $consumer, $message, $exception, $receivedAt, $this->logger);
 

From cfb5cf3cc5a7a0916274d7c02fc21ad3afcaab2e Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Fri, 22 Jan 2021 15:09:43 +0200
Subject: [PATCH 284/337] Fix test setUp method signatures

---
 Tests/DoctrineConnectionFactoryFactoryTest.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/DoctrineConnectionFactoryFactoryTest.php b/Tests/DoctrineConnectionFactoryFactoryTest.php
index 7282aa4..81bb009 100644
--- a/Tests/DoctrineConnectionFactoryFactoryTest.php
+++ b/Tests/DoctrineConnectionFactoryFactoryTest.php
@@ -25,7 +25,7 @@ class DoctrineConnectionFactoryFactoryTest extends TestCase
      */
     private $factory;
 
-    protected function setUp()
+    protected function setUp(): void
     {
         $this->registry = $this->prophesize(ManagerRegistry::class);
         $this->fallbackFactory = $this->prophesize(ConnectionFactoryFactoryInterface::class);

From 014c651f7adc728fbf484fb910fd88779b92f0ea Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Sun, 24 Jan 2021 20:35:57 +0200
Subject: [PATCH 285/337] Fix phpunit compatibility

- null queues;
- array subset;
- is array;
- is callable;
- read attribute;
- add ext-pcntl.
---
 .../DelayRedeliveredMessageExtensionTest.php  |  9 +++---
 .../ExclusiveCommandExtensionTest.php         | 15 +++++-----
 .../SetRouterPropertiesExtensionTest.php      |  9 +++---
 Tests/Client/Driver/AmqpDriverTest.php        |  3 +-
 Tests/Client/Driver/GenericDriverTest.php     |  3 +-
 .../Client/Driver/GenericDriverTestsTrait.php |  5 ++--
 Tests/Client/Driver/RabbitMqDriverTest.php    |  3 +-
 Tests/Client/ResourcesTest.php                |  6 ++--
 Tests/Client/TraceableProducerTest.php        | 29 ++++++++++---------
 Tests/Consumption/QueueConsumerTest.php       |  5 +++-
 Tests/ResourcesTest.php                       | 12 ++++----
 .../BuildClientExtensionsPassTest.php         | 14 ++++-----
 .../BuildCommandSubscriberRoutesPassTest.php  | 18 ++++++------
 .../BuildConsumptionExtensionsPassTest.php    | 14 ++++-----
 .../BuildProcessorRoutesPassTest.php          | 14 ++++-----
 .../BuildTopicSubscriberRoutesPassTest.php    | 16 +++++-----
 .../Client/FlushSpoolProducerListenerTest.php |  4 +--
 .../BuildConsumptionExtensionsPassTest.php    | 14 ++++-----
 18 files changed, 99 insertions(+), 94 deletions(-)

diff --git a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
index 050b4f2..61a307a 100644
--- a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
@@ -15,6 +15,7 @@
 use Interop\Queue\Destination;
 use Interop\Queue\Message as TransportMessage;
 use Interop\Queue\Processor;
+use Interop\Queue\Queue;
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
@@ -169,17 +170,15 @@ private function createProcessorMock(): Processor
     }
 
     /**
-     * @param mixed $queue
-     *
-     * @return MockObject
+     * @return MockObject|Consumer
      */
-    private function createConsumerStub($queue): Consumer
+    private function createConsumerStub(?Queue $queue): Consumer
     {
         $consumerMock = $this->createMock(Consumer::class);
         $consumerMock
             ->expects($this->any())
             ->method('getQueue')
-            ->willReturn($queue)
+            ->willReturn($queue ?? new NullQueue('queue'))
         ;
 
         return $consumerMock;
diff --git a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
index 878778b..1e58d14 100644
--- a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
@@ -15,6 +15,7 @@
 use Interop\Queue\Consumer;
 use Interop\Queue\Context as InteropContext;
 use Interop\Queue\Processor;
+use Interop\Queue\Queue;
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
@@ -243,15 +244,15 @@ public function testShouldDoNothingIfAnotherQueue()
     }
 
     /**
-     * @return MockObject
+     * @return MockObject|DriverInterface
      */
-    private function createDriverStub(RouteCollection $routeCollection = null): DriverInterface
+    private function createDriverStub(?RouteCollection $routeCollection = null): DriverInterface
     {
         $driver = $this->createMock(DriverInterface::class);
         $driver
             ->expects($this->any())
             ->method('getRouteCollection')
-            ->willReturn($routeCollection)
+            ->willReturn($routeCollection ?? new RouteCollection())
         ;
 
         return $driver;
@@ -274,17 +275,15 @@ private function createProcessorMock(): Processor
     }
 
     /**
-     * @param mixed $queue
-     *
-     * @return MockObject
+     * @return MockObject|Consumer
      */
-    private function createConsumerStub($queue): Consumer
+    private function createConsumerStub(?Queue $queue): Consumer
     {
         $consumerMock = $this->createMock(Consumer::class);
         $consumerMock
             ->expects($this->any())
             ->method('getQueue')
-            ->willReturn($queue)
+            ->willReturn($queue ?? new NullQueue('queue'))
         ;
 
         return $consumerMock;
diff --git a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
index 18760e9..e5015ff 100644
--- a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
@@ -13,6 +13,7 @@
 use Interop\Queue\Consumer;
 use Interop\Queue\Context as InteropContext;
 use Interop\Queue\Processor;
+use Interop\Queue\Queue;
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
@@ -186,17 +187,15 @@ private function createProcessorMock(): Processor
     }
 
     /**
-     * @param mixed $queue
-     *
-     * @return MockObject
+     * @return MockObject|Consumer
      */
-    private function createConsumerStub($queue): Consumer
+    private function createConsumerStub(?Queue $queue): Consumer
     {
         $consumerMock = $this->createMock(Consumer::class);
         $consumerMock
             ->expects($this->any())
             ->method('getQueue')
-            ->willReturn($queue)
+            ->willReturn($queue ?? new NullQueue('queue'))
         ;
 
         return $consumerMock;
diff --git a/Tests/Client/Driver/AmqpDriverTest.php b/Tests/Client/Driver/AmqpDriverTest.php
index 4e4cc62..3234c45 100644
--- a/Tests/Client/Driver/AmqpDriverTest.php
+++ b/Tests/Client/Driver/AmqpDriverTest.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Tests\Client\Driver;
 
+use DMS\PHPUnitExtensions\ArraySubset\Assert;
 use Enqueue\Client\Config;
 use Enqueue\Client\Driver\AmqpDriver;
 use Enqueue\Client\Driver\GenericDriver;
@@ -336,7 +337,7 @@ protected function getRouterTransportName(): string
     protected function assertTransportMessage(InteropMessage $transportMessage): void
     {
         $this->assertSame('body', $transportMessage->getBody());
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
             'hkey' => 'hval',
             'delivery_mode' => AmqpMessage::DELIVERY_MODE_PERSISTENT,
             'content_type' => 'ContentType',
diff --git a/Tests/Client/Driver/GenericDriverTest.php b/Tests/Client/Driver/GenericDriverTest.php
index 98b6c22..78f7f6e 100644
--- a/Tests/Client/Driver/GenericDriverTest.php
+++ b/Tests/Client/Driver/GenericDriverTest.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Tests\Client\Driver;
 
+use DMS\PHPUnitExtensions\ArraySubset\Assert;
 use Enqueue\Client\Config;
 use Enqueue\Client\Driver\GenericDriver;
 use Enqueue\Client\DriverInterface;
@@ -60,7 +61,7 @@ protected function createMessage(): InteropMessage
     protected function assertTransportMessage(InteropMessage $transportMessage): void
     {
         $this->assertSame('body', $transportMessage->getBody());
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
             'hkey' => 'hval',
             'message_id' => 'theMessageId',
             'timestamp' => 1000,
diff --git a/Tests/Client/Driver/GenericDriverTestsTrait.php b/Tests/Client/Driver/GenericDriverTestsTrait.php
index 4f1c815..d5ad498 100644
--- a/Tests/Client/Driver/GenericDriverTestsTrait.php
+++ b/Tests/Client/Driver/GenericDriverTestsTrait.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Tests\Client\Driver;
 
+use DMS\PHPUnitExtensions\ArraySubset\Assert;
 use Enqueue\Client\Config;
 use Enqueue\Client\DriverInterface;
 use Enqueue\Client\Message;
@@ -1191,10 +1192,10 @@ protected function assertTransportMessage(InteropMessage $transportMessage): voi
     protected function assertClientMessage(Message $clientMessage): void
     {
         $this->assertSame('body', $clientMessage->getBody());
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
             'hkey' => 'hval',
         ], $clientMessage->getHeaders());
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
             'pkey' => 'pval',
             Config::CONTENT_TYPE => 'theContentType',
             Config::EXPIRE => '22',
diff --git a/Tests/Client/Driver/RabbitMqDriverTest.php b/Tests/Client/Driver/RabbitMqDriverTest.php
index 2f6e20a..6b61465 100644
--- a/Tests/Client/Driver/RabbitMqDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqDriverTest.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Tests\Client\Driver;
 
+use DMS\PHPUnitExtensions\ArraySubset\Assert;
 use Enqueue\Client\Config;
 use Enqueue\Client\Driver\AmqpDriver;
 use Enqueue\Client\Driver\GenericDriver;
@@ -115,7 +116,7 @@ protected function getRouterTransportName(): string
     protected function assertTransportMessage(InteropMessage $transportMessage): void
     {
         $this->assertSame('body', $transportMessage->getBody());
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
             'hkey' => 'hval',
             'delivery_mode' => AmqpMessage::DELIVERY_MODE_PERSISTENT,
             'content_type' => 'ContentType',
diff --git a/Tests/Client/ResourcesTest.php b/Tests/Client/ResourcesTest.php
index c2d2b11..e79fb9d 100644
--- a/Tests/Client/ResourcesTest.php
+++ b/Tests/Client/ResourcesTest.php
@@ -28,7 +28,7 @@ public function testShouldGetAvailableDriverInExpectedFormat()
     {
         $availableDrivers = Resources::getAvailableDrivers();
 
-        $this->assertInternalType('array', $availableDrivers);
+        self::assertIsArray($availableDrivers);
         $this->assertGreaterThan(0, count($availableDrivers));
 
         $driverInfo = $availableDrivers[0];
@@ -50,7 +50,7 @@ public function testShouldGetAvailableDriverWithRequiredExtensionInExpectedForma
     {
         $availableDrivers = Resources::getAvailableDrivers();
 
-        $this->assertInternalType('array', $availableDrivers);
+        self::assertIsArray($availableDrivers);
         $this->assertGreaterThan(0, count($availableDrivers));
 
         $driverInfo = $availableDrivers[1];
@@ -72,7 +72,7 @@ public function testShouldGetKnownDriversInExpectedFormat()
     {
         $knownDrivers = Resources::getAvailableDrivers();
 
-        $this->assertInternalType('array', $knownDrivers);
+        self::assertIsArray($knownDrivers);
         $this->assertGreaterThan(0, count($knownDrivers));
 
         $driverInfo = $knownDrivers[0];
diff --git a/Tests/Client/TraceableProducerTest.php b/Tests/Client/TraceableProducerTest.php
index ff5ee83..ac0a529 100644
--- a/Tests/Client/TraceableProducerTest.php
+++ b/Tests/Client/TraceableProducerTest.php
@@ -2,6 +2,7 @@
 
 namespace Enqueue\Tests\Client;
 
+use DMS\PHPUnitExtensions\ArraySubset\Assert;
 use Enqueue\Client\Message;
 use Enqueue\Client\ProducerInterface;
 use Enqueue\Client\TraceableProducer;
@@ -45,7 +46,7 @@ public function testShouldCollectInfoIfStringGivenAsEventMessage()
 
         $producer->sendEvent('aFooTopic', 'aFooBody');
 
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
             [
                 'topic' => 'aFooTopic',
                 'command' => null,
@@ -70,7 +71,7 @@ public function testShouldCollectInfoIfArrayGivenAsEventMessage()
 
         $producer->sendEvent('aFooTopic', ['foo' => 'fooVal', 'bar' => 'barVal']);
 
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
             [
                 'topic' => 'aFooTopic',
                 'command' => null,
@@ -106,7 +107,7 @@ public function testShouldCollectInfoIfEventMessageObjectGivenAsMessage()
 
         $producer->sendEvent('aFooTopic', $message);
 
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
             [
                 'topic' => 'aFooTopic',
                 'command' => null,
@@ -168,7 +169,7 @@ public function testShouldCollectInfoIfStringGivenAsCommandMessage()
 
         $producer->sendCommand('aFooCommand', 'aFooBody');
 
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
             [
                 'topic' => null,
                 'command' => 'aFooCommand',
@@ -193,7 +194,7 @@ public function testShouldCollectInfoIfArrayGivenAsCommandMessage()
 
         $producer->sendCommand('aFooCommand', ['foo' => 'fooVal', 'bar' => 'barVal']);
 
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
             [
                 'topic' => null,
                 'command' => 'aFooCommand',
@@ -229,7 +230,7 @@ public function testShouldCollectInfoIfCommandMessageObjectGivenAsMessage()
 
         $producer->sendCommand('aFooCommand', $message);
 
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
             [
                 'topic' => null,
                 'command' => 'aFooCommand',
@@ -275,7 +276,7 @@ public function testShouldAllowGetInfoSentToSameTopic()
         $producer->sendEvent('aFooTopic', 'aFooBody');
         $producer->sendEvent('aFooTopic', 'aFooBody');
 
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
                 ['topic' => 'aFooTopic', 'body' => 'aFooBody'],
                 ['topic' => 'aFooTopic', 'body' => 'aFooBody'],
         ], $producer->getTraces());
@@ -288,7 +289,7 @@ public function testShouldAllowGetInfoSentToDifferentTopics()
         $producer->sendEvent('aFooTopic', 'aFooBody');
         $producer->sendEvent('aBarTopic', 'aBarBody');
 
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
             ['topic' => 'aFooTopic', 'body' => 'aFooBody'],
             ['topic' => 'aBarTopic', 'body' => 'aBarBody'],
         ], $producer->getTraces());
@@ -301,11 +302,11 @@ public function testShouldAllowGetInfoSentToSpecialTopic()
         $producer->sendEvent('aFooTopic', 'aFooBody');
         $producer->sendEvent('aBarTopic', 'aBarBody');
 
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
             ['topic' => 'aFooTopic', 'body' => 'aFooBody'],
         ], $producer->getTopicTraces('aFooTopic'));
 
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
             ['topic' => 'aBarTopic', 'body' => 'aBarBody'],
         ], $producer->getTopicTraces('aBarTopic'));
     }
@@ -317,7 +318,7 @@ public function testShouldAllowGetInfoSentToSameCommand()
         $producer->sendCommand('aFooCommand', 'aFooBody');
         $producer->sendCommand('aFooCommand', 'aFooBody');
 
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
             ['command' => 'aFooCommand', 'body' => 'aFooBody'],
             ['command' => 'aFooCommand', 'body' => 'aFooBody'],
         ], $producer->getTraces());
@@ -330,7 +331,7 @@ public function testShouldAllowGetInfoSentToDifferentCommands()
         $producer->sendCommand('aFooCommand', 'aFooBody');
         $producer->sendCommand('aBarCommand', 'aBarBody');
 
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
             ['command' => 'aFooCommand', 'body' => 'aFooBody'],
             ['command' => 'aBarCommand', 'body' => 'aBarBody'],
         ], $producer->getTraces());
@@ -343,11 +344,11 @@ public function testShouldAllowGetInfoSentToSpecialCommand()
         $producer->sendCommand('aFooCommand', 'aFooBody');
         $producer->sendCommand('aBarCommand', 'aBarBody');
 
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
             ['command' => 'aFooCommand', 'body' => 'aFooBody'],
         ], $producer->getCommandTraces('aFooCommand'));
 
-        $this->assertArraySubset([
+        Assert::assertArraySubset([
             ['command' => 'aBarCommand', 'body' => 'aBarBody'],
         ], $producer->getCommandTraces('aBarCommand'));
     }
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 9e25ab9..949c9ca 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -21,6 +21,7 @@
 use Enqueue\Consumption\QueueConsumer;
 use Enqueue\Consumption\Result;
 use Enqueue\Null\NullQueue;
+use Enqueue\Test\ReadAttributeTrait;
 use Enqueue\Tests\Consumption\Mock\BreakCycleExtension;
 use Enqueue\Tests\Consumption\Mock\DummySubscriptionConsumer;
 use Interop\Queue\Consumer;
@@ -36,6 +37,8 @@
 
 class QueueConsumerTest extends TestCase
 {
+    use ReadAttributeTrait;
+
     public function testCouldBeConstructedWithAllArguments()
     {
         new QueueConsumer($this->createContextStub(), null, [], null, 0);
@@ -177,7 +180,7 @@ public function testShouldAllowBindCallbackToQueueName()
 
         $boundProcessors = $this->readAttribute($consumer, 'boundProcessors');
 
-        $this->assertInternalType('array', $boundProcessors);
+        self::assertIsArray($boundProcessors);
         $this->assertCount(1, $boundProcessors);
         $this->assertArrayHasKey($queueName, $boundProcessors);
 
diff --git a/Tests/ResourcesTest.php b/Tests/ResourcesTest.php
index 214a083..ec713fd 100644
--- a/Tests/ResourcesTest.php
+++ b/Tests/ResourcesTest.php
@@ -28,7 +28,7 @@ public function testShouldGetAvailableConnectionsInExpectedFormat()
     {
         $availableConnections = Resources::getAvailableConnections();
 
-        $this->assertInternalType('array', $availableConnections);
+        self::assertIsArray($availableConnections);
         $this->assertArrayHasKey(RedisConnectionFactory::class, $availableConnections);
 
         $connectionInfo = $availableConnections[RedisConnectionFactory::class];
@@ -46,7 +46,7 @@ public function testShouldGetKnownConnectionsInExpectedFormat()
     {
         $availableConnections = Resources::getKnownConnections();
 
-        $this->assertInternalType('array', $availableConnections);
+        self::assertIsArray($availableConnections);
         $this->assertArrayHasKey(RedisConnectionFactory::class, $availableConnections);
 
         $connectionInfo = $availableConnections[RedisConnectionFactory::class];
@@ -93,12 +93,12 @@ public function testShouldAllowRegisterConnectionThatIsNotInstalled()
         Resources::addConnection('theConnectionClass', ['foo'], [], 'foo');
 
         $knownConnections = Resources::getKnownConnections();
-        $this->assertInternalType('array', $knownConnections);
+        self::assertIsArray($knownConnections);
         $this->assertArrayHasKey('theConnectionClass', $knownConnections);
 
         $availableConnections = Resources::getAvailableConnections();
 
-        $this->assertInternalType('array', $availableConnections);
+        self::assertIsArray($availableConnections);
         $this->assertArrayNotHasKey('theConnectionClass', $availableConnections);
     }
 
@@ -115,7 +115,7 @@ public function testShouldAllowGetPreviouslyRegisteredConnection()
 
         $availableConnections = Resources::getAvailableConnections();
 
-        $this->assertInternalType('array', $availableConnections);
+        self::assertIsArray($availableConnections);
         $this->assertArrayHasKey($connectionClass, $availableConnections);
 
         $connectionInfo = $availableConnections[$connectionClass];
@@ -133,7 +133,7 @@ public function testShouldHaveRegisteredWampConfiguration()
     {
         $availableConnections = Resources::getKnownConnections();
 
-        $this->assertInternalType('array', $availableConnections);
+        self::assertIsArray($availableConnections);
         $this->assertArrayHasKey(WampConnectionFactory::class, $availableConnections);
 
         $connectionInfo = $availableConnections[WampConnectionFactory::class];
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php
index af69293..90c7493 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php
@@ -72,7 +72,7 @@ public function testShouldRegisterClientExtension()
         $pass = new BuildClientExtensionsPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $extensions->getArgument(0));
+        self::assertIsArray($extensions->getArgument(0));
         $this->assertEquals([
             new Reference('aFooExtension'),
             new Reference('aBarExtension'),
@@ -99,7 +99,7 @@ public function testShouldIgnoreOtherClientExtensions()
         $pass = new BuildClientExtensionsPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $extensions->getArgument(0));
+        self::assertIsArray($extensions->getArgument(0));
         $this->assertEquals([
             new Reference('aFooExtension'),
         ], $extensions->getArgument(0));
@@ -125,7 +125,7 @@ public function testShouldAddExtensionIfClientAll()
         $pass = new BuildClientExtensionsPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $extensions->getArgument(0));
+        self::assertIsArray($extensions->getArgument(0));
         $this->assertEquals([
             new Reference('aFooExtension'),
         ], $extensions->getArgument(0));
@@ -151,7 +151,7 @@ public function testShouldTreatTagsWithoutClientAsDefaultClient()
         $pass = new BuildClientExtensionsPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $extensions->getArgument(0));
+        self::assertIsArray($extensions->getArgument(0));
         $this->assertEquals([
             new Reference('aFooExtension'),
             new Reference('aBarExtension'),
@@ -247,7 +247,7 @@ public function testShouldMergeWithAddedPreviously()
         $pass = new BuildClientExtensionsPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $extensions->getArgument(0));
+        self::assertIsArray($extensions->getArgument(0));
         $this->assertCount(4, $extensions->getArgument(0));
     }
 
@@ -275,12 +275,12 @@ public function testShouldRegisterProcessorWithMatchedNameToCorrespondingExtensi
         $pass = new BuildClientExtensionsPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $fooExtensions->getArgument(0));
+        self::assertIsArray($fooExtensions->getArgument(0));
         $this->assertEquals([
             new Reference('aFooExtension'),
         ], $fooExtensions->getArgument(0));
 
-        $this->assertInternalType('array', $barExtensions->getArgument(0));
+        self::assertIsArray($barExtensions->getArgument(0));
         $this->assertEquals([
             new Reference('aBarExtension'),
         ], $barExtensions->getArgument(0));
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
index 346c0cb..a2e964d 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
@@ -96,7 +96,7 @@ public function testShouldRegisterProcessorWithMatchedName()
 
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(1, $routeCollection->getArgument(0));
     }
 
@@ -120,7 +120,7 @@ public function testShouldRegisterProcessorWithoutNameToDefaultClient()
 
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(1, $routeCollection->getArgument(0));
     }
 
@@ -144,7 +144,7 @@ public function testShouldRegisterProcessorIfClientNameEqualsAll()
 
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(1, $routeCollection->getArgument(0));
     }
 
@@ -166,7 +166,7 @@ public function testShouldRegisterProcessorIfCommandsIsString()
         $pass = new BuildCommandSubscriberRoutesPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(1, $routeCollection->getArgument(0));
 
         $this->assertEquals(
@@ -222,7 +222,7 @@ public function testShouldRegisterProcessorIfCommandsAreStrings()
         $pass = new BuildCommandSubscriberRoutesPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(2, $routeCollection->getArgument(0));
 
         $this->assertEquals(
@@ -266,7 +266,7 @@ public function testShouldRegisterProcessorIfParamSingleCommandArray()
         $pass = new BuildCommandSubscriberRoutesPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
 
         $this->assertCount(1, $routeCollection->getArgument(0));
 
@@ -305,7 +305,7 @@ public function testShouldRegisterProcessorIfCommandsAreParamArrays()
         $pass = new BuildCommandSubscriberRoutesPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(2, $routeCollection->getArgument(0));
 
         $this->assertEquals(
@@ -372,7 +372,7 @@ public function testShouldMergeExtractedRoutesWithAlreadySetInCollection()
         $pass = new BuildCommandSubscriberRoutesPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(3, $routeCollection->getArgument(0));
 
         $this->assertEquals(
@@ -422,7 +422,7 @@ public function testShouldRegister08CommandProcessor()
         $pass = new BuildCommandSubscriberRoutesPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(1, $routeCollection->getArgument(0));
 
         $this->assertEquals(
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php
index db991e9..a946362 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php
@@ -72,7 +72,7 @@ public function testShouldRegisterClientExtension()
         $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $extensions->getArgument(0));
+        self::assertIsArray($extensions->getArgument(0));
         $this->assertEquals([
             new Reference('aFooExtension'),
             new Reference('aBarExtension'),
@@ -99,7 +99,7 @@ public function testShouldIgnoreOtherClientExtensions()
         $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $extensions->getArgument(0));
+        self::assertIsArray($extensions->getArgument(0));
         $this->assertEquals([
             new Reference('aFooExtension'),
         ], $extensions->getArgument(0));
@@ -125,7 +125,7 @@ public function testShouldAddExtensionIfClientAll()
         $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $extensions->getArgument(0));
+        self::assertIsArray($extensions->getArgument(0));
         $this->assertEquals([
             new Reference('aFooExtension'),
         ], $extensions->getArgument(0));
@@ -151,7 +151,7 @@ public function testShouldTreatTagsWithoutClientAsDefaultClient()
         $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $extensions->getArgument(0));
+        self::assertIsArray($extensions->getArgument(0));
         $this->assertEquals([
             new Reference('aFooExtension'),
             new Reference('aBarExtension'),
@@ -247,7 +247,7 @@ public function testShouldMergeWithAddedPreviously()
         $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $extensions->getArgument(0));
+        self::assertIsArray($extensions->getArgument(0));
         $this->assertCount(4, $extensions->getArgument(0));
     }
 
@@ -275,12 +275,12 @@ public function testShouldRegisterProcessorWithMatchedNameToCorrespondingExtensi
         $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $fooExtensions->getArgument(0));
+        self::assertIsArray($fooExtensions->getArgument(0));
         $this->assertEquals([
             new Reference('aFooExtension'),
         ], $fooExtensions->getArgument(0));
 
-        $this->assertInternalType('array', $barExtensions->getArgument(0));
+        self::assertIsArray($barExtensions->getArgument(0));
         $this->assertEquals([
             new Reference('aBarExtension'),
         ], $barExtensions->getArgument(0));
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php
index 74553e6..b2839c4 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php
@@ -112,7 +112,7 @@ public function testShouldRegisterProcessorWithMatchedName()
 
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(1, $routeCollection->getArgument(0));
     }
 
@@ -136,7 +136,7 @@ public function testShouldRegisterProcessorWithoutNameToDefaultClient()
 
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(1, $routeCollection->getArgument(0));
     }
 
@@ -160,7 +160,7 @@ public function testShouldRegisterProcessorIfClientNameEqualsAll()
 
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(1, $routeCollection->getArgument(0));
     }
 
@@ -180,7 +180,7 @@ public function testShouldRegisterAsTopicProcessor()
         $pass = new BuildProcessorRoutesPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(1, $routeCollection->getArgument(0));
 
         $this->assertEquals(
@@ -212,7 +212,7 @@ public function testShouldRegisterAsCommandProcessor()
         $pass = new BuildProcessorRoutesPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(1, $routeCollection->getArgument(0));
 
         $this->assertEquals(
@@ -244,7 +244,7 @@ public function testShouldRegisterWithCustomProcessorName()
         $pass = new BuildProcessorRoutesPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(1, $routeCollection->getArgument(0));
 
         $this->assertEquals(
@@ -279,7 +279,7 @@ public function testShouldMergeExtractedRoutesWithAlreadySetInCollection()
         $pass = new BuildProcessorRoutesPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(3, $routeCollection->getArgument(0));
 
         $this->assertEquals(
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
index 65b64dc..af1c6a3 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
@@ -96,7 +96,7 @@ public function testShouldRegisterProcessorWithMatchedName()
 
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(1, $routeCollection->getArgument(0));
     }
 
@@ -120,7 +120,7 @@ public function testShouldRegisterProcessorWithoutNameToDefaultClient()
 
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(1, $routeCollection->getArgument(0));
     }
 
@@ -144,7 +144,7 @@ public function testShouldRegisterProcessorIfClientNameEqualsAll()
 
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(1, $routeCollection->getArgument(0));
     }
 
@@ -166,7 +166,7 @@ public function testShouldRegisterProcessorIfTopicsIsString()
         $pass = new BuildTopicSubscriberRoutesPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(1, $routeCollection->getArgument(0));
 
         $this->assertEquals(
@@ -222,7 +222,7 @@ public function testShouldRegisterProcessorIfTopicsAreStrings()
         $pass = new BuildTopicSubscriberRoutesPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(2, $routeCollection->getArgument(0));
 
         $this->assertEquals(
@@ -265,7 +265,7 @@ public function testShouldRegisterProcessorIfTopicsAreParamArrays()
         $pass = new BuildTopicSubscriberRoutesPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(2, $routeCollection->getArgument(0));
 
         $this->assertEquals(
@@ -332,7 +332,7 @@ public function testShouldMergeExtractedRoutesWithAlreadySetInCollection()
         $pass = new BuildTopicSubscriberRoutesPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(3, $routeCollection->getArgument(0));
 
         $this->assertEquals(
@@ -379,7 +379,7 @@ public function testShouldRegister08TopicSubscriber()
         $pass = new BuildTopicSubscriberRoutesPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $routeCollection->getArgument(0));
+        self::assertIsArray($routeCollection->getArgument(0));
         $this->assertCount(2, $routeCollection->getArgument(0));
 
         $this->assertEquals(
diff --git a/Tests/Symfony/Client/FlushSpoolProducerListenerTest.php b/Tests/Symfony/Client/FlushSpoolProducerListenerTest.php
index e7ca3ff..3476c10 100644
--- a/Tests/Symfony/Client/FlushSpoolProducerListenerTest.php
+++ b/Tests/Symfony/Client/FlushSpoolProducerListenerTest.php
@@ -23,7 +23,7 @@ public function testShouldSubscribeOnKernelTerminateEvent()
     {
         $events = FlushSpoolProducerListener::getSubscribedEvents();
 
-        $this->assertInternalType('array', $events);
+        self::assertIsArray($events);
         $this->assertArrayHasKey(KernelEvents::TERMINATE, $events);
 
         $this->assertEquals('flushMessages', $events[KernelEvents::TERMINATE]);
@@ -33,7 +33,7 @@ public function testShouldSubscribeOnConsoleTerminateEvent()
     {
         $events = FlushSpoolProducerListener::getSubscribedEvents();
 
-        $this->assertInternalType('array', $events);
+        self::assertIsArray($events);
         $this->assertArrayHasKey(ConsoleEvents::TERMINATE, $events);
 
         $this->assertEquals('flushMessages', $events[ConsoleEvents::TERMINATE]);
diff --git a/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php b/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
index b26b448..f16daf0 100644
--- a/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
+++ b/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
@@ -72,7 +72,7 @@ public function testShouldRegisterTransportExtension()
         $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $extensions->getArgument(0));
+        self::assertIsArray($extensions->getArgument(0));
         $this->assertEquals([
             new Reference('aFooExtension'),
             new Reference('aBarExtension'),
@@ -99,7 +99,7 @@ public function testShouldIgnoreOtherTransportExtensions()
         $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $extensions->getArgument(0));
+        self::assertIsArray($extensions->getArgument(0));
         $this->assertEquals([
             new Reference('aFooExtension'),
         ], $extensions->getArgument(0));
@@ -125,7 +125,7 @@ public function testShouldAddExtensionIfTransportAll()
         $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $extensions->getArgument(0));
+        self::assertIsArray($extensions->getArgument(0));
         $this->assertEquals([
             new Reference('aFooExtension'),
         ], $extensions->getArgument(0));
@@ -151,7 +151,7 @@ public function testShouldTreatTagsWithoutTransportAsDefaultTransport()
         $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $extensions->getArgument(0));
+        self::assertIsArray($extensions->getArgument(0));
         $this->assertEquals([
             new Reference('aFooExtension'),
             new Reference('aBarExtension'),
@@ -247,7 +247,7 @@ public function testShouldMergeWithAddedPreviously()
         $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $extensions->getArgument(0));
+        self::assertIsArray($extensions->getArgument(0));
         $this->assertCount(4, $extensions->getArgument(0));
     }
 
@@ -275,12 +275,12 @@ public function testShouldRegisterProcessorWithMatchedNameToCorrespondingRegistr
         $pass = new BuildConsumptionExtensionsPass();
         $pass->process($container);
 
-        $this->assertInternalType('array', $fooExtensions->getArgument(0));
+        self::assertIsArray($fooExtensions->getArgument(0));
         $this->assertEquals([
             new Reference('aFooExtension'),
         ], $fooExtensions->getArgument(0));
 
-        $this->assertInternalType('array', $barExtensions->getArgument(0));
+        self::assertIsArray($barExtensions->getArgument(0));
         $this->assertEquals([
             new Reference('aBarExtension'),
         ], $barExtensions->getArgument(0));

From f7ac9dfb7ab033b1af7009763dfee3cebd537ee4 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Sun, 24 Jan 2021 20:45:22 +0200
Subject: [PATCH 286/337] Fix more attributes; nullable params

---
 .../ExclusiveCommandExtensionTest.php                     | 2 +-
 Tests/Client/RouterProcessorTest.php                      | 8 +++++---
 Tests/Consumption/FallbackSubscriptionConsumerTest.php    | 3 +++
 Tests/Symfony/Client/ConsumeCommandTest.php               | 4 ++--
 Tests/Symfony/Client/RoutesCommandTest.php                | 2 +-
 Tests/Symfony/Client/SimpleConsumeCommandTest.php         | 4 ++--
 6 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
index 1e58d14..dc2c00c 100644
--- a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
@@ -252,7 +252,7 @@ private function createDriverStub(?RouteCollection $routeCollection = null): Dri
         $driver
             ->expects($this->any())
             ->method('getRouteCollection')
-            ->willReturn($routeCollection ?? new RouteCollection())
+            ->willReturn($routeCollection ?? new RouteCollection([]))
         ;
 
         return $driver;
diff --git a/Tests/Client/RouterProcessorTest.php b/Tests/Client/RouterProcessorTest.php
index cdd44df..e2611d0 100644
--- a/Tests/Client/RouterProcessorTest.php
+++ b/Tests/Client/RouterProcessorTest.php
@@ -13,6 +13,7 @@
 use Enqueue\Null\NullContext;
 use Enqueue\Null\NullMessage;
 use Enqueue\Test\ClassExtensionTrait;
+use Enqueue\Test\ReadAttributeTrait;
 use Interop\Queue\Destination;
 use Interop\Queue\Message as TransportMessage;
 use Interop\Queue\Processor;
@@ -21,6 +22,7 @@
 class RouterProcessorTest extends TestCase
 {
     use ClassExtensionTrait;
+    use ReadAttributeTrait;
 
     public function testShouldImplementProcessorInterface()
     {
@@ -195,15 +197,15 @@ public function testShouldDoNotModifyOriginalMessage()
     }
 
     /**
-     * @return \PHPUnit\Framework\MockObject\MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject|DriverInterface
      */
-    private function createDriverStub(RouteCollection $routeCollection = null): DriverInterface
+    private function createDriverStub(?RouteCollection $routeCollection = null): DriverInterface
     {
         $driver = $this->createMock(DriverInterface::class);
         $driver
             ->expects($this->any())
             ->method('getRouteCollection')
-            ->willReturn($routeCollection)
+            ->willReturn($routeCollection ?? new RouteCollection([]))
         ;
 
         return $driver;
diff --git a/Tests/Consumption/FallbackSubscriptionConsumerTest.php b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
index 6206673..4f8ac7a 100644
--- a/Tests/Consumption/FallbackSubscriptionConsumerTest.php
+++ b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
@@ -3,6 +3,7 @@
 namespace Enqueue\Tests\Consumption;
 
 use Enqueue\Consumption\FallbackSubscriptionConsumer;
+use Enqueue\Test\ReadAttributeTrait;
 use Interop\Queue\Consumer;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Queue as InteropQueue;
@@ -11,6 +12,8 @@
 
 class FallbackSubscriptionConsumerTest extends TestCase
 {
+    use ReadAttributeTrait;
+
     public function testShouldImplementSubscriptionConsumerInterface()
     {
         $rc = new \ReflectionClass(FallbackSubscriptionConsumer::class);
diff --git a/Tests/Symfony/Client/ConsumeCommandTest.php b/Tests/Symfony/Client/ConsumeCommandTest.php
index be7de22..706aee0 100644
--- a/Tests/Symfony/Client/ConsumeCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeCommandTest.php
@@ -605,13 +605,13 @@ private function createQueueConsumerMock()
     /**
      * @return \PHPUnit\Framework\MockObject\MockObject|DriverInterface
      */
-    private function createDriverStub(RouteCollection $routeCollection = null): DriverInterface
+    private function createDriverStub(?RouteCollection $routeCollection = null): DriverInterface
     {
         $driverMock = $this->createMock(DriverInterface::class);
         $driverMock
             ->expects($this->any())
             ->method('getRouteCollection')
-            ->willReturn($routeCollection)
+            ->willReturn($routeCollection ?? new RouteCollection([]))
         ;
 
         $driverMock
diff --git a/Tests/Symfony/Client/RoutesCommandTest.php b/Tests/Symfony/Client/RoutesCommandTest.php
index d9935bd..de29e4c 100644
--- a/Tests/Symfony/Client/RoutesCommandTest.php
+++ b/Tests/Symfony/Client/RoutesCommandTest.php
@@ -331,7 +331,7 @@ public function testShouldOutputRouteOptions()
     }
 
     /**
-     * @return \PHPUnit\Framework\MockObject\MockObject
+     * @return \PHPUnit\Framework\MockObject\MockObject|DriverInterface
      */
     private function createDriverStub(Config $config, RouteCollection $routeCollection): DriverInterface
     {
diff --git a/Tests/Symfony/Client/SimpleConsumeCommandTest.php b/Tests/Symfony/Client/SimpleConsumeCommandTest.php
index 8f23acd..fb89dae 100644
--- a/Tests/Symfony/Client/SimpleConsumeCommandTest.php
+++ b/Tests/Symfony/Client/SimpleConsumeCommandTest.php
@@ -122,13 +122,13 @@ private function createQueueConsumerMock()
     /**
      * @return \PHPUnit\Framework\MockObject\MockObject|DriverInterface
      */
-    private function createDriverStub(RouteCollection $routeCollection = null): DriverInterface
+    private function createDriverStub(?RouteCollection $routeCollection = null): DriverInterface
     {
         $driverMock = $this->createMock(DriverInterface::class);
         $driverMock
             ->expects($this->any())
             ->method('getRouteCollection')
-            ->willReturn($routeCollection)
+            ->willReturn($routeCollection ?? new RouteCollection([]))
         ;
 
         $driverMock

From 7fb6108fe91d5e802a5b31a3f4e2bbada463b4ab Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Sun, 24 Jan 2021 20:58:12 +0200
Subject: [PATCH 287/337] Fix more attributes; void returns; assert string
 contains; resource;

---
 Tests/Consumption/QueueConsumerTest.php               | 4 ----
 Tests/Symfony/Client/RoutesCommandTest.php            | 2 +-
 Tests/Symfony/Client/SetupBrokerCommandTest.php       | 4 ++--
 Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php | 2 +-
 Tests/Util/UUIDTest.php                               | 2 +-
 5 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 949c9ca..5023bea 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -206,7 +206,6 @@ public function testShouldUseContextSubscriptionConsumerIfSupport()
         $contextSubscriptionConsumer
             ->expects($this->once())
             ->method('consume')
-            ->willReturn(null)
         ;
 
         $fallbackSubscriptionConsumer = $this->createSubscriptionConsumerMock();
@@ -254,7 +253,6 @@ public function testShouldUseFallbackSubscriptionConsumerIfNotSupported()
         $fallbackSubscriptionConsumer
             ->expects($this->once())
             ->method('consume')
-            ->willReturn(null)
         ;
 
         $contextMock = $this->createContextWithoutSubscriptionConsumerMock();
@@ -291,7 +289,6 @@ public function testShouldSubscribeToGivenQueueWithExpectedTimeout()
             ->expects($this->once())
             ->method('consume')
             ->with(12345)
-            ->willReturn(null)
         ;
 
         $contextMock = $this->createContextWithoutSubscriptionConsumerMock();
@@ -322,7 +319,6 @@ public function testShouldSubscribeToGivenQueueAndQuitAfterFifthConsumeCycle()
         $subscriptionConsumerMock
             ->expects($this->exactly(5))
             ->method('consume')
-            ->willReturn(null)
         ;
 
         $contextMock = $this->createContextWithoutSubscriptionConsumerMock();
diff --git a/Tests/Symfony/Client/RoutesCommandTest.php b/Tests/Symfony/Client/RoutesCommandTest.php
index de29e4c..04980c1 100644
--- a/Tests/Symfony/Client/RoutesCommandTest.php
+++ b/Tests/Symfony/Client/RoutesCommandTest.php
@@ -116,7 +116,7 @@ public function testShouldUseFooDriver()
             '--client' => 'foo',
         ]);
 
-        $this->assertContains('Found 1 routes', $tester->getDisplay());
+        $this->assertStringContainsString('Found 1 routes', $tester->getDisplay());
     }
 
     public function testThrowIfClientNotFound()
diff --git a/Tests/Symfony/Client/SetupBrokerCommandTest.php b/Tests/Symfony/Client/SetupBrokerCommandTest.php
index 358206d..82345b2 100644
--- a/Tests/Symfony/Client/SetupBrokerCommandTest.php
+++ b/Tests/Symfony/Client/SetupBrokerCommandTest.php
@@ -78,7 +78,7 @@ public function testShouldCallDriverSetupBrokerMethod()
         $tester = new CommandTester($command);
         $tester->execute([]);
 
-        $this->assertContains('Broker set up', $tester->getDisplay());
+        $this->assertStringContainsString('Broker set up', $tester->getDisplay());
     }
 
     public function testShouldCallRequestedClientDriverSetupBrokerMethod()
@@ -105,7 +105,7 @@ public function testShouldCallRequestedClientDriverSetupBrokerMethod()
             '--client' => 'foo',
         ]);
 
-        $this->assertContains('Broker set up', $tester->getDisplay());
+        $this->assertStringContainsString('Broker set up', $tester->getDisplay());
     }
 
     public function testShouldThrowIfClientNotFound()
diff --git a/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php b/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php
index 0d30582..f9a6612 100644
--- a/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php
+++ b/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php
@@ -74,7 +74,7 @@ public function testShouldCallDriverSetupBrokerMethod()
         $tester = new CommandTester($command);
         $tester->execute([]);
 
-        $this->assertContains('Broker set up', $tester->getDisplay());
+        $this->assertStringContainsString('Broker set up', $tester->getDisplay());
     }
 
     /**
diff --git a/Tests/Util/UUIDTest.php b/Tests/Util/UUIDTest.php
index ac30903..f21693e 100644
--- a/Tests/Util/UUIDTest.php
+++ b/Tests/Util/UUIDTest.php
@@ -11,7 +11,7 @@ public function testShouldGenerateUniqueId()
     {
         $uuid = UUID::generate();
 
-        $this->assertInternalType('string', $uuid);
+        $this->assertIsString($uuid);
         $this->assertEquals(36, strlen($uuid));
     }
 

From 07c51213a6782334469c9dbc1ea55c0e30b88b55 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Sun, 24 Jan 2021 21:07:30 +0200
Subject: [PATCH 288/337] Fix more attributes and nullables

---
 Tests/Consumption/QueueConsumerTest.php          | 3 +++
 Tests/Symfony/Client/ConsumeCommandTest.php      | 3 +++
 Tests/Symfony/Consumption/ConsumeCommandTest.php | 3 +++
 3 files changed, 9 insertions(+)

diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 5023bea..a087926 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -1537,6 +1537,9 @@ private function createExtension()
      */
     private function createConsumerStub($queue = null): Consumer
     {
+        if ($queue === null) {
+            $queue = 'queue';
+        }
         if (is_string($queue)) {
             $queue = new NullQueue($queue);
         }
diff --git a/Tests/Symfony/Client/ConsumeCommandTest.php b/Tests/Symfony/Client/ConsumeCommandTest.php
index 706aee0..fcc0995 100644
--- a/Tests/Symfony/Client/ConsumeCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeCommandTest.php
@@ -677,6 +677,9 @@ private function createExtension()
      */
     private function createConsumerStub($queue = null): Consumer
     {
+        if ($queue === null) {
+            $queue = 'queue';
+        }
         if (is_string($queue)) {
             $queue = new NullQueue($queue);
         }
diff --git a/Tests/Symfony/Consumption/ConsumeCommandTest.php b/Tests/Symfony/Consumption/ConsumeCommandTest.php
index c2f2257..cfcfd52 100644
--- a/Tests/Symfony/Consumption/ConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConsumeCommandTest.php
@@ -221,6 +221,9 @@ private function createExtension()
      */
     private function createConsumerStub($queue = null): Consumer
     {
+        if ($queue === null) {
+            $queue = 'queue';
+        }
         if (is_string($queue)) {
             $queue = new NullQueue($queue);
         }

From 66d4db654130def72c72fe745a0b4eb7f58d1c0b Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Thu, 21 Jan 2021 16:33:24 +0200
Subject: [PATCH 289/337] =?UTF-8?q?Fix=20at()=20warnings=20for=20non-logge?=
 =?UTF-8?q?r=20mocks=20=E2=80=94=20don't=20test=20for=20method=20call=20or?=
 =?UTF-8?q?der?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 Tests/Client/Driver/AmqpDriverTest.php        | 23 +++++++--------
 Tests/Client/Driver/FsDriverTest.php          |  8 +++---
 Tests/Client/Driver/GpsDriverTest.php         | 12 ++++----
 .../Client/Driver/RabbitMqStompDriverTest.php | 16 +++++------
 Tests/Client/Driver/RdKafkaDriverTest.php     |  6 ++--
 Tests/Client/Driver/SqsDriverTest.php         |  8 +++---
 Tests/Client/ProducerSendCommandTest.php      |  8 +++---
 Tests/Client/ProducerSendEventTest.php        | 12 ++++----
 Tests/Client/SpoolProducerTest.php            | 12 ++++----
 .../RouteRecipientListProcessorTest.php       |  4 +--
 Tests/Symfony/Client/ConsumeCommandTest.php   | 28 +++++++++----------
 .../ConfigurableConsumeCommandTest.php        | 12 ++++----
 12 files changed, 75 insertions(+), 74 deletions(-)

diff --git a/Tests/Client/Driver/AmqpDriverTest.php b/Tests/Client/Driver/AmqpDriverTest.php
index 3234c45..08b22c7 100644
--- a/Tests/Client/Driver/AmqpDriverTest.php
+++ b/Tests/Client/Driver/AmqpDriverTest.php
@@ -22,6 +22,7 @@
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\Queue as InteropQueue;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 
 class AmqpDriverTest extends TestCase
@@ -190,54 +191,54 @@ public function testShouldSetupBroker()
         $context = $this->createContextMock();
         // setup router
         $context
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('createTopic')
             ->willReturn($routerTopic)
         ;
         $context
-            ->expects($this->at(1))
+            ->expects(self::once())
             ->method('declareTopic')
             ->with($this->identicalTo($routerTopic))
         ;
 
         $context
-            ->expects($this->at(2))
+            ->expects(self::once())
             ->method('createQueue')
             ->willReturn($routerQueue)
         ;
         $context
-            ->expects($this->at(3))
+            ->expects(self::once())
             ->method('declareQueue')
             ->with($this->identicalTo($routerQueue))
         ;
 
         $context
-            ->expects($this->at(4))
+            ->expects(self::once())
             ->method('bind')
             ->with($this->isInstanceOf(AmqpBind::class))
         ;
 
         // setup processor with default queue
         $context
-            ->expects($this->at(5))
+            ->expects(self::once())
             ->method('createQueue')
             ->with($this->getDefaultQueueTransportName())
             ->willReturn($processorWithDefaultQueue)
         ;
         $context
-            ->expects($this->at(6))
+            ->expects(self::once())
             ->method('declareQueue')
             ->with($this->identicalTo($processorWithDefaultQueue))
         ;
 
         $context
-            ->expects($this->at(7))
+            ->expects(self::once())
             ->method('createQueue')
             ->with($this->getCustomQueueTransportName())
             ->willReturn($processorWithCustomQueue)
         ;
         $context
-            ->expects($this->at(8))
+            ->expects(self::once())
             ->method('declareQueue')
             ->with($this->identicalTo($processorWithCustomQueue))
         ;
@@ -290,7 +291,7 @@ protected function createDriver(...$args): DriverInterface
     }
 
     /**
-     * @return AmqpContext
+     * @return AmqpContext|MockObject
      */
     protected function createContextMock(): Context
     {
@@ -298,7 +299,7 @@ protected function createContextMock(): Context
     }
 
     /**
-     * @return AmqpProducer
+     * @return AmqpProducer|MockObject
      */
     protected function createProducerMock(): InteropProducer
     {
diff --git a/Tests/Client/Driver/FsDriverTest.php b/Tests/Client/Driver/FsDriverTest.php
index f1cd02f..a235b64 100644
--- a/Tests/Client/Driver/FsDriverTest.php
+++ b/Tests/Client/Driver/FsDriverTest.php
@@ -44,23 +44,23 @@ public function testShouldSetupBroker()
         $context = $this->createContextMock();
         // setup router
         $context
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('createQueue')
             ->willReturn($routerQueue)
         ;
         $context
-            ->expects($this->at(1))
+            ->expects(self::once())
             ->method('declareDestination')
             ->with($this->identicalTo($routerQueue))
         ;
         // setup processor queue
         $context
-            ->expects($this->at(2))
+            ->expects(self::once())
             ->method('createQueue')
             ->willReturn($processorQueue)
         ;
         $context
-            ->expects($this->at(3))
+            ->expects(self::once())
             ->method('declareDestination')
             ->with($this->identicalTo($processorQueue))
         ;
diff --git a/Tests/Client/Driver/GpsDriverTest.php b/Tests/Client/Driver/GpsDriverTest.php
index c0cac04..37a97be 100644
--- a/Tests/Client/Driver/GpsDriverTest.php
+++ b/Tests/Client/Driver/GpsDriverTest.php
@@ -46,35 +46,35 @@ public function testShouldSetupBroker()
         $context = $this->createContextMock();
         // setup router
         $context
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('createTopic')
             ->willReturn($routerTopic)
         ;
         $context
-            ->expects($this->at(1))
+            ->expects(self::once())
             ->method('createQueue')
             ->willReturn($routerQueue)
         ;
         $context
-            ->expects($this->at(2))
+            ->expects(self::once())
             ->method('subscribe')
             ->with($this->identicalTo($routerTopic), $this->identicalTo($routerQueue))
         ;
         $context
-            ->expects($this->at(3))
+            ->expects(self::once())
             ->method('createQueue')
             ->with($this->getDefaultQueueTransportName())
             ->willReturn($processorQueue)
         ;
         // setup processor queue
         $context
-            ->expects($this->at(4))
+            ->expects(self::once())
             ->method('createTopic')
             ->with($this->getDefaultQueueTransportName())
             ->willReturn($processorTopic)
         ;
         $context
-            ->expects($this->at(5))
+            ->expects(self::once())
             ->method('subscribe')
             ->with($this->identicalTo($processorTopic), $this->identicalTo($processorQueue))
         ;
diff --git a/Tests/Client/Driver/RabbitMqStompDriverTest.php b/Tests/Client/Driver/RabbitMqStompDriverTest.php
index 4152f93..f59376b 100644
--- a/Tests/Client/Driver/RabbitMqStompDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqStompDriverTest.php
@@ -196,12 +196,12 @@ public function shouldSendMessageToDelayExchangeIfDelaySet()
 
         $producer = $this->createProducerMock();
         $producer
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('setDeliveryDelay')
             ->with(10000)
         ;
         $producer
-            ->expects($this->at(1))
+            ->expects(self::once())
             ->method('setDeliveryDelay')
             ->with(null)
         ;
@@ -299,7 +299,7 @@ public function testShouldSetupBroker()
 
         $managementClient = $this->createManagementClientMock();
         $managementClient
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('declareExchange')
             ->with('aprefix.router', [
                 'type' => 'fanout',
@@ -308,7 +308,7 @@ public function testShouldSetupBroker()
             ])
         ;
         $managementClient
-            ->expects($this->at(1))
+            ->expects(self::once())
             ->method('declareQueue')
             ->with('aprefix.default', [
                 'durable' => true,
@@ -319,12 +319,12 @@ public function testShouldSetupBroker()
             ])
         ;
         $managementClient
-            ->expects($this->at(2))
+            ->expects(self::once())
             ->method('bind')
             ->with('aprefix.router', 'aprefix.default', 'aprefix.default')
         ;
         $managementClient
-            ->expects($this->at(3))
+            ->expects(self::once())
             ->method('declareQueue')
             ->with('aprefix.default', [
                 'durable' => true,
@@ -399,7 +399,7 @@ public function testSetupBrokerShouldCreateDelayExchangeIfEnabled()
 
         $managementClient = $this->createManagementClientMock();
         $managementClient
-            ->expects($this->at(4))
+            ->expects(self::once())
             ->method('declareExchange')
             ->with('aprefix.default.delayed', [
                 'type' => 'x-delayed-message',
@@ -411,7 +411,7 @@ public function testSetupBrokerShouldCreateDelayExchangeIfEnabled()
             ])
         ;
         $managementClient
-            ->expects($this->at(5))
+            ->expects(self::once())
             ->method('bind')
             ->with('aprefix.default.delayed', 'aprefix.default', 'aprefix.default')
         ;
diff --git a/Tests/Client/Driver/RdKafkaDriverTest.php b/Tests/Client/Driver/RdKafkaDriverTest.php
index 659590b..16d1da9 100644
--- a/Tests/Client/Driver/RdKafkaDriverTest.php
+++ b/Tests/Client/Driver/RdKafkaDriverTest.php
@@ -47,17 +47,17 @@ public function testShouldSetupBroker()
         $context = $this->createContextMock();
 
         $context
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('createQueue')
             ->willReturn($routerTopic)
         ;
         $context
-            ->expects($this->at(1))
+            ->expects(self::once())
             ->method('createQueue')
             ->willReturn($routerQueue)
         ;
         $context
-            ->expects($this->at(2))
+            ->expects(self::once())
             ->method('createQueue')
             ->willReturn($processorTopic)
         ;
diff --git a/Tests/Client/Driver/SqsDriverTest.php b/Tests/Client/Driver/SqsDriverTest.php
index 2e3005e..fb4af53 100644
--- a/Tests/Client/Driver/SqsDriverTest.php
+++ b/Tests/Client/Driver/SqsDriverTest.php
@@ -42,25 +42,25 @@ public function testShouldSetupBroker()
         $context = $this->createContextMock();
         // setup router
         $context
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('createQueue')
             ->with('aprefix_dot_default')
             ->willReturn($routerQueue)
         ;
         $context
-            ->expects($this->at(1))
+            ->expects(self::once())
             ->method('declareQueue')
             ->with($this->identicalTo($routerQueue))
         ;
         // setup processor queue
         $context
-            ->expects($this->at(2))
+            ->expects(self::once())
             ->method('createQueue')
             ->with('aprefix_dot_default')
             ->willReturn($processorQueue)
         ;
         $context
-            ->expects($this->at(3))
+            ->expects(self::once())
             ->method('declareQueue')
             ->with($this->identicalTo($processorQueue))
         ;
diff --git a/Tests/Client/ProducerSendCommandTest.php b/Tests/Client/ProducerSendCommandTest.php
index 8ac0704..9532ba3 100644
--- a/Tests/Client/ProducerSendCommandTest.php
+++ b/Tests/Client/ProducerSendCommandTest.php
@@ -370,7 +370,7 @@ public function testShouldCallPreSendCommandExtensionMethodWhenSendToBus()
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('onPreSendCommand')
             ->willReturnCallback(function (PreSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
@@ -408,7 +408,7 @@ public function testShouldCallPreSendCommandExtensionMethodWhenSendToApplication
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('onPreSendCommand')
             ->willReturnCallback(function (PreSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
@@ -446,7 +446,7 @@ public function testShouldCallPreDriverSendExtensionMethod()
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('onDriverPreSend')
             ->willReturnCallback(function (DriverPreSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
@@ -478,7 +478,7 @@ public function testShouldCallPostSendExtensionMethod()
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('onDriverPreSend')
             ->willReturnCallback(function (PostSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
diff --git a/Tests/Client/ProducerSendEventTest.php b/Tests/Client/ProducerSendEventTest.php
index 29a14b7..b482492 100644
--- a/Tests/Client/ProducerSendEventTest.php
+++ b/Tests/Client/ProducerSendEventTest.php
@@ -326,7 +326,7 @@ public function testShouldCallPreSendEventExtensionMethodWhenSendToBus()
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('onPreSendEvent')
             ->willReturnCallback(function (PreSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
@@ -364,7 +364,7 @@ public function testShouldCallPreSendEventExtensionMethodWhenSendToApplicationRo
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('onPreSendEvent')
             ->willReturnCallback(function (PreSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
@@ -402,7 +402,7 @@ public function testShouldCallPreDriverSendExtensionMethodWhenSendToMessageBus()
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('onDriverPreSend')
             ->willReturnCallback(function (DriverPreSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
@@ -434,7 +434,7 @@ public function testShouldCallPreDriverSendExtensionMethodWhenSendToApplicationR
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('onDriverPreSend')
             ->willReturnCallback(function (DriverPreSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
@@ -466,7 +466,7 @@ public function testShouldCallPostSendExtensionMethodWhenSendToMessageBus()
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('onPostSend')
             ->willReturnCallback(function (PostSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
@@ -498,7 +498,7 @@ public function testShouldCallPostSendExtensionMethodWhenSendToApplicationRouter
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('onDriverPreSend')
             ->willReturnCallback(function (PostSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
diff --git a/Tests/Client/SpoolProducerTest.php b/Tests/Client/SpoolProducerTest.php
index 024fd6d..df9e728 100644
--- a/Tests/Client/SpoolProducerTest.php
+++ b/Tests/Client/SpoolProducerTest.php
@@ -68,17 +68,17 @@ public function testShouldSendQueuedEventMessagesOnFlush()
 
         $realProducer = $this->createProducerMock();
         $realProducer
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('sendEvent')
             ->with('foo_topic', 'first')
         ;
         $realProducer
-            ->expects($this->at(1))
+            ->expects(self::once())
             ->method('sendEvent')
             ->with('bar_topic', ['second'])
         ;
         $realProducer
-            ->expects($this->at(2))
+            ->expects(self::once())
             ->method('sendEvent')
             ->with('baz_topic', $this->identicalTo($message))
         ;
@@ -103,17 +103,17 @@ public function testShouldSendQueuedCommandMessagesOnFlush()
 
         $realProducer = $this->createProducerMock();
         $realProducer
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('sendCommand')
             ->with('foo_command', 'first')
         ;
         $realProducer
-            ->expects($this->at(1))
+            ->expects(self::once())
             ->method('sendCommand')
             ->with('bar_command', ['second'])
         ;
         $realProducer
-            ->expects($this->at(2))
+            ->expects(self::once())
             ->method('sendCommand')
             ->with('baz_command', $this->identicalTo($message))
         ;
diff --git a/Tests/Router/RouteRecipientListProcessorTest.php b/Tests/Router/RouteRecipientListProcessorTest.php
index 8a2e089..dfc8206 100644
--- a/Tests/Router/RouteRecipientListProcessorTest.php
+++ b/Tests/Router/RouteRecipientListProcessorTest.php
@@ -45,12 +45,12 @@ public function testShouldProduceRecipientsMessagesAndAckOriginalMessage()
 
         $producerMock = $this->createProducerMock();
         $producerMock
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('send')
             ->with($this->identicalTo($fooRecipient->getDestination()), $this->identicalTo($fooRecipient->getMessage()))
         ;
         $producerMock
-            ->expects($this->at(1))
+            ->expects(self::once())
             ->method('send')
             ->with($this->identicalTo($barRecipient->getDestination()), $this->identicalTo($barRecipient->getMessage()))
         ;
diff --git a/Tests/Symfony/Client/ConsumeCommandTest.php b/Tests/Symfony/Client/ConsumeCommandTest.php
index fcc0995..4f1dfe2 100644
--- a/Tests/Symfony/Client/ConsumeCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeCommandTest.php
@@ -269,13 +269,13 @@ public function testShouldBindCustomExecuteConsumptionAndUseCustomClientDestinat
 
         $driver = $this->createDriverStub($routeCollection);
         $driver
-            ->expects($this->at(3))
+            ->expects(self::once())
             ->method('createQueue')
             ->with('default', true)
             ->willReturn($defaultQueue)
         ;
         $driver
-            ->expects($this->at(4))
+            ->expects(self::once())
             ->method('createQueue')
             ->with('custom', true)
             ->willReturn($customQueue)
@@ -283,17 +283,17 @@ public function testShouldBindCustomExecuteConsumptionAndUseCustomClientDestinat
 
         $consumer = $this->createQueueConsumerMock();
         $consumer
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('bind')
             ->with($this->identicalTo($defaultQueue), $this->identicalTo($processor))
         ;
         $consumer
-            ->expects($this->at(1))
+            ->expects(self::once())
             ->method('bind')
             ->with($this->identicalTo($customQueue), $this->identicalTo($processor))
         ;
         $consumer
-            ->expects($this->at(2))
+            ->expects(self::once())
             ->method('consume')
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
@@ -408,13 +408,13 @@ public function testShouldBindQueuesOnlyOnce()
 
         $driver = $this->createDriverStub($routeCollection);
         $driver
-            ->expects($this->at(3))
+            ->expects(self::once())
             ->method('createQueue')
             ->with('default', true)
             ->willReturn($defaultQueue)
         ;
         $driver
-            ->expects($this->at(4))
+            ->expects(self::once())
             ->method('createQueue', true)
             ->with('custom')
             ->willReturn($customQueue)
@@ -422,17 +422,17 @@ public function testShouldBindQueuesOnlyOnce()
 
         $consumer = $this->createQueueConsumerMock();
         $consumer
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('bind')
             ->with($this->identicalTo($defaultQueue), $this->identicalTo($processor))
         ;
         $consumer
-            ->expects($this->at(1))
+            ->expects(self::once())
             ->method('bind')
             ->with($this->identicalTo($customQueue), $this->identicalTo($processor))
         ;
         $consumer
-            ->expects($this->at(2))
+            ->expects(self::once())
             ->method('consume')
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
@@ -473,7 +473,7 @@ public function testShouldNotBindExternalRoutes()
             ->with($this->identicalTo($defaultQueue), $this->identicalTo($processor))
         ;
         $consumer
-            ->expects($this->at(1))
+            ->expects(self::once())
             ->method('consume')
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
@@ -513,19 +513,19 @@ public function testShouldSkipQueueConsumptionAndUseCustomClientDestinationName(
 
         $driver = $this->createDriverStub($routeCollection);
         $driver
-            ->expects($this->at(3))
+            ->expects(self::once())
             ->method('createQueue', true)
             ->with('default')
             ->willReturn($queue)
         ;
         $driver
-            ->expects($this->at(4))
+            ->expects(self::once())
             ->method('createQueue', true)
             ->with('fooQueue')
             ->willReturn($queue)
         ;
         $driver
-            ->expects($this->at(5))
+            ->expects(self::once())
             ->method('createQueue', true)
             ->with('ololoQueue')
             ->willReturn($queue)
diff --git a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
index c788d5e..083354c 100644
--- a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
@@ -163,17 +163,17 @@ public function testShouldExecuteConsumptionWithSeveralCustomQueues()
 
         $consumer = $this->createQueueConsumerMock();
         $consumer
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('bind')
             ->with('queue-name', $this->identicalTo($processor))
         ;
         $consumer
-            ->expects($this->at(1))
+            ->expects(self::once())
             ->method('bind')
             ->with('another-queue-name', $this->identicalTo($processor))
         ;
         $consumer
-            ->expects($this->at(2))
+            ->expects(self::once())
             ->method('consume')
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
@@ -205,17 +205,17 @@ public static function getSubscribedQueues()
 
         $consumer = $this->createQueueConsumerMock();
         $consumer
-            ->expects($this->at(0))
+            ->expects(self::once())
             ->method('bind')
             ->with('fooSubscribedQueues', $this->identicalTo($processor))
         ;
         $consumer
-            ->expects($this->at(1))
+            ->expects(self::once())
             ->method('bind')
             ->with('barSubscribedQueues', $this->identicalTo($processor))
         ;
         $consumer
-            ->expects($this->at(2))
+            ->expects(self::once())
             ->method('consume')
             ->with($this->isInstanceOf(ChainExtension::class))
         ;

From ce3d016547392156e46f72481bced51419ef5818 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Thu, 21 Jan 2021 16:37:57 +0200
Subject: [PATCH 290/337] =?UTF-8?q?Fix=20logger-related=20at()=20warnings?=
 =?UTF-8?q?=20=E2=80=94=20replace=20mocks=20with=20TestLogger?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../DelayRedeliveredMessageExtensionTest.php  | 35 +++-----
 .../Client/Driver/RabbitMqStompDriverTest.php | 89 +++++++++----------
 2 files changed, 54 insertions(+), 70 deletions(-)

diff --git a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
index 61a307a..a92dc8d 100644
--- a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
@@ -18,8 +18,8 @@
 use Interop\Queue\Queue;
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
-use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
+use Psr\Log\Test\TestLogger;
 
 class DelayRedeliveredMessageExtensionTest extends TestCase
 {
@@ -55,20 +55,7 @@ public function testShouldSendDelayedMessageAndRejectOriginalMessage()
             ->willReturn($delayedMessage)
         ;
 
-        $logger = $this->createLoggerMock();
-        $logger
-            ->expects(self::at(0))
-            ->method('debug')
-            ->with('[DelayRedeliveredMessageExtension] Send delayed message')
-        ;
-        $logger
-            ->expects(self::at(1))
-            ->method('debug')
-            ->with(
-                '[DelayRedeliveredMessageExtension] '.
-                'Reject redelivered original message by setting reject status to context.'
-            )
-        ;
+        $logger = new TestLogger();
 
         $messageReceived = new MessageReceived(
             $this->createContextMock(),
@@ -93,6 +80,16 @@ public function testShouldSendDelayedMessageAndRejectOriginalMessage()
         $this->assertEquals([
             'enqueue.redelivery_count' => 1,
         ], $delayedMessage->getProperties());
+
+        self::assertTrue(
+            $logger->hasDebugThatContains('[DelayRedeliveredMessageExtension] Send delayed message')
+        );
+        self::assertTrue(
+            $logger->hasDebugThatContains(
+                '[DelayRedeliveredMessageExtension] '.
+                'Reject redelivered original message by setting reject status to context.'
+            )
+        );
     }
 
     public function testShouldDoNothingIfMessageIsNotRedelivered()
@@ -184,14 +181,6 @@ private function createConsumerStub(?Queue $queue): Consumer
         return $consumerMock;
     }
 
-    /**
-     * @return MockObject
-     */
-    private function createLoggerMock(): LoggerInterface
-    {
-        return $this->createMock(LoggerInterface::class);
-    }
-
     private function createDriverSendResult(): DriverSendResult
     {
         return new DriverSendResult(
diff --git a/Tests/Client/Driver/RabbitMqStompDriverTest.php b/Tests/Client/Driver/RabbitMqStompDriverTest.php
index f59376b..79e8c33 100644
--- a/Tests/Client/Driver/RabbitMqStompDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqStompDriverTest.php
@@ -24,7 +24,7 @@
 use Interop\Queue\Queue as InteropQueue;
 use Interop\Queue\Topic as InteropTopic;
 use PHPUnit\Framework\TestCase;
-use Psr\Log\LoggerInterface;
+use Psr\Log\Test\TestLogger;
 
 class RabbitMqStompDriverTest extends TestCase
 {
@@ -281,14 +281,15 @@ public function testShouldNotSetupBrokerIfManagementPluginInstalledOptionIsNotEn
             $this->createManagementClientMock()
         );
 
-        $logger = $this->createLoggerMock();
-        $logger
-            ->expects($this->once())
-            ->method('debug')
-            ->with('[RabbitMqStompDriver] Could not setup broker. The option `management_plugin_installed` is not enabled. Please enable that option and install rabbit management plugin')
-        ;
+        $logger = new TestLogger();
 
         $driver->setupBroker($logger);
+
+        self::assertTrue(
+            $logger->hasDebugThatContains(
+                '[RabbitMqStompDriver] Could not setup broker. The option `management_plugin_installed` is not enabled. Please enable that option and install rabbit management plugin'
+            )
+        );
     }
 
     public function testShouldSetupBroker()
@@ -366,29 +367,30 @@ public function testShouldSetupBroker()
             $managementClient
         );
 
-        $logger = $this->createLoggerMock();
-        $logger
-            ->expects($this->at(0))
-            ->method('debug')
-            ->with('[RabbitMqStompDriver] Declare router exchange: aprefix.router')
-        ;
-        $logger
-            ->expects($this->at(1))
-            ->method('debug')
-            ->with('[RabbitMqStompDriver] Declare router queue: aprefix.default')
-        ;
-        $logger
-            ->expects($this->at(2))
-            ->method('debug')
-            ->with('[RabbitMqStompDriver] Bind router queue to exchange: aprefix.default -> aprefix.router')
-        ;
-        $logger
-            ->expects($this->at(3))
-            ->method('debug')
-            ->with('[RabbitMqStompDriver] Declare processor queue: aprefix.default')
-        ;
+        $logger = new TestLogger();
 
         $driver->setupBroker($logger);
+
+        self::assertTrue(
+            $logger->hasDebugThatContains(
+                '[RabbitMqStompDriver] Declare router exchange: aprefix.router'
+            )
+        );
+        self::assertTrue(
+            $logger->hasDebugThatContains(
+                '[RabbitMqStompDriver] Declare router queue: aprefix.default'
+            )
+        );
+        self::assertTrue(
+            $logger->hasDebugThatContains(
+                '[RabbitMqStompDriver] Bind router queue to exchange: aprefix.default -> aprefix.router'
+            )
+        );
+        self::assertTrue(
+            $logger->hasDebugThatContains(
+                '[RabbitMqStompDriver] Declare processor queue: aprefix.default'
+            )
+        );
     }
 
     public function testSetupBrokerShouldCreateDelayExchangeIfEnabled()
@@ -458,19 +460,20 @@ public function testSetupBrokerShouldCreateDelayExchangeIfEnabled()
             $managementClient
         );
 
-        $logger = $this->createLoggerMock();
-        $logger
-            ->expects($this->at(4))
-            ->method('debug')
-            ->with('[RabbitMqStompDriver] Declare delay exchange: aprefix.default.delayed')
-        ;
-        $logger
-            ->expects($this->at(5))
-            ->method('debug')
-            ->with('[RabbitMqStompDriver] Bind processor queue to delay exchange: aprefix.default -> aprefix.default.delayed')
-        ;
+        $logger = new TestLogger();
 
         $driver->setupBroker($logger);
+
+        self::assertTrue(
+            $logger->hasDebugThatContains(
+                '[RabbitMqStompDriver] Declare delay exchange: aprefix.default.delayed'
+            )
+        );
+        self::assertTrue(
+            $logger->hasDebugThatContains(
+                '[RabbitMqStompDriver] Bind processor queue to delay exchange: aprefix.default -> aprefix.default.delayed'
+            )
+        );
     }
 
     protected function createDriver(...$args): DriverInterface
@@ -584,12 +587,4 @@ private function createManagementClientMock(): StompManagementClient
     {
         return $this->createMock(StompManagementClient::class);
     }
-
-    /**
-     * @return \PHPUnit\Framework\MockObject\MockObject|LoggerInterface
-     */
-    private function createLoggerMock()
-    {
-        return $this->createMock(LoggerInterface::class);
-    }
 }

From b1cadcf2d6f42c62789447d77dcdf9e5749a8b68 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Thu, 21 Jan 2021 16:53:04 +0200
Subject: [PATCH 291/337] Fix renamed assertions and prophecy warnings

---
 .../DependencyInjection/BuildProcessorRegistryPassTest.php      | 2 +-
 .../DependencyInjection/BuildProcessorRegistryPassTest.php      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php
index 4d95ca0..8541f39 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php
@@ -136,7 +136,7 @@ private function assertLocatorServices(ContainerBuilder $container, $locatorId,
         $locatorId = (string) $locatorId;
 
         $this->assertTrue($container->hasDefinition($locatorId));
-        $this->assertRegExp('/\.?service_locator\..*?\.enqueue\./', $locatorId);
+        $this->assertMatchesRegularExpression('/\.?service_locator\..*?\.enqueue\./', $locatorId);
 
         $match = [];
         if (false == preg_match('/(\.?service_locator\..*?)\.enqueue\./', $locatorId, $match)) {
diff --git a/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php b/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
index 989d048..8d8edc5 100644
--- a/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
+++ b/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
@@ -199,7 +199,7 @@ private function assertLocatorServices(ContainerBuilder $container, $locatorId,
         $locatorId = (string) $locatorId;
 
         $this->assertTrue($container->hasDefinition($locatorId));
-        $this->assertRegExp('/\.?service_locator\..*?\.enqueue\./', $locatorId);
+        $this->assertMatchesRegularExpression('/\.?service_locator\..*?\.enqueue\./', $locatorId);
 
         $match = [];
         if (false == preg_match('/(\.?service_locator\..*?)\.enqueue\./', $locatorId, $match)) {

From c9411a8b0d2a0fd989e682df4654ee9530c5b53e Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Thu, 21 Jan 2021 16:24:09 +0200
Subject: [PATCH 292/337] Fix prophecy warning

---
 Tests/DoctrineConnectionFactoryFactoryTest.php | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Tests/DoctrineConnectionFactoryFactoryTest.php b/Tests/DoctrineConnectionFactoryFactoryTest.php
index 81bb009..14f7b10 100644
--- a/Tests/DoctrineConnectionFactoryFactoryTest.php
+++ b/Tests/DoctrineConnectionFactoryFactoryTest.php
@@ -9,9 +9,12 @@
 use Enqueue\Dbal\ManagerRegistryConnectionFactory;
 use Enqueue\Doctrine\DoctrineConnectionFactoryFactory;
 use PHPUnit\Framework\TestCase;
+use Prophecy\PhpUnit\ProphecyTrait;
 
 class DoctrineConnectionFactoryFactoryTest extends TestCase
 {
+    use ProphecyTrait;
+
     /**
      * @var ManagerRegistry|\Prophecy\Prophecy\ObjectProphecy
      */

From 9ebd66768027b3aa27ff3f2bbe382f913fe235a8 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Sun, 24 Jan 2021 21:33:39 +0200
Subject: [PATCH 293/337] Fix mocks and logger

---
 Tests/Client/ProducerSendCommandTest.php | 2 +-
 Tests/Client/ProducerSendEventTest.php   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Tests/Client/ProducerSendCommandTest.php b/Tests/Client/ProducerSendCommandTest.php
index 9532ba3..4434267 100644
--- a/Tests/Client/ProducerSendCommandTest.php
+++ b/Tests/Client/ProducerSendCommandTest.php
@@ -479,7 +479,7 @@ public function testShouldCallPostSendExtensionMethod()
 
         $extension
             ->expects(self::once())
-            ->method('onDriverPreSend')
+            ->method('onPostSend')
             ->willReturnCallback(function (PostSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
                 $this->assertSame($producer, $context->getProducer());
diff --git a/Tests/Client/ProducerSendEventTest.php b/Tests/Client/ProducerSendEventTest.php
index b482492..2a3bbcb 100644
--- a/Tests/Client/ProducerSendEventTest.php
+++ b/Tests/Client/ProducerSendEventTest.php
@@ -499,7 +499,7 @@ public function testShouldCallPostSendExtensionMethodWhenSendToApplicationRouter
 
         $extension
             ->expects(self::once())
-            ->method('onDriverPreSend')
+            ->method('onPostSend')
             ->willReturnCallback(function (PostSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
                 $this->assertSame($producer, $context->getProducer());

From a7c9fb3305fe71315864f52b5682b88aba9f3577 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Sun, 24 Jan 2021 22:24:25 +0200
Subject: [PATCH 294/337] =?UTF-8?q?Revert=20"Fix=20at()=20warnings=20for?=
 =?UTF-8?q?=20non-logger=20mocks=20=E2=80=94=20don't=20test=20for=20method?=
 =?UTF-8?q?=20call=20order"?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This reverts commit 140bc0104221a0c488f02b22c5c366b7038a17cd.

# Conflicts:
#	pkg/enqueue/Tests/Client/ProducerSendCommandTest.php
#	pkg/enqueue/Tests/Client/ProducerSendEventTest.php
---
 Tests/Client/Driver/AmqpDriverTest.php        | 23 ++++++++-------
 Tests/Client/Driver/FsDriverTest.php          |  8 +++---
 Tests/Client/Driver/GpsDriverTest.php         | 12 ++++----
 .../Client/Driver/RabbitMqStompDriverTest.php | 16 +++++------
 Tests/Client/Driver/RdKafkaDriverTest.php     |  6 ++--
 Tests/Client/Driver/SqsDriverTest.php         |  8 +++---
 Tests/Client/ProducerSendCommandTest.php      |  8 +++---
 Tests/Client/ProducerSendEventTest.php        | 12 ++++----
 Tests/Client/SpoolProducerTest.php            | 12 ++++----
 .../RouteRecipientListProcessorTest.php       |  4 +--
 Tests/Symfony/Client/ConsumeCommandTest.php   | 28 +++++++++----------
 .../ConfigurableConsumeCommandTest.php        | 12 ++++----
 12 files changed, 74 insertions(+), 75 deletions(-)

diff --git a/Tests/Client/Driver/AmqpDriverTest.php b/Tests/Client/Driver/AmqpDriverTest.php
index 08b22c7..3234c45 100644
--- a/Tests/Client/Driver/AmqpDriverTest.php
+++ b/Tests/Client/Driver/AmqpDriverTest.php
@@ -22,7 +22,6 @@
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\Queue as InteropQueue;
-use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 
 class AmqpDriverTest extends TestCase
@@ -191,54 +190,54 @@ public function testShouldSetupBroker()
         $context = $this->createContextMock();
         // setup router
         $context
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('createTopic')
             ->willReturn($routerTopic)
         ;
         $context
-            ->expects(self::once())
+            ->expects($this->at(1))
             ->method('declareTopic')
             ->with($this->identicalTo($routerTopic))
         ;
 
         $context
-            ->expects(self::once())
+            ->expects($this->at(2))
             ->method('createQueue')
             ->willReturn($routerQueue)
         ;
         $context
-            ->expects(self::once())
+            ->expects($this->at(3))
             ->method('declareQueue')
             ->with($this->identicalTo($routerQueue))
         ;
 
         $context
-            ->expects(self::once())
+            ->expects($this->at(4))
             ->method('bind')
             ->with($this->isInstanceOf(AmqpBind::class))
         ;
 
         // setup processor with default queue
         $context
-            ->expects(self::once())
+            ->expects($this->at(5))
             ->method('createQueue')
             ->with($this->getDefaultQueueTransportName())
             ->willReturn($processorWithDefaultQueue)
         ;
         $context
-            ->expects(self::once())
+            ->expects($this->at(6))
             ->method('declareQueue')
             ->with($this->identicalTo($processorWithDefaultQueue))
         ;
 
         $context
-            ->expects(self::once())
+            ->expects($this->at(7))
             ->method('createQueue')
             ->with($this->getCustomQueueTransportName())
             ->willReturn($processorWithCustomQueue)
         ;
         $context
-            ->expects(self::once())
+            ->expects($this->at(8))
             ->method('declareQueue')
             ->with($this->identicalTo($processorWithCustomQueue))
         ;
@@ -291,7 +290,7 @@ protected function createDriver(...$args): DriverInterface
     }
 
     /**
-     * @return AmqpContext|MockObject
+     * @return AmqpContext
      */
     protected function createContextMock(): Context
     {
@@ -299,7 +298,7 @@ protected function createContextMock(): Context
     }
 
     /**
-     * @return AmqpProducer|MockObject
+     * @return AmqpProducer
      */
     protected function createProducerMock(): InteropProducer
     {
diff --git a/Tests/Client/Driver/FsDriverTest.php b/Tests/Client/Driver/FsDriverTest.php
index a235b64..f1cd02f 100644
--- a/Tests/Client/Driver/FsDriverTest.php
+++ b/Tests/Client/Driver/FsDriverTest.php
@@ -44,23 +44,23 @@ public function testShouldSetupBroker()
         $context = $this->createContextMock();
         // setup router
         $context
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('createQueue')
             ->willReturn($routerQueue)
         ;
         $context
-            ->expects(self::once())
+            ->expects($this->at(1))
             ->method('declareDestination')
             ->with($this->identicalTo($routerQueue))
         ;
         // setup processor queue
         $context
-            ->expects(self::once())
+            ->expects($this->at(2))
             ->method('createQueue')
             ->willReturn($processorQueue)
         ;
         $context
-            ->expects(self::once())
+            ->expects($this->at(3))
             ->method('declareDestination')
             ->with($this->identicalTo($processorQueue))
         ;
diff --git a/Tests/Client/Driver/GpsDriverTest.php b/Tests/Client/Driver/GpsDriverTest.php
index 37a97be..c0cac04 100644
--- a/Tests/Client/Driver/GpsDriverTest.php
+++ b/Tests/Client/Driver/GpsDriverTest.php
@@ -46,35 +46,35 @@ public function testShouldSetupBroker()
         $context = $this->createContextMock();
         // setup router
         $context
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('createTopic')
             ->willReturn($routerTopic)
         ;
         $context
-            ->expects(self::once())
+            ->expects($this->at(1))
             ->method('createQueue')
             ->willReturn($routerQueue)
         ;
         $context
-            ->expects(self::once())
+            ->expects($this->at(2))
             ->method('subscribe')
             ->with($this->identicalTo($routerTopic), $this->identicalTo($routerQueue))
         ;
         $context
-            ->expects(self::once())
+            ->expects($this->at(3))
             ->method('createQueue')
             ->with($this->getDefaultQueueTransportName())
             ->willReturn($processorQueue)
         ;
         // setup processor queue
         $context
-            ->expects(self::once())
+            ->expects($this->at(4))
             ->method('createTopic')
             ->with($this->getDefaultQueueTransportName())
             ->willReturn($processorTopic)
         ;
         $context
-            ->expects(self::once())
+            ->expects($this->at(5))
             ->method('subscribe')
             ->with($this->identicalTo($processorTopic), $this->identicalTo($processorQueue))
         ;
diff --git a/Tests/Client/Driver/RabbitMqStompDriverTest.php b/Tests/Client/Driver/RabbitMqStompDriverTest.php
index 79e8c33..fbcb686 100644
--- a/Tests/Client/Driver/RabbitMqStompDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqStompDriverTest.php
@@ -196,12 +196,12 @@ public function shouldSendMessageToDelayExchangeIfDelaySet()
 
         $producer = $this->createProducerMock();
         $producer
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('setDeliveryDelay')
             ->with(10000)
         ;
         $producer
-            ->expects(self::once())
+            ->expects($this->at(1))
             ->method('setDeliveryDelay')
             ->with(null)
         ;
@@ -300,7 +300,7 @@ public function testShouldSetupBroker()
 
         $managementClient = $this->createManagementClientMock();
         $managementClient
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('declareExchange')
             ->with('aprefix.router', [
                 'type' => 'fanout',
@@ -309,7 +309,7 @@ public function testShouldSetupBroker()
             ])
         ;
         $managementClient
-            ->expects(self::once())
+            ->expects($this->at(1))
             ->method('declareQueue')
             ->with('aprefix.default', [
                 'durable' => true,
@@ -320,12 +320,12 @@ public function testShouldSetupBroker()
             ])
         ;
         $managementClient
-            ->expects(self::once())
+            ->expects($this->at(2))
             ->method('bind')
             ->with('aprefix.router', 'aprefix.default', 'aprefix.default')
         ;
         $managementClient
-            ->expects(self::once())
+            ->expects($this->at(3))
             ->method('declareQueue')
             ->with('aprefix.default', [
                 'durable' => true,
@@ -401,7 +401,7 @@ public function testSetupBrokerShouldCreateDelayExchangeIfEnabled()
 
         $managementClient = $this->createManagementClientMock();
         $managementClient
-            ->expects(self::once())
+            ->expects($this->at(4))
             ->method('declareExchange')
             ->with('aprefix.default.delayed', [
                 'type' => 'x-delayed-message',
@@ -413,7 +413,7 @@ public function testSetupBrokerShouldCreateDelayExchangeIfEnabled()
             ])
         ;
         $managementClient
-            ->expects(self::once())
+            ->expects($this->at(5))
             ->method('bind')
             ->with('aprefix.default.delayed', 'aprefix.default', 'aprefix.default')
         ;
diff --git a/Tests/Client/Driver/RdKafkaDriverTest.php b/Tests/Client/Driver/RdKafkaDriverTest.php
index 16d1da9..659590b 100644
--- a/Tests/Client/Driver/RdKafkaDriverTest.php
+++ b/Tests/Client/Driver/RdKafkaDriverTest.php
@@ -47,17 +47,17 @@ public function testShouldSetupBroker()
         $context = $this->createContextMock();
 
         $context
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('createQueue')
             ->willReturn($routerTopic)
         ;
         $context
-            ->expects(self::once())
+            ->expects($this->at(1))
             ->method('createQueue')
             ->willReturn($routerQueue)
         ;
         $context
-            ->expects(self::once())
+            ->expects($this->at(2))
             ->method('createQueue')
             ->willReturn($processorTopic)
         ;
diff --git a/Tests/Client/Driver/SqsDriverTest.php b/Tests/Client/Driver/SqsDriverTest.php
index fb4af53..2e3005e 100644
--- a/Tests/Client/Driver/SqsDriverTest.php
+++ b/Tests/Client/Driver/SqsDriverTest.php
@@ -42,25 +42,25 @@ public function testShouldSetupBroker()
         $context = $this->createContextMock();
         // setup router
         $context
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('createQueue')
             ->with('aprefix_dot_default')
             ->willReturn($routerQueue)
         ;
         $context
-            ->expects(self::once())
+            ->expects($this->at(1))
             ->method('declareQueue')
             ->with($this->identicalTo($routerQueue))
         ;
         // setup processor queue
         $context
-            ->expects(self::once())
+            ->expects($this->at(2))
             ->method('createQueue')
             ->with('aprefix_dot_default')
             ->willReturn($processorQueue)
         ;
         $context
-            ->expects(self::once())
+            ->expects($this->at(3))
             ->method('declareQueue')
             ->with($this->identicalTo($processorQueue))
         ;
diff --git a/Tests/Client/ProducerSendCommandTest.php b/Tests/Client/ProducerSendCommandTest.php
index 4434267..9500e9d 100644
--- a/Tests/Client/ProducerSendCommandTest.php
+++ b/Tests/Client/ProducerSendCommandTest.php
@@ -370,7 +370,7 @@ public function testShouldCallPreSendCommandExtensionMethodWhenSendToBus()
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('onPreSendCommand')
             ->willReturnCallback(function (PreSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
@@ -408,7 +408,7 @@ public function testShouldCallPreSendCommandExtensionMethodWhenSendToApplication
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('onPreSendCommand')
             ->willReturnCallback(function (PreSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
@@ -446,7 +446,7 @@ public function testShouldCallPreDriverSendExtensionMethod()
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('onDriverPreSend')
             ->willReturnCallback(function (DriverPreSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
@@ -478,7 +478,7 @@ public function testShouldCallPostSendExtensionMethod()
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('onPostSend')
             ->willReturnCallback(function (PostSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
diff --git a/Tests/Client/ProducerSendEventTest.php b/Tests/Client/ProducerSendEventTest.php
index 2a3bbcb..c92b495 100644
--- a/Tests/Client/ProducerSendEventTest.php
+++ b/Tests/Client/ProducerSendEventTest.php
@@ -326,7 +326,7 @@ public function testShouldCallPreSendEventExtensionMethodWhenSendToBus()
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('onPreSendEvent')
             ->willReturnCallback(function (PreSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
@@ -364,7 +364,7 @@ public function testShouldCallPreSendEventExtensionMethodWhenSendToApplicationRo
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('onPreSendEvent')
             ->willReturnCallback(function (PreSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
@@ -402,7 +402,7 @@ public function testShouldCallPreDriverSendExtensionMethodWhenSendToMessageBus()
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('onDriverPreSend')
             ->willReturnCallback(function (DriverPreSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
@@ -434,7 +434,7 @@ public function testShouldCallPreDriverSendExtensionMethodWhenSendToApplicationR
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('onDriverPreSend')
             ->willReturnCallback(function (DriverPreSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
@@ -466,7 +466,7 @@ public function testShouldCallPostSendExtensionMethodWhenSendToMessageBus()
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('onPostSend')
             ->willReturnCallback(function (PostSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
@@ -498,7 +498,7 @@ public function testShouldCallPostSendExtensionMethodWhenSendToApplicationRouter
         $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension);
 
         $extension
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('onPostSend')
             ->willReturnCallback(function (PostSend $context) use ($message, $producer, $driver) {
                 $this->assertSame($message, $context->getMessage());
diff --git a/Tests/Client/SpoolProducerTest.php b/Tests/Client/SpoolProducerTest.php
index df9e728..024fd6d 100644
--- a/Tests/Client/SpoolProducerTest.php
+++ b/Tests/Client/SpoolProducerTest.php
@@ -68,17 +68,17 @@ public function testShouldSendQueuedEventMessagesOnFlush()
 
         $realProducer = $this->createProducerMock();
         $realProducer
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('sendEvent')
             ->with('foo_topic', 'first')
         ;
         $realProducer
-            ->expects(self::once())
+            ->expects($this->at(1))
             ->method('sendEvent')
             ->with('bar_topic', ['second'])
         ;
         $realProducer
-            ->expects(self::once())
+            ->expects($this->at(2))
             ->method('sendEvent')
             ->with('baz_topic', $this->identicalTo($message))
         ;
@@ -103,17 +103,17 @@ public function testShouldSendQueuedCommandMessagesOnFlush()
 
         $realProducer = $this->createProducerMock();
         $realProducer
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('sendCommand')
             ->with('foo_command', 'first')
         ;
         $realProducer
-            ->expects(self::once())
+            ->expects($this->at(1))
             ->method('sendCommand')
             ->with('bar_command', ['second'])
         ;
         $realProducer
-            ->expects(self::once())
+            ->expects($this->at(2))
             ->method('sendCommand')
             ->with('baz_command', $this->identicalTo($message))
         ;
diff --git a/Tests/Router/RouteRecipientListProcessorTest.php b/Tests/Router/RouteRecipientListProcessorTest.php
index dfc8206..8a2e089 100644
--- a/Tests/Router/RouteRecipientListProcessorTest.php
+++ b/Tests/Router/RouteRecipientListProcessorTest.php
@@ -45,12 +45,12 @@ public function testShouldProduceRecipientsMessagesAndAckOriginalMessage()
 
         $producerMock = $this->createProducerMock();
         $producerMock
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('send')
             ->with($this->identicalTo($fooRecipient->getDestination()), $this->identicalTo($fooRecipient->getMessage()))
         ;
         $producerMock
-            ->expects(self::once())
+            ->expects($this->at(1))
             ->method('send')
             ->with($this->identicalTo($barRecipient->getDestination()), $this->identicalTo($barRecipient->getMessage()))
         ;
diff --git a/Tests/Symfony/Client/ConsumeCommandTest.php b/Tests/Symfony/Client/ConsumeCommandTest.php
index 4f1dfe2..fcc0995 100644
--- a/Tests/Symfony/Client/ConsumeCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeCommandTest.php
@@ -269,13 +269,13 @@ public function testShouldBindCustomExecuteConsumptionAndUseCustomClientDestinat
 
         $driver = $this->createDriverStub($routeCollection);
         $driver
-            ->expects(self::once())
+            ->expects($this->at(3))
             ->method('createQueue')
             ->with('default', true)
             ->willReturn($defaultQueue)
         ;
         $driver
-            ->expects(self::once())
+            ->expects($this->at(4))
             ->method('createQueue')
             ->with('custom', true)
             ->willReturn($customQueue)
@@ -283,17 +283,17 @@ public function testShouldBindCustomExecuteConsumptionAndUseCustomClientDestinat
 
         $consumer = $this->createQueueConsumerMock();
         $consumer
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('bind')
             ->with($this->identicalTo($defaultQueue), $this->identicalTo($processor))
         ;
         $consumer
-            ->expects(self::once())
+            ->expects($this->at(1))
             ->method('bind')
             ->with($this->identicalTo($customQueue), $this->identicalTo($processor))
         ;
         $consumer
-            ->expects(self::once())
+            ->expects($this->at(2))
             ->method('consume')
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
@@ -408,13 +408,13 @@ public function testShouldBindQueuesOnlyOnce()
 
         $driver = $this->createDriverStub($routeCollection);
         $driver
-            ->expects(self::once())
+            ->expects($this->at(3))
             ->method('createQueue')
             ->with('default', true)
             ->willReturn($defaultQueue)
         ;
         $driver
-            ->expects(self::once())
+            ->expects($this->at(4))
             ->method('createQueue', true)
             ->with('custom')
             ->willReturn($customQueue)
@@ -422,17 +422,17 @@ public function testShouldBindQueuesOnlyOnce()
 
         $consumer = $this->createQueueConsumerMock();
         $consumer
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('bind')
             ->with($this->identicalTo($defaultQueue), $this->identicalTo($processor))
         ;
         $consumer
-            ->expects(self::once())
+            ->expects($this->at(1))
             ->method('bind')
             ->with($this->identicalTo($customQueue), $this->identicalTo($processor))
         ;
         $consumer
-            ->expects(self::once())
+            ->expects($this->at(2))
             ->method('consume')
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
@@ -473,7 +473,7 @@ public function testShouldNotBindExternalRoutes()
             ->with($this->identicalTo($defaultQueue), $this->identicalTo($processor))
         ;
         $consumer
-            ->expects(self::once())
+            ->expects($this->at(1))
             ->method('consume')
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
@@ -513,19 +513,19 @@ public function testShouldSkipQueueConsumptionAndUseCustomClientDestinationName(
 
         $driver = $this->createDriverStub($routeCollection);
         $driver
-            ->expects(self::once())
+            ->expects($this->at(3))
             ->method('createQueue', true)
             ->with('default')
             ->willReturn($queue)
         ;
         $driver
-            ->expects(self::once())
+            ->expects($this->at(4))
             ->method('createQueue', true)
             ->with('fooQueue')
             ->willReturn($queue)
         ;
         $driver
-            ->expects(self::once())
+            ->expects($this->at(5))
             ->method('createQueue', true)
             ->with('ololoQueue')
             ->willReturn($queue)
diff --git a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
index 083354c..c788d5e 100644
--- a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
@@ -163,17 +163,17 @@ public function testShouldExecuteConsumptionWithSeveralCustomQueues()
 
         $consumer = $this->createQueueConsumerMock();
         $consumer
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('bind')
             ->with('queue-name', $this->identicalTo($processor))
         ;
         $consumer
-            ->expects(self::once())
+            ->expects($this->at(1))
             ->method('bind')
             ->with('another-queue-name', $this->identicalTo($processor))
         ;
         $consumer
-            ->expects(self::once())
+            ->expects($this->at(2))
             ->method('consume')
             ->with($this->isInstanceOf(ChainExtension::class))
         ;
@@ -205,17 +205,17 @@ public static function getSubscribedQueues()
 
         $consumer = $this->createQueueConsumerMock();
         $consumer
-            ->expects(self::once())
+            ->expects($this->at(0))
             ->method('bind')
             ->with('fooSubscribedQueues', $this->identicalTo($processor))
         ;
         $consumer
-            ->expects(self::once())
+            ->expects($this->at(1))
             ->method('bind')
             ->with('barSubscribedQueues', $this->identicalTo($processor))
         ;
         $consumer
-            ->expects(self::once())
+            ->expects($this->at(2))
             ->method('consume')
             ->with($this->isInstanceOf(ChainExtension::class))
         ;

From d6195a9a692b55e7376d062bc3951917436f3ee6 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Mon, 25 Jan 2021 09:46:24 +0200
Subject: [PATCH 295/337] Fix CS

---
 Tests/Client/Driver/AmqpDriverTest.php           | 3 ---
 Tests/Client/Driver/RabbitMqDriverTest.php       | 3 ---
 Tests/Consumption/QueueConsumerTest.php          | 2 +-
 Tests/Symfony/Client/ConsumeCommandTest.php      | 2 +-
 Tests/Symfony/Consumption/ConsumeCommandTest.php | 2 +-
 5 files changed, 3 insertions(+), 9 deletions(-)

diff --git a/Tests/Client/Driver/AmqpDriverTest.php b/Tests/Client/Driver/AmqpDriverTest.php
index 3234c45..2cfb170 100644
--- a/Tests/Client/Driver/AmqpDriverTest.php
+++ b/Tests/Client/Driver/AmqpDriverTest.php
@@ -313,9 +313,6 @@ protected function createQueue(string $name): InteropQueue
         return new AmqpQueue($name);
     }
 
-    /**
-     * @return AmqpTopic
-     */
     protected function createTopic(string $name): AmqpTopic
     {
         return new AmqpTopic($name);
diff --git a/Tests/Client/Driver/RabbitMqDriverTest.php b/Tests/Client/Driver/RabbitMqDriverTest.php
index 6b61465..b209d85 100644
--- a/Tests/Client/Driver/RabbitMqDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqDriverTest.php
@@ -92,9 +92,6 @@ protected function createQueue(string $name): InteropQueue
         return new AmqpQueue($name);
     }
 
-    /**
-     * @return AmqpTopic
-     */
     protected function createTopic(string $name): AmqpTopic
     {
         return new AmqpTopic($name);
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index a087926..ecf7f9a 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -1537,7 +1537,7 @@ private function createExtension()
      */
     private function createConsumerStub($queue = null): Consumer
     {
-        if ($queue === null) {
+        if (null === $queue) {
             $queue = 'queue';
         }
         if (is_string($queue)) {
diff --git a/Tests/Symfony/Client/ConsumeCommandTest.php b/Tests/Symfony/Client/ConsumeCommandTest.php
index fcc0995..1d94e6e 100644
--- a/Tests/Symfony/Client/ConsumeCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeCommandTest.php
@@ -677,7 +677,7 @@ private function createExtension()
      */
     private function createConsumerStub($queue = null): Consumer
     {
-        if ($queue === null) {
+        if (null === $queue) {
             $queue = 'queue';
         }
         if (is_string($queue)) {
diff --git a/Tests/Symfony/Consumption/ConsumeCommandTest.php b/Tests/Symfony/Consumption/ConsumeCommandTest.php
index cfcfd52..3483971 100644
--- a/Tests/Symfony/Consumption/ConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConsumeCommandTest.php
@@ -221,7 +221,7 @@ private function createExtension()
      */
     private function createConsumerStub($queue = null): Consumer
     {
-        if ($queue === null) {
+        if (null === $queue) {
             $queue = 'queue';
         }
         if (is_string($queue)) {

From 811fa963c84f012e7ccca33d935a710ee788406e Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Mon, 25 Jan 2021 09:51:30 +0200
Subject: [PATCH 296/337] Update PHP and PHPUnit across all packages; fix
 ext-rdkafka requirement

---
 composer.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/composer.json b/composer.json
index ed96030..e8c3f3a 100644
--- a/composer.json
+++ b/composer.json
@@ -6,7 +6,7 @@
     "homepage": "https://enqueue.forma-pro.com/",
     "license": "MIT",
     "require": {
-        "php": "^7.1.3",
+        "php": "^7.3",
         "queue-interop/amqp-interop": "^0.8",
         "queue-interop/queue-interop": "^0.8",
         "enqueue/null": "^0.10",
@@ -16,7 +16,7 @@
         "psr/container": "^1"
     },
     "require-dev": {
-        "phpunit/phpunit": "~7.5",
+        "phpunit/phpunit": "^9.5",
         "symfony/console": "^4.3|^5",
         "symfony/dependency-injection": "^4.3|^5",
         "symfony/config": "^4.3|^5",

From 30c314301f353557f8984906ff6d0738dfb86150 Mon Sep 17 00:00:00 2001
From: florian <florian@gensdeconfiance.com>
Date: Tue, 2 Feb 2021 15:53:52 +0100
Subject: [PATCH 297/337] add sns driver + use profile to establish connection

---
 Resources.php | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/Resources.php b/Resources.php
index 75884ba..713450f 100644
--- a/Resources.php
+++ b/Resources.php
@@ -14,6 +14,7 @@
 use Enqueue\Pheanstalk\PheanstalkConnectionFactory;
 use Enqueue\RdKafka\RdKafkaConnectionFactory;
 use Enqueue\Redis\RedisConnectionFactory;
+use Enqueue\Sns\SnsConnectionFactory;
 use Enqueue\SnsQs\SnsQsConnectionFactory;
 use Enqueue\Sqs\SqsConnectionFactory;
 use Enqueue\Stomp\StompConnectionFactory;
@@ -42,7 +43,7 @@ public static function getAvailableConnections(): array
 
         $availableMap = [];
         foreach ($map as $connectionClass => $item) {
-            if (class_exists($connectionClass)) {
+            if (\class_exists($connectionClass)) {
                 $availableMap[$connectionClass] = $item;
             }
         }
@@ -156,6 +157,10 @@ public static function getKnownConnections(): array
                 'schemes' => ['sqs'],
                 'supportedSchemeExtensions' => [],
                 'package' => 'enqueue/sqs', ];
+            $map[SnsConnectionFactory::class] = [
+                'schemes' => ['sns'],
+                'supportedSchemeExtensions' => [],
+                'package' => 'enqueue/sns', ];
             $map[SnsQsConnectionFactory::class] = [
                 'schemes' => ['snsqs'],
                 'supportedSchemeExtensions' => [],
@@ -183,9 +188,9 @@ public static function getKnownConnections(): array
 
     public static function addConnection(string $connectionFactoryClass, array $schemes, array $extensions, string $package): void
     {
-        if (class_exists($connectionFactoryClass)) {
+        if (\class_exists($connectionFactoryClass)) {
             if (false == (new \ReflectionClass($connectionFactoryClass))->implementsInterface(ConnectionFactory::class)) {
-                throw new \InvalidArgumentException(sprintf('The connection factory class "%s" must implement "%s" interface.', $connectionFactoryClass, ConnectionFactory::class));
+                throw new \InvalidArgumentException(\sprintf('The connection factory class "%s" must implement "%s" interface.', $connectionFactoryClass, ConnectionFactory::class));
             }
         }
 

From 27811a0d6ce48463c32d95967ffa285c355f8156 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Tue, 9 Feb 2021 02:14:42 +0200
Subject: [PATCH 298/337] Fix package CI

- Add push to master trigger;
- Replace the clunky cache + composer install steps with an Action;
- Replace Travis with GH WF in packages;
- Add the missing CI to amqp-tools;
- Replace the shields.
---
 .github/workflows/ci.yml | 30 ++++++++++++++++++++++++++++++
 .travis.yml              | 27 ---------------------------
 README.md                |  2 +-
 3 files changed, 31 insertions(+), 28 deletions(-)
 create mode 100644 .github/workflows/ci.yml
 delete mode 100644 .travis.yml

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..390f707
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,30 @@
+name: CI
+on:
+  pull_request:
+  push:
+    branches:
+      - master
+jobs:
+  tests:
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        php: ['7.3', '7.4']
+
+    name: PHP ${{ matrix.php }} tests
+
+    steps:
+      - uses: actions/checkout@v2
+
+      - uses: shivammathur/setup-php@v2
+        with:
+          php-version: ${{ matrix.php }}
+          coverage: none
+          extensions: mongodb
+
+      - run: php Tests/fix_composer_json.php
+
+      - uses: "ramsey/composer-install@v1"
+
+      - run: vendor/bin/phpunit --exclude-group=functional
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 2c830d0..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-sudo: false
-
-git:
-    depth: 10
-
-language: php
-
-php:
-    - '7.1'
-
-services:
-  - mongodb
-
-before_install:
-    - echo "extension = mongodb.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
-
-cache:
-    directories:
-        - $HOME/.composer/cache
-
-install:
-    - php Tests/fix_composer_json.php
-    - composer self-update
-    - composer install
-
-script:
-    - vendor/bin/phpunit --exclude-group=functional
diff --git a/README.md b/README.md
index 2e5aa5a..f617157 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ Enqueue is an MIT-licensed open source project with its ongoing development made
 # Message Queue.
 
 [![Gitter](https://badges.gitter.im/php-enqueue/Lobby.svg)](https://gitter.im/php-enqueue/Lobby)
-[![Build Status](https://travis-ci.org/php-enqueue/enqueue.png?branch=master)](https://travis-ci.org/php-enqueue/enqueue)
+[![Build Status](https://img.shields.io/github/workflow/status/php-enqueue/enqueue/CI)](https://github.com/php-enqueue/enqueue/actions?query=workflow%3ACI)
 [![Total Downloads](https://poser.pugx.org/enqueue/enqueue/d/total.png)](https://packagist.org/packages/enqueue/enqueue)
 [![Latest Stable Version](https://poser.pugx.org/enqueue/enqueue/version.png)](https://packagist.org/packages/enqueue/enqueue)
 

From de0e090a9b7d4d7075edcbe600ecaeb40730657a Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Mon, 25 Jan 2021 10:26:59 +0200
Subject: [PATCH 299/337] Allow php8 in all composer.json files

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index e8c3f3a..3c525f5 100644
--- a/composer.json
+++ b/composer.json
@@ -6,7 +6,7 @@
     "homepage": "https://enqueue.forma-pro.com/",
     "license": "MIT",
     "require": {
-        "php": "^7.3",
+        "php": "^7.3|^8.0",
         "queue-interop/amqp-interop": "^0.8",
         "queue-interop/queue-interop": "^0.8",
         "enqueue/null": "^0.10",

From 411d3f54bbcaeb568eaba0cfc122906372764240 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Mon, 25 Jan 2021 11:53:46 +0200
Subject: [PATCH 300/337] Fix exception message variations between versions

---
 Tests/Symfony/ContainerProcessorRegistryTest.php | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Tests/Symfony/ContainerProcessorRegistryTest.php b/Tests/Symfony/ContainerProcessorRegistryTest.php
index bf6e3cd..d1c4186 100644
--- a/Tests/Symfony/ContainerProcessorRegistryTest.php
+++ b/Tests/Symfony/ContainerProcessorRegistryTest.php
@@ -69,7 +69,11 @@ public function testThrowErrorIfServiceDoesNotImplementProcessorReturnType()
         $registry = new ContainerProcessorRegistry($containerMock);
 
         $this->expectException(\TypeError::class);
-        $this->expectExceptionMessage('Return value of Enqueue\Symfony\ContainerProcessorRegistry::get() must implement interface Interop\Queue\Processor, instance of stdClass returned');
+        // Exception messages vary slightly between versions
+        $this->expectExceptionMessageMatches(
+            '/Return value of Enqueue\\Symfony\\ContainerProcessorRegistry::get\(\) .+ Interop\\Queue\\Processor, .?stdClass returned/'
+        );
+
         $registry->get('processor-name');
     }
 

From ebd00e66fc6f27c588e92b20935ef389127f5b35 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Mon, 25 Jan 2021 11:55:58 +0200
Subject: [PATCH 301/337] Derp

---
 Tests/Symfony/ContainerProcessorRegistryTest.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/Symfony/ContainerProcessorRegistryTest.php b/Tests/Symfony/ContainerProcessorRegistryTest.php
index d1c4186..9b90d33 100644
--- a/Tests/Symfony/ContainerProcessorRegistryTest.php
+++ b/Tests/Symfony/ContainerProcessorRegistryTest.php
@@ -71,7 +71,7 @@ public function testThrowErrorIfServiceDoesNotImplementProcessorReturnType()
         $this->expectException(\TypeError::class);
         // Exception messages vary slightly between versions
         $this->expectExceptionMessageMatches(
-            '/Return value of Enqueue\\Symfony\\ContainerProcessorRegistry::get\(\) .+ Interop\\Queue\\Processor, .?stdClass returned/'
+            '/Return value of Enqueue\\Symfony\\ContainerProcessorRegistry::get\(\) .+ Interop\\Queue\\Processor, .*stdClass returned/'
         );
 
         $registry->get('processor-name');

From 3d0d6872973cc73455b8751a74f4c3a520f3a8db Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Mon, 25 Jan 2021 12:02:46 +0200
Subject: [PATCH 302/337] Derp

---
 Tests/Symfony/ContainerProcessorRegistryTest.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/Symfony/ContainerProcessorRegistryTest.php b/Tests/Symfony/ContainerProcessorRegistryTest.php
index 9b90d33..2169e27 100644
--- a/Tests/Symfony/ContainerProcessorRegistryTest.php
+++ b/Tests/Symfony/ContainerProcessorRegistryTest.php
@@ -71,7 +71,7 @@ public function testThrowErrorIfServiceDoesNotImplementProcessorReturnType()
         $this->expectException(\TypeError::class);
         // Exception messages vary slightly between versions
         $this->expectExceptionMessageMatches(
-            '/Return value of Enqueue\\Symfony\\ContainerProcessorRegistry::get\(\) .+ Interop\\Queue\\Processor, .*stdClass returned/'
+            '/Enqueue\\Symfony\\ContainerProcessorRegistry::get\(\) .+ Interop\\Queue\\Processor, .*stdClass returned/'
         );
 
         $registry->get('processor-name');

From 612b379cf050a64f2dee15954246c256743c445c Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Mon, 25 Jan 2021 12:09:46 +0200
Subject: [PATCH 303/337] =?UTF-8?q?=F0=9F=A4=94?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 Tests/Symfony/ContainerProcessorRegistryTest.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/Symfony/ContainerProcessorRegistryTest.php b/Tests/Symfony/ContainerProcessorRegistryTest.php
index 2169e27..bf79571 100644
--- a/Tests/Symfony/ContainerProcessorRegistryTest.php
+++ b/Tests/Symfony/ContainerProcessorRegistryTest.php
@@ -71,7 +71,7 @@ public function testThrowErrorIfServiceDoesNotImplementProcessorReturnType()
         $this->expectException(\TypeError::class);
         // Exception messages vary slightly between versions
         $this->expectExceptionMessageMatches(
-            '/Enqueue\\Symfony\\ContainerProcessorRegistry::get\(\) .+ Interop\\Queue\\Processor, .*stdClass returned/'
+            '/Enqueue\\Symfony\\ContainerProcessorRegistry\:\:get\(\).+ Interop\\Queue\\Processor, .*stdClass returned/'
         );
 
         $registry->get('processor-name');

From d60338adb90d6acb1f176c4a019e3bc7829f86a6 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Mon, 25 Jan 2021 12:14:59 +0200
Subject: [PATCH 304/337] Ok got the regex

---
 Tests/Symfony/ContainerProcessorRegistryTest.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/Symfony/ContainerProcessorRegistryTest.php b/Tests/Symfony/ContainerProcessorRegistryTest.php
index bf79571..e0dcf2e 100644
--- a/Tests/Symfony/ContainerProcessorRegistryTest.php
+++ b/Tests/Symfony/ContainerProcessorRegistryTest.php
@@ -71,7 +71,7 @@ public function testThrowErrorIfServiceDoesNotImplementProcessorReturnType()
         $this->expectException(\TypeError::class);
         // Exception messages vary slightly between versions
         $this->expectExceptionMessageMatches(
-            '/Enqueue\\Symfony\\ContainerProcessorRegistry\:\:get\(\).+ Interop\\Queue\\Processor, .*stdClass returned/'
+            '/Enqueue\\\\Symfony\\\\ContainerProcessorRegistry::get\(\).+ Interop\\\\Queue\\\\Processor,.*stdClass returned/'
         );
 
         $registry->get('processor-name');

From e289ceb082671b61d1ad1471a16d372b80095599 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Tue, 9 Feb 2021 14:01:28 +0200
Subject: [PATCH 305/337] Add php 8 to packages' build matrices; normalize
 composer reqs

---
 .github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 390f707..b394958 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -10,7 +10,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        php: ['7.3', '7.4']
+        php: ['7.3', '7.4', '8.0']
 
     name: PHP ${{ matrix.php }} tests
 

From 8c84d1fb04b1c091e9b56bebf6be377b0cb481ba Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Thu, 11 Feb 2021 03:45:55 +0200
Subject: [PATCH 306/337] Bump some minimal deps

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 3c525f5..09822cf 100644
--- a/composer.json
+++ b/composer.json
@@ -11,7 +11,7 @@
         "queue-interop/queue-interop": "^0.8",
         "enqueue/null": "^0.10",
         "enqueue/dsn": "^0.10",
-        "ramsey/uuid": "^2|^3.5|^4",
+        "ramsey/uuid": "^3.5|^4",
         "psr/log": "^1",
         "psr/container": "^1"
     },

From 4bc475f9ebd447951eecc45063a135c089f3f8a0 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Wed, 17 Feb 2021 20:57:43 +0200
Subject: [PATCH 307/337] Bump amqp-interop and queue-interop

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 09822cf..8ff51a4 100644
--- a/composer.json
+++ b/composer.json
@@ -7,7 +7,7 @@
     "license": "MIT",
     "require": {
         "php": "^7.3|^8.0",
-        "queue-interop/amqp-interop": "^0.8",
+        "queue-interop/amqp-interop": "^0.8.2",
         "queue-interop/queue-interop": "^0.8",
         "enqueue/null": "^0.10",
         "enqueue/dsn": "^0.10",

From 4db0365834512304ba463f399a6f126cba05c34f Mon Sep 17 00:00:00 2001
From: Witold Wasiczko <witold@wasiczko.pl>
Date: Wed, 8 Sep 2021 19:09:48 +0200
Subject: [PATCH 308/337] Allow psr/log v2 and v3

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 8ff51a4..a36d5f7 100644
--- a/composer.json
+++ b/composer.json
@@ -12,7 +12,7 @@
         "enqueue/null": "^0.10",
         "enqueue/dsn": "^0.10",
         "ramsey/uuid": "^3.5|^4",
-        "psr/log": "^1",
+        "psr/log": "^1.0 || ^2.0 || ^3.0",
         "psr/container": "^1"
     },
     "require-dev": {

From 406190fc7b3458777e497512f3c3addbd4d13f31 Mon Sep 17 00:00:00 2001
From: ADmad <ADmad@users.noreply.github.com>
Date: Wed, 20 Oct 2021 17:50:04 +0000
Subject: [PATCH 309/337] Allow psr/container v2.

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index a36d5f7..fdb3657 100644
--- a/composer.json
+++ b/composer.json
@@ -13,7 +13,7 @@
         "enqueue/dsn": "^0.10",
         "ramsey/uuid": "^3.5|^4",
         "psr/log": "^1.0 || ^2.0 || ^3.0",
-        "psr/container": "^1"
+        "psr/container": "^1 || ^2.0"
     },
     "require-dev": {
         "phpunit/phpunit": "^9.5",

From f0f62f0fa0b25fbb95fd4911c0f640b6d6891c8b Mon Sep 17 00:00:00 2001
From: Shyim <6224096+shyim@users.noreply.github.com>
Date: Mon, 13 Dec 2021 16:46:21 +0100
Subject: [PATCH 310/337] Fix types for Symfony 5.4

---
 Symfony/Client/FlushSpoolProducerListener.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Symfony/Client/FlushSpoolProducerListener.php b/Symfony/Client/FlushSpoolProducerListener.php
index 1f5fdcb..e4ee5fc 100644
--- a/Symfony/Client/FlushSpoolProducerListener.php
+++ b/Symfony/Client/FlushSpoolProducerListener.php
@@ -30,7 +30,7 @@ public function flushMessages()
     /**
      * {@inheritdoc}
      */
-    public static function getSubscribedEvents()
+    public static function getSubscribedEvents(): array
     {
         $events = [];
 

From 790b80ae3e3d4e06dca8f38a0a3744901dd985b1 Mon Sep 17 00:00:00 2001
From: Shyim <6224096+shyim@users.noreply.github.com>
Date: Tue, 4 Jan 2022 16:27:51 +0100
Subject: [PATCH 311/337] Fix codestyle

---
 Symfony/Client/FlushSpoolProducerListener.php | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/Symfony/Client/FlushSpoolProducerListener.php b/Symfony/Client/FlushSpoolProducerListener.php
index e4ee5fc..8fa6fb4 100644
--- a/Symfony/Client/FlushSpoolProducerListener.php
+++ b/Symfony/Client/FlushSpoolProducerListener.php
@@ -14,9 +14,6 @@ class FlushSpoolProducerListener implements EventSubscriberInterface
      */
     private $producer;
 
-    /**
-     * @param SpoolProducer $producer
-     */
     public function __construct(SpoolProducer $producer)
     {
         $this->producer = $producer;

From 0146a5b12c49f500866d04a8dd604707dd1f3c29 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Thu, 10 Feb 2022 18:49:44 +0200
Subject: [PATCH 312/337] Add support for Symfony 6

---
 Container/Container.php                       |  2 +-
 .../DelayRedeliveredMessageExtensionTest.php  |  2 +-
 .../Client/Driver/RabbitMqStompDriverTest.php |  2 +-
 composer.json                                 | 20 +++++++++----------
 4 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/Container/Container.php b/Container/Container.php
index 255def3..5abec27 100644
--- a/Container/Container.php
+++ b/Container/Container.php
@@ -25,7 +25,7 @@ public function get($id)
         return $this->services[$id];
     }
 
-    public function has($id)
+    public function has(string $id): bool
     {
         return array_key_exists($id, $this->services);
     }
diff --git a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
index a92dc8d..0481d27 100644
--- a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
@@ -10,6 +10,7 @@
 use Enqueue\Consumption\Result;
 use Enqueue\Null\NullMessage;
 use Enqueue\Null\NullQueue;
+use Enqueue\Test\TestLogger;
 use Interop\Queue\Consumer;
 use Interop\Queue\Context as InteropContext;
 use Interop\Queue\Destination;
@@ -19,7 +20,6 @@
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\NullLogger;
-use Psr\Log\Test\TestLogger;
 
 class DelayRedeliveredMessageExtensionTest extends TestCase
 {
diff --git a/Tests/Client/Driver/RabbitMqStompDriverTest.php b/Tests/Client/Driver/RabbitMqStompDriverTest.php
index fbcb686..9fc72be 100644
--- a/Tests/Client/Driver/RabbitMqStompDriverTest.php
+++ b/Tests/Client/Driver/RabbitMqStompDriverTest.php
@@ -18,13 +18,13 @@
 use Enqueue\Stomp\StompMessage;
 use Enqueue\Stomp\StompProducer;
 use Enqueue\Test\ClassExtensionTrait;
+use Enqueue\Test\TestLogger;
 use Interop\Queue\Context;
 use Interop\Queue\Message as InteropMessage;
 use Interop\Queue\Producer as InteropProducer;
 use Interop\Queue\Queue as InteropQueue;
 use Interop\Queue\Topic as InteropTopic;
 use PHPUnit\Framework\TestCase;
-use Psr\Log\Test\TestLogger;
 
 class RabbitMqStompDriverTest extends TestCase
 {
diff --git a/composer.json b/composer.json
index fdb3657..82c7752 100644
--- a/composer.json
+++ b/composer.json
@@ -13,16 +13,16 @@
         "enqueue/dsn": "^0.10",
         "ramsey/uuid": "^3.5|^4",
         "psr/log": "^1.0 || ^2.0 || ^3.0",
-        "psr/container": "^1 || ^2.0"
+        "psr/container": "^1.1 || ^2.0"
     },
     "require-dev": {
         "phpunit/phpunit": "^9.5",
-        "symfony/console": "^4.3|^5",
-        "symfony/dependency-injection": "^4.3|^5",
-        "symfony/config": "^4.3|^5",
-        "symfony/event-dispatcher": "^4.3|^5",
-        "symfony/http-kernel": "^4.3|^5",
-        "symfony/yaml": "^4.3|^5",
+        "symfony/console": "^4.3|^5|^6.0",
+        "symfony/dependency-injection": "^4.3|^5|^6.0",
+        "symfony/config": "^4.3|^5|^6.0",
+        "symfony/event-dispatcher": "^4.3|^5|^6.0",
+        "symfony/http-kernel": "^4.3|^5|^6.0",
+        "symfony/yaml": "^4.3|^5|^6.0",
         "enqueue/amqp-ext": "0.10.x-dev",
         "enqueue/amqp-lib": "0.10.x-dev",
         "enqueue/amqp-bunny": "0.10.x-dev",
@@ -42,9 +42,9 @@
         "enqueue/dsn": "0.10.x-dev"
     },
     "suggest": {
-        "symfony/console": "^2.8|^3|^4|^5 If you want to use cli commands",
-        "symfony/dependency-injection": "^4.3|^5",
-        "symfony/config": "^4.3|^5",
+        "symfony/console": "^2.8|^3|^4|^5|^6.0 If you want to use cli commands",
+        "symfony/dependency-injection": "^4.3|^5|^6.0",
+        "symfony/config": "^4.3|^5|^6.0",
         "enqueue/amqp-ext": "AMQP transport (based on php extension)",
         "enqueue/stomp": "STOMP transport",
         "enqueue/fs": "Filesystem transport",

From 8f97d308d218bd52aba244a2aff3a665b225a133 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Wed, 16 Feb 2022 13:14:49 +0200
Subject: [PATCH 313/337] =?UTF-8?q?Drop=20Symfony=20<=205.1=20=F0=9F=94=A5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 composer.json | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/composer.json b/composer.json
index 82c7752..0798bf4 100644
--- a/composer.json
+++ b/composer.json
@@ -17,12 +17,12 @@
     },
     "require-dev": {
         "phpunit/phpunit": "^9.5",
-        "symfony/console": "^4.3|^5|^6.0",
-        "symfony/dependency-injection": "^4.3|^5|^6.0",
-        "symfony/config": "^4.3|^5|^6.0",
-        "symfony/event-dispatcher": "^4.3|^5|^6.0",
-        "symfony/http-kernel": "^4.3|^5|^6.0",
-        "symfony/yaml": "^4.3|^5|^6.0",
+        "symfony/console": "^5.1|^6.0",
+        "symfony/dependency-injection": "^5.1|^6.0",
+        "symfony/config": "^5.1|^6.0",
+        "symfony/event-dispatcher": "^5.1|^6.0",
+        "symfony/http-kernel": "^5.1|^6.0",
+        "symfony/yaml": "^5.1|^6.0",
         "enqueue/amqp-ext": "0.10.x-dev",
         "enqueue/amqp-lib": "0.10.x-dev",
         "enqueue/amqp-bunny": "0.10.x-dev",
@@ -42,9 +42,9 @@
         "enqueue/dsn": "0.10.x-dev"
     },
     "suggest": {
-        "symfony/console": "^2.8|^3|^4|^5|^6.0 If you want to use cli commands",
-        "symfony/dependency-injection": "^4.3|^5|^6.0",
-        "symfony/config": "^4.3|^5|^6.0",
+        "symfony/console": "^5.1|^6.0 If you want to use cli commands",
+        "symfony/dependency-injection": "^5.1|^6.0",
+        "symfony/config": "^5.1|^6.0",
         "enqueue/amqp-ext": "AMQP transport (based on php extension)",
         "enqueue/stomp": "STOMP transport",
         "enqueue/fs": "Filesystem transport",

From 0e77def0bf0a86a083ffb796b0b0f9d71a8d897e Mon Sep 17 00:00:00 2001
From: Markus Schmid <schmid-markus@online.de>
Date: Tue, 19 Jul 2022 11:55:59 +0200
Subject: [PATCH 314/337] symfony config: allow null values for
 connection_factory_class and factory_service

---
 Symfony/DependencyInjection/TransportFactory.php   |  8 ++++----
 .../DependencyInjection/TransportFactoryTest.php   | 14 +++++++++++++-
 2 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/Symfony/DependencyInjection/TransportFactory.php b/Symfony/DependencyInjection/TransportFactory.php
index 54dba4b..944b1a3 100644
--- a/Symfony/DependencyInjection/TransportFactory.php
+++ b/Symfony/DependencyInjection/TransportFactory.php
@@ -132,15 +132,15 @@ public function buildConnectionFactory(ContainerBuilder $container, array $confi
         $container->register($factoryFactoryId, $config['factory_class'] ?? ConnectionFactoryFactory::class);
 
         $factoryFactoryService = new Reference(
-            array_key_exists('factory_service', $config) ? $config['factory_service'] : $factoryFactoryId
+            $config['factory_service'] ?? $factoryFactoryId
         );
 
         unset($config['factory_service'], $config['factory_class']);
 
-        if (array_key_exists('connection_factory_class', $config)) {
-            $connectionFactoryClass = $config['connection_factory_class'];
-            unset($config['connection_factory_class']);
+        $connectionFactoryClass = $config['connection_factory_class'] ?? null;
+        unset($config['connection_factory_class']);
 
+        if (isset($connectionFactoryClass)) {
             $container->register($factoryId, $connectionFactoryClass)
                 ->addArgument($config)
             ;
diff --git a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index 6886871..a4f9d45 100644
--- a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -259,7 +259,14 @@ public function testShouldBuildConnectionFactoryFromDSN()
 
         $transport = new TransportFactory('default');
 
-        $transport->buildConnectionFactory($container, ['dsn' => 'foo://bar/baz']);
+        $config = [
+            'dsn' => 'foo://bar/baz',
+            'connection_factory_class' => null,
+            'factory_service' => null,
+            'factory_class' => null,
+        ];
+
+        $transport->buildConnectionFactory($container, $config);
 
         $this->assertTrue($container->hasDefinition('enqueue.transport.default.connection_factory'));
 
@@ -272,6 +279,11 @@ public function testShouldBuildConnectionFactoryFromDSN()
             [['dsn' => 'foo://bar/baz']],
             $container->getDefinition('enqueue.transport.default.connection_factory')->getArguments())
         ;
+
+        $this->assertEquals(
+            [new Reference('enqueue.transport.default.connection_factory_factory'), 'create'],
+            $container->getDefinition('enqueue.transport.default.connection_factory')->getFactory())
+        ;
     }
 
     public function testShouldBuildConnectionFactoryUsingCustomFactoryClass()

From b7e298485abcb3b3428876672d454f217e78b857 Mon Sep 17 00:00:00 2001
From: Markus Schmid <schmid-markus@online.de>
Date: Tue, 19 Jul 2022 12:20:29 +0200
Subject: [PATCH 315/337] remove duplicated test

---
 Tests/Symfony/DependencyInjection/TransportFactoryTest.php | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index a4f9d45..c5ff1c8 100644
--- a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -279,11 +279,6 @@ public function testShouldBuildConnectionFactoryFromDSN()
             [['dsn' => 'foo://bar/baz']],
             $container->getDefinition('enqueue.transport.default.connection_factory')->getArguments())
         ;
-
-        $this->assertEquals(
-            [new Reference('enqueue.transport.default.connection_factory_factory'), 'create'],
-            $container->getDefinition('enqueue.transport.default.connection_factory')->getFactory())
-        ;
     }
 
     public function testShouldBuildConnectionFactoryUsingCustomFactoryClass()

From fdd4ab8d70038711f076c2165df27afe5eb25191 Mon Sep 17 00:00:00 2001
From: Alex Mayer <amayer5125@gmail.com>
Date: Sat, 25 Feb 2023 22:18:02 -0500
Subject: [PATCH 316/337] Fix Shield URLs in READMEs

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index f617157..9e1dd50 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ Enqueue is an MIT-licensed open source project with its ongoing development made
 # Message Queue.
 
 [![Gitter](https://badges.gitter.im/php-enqueue/Lobby.svg)](https://gitter.im/php-enqueue/Lobby)
-[![Build Status](https://img.shields.io/github/workflow/status/php-enqueue/enqueue/CI)](https://github.com/php-enqueue/enqueue/actions?query=workflow%3ACI)
+[![Build Status](https://img.shields.io/github/actions/workflow/status/php-enqueue/enqueue/ci.yml?branch=master)](https://github.com/php-enqueue/enqueue/actions?query=workflow%3ACI)
 [![Total Downloads](https://poser.pugx.org/enqueue/enqueue/d/total.png)](https://packagist.org/packages/enqueue/enqueue)
 [![Latest Stable Version](https://poser.pugx.org/enqueue/enqueue/version.png)](https://packagist.org/packages/enqueue/enqueue)
 

From 054e9ad44391ca0571116b6494d287389f3696a4 Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Sun, 19 Feb 2023 15:13:41 +0200
Subject: [PATCH 317/337] Properly drop old PHP and Symfony versions

---
 composer.json | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/composer.json b/composer.json
index 0798bf4..0da00ee 100644
--- a/composer.json
+++ b/composer.json
@@ -6,7 +6,7 @@
     "homepage": "https://enqueue.forma-pro.com/",
     "license": "MIT",
     "require": {
-        "php": "^7.3|^8.0",
+        "php": "^7.4|^8.0",
         "queue-interop/amqp-interop": "^0.8.2",
         "queue-interop/queue-interop": "^0.8",
         "enqueue/null": "^0.10",
@@ -17,12 +17,12 @@
     },
     "require-dev": {
         "phpunit/phpunit": "^9.5",
-        "symfony/console": "^5.1|^6.0",
-        "symfony/dependency-injection": "^5.1|^6.0",
-        "symfony/config": "^5.1|^6.0",
-        "symfony/event-dispatcher": "^5.1|^6.0",
-        "symfony/http-kernel": "^5.1|^6.0",
-        "symfony/yaml": "^5.1|^6.0",
+        "symfony/console": "^5.41|^6.0",
+        "symfony/dependency-injection": "^5.4|^6.0",
+        "symfony/config": "^5.4|^6.0",
+        "symfony/event-dispatcher": "^5.4|^6.0",
+        "symfony/http-kernel": "^5.4|^6.0",
+        "symfony/yaml": "^5.4|^6.0",
         "enqueue/amqp-ext": "0.10.x-dev",
         "enqueue/amqp-lib": "0.10.x-dev",
         "enqueue/amqp-bunny": "0.10.x-dev",
@@ -42,9 +42,9 @@
         "enqueue/dsn": "0.10.x-dev"
     },
     "suggest": {
-        "symfony/console": "^5.1|^6.0 If you want to use cli commands",
-        "symfony/dependency-injection": "^5.1|^6.0",
-        "symfony/config": "^5.1|^6.0",
+        "symfony/console": "^5.4|^6.0 If you want to use cli commands",
+        "symfony/dependency-injection": "^5.4|^6.0",
+        "symfony/config": "^5.4|^6.0",
         "enqueue/amqp-ext": "AMQP transport (based on php extension)",
         "enqueue/stomp": "STOMP transport",
         "enqueue/fs": "Filesystem transport",

From 09b0684f4068cd1b39b3f74db57de6fe83fba4af Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Mon, 20 Mar 2023 16:38:26 +0200
Subject: [PATCH 318/337] Remove special group for rdkafka tests

---
 Tests/Client/Driver/RdKafkaDriverTest.php | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/Tests/Client/Driver/RdKafkaDriverTest.php b/Tests/Client/Driver/RdKafkaDriverTest.php
index 659590b..3b92042 100644
--- a/Tests/Client/Driver/RdKafkaDriverTest.php
+++ b/Tests/Client/Driver/RdKafkaDriverTest.php
@@ -19,9 +19,6 @@
 use Interop\Queue\Queue as InteropQueue;
 use PHPUnit\Framework\TestCase;
 
-/**
- * @group rdkafka
- */
 class RdKafkaDriverTest extends TestCase
 {
     use ClassExtensionTrait;

From 4b9dc9265eac6980e0eed25d15d46ff59b32935c Mon Sep 17 00:00:00 2001
From: Andrew M-Y <andrejs@gmail.com>
Date: Mon, 20 Mar 2023 16:49:00 +0200
Subject: [PATCH 319/337] Fix CS

---
 Tests/Client/Driver/RdKafkaDriverTest.php | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/Tests/Client/Driver/RdKafkaDriverTest.php b/Tests/Client/Driver/RdKafkaDriverTest.php
index 3b92042..c5e40e7 100644
--- a/Tests/Client/Driver/RdKafkaDriverTest.php
+++ b/Tests/Client/Driver/RdKafkaDriverTest.php
@@ -99,9 +99,6 @@ protected function createQueue(string $name): InteropQueue
         return new RdKafkaTopic($name);
     }
 
-    /**
-     * @return RdKafkaTopic
-     */
     protected function createTopic(string $name): RdKafkaTopic
     {
         return new RdKafkaTopic($name);

From 9273d0760b40594bfc721c36117824f660a388df Mon Sep 17 00:00:00 2001
From: Witold Wasiczko <witold@wasiczko.pl>
Date: Thu, 23 Mar 2023 10:50:55 +0100
Subject: [PATCH 320/337] Update and fix pkg test with 8.1 and 8.2 PHP support

---
 .github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b394958..28a46e9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -10,7 +10,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        php: ['7.3', '7.4', '8.0']
+        php: ['7.4', '8.0', '8.1', '8.2']
 
     name: PHP ${{ matrix.php }} tests
 

From 38896472901847d41df1150c81c5a9ce4d8aa5f9 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Mon, 12 Jun 2023 15:39:04 +0200
Subject: [PATCH 321/337] Add throws tag

---
 Client/PreSendCommandExtensionInterface.php | 3 +++
 Client/PreSendEventExtensionInterface.php   | 3 +++
 Client/ProducerInterface.php                | 4 ++++
 3 files changed, 10 insertions(+)

diff --git a/Client/PreSendCommandExtensionInterface.php b/Client/PreSendCommandExtensionInterface.php
index b2f6c7a..cefec09 100644
--- a/Client/PreSendCommandExtensionInterface.php
+++ b/Client/PreSendCommandExtensionInterface.php
@@ -4,5 +4,8 @@
 
 interface PreSendCommandExtensionInterface
 {
+    /**
+     * @throws \Exception
+     */
     public function onPreSendCommand(PreSend $context): void;
 }
diff --git a/Client/PreSendEventExtensionInterface.php b/Client/PreSendEventExtensionInterface.php
index 1eaaae5..ecb0519 100644
--- a/Client/PreSendEventExtensionInterface.php
+++ b/Client/PreSendEventExtensionInterface.php
@@ -4,5 +4,8 @@
 
 interface PreSendEventExtensionInterface
 {
+    /**
+     * @throws \Exception
+     */
     public function onPreSendEvent(PreSend $context): void;
 }
diff --git a/Client/ProducerInterface.php b/Client/ProducerInterface.php
index 1c7b056..3c88480 100644
--- a/Client/ProducerInterface.php
+++ b/Client/ProducerInterface.php
@@ -10,6 +10,8 @@ interface ProducerInterface
      * The message could be pretty much everything as long as you have a client extension that transforms a body to string on onPreSendEvent.
      *
      * @param string|array|Message $message
+     *
+     * @throws \Exception
      */
     public function sendEvent(string $topic, $message): void;
 
@@ -18,6 +20,8 @@ public function sendEvent(string $topic, $message): void;
      * The promise is returned if needReply argument is true.
      *
      * @param string|array|Message $message
+     *
+     * @throws \Exception
      */
     public function sendCommand(string $command, $message, bool $needReply = false): ?Promise;
 }

From 109bb1d45e8ad96bb7f47802019194ac8b86d42d Mon Sep 17 00:00:00 2001
From: Vincent Langlet <VincentLanglet@users.noreply.github.com>
Date: Tue, 4 Apr 2023 00:23:40 +0200
Subject: [PATCH 322/337] Add explicit @return

---
 Client/DelegateProcessor.php | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Client/DelegateProcessor.php b/Client/DelegateProcessor.php
index 18985b4..e8a72ad 100644
--- a/Client/DelegateProcessor.php
+++ b/Client/DelegateProcessor.php
@@ -24,6 +24,8 @@ public function __construct(ProcessorRegistryInterface $registry)
 
     /**
      * {@inheritdoc}
+     *
+     * @return string|object
      */
     public function process(InteropMessage $message, Context $context)
     {

From 03eff4c6b5f0864afc17abbd947692bfed98cf72 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Tue, 16 May 2023 16:32:17 +0200
Subject: [PATCH 323/337] Fix cs

---
 Client/DelegateProcessor.php | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/Client/DelegateProcessor.php b/Client/DelegateProcessor.php
index e8a72ad..0fbe9c2 100644
--- a/Client/DelegateProcessor.php
+++ b/Client/DelegateProcessor.php
@@ -14,9 +14,6 @@ class DelegateProcessor implements Processor
      */
     private $registry;
 
-    /**
-     * @param ProcessorRegistryInterface $registry
-     */
     public function __construct(ProcessorRegistryInterface $registry)
     {
         $this->registry = $registry;
@@ -31,10 +28,7 @@ public function process(InteropMessage $message, Context $context)
     {
         $processorName = $message->getProperty(Config::PROCESSOR);
         if (false == $processorName) {
-            throw new \LogicException(sprintf(
-                'Got message without required parameter: "%s"',
-                Config::PROCESSOR
-            ));
+            throw new \LogicException(sprintf('Got message without required parameter: "%s"', Config::PROCESSOR));
         }
 
         return $this->registry->get($processorName)->process($message, $context);

From b39c388336b62868b93a70465171b7c27982630f Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Tue, 4 Jul 2023 14:49:32 +0200
Subject: [PATCH 324/337] Precise phpdoc for static analysis

---
 Client/CommandSubscriberInterface.php | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/Client/CommandSubscriberInterface.php b/Client/CommandSubscriberInterface.php
index 9ee09d0..d7b06da 100644
--- a/Client/CommandSubscriberInterface.php
+++ b/Client/CommandSubscriberInterface.php
@@ -2,6 +2,15 @@
 
 namespace Enqueue\Client;
 
+/**
+ * @phpstan-type CommandConfig = array{
+ *     command: string,
+ *     processor?: string,
+ *     queue?: string,
+ *     prefix_queue?: bool,
+ *     exclusive?: bool,
+ * }
+ */
 interface CommandSubscriberInterface
 {
     /**
@@ -44,6 +53,8 @@ interface CommandSubscriberInterface
      * Note: If you set "prefix_queue" to true then the "queue" is used as is and therefor the driver is not used to create a transport queue name.
      *
      * @return string|array
+     *
+     * @phpstan-return string|CommandConfig|array<CommandConfig>
      */
     public static function getSubscribedCommand();
 }

From 4b3c6e98215cbc67c79fe0bfb6af55ccaac0e8fe Mon Sep 17 00:00:00 2001
From: Ruud Kamphuis <ruudk@users.noreply.github.com>
Date: Wed, 16 Aug 2023 18:31:45 +0200
Subject: [PATCH 325/337] Use `#[AsCommand]` attribute on commands

Fixes the following deprecations:

```json
  {
    "message": "Since symfony/console 6.1: Relying on the static property \"$defaultName\" for setting a command name is deprecated. Add the \"Symfony\\Component\\Console\\Attribute\\AsCommand\" attribute to the \"Enqueue\\Symfony\\Consumption\\ConfigurableConsumeCommand\" class instead.",
    "count": 1
  },
  {
    "message": "Since symfony/console 6.1: Relying on the static property \"$defaultName\" for setting a command name is deprecated. Add the \"Symfony\\Component\\Console\\Attribute\\AsCommand\" attribute to the \"Enqueue\\Symfony\\Client\\ConsumeCommand\" class instead.",
    "count": 1
  },
  {
    "message": "Since symfony/console 6.1: Relying on the static property \"$defaultName\" for setting a command name is deprecated. Add the \"Symfony\\Component\\Console\\Attribute\\AsCommand\" attribute to the \"Enqueue\\Symfony\\Client\\ProduceCommand\" class instead.",
    "count": 1
  },
  {
    "message": "Since symfony/console 6.1: Relying on the static property \"$defaultName\" for setting a command name is deprecated. Add the \"Symfony\\Component\\Console\\Attribute\\AsCommand\" attribute to the \"Enqueue\\Symfony\\Client\\SetupBrokerCommand\" class instead.",
    "count": 1
  },
  {
    "message": "Since symfony/console 6.1: Relying on the static property \"$defaultName\" for setting a command name is deprecated. Add the \"Symfony\\Component\\Console\\Attribute\\AsCommand\" attribute to the \"Enqueue\\Symfony\\Client\\RoutesCommand\" class instead.",
    "count": 1
  },
```
---
 Symfony/Client/ConsumeCommand.php                  | 6 ++++--
 Symfony/Client/ProduceCommand.php                  | 2 ++
 Symfony/Client/RoutesCommand.php                   | 2 ++
 Symfony/Client/SetupBrokerCommand.php              | 2 ++
 Symfony/Consumption/ConfigurableConsumeCommand.php | 4 +++-
 Symfony/Consumption/ConsumeCommand.php             | 4 +++-
 6 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/Symfony/Client/ConsumeCommand.php b/Symfony/Client/ConsumeCommand.php
index 4b65f62..39af6d5 100644
--- a/Symfony/Client/ConsumeCommand.php
+++ b/Symfony/Client/ConsumeCommand.php
@@ -13,18 +13,20 @@
 use Interop\Queue\Processor;
 use Psr\Container\ContainerInterface;
 use Psr\Container\NotFoundExceptionInterface;
+use Symfony\Component\Console\Attribute\AsCommand;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputArgument;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Output\OutputInterface;
 
+#[AsCommand('enqueue:consume')]
 class ConsumeCommand extends Command
 {
+    use ChooseLoggerCommandTrait;
     use LimitsExtensionsCommandTrait;
-    use SetupBrokerExtensionCommandTrait;
     use QueueConsumerOptionsCommandTrait;
-    use ChooseLoggerCommandTrait;
+    use SetupBrokerExtensionCommandTrait;
 
     protected static $defaultName = 'enqueue:consume';
 
diff --git a/Symfony/Client/ProduceCommand.php b/Symfony/Client/ProduceCommand.php
index 667871c..6064f82 100644
--- a/Symfony/Client/ProduceCommand.php
+++ b/Symfony/Client/ProduceCommand.php
@@ -6,12 +6,14 @@
 use Enqueue\Client\ProducerInterface;
 use Psr\Container\ContainerInterface;
 use Psr\Container\NotFoundExceptionInterface;
+use Symfony\Component\Console\Attribute\AsCommand;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputArgument;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Output\OutputInterface;
 
+#[AsCommand('enqueue:produce')]
 class ProduceCommand extends Command
 {
     protected static $defaultName = 'enqueue:produce';
diff --git a/Symfony/Client/RoutesCommand.php b/Symfony/Client/RoutesCommand.php
index 5419f3b..59a4a4d 100644
--- a/Symfony/Client/RoutesCommand.php
+++ b/Symfony/Client/RoutesCommand.php
@@ -6,6 +6,7 @@
 use Enqueue\Client\Route;
 use Psr\Container\ContainerInterface;
 use Psr\Container\NotFoundExceptionInterface;
+use Symfony\Component\Console\Attribute\AsCommand;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Helper\Table;
 use Symfony\Component\Console\Helper\TableSeparator;
@@ -13,6 +14,7 @@
 use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Output\OutputInterface;
 
+#[AsCommand('enqueue:routes')]
 class RoutesCommand extends Command
 {
     protected static $defaultName = 'enqueue:routes';
diff --git a/Symfony/Client/SetupBrokerCommand.php b/Symfony/Client/SetupBrokerCommand.php
index 65d52ee..68aebb5 100644
--- a/Symfony/Client/SetupBrokerCommand.php
+++ b/Symfony/Client/SetupBrokerCommand.php
@@ -5,12 +5,14 @@
 use Enqueue\Client\DriverInterface;
 use Psr\Container\ContainerInterface;
 use Psr\Container\NotFoundExceptionInterface;
+use Symfony\Component\Console\Attribute\AsCommand;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Logger\ConsoleLogger;
 use Symfony\Component\Console\Output\OutputInterface;
 
+#[AsCommand('enqueue:setup-broker')]
 class SetupBrokerCommand extends Command
 {
     protected static $defaultName = 'enqueue:setup-broker';
diff --git a/Symfony/Consumption/ConfigurableConsumeCommand.php b/Symfony/Consumption/ConfigurableConsumeCommand.php
index 9ec8a36..234eb04 100644
--- a/Symfony/Consumption/ConfigurableConsumeCommand.php
+++ b/Symfony/Consumption/ConfigurableConsumeCommand.php
@@ -8,17 +8,19 @@
 use Enqueue\ProcessorRegistryInterface;
 use Psr\Container\ContainerInterface;
 use Psr\Container\NotFoundExceptionInterface;
+use Symfony\Component\Console\Attribute\AsCommand;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputArgument;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Output\OutputInterface;
 
+#[AsCommand('enqueue:transport:consume')]
 class ConfigurableConsumeCommand extends Command
 {
+    use ChooseLoggerCommandTrait;
     use LimitsExtensionsCommandTrait;
     use QueueConsumerOptionsCommandTrait;
-    use ChooseLoggerCommandTrait;
 
     protected static $defaultName = 'enqueue:transport:consume';
 
diff --git a/Symfony/Consumption/ConsumeCommand.php b/Symfony/Consumption/ConsumeCommand.php
index b1a54c1..870cc5f 100644
--- a/Symfony/Consumption/ConsumeCommand.php
+++ b/Symfony/Consumption/ConsumeCommand.php
@@ -7,16 +7,18 @@
 use Enqueue\Consumption\QueueConsumerInterface;
 use Psr\Container\ContainerInterface;
 use Psr\Container\NotFoundExceptionInterface;
+use Symfony\Component\Console\Attribute\AsCommand;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Output\OutputInterface;
 
+#[AsCommand('enqueue:transport:consume')]
 class ConsumeCommand extends Command
 {
+    use ChooseLoggerCommandTrait;
     use LimitsExtensionsCommandTrait;
     use QueueConsumerOptionsCommandTrait;
-    use ChooseLoggerCommandTrait;
 
     protected static $defaultName = 'enqueue:transport:consume';
 

From 4d5f16316dbeafd9031c1541c69044ddf2e8440f Mon Sep 17 00:00:00 2001
From: Damian Dziaduch <damian@dziaduch.pl>
Date: Tue, 17 Sep 2024 14:45:33 +0200
Subject: [PATCH 326/337] Drop useless call to end method

Due to this, upgrading from SF 6.4.7 to more recent versions is impossible.
---
 Symfony/Client/DependencyInjection/ClientFactory.php | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/Symfony/Client/DependencyInjection/ClientFactory.php b/Symfony/Client/DependencyInjection/ClientFactory.php
index 2eede31..be020dc 100644
--- a/Symfony/Client/DependencyInjection/ClientFactory.php
+++ b/Symfony/Client/DependencyInjection/ClientFactory.php
@@ -74,12 +74,8 @@ public static function getConfiguration(bool $debug, string $name = 'client'): N
             ->scalarNode('router_processor')->defaultNull()->end()
             ->integerNode('redelivered_delay_time')->min(0)->defaultValue(0)->end()
             ->scalarNode('default_queue')->defaultValue('default')->cannotBeEmpty()->end()
-            ->arrayNode('driver_options')
-                ->addDefaultsIfNotSet()
-                ->info('The array contains driver specific options')
-                ->ignoreExtraKeys(false)
+            ->arrayNode('driver_options')->addDefaultsIfNotSet()->info('The array contains driver specific options')->ignoreExtraKeys(false)->end()
             ->end()
-            ->end()->end()
         ;
 
         return $builder;

From 4edbcfd9ee5cef747834c8d66b7517291e0bbd66 Mon Sep 17 00:00:00 2001
From: Viktor Zavitkov <zavitkov@icloud.com>
Date: Thu, 14 Nov 2024 13:22:17 +0300
Subject: [PATCH 327/337] fix deprecations

---
 Doctrine/DoctrineSchemaCompilerPass.php            | 2 +-
 Symfony/Client/ConsumeCommand.php                  | 2 --
 Symfony/Client/ProduceCommand.php                  | 2 --
 Symfony/Client/RoutesCommand.php                   | 2 --
 Symfony/Client/SetupBrokerCommand.php              | 2 --
 Symfony/Consumption/ConfigurableConsumeCommand.php | 2 --
 Symfony/Consumption/ConsumeCommand.php             | 2 --
 7 files changed, 1 insertion(+), 13 deletions(-)

diff --git a/Doctrine/DoctrineSchemaCompilerPass.php b/Doctrine/DoctrineSchemaCompilerPass.php
index 25016a7..0eb3784 100644
--- a/Doctrine/DoctrineSchemaCompilerPass.php
+++ b/Doctrine/DoctrineSchemaCompilerPass.php
@@ -11,7 +11,7 @@
 
 class DoctrineSchemaCompilerPass implements CompilerPassInterface
 {
-    public function process(ContainerBuilder $container)
+    public function process(ContainerBuilder $container): void
     {
         if (false === $container->hasDefinition('doctrine')) {
             return;
diff --git a/Symfony/Client/ConsumeCommand.php b/Symfony/Client/ConsumeCommand.php
index 39af6d5..c97dd3a 100644
--- a/Symfony/Client/ConsumeCommand.php
+++ b/Symfony/Client/ConsumeCommand.php
@@ -28,8 +28,6 @@ class ConsumeCommand extends Command
     use QueueConsumerOptionsCommandTrait;
     use SetupBrokerExtensionCommandTrait;
 
-    protected static $defaultName = 'enqueue:consume';
-
     /**
      * @var ContainerInterface
      */
diff --git a/Symfony/Client/ProduceCommand.php b/Symfony/Client/ProduceCommand.php
index 6064f82..512dd5f 100644
--- a/Symfony/Client/ProduceCommand.php
+++ b/Symfony/Client/ProduceCommand.php
@@ -16,8 +16,6 @@
 #[AsCommand('enqueue:produce')]
 class ProduceCommand extends Command
 {
-    protected static $defaultName = 'enqueue:produce';
-
     /**
      * @var ContainerInterface
      */
diff --git a/Symfony/Client/RoutesCommand.php b/Symfony/Client/RoutesCommand.php
index 59a4a4d..99979d5 100644
--- a/Symfony/Client/RoutesCommand.php
+++ b/Symfony/Client/RoutesCommand.php
@@ -17,8 +17,6 @@
 #[AsCommand('enqueue:routes')]
 class RoutesCommand extends Command
 {
-    protected static $defaultName = 'enqueue:routes';
-
     /**
      * @var ContainerInterface
      */
diff --git a/Symfony/Client/SetupBrokerCommand.php b/Symfony/Client/SetupBrokerCommand.php
index 68aebb5..78bb404 100644
--- a/Symfony/Client/SetupBrokerCommand.php
+++ b/Symfony/Client/SetupBrokerCommand.php
@@ -15,8 +15,6 @@
 #[AsCommand('enqueue:setup-broker')]
 class SetupBrokerCommand extends Command
 {
-    protected static $defaultName = 'enqueue:setup-broker';
-
     /**
      * @var ContainerInterface
      */
diff --git a/Symfony/Consumption/ConfigurableConsumeCommand.php b/Symfony/Consumption/ConfigurableConsumeCommand.php
index 234eb04..2847ca5 100644
--- a/Symfony/Consumption/ConfigurableConsumeCommand.php
+++ b/Symfony/Consumption/ConfigurableConsumeCommand.php
@@ -22,8 +22,6 @@ class ConfigurableConsumeCommand extends Command
     use LimitsExtensionsCommandTrait;
     use QueueConsumerOptionsCommandTrait;
 
-    protected static $defaultName = 'enqueue:transport:consume';
-
     /**
      * @var ContainerInterface
      */
diff --git a/Symfony/Consumption/ConsumeCommand.php b/Symfony/Consumption/ConsumeCommand.php
index 870cc5f..c8de790 100644
--- a/Symfony/Consumption/ConsumeCommand.php
+++ b/Symfony/Consumption/ConsumeCommand.php
@@ -20,8 +20,6 @@ class ConsumeCommand extends Command
     use LimitsExtensionsCommandTrait;
     use QueueConsumerOptionsCommandTrait;
 
-    protected static $defaultName = 'enqueue:transport:consume';
-
     /**
      * @var ContainerInterface
      */

From c6a6db244c661eebb81b35286508734e343a2f92 Mon Sep 17 00:00:00 2001
From: Viktor Zavitkov <zavitkov@icloud.com>
Date: Thu, 14 Nov 2024 15:04:57 +0300
Subject: [PATCH 328/337] fix tests

---
 Tests/Symfony/Client/ConsumeCommandTest.php   | 20 +++++++++++++++----
 Tests/Symfony/Client/ProduceCommandTest.php   | 18 ++++++++++++++---
 Tests/Symfony/Client/RoutesCommandTest.php    | 18 ++++++++++++++---
 .../Symfony/Client/SetupBrokerCommandTest.php | 18 ++++++++++++++---
 .../Client/SimpleConsumeCommandTest.php       |  7 -------
 .../Client/SimpleProduceCommandTest.php       |  7 -------
 .../Client/SimpleRoutesCommandTest.php        |  7 -------
 .../Client/SimpleSetupBrokerCommandTest.php   |  7 -------
 .../ConfigurableConsumeCommandTest.php        | 20 +++++++++++++++----
 .../Consumption/ConsumeCommandTest.php        | 20 +++++++++++++++----
 .../Consumption/SimpleConsumeCommandTest.php  |  7 -------
 11 files changed, 93 insertions(+), 56 deletions(-)

diff --git a/Tests/Symfony/Client/ConsumeCommandTest.php b/Tests/Symfony/Client/ConsumeCommandTest.php
index 1d94e6e..8076aaf 100644
--- a/Tests/Symfony/Client/ConsumeCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeCommandTest.php
@@ -22,6 +22,7 @@
 use Interop\Queue\Queue;
 use PHPUnit\Framework\TestCase;
 use Psr\Container\ContainerInterface;
+use Symfony\Component\Console\Attribute\AsCommand;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Tester\CommandTester;
 
@@ -44,11 +45,22 @@ public function testCouldBeConstructedWithRequiredAttributes()
         new ConsumeCommand($this->createMock(ContainerInterface::class), 'default');
     }
 
-    public function testShouldHaveCommandName()
+    public function testShouldHaveAsCommandAttributeWithCommandName()
     {
-        $command = new ConsumeCommand($this->createMock(ContainerInterface::class), 'default');
+        $commandClass = ConsumeCommand::class;
+
+        $reflectionClass = new \ReflectionClass($commandClass);
+
+        $attributes = $reflectionClass->getAttributes(AsCommand::class);
+
+        $this->assertNotEmpty($attributes, 'The command does not have the AsCommand attribute.');
+
+        // Get the first attribute instance (assuming there is only one AsCommand attribute)
+        $asCommandAttribute = $attributes[0];
 
-        $this->assertEquals('enqueue:consume', $command->getName());
+        // Verify the 'name' parameter value
+        $attributeInstance = $asCommandAttribute->newInstance();
+        $this->assertEquals('enqueue:consume', $attributeInstance->name, 'The command name is not set correctly in the AsCommand attribute.');
     }
 
     public function testShouldHaveExpectedOptions()
@@ -641,7 +653,7 @@ private function createContextWithoutSubscriptionConsumerMock(): InteropContext
     /**
      * @return \PHPUnit\Framework\MockObject\MockObject|InteropContext
      */
-    private function createContextStub(Consumer $consumer = null): InteropContext
+    private function createContextStub(?Consumer $consumer = null): InteropContext
     {
         $context = $this->createContextWithoutSubscriptionConsumerMock();
         $context
diff --git a/Tests/Symfony/Client/ProduceCommandTest.php b/Tests/Symfony/Client/ProduceCommandTest.php
index 0dffc3d..2efc476 100644
--- a/Tests/Symfony/Client/ProduceCommandTest.php
+++ b/Tests/Symfony/Client/ProduceCommandTest.php
@@ -9,6 +9,7 @@
 use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
 use Psr\Container\ContainerInterface;
+use Symfony\Component\Console\Attribute\AsCommand;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Tester\CommandTester;
 
@@ -31,11 +32,22 @@ public function testCouldBeConstructedWithContainerAsFirstArgument()
         new ProduceCommand($this->createMock(ContainerInterface::class), 'default');
     }
 
-    public function testShouldHaveCommandName()
+    public function testShouldHaveAsCommandAttributeWithCommandName()
     {
-        $command = new ProduceCommand($this->createMock(ContainerInterface::class), 'default');
+        $commandClass = ProduceCommand::class;
+
+        $reflectionClass = new \ReflectionClass($commandClass);
+
+        $attributes = $reflectionClass->getAttributes(AsCommand::class);
+
+        $this->assertNotEmpty($attributes, 'The command does not have the AsCommand attribute.');
+
+        // Get the first attribute instance (assuming there is only one AsCommand attribute)
+        $asCommandAttribute = $attributes[0];
 
-        $this->assertEquals('enqueue:produce', $command->getName());
+        // Verify the 'name' parameter value
+        $attributeInstance = $asCommandAttribute->newInstance();
+        $this->assertEquals('enqueue:produce', $attributeInstance->name, 'The command name is not set correctly in the AsCommand attribute.');
     }
 
     public function testShouldHaveExpectedOptions()
diff --git a/Tests/Symfony/Client/RoutesCommandTest.php b/Tests/Symfony/Client/RoutesCommandTest.php
index 04980c1..c2168c4 100644
--- a/Tests/Symfony/Client/RoutesCommandTest.php
+++ b/Tests/Symfony/Client/RoutesCommandTest.php
@@ -11,6 +11,7 @@
 use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
 use Psr\Container\ContainerInterface;
+use Symfony\Component\Console\Attribute\AsCommand;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Tester\CommandTester;
 
@@ -33,11 +34,22 @@ public function testCouldBeConstructedWithConfigAndRouteCollectionAsArguments()
         new RoutesCommand($this->createMock(ContainerInterface::class), 'default');
     }
 
-    public function testShouldHaveCommandName()
+    public function testShouldHaveAsCommandAttributeWithCommandName()
     {
-        $command = new RoutesCommand($this->createMock(ContainerInterface::class), 'default');
+        $commandClass = RoutesCommand::class;
+
+        $reflectionClass = new \ReflectionClass($commandClass);
+
+        $attributes = $reflectionClass->getAttributes(AsCommand::class);
+
+        $this->assertNotEmpty($attributes, 'The command does not have the AsCommand attribute.');
+
+        // Get the first attribute instance (assuming there is only one AsCommand attribute)
+        $asCommandAttribute = $attributes[0];
 
-        $this->assertEquals('enqueue:routes', $command->getName());
+        // Verify the 'name' parameter value
+        $attributeInstance = $asCommandAttribute->newInstance();
+        $this->assertEquals('enqueue:routes', $attributeInstance->name, 'The command name is not set correctly in the AsCommand attribute.');
     }
 
     public function testShouldHaveCommandAliases()
diff --git a/Tests/Symfony/Client/SetupBrokerCommandTest.php b/Tests/Symfony/Client/SetupBrokerCommandTest.php
index 82345b2..89ca379 100644
--- a/Tests/Symfony/Client/SetupBrokerCommandTest.php
+++ b/Tests/Symfony/Client/SetupBrokerCommandTest.php
@@ -8,6 +8,7 @@
 use Enqueue\Test\ClassExtensionTrait;
 use PHPUnit\Framework\TestCase;
 use Psr\Container\ContainerInterface;
+use Symfony\Component\Console\Attribute\AsCommand;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Tester\CommandTester;
 
@@ -30,11 +31,22 @@ public function testCouldBeConstructedWithContainerAsFirstArgument()
         new SetupBrokerCommand($this->createMock(ContainerInterface::class), 'default');
     }
 
-    public function testShouldHaveCommandName()
+    public function testShouldHaveAsCommandAttributeWithCommandName()
     {
-        $command = new SetupBrokerCommand($this->createMock(ContainerInterface::class), 'default');
+        $commandClass = SetupBrokerCommand::class;
+
+        $reflectionClass = new \ReflectionClass($commandClass);
+
+        $attributes = $reflectionClass->getAttributes(AsCommand::class);
+
+        $this->assertNotEmpty($attributes, 'The command does not have the AsCommand attribute.');
+
+        // Get the first attribute instance (assuming there is only one AsCommand attribute)
+        $asCommandAttribute = $attributes[0];
 
-        $this->assertEquals('enqueue:setup-broker', $command->getName());
+        // Verify the 'name' parameter value
+        $attributeInstance = $asCommandAttribute->newInstance();
+        $this->assertEquals('enqueue:setup-broker', $attributeInstance->name, 'The command name is not set correctly in the AsCommand attribute.');
     }
 
     public function testShouldHaveCommandAliases()
diff --git a/Tests/Symfony/Client/SimpleConsumeCommandTest.php b/Tests/Symfony/Client/SimpleConsumeCommandTest.php
index fb89dae..60d4093 100644
--- a/Tests/Symfony/Client/SimpleConsumeCommandTest.php
+++ b/Tests/Symfony/Client/SimpleConsumeCommandTest.php
@@ -34,13 +34,6 @@ public function testCouldBeConstructedWithRequiredAttributes()
         new SimpleConsumeCommand($this->createQueueConsumerMock(), $this->createDriverStub(), $this->createDelegateProcessorMock());
     }
 
-    public function testShouldHaveCommandName()
-    {
-        $command = new SimpleConsumeCommand($this->createQueueConsumerMock(), $this->createDriverStub(), $this->createDelegateProcessorMock());
-
-        $this->assertEquals('enqueue:consume', $command->getName());
-    }
-
     public function testShouldHaveExpectedOptions()
     {
         $command = new SimpleConsumeCommand($this->createQueueConsumerMock(), $this->createDriverStub(), $this->createDelegateProcessorMock());
diff --git a/Tests/Symfony/Client/SimpleProduceCommandTest.php b/Tests/Symfony/Client/SimpleProduceCommandTest.php
index 71ac20f..2775870 100644
--- a/Tests/Symfony/Client/SimpleProduceCommandTest.php
+++ b/Tests/Symfony/Client/SimpleProduceCommandTest.php
@@ -28,13 +28,6 @@ public function testCouldBeConstructedWithContainerAsFirstArgument()
         new SimpleProduceCommand($this->createProducerMock());
     }
 
-    public function testShouldHaveCommandName()
-    {
-        $command = new SimpleProduceCommand($this->createProducerMock());
-
-        $this->assertEquals('enqueue:produce', $command->getName());
-    }
-
     public function testShouldHaveExpectedOptions()
     {
         $command = new SimpleProduceCommand($this->createProducerMock());
diff --git a/Tests/Symfony/Client/SimpleRoutesCommandTest.php b/Tests/Symfony/Client/SimpleRoutesCommandTest.php
index 469334b..356f3f9 100644
--- a/Tests/Symfony/Client/SimpleRoutesCommandTest.php
+++ b/Tests/Symfony/Client/SimpleRoutesCommandTest.php
@@ -30,13 +30,6 @@ public function testCouldBeConstructedWithConfigAndRouteCollectionAsArguments()
         new SimpleRoutesCommand($this->createDriverMock());
     }
 
-    public function testShouldHaveCommandName()
-    {
-        $command = new SimpleRoutesCommand($this->createDriverMock());
-
-        $this->assertEquals('enqueue:routes', $command->getName());
-    }
-
     public function testShouldHaveCommandAliases()
     {
         $command = new SimpleRoutesCommand($this->createDriverMock());
diff --git a/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php b/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php
index f9a6612..91802ff 100644
--- a/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php
+++ b/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php
@@ -28,13 +28,6 @@ public function testCouldBeConstructedWithContainerAsFirstArgument()
         new SimpleSetupBrokerCommand($this->createClientDriverMock());
     }
 
-    public function testShouldHaveCommandName()
-    {
-        $command = new SimpleSetupBrokerCommand($this->createClientDriverMock());
-
-        $this->assertEquals('enqueue:setup-broker', $command->getName());
-    }
-
     public function testShouldHaveCommandAliases()
     {
         $command = new SimpleSetupBrokerCommand($this->createClientDriverMock());
diff --git a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
index c788d5e..84d6dff 100644
--- a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
@@ -15,6 +15,7 @@
 use Interop\Queue\Queue as InteropQueue;
 use PHPUnit\Framework\TestCase;
 use Psr\Container\ContainerInterface;
+use Symfony\Component\Console\Attribute\AsCommand;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Tester\CommandTester;
 
@@ -37,11 +38,22 @@ public function testCouldBeConstructedWithRequiredAttributes()
         new ConfigurableConsumeCommand($this->createMock(ContainerInterface::class), 'default');
     }
 
-    public function testShouldHaveCommandName()
+    public function testShouldHaveAsCommandAttributeWithCommandName()
     {
-        $command = new ConfigurableConsumeCommand($this->createMock(ContainerInterface::class), 'default');
+        $commandClass = ConfigurableConsumeCommand::class;
+
+        $reflectionClass = new \ReflectionClass($commandClass);
+
+        $attributes = $reflectionClass->getAttributes(AsCommand::class);
+
+        $this->assertNotEmpty($attributes, 'The command does not have the AsCommand attribute.');
+
+        // Get the first attribute instance (assuming there is only one AsCommand attribute)
+        $asCommandAttribute = $attributes[0];
 
-        $this->assertEquals('enqueue:transport:consume', $command->getName());
+        // Verify the 'name' parameter value
+        $attributeInstance = $asCommandAttribute->newInstance();
+        $this->assertEquals('enqueue:transport:consume', $attributeInstance->name, 'The command name is not set correctly in the AsCommand attribute.');
     }
 
     public function testShouldHaveExpectedOptions()
@@ -192,7 +204,7 @@ public function testShouldExecuteConsumptionWithSeveralCustomQueues()
 
     public function testShouldExecuteConsumptionWhenProcessorImplementsQueueSubscriberInterface()
     {
-        $processor = new class() implements Processor, QueueSubscriberInterface {
+        $processor = new class implements Processor, QueueSubscriberInterface {
             public function process(InteropMessage $message, Context $context)
             {
             }
diff --git a/Tests/Symfony/Consumption/ConsumeCommandTest.php b/Tests/Symfony/Consumption/ConsumeCommandTest.php
index 3483971..3c16cde 100644
--- a/Tests/Symfony/Consumption/ConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConsumeCommandTest.php
@@ -17,6 +17,7 @@
 use Interop\Queue\Queue;
 use PHPUnit\Framework\TestCase;
 use Psr\Container\ContainerInterface;
+use Symfony\Component\Console\Attribute\AsCommand;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Tester\CommandTester;
 
@@ -39,11 +40,22 @@ public function testCouldBeConstructedWithRequiredAttributes()
         new ConsumeCommand($this->createMock(ContainerInterface::class), 'default');
     }
 
-    public function testShouldHaveCommandName()
+    public function testShouldHaveAsCommandAttributeWithCommandName()
     {
-        $command = new ConsumeCommand($this->createMock(ContainerInterface::class), 'default');
+        $commandClass = ConsumeCommand::class;
+
+        $reflectionClass = new \ReflectionClass($commandClass);
+
+        $attributes = $reflectionClass->getAttributes(AsCommand::class);
+
+        $this->assertNotEmpty($attributes, 'The command does not have the AsCommand attribute.');
+
+        // Get the first attribute instance (assuming there is only one AsCommand attribute)
+        $asCommandAttribute = $attributes[0];
 
-        $this->assertEquals('enqueue:transport:consume', $command->getName());
+        // Verify the 'name' parameter value
+        $attributeInstance = $asCommandAttribute->newInstance();
+        $this->assertEquals('enqueue:transport:consume', $attributeInstance->name, 'The command name is not set correctly in the AsCommand attribute.');
     }
 
     public function testShouldHaveExpectedOptions()
@@ -185,7 +197,7 @@ private function createContextWithoutSubscriptionConsumerMock(): InteropContext
     /**
      * @return \PHPUnit\Framework\MockObject\MockObject|InteropContext
      */
-    private function createContextStub(Consumer $consumer = null): InteropContext
+    private function createContextStub(?Consumer $consumer = null): InteropContext
     {
         $context = $this->createContextWithoutSubscriptionConsumerMock();
         $context
diff --git a/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php b/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php
index 19b9d76..d54a70e 100644
--- a/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php
@@ -29,13 +29,6 @@ public function testCouldBeConstructedWithRequiredAttributes()
         new SimpleConsumeCommand($this->createQueueConsumerMock());
     }
 
-    public function testShouldHaveCommandName()
-    {
-        $command = new SimpleConsumeCommand($this->createQueueConsumerMock());
-
-        $this->assertEquals('enqueue:transport:consume', $command->getName());
-    }
-
     public function testShouldHaveExpectedOptions()
     {
         $command = new SimpleConsumeCommand($this->createQueueConsumerMock());

From 370b303b8bbbb2b03300247cd7283c084d11f0d4 Mon Sep 17 00:00:00 2001
From: Viktor Zavitkov <zavitkov@icloud.com>
Date: Thu, 14 Nov 2024 15:24:42 +0300
Subject: [PATCH 329/337] fix code style

---
 Symfony/Client/ConsumeCommand.php                  | 4 ++--
 Symfony/Client/ProduceCommand.php                  | 2 +-
 Symfony/Client/RoutesCommand.php                   | 2 +-
 Symfony/Client/SetupBrokerCommand.php              | 2 +-
 Symfony/Consumption/ConfigurableConsumeCommand.php | 4 ++--
 Symfony/Consumption/ConsumeCommand.php             | 2 +-
 6 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/Symfony/Client/ConsumeCommand.php b/Symfony/Client/ConsumeCommand.php
index c97dd3a..5a06767 100644
--- a/Symfony/Client/ConsumeCommand.php
+++ b/Symfony/Client/ConsumeCommand.php
@@ -58,7 +58,7 @@ public function __construct(
         string $defaultClient,
         string $queueConsumerIdPattern = 'enqueue.client.%s.queue_consumer',
         string $driverIdPattern = 'enqueue.client.%s.driver',
-        string $processorIdPatter = 'enqueue.client.%s.delegate_processor'
+        string $processorIdPatter = 'enqueue.client.%s.delegate_processor',
     ) {
         $this->container = $container;
         $this->defaultClient = $defaultClient;
@@ -66,7 +66,7 @@ public function __construct(
         $this->driverIdPattern = $driverIdPattern;
         $this->processorIdPattern = $processorIdPatter;
 
-        parent::__construct(self::$defaultName);
+        parent::__construct();
     }
 
     protected function configure(): void
diff --git a/Symfony/Client/ProduceCommand.php b/Symfony/Client/ProduceCommand.php
index 512dd5f..bd23c16 100644
--- a/Symfony/Client/ProduceCommand.php
+++ b/Symfony/Client/ProduceCommand.php
@@ -37,7 +37,7 @@ public function __construct(ContainerInterface $container, string $defaultClient
         $this->defaultClient = $defaultClient;
         $this->producerIdPattern = $producerIdPattern;
 
-        parent::__construct(static::$defaultName);
+        parent::__construct();
     }
 
     protected function configure(): void
diff --git a/Symfony/Client/RoutesCommand.php b/Symfony/Client/RoutesCommand.php
index 99979d5..0646e37 100644
--- a/Symfony/Client/RoutesCommand.php
+++ b/Symfony/Client/RoutesCommand.php
@@ -43,7 +43,7 @@ public function __construct(ContainerInterface $container, string $defaultClient
         $this->defaultClient = $defaultClient;
         $this->driverIdPatter = $driverIdPatter;
 
-        parent::__construct(static::$defaultName);
+        parent::__construct();
     }
 
     protected function configure(): void
diff --git a/Symfony/Client/SetupBrokerCommand.php b/Symfony/Client/SetupBrokerCommand.php
index 78bb404..b74902a 100644
--- a/Symfony/Client/SetupBrokerCommand.php
+++ b/Symfony/Client/SetupBrokerCommand.php
@@ -36,7 +36,7 @@ public function __construct(ContainerInterface $container, string $defaultClient
         $this->defaultClient = $defaultClient;
         $this->driverIdPattern = $driverIdPattern;
 
-        parent::__construct(static::$defaultName);
+        parent::__construct();
     }
 
     protected function configure(): void
diff --git a/Symfony/Consumption/ConfigurableConsumeCommand.php b/Symfony/Consumption/ConfigurableConsumeCommand.php
index 2847ca5..230a0dc 100644
--- a/Symfony/Consumption/ConfigurableConsumeCommand.php
+++ b/Symfony/Consumption/ConfigurableConsumeCommand.php
@@ -46,14 +46,14 @@ public function __construct(
         ContainerInterface $container,
         string $defaultTransport,
         string $queueConsumerIdPattern = 'enqueue.transport.%s.queue_consumer',
-        string $processorRegistryIdPattern = 'enqueue.transport.%s.processor_registry'
+        string $processorRegistryIdPattern = 'enqueue.transport.%s.processor_registry',
     ) {
         $this->container = $container;
         $this->defaultTransport = $defaultTransport;
         $this->queueConsumerIdPattern = $queueConsumerIdPattern;
         $this->processorRegistryIdPattern = $processorRegistryIdPattern;
 
-        parent::__construct(static::$defaultName);
+        parent::__construct();
     }
 
     protected function configure(): void
diff --git a/Symfony/Consumption/ConsumeCommand.php b/Symfony/Consumption/ConsumeCommand.php
index c8de790..1c82f3c 100644
--- a/Symfony/Consumption/ConsumeCommand.php
+++ b/Symfony/Consumption/ConsumeCommand.php
@@ -41,7 +41,7 @@ public function __construct(ContainerInterface $container, string $defaultTransp
         $this->defaultTransport = $defaultTransport;
         $this->queueConsumerIdPattern = $queueConsumerIdPattern;
 
-        parent::__construct(static::$defaultName);
+        parent::__construct();
     }
 
     protected function configure(): void

From 9b71c6b8079a670142f583650365120847d4f42c Mon Sep 17 00:00:00 2001
From: Viktor Zavitkov <zavitkov@icloud.com>
Date: Fri, 15 Nov 2024 11:35:41 +0300
Subject: [PATCH 330/337] fix phpstan

---
 Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
index 84d6dff..e697b4a 100644
--- a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
@@ -204,7 +204,7 @@ public function testShouldExecuteConsumptionWithSeveralCustomQueues()
 
     public function testShouldExecuteConsumptionWhenProcessorImplementsQueueSubscriberInterface()
     {
-        $processor = new class implements Processor, QueueSubscriberInterface {
+        $processor = new class() implements Processor, QueueSubscriberInterface {
             public function process(InteropMessage $message, Context $context)
             {
             }

From 5d37d8b3fe09912cabd69fdb569b49ee66ceaa99 Mon Sep 17 00:00:00 2001
From: Viktor Zavitkov <zavitkov@icloud.com>
Date: Fri, 15 Nov 2024 15:23:39 +0300
Subject: [PATCH 331/337] fix code style and php8.2 sf7 dependencies

---
 Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
index e697b4a..c448c1c 100644
--- a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
@@ -204,8 +204,8 @@ public function testShouldExecuteConsumptionWithSeveralCustomQueues()
 
     public function testShouldExecuteConsumptionWhenProcessorImplementsQueueSubscriberInterface()
     {
-        $processor = new class() implements Processor, QueueSubscriberInterface {
-            public function process(InteropMessage $message, Context $context)
+        $processor = new class implements Processor, QueueSubscriberInterface {
+            public function process(InteropMessage $message, Context $context): void
             {
             }
 

From 82d23fb85816d81efb83cb6d30cf4086bb706b19 Mon Sep 17 00:00:00 2001
From: James Read <james.read.18@gmail.com>
Date: Sat, 11 Jan 2025 20:47:49 +0000
Subject: [PATCH 332/337] Fixing risky tests

Makring tests that have no assertions with the annotation to suppress
phpunits warning around risky tests

closes 1370
---
 Tests/ArrayProcessorRegistryTest.php                     | 3 +++
 Tests/Client/ChainExtensionTest.php                      | 3 +++
 .../DelayRedeliveredMessageExtensionTest.php             | 3 +++
 .../ExclusiveCommandExtensionTest.php                    | 3 +++
 .../FlushSpoolProducerExtensionTest.php                  | 3 +++
 Tests/Client/ConsumptionExtension/LogExtensionTest.php   | 3 +++
 .../SetRouterPropertiesExtensionTest.php                 | 3 +++
 .../ConsumptionExtension/SetupBrokerExtensionTest.php    | 3 +++
 Tests/Client/DelegateProcessorTest.php                   | 3 +++
 Tests/Client/DriverFactoryTest.php                       | 3 +++
 Tests/Client/DriverPreSendTest.php                       | 3 +++
 Tests/Client/Extension/PrepareBodyExtensionTest.php      | 3 +++
 Tests/Client/PostSendTest.php                            | 3 +++
 Tests/Client/PreSendTest.php                             | 3 +++
 Tests/Client/ProducerTest.php                            | 6 ++++++
 Tests/Client/SpoolProducerTest.php                       | 3 +++
 Tests/Client/TraceableProducerTest.php                   | 3 +++
 Tests/ConnectionFactoryFactoryTest.php                   | 9 +++++++++
 Tests/Consumption/CallbackProcessorTest.php              | 3 +++
 Tests/Consumption/ChainExtensionTest.php                 | 3 +++
 .../IllegalContextModificationExceptionTest.php          | 3 +++
 .../Exception/InvalidArgumentExceptionTest.php           | 6 ++++++
 Tests/Consumption/Exception/LogicExceptionTest.php       | 3 +++
 .../Extension/LimitConsumedMessagesExtensionTest.php     | 3 +++
 .../Extension/LimitConsumerMemoryExtensionTest.php       | 3 +++
 .../Extension/LimitConsumptionTimeExtensionTest.php      | 3 +++
 Tests/Consumption/Extension/LogExtensionTest.php         | 3 +++
 Tests/Consumption/Extension/LoggerExtensionTest.php      | 3 +++
 Tests/Consumption/Extension/NicenessExtensionTest.php    | 3 +++
 Tests/Consumption/Extension/ReplyExtensionTest.php       | 3 +++
 Tests/Consumption/FallbackSubscriptionConsumerTest.php   | 6 ++++++
 Tests/Consumption/QueueConsumerTest.php                  | 9 +++++++++
 Tests/Router/RouteRecipientListProcessorTest.php         | 3 +++
 Tests/Rpc/RpcClientTest.php                              | 3 +++
 Tests/Symfony/Client/ConsumeCommandTest.php              | 3 +++
 .../AnalyzeRouteCollectionPassTest.php                   | 3 +++
 .../BuildClientExtensionsPassTest.php                    | 3 +++
 .../BuildCommandSubscriberRoutesPassTest.php             | 3 +++
 .../BuildConsumptionExtensionsPassTest.php               | 3 +++
 .../BuildProcessorRegistryPassTest.php                   | 3 +++
 .../DependencyInjection/BuildProcessorRoutesPassTest.php | 3 +++
 .../BuildTopicSubscriberRoutesPassTest.php               | 3 +++
 Tests/Symfony/Client/FlushSpoolProducerListenerTest.php  | 3 +++
 Tests/Symfony/Client/ProduceCommandTest.php              | 3 +++
 Tests/Symfony/Client/RoutesCommandTest.php               | 3 +++
 Tests/Symfony/Client/SetupBrokerCommandTest.php          | 3 +++
 Tests/Symfony/Client/SimpleConsumeCommandTest.php        | 3 +++
 Tests/Symfony/Client/SimpleProduceCommandTest.php        | 3 +++
 Tests/Symfony/Client/SimpleRoutesCommandTest.php         | 3 +++
 Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php    | 3 +++
 .../Consumption/ConfigurableConsumeCommandTest.php       | 3 +++
 Tests/Symfony/Consumption/ConsumeCommandTest.php         | 3 +++
 Tests/Symfony/Consumption/SimpleConsumeCommandTest.php   | 3 +++
 Tests/Symfony/ContainerProcessorRegistryTest.php         | 3 +++
 .../BuildConsumptionExtensionsPassTest.php               | 3 +++
 .../BuildProcessorRegistryPassTest.php                   | 3 +++
 Tests/Symfony/LazyProducerTest.php                       | 3 +++
 Tests/Util/VarExportTest.php                             | 3 +++
 58 files changed, 195 insertions(+)

diff --git a/Tests/ArrayProcessorRegistryTest.php b/Tests/ArrayProcessorRegistryTest.php
index 897276d..c093754 100644
--- a/Tests/ArrayProcessorRegistryTest.php
+++ b/Tests/ArrayProcessorRegistryTest.php
@@ -18,6 +18,9 @@ public function testShouldImplementProcessorRegistryInterface()
         $this->assertClassImplements(ProcessorRegistryInterface::class, ArrayProcessorRegistry::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutAnyArgument()
     {
         new ArrayProcessorRegistry();
diff --git a/Tests/Client/ChainExtensionTest.php b/Tests/Client/ChainExtensionTest.php
index a59005b..c5dc604 100644
--- a/Tests/Client/ChainExtensionTest.php
+++ b/Tests/Client/ChainExtensionTest.php
@@ -29,6 +29,9 @@ public function testShouldBeFinal()
         $this->assertClassFinal(ChainExtension::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithExtensionsArray()
     {
         new ChainExtension([$this->createExtension(), $this->createExtension()]);
diff --git a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
index 0481d27..6d50246 100644
--- a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
@@ -23,6 +23,9 @@
 
 class DelayRedeliveredMessageExtensionTest extends TestCase
 {
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithRequiredArguments()
     {
         new DelayRedeliveredMessageExtension($this->createDriverMock(), 12345);
diff --git a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
index dc2c00c..9a2270d 100644
--- a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
@@ -34,6 +34,9 @@ public function testShouldBeFinal()
         $this->assertClassFinal(ExclusiveCommandExtension::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithDriverAsFirstArgument()
     {
         new ExclusiveCommandExtension($this->createDriverStub());
diff --git a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
index 63fb50c..d69969b 100644
--- a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
@@ -30,6 +30,9 @@ public function testShouldImplementEndExtensionInterface()
         $this->assertClassImplements(EndExtensionInterface::class, FlushSpoolProducerExtension::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithSpoolProducerAsFirstArgument()
     {
         new FlushSpoolProducerExtension($this->createSpoolProducerMock());
diff --git a/Tests/Client/ConsumptionExtension/LogExtensionTest.php b/Tests/Client/ConsumptionExtension/LogExtensionTest.php
index 063ea23..fe9e6ab 100644
--- a/Tests/Client/ConsumptionExtension/LogExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/LogExtensionTest.php
@@ -55,6 +55,9 @@ public function testShouldSubClassOfLogExtension()
         $this->assertClassExtends(\Enqueue\Consumption\Extension\LogExtension::class, LogExtension::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutAnyArguments()
     {
         new LogExtension();
diff --git a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
index e5015ff..5337d04 100644
--- a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
@@ -27,6 +27,9 @@ public function testShouldImplementMessageReceivedExtensionInterface()
         $this->assertClassImplements(MessageReceivedExtensionInterface::class, SetRouterPropertiesExtension::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithRequiredArguments()
     {
         new SetRouterPropertiesExtension($this->createDriverMock());
diff --git a/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php b/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php
index 4b241aa..a72ae38 100644
--- a/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php
@@ -21,6 +21,9 @@ public function testShouldImplementStartExtensionInterface()
         $this->assertClassImplements(StartExtensionInterface::class, SetupBrokerExtension::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithRequiredArguments()
     {
         new SetupBrokerExtension($this->createDriverMock());
diff --git a/Tests/Client/DelegateProcessorTest.php b/Tests/Client/DelegateProcessorTest.php
index 40ef179..f9bd83e 100644
--- a/Tests/Client/DelegateProcessorTest.php
+++ b/Tests/Client/DelegateProcessorTest.php
@@ -13,6 +13,9 @@
 
 class DelegateProcessorTest extends TestCase
 {
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithRequiredArguments()
     {
         new DelegateProcessor($this->createProcessorRegistryMock());
diff --git a/Tests/Client/DriverFactoryTest.php b/Tests/Client/DriverFactoryTest.php
index cce6311..605358a 100644
--- a/Tests/Client/DriverFactoryTest.php
+++ b/Tests/Client/DriverFactoryTest.php
@@ -61,6 +61,9 @@ public function testShouldBeFinal()
         $this->assertTrue($rc->isFinal());
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutAnyArguments()
     {
         new DriverFactory();
diff --git a/Tests/Client/DriverPreSendTest.php b/Tests/Client/DriverPreSendTest.php
index 85686b4..f3c1ee3 100644
--- a/Tests/Client/DriverPreSendTest.php
+++ b/Tests/Client/DriverPreSendTest.php
@@ -19,6 +19,9 @@ public function testShouldBeFinal()
         self::assertClassFinal(DriverPreSend::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithExpectedArguments()
     {
         new DriverPreSend(
diff --git a/Tests/Client/Extension/PrepareBodyExtensionTest.php b/Tests/Client/Extension/PrepareBodyExtensionTest.php
index 454034a..81b29e8 100644
--- a/Tests/Client/Extension/PrepareBodyExtensionTest.php
+++ b/Tests/Client/Extension/PrepareBodyExtensionTest.php
@@ -22,6 +22,9 @@ public function testShouldImplementExtensionInterface()
         $this->assertTrue($rc->implementsInterface(PreSendCommandExtensionInterface::class));
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldConstructedWithoutAnyArguments()
     {
         new PrepareBodyExtension();
diff --git a/Tests/Client/PostSendTest.php b/Tests/Client/PostSendTest.php
index 0492770..9eb845e 100644
--- a/Tests/Client/PostSendTest.php
+++ b/Tests/Client/PostSendTest.php
@@ -21,6 +21,9 @@ public function testShouldBeFinal()
         self::assertClassFinal(PostSend::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithExpectedArguments()
     {
         new PostSend(
diff --git a/Tests/Client/PreSendTest.php b/Tests/Client/PreSendTest.php
index 408be1c..40dffaf 100644
--- a/Tests/Client/PreSendTest.php
+++ b/Tests/Client/PreSendTest.php
@@ -18,6 +18,9 @@ public function testShouldBeFinal()
         self::assertClassFinal(PreSend::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithExpectedArguments()
     {
         new PreSend(
diff --git a/Tests/Client/ProducerTest.php b/Tests/Client/ProducerTest.php
index 558e70e..38dd1f0 100644
--- a/Tests/Client/ProducerTest.php
+++ b/Tests/Client/ProducerTest.php
@@ -24,11 +24,17 @@ public function testShouldBeFinal()
         self::assertClassFinal(Producer::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithRequiredArguments()
     {
         new Producer($this->createDriverMock(), $this->createRpcFactoryMock());
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithOptionalArguments()
     {
         new Producer(
diff --git a/Tests/Client/SpoolProducerTest.php b/Tests/Client/SpoolProducerTest.php
index 024fd6d..511b91e 100644
--- a/Tests/Client/SpoolProducerTest.php
+++ b/Tests/Client/SpoolProducerTest.php
@@ -18,6 +18,9 @@ public function testShouldImplementProducerInterface()
         self::assertClassImplements(ProducerInterface::class, SpoolProducer::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithRealProducer()
     {
         new SpoolProducer($this->createProducerMock());
diff --git a/Tests/Client/TraceableProducerTest.php b/Tests/Client/TraceableProducerTest.php
index ac0a529..3ce23f8 100644
--- a/Tests/Client/TraceableProducerTest.php
+++ b/Tests/Client/TraceableProducerTest.php
@@ -18,6 +18,9 @@ public function testShouldImplementProducerInterface()
         $this->assertClassImplements(ProducerInterface::class, TraceableProducer::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithInternalMessageProducer()
     {
         new TraceableProducer($this->createProducerMock());
diff --git a/Tests/ConnectionFactoryFactoryTest.php b/Tests/ConnectionFactoryFactoryTest.php
index 3fb4fb5..c522c63 100644
--- a/Tests/ConnectionFactoryFactoryTest.php
+++ b/Tests/ConnectionFactoryFactoryTest.php
@@ -37,11 +37,17 @@ public function testShouldBeFinal()
         $this->assertTrue($rc->isFinal());
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutAnyArguments()
     {
         new ConnectionFactoryFactory();
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testShouldAcceptStringDSN()
     {
         $factory = new ConnectionFactoryFactory();
@@ -49,6 +55,9 @@ public function testShouldAcceptStringDSN()
         $factory->create('null:');
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testShouldAcceptArrayWithDsnKey()
     {
         $factory = new ConnectionFactoryFactory();
diff --git a/Tests/Consumption/CallbackProcessorTest.php b/Tests/Consumption/CallbackProcessorTest.php
index f134cfe..9cafdb8 100644
--- a/Tests/Consumption/CallbackProcessorTest.php
+++ b/Tests/Consumption/CallbackProcessorTest.php
@@ -18,6 +18,9 @@ public function testShouldImplementProcessorInterface()
         $this->assertClassImplements(Processor::class, CallbackProcessor::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithCallableAsArgument()
     {
         new CallbackProcessor(function () {
diff --git a/Tests/Consumption/ChainExtensionTest.php b/Tests/Consumption/ChainExtensionTest.php
index 23bd4f9..c19cc2f 100644
--- a/Tests/Consumption/ChainExtensionTest.php
+++ b/Tests/Consumption/ChainExtensionTest.php
@@ -33,6 +33,9 @@ public function testShouldImplementExtensionInterface()
         $this->assertClassImplements(ExtensionInterface::class, ChainExtension::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithExtensionsArray()
     {
         new ChainExtension([$this->createExtension(), $this->createExtension()]);
diff --git a/Tests/Consumption/Exception/IllegalContextModificationExceptionTest.php b/Tests/Consumption/Exception/IllegalContextModificationExceptionTest.php
index 0885b50..5de5c2f 100644
--- a/Tests/Consumption/Exception/IllegalContextModificationExceptionTest.php
+++ b/Tests/Consumption/Exception/IllegalContextModificationExceptionTest.php
@@ -21,6 +21,9 @@ public function testShouldExtendLogicException()
         $this->assertClassExtends(\LogicException::class, IllegalContextModificationException::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutAnyArguments()
     {
         new IllegalContextModificationException();
diff --git a/Tests/Consumption/Exception/InvalidArgumentExceptionTest.php b/Tests/Consumption/Exception/InvalidArgumentExceptionTest.php
index 296c762..cc2ca31 100644
--- a/Tests/Consumption/Exception/InvalidArgumentExceptionTest.php
+++ b/Tests/Consumption/Exception/InvalidArgumentExceptionTest.php
@@ -21,6 +21,9 @@ public function testShouldExtendLogicException()
         $this->assertClassExtends(\LogicException::class, InvalidArgumentException::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutAnyArguments()
     {
         new InvalidArgumentException();
@@ -36,6 +39,9 @@ public function testThrowIfAssertInstanceOfNotSameAsExpected()
         InvalidArgumentException::assertInstanceOf(new \SplStack(), \SplQueue::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testShouldDoNothingIfAssertDestinationInstanceOfSameAsExpected()
     {
         InvalidArgumentException::assertInstanceOf(new \SplQueue(), \SplQueue::class);
diff --git a/Tests/Consumption/Exception/LogicExceptionTest.php b/Tests/Consumption/Exception/LogicExceptionTest.php
index ddd2580..f7a68c9 100644
--- a/Tests/Consumption/Exception/LogicExceptionTest.php
+++ b/Tests/Consumption/Exception/LogicExceptionTest.php
@@ -21,6 +21,9 @@ public function testShouldExtendLogicException()
         $this->assertClassExtends(\LogicException::class, LogicException::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutAnyArguments()
     {
         new LogicException();
diff --git a/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php b/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
index e1d4c50..15d2214 100644
--- a/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
@@ -16,6 +16,9 @@
 
 class LimitConsumedMessagesExtensionTest extends TestCase
 {
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithRequiredArguments()
     {
         new LimitConsumedMessagesExtension(12345);
diff --git a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
index 5a95486..22d3382 100644
--- a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
@@ -17,6 +17,9 @@
 
 class LimitConsumerMemoryExtensionTest extends TestCase
 {
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithRequiredArguments()
     {
         new LimitConsumerMemoryExtension(12345);
diff --git a/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php b/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
index fa730e3..fbd61d4 100644
--- a/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
@@ -17,6 +17,9 @@
 
 class LimitConsumptionTimeExtensionTest extends TestCase
 {
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithRequiredArguments()
     {
         new LimitConsumptionTimeExtension(new \DateTime('+1 day'));
diff --git a/Tests/Consumption/Extension/LogExtensionTest.php b/Tests/Consumption/Extension/LogExtensionTest.php
index 4bc4ae9..f529dcd 100644
--- a/Tests/Consumption/Extension/LogExtensionTest.php
+++ b/Tests/Consumption/Extension/LogExtensionTest.php
@@ -49,6 +49,9 @@ public function testShouldImplementPostMessageReceivedExtensionInterface()
         $this->assertClassImplements(PostMessageReceivedExtensionInterface::class, LogExtension::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutAnyArguments()
     {
         new LogExtension();
diff --git a/Tests/Consumption/Extension/LoggerExtensionTest.php b/Tests/Consumption/Extension/LoggerExtensionTest.php
index f3641ad..227c083 100644
--- a/Tests/Consumption/Extension/LoggerExtensionTest.php
+++ b/Tests/Consumption/Extension/LoggerExtensionTest.php
@@ -20,6 +20,9 @@ public function testShouldImplementInitLoggerExtensionInterface()
         $this->assertClassImplements(InitLoggerExtensionInterface::class, LoggerExtension::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithLoggerAsFirstArgument()
     {
         new LoggerExtension($this->createLogger());
diff --git a/Tests/Consumption/Extension/NicenessExtensionTest.php b/Tests/Consumption/Extension/NicenessExtensionTest.php
index a2937d0..15c004d 100644
--- a/Tests/Consumption/Extension/NicenessExtensionTest.php
+++ b/Tests/Consumption/Extension/NicenessExtensionTest.php
@@ -11,6 +11,9 @@
 
 class NicenessExtensionTest extends TestCase
 {
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithRequiredArguments()
     {
         new NicenessExtension(0);
diff --git a/Tests/Consumption/Extension/ReplyExtensionTest.php b/Tests/Consumption/Extension/ReplyExtensionTest.php
index 4088956..3963e94 100644
--- a/Tests/Consumption/Extension/ReplyExtensionTest.php
+++ b/Tests/Consumption/Extension/ReplyExtensionTest.php
@@ -25,6 +25,9 @@ public function testShouldImplementPostMessageReceivedExtensionInterface()
         $this->assertClassImplements(PostMessageReceivedExtensionInterface::class, ReplyExtension::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutAnyArguments()
     {
         new ReplyExtension();
diff --git a/Tests/Consumption/FallbackSubscriptionConsumerTest.php b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
index 4f8ac7a..aecf716 100644
--- a/Tests/Consumption/FallbackSubscriptionConsumerTest.php
+++ b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
@@ -21,6 +21,9 @@ public function testShouldImplementSubscriptionConsumerInterface()
         $this->assertTrue($rc->implementsInterface(SubscriptionConsumer::class));
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutAnyArguments()
     {
         new FallbackSubscriptionConsumer();
@@ -69,6 +72,9 @@ public function testThrowsIfTrySubscribeAnotherConsumerToAlreadySubscribedQueue(
         $subscriptionConsumer->subscribe($barConsumer, $barCallback);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testShouldAllowSubscribeSameConsumerAndCallbackSecondTime()
     {
         $subscriptionConsumer = new FallbackSubscriptionConsumer();
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index ecf7f9a..ce72cb9 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -39,16 +39,25 @@ class QueueConsumerTest extends TestCase
 {
     use ReadAttributeTrait;
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithAllArguments()
     {
         new QueueConsumer($this->createContextStub(), null, [], null, 0);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithContextOnly()
     {
         new QueueConsumer($this->createContextStub());
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithContextAndSingleExtension()
     {
         new QueueConsumer($this->createContextStub(), $this->createExtension());
diff --git a/Tests/Router/RouteRecipientListProcessorTest.php b/Tests/Router/RouteRecipientListProcessorTest.php
index 8a2e089..7e38f96 100644
--- a/Tests/Router/RouteRecipientListProcessorTest.php
+++ b/Tests/Router/RouteRecipientListProcessorTest.php
@@ -23,6 +23,9 @@ public function testShouldImplementProcessorInterface()
         $this->assertClassImplements(Processor::class, RouteRecipientListProcessor::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithRouterAsFirstArgument()
     {
         new RouteRecipientListProcessor($this->createRecipientListRouterMock());
diff --git a/Tests/Rpc/RpcClientTest.php b/Tests/Rpc/RpcClientTest.php
index e2035c4..e8a3720 100644
--- a/Tests/Rpc/RpcClientTest.php
+++ b/Tests/Rpc/RpcClientTest.php
@@ -15,6 +15,9 @@
 
 class RpcClientTest extends TestCase
 {
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithContextAsFirstArgument()
     {
         new RpcClient($this->createContextMock());
diff --git a/Tests/Symfony/Client/ConsumeCommandTest.php b/Tests/Symfony/Client/ConsumeCommandTest.php
index 8076aaf..17be30a 100644
--- a/Tests/Symfony/Client/ConsumeCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeCommandTest.php
@@ -40,6 +40,9 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(ConsumeCommand::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithRequiredAttributes()
     {
         new ConsumeCommand($this->createMock(ContainerInterface::class), 'default');
diff --git a/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php b/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php
index 8dcffe4..fc3051b 100644
--- a/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php
@@ -23,6 +23,9 @@ public function testShouldBeFinal()
         $this->assertClassFinal(AnalyzeRouteCollectionPass::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutArguments()
     {
         new AnalyzeRouteCollectionPass();
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php
index 90c7493..baae3c7 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php
@@ -25,6 +25,9 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildClientExtensionsPass::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutArguments()
     {
         new BuildClientExtensionsPass();
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
index a2e964d..7317a8e 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
@@ -29,6 +29,9 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildCommandSubscriberRoutesPass::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutArguments()
     {
         new BuildCommandSubscriberRoutesPass();
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php
index a946362..64966d6 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php
@@ -25,6 +25,9 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildConsumptionExtensionsPass::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutArguments()
     {
         new BuildConsumptionExtensionsPass();
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php
index 8541f39..98679ab 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php
@@ -26,6 +26,9 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildProcessorRegistryPass::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutArguments()
     {
         new BuildProcessorRegistryPass();
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php
index b2839c4..d4ac298 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php
@@ -25,6 +25,9 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildProcessorRoutesPass::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutArguments()
     {
         new BuildProcessorRoutesPass();
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
index af1c6a3..0ada606 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
@@ -29,6 +29,9 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildTopicSubscriberRoutesPass::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutArguments()
     {
         new BuildTopicSubscriberRoutesPass();
diff --git a/Tests/Symfony/Client/FlushSpoolProducerListenerTest.php b/Tests/Symfony/Client/FlushSpoolProducerListenerTest.php
index 3476c10..feae3b5 100644
--- a/Tests/Symfony/Client/FlushSpoolProducerListenerTest.php
+++ b/Tests/Symfony/Client/FlushSpoolProducerListenerTest.php
@@ -39,6 +39,9 @@ public function testShouldSubscribeOnConsoleTerminateEvent()
         $this->assertEquals('flushMessages', $events[ConsoleEvents::TERMINATE]);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithSpoolProducerAsFirstArgument()
     {
         new FlushSpoolProducerListener($this->createSpoolProducerMock());
diff --git a/Tests/Symfony/Client/ProduceCommandTest.php b/Tests/Symfony/Client/ProduceCommandTest.php
index 2efc476..b6a1d18 100644
--- a/Tests/Symfony/Client/ProduceCommandTest.php
+++ b/Tests/Symfony/Client/ProduceCommandTest.php
@@ -27,6 +27,9 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(ProduceCommand::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithContainerAsFirstArgument()
     {
         new ProduceCommand($this->createMock(ContainerInterface::class), 'default');
diff --git a/Tests/Symfony/Client/RoutesCommandTest.php b/Tests/Symfony/Client/RoutesCommandTest.php
index c2168c4..8adf710 100644
--- a/Tests/Symfony/Client/RoutesCommandTest.php
+++ b/Tests/Symfony/Client/RoutesCommandTest.php
@@ -29,6 +29,9 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(RoutesCommand::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithConfigAndRouteCollectionAsArguments()
     {
         new RoutesCommand($this->createMock(ContainerInterface::class), 'default');
diff --git a/Tests/Symfony/Client/SetupBrokerCommandTest.php b/Tests/Symfony/Client/SetupBrokerCommandTest.php
index 89ca379..53a8d44 100644
--- a/Tests/Symfony/Client/SetupBrokerCommandTest.php
+++ b/Tests/Symfony/Client/SetupBrokerCommandTest.php
@@ -26,6 +26,9 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(SetupBrokerCommand::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithContainerAsFirstArgument()
     {
         new SetupBrokerCommand($this->createMock(ContainerInterface::class), 'default');
diff --git a/Tests/Symfony/Client/SimpleConsumeCommandTest.php b/Tests/Symfony/Client/SimpleConsumeCommandTest.php
index 60d4093..97f1c6c 100644
--- a/Tests/Symfony/Client/SimpleConsumeCommandTest.php
+++ b/Tests/Symfony/Client/SimpleConsumeCommandTest.php
@@ -29,6 +29,9 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(SimpleConsumeCommand::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithRequiredAttributes()
     {
         new SimpleConsumeCommand($this->createQueueConsumerMock(), $this->createDriverStub(), $this->createDelegateProcessorMock());
diff --git a/Tests/Symfony/Client/SimpleProduceCommandTest.php b/Tests/Symfony/Client/SimpleProduceCommandTest.php
index 2775870..50d46c9 100644
--- a/Tests/Symfony/Client/SimpleProduceCommandTest.php
+++ b/Tests/Symfony/Client/SimpleProduceCommandTest.php
@@ -23,6 +23,9 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(SimpleProduceCommand::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithContainerAsFirstArgument()
     {
         new SimpleProduceCommand($this->createProducerMock());
diff --git a/Tests/Symfony/Client/SimpleRoutesCommandTest.php b/Tests/Symfony/Client/SimpleRoutesCommandTest.php
index 356f3f9..71bb5cf 100644
--- a/Tests/Symfony/Client/SimpleRoutesCommandTest.php
+++ b/Tests/Symfony/Client/SimpleRoutesCommandTest.php
@@ -25,6 +25,9 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(SimpleRoutesCommand::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithConfigAndRouteCollectionAsArguments()
     {
         new SimpleRoutesCommand($this->createDriverMock());
diff --git a/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php b/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php
index 91802ff..d3e8f21 100644
--- a/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php
+++ b/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php
@@ -23,6 +23,9 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(SimpleSetupBrokerCommand::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithContainerAsFirstArgument()
     {
         new SimpleSetupBrokerCommand($this->createClientDriverMock());
diff --git a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
index c448c1c..82342e4 100644
--- a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
@@ -33,6 +33,9 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(ConfigurableConsumeCommand::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithRequiredAttributes()
     {
         new ConfigurableConsumeCommand($this->createMock(ContainerInterface::class), 'default');
diff --git a/Tests/Symfony/Consumption/ConsumeCommandTest.php b/Tests/Symfony/Consumption/ConsumeCommandTest.php
index 3c16cde..b56ef68 100644
--- a/Tests/Symfony/Consumption/ConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConsumeCommandTest.php
@@ -35,6 +35,9 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(ConsumeCommand::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithRequiredAttributes()
     {
         new ConsumeCommand($this->createMock(ContainerInterface::class), 'default');
diff --git a/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php b/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php
index d54a70e..7648587 100644
--- a/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php
@@ -24,6 +24,9 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(SimpleConsumeCommand::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithRequiredAttributes()
     {
         new SimpleConsumeCommand($this->createQueueConsumerMock());
diff --git a/Tests/Symfony/ContainerProcessorRegistryTest.php b/Tests/Symfony/ContainerProcessorRegistryTest.php
index e0dcf2e..1370b4d 100644
--- a/Tests/Symfony/ContainerProcessorRegistryTest.php
+++ b/Tests/Symfony/ContainerProcessorRegistryTest.php
@@ -23,6 +23,9 @@ public function testShouldBeFinal()
         $this->assertClassFinal(ContainerProcessorRegistry::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithContainerAsFirstArgument()
     {
         new ContainerProcessorRegistry($this->createContainerMock());
diff --git a/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php b/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
index f16daf0..c558aad 100644
--- a/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
+++ b/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
@@ -25,6 +25,9 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildConsumptionExtensionsPass::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutArguments()
     {
         new BuildConsumptionExtensionsPass();
diff --git a/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php b/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
index 8d8edc5..e3a3330 100644
--- a/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
+++ b/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
@@ -26,6 +26,9 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildProcessorRegistryPass::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithoutArguments()
     {
         new BuildProcessorRegistryPass();
diff --git a/Tests/Symfony/LazyProducerTest.php b/Tests/Symfony/LazyProducerTest.php
index 1b15ce2..7fbe714 100644
--- a/Tests/Symfony/LazyProducerTest.php
+++ b/Tests/Symfony/LazyProducerTest.php
@@ -20,6 +20,9 @@ public function testShouldImplementProducerInterface()
         $this->assertClassImplements(ProducerInterface::class, LazyProducer::class);
     }
 
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithContainerAndServiceId()
     {
         new LazyProducer($this->createContainerMock(), 'realProducerId');
diff --git a/Tests/Util/VarExportTest.php b/Tests/Util/VarExportTest.php
index 1d2384a..a8fa92f 100644
--- a/Tests/Util/VarExportTest.php
+++ b/Tests/Util/VarExportTest.php
@@ -7,6 +7,9 @@
 
 class VarExportTest extends TestCase
 {
+    /**
+     * @doesNotPerformAssertions
+     */
     public function testCouldBeConstructedWithValueAsArgument()
     {
         new VarExport('aVal');

From 3ef320658a1fc59fc92b54d8afa161718a6212b4 Mon Sep 17 00:00:00 2001
From: James Read <james.read.18@gmail.com>
Date: Wed, 15 Jan 2025 21:17:06 +0000
Subject: [PATCH 333/337] Removing constructor tests

Removing all tests that creates a new instance but doesn't call any
methods nor preform any assertions.
---
 Tests/ArrayProcessorRegistryTest.php          |  8 -------
 Tests/Client/ChainExtensionTest.php           |  8 -------
 .../DelayRedeliveredMessageExtensionTest.php  |  7 ------
 .../ExclusiveCommandExtensionTest.php         |  8 -------
 .../FlushSpoolProducerExtensionTest.php       |  8 -------
 .../ConsumptionExtension/LogExtensionTest.php |  8 -------
 .../SetRouterPropertiesExtensionTest.php      |  8 -------
 .../SetupBrokerExtensionTest.php              |  8 -------
 Tests/Client/DelegateProcessorTest.php        |  8 -------
 Tests/Client/DriverFactoryTest.php            |  8 -------
 Tests/Client/DriverPreSendTest.php            | 12 ----------
 .../Extension/PrepareBodyExtensionTest.php    |  8 -------
 Tests/Client/PostSendTest.php                 | 14 -----------
 Tests/Client/PreSendTest.php                  | 13 ----------
 Tests/Client/ProducerTest.php                 | 20 ----------------
 Tests/Client/SpoolProducerTest.php            |  8 -------
 Tests/Client/TraceableProducerTest.php        |  8 -------
 Tests/ConnectionFactoryFactoryTest.php        |  8 -------
 Tests/Consumption/CallbackProcessorTest.php   |  9 -------
 Tests/Consumption/ChainExtensionTest.php      |  8 -------
 ...llegalContextModificationExceptionTest.php |  8 -------
 .../InvalidArgumentExceptionTest.php          |  8 -------
 .../Exception/LogicExceptionTest.php          |  8 -------
 .../LimitConsumedMessagesExtensionTest.php    |  8 -------
 .../LimitConsumerMemoryExtensionTest.php      |  8 -------
 .../LimitConsumptionTimeExtensionTest.php     |  8 -------
 .../Extension/LogExtensionTest.php            |  8 -------
 .../Extension/LoggerExtensionTest.php         |  8 -------
 .../Extension/NicenessExtensionTest.php       |  8 -------
 .../Extension/ReplyExtensionTest.php          |  8 -------
 .../FallbackSubscriptionConsumerTest.php      |  8 -------
 Tests/Consumption/QueueConsumerTest.php       | 24 -------------------
 .../RouteRecipientListProcessorTest.php       |  8 -------
 Tests/Rpc/RpcClientTest.php                   |  8 -------
 Tests/Symfony/Client/ConsumeCommandTest.php   |  8 -------
 .../AnalyzeRouteCollectionPassTest.php        |  8 -------
 .../BuildClientExtensionsPassTest.php         |  8 -------
 .../BuildCommandSubscriberRoutesPassTest.php  |  8 -------
 .../BuildConsumptionExtensionsPassTest.php    |  8 -------
 .../BuildProcessorRegistryPassTest.php        |  8 -------
 .../BuildProcessorRoutesPassTest.php          |  8 -------
 .../BuildTopicSubscriberRoutesPassTest.php    |  8 -------
 .../Client/FlushSpoolProducerListenerTest.php |  8 -------
 Tests/Symfony/Client/ProduceCommandTest.php   |  8 -------
 Tests/Symfony/Client/RoutesCommandTest.php    |  8 -------
 .../Symfony/Client/SetupBrokerCommandTest.php |  8 -------
 .../Client/SimpleConsumeCommandTest.php       |  8 -------
 .../Client/SimpleProduceCommandTest.php       |  8 -------
 .../Client/SimpleRoutesCommandTest.php        |  8 -------
 .../Client/SimpleSetupBrokerCommandTest.php   |  8 -------
 .../ConfigurableConsumeCommandTest.php        |  8 -------
 .../Consumption/ConsumeCommandTest.php        |  8 -------
 .../Consumption/SimpleConsumeCommandTest.php  |  8 -------
 .../ContainerProcessorRegistryTest.php        |  8 -------
 .../BuildConsumptionExtensionsPassTest.php    |  8 -------
 .../BuildProcessorRegistryPassTest.php        |  8 -------
 Tests/Symfony/LazyProducerTest.php            |  8 -------
 Tests/Util/VarExportTest.php                  |  8 -------
 58 files changed, 507 deletions(-)

diff --git a/Tests/ArrayProcessorRegistryTest.php b/Tests/ArrayProcessorRegistryTest.php
index c093754..93ad3ec 100644
--- a/Tests/ArrayProcessorRegistryTest.php
+++ b/Tests/ArrayProcessorRegistryTest.php
@@ -18,14 +18,6 @@ public function testShouldImplementProcessorRegistryInterface()
         $this->assertClassImplements(ProcessorRegistryInterface::class, ArrayProcessorRegistry::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutAnyArgument()
-    {
-        new ArrayProcessorRegistry();
-    }
-
     public function testShouldThrowExceptionIfProcessorIsNotSet()
     {
         $registry = new ArrayProcessorRegistry();
diff --git a/Tests/Client/ChainExtensionTest.php b/Tests/Client/ChainExtensionTest.php
index c5dc604..0f42bcf 100644
--- a/Tests/Client/ChainExtensionTest.php
+++ b/Tests/Client/ChainExtensionTest.php
@@ -29,14 +29,6 @@ public function testShouldBeFinal()
         $this->assertClassFinal(ChainExtension::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithExtensionsArray()
-    {
-        new ChainExtension([$this->createExtension(), $this->createExtension()]);
-    }
-
     public function testThrowIfArrayContainsNotExtension()
     {
         $this->expectException(\LogicException::class);
diff --git a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
index 6d50246..56b855f 100644
--- a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
@@ -23,13 +23,6 @@
 
 class DelayRedeliveredMessageExtensionTest extends TestCase
 {
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new DelayRedeliveredMessageExtension($this->createDriverMock(), 12345);
-    }
 
     public function testShouldSendDelayedMessageAndRejectOriginalMessage()
     {
diff --git a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
index 9a2270d..b1e47c8 100644
--- a/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php
@@ -34,14 +34,6 @@ public function testShouldBeFinal()
         $this->assertClassFinal(ExclusiveCommandExtension::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithDriverAsFirstArgument()
-    {
-        new ExclusiveCommandExtension($this->createDriverStub());
-    }
-
     public function testShouldDoNothingIfMessageHasTopicPropertySetOnPreReceive()
     {
         $message = new NullMessage();
diff --git a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
index d69969b..6a782c5 100644
--- a/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/FlushSpoolProducerExtensionTest.php
@@ -30,14 +30,6 @@ public function testShouldImplementEndExtensionInterface()
         $this->assertClassImplements(EndExtensionInterface::class, FlushSpoolProducerExtension::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithSpoolProducerAsFirstArgument()
-    {
-        new FlushSpoolProducerExtension($this->createSpoolProducerMock());
-    }
-
     public function testShouldFlushSpoolProducerOnEnd()
     {
         $producer = $this->createSpoolProducerMock();
diff --git a/Tests/Client/ConsumptionExtension/LogExtensionTest.php b/Tests/Client/ConsumptionExtension/LogExtensionTest.php
index fe9e6ab..db75767 100644
--- a/Tests/Client/ConsumptionExtension/LogExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/LogExtensionTest.php
@@ -55,14 +55,6 @@ public function testShouldSubClassOfLogExtension()
         $this->assertClassExtends(\Enqueue\Consumption\Extension\LogExtension::class, LogExtension::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutAnyArguments()
-    {
-        new LogExtension();
-    }
-
     public function testShouldLogStartOnStart()
     {
         $logger = $this->createLogger();
diff --git a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
index 5337d04..d521aef 100644
--- a/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php
@@ -27,14 +27,6 @@ public function testShouldImplementMessageReceivedExtensionInterface()
         $this->assertClassImplements(MessageReceivedExtensionInterface::class, SetRouterPropertiesExtension::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new SetRouterPropertiesExtension($this->createDriverMock());
-    }
-
     public function testShouldSetRouterProcessorPropertyIfNotSetAndOnRouterQueue()
     {
         $config = Config::create('test', '.', '', '', 'router-queue', '', 'router-processor-name');
diff --git a/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php b/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php
index a72ae38..fbd3679 100644
--- a/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/SetupBrokerExtensionTest.php
@@ -21,14 +21,6 @@ public function testShouldImplementStartExtensionInterface()
         $this->assertClassImplements(StartExtensionInterface::class, SetupBrokerExtension::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new SetupBrokerExtension($this->createDriverMock());
-    }
-
     public function testShouldSetupBroker()
     {
         $logger = new NullLogger();
diff --git a/Tests/Client/DelegateProcessorTest.php b/Tests/Client/DelegateProcessorTest.php
index f9bd83e..9743cf4 100644
--- a/Tests/Client/DelegateProcessorTest.php
+++ b/Tests/Client/DelegateProcessorTest.php
@@ -13,14 +13,6 @@
 
 class DelegateProcessorTest extends TestCase
 {
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new DelegateProcessor($this->createProcessorRegistryMock());
-    }
-
     public function testShouldThrowExceptionIfProcessorNameIsNotSet()
     {
         $this->expectException(\LogicException::class);
diff --git a/Tests/Client/DriverFactoryTest.php b/Tests/Client/DriverFactoryTest.php
index 605358a..ef504e0 100644
--- a/Tests/Client/DriverFactoryTest.php
+++ b/Tests/Client/DriverFactoryTest.php
@@ -61,14 +61,6 @@ public function testShouldBeFinal()
         $this->assertTrue($rc->isFinal());
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutAnyArguments()
-    {
-        new DriverFactory();
-    }
-
     public function testThrowIfPackageThatSupportSchemeNotInstalled()
     {
         $scheme = 'scheme5b7aa7d7cd213';
diff --git a/Tests/Client/DriverPreSendTest.php b/Tests/Client/DriverPreSendTest.php
index f3c1ee3..32af2a8 100644
--- a/Tests/Client/DriverPreSendTest.php
+++ b/Tests/Client/DriverPreSendTest.php
@@ -19,18 +19,6 @@ public function testShouldBeFinal()
         self::assertClassFinal(DriverPreSend::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithExpectedArguments()
-    {
-        new DriverPreSend(
-            new Message(),
-            $this->createProducerMock(),
-            $this->createDriverMock()
-        );
-    }
-
     public function testShouldAllowGetArgumentSetInConstructor()
     {
         $expectedMessage = new Message();
diff --git a/Tests/Client/Extension/PrepareBodyExtensionTest.php b/Tests/Client/Extension/PrepareBodyExtensionTest.php
index 81b29e8..d1c2603 100644
--- a/Tests/Client/Extension/PrepareBodyExtensionTest.php
+++ b/Tests/Client/Extension/PrepareBodyExtensionTest.php
@@ -22,14 +22,6 @@ public function testShouldImplementExtensionInterface()
         $this->assertTrue($rc->implementsInterface(PreSendCommandExtensionInterface::class));
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldConstructedWithoutAnyArguments()
-    {
-        new PrepareBodyExtension();
-    }
-
     /**
      * @dataProvider provideMessages
      *
diff --git a/Tests/Client/PostSendTest.php b/Tests/Client/PostSendTest.php
index 9eb845e..ba51710 100644
--- a/Tests/Client/PostSendTest.php
+++ b/Tests/Client/PostSendTest.php
@@ -21,20 +21,6 @@ public function testShouldBeFinal()
         self::assertClassFinal(PostSend::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithExpectedArguments()
-    {
-        new PostSend(
-            new Message(),
-            $this->createProducerMock(),
-            $this->createDriverMock(),
-            $this->createDestinationMock(),
-            $this->createTransportMessageMock()
-        );
-    }
-
     public function testShouldAllowGetArgumentSetInConstructor()
     {
         $expectedMessage = new Message();
diff --git a/Tests/Client/PreSendTest.php b/Tests/Client/PreSendTest.php
index 40dffaf..ae94003 100644
--- a/Tests/Client/PreSendTest.php
+++ b/Tests/Client/PreSendTest.php
@@ -18,19 +18,6 @@ public function testShouldBeFinal()
         self::assertClassFinal(PreSend::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithExpectedArguments()
-    {
-        new PreSend(
-            'aCommandOrTopic',
-            new Message(),
-            $this->createProducerMock(),
-            $this->createDriverMock()
-        );
-    }
-
     public function testShouldAllowGetArgumentSetInConstructor()
     {
         $expectedCommandOrTopic = 'theCommandOrTopic';
diff --git a/Tests/Client/ProducerTest.php b/Tests/Client/ProducerTest.php
index 38dd1f0..9d4e2a3 100644
--- a/Tests/Client/ProducerTest.php
+++ b/Tests/Client/ProducerTest.php
@@ -24,26 +24,6 @@ public function testShouldBeFinal()
         self::assertClassFinal(Producer::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new Producer($this->createDriverMock(), $this->createRpcFactoryMock());
-    }
-
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithOptionalArguments()
-    {
-        new Producer(
-            $this->createDriverMock(),
-            $this->createRpcFactoryMock(),
-            $this->createMock(ExtensionInterface::class)
-        );
-    }
-
     /**
      * @return \PHPUnit\Framework\MockObject\MockObject
      */
diff --git a/Tests/Client/SpoolProducerTest.php b/Tests/Client/SpoolProducerTest.php
index 511b91e..014fe49 100644
--- a/Tests/Client/SpoolProducerTest.php
+++ b/Tests/Client/SpoolProducerTest.php
@@ -18,14 +18,6 @@ public function testShouldImplementProducerInterface()
         self::assertClassImplements(ProducerInterface::class, SpoolProducer::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithRealProducer()
-    {
-        new SpoolProducer($this->createProducerMock());
-    }
-
     public function testShouldQueueEventMessageOnSend()
     {
         $message = new Message();
diff --git a/Tests/Client/TraceableProducerTest.php b/Tests/Client/TraceableProducerTest.php
index 3ce23f8..ff04285 100644
--- a/Tests/Client/TraceableProducerTest.php
+++ b/Tests/Client/TraceableProducerTest.php
@@ -18,14 +18,6 @@ public function testShouldImplementProducerInterface()
         $this->assertClassImplements(ProducerInterface::class, TraceableProducer::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithInternalMessageProducer()
-    {
-        new TraceableProducer($this->createProducerMock());
-    }
-
     public function testShouldPassAllArgumentsToInternalEventMessageProducerSendMethod()
     {
         $topic = 'theTopic';
diff --git a/Tests/ConnectionFactoryFactoryTest.php b/Tests/ConnectionFactoryFactoryTest.php
index c522c63..fe2de69 100644
--- a/Tests/ConnectionFactoryFactoryTest.php
+++ b/Tests/ConnectionFactoryFactoryTest.php
@@ -37,14 +37,6 @@ public function testShouldBeFinal()
         $this->assertTrue($rc->isFinal());
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutAnyArguments()
-    {
-        new ConnectionFactoryFactory();
-    }
-
     /**
      * @doesNotPerformAssertions
      */
diff --git a/Tests/Consumption/CallbackProcessorTest.php b/Tests/Consumption/CallbackProcessorTest.php
index 9cafdb8..86adbd3 100644
--- a/Tests/Consumption/CallbackProcessorTest.php
+++ b/Tests/Consumption/CallbackProcessorTest.php
@@ -18,15 +18,6 @@ public function testShouldImplementProcessorInterface()
         $this->assertClassImplements(Processor::class, CallbackProcessor::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithCallableAsArgument()
-    {
-        new CallbackProcessor(function () {
-        });
-    }
-
     public function testShouldCallCallbackAndProxyItsReturnedValue()
     {
         $expectedMessage = new NullMessage();
diff --git a/Tests/Consumption/ChainExtensionTest.php b/Tests/Consumption/ChainExtensionTest.php
index c19cc2f..198d000 100644
--- a/Tests/Consumption/ChainExtensionTest.php
+++ b/Tests/Consumption/ChainExtensionTest.php
@@ -33,14 +33,6 @@ public function testShouldImplementExtensionInterface()
         $this->assertClassImplements(ExtensionInterface::class, ChainExtension::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithExtensionsArray()
-    {
-        new ChainExtension([$this->createExtension(), $this->createExtension()]);
-    }
-
     public function testShouldProxyOnInitLoggerToAllInternalExtensions()
     {
         $context = new InitLogger(new NullLogger());
diff --git a/Tests/Consumption/Exception/IllegalContextModificationExceptionTest.php b/Tests/Consumption/Exception/IllegalContextModificationExceptionTest.php
index 5de5c2f..241f4ad 100644
--- a/Tests/Consumption/Exception/IllegalContextModificationExceptionTest.php
+++ b/Tests/Consumption/Exception/IllegalContextModificationExceptionTest.php
@@ -20,12 +20,4 @@ public function testShouldExtendLogicException()
     {
         $this->assertClassExtends(\LogicException::class, IllegalContextModificationException::class);
     }
-
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutAnyArguments()
-    {
-        new IllegalContextModificationException();
-    }
 }
diff --git a/Tests/Consumption/Exception/InvalidArgumentExceptionTest.php b/Tests/Consumption/Exception/InvalidArgumentExceptionTest.php
index cc2ca31..c1c5db3 100644
--- a/Tests/Consumption/Exception/InvalidArgumentExceptionTest.php
+++ b/Tests/Consumption/Exception/InvalidArgumentExceptionTest.php
@@ -21,14 +21,6 @@ public function testShouldExtendLogicException()
         $this->assertClassExtends(\LogicException::class, InvalidArgumentException::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutAnyArguments()
-    {
-        new InvalidArgumentException();
-    }
-
     public function testThrowIfAssertInstanceOfNotSameAsExpected()
     {
         $this->expectException(InvalidArgumentException::class);
diff --git a/Tests/Consumption/Exception/LogicExceptionTest.php b/Tests/Consumption/Exception/LogicExceptionTest.php
index f7a68c9..2655609 100644
--- a/Tests/Consumption/Exception/LogicExceptionTest.php
+++ b/Tests/Consumption/Exception/LogicExceptionTest.php
@@ -20,12 +20,4 @@ public function testShouldExtendLogicException()
     {
         $this->assertClassExtends(\LogicException::class, LogicException::class);
     }
-
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutAnyArguments()
-    {
-        new LogicException();
-    }
 }
diff --git a/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php b/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
index 15d2214..137e30b 100644
--- a/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumedMessagesExtensionTest.php
@@ -16,14 +16,6 @@
 
 class LimitConsumedMessagesExtensionTest extends TestCase
 {
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new LimitConsumedMessagesExtension(12345);
-    }
-
     public function testOnPreConsumeShouldInterruptWhenLimitIsReached()
     {
         $logger = $this->createLoggerMock();
diff --git a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
index 22d3382..1548299 100644
--- a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
@@ -17,14 +17,6 @@
 
 class LimitConsumerMemoryExtensionTest extends TestCase
 {
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new LimitConsumerMemoryExtension(12345);
-    }
-
     public function testShouldThrowExceptionIfMemoryLimitIsNotInt()
     {
         $this->expectException(\InvalidArgumentException::class);
diff --git a/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php b/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
index fbd61d4..fa6cb76 100644
--- a/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumptionTimeExtensionTest.php
@@ -17,14 +17,6 @@
 
 class LimitConsumptionTimeExtensionTest extends TestCase
 {
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new LimitConsumptionTimeExtension(new \DateTime('+1 day'));
-    }
-
     public function testOnPreConsumeShouldInterruptExecutionIfConsumptionTimeExceeded()
     {
         $context = new PreConsume(
diff --git a/Tests/Consumption/Extension/LogExtensionTest.php b/Tests/Consumption/Extension/LogExtensionTest.php
index f529dcd..006a2c5 100644
--- a/Tests/Consumption/Extension/LogExtensionTest.php
+++ b/Tests/Consumption/Extension/LogExtensionTest.php
@@ -49,14 +49,6 @@ public function testShouldImplementPostMessageReceivedExtensionInterface()
         $this->assertClassImplements(PostMessageReceivedExtensionInterface::class, LogExtension::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutAnyArguments()
-    {
-        new LogExtension();
-    }
-
     public function testShouldLogStartOnStart()
     {
         $logger = $this->createLogger();
diff --git a/Tests/Consumption/Extension/LoggerExtensionTest.php b/Tests/Consumption/Extension/LoggerExtensionTest.php
index 227c083..455468e 100644
--- a/Tests/Consumption/Extension/LoggerExtensionTest.php
+++ b/Tests/Consumption/Extension/LoggerExtensionTest.php
@@ -20,14 +20,6 @@ public function testShouldImplementInitLoggerExtensionInterface()
         $this->assertClassImplements(InitLoggerExtensionInterface::class, LoggerExtension::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithLoggerAsFirstArgument()
-    {
-        new LoggerExtension($this->createLogger());
-    }
-
     public function testShouldSetLoggerToContextOnInitLogger()
     {
         $logger = $this->createLogger();
diff --git a/Tests/Consumption/Extension/NicenessExtensionTest.php b/Tests/Consumption/Extension/NicenessExtensionTest.php
index 15c004d..734bc84 100644
--- a/Tests/Consumption/Extension/NicenessExtensionTest.php
+++ b/Tests/Consumption/Extension/NicenessExtensionTest.php
@@ -11,14 +11,6 @@
 
 class NicenessExtensionTest extends TestCase
 {
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new NicenessExtension(0);
-    }
-
     public function testShouldThrowExceptionOnInvalidArgument()
     {
         $this->expectException(\InvalidArgumentException::class);
diff --git a/Tests/Consumption/Extension/ReplyExtensionTest.php b/Tests/Consumption/Extension/ReplyExtensionTest.php
index 3963e94..cb65816 100644
--- a/Tests/Consumption/Extension/ReplyExtensionTest.php
+++ b/Tests/Consumption/Extension/ReplyExtensionTest.php
@@ -25,14 +25,6 @@ public function testShouldImplementPostMessageReceivedExtensionInterface()
         $this->assertClassImplements(PostMessageReceivedExtensionInterface::class, ReplyExtension::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutAnyArguments()
-    {
-        new ReplyExtension();
-    }
-
     public function testShouldDoNothingIfReceivedMessageNotHaveReplyToSet()
     {
         $extension = new ReplyExtension();
diff --git a/Tests/Consumption/FallbackSubscriptionConsumerTest.php b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
index aecf716..5391f0c 100644
--- a/Tests/Consumption/FallbackSubscriptionConsumerTest.php
+++ b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
@@ -21,14 +21,6 @@ public function testShouldImplementSubscriptionConsumerInterface()
         $this->assertTrue($rc->implementsInterface(SubscriptionConsumer::class));
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutAnyArguments()
-    {
-        new FallbackSubscriptionConsumer();
-    }
-
     public function testShouldInitSubscribersPropertyWithEmptyArray()
     {
         $subscriptionConsumer = new FallbackSubscriptionConsumer();
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index ce72cb9..06edab3 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -39,30 +39,6 @@ class QueueConsumerTest extends TestCase
 {
     use ReadAttributeTrait;
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithAllArguments()
-    {
-        new QueueConsumer($this->createContextStub(), null, [], null, 0);
-    }
-
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithContextOnly()
-    {
-        new QueueConsumer($this->createContextStub());
-    }
-
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithContextAndSingleExtension()
-    {
-        new QueueConsumer($this->createContextStub(), $this->createExtension());
-    }
-
     public function testShouldSetEmptyArrayToBoundProcessorsPropertyInConstructor()
     {
         $consumer = new QueueConsumer($this->createContextStub(), null, [], null, 0);
diff --git a/Tests/Router/RouteRecipientListProcessorTest.php b/Tests/Router/RouteRecipientListProcessorTest.php
index 7e38f96..ae878fc 100644
--- a/Tests/Router/RouteRecipientListProcessorTest.php
+++ b/Tests/Router/RouteRecipientListProcessorTest.php
@@ -23,14 +23,6 @@ public function testShouldImplementProcessorInterface()
         $this->assertClassImplements(Processor::class, RouteRecipientListProcessor::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithRouterAsFirstArgument()
-    {
-        new RouteRecipientListProcessor($this->createRecipientListRouterMock());
-    }
-
     public function testShouldProduceRecipientsMessagesAndAckOriginalMessage()
     {
         $fooRecipient = new Recipient(new NullQueue('aName'), new NullMessage());
diff --git a/Tests/Rpc/RpcClientTest.php b/Tests/Rpc/RpcClientTest.php
index e8a3720..db4813c 100644
--- a/Tests/Rpc/RpcClientTest.php
+++ b/Tests/Rpc/RpcClientTest.php
@@ -15,14 +15,6 @@
 
 class RpcClientTest extends TestCase
 {
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithContextAsFirstArgument()
-    {
-        new RpcClient($this->createContextMock());
-    }
-
     public function testShouldSetReplyToIfNotSet()
     {
         $context = new NullContext();
diff --git a/Tests/Symfony/Client/ConsumeCommandTest.php b/Tests/Symfony/Client/ConsumeCommandTest.php
index 17be30a..3758ca9 100644
--- a/Tests/Symfony/Client/ConsumeCommandTest.php
+++ b/Tests/Symfony/Client/ConsumeCommandTest.php
@@ -40,14 +40,6 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(ConsumeCommand::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithRequiredAttributes()
-    {
-        new ConsumeCommand($this->createMock(ContainerInterface::class), 'default');
-    }
-
     public function testShouldHaveAsCommandAttributeWithCommandName()
     {
         $commandClass = ConsumeCommand::class;
diff --git a/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php b/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php
index fc3051b..568de64 100644
--- a/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php
@@ -23,14 +23,6 @@ public function testShouldBeFinal()
         $this->assertClassFinal(AnalyzeRouteCollectionPass::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutArguments()
-    {
-        new AnalyzeRouteCollectionPass();
-    }
-
     public function testThrowIfEnqueueClientsParameterNotSet()
     {
         $pass = new AnalyzeRouteCollectionPass();
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php
index baae3c7..7537903 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php
@@ -25,14 +25,6 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildClientExtensionsPass::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutArguments()
-    {
-        new BuildClientExtensionsPass();
-    }
-
     public function testThrowIfEnqueueClientsParameterNotSet()
     {
         $pass = new BuildClientExtensionsPass();
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
index 7317a8e..1a5f65a 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
@@ -29,14 +29,6 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildCommandSubscriberRoutesPass::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutArguments()
-    {
-        new BuildCommandSubscriberRoutesPass();
-    }
-
     public function testThrowIfEnqueueClientsParameterNotSet()
     {
         $pass = new BuildCommandSubscriberRoutesPass();
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php
index 64966d6..c297505 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php
@@ -25,14 +25,6 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildConsumptionExtensionsPass::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutArguments()
-    {
-        new BuildConsumptionExtensionsPass();
-    }
-
     public function testThrowIfEnqueueClientsParameterNotSet()
     {
         $pass = new BuildConsumptionExtensionsPass();
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php
index 98679ab..5c9ac48 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php
@@ -26,14 +26,6 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildProcessorRegistryPass::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutArguments()
-    {
-        new BuildProcessorRegistryPass();
-    }
-
     public function testThrowIfEnqueueClientsParameterNotSet()
     {
         $pass = new BuildProcessorRegistryPass();
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php
index d4ac298..0351c45 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php
@@ -25,14 +25,6 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildProcessorRoutesPass::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutArguments()
-    {
-        new BuildProcessorRoutesPass();
-    }
-
     public function testThrowIfEnqueueClientsParameterNotSet()
     {
         $pass = new BuildProcessorRoutesPass();
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
index 0ada606..b642b90 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
@@ -29,14 +29,6 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildTopicSubscriberRoutesPass::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutArguments()
-    {
-        new BuildTopicSubscriberRoutesPass();
-    }
-
     public function testThrowIfEnqueueClientsParameterNotSet()
     {
         $pass = new BuildTopicSubscriberRoutesPass();
diff --git a/Tests/Symfony/Client/FlushSpoolProducerListenerTest.php b/Tests/Symfony/Client/FlushSpoolProducerListenerTest.php
index feae3b5..539d332 100644
--- a/Tests/Symfony/Client/FlushSpoolProducerListenerTest.php
+++ b/Tests/Symfony/Client/FlushSpoolProducerListenerTest.php
@@ -39,14 +39,6 @@ public function testShouldSubscribeOnConsoleTerminateEvent()
         $this->assertEquals('flushMessages', $events[ConsoleEvents::TERMINATE]);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithSpoolProducerAsFirstArgument()
-    {
-        new FlushSpoolProducerListener($this->createSpoolProducerMock());
-    }
-
     public function testShouldFlushSpoolProducerOnFlushMessagesCall()
     {
         $producerMock = $this->createSpoolProducerMock();
diff --git a/Tests/Symfony/Client/ProduceCommandTest.php b/Tests/Symfony/Client/ProduceCommandTest.php
index b6a1d18..daa9091 100644
--- a/Tests/Symfony/Client/ProduceCommandTest.php
+++ b/Tests/Symfony/Client/ProduceCommandTest.php
@@ -27,14 +27,6 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(ProduceCommand::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithContainerAsFirstArgument()
-    {
-        new ProduceCommand($this->createMock(ContainerInterface::class), 'default');
-    }
-
     public function testShouldHaveAsCommandAttributeWithCommandName()
     {
         $commandClass = ProduceCommand::class;
diff --git a/Tests/Symfony/Client/RoutesCommandTest.php b/Tests/Symfony/Client/RoutesCommandTest.php
index 8adf710..89bd7f7 100644
--- a/Tests/Symfony/Client/RoutesCommandTest.php
+++ b/Tests/Symfony/Client/RoutesCommandTest.php
@@ -29,14 +29,6 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(RoutesCommand::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithConfigAndRouteCollectionAsArguments()
-    {
-        new RoutesCommand($this->createMock(ContainerInterface::class), 'default');
-    }
-
     public function testShouldHaveAsCommandAttributeWithCommandName()
     {
         $commandClass = RoutesCommand::class;
diff --git a/Tests/Symfony/Client/SetupBrokerCommandTest.php b/Tests/Symfony/Client/SetupBrokerCommandTest.php
index 53a8d44..c81c4e1 100644
--- a/Tests/Symfony/Client/SetupBrokerCommandTest.php
+++ b/Tests/Symfony/Client/SetupBrokerCommandTest.php
@@ -26,14 +26,6 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(SetupBrokerCommand::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithContainerAsFirstArgument()
-    {
-        new SetupBrokerCommand($this->createMock(ContainerInterface::class), 'default');
-    }
-
     public function testShouldHaveAsCommandAttributeWithCommandName()
     {
         $commandClass = SetupBrokerCommand::class;
diff --git a/Tests/Symfony/Client/SimpleConsumeCommandTest.php b/Tests/Symfony/Client/SimpleConsumeCommandTest.php
index 97f1c6c..21c491e 100644
--- a/Tests/Symfony/Client/SimpleConsumeCommandTest.php
+++ b/Tests/Symfony/Client/SimpleConsumeCommandTest.php
@@ -29,14 +29,6 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(SimpleConsumeCommand::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithRequiredAttributes()
-    {
-        new SimpleConsumeCommand($this->createQueueConsumerMock(), $this->createDriverStub(), $this->createDelegateProcessorMock());
-    }
-
     public function testShouldHaveExpectedOptions()
     {
         $command = new SimpleConsumeCommand($this->createQueueConsumerMock(), $this->createDriverStub(), $this->createDelegateProcessorMock());
diff --git a/Tests/Symfony/Client/SimpleProduceCommandTest.php b/Tests/Symfony/Client/SimpleProduceCommandTest.php
index 50d46c9..3ff81bf 100644
--- a/Tests/Symfony/Client/SimpleProduceCommandTest.php
+++ b/Tests/Symfony/Client/SimpleProduceCommandTest.php
@@ -23,14 +23,6 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(SimpleProduceCommand::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithContainerAsFirstArgument()
-    {
-        new SimpleProduceCommand($this->createProducerMock());
-    }
-
     public function testShouldHaveExpectedOptions()
     {
         $command = new SimpleProduceCommand($this->createProducerMock());
diff --git a/Tests/Symfony/Client/SimpleRoutesCommandTest.php b/Tests/Symfony/Client/SimpleRoutesCommandTest.php
index 71bb5cf..20ee454 100644
--- a/Tests/Symfony/Client/SimpleRoutesCommandTest.php
+++ b/Tests/Symfony/Client/SimpleRoutesCommandTest.php
@@ -25,14 +25,6 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(SimpleRoutesCommand::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithConfigAndRouteCollectionAsArguments()
-    {
-        new SimpleRoutesCommand($this->createDriverMock());
-    }
-
     public function testShouldHaveCommandAliases()
     {
         $command = new SimpleRoutesCommand($this->createDriverMock());
diff --git a/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php b/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php
index d3e8f21..3702dbf 100644
--- a/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php
+++ b/Tests/Symfony/Client/SimpleSetupBrokerCommandTest.php
@@ -23,14 +23,6 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(SimpleSetupBrokerCommand::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithContainerAsFirstArgument()
-    {
-        new SimpleSetupBrokerCommand($this->createClientDriverMock());
-    }
-
     public function testShouldHaveCommandAliases()
     {
         $command = new SimpleSetupBrokerCommand($this->createClientDriverMock());
diff --git a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
index 82342e4..251e264 100644
--- a/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConfigurableConsumeCommandTest.php
@@ -33,14 +33,6 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(ConfigurableConsumeCommand::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithRequiredAttributes()
-    {
-        new ConfigurableConsumeCommand($this->createMock(ContainerInterface::class), 'default');
-    }
-
     public function testShouldHaveAsCommandAttributeWithCommandName()
     {
         $commandClass = ConfigurableConsumeCommand::class;
diff --git a/Tests/Symfony/Consumption/ConsumeCommandTest.php b/Tests/Symfony/Consumption/ConsumeCommandTest.php
index b56ef68..f07bef0 100644
--- a/Tests/Symfony/Consumption/ConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/ConsumeCommandTest.php
@@ -35,14 +35,6 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(ConsumeCommand::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithRequiredAttributes()
-    {
-        new ConsumeCommand($this->createMock(ContainerInterface::class), 'default');
-    }
-
     public function testShouldHaveAsCommandAttributeWithCommandName()
     {
         $commandClass = ConsumeCommand::class;
diff --git a/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php b/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php
index 7648587..eeb38bf 100644
--- a/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php
+++ b/Tests/Symfony/Consumption/SimpleConsumeCommandTest.php
@@ -24,14 +24,6 @@ public function testShouldNotBeFinal()
         $this->assertClassNotFinal(SimpleConsumeCommand::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithRequiredAttributes()
-    {
-        new SimpleConsumeCommand($this->createQueueConsumerMock());
-    }
-
     public function testShouldHaveExpectedOptions()
     {
         $command = new SimpleConsumeCommand($this->createQueueConsumerMock());
diff --git a/Tests/Symfony/ContainerProcessorRegistryTest.php b/Tests/Symfony/ContainerProcessorRegistryTest.php
index 1370b4d..5504e8e 100644
--- a/Tests/Symfony/ContainerProcessorRegistryTest.php
+++ b/Tests/Symfony/ContainerProcessorRegistryTest.php
@@ -23,14 +23,6 @@ public function testShouldBeFinal()
         $this->assertClassFinal(ContainerProcessorRegistry::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithContainerAsFirstArgument()
-    {
-        new ContainerProcessorRegistry($this->createContainerMock());
-    }
-
     public function testShouldAllowGetProcessor()
     {
         $processorMock = $this->createProcessorMock();
diff --git a/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php b/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
index c558aad..bdccd33 100644
--- a/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
+++ b/Tests/Symfony/DependencyInjection/BuildConsumptionExtensionsPassTest.php
@@ -25,14 +25,6 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildConsumptionExtensionsPass::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutArguments()
-    {
-        new BuildConsumptionExtensionsPass();
-    }
-
     public function testThrowIfEnqueueTransportsParameterNotSet()
     {
         $pass = new BuildConsumptionExtensionsPass();
diff --git a/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php b/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
index e3a3330..134c216 100644
--- a/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
+++ b/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php
@@ -26,14 +26,6 @@ public function testShouldBeFinal()
         $this->assertClassFinal(BuildProcessorRegistryPass::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithoutArguments()
-    {
-        new BuildProcessorRegistryPass();
-    }
-
     public function testThrowIfEnqueueTransportsParameterNotSet()
     {
         $pass = new BuildProcessorRegistryPass();
diff --git a/Tests/Symfony/LazyProducerTest.php b/Tests/Symfony/LazyProducerTest.php
index 7fbe714..c8ba596 100644
--- a/Tests/Symfony/LazyProducerTest.php
+++ b/Tests/Symfony/LazyProducerTest.php
@@ -20,14 +20,6 @@ public function testShouldImplementProducerInterface()
         $this->assertClassImplements(ProducerInterface::class, LazyProducer::class);
     }
 
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithContainerAndServiceId()
-    {
-        new LazyProducer($this->createContainerMock(), 'realProducerId');
-    }
-
     public function testShouldNotCallRealProducerInConstructor()
     {
         $containerMock = $this->createContainerMock();
diff --git a/Tests/Util/VarExportTest.php b/Tests/Util/VarExportTest.php
index a8fa92f..e8034a1 100644
--- a/Tests/Util/VarExportTest.php
+++ b/Tests/Util/VarExportTest.php
@@ -7,14 +7,6 @@
 
 class VarExportTest extends TestCase
 {
-    /**
-     * @doesNotPerformAssertions
-     */
-    public function testCouldBeConstructedWithValueAsArgument()
-    {
-        new VarExport('aVal');
-    }
-
     /**
      * @dataProvider provideValues
      *

From 6e135361d0fb96ef5b9341650f615ccfd2491cd4 Mon Sep 17 00:00:00 2001
From: James Read <james.read.18@gmail.com>
Date: Wed, 15 Jan 2025 22:51:32 +0000
Subject: [PATCH 334/337] Running php-cs-fixer

Running php-cs-fixer to fix CS drift
---
 Client/ChainExtension.php                     |  2 +-
 Client/Config.php                             | 32 +++++++++----------
 .../DelayRedeliveredMessageExtension.php      |  5 ++-
 .../SetRouterPropertiesExtension.php          |  3 --
 .../SetupBrokerExtension.php                  |  3 --
 Client/DelegateProcessor.php                  |  2 --
 Client/Driver/AmqpDriver.php                  | 10 ++----
 Client/Driver/DbalDriver.php                  |  2 +-
 Client/Driver/FsDriver.php                    |  4 +--
 Client/Driver/GenericDriver.php               |  4 +--
 Client/Driver/GpsDriver.php                   |  2 +-
 Client/Driver/MongodbDriver.php               |  2 +-
 Client/Driver/RabbitMqDriver.php              |  2 +-
 Client/Driver/RabbitMqStompDriver.php         |  2 +-
 Client/Driver/RdKafkaDriver.php               |  2 +-
 Client/Driver/SnsQsDriver.php                 |  2 +-
 Client/Driver/SqsDriver.php                   |  2 +-
 Client/Driver/StompDriver.php                 |  2 +-
 Client/Driver/StompManagementClient.php       |  2 +-
 Client/DriverFactory.php                      | 12 ++-----
 Client/DriverInterface.php                    |  2 +-
 Client/Extension/PrepareBodyExtension.php     | 10 ++----
 Client/Message.php                            | 24 ++++----------
 Client/MessagePriority.php                    | 10 +++---
 Client/PostSend.php                           |  2 +-
 Client/PreSend.php                            |  4 +--
 Client/Producer.php                           |  7 ++--
 Client/Resources.php                          |  2 +-
 Client/Route.php                              |  6 ++--
 Client/TraceableProducer.php                  |  2 +-
 ConnectionFactoryFactory.php                  | 12 ++-----
 ConnectionFactoryFactoryInterface.php         |  1 -
 Consumption/CallbackProcessor.php             |  6 ----
 Consumption/ChainExtension.php                |  2 +-
 Consumption/Context/End.php                   |  2 +-
 Consumption/Context/MessageReceived.php       |  2 +-
 Consumption/Context/PostMessageReceived.php   |  2 +-
 .../Exception/InvalidArgumentException.php    |  7 +---
 .../LimitConsumedMessagesExtension.php        |  3 --
 .../LimitConsumerMemoryExtension.php          |  5 +--
 .../LimitConsumptionTimeExtension.php         |  7 ++--
 Consumption/Extension/LoggerExtension.php     |  5 +--
 Consumption/Extension/NicenessExtension.php   | 10 ++----
 Consumption/Extension/SignalExtension.php     | 12 +++----
 Consumption/QueueConsumer.php                 | 14 ++++----
 Consumption/QueueConsumerInterface.php        |  4 +--
 Consumption/Result.php                        | 22 ++++---------
 Resources.php                                 |  2 +-
 Router/Recipient.php                          |  4 ---
 Router/RecipientListRouterInterface.php       |  2 --
 Router/RouteRecipientListProcessor.php        |  6 ----
 Rpc/Promise.php                               | 11 ++-----
 Rpc/RpcClient.php                             | 14 ++------
 Rpc/RpcFactory.php                            |  3 --
 .../AnalyzeRouteCollectionPass.php            | 24 +++-----------
 .../BuildClientExtensionsPass.php             |  2 +-
 .../BuildCommandSubscriberRoutesPass.php      |  8 ++---
 .../BuildConsumptionExtensionsPass.php        |  2 +-
 .../BuildTopicSubscriberRoutesPass.php        |  8 ++---
 Symfony/Client/FlushSpoolProducerListener.php |  3 --
 .../SetupBrokerExtensionCommandTrait.php      |  6 ----
 .../LimitsExtensionsCommandTrait.php          |  3 --
 .../QueueConsumerOptionsCommandTrait.php      |  7 ----
 .../BuildConsumptionExtensionsPass.php        |  2 +-
 .../DelayRedeliveredMessageExtensionTest.php  |  1 -
 Tests/Client/DriverFactoryTest.php            |  4 +--
 .../Extension/PrepareBodyExtensionTest.php    |  3 +-
 Tests/Client/PreSendTest.php                  |  6 ++--
 Tests/Client/ProducerTest.php                 |  1 -
 Tests/Client/RouterProcessorTest.php          |  2 +-
 Tests/Client/TraceableProducerTest.php        |  6 ++--
 Tests/ConnectionFactoryFactoryTest.php        |  2 +-
 .../LimitConsumerMemoryExtensionTest.php      |  6 ++--
 .../Extension/LoggerExtensionTest.php         |  2 +-
 Tests/Consumption/QueueConsumerTest.php       |  2 +-
 .../BuildCommandSubscriberRoutesPassTest.php  | 18 +++++------
 .../BuildTopicSubscriberRoutesPassTest.php    | 16 +++++-----
 .../LimitsExtensionsCommandTraitTest.php      |  2 --
 .../TransportFactoryTest.php                  | 12 +++----
 Tests/Util/JSONTest.php                       |  2 --
 Tests/Util/VarExportTest.php                  |  3 --
 Tests/fix_composer_json.php                   |  2 +-
 Util/JSON.php                                 | 27 ++++------------
 Util/Stringify.php                            |  8 +----
 Util/VarExport.php                            |  6 ----
 85 files changed, 160 insertions(+), 360 deletions(-)

diff --git a/Client/ChainExtension.php b/Client/ChainExtension.php
index d01da37..655b75f 100644
--- a/Client/ChainExtension.php
+++ b/Client/ChainExtension.php
@@ -67,7 +67,7 @@ public function __construct(array $extensions)
             }
 
             if (false == $extensionValid) {
-                throw new \LogicException(sprintf('Invalid extension given %s', get_class($extension)));
+                throw new \LogicException(sprintf('Invalid extension given %s', $extension::class));
             }
         });
     }
diff --git a/Client/Config.php b/Client/Config.php
index 8883842..34057e1 100644
--- a/Client/Config.php
+++ b/Client/Config.php
@@ -4,13 +4,13 @@
 
 class Config
 {
-    const TOPIC = 'enqueue.topic';
-    const COMMAND = 'enqueue.command';
-    const PROCESSOR = 'enqueue.processor';
-    const EXPIRE = 'enqueue.expire';
-    const PRIORITY = 'enqueue.priority';
-    const DELAY = 'enqueue.delay';
-    const CONTENT_TYPE = 'enqueue.content_type';
+    public const TOPIC = 'enqueue.topic';
+    public const COMMAND = 'enqueue.command';
+    public const PROCESSOR = 'enqueue.processor';
+    public const EXPIRE = 'enqueue.expire';
+    public const PRIORITY = 'enqueue.priority';
+    public const DELAY = 'enqueue.delay';
+    public const CONTENT_TYPE = 'enqueue.content_type';
 
     /**
      * @var string
@@ -66,7 +66,7 @@ public function __construct(
         string $defaultQueue,
         string $routerProcessor,
         array $transportConfig,
-        array $driverConfig
+        array $driverConfig,
     ) {
         $this->prefix = trim($prefix);
         $this->app = trim($app);
@@ -153,15 +153,15 @@ public function getDriverOptions(): array
     }
 
     public static function create(
-        string $prefix = null,
-        string $separator = null,
-        string $app = null,
-        string $routerTopic = null,
-        string $routerQueue = null,
-        string $defaultQueue = null,
-        string $routerProcessor = null,
+        ?string $prefix = null,
+        ?string $separator = null,
+        ?string $app = null,
+        ?string $routerTopic = null,
+        ?string $routerQueue = null,
+        ?string $defaultQueue = null,
+        ?string $routerProcessor = null,
         array $transportConfig = [],
-        array $driverConfig = []
+        array $driverConfig = [],
     ): self {
         return new static(
             $prefix ?: '',
diff --git a/Client/ConsumptionExtension/DelayRedeliveredMessageExtension.php b/Client/ConsumptionExtension/DelayRedeliveredMessageExtension.php
index 77e3bc4..475e2cf 100644
--- a/Client/ConsumptionExtension/DelayRedeliveredMessageExtension.php
+++ b/Client/ConsumptionExtension/DelayRedeliveredMessageExtension.php
@@ -9,7 +9,7 @@
 
 class DelayRedeliveredMessageExtension implements MessageReceivedExtensionInterface
 {
-    const PROPERTY_REDELIVER_COUNT = 'enqueue.redelivery_count';
+    public const PROPERTY_REDELIVER_COUNT = 'enqueue.redelivery_count';
 
     /**
      * @var DriverInterface
@@ -24,8 +24,7 @@ class DelayRedeliveredMessageExtension implements MessageReceivedExtensionInterf
     private $delay;
 
     /**
-     * @param DriverInterface $driver
-     * @param int             $delay  The number of seconds the message should be delayed
+     * @param int $delay The number of seconds the message should be delayed
      */
     public function __construct(DriverInterface $driver, $delay)
     {
diff --git a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
index c7682f6..0d22783 100644
--- a/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
+++ b/Client/ConsumptionExtension/SetRouterPropertiesExtension.php
@@ -14,9 +14,6 @@ class SetRouterPropertiesExtension implements MessageReceivedExtensionInterface
      */
     private $driver;
 
-    /**
-     * @param DriverInterface $driver
-     */
     public function __construct(DriverInterface $driver)
     {
         $this->driver = $driver;
diff --git a/Client/ConsumptionExtension/SetupBrokerExtension.php b/Client/ConsumptionExtension/SetupBrokerExtension.php
index e355807..44d610f 100644
--- a/Client/ConsumptionExtension/SetupBrokerExtension.php
+++ b/Client/ConsumptionExtension/SetupBrokerExtension.php
@@ -18,9 +18,6 @@ class SetupBrokerExtension implements StartExtensionInterface
      */
     private $isDone;
 
-    /**
-     * @param DriverInterface $driver
-     */
     public function __construct(DriverInterface $driver)
     {
         $this->driver = $driver;
diff --git a/Client/DelegateProcessor.php b/Client/DelegateProcessor.php
index 0fbe9c2..7582c52 100644
--- a/Client/DelegateProcessor.php
+++ b/Client/DelegateProcessor.php
@@ -20,8 +20,6 @@ public function __construct(ProcessorRegistryInterface $registry)
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return string|object
      */
     public function process(InteropMessage $message, Context $context)
diff --git a/Client/Driver/AmqpDriver.php b/Client/Driver/AmqpDriver.php
index be62753..1def3fb 100644
--- a/Client/Driver/AmqpDriver.php
+++ b/Client/Driver/AmqpDriver.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace  Enqueue\Client\Driver;
+namespace Enqueue\Client\Driver;
 
 use Enqueue\AmqpExt\AmqpProducer;
 use Enqueue\Client\Message;
@@ -45,11 +45,7 @@ public function createTransportMessage(Message $clientMessage): InteropMessage
         $priorityMap = $this->getPriorityMap();
         if ($priority = $clientMessage->getPriority()) {
             if (false == array_key_exists($priority, $priorityMap)) {
-                throw new \InvalidArgumentException(sprintf(
-                    'Cant convert client priority "%s" to transport one. Could be one of "%s"',
-                    $priority,
-                    implode('", "', array_keys($priorityMap))
-                ));
+                throw new \InvalidArgumentException(sprintf('Cant convert client priority "%s" to transport one. Could be one of "%s"', $priority, implode('", "', array_keys($priorityMap))));
             }
 
             $transportMessage->setPriority($priorityMap[$priority]);
@@ -58,7 +54,7 @@ public function createTransportMessage(Message $clientMessage): InteropMessage
         return $transportMessage;
     }
 
-    public function setupBroker(LoggerInterface $logger = null): void
+    public function setupBroker(?LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $log = function ($text, ...$args) use ($logger) {
diff --git a/Client/Driver/DbalDriver.php b/Client/Driver/DbalDriver.php
index 8b1f326..34875ef 100644
--- a/Client/Driver/DbalDriver.php
+++ b/Client/Driver/DbalDriver.php
@@ -16,7 +16,7 @@ public function __construct(DbalContext $context, ...$args)
         parent::__construct($context, ...$args);
     }
 
-    public function setupBroker(LoggerInterface $logger = null): void
+    public function setupBroker(?LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $log = function ($text, ...$args) use ($logger) {
diff --git a/Client/Driver/FsDriver.php b/Client/Driver/FsDriver.php
index 9de59c2..f578b17 100644
--- a/Client/Driver/FsDriver.php
+++ b/Client/Driver/FsDriver.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace  Enqueue\Client\Driver;
+namespace Enqueue\Client\Driver;
 
 use Enqueue\Fs\FsContext;
 use Enqueue\Fs\FsDestination;
@@ -18,7 +18,7 @@ public function __construct(FsContext $context, ...$args)
         parent::__construct($context, ...$args);
     }
 
-    public function setupBroker(LoggerInterface $logger = null): void
+    public function setupBroker(?LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $log = function ($text, ...$args) use ($logger) {
diff --git a/Client/Driver/GenericDriver.php b/Client/Driver/GenericDriver.php
index 529f632..d509677 100644
--- a/Client/Driver/GenericDriver.php
+++ b/Client/Driver/GenericDriver.php
@@ -39,7 +39,7 @@ class GenericDriver implements DriverInterface
     public function __construct(
         Context $context,
         Config $config,
-        RouteCollection $routeCollection
+        RouteCollection $routeCollection,
     ) {
         $this->context = $context;
         $this->config = $config;
@@ -120,7 +120,7 @@ public function sendToProcessor(Message $message): DriverSendResult
         return new DriverSendResult($queue, $transportMessage);
     }
 
-    public function setupBroker(LoggerInterface $logger = null): void
+    public function setupBroker(?LoggerInterface $logger = null): void
     {
     }
 
diff --git a/Client/Driver/GpsDriver.php b/Client/Driver/GpsDriver.php
index 37a7110..32d14f7 100644
--- a/Client/Driver/GpsDriver.php
+++ b/Client/Driver/GpsDriver.php
@@ -20,7 +20,7 @@ public function __construct(GpsContext $context, ...$args)
         parent::__construct($context, ...$args);
     }
 
-    public function setupBroker(LoggerInterface $logger = null): void
+    public function setupBroker(?LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $log = function ($text, ...$args) use ($logger) {
diff --git a/Client/Driver/MongodbDriver.php b/Client/Driver/MongodbDriver.php
index 19f2c57..1c9cff4 100644
--- a/Client/Driver/MongodbDriver.php
+++ b/Client/Driver/MongodbDriver.php
@@ -16,7 +16,7 @@ public function __construct(MongodbContext $context, ...$args)
         parent::__construct($context, ...$args);
     }
 
-    public function setupBroker(LoggerInterface $logger = null): void
+    public function setupBroker(?LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $log = function ($text, ...$args) use ($logger) {
diff --git a/Client/Driver/RabbitMqDriver.php b/Client/Driver/RabbitMqDriver.php
index 096de6d..f215d55 100644
--- a/Client/Driver/RabbitMqDriver.php
+++ b/Client/Driver/RabbitMqDriver.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace  Enqueue\Client\Driver;
+namespace Enqueue\Client\Driver;
 
 use Interop\Amqp\AmqpQueue;
 use Interop\Queue\Queue as InteropQueue;
diff --git a/Client/Driver/RabbitMqStompDriver.php b/Client/Driver/RabbitMqStompDriver.php
index 36bdd86..7af2db8 100644
--- a/Client/Driver/RabbitMqStompDriver.php
+++ b/Client/Driver/RabbitMqStompDriver.php
@@ -62,7 +62,7 @@ public function createTransportMessage(Message $message): InteropMessage
         return $transportMessage;
     }
 
-    public function setupBroker(LoggerInterface $logger = null): void
+    public function setupBroker(?LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $log = function ($text, ...$args) use ($logger) {
diff --git a/Client/Driver/RdKafkaDriver.php b/Client/Driver/RdKafkaDriver.php
index 37013bb..2609e3f 100644
--- a/Client/Driver/RdKafkaDriver.php
+++ b/Client/Driver/RdKafkaDriver.php
@@ -19,7 +19,7 @@ public function __construct(RdKafkaContext $context, ...$args)
         parent::__construct($context, ...$args);
     }
 
-    public function setupBroker(LoggerInterface $logger = null): void
+    public function setupBroker(?LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $logger->debug('[RdKafkaDriver] setup broker');
diff --git a/Client/Driver/SnsQsDriver.php b/Client/Driver/SnsQsDriver.php
index 2b1d4f2..f4bde10 100644
--- a/Client/Driver/SnsQsDriver.php
+++ b/Client/Driver/SnsQsDriver.php
@@ -19,7 +19,7 @@ public function __construct(SnsQsContext $context, ...$args)
         parent::__construct($context, ...$args);
     }
 
-    public function setupBroker(LoggerInterface $logger = null): void
+    public function setupBroker(?LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $log = function ($text, ...$args) use ($logger) {
diff --git a/Client/Driver/SqsDriver.php b/Client/Driver/SqsDriver.php
index bf66c05..49b696a 100644
--- a/Client/Driver/SqsDriver.php
+++ b/Client/Driver/SqsDriver.php
@@ -18,7 +18,7 @@ public function __construct(SqsContext $context, ...$args)
         parent::__construct($context, ...$args);
     }
 
-    public function setupBroker(LoggerInterface $logger = null): void
+    public function setupBroker(?LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $log = function ($text, ...$args) use ($logger) {
diff --git a/Client/Driver/StompDriver.php b/Client/Driver/StompDriver.php
index 7040c71..811ad76 100644
--- a/Client/Driver/StompDriver.php
+++ b/Client/Driver/StompDriver.php
@@ -22,7 +22,7 @@ public function __construct(StompContext $context, ...$args)
         parent::__construct($context, ...$args);
     }
 
-    public function setupBroker(LoggerInterface $logger = null): void
+    public function setupBroker(?LoggerInterface $logger = null): void
     {
         $logger = $logger ?: new NullLogger();
         $logger->debug('[StompDriver] Stomp protocol does not support broker configuration');
diff --git a/Client/Driver/StompManagementClient.php b/Client/Driver/StompManagementClient.php
index c068a57..5880672 100644
--- a/Client/Driver/StompManagementClient.php
+++ b/Client/Driver/StompManagementClient.php
@@ -37,7 +37,7 @@ public function declareExchange(string $name, array $options)
         return $this->client->exchanges()->create($this->vhost, $name, $options);
     }
 
-    public function bind(string $exchange, string $queue, string $routingKey = null, $arguments = null)
+    public function bind(string $exchange, string $queue, ?string $routingKey = null, $arguments = null)
     {
         return $this->client->bindings()->create($this->vhost, $exchange, $queue, $routingKey, $arguments);
     }
diff --git a/Client/DriverFactory.php b/Client/DriverFactory.php
index 0dd9412..1d383ac 100644
--- a/Client/DriverFactory.php
+++ b/Client/DriverFactory.php
@@ -31,18 +31,10 @@ public function create(ConnectionFactory $factory, Config $config, RouteCollecti
 
         $knownDrivers = Resources::getKnownDrivers();
         if ($driverInfo = $this->findDriverInfo($dsn, $knownDrivers)) {
-            throw new \LogicException(sprintf(
-                'To use given scheme "%s" a package has to be installed. Run "composer req %s" to add it.',
-                $dsn->getScheme(),
-                implode(' ', $driverInfo['packages'])
-            ));
+            throw new \LogicException(sprintf('To use given scheme "%s" a package has to be installed. Run "composer req %s" to add it.', $dsn->getScheme(), implode(' ', $driverInfo['packages'])));
         }
 
-        throw new \LogicException(sprintf(
-            'A given scheme "%s" is not supported. Maybe it is a custom driver, make sure you registered it with "%s::addDriver".',
-            $dsn->getScheme(),
-            Resources::class
-        ));
+        throw new \LogicException(sprintf('A given scheme "%s" is not supported. Maybe it is a custom driver, make sure you registered it with "%s::addDriver".', $dsn->getScheme(), Resources::class));
     }
 
     private function findDriverInfo(Dsn $dsn, array $factories): ?array
diff --git a/Client/DriverInterface.php b/Client/DriverInterface.php
index 1832e0f..9d1dde6 100644
--- a/Client/DriverInterface.php
+++ b/Client/DriverInterface.php
@@ -27,7 +27,7 @@ public function createRouteQueue(Route $route): InteropQueue;
      * Prepare broker for work.
      * Creates all required queues, exchanges, topics, bindings etc.
      */
-    public function setupBroker(LoggerInterface $logger = null): void;
+    public function setupBroker(?LoggerInterface $logger = null): void;
 
     public function getConfig(): Config;
 
diff --git a/Client/Extension/PrepareBodyExtension.php b/Client/Extension/PrepareBodyExtension.php
index eeb0ded..e792454 100644
--- a/Client/Extension/PrepareBodyExtension.php
+++ b/Client/Extension/PrepareBodyExtension.php
@@ -32,10 +32,7 @@ private function prepareBody(Message $message): void
             // only array of scalars is allowed.
             array_walk_recursive($body, function ($value) {
                 if (!is_scalar($value) && null !== $value) {
-                    throw new \LogicException(sprintf(
-                        'The message\'s body must be an array of scalars. Found not scalar in the array: %s',
-                        is_object($value) ? get_class($value) : gettype($value)
-                    ));
+                    throw new \LogicException(sprintf('The message\'s body must be an array of scalars. Found not scalar in the array: %s', is_object($value) ? $value::class : gettype($value)));
                 }
             });
 
@@ -45,10 +42,7 @@ private function prepareBody(Message $message): void
             $contentType = $contentType ?: 'application/json';
             $body = JSON::encode($body);
         } else {
-            throw new \InvalidArgumentException(sprintf(
-                'The message\'s body must be either null, scalar, array or object (implements \JsonSerializable). Got: %s',
-                is_object($body) ? get_class($body) : gettype($body)
-            ));
+            throw new \InvalidArgumentException(sprintf('The message\'s body must be either null, scalar, array or object (implements \JsonSerializable). Got: %s', is_object($body) ? $body::class : gettype($body)));
         }
 
         $message->setContentType($contentType);
diff --git a/Client/Message.php b/Client/Message.php
index b1cc9c2..7e51ea1 100644
--- a/Client/Message.php
+++ b/Client/Message.php
@@ -7,12 +7,12 @@ class Message
     /**
      * @const string
      */
-    const SCOPE_MESSAGE_BUS = 'enqueue.scope.message_bus';
+    public const SCOPE_MESSAGE_BUS = 'enqueue.scope.message_bus';
 
     /**
      * @const string
      */
-    const SCOPE_APP = 'enqueue.scope.app';
+    public const SCOPE_APP = 'enqueue.scope.app';
 
     /**
      * @var string|null
@@ -256,10 +256,8 @@ public function getHeaders()
     }
 
     /**
-     * @param string $name
-     * @param mixed  $default
-     *
-     * @return mixed
+     * @param string     $name
+     * @param mixed|null $default
      */
     public function getHeader($name, $default = null)
     {
@@ -268,16 +266,12 @@ public function getHeader($name, $default = null)
 
     /**
      * @param string $name
-     * @param mixed  $value
      */
     public function setHeader($name, $value)
     {
         $this->headers[$name] = $value;
     }
 
-    /**
-     * @param array $headers
-     */
     public function setHeaders(array $headers)
     {
         $this->headers = $headers;
@@ -291,19 +285,14 @@ public function getProperties()
         return $this->properties;
     }
 
-    /**
-     * @param array $properties
-     */
     public function setProperties(array $properties)
     {
         $this->properties = $properties;
     }
 
     /**
-     * @param string $name
-     * @param mixed  $default
-     *
-     * @return mixed
+     * @param string     $name
+     * @param mixed|null $default
      */
     public function getProperty($name, $default = null)
     {
@@ -312,7 +301,6 @@ public function getProperty($name, $default = null)
 
     /**
      * @param string $name
-     * @param mixed  $value
      */
     public function setProperty($name, $value)
     {
diff --git a/Client/MessagePriority.php b/Client/MessagePriority.php
index efa658c..e14be9a 100644
--- a/Client/MessagePriority.php
+++ b/Client/MessagePriority.php
@@ -4,9 +4,9 @@
 
 class MessagePriority
 {
-    const VERY_LOW = 'enqueue.message_queue.client.very_low_message_priority';
-    const LOW = 'enqueue.message_queue.client.low_message_priority';
-    const NORMAL = 'enqueue.message_queue.client.normal_message_priority';
-    const HIGH = 'enqueue.message_queue.client.high_message_priority';
-    const VERY_HIGH = 'enqueue.message_queue.client.very_high_message_priority';
+    public const VERY_LOW = 'enqueue.message_queue.client.very_low_message_priority';
+    public const LOW = 'enqueue.message_queue.client.low_message_priority';
+    public const NORMAL = 'enqueue.message_queue.client.normal_message_priority';
+    public const HIGH = 'enqueue.message_queue.client.high_message_priority';
+    public const VERY_HIGH = 'enqueue.message_queue.client.very_high_message_priority';
 }
diff --git a/Client/PostSend.php b/Client/PostSend.php
index 7bce741..5d9526e 100644
--- a/Client/PostSend.php
+++ b/Client/PostSend.php
@@ -22,7 +22,7 @@ public function __construct(
         ProducerInterface $producer,
         DriverInterface $driver,
         Destination $transportDestination,
-        TransportMessage $transportMessage
+        TransportMessage $transportMessage,
     ) {
         $this->message = $message;
         $this->producer = $producer;
diff --git a/Client/PreSend.php b/Client/PreSend.php
index afd6401..b60f90b 100644
--- a/Client/PreSend.php
+++ b/Client/PreSend.php
@@ -18,7 +18,7 @@ public function __construct(
         string $commandOrTopic,
         Message $message,
         ProducerInterface $producer,
-        DriverInterface $driver
+        DriverInterface $driver,
     ) {
         $this->message = $message;
         $this->commandOrTopic = $commandOrTopic;
@@ -48,7 +48,7 @@ public function changeTopic(string $newTopic): void
         $this->commandOrTopic = $newTopic;
     }
 
-    public function changeBody($body, string $contentType = null): void
+    public function changeBody($body, ?string $contentType = null): void
     {
         $this->message->setBody($body);
 
diff --git a/Client/Producer.php b/Client/Producer.php
index 6ea612b..db50744 100644
--- a/Client/Producer.php
+++ b/Client/Producer.php
@@ -27,7 +27,7 @@ final class Producer implements ProducerInterface
     public function __construct(
         DriverInterface $driver,
         RpcFactory $rpcFactory,
-        ExtensionInterface $extension = null
+        ?ExtensionInterface $extension = null,
     ) {
         $this->driver = $driver;
         $this->rpcFactory = $rpcFactory;
@@ -97,10 +97,7 @@ public function sendCommand(string $command, $message, bool $needReply = false):
     private function doSend(Message $message): void
     {
         if (false === is_string($message->getBody())) {
-            throw new \LogicException(sprintf(
-                'The message body must be string at this stage, got "%s". Make sure you passed string as message or there is an extension that converts custom input to string.',
-                is_object($message->getBody()) ? get_class($message->getBody()) : gettype($message->getBody())
-            ));
+            throw new \LogicException(sprintf('The message body must be string at this stage, got "%s". Make sure you passed string as message or there is an extension that converts custom input to string.', is_object($message->getBody()) ? get_class($message->getBody()) : gettype($message->getBody())));
         }
 
         if ($message->getProperty(Config::PROCESSOR)) {
diff --git a/Client/Resources.php b/Client/Resources.php
index 6ea5988..a5cc684 100644
--- a/Client/Resources.php
+++ b/Client/Resources.php
@@ -26,7 +26,7 @@ final class Resources
      *
      * @var array
      */
-    private static $knownDrivers = null;
+    private static $knownDrivers;
 
     private function __construct()
     {
diff --git a/Client/Route.php b/Client/Route.php
index 5c98fa6..8b9e31e 100644
--- a/Client/Route.php
+++ b/Client/Route.php
@@ -4,9 +4,9 @@
 
 final class Route
 {
-    const TOPIC = 'enqueue.client.topic_route';
+    public const TOPIC = 'enqueue.client.topic_route';
 
-    const COMMAND = 'enqueue.client.command_route';
+    public const COMMAND = 'enqueue.client.command_route';
 
     /**
      * @var string
@@ -32,7 +32,7 @@ public function __construct(
         string $source,
         string $sourceType,
         string $processor,
-        array $options = []
+        array $options = [],
     ) {
         $this->source = $source;
         $this->sourceType = $sourceType;
diff --git a/Client/TraceableProducer.php b/Client/TraceableProducer.php
index 59b0c7b..b0bd613 100644
--- a/Client/TraceableProducer.php
+++ b/Client/TraceableProducer.php
@@ -71,7 +71,7 @@ public function clearTraces(): void
         $this->traces = [];
     }
 
-    private function collectTrace(string $topic = null, string $command = null, $message): void
+    private function collectTrace(?string $topic, ?string $command, $message): void
     {
         $trace = [
             'topic' => $topic,
diff --git a/ConnectionFactoryFactory.php b/ConnectionFactoryFactory.php
index d89c671..d23518c 100644
--- a/ConnectionFactoryFactory.php
+++ b/ConnectionFactoryFactory.php
@@ -29,18 +29,10 @@ public function create($config): ConnectionFactory
 
         $knownConnections = Resources::getKnownConnections();
         if ($factoryClass = $this->findFactoryClass($dsn, $knownConnections)) {
-            throw new \LogicException(sprintf(
-                'To use given scheme "%s" a package has to be installed. Run "composer req %s" to add it.',
-                $dsn->getScheme(),
-                $knownConnections[$factoryClass]['package']
-            ));
+            throw new \LogicException(sprintf('To use given scheme "%s" a package has to be installed. Run "composer req %s" to add it.', $dsn->getScheme(), $knownConnections[$factoryClass]['package']));
         }
 
-        throw new \LogicException(sprintf(
-            'A given scheme "%s" is not supported. Maybe it is a custom connection, make sure you registered it with "%s::addConnection".',
-            $dsn->getScheme(),
-            Resources::class
-        ));
+        throw new \LogicException(sprintf('A given scheme "%s" is not supported. Maybe it is a custom connection, make sure you registered it with "%s::addConnection".', $dsn->getScheme(), Resources::class));
     }
 
     private function findFactoryClass(Dsn $dsn, array $factories): ?string
diff --git a/ConnectionFactoryFactoryInterface.php b/ConnectionFactoryFactoryInterface.php
index d55808c..f4ca4a6 100644
--- a/ConnectionFactoryFactoryInterface.php
+++ b/ConnectionFactoryFactoryInterface.php
@@ -13,7 +13,6 @@ interface ConnectionFactoryFactoryInterface
      * The other array options are treated as default values.
      * Options from DSN overwrite them.
      *
-     *
      * @param string|array $config
      *
      * @throws \InvalidArgumentException if invalid config provided
diff --git a/Consumption/CallbackProcessor.php b/Consumption/CallbackProcessor.php
index 988b765..d15978f 100644
--- a/Consumption/CallbackProcessor.php
+++ b/Consumption/CallbackProcessor.php
@@ -13,17 +13,11 @@ class CallbackProcessor implements Processor
      */
     private $callback;
 
-    /**
-     * @param callable $callback
-     */
     public function __construct(callable $callback)
     {
         $this->callback = $callback;
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function process(InteropMessage $message, Context $context)
     {
         return call_user_func($this->callback, $message, $context);
diff --git a/Consumption/ChainExtension.php b/Consumption/ChainExtension.php
index e2c11a1..83b4eba 100644
--- a/Consumption/ChainExtension.php
+++ b/Consumption/ChainExtension.php
@@ -117,7 +117,7 @@ public function __construct(array $extensions)
             }
 
             if (false == $extensionValid) {
-                throw new \LogicException(sprintf('Invalid extension given %s', get_class($extension)));
+                throw new \LogicException(sprintf('Invalid extension given %s', $extension::class));
             }
         });
     }
diff --git a/Consumption/Context/End.php b/Consumption/Context/End.php
index ec8c1f7..07853b3 100644
--- a/Consumption/Context/End.php
+++ b/Consumption/Context/End.php
@@ -37,7 +37,7 @@ public function __construct(
         int $startTime,
         int $endTime,
         LoggerInterface $logger,
-        ?int $exitStatus = null
+        ?int $exitStatus = null,
     ) {
         $this->context = $context;
         $this->logger = $logger;
diff --git a/Consumption/Context/MessageReceived.php b/Consumption/Context/MessageReceived.php
index ad6b6b9..35abf1c 100644
--- a/Consumption/Context/MessageReceived.php
+++ b/Consumption/Context/MessageReceived.php
@@ -52,7 +52,7 @@ public function __construct(
         Message $message,
         Processor $processor,
         int $receivedAt,
-        LoggerInterface $logger
+        LoggerInterface $logger,
     ) {
         $this->context = $context;
         $this->consumer = $consumer;
diff --git a/Consumption/Context/PostMessageReceived.php b/Consumption/Context/PostMessageReceived.php
index 91b5326..23df2c8 100644
--- a/Consumption/Context/PostMessageReceived.php
+++ b/Consumption/Context/PostMessageReceived.php
@@ -56,7 +56,7 @@ public function __construct(
         Message $message,
         $result,
         int $receivedAt,
-        LoggerInterface $logger
+        LoggerInterface $logger,
     ) {
         $this->context = $context;
         $this->consumer = $consumer;
diff --git a/Consumption/Exception/InvalidArgumentException.php b/Consumption/Exception/InvalidArgumentException.php
index da6015d..76ea83a 100644
--- a/Consumption/Exception/InvalidArgumentException.php
+++ b/Consumption/Exception/InvalidArgumentException.php
@@ -5,7 +5,6 @@
 class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
 {
     /**
-     * @param mixed  $argument
      * @param string $class
      *
      * @throws static
@@ -13,11 +12,7 @@ class InvalidArgumentException extends \InvalidArgumentException implements Exce
     public static function assertInstanceOf($argument, $class)
     {
         if (false == $argument instanceof $class) {
-            throw new static(sprintf(
-                'The argument must be an instance of %s but got %s.',
-                $class,
-                is_object($argument) ? get_class($argument) : gettype($argument)
-            ));
+            throw new static(sprintf('The argument must be an instance of %s but got %s.', $class, is_object($argument) ? $argument::class : gettype($argument)));
         }
     }
 }
diff --git a/Consumption/Extension/LimitConsumedMessagesExtension.php b/Consumption/Extension/LimitConsumedMessagesExtension.php
index e89957a..0dc6fec 100644
--- a/Consumption/Extension/LimitConsumedMessagesExtension.php
+++ b/Consumption/Extension/LimitConsumedMessagesExtension.php
@@ -20,9 +20,6 @@ class LimitConsumedMessagesExtension implements PreConsumeExtensionInterface, Po
      */
     protected $messageConsumed;
 
-    /**
-     * @param int $messageLimit
-     */
     public function __construct(int $messageLimit)
     {
         $this->messageLimit = $messageLimit;
diff --git a/Consumption/Extension/LimitConsumerMemoryExtension.php b/Consumption/Extension/LimitConsumerMemoryExtension.php
index 13d4a28..7edbf23 100644
--- a/Consumption/Extension/LimitConsumerMemoryExtension.php
+++ b/Consumption/Extension/LimitConsumerMemoryExtension.php
@@ -23,10 +23,7 @@ class LimitConsumerMemoryExtension implements PreConsumeExtensionInterface, Post
     public function __construct($memoryLimit)
     {
         if (false == is_int($memoryLimit)) {
-            throw new \InvalidArgumentException(sprintf(
-                'Expected memory limit is int but got: "%s"',
-                is_object($memoryLimit) ? get_class($memoryLimit) : gettype($memoryLimit)
-            ));
+            throw new \InvalidArgumentException(sprintf('Expected memory limit is int but got: "%s"', is_object($memoryLimit) ? $memoryLimit::class : gettype($memoryLimit)));
         }
 
         $this->memoryLimit = $memoryLimit * 1024 * 1024;
diff --git a/Consumption/Extension/LimitConsumptionTimeExtension.php b/Consumption/Extension/LimitConsumptionTimeExtension.php
index f0a5779..1953aa2 100644
--- a/Consumption/Extension/LimitConsumptionTimeExtension.php
+++ b/Consumption/Extension/LimitConsumptionTimeExtension.php
@@ -17,9 +17,6 @@ class LimitConsumptionTimeExtension implements PreConsumeExtensionInterface, Pos
      */
     protected $timeLimit;
 
-    /**
-     * @param \DateTime $timeLimit
-     */
     public function __construct(\DateTime $timeLimit)
     {
         $this->timeLimit = $timeLimit;
@@ -53,8 +50,8 @@ protected function shouldBeStopped(LoggerInterface $logger): bool
             $logger->debug(sprintf(
                 '[LimitConsumptionTimeExtension] Execution interrupted as limit time has passed.'.
                 ' now: "%s", time-limit: "%s"',
-                $now->format(DATE_ISO8601),
-                $this->timeLimit->format(DATE_ISO8601)
+                $now->format(\DATE_ISO8601),
+                $this->timeLimit->format(\DATE_ISO8601)
             ));
 
             return true;
diff --git a/Consumption/Extension/LoggerExtension.php b/Consumption/Extension/LoggerExtension.php
index 0de2739..90e92be 100644
--- a/Consumption/Extension/LoggerExtension.php
+++ b/Consumption/Extension/LoggerExtension.php
@@ -13,9 +13,6 @@ class LoggerExtension implements InitLoggerExtensionInterface
      */
     private $logger;
 
-    /**
-     * @param LoggerInterface $logger
-     */
     public function __construct(LoggerInterface $logger)
     {
         $this->logger = $logger;
@@ -28,7 +25,7 @@ public function onInitLogger(InitLogger $context): void
         if ($previousLogger !== $this->logger) {
             $context->changeLogger($this->logger);
 
-            $this->logger->debug(sprintf('Change logger from "%s" to "%s"', get_class($previousLogger), get_class($this->logger)));
+            $this->logger->debug(sprintf('Change logger from "%s" to "%s"', $previousLogger::class, get_class($this->logger)));
         }
     }
 }
diff --git a/Consumption/Extension/NicenessExtension.php b/Consumption/Extension/NicenessExtension.php
index 983e04b..436a8ec 100644
--- a/Consumption/Extension/NicenessExtension.php
+++ b/Consumption/Extension/NicenessExtension.php
@@ -20,10 +20,7 @@ class NicenessExtension implements StartExtensionInterface
     public function __construct($niceness)
     {
         if (false === is_int($niceness)) {
-            throw new \InvalidArgumentException(sprintf(
-                'Expected niceness value is int but got: "%s"',
-                is_object($niceness) ? get_class($niceness) : gettype($niceness)
-            ));
+            throw new \InvalidArgumentException(sprintf('Expected niceness value is int but got: "%s"', is_object($niceness) ? $niceness::class : gettype($niceness)));
         }
 
         $this->niceness = $niceness;
@@ -34,10 +31,7 @@ public function onStart(Start $context): void
         if (0 !== $this->niceness) {
             $changed = @proc_nice($this->niceness);
             if (!$changed) {
-                throw new \InvalidArgumentException(sprintf(
-                    'Cannot change process niceness, got warning: "%s"',
-                    error_get_last()['message']
-                ));
+                throw new \InvalidArgumentException(sprintf('Cannot change process niceness, got warning: "%s"', error_get_last()['message']));
             }
         }
     }
diff --git a/Consumption/Extension/SignalExtension.php b/Consumption/Extension/SignalExtension.php
index 67354b9..8ea5307 100644
--- a/Consumption/Extension/SignalExtension.php
+++ b/Consumption/Extension/SignalExtension.php
@@ -33,9 +33,9 @@ public function onStart(Start $context): void
 
         pcntl_async_signals(true);
 
-        pcntl_signal(SIGTERM, [$this, 'handleSignal']);
-        pcntl_signal(SIGQUIT, [$this, 'handleSignal']);
-        pcntl_signal(SIGINT, [$this, 'handleSignal']);
+        pcntl_signal(\SIGTERM, [$this, 'handleSignal']);
+        pcntl_signal(\SIGQUIT, [$this, 'handleSignal']);
+        pcntl_signal(\SIGINT, [$this, 'handleSignal']);
 
         $this->logger = $context->getLogger();
         $this->interruptConsumption = false;
@@ -71,9 +71,9 @@ public function handleSignal(int $signal): void
         }
 
         switch ($signal) {
-            case SIGTERM:  // 15 : supervisor default stop
-            case SIGQUIT:  // 3  : kill -s QUIT
-            case SIGINT:   // 2  : ctrl+c
+            case \SIGTERM:  // 15 : supervisor default stop
+            case \SIGQUIT:  // 3  : kill -s QUIT
+            case \SIGINT:   // 2  : ctrl+c
                 if ($this->logger) {
                     $this->logger->debug('[SignalExtension] Interrupt consumption');
                 }
diff --git a/Consumption/QueueConsumer.php b/Consumption/QueueConsumer.php
index 80936bf..f43f5c4 100644
--- a/Consumption/QueueConsumer.php
+++ b/Consumption/QueueConsumer.php
@@ -63,10 +63,10 @@ final class QueueConsumer implements QueueConsumerInterface
      */
     public function __construct(
         InteropContext $interopContext,
-        ExtensionInterface $extension = null,
+        ?ExtensionInterface $extension = null,
         array $boundProcessors = [],
-        LoggerInterface $logger = null,
-        int $receiveTimeout = 10000
+        ?LoggerInterface $logger = null,
+        int $receiveTimeout = 10000,
     ) {
         $this->interopContext = $interopContext;
         $this->receiveTimeout = $receiveTimeout;
@@ -122,7 +122,7 @@ public function bindCallback($queue, callable $processor): QueueConsumerInterfac
         return $this->bind($queue, new CallbackProcessor($processor));
     }
 
-    public function consume(ExtensionInterface $runtimeExtension = null): void
+    public function consume(?ExtensionInterface $runtimeExtension = null): void
     {
         $extension = $runtimeExtension ?
             new ChainExtension([$this->staticExtension, $runtimeExtension]) :
@@ -195,7 +195,7 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
             if (null === $result) {
                 try {
                     $result = $processor->process($message, $this->interopContext);
-                } catch (\Exception | \Throwable $e) {
+                } catch (\Exception|\Throwable $e) {
                     $result = $this->onProcessorException($extension, $consumer, $message, $e, $receivedAt);
                 }
             }
@@ -278,15 +278,13 @@ public function consume(ExtensionInterface $runtimeExtension = null): void
 
     /**
      * @internal
-     *
-     * @param SubscriptionConsumer $fallbackSubscriptionConsumer
      */
     public function setFallbackSubscriptionConsumer(SubscriptionConsumer $fallbackSubscriptionConsumer): void
     {
         $this->fallbackSubscriptionConsumer = $fallbackSubscriptionConsumer;
     }
 
-    private function onEnd(ExtensionInterface $extension, int $startTime, ?int $exitStatus = null, SubscriptionConsumer $subscriptionConsumer = null): void
+    private function onEnd(ExtensionInterface $extension, int $startTime, ?int $exitStatus = null, ?SubscriptionConsumer $subscriptionConsumer = null): void
     {
         $endTime = (int) (microtime(true) * 1000);
 
diff --git a/Consumption/QueueConsumerInterface.php b/Consumption/QueueConsumerInterface.php
index 430aa87..ee25652 100644
--- a/Consumption/QueueConsumerInterface.php
+++ b/Consumption/QueueConsumerInterface.php
@@ -34,9 +34,7 @@ public function bindCallback($queueName, callable $processor): self;
      * Runtime extension - is an extension or a collection of extensions which could be set on runtime.
      * Here's a good example: @see LimitsExtensionsCommandTrait.
      *
-     * @param ExtensionInterface|null $runtimeExtension
-     *
      * @throws \Exception
      */
-    public function consume(ExtensionInterface $runtimeExtension = null): void;
+    public function consume(?ExtensionInterface $runtimeExtension = null): void;
 }
diff --git a/Consumption/Result.php b/Consumption/Result.php
index c5659bc..efa929c 100644
--- a/Consumption/Result.php
+++ b/Consumption/Result.php
@@ -10,19 +10,19 @@ class Result
     /**
      * @see Processor::ACK for more details
      */
-    const ACK = Processor::ACK;
+    public const ACK = Processor::ACK;
 
     /**
      * @see Processor::REJECT for more details
      */
-    const REJECT = Processor::REJECT;
+    public const REJECT = Processor::REJECT;
 
     /**
      * @see Processor::REQUEUE for more details
      */
-    const REQUEUE = Processor::REQUEUE;
+    public const REQUEUE = Processor::REQUEUE;
 
-    const ALREADY_ACKNOWLEDGED = 'enqueue.already_acknowledged';
+    public const ALREADY_ACKNOWLEDGED = 'enqueue.already_acknowledged';
 
     /**
      * @var string
@@ -39,10 +39,6 @@ class Result
      */
     private $reply;
 
-    /**
-     * @param mixed $status
-     * @param mixed $reason
-     */
     public function __construct($status, $reason = '')
     {
         $this->status = (string) $status;
@@ -81,10 +77,7 @@ public function getReply()
         return $this->reply;
     }
 
-    /**
-     * @param InteropMessage|null $reply
-     */
-    public function setReply(InteropMessage $reply = null)
+    public function setReply(?InteropMessage $reply = null)
     {
         $this->reply = $reply;
     }
@@ -120,9 +113,8 @@ public static function requeue($reason = '')
     }
 
     /**
-     * @param InteropMessage $replyMessage
-     * @param string         $status
-     * @param string|null    $reason
+     * @param string      $status
+     * @param string|null $reason
      *
      * @return static
      */
diff --git a/Resources.php b/Resources.php
index 713450f..4c50000 100644
--- a/Resources.php
+++ b/Resources.php
@@ -31,7 +31,7 @@ final class Resources
      *
      * @var array
      */
-    private static $knownConnections = null;
+    private static $knownConnections;
 
     private function __construct()
     {
diff --git a/Router/Recipient.php b/Router/Recipient.php
index c7eac6a..d2f668f 100644
--- a/Router/Recipient.php
+++ b/Router/Recipient.php
@@ -17,10 +17,6 @@ class Recipient
      */
     private $message;
 
-    /**
-     * @param Destination    $destination
-     * @param InteropMessage $message
-     */
     public function __construct(Destination $destination, InteropMessage $message)
     {
         $this->destination = $destination;
diff --git a/Router/RecipientListRouterInterface.php b/Router/RecipientListRouterInterface.php
index d00dd2a..6bb950f 100644
--- a/Router/RecipientListRouterInterface.php
+++ b/Router/RecipientListRouterInterface.php
@@ -7,8 +7,6 @@
 interface RecipientListRouterInterface
 {
     /**
-     * @param InteropMessage $message
-     *
      * @return \Traversable|Recipient[]
      */
     public function route(InteropMessage $message);
diff --git a/Router/RouteRecipientListProcessor.php b/Router/RouteRecipientListProcessor.php
index 22e32e2..22488e3 100644
--- a/Router/RouteRecipientListProcessor.php
+++ b/Router/RouteRecipientListProcessor.php
@@ -13,17 +13,11 @@ class RouteRecipientListProcessor implements Processor
      */
     private $router;
 
-    /**
-     * @param RecipientListRouterInterface $router
-     */
     public function __construct(RecipientListRouterInterface $router)
     {
         $this->router = $router;
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function process(InteropMessage $message, Context $context)
     {
         $producer = $context->createProducer();
diff --git a/Rpc/Promise.php b/Rpc/Promise.php
index 0a5d28f..01b47e1 100644
--- a/Rpc/Promise.php
+++ b/Rpc/Promise.php
@@ -31,11 +31,6 @@ class Promise
      */
     private $message;
 
-    /**
-     * @param \Closure $receiveCallback
-     * @param \Closure $receiveNoWaitCallback
-     * @param \Closure $finallyCallback
-     */
     public function __construct(\Closure $receiveCallback, \Closure $receiveNoWaitCallback, \Closure $finallyCallback)
     {
         $this->receiveCallback = $receiveCallback;
@@ -106,8 +101,7 @@ public function isDeleteReplyQueue()
     }
 
     /**
-     * @param \Closure $cb
-     * @param array    $args
+     * @param array $args
      *
      * @return InteropMessage
      */
@@ -116,8 +110,7 @@ private function doReceive(\Closure $cb, ...$args)
         $message = call_user_func_array($cb, $args);
 
         if (null !== $message && false == $message instanceof InteropMessage) {
-            throw new \RuntimeException(sprintf(
-                'Expected "%s" but got: "%s"', InteropMessage::class, is_object($message) ? get_class($message) : gettype($message)));
+            throw new \RuntimeException(sprintf('Expected "%s" but got: "%s"', InteropMessage::class, is_object($message) ? $message::class : gettype($message)));
         }
 
         return $message;
diff --git a/Rpc/RpcClient.php b/Rpc/RpcClient.php
index 591a4cc..bd3d7ce 100644
--- a/Rpc/RpcClient.php
+++ b/Rpc/RpcClient.php
@@ -19,20 +19,14 @@ class RpcClient
      */
     private $rpcFactory;
 
-    /**
-     * @param Context    $context
-     * @param RpcFactory $promiseFactory
-     */
-    public function __construct(Context $context, RpcFactory $promiseFactory = null)
+    public function __construct(Context $context, ?RpcFactory $promiseFactory = null)
     {
         $this->context = $context;
         $this->rpcFactory = $promiseFactory ?: new RpcFactory($context);
     }
 
     /**
-     * @param Destination    $destination
-     * @param InteropMessage $message
-     * @param int            $timeout
+     * @param int $timeout
      *
      * @throws TimeoutException if the wait timeout is reached
      *
@@ -44,9 +38,7 @@ public function call(Destination $destination, InteropMessage $message, $timeout
     }
 
     /**
-     * @param Destination    $destination
-     * @param InteropMessage $message
-     * @param int            $timeout
+     * @param int $timeout
      *
      * @return Promise
      */
diff --git a/Rpc/RpcFactory.php b/Rpc/RpcFactory.php
index bd054b1..9100bab 100644
--- a/Rpc/RpcFactory.php
+++ b/Rpc/RpcFactory.php
@@ -11,9 +11,6 @@ class RpcFactory
      */
     private $context;
 
-    /**
-     * @param Context $context
-     */
     public function __construct(Context $context)
     {
         $this->context = $context;
diff --git a/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php b/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php
index c541f41..577f159 100644
--- a/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php
+++ b/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php
@@ -38,11 +38,7 @@ private function exclusiveCommandsCouldNotBeRunOnDefaultQueue(RouteCollection $c
     {
         foreach ($collection->all() as $route) {
             if ($route->isCommand() && $route->isProcessorExclusive() && false == $route->getQueue()) {
-                throw new \LogicException(sprintf(
-                    'The command "%s" processor "%s" is exclusive but queue is not specified. Exclusive processors could not be run on a default queue.',
-                    $route->getSource(),
-                    $route->getProcessor()
-                ));
+                throw new \LogicException(sprintf('The command "%s" processor "%s" is exclusive but queue is not specified. Exclusive processors could not be run on a default queue.', $route->getSource(), $route->getProcessor()));
             }
         }
     }
@@ -61,25 +57,13 @@ private function exclusiveCommandProcessorMustBeSingleOnGivenQueue(RouteCollecti
 
             if ($route->isPrefixQueue()) {
                 if (array_key_exists($route->getQueue(), $prefixedQueues)) {
-                    throw new \LogicException(sprintf(
-                        'The command "%s" processor "%s" is exclusive. The queue "%s" already has another exclusive command processor "%s" bound to it.',
-                        $route->getSource(),
-                        $route->getProcessor(),
-                        $route->getQueue(),
-                        $prefixedQueues[$route->getQueue()]
-                    ));
+                    throw new \LogicException(sprintf('The command "%s" processor "%s" is exclusive. The queue "%s" already has another exclusive command processor "%s" bound to it.', $route->getSource(), $route->getProcessor(), $route->getQueue(), $prefixedQueues[$route->getQueue()]));
                 }
 
                 $prefixedQueues[$route->getQueue()] = $route->getProcessor();
             } else {
                 if (array_key_exists($route->getQueue(), $queues)) {
-                    throw new \LogicException(sprintf(
-                        'The command "%s" processor "%s" is exclusive. The queue "%s" already has another exclusive command processor "%s" bound to it.',
-                        $route->getSource(),
-                        $route->getProcessor(),
-                        $route->getQueue(),
-                        $queues[$route->getQueue()]
-                    ));
+                    throw new \LogicException(sprintf('The command "%s" processor "%s" is exclusive. The queue "%s" already has another exclusive command processor "%s" bound to it.', $route->getSource(), $route->getProcessor(), $route->getQueue(), $queues[$route->getQueue()]));
                 }
 
                 $queues[$route->getQueue()] = $route->getProcessor();
@@ -102,7 +86,7 @@ private function customQueueNamesUnique(RouteCollection $collection): void
         $notPrefixedQueues = [];
 
         foreach ($collection->all() as $route) {
-            //default queue
+            // default queue
             $queueName = $route->getQueue();
             if (false == $queueName) {
                 return;
diff --git a/Symfony/Client/DependencyInjection/BuildClientExtensionsPass.php b/Symfony/Client/DependencyInjection/BuildClientExtensionsPass.php
index 5ad45fd..92124f2 100644
--- a/Symfony/Client/DependencyInjection/BuildClientExtensionsPass.php
+++ b/Symfony/Client/DependencyInjection/BuildClientExtensionsPass.php
@@ -46,7 +46,7 @@ public function process(ContainerBuilder $container): void
                 }
             }
 
-            krsort($groupByPriority, SORT_NUMERIC);
+            krsort($groupByPriority, \SORT_NUMERIC);
 
             $flatExtensions = [];
             foreach ($groupByPriority as $extension) {
diff --git a/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php b/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php
index 5eee3ef..4adc09e 100644
--- a/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php
+++ b/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php
@@ -68,7 +68,7 @@ public function process(ContainerBuilder $container): void
 
                     // 0.8 command subscriber
                     if (isset($commands['processorName'])) {
-                        @trigger_error('The command subscriber 0.8 syntax is deprecated since Enqueue 0.9.', E_USER_DEPRECATED);
+                        @trigger_error('The command subscriber 0.8 syntax is deprecated since Enqueue 0.9.', \E_USER_DEPRECATED);
 
                         $source = $commands['processorName'];
                         $processor = $params['processorName'] ?? $serviceId;
@@ -117,11 +117,7 @@ public function process(ContainerBuilder $container): void
 
                             $routeCollection->add(new Route($source, Route::COMMAND, $processor, $options));
                         } else {
-                            throw new \LogicException(sprintf(
-                                'Command subscriber configuration is invalid for "%s::getSubscribedCommand()". "%s"',
-                                $processorClass,
-                                json_encode($processorClass::getSubscribedCommand())
-                            ));
+                            throw new \LogicException(sprintf('Command subscriber configuration is invalid for "%s::getSubscribedCommand()". "%s"', $processorClass, json_encode($processorClass::getSubscribedCommand())));
                         }
                     }
                 }
diff --git a/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php b/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php
index c1f1ce9..274847c 100644
--- a/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php
+++ b/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php
@@ -46,7 +46,7 @@ public function process(ContainerBuilder $container): void
                 }
             }
 
-            krsort($groupByPriority, SORT_NUMERIC);
+            krsort($groupByPriority, \SORT_NUMERIC);
 
             $flatExtensions = [];
             foreach ($groupByPriority as $extension) {
diff --git a/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php b/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php
index 4999921..ef01e6f 100644
--- a/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php
+++ b/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPass.php
@@ -72,7 +72,7 @@ public function process(ContainerBuilder $container): void
 
                         // 0.8 topic subscriber
                         } elseif (is_array($params) && is_string($key)) {
-                            @trigger_error('The topic subscriber 0.8 syntax is deprecated since Enqueue 0.9.', E_USER_DEPRECATED);
+                            @trigger_error('The topic subscriber 0.8 syntax is deprecated since Enqueue 0.9.', \E_USER_DEPRECATED);
 
                             $source = $key;
                             $processor = $params['processorName'] ?? $serviceId;
@@ -109,11 +109,7 @@ public function process(ContainerBuilder $container): void
 
                             $routeCollection->add(new Route($source, Route::TOPIC, $processor, $options));
                         } else {
-                            throw new \LogicException(sprintf(
-                                'Topic subscriber configuration is invalid for "%s::getSubscribedTopics()". Got "%s"',
-                                $processorClass,
-                                json_encode($processorClass::getSubscribedTopics())
-                            ));
+                            throw new \LogicException(sprintf('Topic subscriber configuration is invalid for "%s::getSubscribedTopics()". Got "%s"', $processorClass, json_encode($processorClass::getSubscribedTopics())));
                         }
                     }
                 }
diff --git a/Symfony/Client/FlushSpoolProducerListener.php b/Symfony/Client/FlushSpoolProducerListener.php
index 8fa6fb4..00543f6 100644
--- a/Symfony/Client/FlushSpoolProducerListener.php
+++ b/Symfony/Client/FlushSpoolProducerListener.php
@@ -24,9 +24,6 @@ public function flushMessages()
         $this->producer->flush();
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public static function getSubscribedEvents(): array
     {
         $events = [];
diff --git a/Symfony/Client/SetupBrokerExtensionCommandTrait.php b/Symfony/Client/SetupBrokerExtensionCommandTrait.php
index 2888f56..96e0d2d 100644
--- a/Symfony/Client/SetupBrokerExtensionCommandTrait.php
+++ b/Symfony/Client/SetupBrokerExtensionCommandTrait.php
@@ -10,9 +10,6 @@
 
 trait SetupBrokerExtensionCommandTrait
 {
-    /**
-     * {@inheritdoc}
-     */
     protected function configureSetupBrokerExtension()
     {
         $this
@@ -21,9 +18,6 @@ protected function configureSetupBrokerExtension()
     }
 
     /**
-     * @param InputInterface  $input
-     * @param DriverInterface $driver
-     *
      * @return ExtensionInterface
      */
     protected function getSetupBrokerExtension(InputInterface $input, DriverInterface $driver)
diff --git a/Symfony/Consumption/LimitsExtensionsCommandTrait.php b/Symfony/Consumption/LimitsExtensionsCommandTrait.php
index d7363db..d8351ac 100644
--- a/Symfony/Consumption/LimitsExtensionsCommandTrait.php
+++ b/Symfony/Consumption/LimitsExtensionsCommandTrait.php
@@ -23,9 +23,6 @@ protected function configureLimitsExtensions()
     }
 
     /**
-     * @param InputInterface  $input
-     * @param OutputInterface $output
-     *
      * @throws \Exception
      *
      * @return ExtensionInterface[]
diff --git a/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php b/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php
index 9df852d..fd736f2 100644
--- a/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php
+++ b/Symfony/Consumption/QueueConsumerOptionsCommandTrait.php
@@ -8,9 +8,6 @@
 
 trait QueueConsumerOptionsCommandTrait
 {
-    /**
-     * {@inheritdoc}
-     */
     protected function configureQueueConsumerOptions()
     {
         $this
@@ -18,10 +15,6 @@ protected function configureQueueConsumerOptions()
         ;
     }
 
-    /**
-     * @param QueueConsumerInterface $consumer
-     * @param InputInterface         $input
-     */
     protected function setQueueConsumerOptions(QueueConsumerInterface $consumer, InputInterface $input)
     {
         if (null !== $receiveTimeout = $input->getOption('receive-timeout')) {
diff --git a/Symfony/DependencyInjection/BuildConsumptionExtensionsPass.php b/Symfony/DependencyInjection/BuildConsumptionExtensionsPass.php
index 352682b..99f274e 100644
--- a/Symfony/DependencyInjection/BuildConsumptionExtensionsPass.php
+++ b/Symfony/DependencyInjection/BuildConsumptionExtensionsPass.php
@@ -43,7 +43,7 @@ public function process(ContainerBuilder $container): void
                 }
             }
 
-            krsort($groupByPriority, SORT_NUMERIC);
+            krsort($groupByPriority, \SORT_NUMERIC);
 
             $flatExtensions = [];
             foreach ($groupByPriority as $extension) {
diff --git a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
index 56b855f..a660126 100644
--- a/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
+++ b/Tests/Client/ConsumptionExtension/DelayRedeliveredMessageExtensionTest.php
@@ -23,7 +23,6 @@
 
 class DelayRedeliveredMessageExtensionTest extends TestCase
 {
-
     public function testShouldSendDelayedMessageAndRejectOriginalMessage()
     {
         $queue = new NullQueue('queue');
diff --git a/Tests/Client/DriverFactoryTest.php b/Tests/Client/DriverFactoryTest.php
index ef504e0..3d9d7b9 100644
--- a/Tests/Client/DriverFactoryTest.php
+++ b/Tests/Client/DriverFactoryTest.php
@@ -105,7 +105,7 @@ public function testReturnsExpectedFactories(
         string $connectionFactoryClass,
         string $contextClass,
         array $conifg,
-        string $expectedDriverClass
+        string $expectedDriverClass,
     ) {
         $connectionFactoryMock = $this->createMock($connectionFactoryClass);
         $connectionFactoryMock
@@ -134,7 +134,7 @@ public static function provideDSN()
         yield ['file:', FsConnectionFactory::class, FsContext::class, [], FsDriver::class];
 
         // https://github.com/php-enqueue/enqueue-dev/issues/511
-//        yield ['gearman:', GearmanConnectionFactory::class, NullContext::class, [], NullDriver::class];
+        //        yield ['gearman:', GearmanConnectionFactory::class, NullContext::class, [], NullDriver::class];
 
         yield ['gps:', GpsConnectionFactory::class, GpsContext::class, [], GpsDriver::class];
 
diff --git a/Tests/Client/Extension/PrepareBodyExtensionTest.php b/Tests/Client/Extension/PrepareBodyExtensionTest.php
index d1c2603..c3032cc 100644
--- a/Tests/Client/Extension/PrepareBodyExtensionTest.php
+++ b/Tests/Client/Extension/PrepareBodyExtensionTest.php
@@ -25,14 +25,13 @@ public function testShouldImplementExtensionInterface()
     /**
      * @dataProvider provideMessages
      *
-     * @param mixed      $body
      * @param mixed|null $contentType
      */
     public function testShouldSendStringUnchangedAndAddPlainTextContentTypeIfEmpty(
         $body,
         $contentType,
         string $expectedBody,
-        string $expectedContentType
+        string $expectedContentType,
     ) {
         $message = new Message($body);
         $message->setContentType($contentType);
diff --git a/Tests/Client/PreSendTest.php b/Tests/Client/PreSendTest.php
index ae94003..01a7e50 100644
--- a/Tests/Client/PreSendTest.php
+++ b/Tests/Client/PreSendTest.php
@@ -51,7 +51,7 @@ public function testCouldChangeTopic()
             $this->createDriverMock()
         );
 
-        //guard
+        // guard
         $this->assertSame('aCommandOrTopic', $context->getTopic());
 
         $context->changeTopic('theChangedTopic');
@@ -68,7 +68,7 @@ public function testCouldChangeCommand()
             $this->createDriverMock()
         );
 
-        //guard
+        // guard
         $this->assertSame('aCommandOrTopic', $context->getCommand());
 
         $context->changeCommand('theChangedCommand');
@@ -85,7 +85,7 @@ public function testCouldChangeBody()
             $this->createDriverMock()
         );
 
-        //guard
+        // guard
         $this->assertSame('aBody', $context->getMessage()->getBody());
         $this->assertNull($context->getMessage()->getContentType());
 
diff --git a/Tests/Client/ProducerTest.php b/Tests/Client/ProducerTest.php
index 9d4e2a3..23b004a 100644
--- a/Tests/Client/ProducerTest.php
+++ b/Tests/Client/ProducerTest.php
@@ -3,7 +3,6 @@
 namespace Enqueue\Tests\Client;
 
 use Enqueue\Client\DriverInterface;
-use Enqueue\Client\ExtensionInterface;
 use Enqueue\Client\Producer;
 use Enqueue\Client\ProducerInterface;
 use Enqueue\Rpc\RpcFactory;
diff --git a/Tests/Client/RouterProcessorTest.php b/Tests/Client/RouterProcessorTest.php
index e2611d0..7d29711 100644
--- a/Tests/Client/RouterProcessorTest.php
+++ b/Tests/Client/RouterProcessorTest.php
@@ -188,7 +188,7 @@ public function testShouldDoNotModifyOriginalMessage()
 
         $result = $processor->process($message, new NullContext());
 
-        //guard
+        // guard
         $this->assertEquals(Result::ACK, $result->getStatus());
 
         $this->assertSame('theBody', $message->getBody());
diff --git a/Tests/Client/TraceableProducerTest.php b/Tests/Client/TraceableProducerTest.php
index ff04285..b0df066 100644
--- a/Tests/Client/TraceableProducerTest.php
+++ b/Tests/Client/TraceableProducerTest.php
@@ -272,8 +272,8 @@ public function testShouldAllowGetInfoSentToSameTopic()
         $producer->sendEvent('aFooTopic', 'aFooBody');
 
         Assert::assertArraySubset([
-                ['topic' => 'aFooTopic', 'body' => 'aFooBody'],
-                ['topic' => 'aFooTopic', 'body' => 'aFooBody'],
+            ['topic' => 'aFooTopic', 'body' => 'aFooBody'],
+            ['topic' => 'aFooTopic', 'body' => 'aFooBody'],
         ], $producer->getTraces());
     }
 
@@ -354,7 +354,7 @@ public function testShouldAllowClearStoredTraces()
 
         $producer->sendEvent('aFooTopic', 'aFooBody');
 
-        //guard
+        // guard
         $this->assertNotEmpty($producer->getTraces());
 
         $producer->clearTraces();
diff --git a/Tests/ConnectionFactoryFactoryTest.php b/Tests/ConnectionFactoryFactoryTest.php
index fe2de69..b6b5b4d 100644
--- a/Tests/ConnectionFactoryFactoryTest.php
+++ b/Tests/ConnectionFactoryFactoryTest.php
@@ -158,7 +158,7 @@ public static function provideDSN()
         yield ['file:', FsConnectionFactory::class];
 
         // https://github.com/php-enqueue/enqueue-dev/issues/511
-//        yield ['gearman:', GearmanConnectionFactory::class];
+        //        yield ['gearman:', GearmanConnectionFactory::class];
 
         yield ['gps:', GpsConnectionFactory::class];
 
diff --git a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
index 1548299..25ac858 100644
--- a/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
+++ b/Tests/Consumption/Extension/LimitConsumerMemoryExtensionTest.php
@@ -124,7 +124,7 @@ public function testOnPreConsumeShouldNotInterruptExecutionIfMemoryLimitIsNotRea
         $this->assertFalse($context->isExecutionInterrupted());
 
         // test
-        $extension = new LimitConsumerMemoryExtension(PHP_INT_MAX);
+        $extension = new LimitConsumerMemoryExtension(\PHP_INT_MAX);
         $extension->onPreConsume($context);
 
         $this->assertFalse($context->isExecutionInterrupted());
@@ -145,7 +145,7 @@ public function testOnPostConsumeShouldNotInterruptExecutionIfMemoryLimitIsNotRe
         $this->assertFalse($postConsume->isExecutionInterrupted());
 
         // test
-        $extension = new LimitConsumerMemoryExtension(PHP_INT_MAX);
+        $extension = new LimitConsumerMemoryExtension(\PHP_INT_MAX);
         $extension->onPostConsume($postConsume);
 
         $this->assertFalse($postConsume->isExecutionInterrupted());
@@ -166,7 +166,7 @@ public function testOnPostMessageReceivedShouldNotInterruptExecutionIfMemoryLimi
         $this->assertFalse($postReceivedMessage->isExecutionInterrupted());
 
         // test
-        $extension = new LimitConsumerMemoryExtension(PHP_INT_MAX);
+        $extension = new LimitConsumerMemoryExtension(\PHP_INT_MAX);
         $extension->onPostMessageReceived($postReceivedMessage);
 
         $this->assertFalse($postReceivedMessage->isExecutionInterrupted());
diff --git a/Tests/Consumption/Extension/LoggerExtensionTest.php b/Tests/Consumption/Extension/LoggerExtensionTest.php
index 455468e..666892e 100644
--- a/Tests/Consumption/Extension/LoggerExtensionTest.php
+++ b/Tests/Consumption/Extension/LoggerExtensionTest.php
@@ -42,7 +42,7 @@ public function testShouldAddInfoMessageOnStart()
         $logger
             ->expects($this->once())
             ->method('debug')
-            ->with(sprintf('Change logger from "%s" to "%s"', get_class($logger), get_class($previousLogger)))
+            ->with(sprintf('Change logger from "%s" to "%s"', $logger::class, $previousLogger::class))
         ;
 
         $extension = new LoggerExtension($logger);
diff --git a/Tests/Consumption/QueueConsumerTest.php b/Tests/Consumption/QueueConsumerTest.php
index 06edab3..2bcc253 100644
--- a/Tests/Consumption/QueueConsumerTest.php
+++ b/Tests/Consumption/QueueConsumerTest.php
@@ -1455,7 +1455,7 @@ private function createContextWithoutSubscriptionConsumerMock(): InteropContext
     /**
      * @return \PHPUnit\Framework\MockObject\MockObject|InteropContext
      */
-    private function createContextStub(Consumer $consumer = null): InteropContext
+    private function createContextStub(?Consumer $consumer = null): InteropContext
     {
         $context = $this->createContextWithoutSubscriptionConsumerMock();
         $context
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
index 1a5f65a..14a48be 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
@@ -154,7 +154,7 @@ public function testShouldRegisterProcessorIfCommandsIsString()
         $container->setParameter('enqueue.clients', ['foo']);
         $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
-        $container->register('aFooProcessor', get_class($processor))
+        $container->register('aFooProcessor', $processor::class)
             ->addTag('enqueue.command_subscriber')
         ;
 
@@ -188,7 +188,7 @@ public function testThrowIfCommandSubscriberReturnsNothing()
         $container->setParameter('enqueue.clients', ['foo']);
         $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
-        $container->register('aFooProcessor', get_class($processor))
+        $container->register('aFooProcessor', $processor::class)
             ->addTag('enqueue.command_subscriber')
         ;
 
@@ -210,7 +210,7 @@ public function testShouldRegisterProcessorIfCommandsAreStrings()
         $container->setParameter('enqueue.clients', ['foo']);
         $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
-        $container->register('aFooProcessor', get_class($processor))
+        $container->register('aFooProcessor', $processor::class)
             ->addTag('enqueue.command_subscriber')
         ;
 
@@ -254,7 +254,7 @@ public function testShouldRegisterProcessorIfParamSingleCommandArray()
         $container->setParameter('enqueue.clients', ['foo']);
         $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
-        $container->register('aFooProcessor', get_class($processor))
+        $container->register('aFooProcessor', $processor::class)
             ->addTag('enqueue.command_subscriber')
         ;
 
@@ -293,7 +293,7 @@ public function testShouldRegisterProcessorIfCommandsAreParamArrays()
         $container->setParameter('enqueue.clients', ['foo']);
         $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
-        $container->register('aFooProcessor', get_class($processor))
+        $container->register('aFooProcessor', $processor::class)
             ->addTag('enqueue.command_subscriber')
         ;
 
@@ -335,7 +335,7 @@ public function testThrowIfCommandSubscriberParamsInvalid()
         $container->setParameter('enqueue.clients', ['foo']);
         $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
-        $container->register('aFooProcessor', get_class($processor))
+        $container->register('aFooProcessor', $processor::class)
             ->addTag('enqueue.command_subscriber')
         ;
 
@@ -360,7 +360,7 @@ public function testShouldMergeExtractedRoutesWithAlreadySetInCollection()
         $container->setParameter('enqueue.clients', ['foo']);
         $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
-        $container->register('aFooProcessor', get_class($processor))
+        $container->register('aFooProcessor', $processor::class)
             ->addTag('enqueue.command_subscriber')
         ;
 
@@ -410,7 +410,7 @@ public function testShouldRegister08CommandProcessor()
         $container->setParameter('enqueue.clients', ['default']);
         $container->setParameter('enqueue.default_client', 'default');
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
-        $container->register('aFooProcessor', get_class($processor))
+        $container->register('aFooProcessor', $processor::class)
             ->addTag('enqueue.command_subscriber')
         ;
 
@@ -438,7 +438,7 @@ public function testShouldRegister08CommandProcessor()
 
     private function createCommandSubscriberProcessor($commandSubscriberReturns = ['aCommand'])
     {
-        $processor = new class() implements Processor, CommandSubscriberInterface {
+        $processor = new class implements Processor, CommandSubscriberInterface {
             public static $return;
 
             public function process(InteropMessage $message, Context $context)
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
index b642b90..b9d798c 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
@@ -154,7 +154,7 @@ public function testShouldRegisterProcessorIfTopicsIsString()
         $container->setParameter('enqueue.clients', ['foo']);
         $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
-        $container->register('aFooProcessor', get_class($processor))
+        $container->register('aFooProcessor', $processor::class)
             ->addTag('enqueue.topic_subscriber')
         ;
 
@@ -188,7 +188,7 @@ public function testThrowIfTopicSubscriberReturnsNothing()
         $container->setParameter('enqueue.clients', ['foo']);
         $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
-        $container->register('aFooProcessor', get_class($processor))
+        $container->register('aFooProcessor', $processor::class)
             ->addTag('enqueue.topic_subscriber')
         ;
 
@@ -210,7 +210,7 @@ public function testShouldRegisterProcessorIfTopicsAreStrings()
         $container->setParameter('enqueue.clients', ['foo']);
         $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
-        $container->register('aFooProcessor', get_class($processor))
+        $container->register('aFooProcessor', $processor::class)
             ->addTag('enqueue.topic_subscriber')
         ;
 
@@ -253,7 +253,7 @@ public function testShouldRegisterProcessorIfTopicsAreParamArrays()
         $container->setParameter('enqueue.clients', ['foo']);
         $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
-        $container->register('aFooProcessor', get_class($processor))
+        $container->register('aFooProcessor', $processor::class)
             ->addTag('enqueue.topic_subscriber')
         ;
 
@@ -295,7 +295,7 @@ public function testThrowIfTopicSubscriberParamsInvalid()
         $container->setParameter('enqueue.clients', ['foo']);
         $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
-        $container->register('aFooProcessor', get_class($processor))
+        $container->register('aFooProcessor', $processor::class)
             ->addTag('enqueue.topic_subscriber')
         ;
 
@@ -320,7 +320,7 @@ public function testShouldMergeExtractedRoutesWithAlreadySetInCollection()
         $container->setParameter('enqueue.clients', ['foo']);
         $container->setParameter('enqueue.default_client', 'foo');
         $container->setDefinition('enqueue.client.foo.route_collection', $routeCollection);
-        $container->register('aFooProcessor', get_class($processor))
+        $container->register('aFooProcessor', $processor::class)
             ->addTag('enqueue.topic_subscriber')
         ;
 
@@ -367,7 +367,7 @@ public function testShouldRegister08TopicSubscriber()
         $container->setParameter('enqueue.clients', ['default']);
         $container->setParameter('enqueue.default_client', 'default');
         $container->setDefinition('enqueue.client.default.route_collection', $routeCollection);
-        $container->register('aFooProcessor', get_class($processor))
+        $container->register('aFooProcessor', $processor::class)
             ->addTag('enqueue.topic_subscriber')
         ;
 
@@ -402,7 +402,7 @@ public function testShouldRegister08TopicSubscriber()
 
     private function createTopicSubscriberProcessor($topicSubscriberReturns = ['aTopic'])
     {
-        $processor = new class() implements Processor, TopicSubscriberInterface {
+        $processor = new class implements Processor, TopicSubscriberInterface {
             public static $return;
 
             public function process(InteropMessage $message, Context $context)
diff --git a/Tests/Symfony/Consumption/LimitsExtensionsCommandTraitTest.php b/Tests/Symfony/Consumption/LimitsExtensionsCommandTraitTest.php
index e929123..f47a321 100644
--- a/Tests/Symfony/Consumption/LimitsExtensionsCommandTraitTest.php
+++ b/Tests/Symfony/Consumption/LimitsExtensionsCommandTraitTest.php
@@ -109,8 +109,6 @@ public function testShouldAddThreeLimitExtensions()
     }
 
     /**
-     * @param mixed $inputValue
-     * @param bool  $enabled
      * @dataProvider provideNicenessValues
      */
     public function testShouldAddNicenessExtension($inputValue, bool $enabled)
diff --git a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
index c5ff1c8..9094074 100644
--- a/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
+++ b/Tests/Symfony/DependencyInjection/TransportFactoryTest.php
@@ -148,7 +148,7 @@ public function testThrowIfFactoryClassAndFactoryServiceSetAtTheSameTime()
                 'dsn' => 'foo:',
                 'factory_class' => 'aFactoryClass',
                 'factory_service' => 'aFactoryService',
-        ], ]]);
+            ], ]]);
     }
 
     public function testThrowIfConnectionFactoryClassUsedWithFactoryClassAtTheSameTime()
@@ -166,7 +166,7 @@ public function testThrowIfConnectionFactoryClassUsedWithFactoryClassAtTheSameTi
                 'dsn' => 'foo:',
                 'connection_factory_class' => 'aFactoryClass',
                 'factory_service' => 'aFactoryService',
-        ], ]]);
+            ], ]]);
     }
 
     public function testThrowIfConnectionFactoryClassUsedWithFactoryServiceAtTheSameTime()
@@ -183,7 +183,7 @@ public function testThrowIfConnectionFactoryClassUsedWithFactoryServiceAtTheSame
                 'dsn' => 'foo:',
                 'connection_factory_class' => 'aFactoryClass',
                 'factory_service' => 'aFactoryService',
-        ], ]]);
+            ], ]]);
     }
 
     public function testShouldAllowSetFactoryClass()
@@ -197,7 +197,7 @@ public function testShouldAllowSetFactoryClass()
             'transport' => [
                 'dsn' => 'foo:',
                 'factory_class' => 'theFactoryClass',
-        ], ]]);
+            ], ]]);
 
         $this->assertArrayHasKey('factory_class', $config['transport']);
         $this->assertSame('theFactoryClass', $config['transport']['factory_class']);
@@ -214,7 +214,7 @@ public function testShouldAllowSetFactoryService()
             'transport' => [
                 'dsn' => 'foo:',
                 'factory_service' => 'theFactoryService',
-        ], ]]);
+            ], ]]);
 
         $this->assertArrayHasKey('factory_service', $config['transport']);
         $this->assertSame('theFactoryService', $config['transport']['factory_service']);
@@ -231,7 +231,7 @@ public function testShouldAllowSetConnectionFactoryClass()
             'transport' => [
                 'dsn' => 'foo:',
                 'connection_factory_class' => 'theFactoryClass',
-        ], ]]);
+            ], ]]);
 
         $this->assertArrayHasKey('connection_factory_class', $config['transport']);
         $this->assertSame('theFactoryClass', $config['transport']['connection_factory_class']);
diff --git a/Tests/Util/JSONTest.php b/Tests/Util/JSONTest.php
index a6f5833..1a3df42 100644
--- a/Tests/Util/JSONTest.php
+++ b/Tests/Util/JSONTest.php
@@ -39,8 +39,6 @@ public function nonStringDataProvider()
 
     /**
      * @dataProvider nonStringDataProvider
-     *
-     * @param mixed $value
      */
     public function testShouldThrowExceptionIfInputIsNotString($value)
     {
diff --git a/Tests/Util/VarExportTest.php b/Tests/Util/VarExportTest.php
index e8034a1..b71e78a 100644
--- a/Tests/Util/VarExportTest.php
+++ b/Tests/Util/VarExportTest.php
@@ -9,9 +9,6 @@ class VarExportTest extends TestCase
 {
     /**
      * @dataProvider provideValues
-     *
-     * @param mixed $value
-     * @param mixed $expected
      */
     public function testShouldConvertValueToStringUsingVarExportFunction($value, $expected)
     {
diff --git a/Tests/fix_composer_json.php b/Tests/fix_composer_json.php
index dfbec62..324f184 100644
--- a/Tests/fix_composer_json.php
+++ b/Tests/fix_composer_json.php
@@ -8,4 +8,4 @@
 $composerJson['config']['platform']['ext-rdkafka'] = '3.3';
 $composerJson['config']['platform']['ext-gearman'] = '2';
 
-file_put_contents(__DIR__.'/../composer.json', json_encode($composerJson, JSON_PRETTY_PRINT));
+file_put_contents(__DIR__.'/../composer.json', json_encode($composerJson, \JSON_PRETTY_PRINT));
diff --git a/Util/JSON.php b/Util/JSON.php
index f85738e..67411af 100644
--- a/Util/JSON.php
+++ b/Util/JSON.php
@@ -14,10 +14,7 @@ class JSON
     public static function decode($string)
     {
         if (!is_string($string)) {
-            throw new \InvalidArgumentException(sprintf(
-                'Accept only string argument but got: "%s"',
-                is_object($string) ? get_class($string) : gettype($string)
-            ));
+            throw new \InvalidArgumentException(sprintf('Accept only string argument but got: "%s"', is_object($string) ? $string::class : gettype($string)));
         }
 
         // PHP7 fix - empty string and null cause syntax error
@@ -26,32 +23,22 @@ public static function decode($string)
         }
 
         $decoded = json_decode($string, true);
-        if (JSON_ERROR_NONE !== json_last_error()) {
-            throw new \InvalidArgumentException(sprintf(
-                'The malformed json given. Error %s and message %s',
-                json_last_error(),
-                json_last_error_msg()
-            ));
+        if (\JSON_ERROR_NONE !== json_last_error()) {
+            throw new \InvalidArgumentException(sprintf('The malformed json given. Error %s and message %s', json_last_error(), json_last_error_msg()));
         }
 
         return $decoded;
     }
 
     /**
-     * @param mixed $value
-     *
      * @return string
      */
     public static function encode($value)
     {
-        $encoded = json_encode($value, JSON_UNESCAPED_UNICODE);
-
-        if (JSON_ERROR_NONE !== json_last_error()) {
-            throw new \InvalidArgumentException(sprintf(
-                'Could not encode value into json. Error %s and message %s',
-                json_last_error(),
-                json_last_error_msg()
-            ));
+        $encoded = json_encode($value, \JSON_UNESCAPED_UNICODE);
+
+        if (\JSON_ERROR_NONE !== json_last_error()) {
+            throw new \InvalidArgumentException(sprintf('Could not encode value into json. Error %s and message %s', json_last_error(), json_last_error_msg()));
         }
 
         return $encoded;
diff --git a/Util/Stringify.php b/Util/Stringify.php
index 39b1f13..12358d7 100644
--- a/Util/Stringify.php
+++ b/Util/Stringify.php
@@ -7,14 +7,8 @@
  */
 class Stringify
 {
-    /**
-     * @var mixed
-     */
     private $value;
 
-    /**
-     * @param mixed $value
-     */
     public function __construct($value)
     {
         $this->value = $value;
@@ -26,7 +20,7 @@ public function __toString(): string
             return $this->value;
         }
 
-        return json_encode($this->value, JSON_UNESCAPED_SLASHES);
+        return json_encode($this->value, \JSON_UNESCAPED_SLASHES);
     }
 
     public static function that($value): self
diff --git a/Util/VarExport.php b/Util/VarExport.php
index 4a48afa..9a91470 100644
--- a/Util/VarExport.php
+++ b/Util/VarExport.php
@@ -7,14 +7,8 @@
  */
 class VarExport
 {
-    /**
-     * @var mixed
-     */
     private $value;
 
-    /**
-     * @param mixed $value
-     */
     public function __construct($value)
     {
         $this->value = $value;

From 8cef42721bf79ef8e9bc631f88ccb07d2034dd9c Mon Sep 17 00:00:00 2001
From: James Read <james.read.18@gmail.com>
Date: Thu, 16 Jan 2025 22:20:11 +0000
Subject: [PATCH 335/337] Fixing static analysis issues

Fixing issue raised by phpstan
---
 Client/Config.php                             |  2 +-
 Client/Driver/StompManagementClient.php       |  2 +-
 .../Exception/InvalidArgumentException.php    |  2 +-
 Consumption/Result.php                        |  8 ++++----
 Rpc/TimeoutException.php                      |  2 +-
 .../SetupBrokerExtensionCommandTrait.php      |  2 +-
 Symfony/DiUtils.php                           |  2 +-
 .../FallbackSubscriptionConsumerTest.php      | 20 ++-----------------
 Tests/Mocks/JsonSerializableObject.php        |  3 ++-
 .../BuildCommandSubscriberRoutesPassTest.php  |  1 +
 .../BuildTopicSubscriberRoutesPassTest.php    |  1 +
 .../Mock/QueueSubscriberProcessor.php         |  1 +
 Tests/Util/Fixtures/JsonSerializableClass.php |  3 ++-
 Util/Stringify.php                            |  2 +-
 14 files changed, 20 insertions(+), 31 deletions(-)

diff --git a/Client/Config.php b/Client/Config.php
index 34057e1..8210dff 100644
--- a/Client/Config.php
+++ b/Client/Config.php
@@ -163,7 +163,7 @@ public static function create(
         array $transportConfig = [],
         array $driverConfig = [],
     ): self {
-        return new static(
+        return new self(
             $prefix ?: '',
             $separator ?: '.',
             $app ?: '',
diff --git a/Client/Driver/StompManagementClient.php b/Client/Driver/StompManagementClient.php
index 5880672..0d64450 100644
--- a/Client/Driver/StompManagementClient.php
+++ b/Client/Driver/StompManagementClient.php
@@ -24,7 +24,7 @@ public function __construct(Client $client, string $vhost = '/')
 
     public static function create(string $vhost = '/', string $host = 'localhost', int $port = 15672, string $login = 'guest', string $password = 'guest'): self
     {
-        return new static(new Client(null, 'http://'.$host.':'.$port, $login, $password), $vhost);
+        return new self(new Client(null, 'http://'.$host.':'.$port, $login, $password), $vhost);
     }
 
     public function declareQueue(string $name, array $options)
diff --git a/Consumption/Exception/InvalidArgumentException.php b/Consumption/Exception/InvalidArgumentException.php
index 76ea83a..89a2f4c 100644
--- a/Consumption/Exception/InvalidArgumentException.php
+++ b/Consumption/Exception/InvalidArgumentException.php
@@ -12,7 +12,7 @@ class InvalidArgumentException extends \InvalidArgumentException implements Exce
     public static function assertInstanceOf($argument, $class)
     {
         if (false == $argument instanceof $class) {
-            throw new static(sprintf('The argument must be an instance of %s but got %s.', $class, is_object($argument) ? $argument::class : gettype($argument)));
+            throw new self(sprintf('The argument must be an instance of %s but got %s.', $class, is_object($argument) ? $argument::class : gettype($argument)));
         }
     }
 }
diff --git a/Consumption/Result.php b/Consumption/Result.php
index efa929c..69dd790 100644
--- a/Consumption/Result.php
+++ b/Consumption/Result.php
@@ -89,7 +89,7 @@ public function setReply(?InteropMessage $reply = null)
      */
     public static function ack($reason = '')
     {
-        return new static(self::ACK, $reason);
+        return new self(self::ACK, $reason);
     }
 
     /**
@@ -99,7 +99,7 @@ public static function ack($reason = '')
      */
     public static function reject($reason)
     {
-        return new static(self::REJECT, $reason);
+        return new self(self::REJECT, $reason);
     }
 
     /**
@@ -109,7 +109,7 @@ public static function reject($reason)
      */
     public static function requeue($reason = '')
     {
-        return new static(self::REQUEUE, $reason);
+        return new self(self::REQUEUE, $reason);
     }
 
     /**
@@ -122,7 +122,7 @@ public static function reply(InteropMessage $replyMessage, $status = self::ACK,
     {
         $status = null === $status ? self::ACK : $status;
 
-        $result = new static($status, $reason);
+        $result = new self($status, $reason);
         $result->setReply($replyMessage);
 
         return $result;
diff --git a/Rpc/TimeoutException.php b/Rpc/TimeoutException.php
index a0b0655..a7f68b9 100644
--- a/Rpc/TimeoutException.php
+++ b/Rpc/TimeoutException.php
@@ -12,6 +12,6 @@ class TimeoutException extends \LogicException
      */
     public static function create($timeout, $correlationId)
     {
-        return new static(sprintf('Rpc call timeout is reached without receiving a reply message. Timeout: %s, CorrelationId: %s', $timeout, $correlationId));
+        return new self(sprintf('Rpc call timeout is reached without receiving a reply message. Timeout: %s, CorrelationId: %s', $timeout, $correlationId));
     }
 }
diff --git a/Symfony/Client/SetupBrokerExtensionCommandTrait.php b/Symfony/Client/SetupBrokerExtensionCommandTrait.php
index 96e0d2d..bcc4f7b 100644
--- a/Symfony/Client/SetupBrokerExtensionCommandTrait.php
+++ b/Symfony/Client/SetupBrokerExtensionCommandTrait.php
@@ -18,7 +18,7 @@ protected function configureSetupBrokerExtension()
     }
 
     /**
-     * @return ExtensionInterface
+     * @return ExtensionInterface|null
      */
     protected function getSetupBrokerExtension(InputInterface $input, DriverInterface $driver)
     {
diff --git a/Symfony/DiUtils.php b/Symfony/DiUtils.php
index 335ff04..be45287 100644
--- a/Symfony/DiUtils.php
+++ b/Symfony/DiUtils.php
@@ -27,7 +27,7 @@ public function __construct(string $moduleName, string $configName)
 
     public static function create(string $moduleName, string $configName): self
     {
-        return new static($moduleName, $configName);
+        return new self($moduleName, $configName);
     }
 
     public function getModuleName(): string
diff --git a/Tests/Consumption/FallbackSubscriptionConsumerTest.php b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
index 5391f0c..73fba7b 100644
--- a/Tests/Consumption/FallbackSubscriptionConsumerTest.php
+++ b/Tests/Consumption/FallbackSubscriptionConsumerTest.php
@@ -147,34 +147,18 @@ public function testShouldConsumeMessagesFromTwoQueuesInExpectedOrder()
         $fourthMessage = $this->createMessageStub('fourth');
         $fifthMessage = $this->createMessageStub('fifth');
 
-        $fooMessages = [null, $firstMessage, null, $secondMessage, $thirdMessage];
-
         $fooConsumer = $this->createConsumerStub('foo_queue');
         $fooConsumer
             ->expects($this->any())
             ->method('receiveNoWait')
-            ->willReturnCallback(function () use (&$fooMessages) {
-                if (empty($fooMessages)) {
-                    return null;
-                }
-
-                return array_shift($fooMessages);
-            })
+            ->willReturnOnConsecutiveCalls(null, $firstMessage, null, $secondMessage, $thirdMessage)
         ;
 
-        $barMessages = [$fourthMessage, null, null, $fifthMessage];
-
         $barConsumer = $this->createConsumerStub('bar_queue');
         $barConsumer
             ->expects($this->any())
             ->method('receiveNoWait')
-            ->willReturnCallback(function () use (&$barMessages) {
-                if (empty($barMessages)) {
-                    return null;
-                }
-
-                return array_shift($barMessages);
-            })
+            ->willReturnOnConsecutiveCalls($fourthMessage, null, null, $fifthMessage)
         ;
 
         $actualOrder = [];
diff --git a/Tests/Mocks/JsonSerializableObject.php b/Tests/Mocks/JsonSerializableObject.php
index 5b74106..84885c3 100644
--- a/Tests/Mocks/JsonSerializableObject.php
+++ b/Tests/Mocks/JsonSerializableObject.php
@@ -4,7 +4,8 @@
 
 class JsonSerializableObject implements \JsonSerializable
 {
-    public function jsonSerialize()
+    #[\ReturnTypeWillChange]
+    public function jsonSerialize(): array
     {
         return ['foo' => 'fooVal'];
     }
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
index 14a48be..e1ed297 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php
@@ -443,6 +443,7 @@ private function createCommandSubscriberProcessor($commandSubscriberReturns = ['
 
             public function process(InteropMessage $message, Context $context)
             {
+                return self::ACK;
             }
 
             public static function getSubscribedCommand()
diff --git a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
index b9d798c..a954d9a 100644
--- a/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
+++ b/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php
@@ -407,6 +407,7 @@ private function createTopicSubscriberProcessor($topicSubscriberReturns = ['aTop
 
             public function process(InteropMessage $message, Context $context)
             {
+                return self::ACK;
             }
 
             public static function getSubscribedTopics()
diff --git a/Tests/Symfony/Consumption/Mock/QueueSubscriberProcessor.php b/Tests/Symfony/Consumption/Mock/QueueSubscriberProcessor.php
index 56b3c93..a210b0e 100644
--- a/Tests/Symfony/Consumption/Mock/QueueSubscriberProcessor.php
+++ b/Tests/Symfony/Consumption/Mock/QueueSubscriberProcessor.php
@@ -11,6 +11,7 @@ class QueueSubscriberProcessor implements Processor, QueueSubscriberInterface
 {
     public function process(InteropMessage $message, Context $context)
     {
+        return self::ACK;
     }
 
     public static function getSubscribedQueues()
diff --git a/Tests/Util/Fixtures/JsonSerializableClass.php b/Tests/Util/Fixtures/JsonSerializableClass.php
index b612978..1a77ce0 100644
--- a/Tests/Util/Fixtures/JsonSerializableClass.php
+++ b/Tests/Util/Fixtures/JsonSerializableClass.php
@@ -6,7 +6,8 @@ class JsonSerializableClass implements \JsonSerializable
 {
     public $keyPublic = 'public';
 
-    public function jsonSerialize()
+    #[\ReturnTypeWillChange]
+    public function jsonSerialize(): array
     {
         return [
             'key' => 'value',
diff --git a/Util/Stringify.php b/Util/Stringify.php
index 12358d7..d8a48a8 100644
--- a/Util/Stringify.php
+++ b/Util/Stringify.php
@@ -25,6 +25,6 @@ public function __toString(): string
 
     public static function that($value): self
     {
-        return new static($value);
+        return new self($value);
     }
 }

From 2524b3f9bfa7c80a23a57749e504651b474ca37f Mon Sep 17 00:00:00 2001
From: James Read <james.read.18@gmail.com>
Date: Sun, 27 Apr 2025 14:30:31 +0100
Subject: [PATCH 336/337] Updating composer

Updating composer.json in pkg directory to correctly reflect the
supported versions of PHP
---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 0da00ee..c336c4b 100644
--- a/composer.json
+++ b/composer.json
@@ -6,7 +6,7 @@
     "homepage": "https://enqueue.forma-pro.com/",
     "license": "MIT",
     "require": {
-        "php": "^7.4|^8.0",
+        "php": "^8.1",
         "queue-interop/amqp-interop": "^0.8.2",
         "queue-interop/queue-interop": "^0.8",
         "enqueue/null": "^0.10",

From 01da495411d33e62867c1a4b3c67d88139785337 Mon Sep 17 00:00:00 2001
From: James Read <james.read.18@gmail.com>
Date: Sat, 19 Apr 2025 13:32:34 +0100
Subject: [PATCH 337/337] Updting lowest dep

Updating the lowest dependecy until deprication warnings are fix.
---
 Client/DriverFactory.php                           | 2 +-
 Symfony/Client/ConsumeCommand.php                  | 2 +-
 Symfony/Client/ProduceCommand.php                  | 2 +-
 Symfony/Client/RoutesCommand.php                   | 2 +-
 Symfony/Client/SetupBrokerCommand.php              | 2 +-
 Symfony/Consumption/ConfigurableConsumeCommand.php | 2 +-
 Symfony/Consumption/ConsumeCommand.php             | 2 +-
 7 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/Client/DriverFactory.php b/Client/DriverFactory.php
index 1d383ac..5c827e7 100644
--- a/Client/DriverFactory.php
+++ b/Client/DriverFactory.php
@@ -76,7 +76,7 @@ private function findDriverInfo(Dsn $dsn, array $factories): ?array
     private function createRabbitMqStompDriver(ConnectionFactory $factory, Dsn $dsn, Config $config, RouteCollection $collection): RabbitMqStompDriver
     {
         $defaultManagementHost = $dsn->getHost() ?: $config->getTransportOption('host', 'localhost');
-        $managementVast = ltrim($dsn->getPath(), '/') ?: $config->getTransportOption('vhost', '/');
+        $managementVast = ltrim($dsn->getPath() ?? '', '/') ?: $config->getTransportOption('vhost', '/');
 
         $managementClient = StompManagementClient::create(
             urldecode($managementVast),
diff --git a/Symfony/Client/ConsumeCommand.php b/Symfony/Client/ConsumeCommand.php
index 5a06767..94b56ad 100644
--- a/Symfony/Client/ConsumeCommand.php
+++ b/Symfony/Client/ConsumeCommand.php
@@ -94,7 +94,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
         try {
             $consumer = $this->getQueueConsumer($client);
         } catch (NotFoundExceptionInterface $e) {
-            throw new \LogicException(sprintf('Client "%s" is not supported.', $client), null, $e);
+            throw new \LogicException(sprintf('Client "%s" is not supported.', $client), previous: $e);
         }
 
         $driver = $this->getDriver($client);
diff --git a/Symfony/Client/ProduceCommand.php b/Symfony/Client/ProduceCommand.php
index bd23c16..953a766 100644
--- a/Symfony/Client/ProduceCommand.php
+++ b/Symfony/Client/ProduceCommand.php
@@ -67,7 +67,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
         try {
             $producer = $this->getProducer($client);
         } catch (NotFoundExceptionInterface $e) {
-            throw new \LogicException(sprintf('Client "%s" is not supported.', $client), null, $e);
+            throw new \LogicException(sprintf('Client "%s" is not supported.', $client), previous: $e);
         }
 
         if ($topic) {
diff --git a/Symfony/Client/RoutesCommand.php b/Symfony/Client/RoutesCommand.php
index 0646e37..04b657e 100644
--- a/Symfony/Client/RoutesCommand.php
+++ b/Symfony/Client/RoutesCommand.php
@@ -63,7 +63,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
         try {
             $this->driver = $this->getDriver($input->getOption('client'));
         } catch (NotFoundExceptionInterface $e) {
-            throw new \LogicException(sprintf('Client "%s" is not supported.', $input->getOption('client')), null, $e);
+            throw new \LogicException(sprintf('Client "%s" is not supported.', $input->getOption('client')), previous: $e);
         }
 
         $routes = $this->driver->getRouteCollection()->all();
diff --git a/Symfony/Client/SetupBrokerCommand.php b/Symfony/Client/SetupBrokerCommand.php
index b74902a..92d5ad0 100644
--- a/Symfony/Client/SetupBrokerCommand.php
+++ b/Symfony/Client/SetupBrokerCommand.php
@@ -55,7 +55,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
         try {
             $this->getDriver($client)->setupBroker(new ConsoleLogger($output));
         } catch (NotFoundExceptionInterface $e) {
-            throw new \LogicException(sprintf('Client "%s" is not supported.', $client), null, $e);
+            throw new \LogicException(sprintf('Client "%s" is not supported.', $client), previous: $e);
         }
 
         $output->writeln('Broker set up');
diff --git a/Symfony/Consumption/ConfigurableConsumeCommand.php b/Symfony/Consumption/ConfigurableConsumeCommand.php
index 230a0dc..34cb66d 100644
--- a/Symfony/Consumption/ConfigurableConsumeCommand.php
+++ b/Symfony/Consumption/ConfigurableConsumeCommand.php
@@ -79,7 +79,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
         try {
             $consumer = $this->getQueueConsumer($transport);
         } catch (NotFoundExceptionInterface $e) {
-            throw new \LogicException(sprintf('Transport "%s" is not supported.', $transport), null, $e);
+            throw new \LogicException(sprintf('Transport "%s" is not supported.', $transport), previous: $e);
         }
 
         $this->setQueueConsumerOptions($consumer, $input);
diff --git a/Symfony/Consumption/ConsumeCommand.php b/Symfony/Consumption/ConsumeCommand.php
index 1c82f3c..b69ae72 100644
--- a/Symfony/Consumption/ConsumeCommand.php
+++ b/Symfony/Consumption/ConsumeCommand.php
@@ -65,7 +65,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
             // QueueConsumer must be pre configured outside of the command!
             $consumer = $this->getQueueConsumer($transport);
         } catch (NotFoundExceptionInterface $e) {
-            throw new \LogicException(sprintf('Transport "%s" is not supported.', $transport), null, $e);
+            throw new \LogicException(sprintf('Transport "%s" is not supported.', $transport), previous: $e);
         }
 
         $this->setQueueConsumerOptions($consumer, $input);
