From ed942f9801818694b88ddc211af36260883c39ae Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Thu, 13 Sep 2018 16:41:57 +0300 Subject: [PATCH 01/31] [client] Introduce route and route collection. Rework tags --- pkg/enqueue/Client/Route.php | 114 ++++++ pkg/enqueue/Client/RouteCollection.php | 102 +++++ pkg/enqueue/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 pkg/enqueue/Client/Route.php create mode 100644 pkg/enqueue/Client/RouteCollection.php create mode 100644 pkg/enqueue/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPass.php create mode 100644 pkg/enqueue/Symfony/DependencyInjection/BuildProcessorRegistryPass.php create mode 100644 pkg/enqueue/Symfony/DependencyInjection/BuildProcessorRoutesPass.php create mode 100644 pkg/enqueue/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPass.php create mode 100644 pkg/enqueue/Tests/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php create mode 100644 pkg/enqueue/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php create mode 100644 pkg/enqueue/Tests/Symfony/DependencyInjection/BuildProcessorRoutesPassTest.php create mode 100644 pkg/enqueue/Tests/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php diff --git a/pkg/enqueue/Client/Route.php b/pkg/enqueue/Client/Route.php new file mode 100644 index 000000000..e11fa19a9 --- /dev/null +++ b/pkg/enqueue/Client/Route.php @@ -0,0 +1,114 @@ +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/pkg/enqueue/Client/RouteCollection.php b/pkg/enqueue/Client/RouteCollection.php new file mode 100644 index 000000000..d2a0307f7 --- /dev/null +++ b/pkg/enqueue/Client/RouteCollection.php @@ -0,0 +1,102 @@ +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/pkg/enqueue/Client/RouterProcessor.php b/pkg/enqueue/Client/RouterProcessor.php index 35efe1b02..2bf977976 100644 --- a/pkg/enqueue/Client/RouterProcessor.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/TopicSubscriberInterface.php b/pkg/enqueue/Client/TopicSubscriberInterface.php index bdaa43f61..4723c670f 100644 --- a/pkg/enqueue/Client/TopicSubscriberInterface.php +++ b/pkg/enqueue/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/pkg/enqueue/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPass.php b/pkg/enqueue/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPass.php new file mode 100644 index 000000000..e9e16a3a8 --- /dev/null +++ b/pkg/enqueue/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPass.php @@ -0,0 +1,103 @@ +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/pkg/enqueue/Symfony/DependencyInjection/BuildProcessorRegistryPass.php b/pkg/enqueue/Symfony/DependencyInjection/BuildProcessorRegistryPass.php new file mode 100644 index 000000000..7cdc2091e --- /dev/null +++ b/pkg/enqueue/Symfony/DependencyInjection/BuildProcessorRegistryPass.php @@ -0,0 +1,54 @@ +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/pkg/enqueue/Symfony/DependencyInjection/BuildProcessorRoutesPass.php b/pkg/enqueue/Symfony/DependencyInjection/BuildProcessorRoutesPass.php new file mode 100644 index 000000000..7a6853b52 --- /dev/null +++ b/pkg/enqueue/Symfony/DependencyInjection/BuildProcessorRoutesPass.php @@ -0,0 +1,80 @@ +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/pkg/enqueue/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPass.php b/pkg/enqueue/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPass.php new file mode 100644 index 000000000..869a9f0d3 --- /dev/null +++ b/pkg/enqueue/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPass.php @@ -0,0 +1,103 @@ +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/pkg/enqueue/Tests/Client/RouterProcessorTest.php b/pkg/enqueue/Tests/Client/RouterProcessorTest.php index e362c1e23..da803b143 100644 --- a/pkg/enqueue/Tests/Client/RouterProcessorTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php b/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php new file mode 100644 index 000000000..3e4d69a71 --- /dev/null +++ b/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php @@ -0,0 +1,353 @@ +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/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php b/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php new file mode 100644 index 000000000..70ad5dfeb --- /dev/null +++ b/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php @@ -0,0 +1,146 @@ +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/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildProcessorRoutesPassTest.php b/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildProcessorRoutesPassTest.php new file mode 100644 index 000000000..517f98bbc --- /dev/null +++ b/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildProcessorRoutesPassTest.php @@ -0,0 +1,282 @@ +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/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php b/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php new file mode 100644 index 000000000..af3b0b178 --- /dev/null +++ b/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php @@ -0,0 +1,353 @@ +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 c710e9f6baebc97956bd14433e88d25ea47aeaa8 Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Fri, 14 Sep 2018 16:18:32 +0300 Subject: [PATCH 02/31] Reowrk router processor, remove client routing compiler pass. --- .../Compiler/BuildClientRoutingPass.php | 47 --- .../ExtractProcessorTagSubscriptionsTrait.php | 88 ++--- .../Compiler/BuildClientRoutingPassTest.php | 318 ------------------ pkg/enqueue/Client/Config.php | 88 ++--- pkg/enqueue/Client/RouteCollection.php | 18 +- pkg/enqueue/Client/RouterProcessor.php | 56 +-- .../BuildProcessorRegistryPass.php | 4 +- .../Tests/Client/RouterProcessorTest.php | 179 ++++++---- 8 files changed, 210 insertions(+), 588 deletions(-) delete mode 100644 pkg/enqueue-bundle/DependencyInjection/Compiler/BuildClientRoutingPass.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildClientRoutingPassTest.php diff --git a/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildClientRoutingPass.php b/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildClientRoutingPass.php deleted file mode 100644 index 3511a3a96..000000000 --- a/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildClientRoutingPass.php +++ /dev/null @@ -1,47 +0,0 @@ -hasDefinition($routerId)) { - return; - } - - $events = []; - $commands = []; - foreach ($container->findTaggedServiceIds($processorTagName) as $serviceId => $tagAttributes) { - $subscriptions = $this->extractSubscriptions($container, $serviceId, $tagAttributes); - - foreach ($subscriptions as $subscription) { - if (Config::COMMAND_TOPIC === $subscription['topicName']) { - $commands[$subscription['processorName']] = $subscription['queueName']; - } else { - $events[$subscription['topicName']][] = [ - $subscription['processorName'], - $subscription['queueName'], - ]; - } - } - } - - $router = $container->getDefinition($routerId); - $router->replaceArgument(1, $events); - $router->replaceArgument(2, $commands); - } -} diff --git a/pkg/enqueue-bundle/DependencyInjection/Compiler/ExtractProcessorTagSubscriptionsTrait.php b/pkg/enqueue-bundle/DependencyInjection/Compiler/ExtractProcessorTagSubscriptionsTrait.php index 58fa57622..dbb737044 100644 --- a/pkg/enqueue-bundle/DependencyInjection/Compiler/ExtractProcessorTagSubscriptionsTrait.php +++ b/pkg/enqueue-bundle/DependencyInjection/Compiler/ExtractProcessorTagSubscriptionsTrait.php @@ -3,7 +3,6 @@ namespace Enqueue\Bundle\DependencyInjection\Compiler; use Enqueue\Client\CommandSubscriberInterface; -use Enqueue\Client\Config; use Enqueue\Client\TopicSubscriberInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; @@ -31,47 +30,45 @@ protected function extractSubscriptions(ContainerBuilder $container, $processorS } }; - $processorClass = $container->getDefinition($processorServiceId)->getClass(); - if (false == class_exists($processorClass)) { - throw new \LogicException(sprintf('The class "%s" could not be found.', $processorClass)); + $processorDefinition = $container->getDefinition($processorServiceId); + $processorClass = $processorDefinition->getClass(); + if (false == $processorDefinition->getFactory() && false == class_exists($processorClass)) { + throw new \LogicException(sprintf('The processor class "%s" could not be found.', $processorClass)); } $defaultQueueName = $resolve($container->getParameter('enqueue.client.default_queue_name')); - $subscriptionPrototype = [ - 'topicName' => null, - 'queueName' => null, - 'queueNameHardcoded' => false, - 'processorName' => null, - 'exclusive' => false, - ]; $data = []; - if (is_subclass_of($processorClass, CommandSubscriberInterface::class)) { + if ($processorClass && is_subclass_of($processorClass, CommandSubscriberInterface::class)) { /** @var CommandSubscriberInterface $processorClass */ $params = $processorClass::getSubscribedCommand(); if (is_string($params)) { if (empty($params)) { - throw new \LogicException('The processor name (it is also the command name) must not be empty.'); + throw new \LogicException('The command name must not be empty.'); } $data[] = [ - 'topicName' => Config::COMMAND_TOPIC, + 'commandName' => $params, 'queueName' => $defaultQueueName, 'queueNameHardcoded' => false, - 'processorName' => $params, + 'processorName' => $processorServiceId, + 'exclusive' => false, ]; } elseif (is_array($params)) { - $params = array_replace($subscriptionPrototype, $params); - if (false == $processorName = $resolve($params['processorName'])) { - throw new \LogicException('The processor name (it is also the command name) must not be empty.'); + if (empty($params['commandName'])) { + throw new \LogicException('The commandName must be set.'); + } + + if (false == $commandName = $resolve($params['commandName'])) { + throw new \LogicException('The commandName must not be empty.'); } $data[] = [ - 'topicName' => Config::COMMAND_TOPIC, - 'queueName' => $resolve($params['queueName']) ?: $defaultQueueName, - 'queueNameHardcoded' => $resolve($params['queueNameHardcoded']), - 'processorName' => $processorName, - 'exclusive' => array_key_exists('exclusive', $params) ? $params['exclusive'] : false, + 'commandName' => $commandName, + 'queueName' => $resolve($params['queueName'] ?? $defaultQueueName), + 'queueNameHardcoded' => $params['queueNameHardcoded'] ?? false, + 'processorName' => $params['processorName'] ?? $commandName, + 'exclusive' => $params['exclusive'] ?? false, ]; } else { throw new \LogicException(sprintf( @@ -81,7 +78,7 @@ protected function extractSubscriptions(ContainerBuilder $container, $processorS } } - if (is_subclass_of($processorClass, TopicSubscriberInterface::class)) { + if ($processorClass && is_subclass_of($processorClass, TopicSubscriberInterface::class)) { /** @var TopicSubscriberInterface $processorClass */ $topics = $processorClass::getSubscribedTopics(); if (!is_array($topics)) { @@ -99,15 +96,15 @@ protected function extractSubscriptions(ContainerBuilder $container, $processorS 'queueName' => $defaultQueueName, 'queueNameHardcoded' => false, 'processorName' => $processorServiceId, + 'exclusive' => false, ]; } elseif (is_array($params)) { - $params = array_replace($subscriptionPrototype, $params); - $data[] = [ 'topicName' => $topicName, - 'queueName' => $resolve($params['queueName']) ?: $defaultQueueName, - 'queueNameHardcoded' => $resolve($params['queueNameHardcoded']), + 'queueName' => $resolve($params['queueName'] ?? $defaultQueueName), + 'queueNameHardcoded' => $params['queueNameHardcoded'] ?? false, 'processorName' => $resolve($params['processorName']) ?: $processorServiceId, + 'exclusive' => false, ]; } else { throw new \LogicException(sprintf( @@ -120,24 +117,37 @@ protected function extractSubscriptions(ContainerBuilder $container, $processorS } if (false == ( + $processorClass || is_subclass_of($processorClass, CommandSubscriberInterface::class) || is_subclass_of($processorClass, TopicSubscriberInterface::class) )) { foreach ($tagAttributes as $tagAttribute) { - $tagAttribute = array_replace($subscriptionPrototype, $tagAttribute); + if (empty($tagAttribute['commandName']) && empty($tagAttribute['topicName'])) { + throw new \LogicException('Either commandName or topicName attribute must be set'); + } + + $topicName = $tagAttribute['topicName'] ?? null; + $commandName = $tagAttribute['commandName'] ?? null; - if (false == $tagAttribute['topicName']) { - throw new \LogicException(sprintf('Topic name is not set on message processor tag but it is required. Service %s', $processorServiceId)); + if ($topicName) { + $data[] = [ + 'topicName' => $resolve($tagAttribute['topicName']), + 'queueName' => $resolve($tagAttribute['queueName'] ?? $defaultQueueName), + 'queueNameHardcoded' => $tagAttribute['queueNameHardcoded'], + 'processorName' => $resolve($tagAttribute['processorName'] ?? $processorServiceId), + 'exclusive' => false, + ]; } - $data[] = [ - 'topicName' => $resolve($tagAttribute['topicName']), - 'queueName' => $resolve($tagAttribute['queueName']) ?: $defaultQueueName, - 'queueNameHardcoded' => $resolve($tagAttribute['queueNameHardcoded']), - 'processorName' => $resolve($tagAttribute['processorName']) ?: $processorServiceId, - 'exclusive' => Config::COMMAND_TOPIC == $resolve($tagAttribute['topicName']) && - array_key_exists('exclusive', $tagAttribute) ? $tagAttribute['exclusive'] : false, - ]; + if ($commandName) { + $data[] = [ + 'commandName' => $resolve($tagAttribute['commandName']), + 'queueName' => $resolve($tagAttribute['queueName'] ?? $defaultQueueName), + 'queueNameHardcoded' => $tagAttribute['queueNameHardcoded'], + 'processorName' => $resolve($tagAttribute['processorName'] ?? $processorServiceId), + 'exclusive' => $tagAttribute['exclusive'] ?? false, + ]; + } } } diff --git a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildClientRoutingPassTest.php b/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildClientRoutingPassTest.php deleted file mode 100644 index f1b678570..000000000 --- a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildClientRoutingPassTest.php +++ /dev/null @@ -1,318 +0,0 @@ -createContainerBuilder(); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor', [ - 'topicName' => 'topic', - 'processorName' => 'processor', - 'queueName' => 'queue', - ]); - $container->setDefinition('processor', $processor); - - $router = new Definition(); - $router->setArguments([null, null, null]); - $container->setDefinition(RouterProcessor::class, $router); - - $pass = new BuildClientRoutingPass(); - $pass->process($container); - - $expectedRoutes = [ - 'topic' => [ - ['processor', 'queue'], - ], - ]; - - $this->assertEquals($expectedRoutes, $router->getArgument(1)); - } - - public function testThrowIfProcessorClassNameCouldNotBeFound() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition('notExistingClass'); - $processor->addTag('enqueue.client.processor', [ - 'processorName' => 'processor', - ]); - $container->setDefinition('processor', $processor); - - $router = new Definition(); - $router->setArguments([null, []]); - $container->setDefinition(RouterProcessor::class, $router); - - $pass = new BuildClientRoutingPass(); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('The class "notExistingClass" could not be found.'); - $pass->process($container); - } - - public function testShouldThrowExceptionIfTopicNameIsNotSet() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor', $processor); - - $router = new Definition(); - $router->setArguments([null, null, null]); - $container->setDefinition(RouterProcessor::class, $router); - - $pass = new BuildClientRoutingPass(); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('Topic name is not set on message processor tag but it is required.'); - $pass->process($container); - } - - public function testShouldSetServiceIdAdProcessorIdIfIsNotSetInTag() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor', [ - 'topicName' => 'topic', - 'queueName' => 'queue', - ]); - $container->setDefinition('processor-service-id', $processor); - - $router = new Definition(); - $router->setArguments([null, null, null]); - $container->setDefinition(RouterProcessor::class, $router); - - $pass = new BuildClientRoutingPass(); - $pass->process($container); - - $expectedRoutes = [ - 'topic' => [ - ['processor-service-id', 'queue'], - ], - ]; - - $this->assertEquals($expectedRoutes, $router->getArgument(1)); - } - - public function testShouldSetDefaultQueueIfNotSetInTag() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor', [ - 'topicName' => 'topic', - ]); - $container->setDefinition('processor-service-id', $processor); - - $router = new Definition(); - $router->setArguments([null, null, null]); - $container->setDefinition(RouterProcessor::class, $router); - - $pass = new BuildClientRoutingPass(); - $pass->process($container); - - $expectedRoutes = [ - 'topic' => [ - ['processor-service-id', 'aDefaultQueueName'], - ], - ]; - - $this->assertEquals($expectedRoutes, $router->getArgument(1)); - } - - public function testShouldBuildRouteFromSubscriberIfOnlyTopicNameSpecified() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(OnlyTopicNameTopicSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-service-id', $processor); - - $router = new Definition(); - $router->setArguments([null, null, null]); - $container->setDefinition(RouterProcessor::class, $router); - - $pass = new BuildClientRoutingPass(); - $pass->process($container); - - $expectedRoutes = [ - 'topic-subscriber-name' => [ - ['processor-service-id', 'aDefaultQueueName'], - ], - ]; - - $this->assertEquals($expectedRoutes, $router->getArgument(1)); - } - - public function testShouldBuildRouteFromSubscriberIfProcessorNameSpecified() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(ProcessorNameTopicSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-service-id', $processor); - - $router = new Definition(); - $router->setArguments([null, null, null]); - $container->setDefinition(RouterProcessor::class, $router); - - $pass = new BuildClientRoutingPass(); - $pass->process($container); - - $expectedRoutes = [ - 'topic-subscriber-name' => [ - ['subscriber-processor-name', 'aDefaultQueueName'], - ], - ]; - - $this->assertEquals($expectedRoutes, $router->getArgument(1)); - } - - public function testShouldBuildRouteFromSubscriberIfQueueNameSpecified() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(QueueNameTopicSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-service-id', $processor); - - $router = new Definition(); - $router->setArguments([null, null, null]); - $container->setDefinition(RouterProcessor::class, $router); - - $pass = new BuildClientRoutingPass(); - $pass->process($container); - - $expectedRoutes = [ - 'topic-subscriber-name' => [ - ['processor-service-id', 'subscriber-queue-name'], - ], - ]; - - $this->assertEquals($expectedRoutes, $router->getArgument(1)); - } - - public function testShouldBuildRouteFromWithoutProcessorNameTopicSubscriber() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(WithoutProcessorNameTopicSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-service-id', $processor); - - $router = new Definition(); - $router->setArguments([null, null, null]); - $container->setDefinition(RouterProcessor::class, $router); - - $pass = new BuildClientRoutingPass(); - $pass->process($container); - - $expectedRoutes = [ - 'without-processor-name' => [ - ['processor-service-id', 'a_queue_name'], - ], - ]; - - $this->assertEquals($expectedRoutes, $router->getArgument(1)); - } - - public function testShouldThrowExceptionWhenTopicSubscriberConfigurationIsInvalid() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(InvalidTopicSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-service-id', $processor); - - $router = new Definition(); - $router->setArguments(['', '']); - $container->setDefinition(RouterProcessor::class, $router); - - $pass = new BuildClientRoutingPass(); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('Topic subscriber configuration is invalid'); - - $pass->process($container); - } - - public function testShouldBuildRouteFromCommandSubscriberIfOnlyCommandNameSpecified() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(OnlyCommandNameSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-service-id', $processor); - - $router = new Definition(); - $router->setArguments([null, null, null]); - $container->setDefinition(RouterProcessor::class, $router); - - $pass = new BuildClientRoutingPass(); - $pass->process($container); - - $expectedRoutes = [ - 'the-command-name' => 'aDefaultQueueName', - ]; - - $this->assertEquals([], $router->getArgument(1)); - $this->assertEquals($expectedRoutes, $router->getArgument(2)); - } - - public function testShouldBuildRouteFromCommandSubscriberIfProcessorNameSpecified() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(ProcessorNameCommandSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-service-id', $processor); - - $router = new Definition(); - $router->setArguments([null, null, null]); - $container->setDefinition(RouterProcessor::class, $router); - - $pass = new BuildClientRoutingPass(); - $pass->process($container); - - $expectedRoutes = [ - 'the-command-name' => 'the-command-queue-name', - ]; - - $this->assertEquals([], $router->getArgument(1)); - $this->assertEquals($expectedRoutes, $router->getArgument(2)); - } - - /** - * @return ContainerBuilder - */ - private function createContainerBuilder() - { - $container = new ContainerBuilder(); - $container->setParameter('enqueue.client.default_queue_name', 'aDefaultQueueName'); - - return $container; - } -} diff --git a/pkg/enqueue/Client/Config.php b/pkg/enqueue/Client/Config.php index d5d8b072a..46c023010 100644 --- a/pkg/enqueue/Client/Config.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/RouteCollection.php b/pkg/enqueue/Client/RouteCollection.php index d2a0307f7..76bcbe451 100644 --- a/pkg/enqueue/Client/RouteCollection.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/RouterProcessor.php b/pkg/enqueue/Client/RouterProcessor.php index 2bf977976..791831685 100644 --- a/pkg/enqueue/Client/RouterProcessor.php +++ b/pkg/enqueue/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/pkg/enqueue/Symfony/DependencyInjection/BuildProcessorRegistryPass.php b/pkg/enqueue/Symfony/DependencyInjection/BuildProcessorRegistryPass.php index 7cdc2091e..4620cef7c 100644 --- a/pkg/enqueue/Symfony/DependencyInjection/BuildProcessorRegistryPass.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/RouterProcessorTest.php b/pkg/enqueue/Tests/Client/RouterProcessorTest.php index da803b143..f69bb07d7 100644 --- a/pkg/enqueue/Tests/Client/RouterProcessorTest.php +++ b/pkg/enqueue/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 e039e88238a72b4a0d4b34b7bfce241706393f3b Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Fri, 14 Sep 2018 16:19:10 +0300 Subject: [PATCH 03/31] add analyze route collection pass --- .../AnalyzeRouteCollectionPass.php | 90 +++++++++++ .../AnalyzeRouteCollectionPassTest.php | 140 ++++++++++++++++++ 2 files changed, 230 insertions(+) create mode 100644 pkg/enqueue/Symfony/DependencyInjection/AnalyzeRouteCollectionPass.php create mode 100644 pkg/enqueue/Tests/Symfony/DependencyInjection/AnalyzeRouteCollectionPassTest.php diff --git a/pkg/enqueue/Symfony/DependencyInjection/AnalyzeRouteCollectionPass.php b/pkg/enqueue/Symfony/DependencyInjection/AnalyzeRouteCollectionPass.php new file mode 100644 index 000000000..b5bd11f48 --- /dev/null +++ b/pkg/enqueue/Symfony/DependencyInjection/AnalyzeRouteCollectionPass.php @@ -0,0 +1,90 @@ +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/pkg/enqueue/Tests/Symfony/DependencyInjection/AnalyzeRouteCollectionPassTest.php b/pkg/enqueue/Tests/Symfony/DependencyInjection/AnalyzeRouteCollectionPassTest.php new file mode 100644 index 000000000..be844d84f --- /dev/null +++ b/pkg/enqueue/Tests/Symfony/DependencyInjection/AnalyzeRouteCollectionPassTest.php @@ -0,0 +1,140 @@ +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 e397abc9ae32695a38ad44e273cb2c9436439eea Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Fri, 14 Sep 2018 16:34:29 +0300 Subject: [PATCH 04/31] Rework exclusive command extension. --- .../BuildExclusiveCommandsExtensionPass.php | 49 ----- pkg/enqueue-bundle/EnqueueBundle.php | 17 +- .../Resources/config/client.yml | 6 + ...uildExclusiveCommandsExtensionPassTest.php | 105 ---------- .../Tests/Unit/EnqueueBundleTest.php | 12 -- .../ExclusiveCommandExtension.php | 65 ++++--- .../ExclusiveCommandExtensionTest.php | 180 ++++++++++-------- 7 files changed, 149 insertions(+), 285 deletions(-) delete mode 100644 pkg/enqueue-bundle/DependencyInjection/Compiler/BuildExclusiveCommandsExtensionPass.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildExclusiveCommandsExtensionPassTest.php diff --git a/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildExclusiveCommandsExtensionPass.php b/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildExclusiveCommandsExtensionPass.php deleted file mode 100644 index 3b0d906bd..000000000 --- a/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildExclusiveCommandsExtensionPass.php +++ /dev/null @@ -1,49 +0,0 @@ -hasDefinition($extensionId)) { - return; - } - - $queueMetaRegistry = $container->getDefinition($extensionId); - - $queueNameToProcessorNameMap = []; - foreach ($container->findTaggedServiceIds($processorTagName) as $serviceId => $tagAttributes) { - $subscriptions = $this->extractSubscriptions($container, $serviceId, $tagAttributes); - - foreach ($subscriptions as $subscription) { - if (Config::COMMAND_TOPIC != $subscription['topicName']) { - continue; - } - - if (false == isset($subscription['exclusive']) || false === $subscription['exclusive']) { - continue; - } - - if (false == $subscription['queueNameHardcoded']) { - throw new \LogicException('The exclusive command could be used only with queueNameHardcoded attribute set to true.'); - } - - $queueNameToProcessorNameMap[$subscription['queueName']] = $subscription['processorName']; - } - } - - $queueMetaRegistry->replaceArgument(0, $queueNameToProcessorNameMap); - } -} diff --git a/pkg/enqueue-bundle/EnqueueBundle.php b/pkg/enqueue-bundle/EnqueueBundle.php index a8a9311b4..947574869 100644 --- a/pkg/enqueue-bundle/EnqueueBundle.php +++ b/pkg/enqueue-bundle/EnqueueBundle.php @@ -6,12 +6,14 @@ use Enqueue\AsyncEventDispatcher\DependencyInjection\AsyncEventsPass; use Enqueue\AsyncEventDispatcher\DependencyInjection\AsyncTransformersPass; use Enqueue\Bundle\DependencyInjection\Compiler\BuildClientExtensionsPass; -use Enqueue\Bundle\DependencyInjection\Compiler\BuildClientRoutingPass; use Enqueue\Bundle\DependencyInjection\Compiler\BuildConsumptionExtensionsPass; -use Enqueue\Bundle\DependencyInjection\Compiler\BuildExclusiveCommandsExtensionPass; -use Enqueue\Bundle\DependencyInjection\Compiler\BuildProcessorRegistryPass; use Enqueue\Bundle\DependencyInjection\Compiler\BuildQueueMetaRegistryPass; use Enqueue\Bundle\DependencyInjection\Compiler\BuildTopicMetaSubscribersPass; +use Enqueue\Symfony\DependencyInjection\AnalyzeRouteCollectionPass; +use Enqueue\Symfony\DependencyInjection\BuildCommandSubscriberRoutesPass; +use Enqueue\Symfony\DependencyInjection\BuildProcessorRegistryPass; +use Enqueue\Symfony\DependencyInjection\BuildProcessorRoutesPass; +use Enqueue\Symfony\DependencyInjection\BuildTopicSubscriberRoutesPass; use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; @@ -21,12 +23,15 @@ class EnqueueBundle extends Bundle public function build(ContainerBuilder $container): void { $container->addCompilerPass(new BuildConsumptionExtensionsPass()); - $container->addCompilerPass(new BuildClientRoutingPass()); - $container->addCompilerPass(new BuildProcessorRegistryPass()); $container->addCompilerPass(new BuildTopicMetaSubscribersPass()); $container->addCompilerPass(new BuildQueueMetaRegistryPass()); $container->addCompilerPass(new BuildClientExtensionsPass()); - $container->addCompilerPass(new BuildExclusiveCommandsExtensionPass()); + + $container->addCompilerPass(new BuildTopicSubscriberRoutesPass('default'), 100); + $container->addCompilerPass(new BuildCommandSubscriberRoutesPass('default'), 100); + $container->addCompilerPass(new BuildProcessorRoutesPass('default'), 100); + $container->addCompilerPass(new AnalyzeRouteCollectionPass('default'), 30); + $container->addCompilerPass(new BuildProcessorRegistryPass('default')); if (class_exists(AsyncEventDispatcherExtension::class)) { $container->addCompilerPass(new AsyncEventsPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 100); diff --git a/pkg/enqueue-bundle/Resources/config/client.yml b/pkg/enqueue-bundle/Resources/config/client.yml index 6066a7ab3..26d81edc2 100644 --- a/pkg/enqueue-bundle/Resources/config/client.yml +++ b/pkg/enqueue-bundle/Resources/config/client.yml @@ -219,3 +219,9 @@ services: enqueue.flush_spool_producer_listener: public: true alias: 'Enqueue\Symfony\Client\FlushSpoolProducerListener' + + enqueue.client.default.exclusive_command_extension: + class: 'Enqueue\Client\ConsumptionExtension\ExclusiveCommandExtension' + arguments: '@enqueue.client.default.driver' + tags: + - { name: 'enqueue.client.extension' } diff --git a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildExclusiveCommandsExtensionPassTest.php b/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildExclusiveCommandsExtensionPassTest.php deleted file mode 100644 index 395078ac2..000000000 --- a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildExclusiveCommandsExtensionPassTest.php +++ /dev/null @@ -1,105 +0,0 @@ -assertClassImplements(CompilerPassInterface::class, BuildExclusiveCommandsExtensionPass::class); - } - - public function testCouldBeConstructedWithoutAnyArguments() - { - new BuildExclusiveCommandsExtensionPass(); - } - - public function testShouldDoNothingIfExclusiveCommandExtensionServiceNotRegistered() - { - $container = new ContainerBuilder(); - - $pass = new BuildExclusiveCommandsExtensionPass(); - $pass->process($container); - } - - public function testShouldReplaceFirstArgumentOfExclusiveCommandExtensionServiceConstructorWithExpectedMap() - { - $container = new ContainerBuilder(); - $container->setParameter('enqueue.client.default_queue_name', 'default'); - $container->register('enqueue.client.exclusive_command_extension', ExclusiveCommandExtension::class) - ->addArgument([]) - ; - - $processor = new Definition(ExclusiveCommandSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-id', $processor); - - $pass = new BuildExclusiveCommandsExtensionPass(); - - $pass->process($container); - - $this->assertEquals([ - 'the-queue-name' => 'the-exclusive-command-name', - ], $container->getDefinition('enqueue.client.exclusive_command_extension')->getArgument(0)); - } - - public function testShouldReplaceFirstArgumentOfExclusiveCommandConfiguredAsTagAttribute() - { - $container = new ContainerBuilder(); - $container->setParameter('enqueue.client.default_queue_name', 'default'); - $container->register('enqueue.client.exclusive_command_extension', ExclusiveCommandExtension::class) - ->addArgument([]) - ; - - $processor = new Definition($this->getMockClass(PsrProcessor::class)); - $processor->addTag('enqueue.client.processor', [ - 'topicName' => Config::COMMAND_TOPIC, - 'processorName' => 'the-exclusive-command-name', - 'queueName' => 'the-queue-name', - 'queueNameHardcoded' => true, - 'exclusive' => true, - ]); - $container->setDefinition('processor-id', $processor); - - $pass = new BuildExclusiveCommandsExtensionPass(); - - $pass->process($container); - - $this->assertEquals([ - 'the-queue-name' => 'the-exclusive-command-name', - ], $container->getDefinition('enqueue.client.exclusive_command_extension')->getArgument(0)); - } - - public function testShouldThrowIfExclusiveSetTrueButQueueNameIsNotHardcoded() - { - $container = new ContainerBuilder(); - $container->setParameter('enqueue.client.default_queue_name', 'default'); - $container->register('enqueue.client.exclusive_command_extension', ExclusiveCommandExtension::class) - ->addArgument([]) - ; - - $processor = new Definition(ExclusiveButQueueNameHardCodedCommandSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-id', $processor); - - $pass = new BuildExclusiveCommandsExtensionPass(); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('The exclusive command could be used only with queueNameHardcoded attribute set to true.'); - $pass->process($container); - } -} diff --git a/pkg/enqueue-bundle/Tests/Unit/EnqueueBundleTest.php b/pkg/enqueue-bundle/Tests/Unit/EnqueueBundleTest.php index 7df0921af..46a647738 100644 --- a/pkg/enqueue-bundle/Tests/Unit/EnqueueBundleTest.php +++ b/pkg/enqueue-bundle/Tests/Unit/EnqueueBundleTest.php @@ -3,9 +3,7 @@ namespace Enqueue\Bundle\Tests\Unit; use Enqueue\Bundle\DependencyInjection\Compiler\BuildClientExtensionsPass; -use Enqueue\Bundle\DependencyInjection\Compiler\BuildClientRoutingPass; use Enqueue\Bundle\DependencyInjection\Compiler\BuildConsumptionExtensionsPass; -use Enqueue\Bundle\DependencyInjection\Compiler\BuildExclusiveCommandsExtensionPass; use Enqueue\Bundle\DependencyInjection\Compiler\BuildProcessorRegistryPass; use Enqueue\Bundle\DependencyInjection\Compiler\BuildQueueMetaRegistryPass; use Enqueue\Bundle\DependencyInjection\Compiler\BuildTopicMetaSubscribersPass; @@ -37,11 +35,6 @@ public function testShouldRegisterExpectedCompilerPasses() ->method('addCompilerPass') ->with($this->isInstanceOf(BuildConsumptionExtensionsPass::class)) ; - $container - ->expects($this->at(1)) - ->method('addCompilerPass') - ->with($this->isInstanceOf(BuildClientRoutingPass::class)) - ; $container ->expects($this->at(2)) ->method('addCompilerPass') @@ -62,11 +55,6 @@ public function testShouldRegisterExpectedCompilerPasses() ->method('addCompilerPass') ->with($this->isInstanceOf(BuildClientExtensionsPass::class)) ; - $container - ->expects($this->at(6)) - ->method('addCompilerPass') - ->with($this->isInstanceOf(BuildExclusiveCommandsExtensionPass::class)) - ; $bundle = new EnqueueBundle(); $bundle->build($container); diff --git a/pkg/enqueue/Client/ConsumptionExtension/ExclusiveCommandExtension.php b/pkg/enqueue/Client/ConsumptionExtension/ExclusiveCommandExtension.php index 824a24448..261ac0231 100644 --- a/pkg/enqueue/Client/ConsumptionExtension/ExclusiveCommandExtension.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php b/pkg/enqueue/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php index c5eb0bba2..d858c9197 100644 --- a/pkg/enqueue/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php +++ b/pkg/enqueue/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 cf03113f381a00e70e1687c4a97889475813721d Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Fri, 14 Sep 2018 16:35:57 +0300 Subject: [PATCH 05/31] remove tests. --- .../Tests/Unit/EnqueueBundleTest.php | 39 ------------------- 1 file changed, 39 deletions(-) diff --git a/pkg/enqueue-bundle/Tests/Unit/EnqueueBundleTest.php b/pkg/enqueue-bundle/Tests/Unit/EnqueueBundleTest.php index 46a647738..d93a1f1f9 100644 --- a/pkg/enqueue-bundle/Tests/Unit/EnqueueBundleTest.php +++ b/pkg/enqueue-bundle/Tests/Unit/EnqueueBundleTest.php @@ -2,15 +2,9 @@ namespace Enqueue\Bundle\Tests\Unit; -use Enqueue\Bundle\DependencyInjection\Compiler\BuildClientExtensionsPass; -use Enqueue\Bundle\DependencyInjection\Compiler\BuildConsumptionExtensionsPass; -use Enqueue\Bundle\DependencyInjection\Compiler\BuildProcessorRegistryPass; -use Enqueue\Bundle\DependencyInjection\Compiler\BuildQueueMetaRegistryPass; -use Enqueue\Bundle\DependencyInjection\Compiler\BuildTopicMetaSubscribersPass; use Enqueue\Bundle\EnqueueBundle; use Enqueue\Test\ClassExtensionTrait; use PHPUnit\Framework\TestCase; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; class EnqueueBundleTest extends TestCase @@ -26,37 +20,4 @@ public function testCouldBeConstructedWithoutAnyArguments() { new EnqueueBundle(); } - - public function testShouldRegisterExpectedCompilerPasses() - { - $container = $this->createMock(ContainerBuilder::class); - $container - ->expects($this->at(0)) - ->method('addCompilerPass') - ->with($this->isInstanceOf(BuildConsumptionExtensionsPass::class)) - ; - $container - ->expects($this->at(2)) - ->method('addCompilerPass') - ->with($this->isInstanceOf(BuildProcessorRegistryPass::class)) - ; - $container - ->expects($this->at(3)) - ->method('addCompilerPass') - ->with($this->isInstanceOf(BuildTopicMetaSubscribersPass::class)) - ; - $container - ->expects($this->at(4)) - ->method('addCompilerPass') - ->with($this->isInstanceOf(BuildQueueMetaRegistryPass::class)) - ; - $container - ->expects($this->at(5)) - ->method('addCompilerPass') - ->with($this->isInstanceOf(BuildClientExtensionsPass::class)) - ; - - $bundle = new EnqueueBundle(); - $bundle->build($container); - } } From 41a57c26a562f01a499e2e338e10ceba912d566d Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Fri, 14 Sep 2018 18:50:58 +0300 Subject: [PATCH 06/31] [client] udpdate config. --- pkg/enqueue/Client/Config.php | 57 +++++++++- pkg/enqueue/Tests/Client/ConfigTest.php | 141 ++++++++++++++++++++++++ 2 files changed, 192 insertions(+), 6 deletions(-) diff --git a/pkg/enqueue/Client/Config.php b/pkg/enqueue/Client/Config.php index 46c023010..4e7dd398c 100644 --- a/pkg/enqueue/Client/Config.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/ConfigTest.php b/pkg/enqueue/Tests/Client/ConfigTest.php index 1ecb0f952..d616b5629 100644 --- a/pkg/enqueue/Tests/Client/ConfigTest.php +++ b/pkg/enqueue/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 00269decf435583430dfb9aa57e260261d9445a8 Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Fri, 14 Sep 2018 19:15:42 +0300 Subject: [PATCH 07/31] [client] Introduce GenericDriver. --- pkg/enqueue/Client/Driver/GenericDriver.php | 230 ++++++ .../Tests/Client/Driver/GenericDriverTest.php | 683 ++++++++++++++++++ 2 files changed, 913 insertions(+) create mode 100644 pkg/enqueue/Client/Driver/GenericDriver.php create mode 100644 pkg/enqueue/Tests/Client/Driver/GenericDriverTest.php diff --git a/pkg/enqueue/Client/Driver/GenericDriver.php b/pkg/enqueue/Client/Driver/GenericDriver.php new file mode 100644 index 000000000..553880241 --- /dev/null +++ b/pkg/enqueue/Client/Driver/GenericDriver.php @@ -0,0 +1,230 @@ +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/pkg/enqueue/Tests/Client/Driver/GenericDriverTest.php b/pkg/enqueue/Tests/Client/Driver/GenericDriverTest.php new file mode 100644 index 000000000..ddf85e91d --- /dev/null +++ b/pkg/enqueue/Tests/Client/Driver/GenericDriverTest.php @@ -0,0 +1,683 @@ +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 b10a43815223b2df236dcc3d59f83f31b93c285e Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Mon, 17 Sep 2018 13:13:15 +0300 Subject: [PATCH 08/31] [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 pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php diff --git a/pkg/enqueue/Tests/Client/Driver/GenericDriverTest.php b/pkg/enqueue/Tests/Client/Driver/GenericDriverTest.php index ddf85e91d..31f92a548 100644 --- a/pkg/enqueue/Tests/Client/Driver/GenericDriverTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php b/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php new file mode 100644 index 000000000..a297a7c90 --- /dev/null +++ b/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php @@ -0,0 +1,682 @@ +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 1037c7f6d0e8c82c45442fa45de1274f81be861d Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Mon, 17 Sep 2018 13:14:05 +0300 Subject: [PATCH 09/31] [client] Rework AmqpDriver fixes https://github.com/php-enqueue/enqueue-dev/issues/523 (for amqp) --- pkg/enqueue/Client/Driver/AmqpDriver.php | 162 ++++--- .../Tests/Client/Driver/AmqpDriverTest.php | 418 ++++++++---------- 2 files changed, 262 insertions(+), 318 deletions(-) diff --git a/pkg/enqueue/Client/Driver/AmqpDriver.php b/pkg/enqueue/Client/Driver/AmqpDriver.php index 714aab643..147d40bbd 100644 --- a/pkg/enqueue/Client/Driver/AmqpDriver.php +++ b/pkg/enqueue/Client/Driver/AmqpDriver.php @@ -1,11 +1,13 @@ 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/pkg/enqueue/Tests/Client/Driver/AmqpDriverTest.php b/pkg/enqueue/Tests/Client/Driver/AmqpDriverTest.php index 41a6432f7..13d399ca3 100644 --- a/pkg/enqueue/Tests/Client/Driver/AmqpDriverTest.php +++ b/pkg/enqueue/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 613de5629b8cccf8e19ee540de7c8b19025a2b1e Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Mon, 17 Sep 2018 18:28:39 +0300 Subject: [PATCH 10/31] [client] GenericDriver should init delivery delay, prioirt, time to live. --- pkg/enqueue/Client/Driver/GenericDriver.php | 46 ++- .../Client/Driver/GenericDriverTestsTrait.php | 288 ++++++++++++++++++ 2 files changed, 328 insertions(+), 6 deletions(-) diff --git a/pkg/enqueue/Client/Driver/GenericDriver.php b/pkg/enqueue/Client/Driver/GenericDriver.php index 553880241..64088d6b3 100644 --- a/pkg/enqueue/Client/Driver/GenericDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php b/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php index a297a7c90..b7f5f2bc2 100644 --- a/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php +++ b/pkg/enqueue/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 cde3e3c9378ab03bac22823b3435f4c5641fa69d Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Mon, 17 Sep 2018 18:29:09 +0300 Subject: [PATCH 11/31] [client] fix tests, update rabbitmq driver. --- pkg/enqueue/Client/Driver/AmqpDriver.php | 37 +- pkg/enqueue/Client/Driver/RabbitMqDriver.php | 123 +--- .../Tests/Client/Driver/AmqpDriverTest.php | 7 +- .../Client/Driver/RabbitMqDriverTest.php | 564 +++--------------- 4 files changed, 85 insertions(+), 646 deletions(-) diff --git a/pkg/enqueue/Client/Driver/AmqpDriver.php b/pkg/enqueue/Client/Driver/AmqpDriver.php index 147d40bbd..0b2a976f5 100644 --- a/pkg/enqueue/Client/Driver/AmqpDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/RabbitMqDriver.php b/pkg/enqueue/Client/Driver/RabbitMqDriver.php index ac9eb1496..fd1f0195a 100644 --- a/pkg/enqueue/Client/Driver/RabbitMqDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/AmqpDriverTest.php b/pkg/enqueue/Tests/Client/Driver/AmqpDriverTest.php index 13d399ca3..9e8ce1b9e 100644 --- a/pkg/enqueue/Tests/Client/Driver/AmqpDriverTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/RabbitMqDriverTest.php b/pkg/enqueue/Tests/Client/Driver/RabbitMqDriverTest.php index 60c92b8b0..8f27232d3 100644 --- a/pkg/enqueue/Tests/Client/Driver/RabbitMqDriverTest.php +++ b/pkg/enqueue/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 2e8493968aeca0636fb716c849813adfe060234d Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Mon, 17 Sep 2018 19:47:27 +0300 Subject: [PATCH 12/31] [client] Extend FsDriver from GenericDriver. --- pkg/enqueue/Client/Driver/AmqpDriver.php | 40 +-- pkg/enqueue/Client/Driver/FsDriver.php | 148 ++------ pkg/enqueue/Client/Driver/RabbitMqDriver.php | 2 +- .../Tests/Client/Driver/FsDriverTest.php | 336 +++--------------- 4 files changed, 73 insertions(+), 453 deletions(-) diff --git a/pkg/enqueue/Client/Driver/AmqpDriver.php b/pkg/enqueue/Client/Driver/AmqpDriver.php index 0b2a976f5..9b62ddcec 100644 --- a/pkg/enqueue/Client/Driver/AmqpDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/FsDriver.php b/pkg/enqueue/Client/Driver/FsDriver.php index 76c22b1e7..376e4191e 100644 --- a/pkg/enqueue/Client/Driver/FsDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/RabbitMqDriver.php b/pkg/enqueue/Client/Driver/RabbitMqDriver.php index fd1f0195a..2d5611b8a 100644 --- a/pkg/enqueue/Client/Driver/RabbitMqDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/FsDriverTest.php b/pkg/enqueue/Tests/Client/Driver/FsDriverTest.php index 6e9cb5530..f65cab0d1 100644 --- a/pkg/enqueue/Tests/Client/Driver/FsDriverTest.php +++ b/pkg/enqueue/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 ca7b0b766b104a922218b4970c4ffd5d0cfddf04 Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Tue, 18 Sep 2018 08:00:27 +0300 Subject: [PATCH 13/31] [client] Extend DbalDriver from GenericDriver. --- pkg/enqueue/Client/Driver/DbalDriver.php | 162 +-------- .../Tests/Client/Driver/DbalDriverTest.php | 327 ++---------------- 2 files changed, 41 insertions(+), 448 deletions(-) diff --git a/pkg/enqueue/Client/Driver/DbalDriver.php b/pkg/enqueue/Client/Driver/DbalDriver.php index d031bdb4b..ce4888552 100644 --- a/pkg/enqueue/Client/Driver/DbalDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/DbalDriverTest.php b/pkg/enqueue/Tests/Client/Driver/DbalDriverTest.php index 859ed5841..ed1064cb9 100644 --- a/pkg/enqueue/Tests/Client/Driver/DbalDriverTest.php +++ b/pkg/enqueue/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 ff3c342db8d2f783fe98c73adb7f82ac36e6315d Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Tue, 18 Sep 2018 10:27:30 +0300 Subject: [PATCH 14/31] [client] Extend GpsDriver from GenericDriver. --- pkg/enqueue/Client/Driver/GpsDriver.php | 148 ++------ .../Tests/Client/Driver/GpsDriverTest.php | 340 +++--------------- 2 files changed, 66 insertions(+), 422 deletions(-) diff --git a/pkg/enqueue/Client/Driver/GpsDriver.php b/pkg/enqueue/Client/Driver/GpsDriver.php index f45313e3b..84b574f84 100644 --- a/pkg/enqueue/Client/Driver/GpsDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/GpsDriverTest.php b/pkg/enqueue/Tests/Client/Driver/GpsDriverTest.php index 622f622f7..dd10e47d4 100644 --- a/pkg/enqueue/Tests/Client/Driver/GpsDriverTest.php +++ b/pkg/enqueue/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 99e178e526d8fdc2c8cb898088d35c312b75ccac Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Tue, 18 Sep 2018 10:48:22 +0300 Subject: [PATCH 15/31] [client] Extend MongodbDriver from GenericDriver. --- pkg/enqueue/Client/Driver/DbalDriver.php | 5 + pkg/enqueue/Client/Driver/GpsDriver.php | 5 + pkg/enqueue/Client/Driver/MongodbDriver.php | 152 +------- .../Tests/Client/Driver/MongodbDriverTest.php | 324 ++---------------- 4 files changed, 54 insertions(+), 432 deletions(-) diff --git a/pkg/enqueue/Client/Driver/DbalDriver.php b/pkg/enqueue/Client/Driver/DbalDriver.php index ce4888552..8b1f32655 100644 --- a/pkg/enqueue/Client/Driver/DbalDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/GpsDriver.php b/pkg/enqueue/Client/Driver/GpsDriver.php index 84b574f84..01dc943d6 100644 --- a/pkg/enqueue/Client/Driver/GpsDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/MongodbDriver.php b/pkg/enqueue/Client/Driver/MongodbDriver.php index 9e53946e3..19f2c57d3 100644 --- a/pkg/enqueue/Client/Driver/MongodbDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/MongodbDriverTest.php b/pkg/enqueue/Tests/Client/Driver/MongodbDriverTest.php index b10a06787..6c9b55a34 100644 --- a/pkg/enqueue/Tests/Client/Driver/MongodbDriverTest.php +++ b/pkg/enqueue/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 84464f0684c57cb42bfdf9de229d1662d0b9be6a Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Tue, 18 Sep 2018 11:11:20 +0300 Subject: [PATCH 16/31] [client] Remove NullDriver. Use generic instead. --- pkg/enqueue/Client/Driver/NullDriver.php | 141 --------- pkg/enqueue/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 pkg/enqueue/Client/Driver/NullDriver.php delete mode 100644 pkg/enqueue/Tests/Client/Driver/NullDriverTest.php diff --git a/pkg/enqueue/Client/Driver/NullDriver.php b/pkg/enqueue/Client/Driver/NullDriver.php deleted file mode 100644 index 41a936681..000000000 --- a/pkg/enqueue/Client/Driver/NullDriver.php +++ /dev/null @@ -1,141 +0,0 @@ -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/pkg/enqueue/Client/Resources.php b/pkg/enqueue/Client/Resources.php index 87da9dd68..bef313b83 100644 --- a/pkg/enqueue/Client/Resources.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/NullDriverTest.php b/pkg/enqueue/Tests/Client/Driver/NullDriverTest.php deleted file mode 100644 index 233e3b5b5..000000000 --- a/pkg/enqueue/Tests/Client/Driver/NullDriverTest.php +++ /dev/null @@ -1,277 +0,0 @@ -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/pkg/enqueue/Tests/Client/DriverFactoryTest.php b/pkg/enqueue/Tests/Client/DriverFactoryTest.php index 6d3e6ceb4..e1b96798e 100644 --- a/pkg/enqueue/Tests/Client/DriverFactoryTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Symfony/Client/Mock/SetupBrokerExtensionCommand.php b/pkg/enqueue/Tests/Symfony/Client/Mock/SetupBrokerExtensionCommand.php index c4b20cf0a..aa0c8126a 100644 --- a/pkg/enqueue/Tests/Symfony/Client/Mock/SetupBrokerExtensionCommand.php +++ b/pkg/enqueue/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 8bac597b3efd0ec46402c25b2dbeca558c203bc1 Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Tue, 18 Sep 2018 11:17:12 +0300 Subject: [PATCH 17/31] [client] Extend StompDriver from GenericDriver. --- pkg/enqueue/Client/Driver/StompDriver.php | 133 +------ .../Tests/Client/Driver/StompDriverTest.php | 340 +++++------------- 2 files changed, 104 insertions(+), 369 deletions(-) diff --git a/pkg/enqueue/Client/Driver/StompDriver.php b/pkg/enqueue/Client/Driver/StompDriver.php index d360847fa..aedd95b6d 100644 --- a/pkg/enqueue/Client/Driver/StompDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/StompDriverTest.php b/pkg/enqueue/Tests/Client/Driver/StompDriverTest.php index a8e9dab43..556d593ff 100644 --- a/pkg/enqueue/Tests/Client/Driver/StompDriverTest.php +++ b/pkg/enqueue/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 34ca2f67c8659399d5d4b936a22a6f819143fc07 Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Tue, 18 Sep 2018 12:18:39 +0300 Subject: [PATCH 18/31] [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 ++++++++---------- pkg/stomp/StompMessage.php | 12 +- pkg/stomp/Tests/StompMessageTest.php | 30 + 6 files changed, 363 insertions(+), 491 deletions(-) diff --git a/pkg/enqueue/Client/Driver/RabbitMqStompDriver.php b/pkg/enqueue/Client/Driver/RabbitMqStompDriver.php index a265f66e6..4a4d69738 100644 --- a/pkg/enqueue/Client/Driver/RabbitMqStompDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/DbalDriverTest.php b/pkg/enqueue/Tests/Client/Driver/DbalDriverTest.php index ed1064cb9..c6fd10d95 100644 --- a/pkg/enqueue/Tests/Client/Driver/DbalDriverTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/MongodbDriverTest.php b/pkg/enqueue/Tests/Client/Driver/MongodbDriverTest.php index 6c9b55a34..da9c7e6eb 100644 --- a/pkg/enqueue/Tests/Client/Driver/MongodbDriverTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/RabbitMqStompDriverTest.php b/pkg/enqueue/Tests/Client/Driver/RabbitMqStompDriverTest.php index b3d5a8b47..a153e460d 100644 --- a/pkg/enqueue/Tests/Client/Driver/RabbitMqStompDriverTest.php +++ b/pkg/enqueue/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); } } diff --git a/pkg/stomp/StompMessage.php b/pkg/stomp/StompMessage.php index e5451b475..b00588519 100644 --- a/pkg/stomp/StompMessage.php +++ b/pkg/stomp/StompMessage.php @@ -64,7 +64,11 @@ public function getProperties(): array public function setProperty(string $name, $value): void { - $this->properties[$name] = $value; + if (null === $value) { + unset($this->properties[$name]); + } else { + $this->properties[$name] = $value; + } } public function getProperty(string $name, $default = null) @@ -84,7 +88,11 @@ public function getHeaders(): array public function setHeader(string $name, $value): void { - $this->headers[$name] = $value; + if (null === $value) { + unset($this->headers[$name]); + } else { + $this->headers[$name] = $value; + } } public function getHeader(string $name, $default = null) diff --git a/pkg/stomp/Tests/StompMessageTest.php b/pkg/stomp/Tests/StompMessageTest.php index 6685433ba..736df79a2 100644 --- a/pkg/stomp/Tests/StompMessageTest.php +++ b/pkg/stomp/Tests/StompMessageTest.php @@ -86,4 +86,34 @@ public function testShouldSetReplyToAsHeader() self::assertSame(['reply-to' => 'theQueueName'], $message->getHeaders()); } + + public function testShouldUnsetHeaderIfNullPassed() + { + $message = new StompMessage(); + + $message->setHeader('aHeader', 'aVal'); + + //guard + $this->assertSame('aVal', $message->getHeader('aHeader')); + + $message->setHeader('aHeader', null); + + $this->assertNull($message->getHeader('aHeader')); + $this->assertSame([], $message->getHeaders()); + } + + public function testShouldUnsetPropertyIfNullPassed() + { + $message = new StompMessage(); + + $message->setProperty('aProperty', 'aVal'); + + //guard + $this->assertSame('aVal', $message->getProperty('aProperty')); + + $message->setProperty('aProperty', null); + + $this->assertNull($message->getProperty('aProperty')); + $this->assertSame([], $message->getProperties()); + } } From 3f26ba7264e30de72f9924494542e0fdc8c8c9ed Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Wed, 19 Sep 2018 14:53:52 +0300 Subject: [PATCH 19/31] [client] Extend rest of drivers from generic one. Rework driver factory. --- pkg/enqueue/Client/Driver/RdKafkaDriver.php | 138 +------ pkg/enqueue/Client/Driver/RedisDriver.php | 130 ------ pkg/enqueue/Client/Driver/SqsDriver.php | 137 ++----- pkg/enqueue/Client/DriverFactory.php | 31 +- pkg/enqueue/Client/DriverInterface.php | 5 + pkg/enqueue/Client/Resources.php | 46 ++- pkg/enqueue/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 +- pkg/enqueue/Tests/Client/ResourcesTest.php | 53 ++- 16 files changed, 294 insertions(+), 1424 deletions(-) delete mode 100644 pkg/enqueue/Client/Driver/RedisDriver.php delete mode 100644 pkg/enqueue/Tests/Client/Driver/RedisDriverTest.php diff --git a/pkg/enqueue/Client/Driver/RdKafkaDriver.php b/pkg/enqueue/Client/Driver/RdKafkaDriver.php index 7f536a709..01b9e2716 100644 --- a/pkg/enqueue/Client/Driver/RdKafkaDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/RedisDriver.php b/pkg/enqueue/Client/Driver/RedisDriver.php deleted file mode 100644 index 10b9cb967..000000000 --- a/pkg/enqueue/Client/Driver/RedisDriver.php +++ /dev/null @@ -1,130 +0,0 @@ -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/pkg/enqueue/Client/Driver/SqsDriver.php b/pkg/enqueue/Client/Driver/SqsDriver.php index 8aba2e172..622c6f7cf 100644 --- a/pkg/enqueue/Client/Driver/SqsDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/DriverFactory.php b/pkg/enqueue/Client/DriverFactory.php index 8c4729737..67d3607c0 100644 --- a/pkg/enqueue/Client/DriverFactory.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/DriverInterface.php b/pkg/enqueue/Client/DriverInterface.php index 11f70f689..e67155631 100644 --- a/pkg/enqueue/Client/DriverInterface.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Resources.php b/pkg/enqueue/Client/Resources.php index bef313b83..09ee43083 100644 --- a/pkg/enqueue/Client/Resources.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Route.php b/pkg/enqueue/Client/Route.php index e11fa19a9..5c98fa6b8 100644 --- a/pkg/enqueue/Client/Route.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/AmqpDriverTest.php b/pkg/enqueue/Tests/Client/Driver/AmqpDriverTest.php index 9e8ce1b9e..91a7f528f 100644 --- a/pkg/enqueue/Tests/Client/Driver/AmqpDriverTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php b/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php index b7f5f2bc2..4c03a1467 100644 --- a/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/GpsDriverTest.php b/pkg/enqueue/Tests/Client/Driver/GpsDriverTest.php index dd10e47d4..15b8e63bd 100644 --- a/pkg/enqueue/Tests/Client/Driver/GpsDriverTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/RabbitMqStompDriverTest.php b/pkg/enqueue/Tests/Client/Driver/RabbitMqStompDriverTest.php index a153e460d..8a866a8aa 100644 --- a/pkg/enqueue/Tests/Client/Driver/RabbitMqStompDriverTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/RdKafkaDriverTest.php b/pkg/enqueue/Tests/Client/Driver/RdKafkaDriverTest.php index f2b7c86d9..3c89fe117 100644 --- a/pkg/enqueue/Tests/Client/Driver/RdKafkaDriverTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/RedisDriverTest.php b/pkg/enqueue/Tests/Client/Driver/RedisDriverTest.php deleted file mode 100644 index 1dd5950bd..000000000 --- a/pkg/enqueue/Tests/Client/Driver/RedisDriverTest.php +++ /dev/null @@ -1,366 +0,0 @@ -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/pkg/enqueue/Tests/Client/Driver/SqsDriverTest.php b/pkg/enqueue/Tests/Client/Driver/SqsDriverTest.php index d64df835a..3888326c7 100644 --- a/pkg/enqueue/Tests/Client/Driver/SqsDriverTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/DriverFactoryTest.php b/pkg/enqueue/Tests/Client/DriverFactoryTest.php index e1b96798e..a51adb342 100644 --- a/pkg/enqueue/Tests/Client/DriverFactoryTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/ResourcesTest.php b/pkg/enqueue/Tests/Client/ResourcesTest.php index 08feabf0a..7311f31d0 100644 --- a/pkg/enqueue/Tests/Client/ResourcesTest.php +++ b/pkg/enqueue/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 f90c8d157e9c03d965679c1961a3343e91a72780 Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Fri, 21 Sep 2018 12:46:49 +0300 Subject: [PATCH 20/31] upd simple client. --- docs/client/quick_tour.md | 2 +- docs/client/rpc_call.md | 3 +- docs/laravel/quick_tour.md | 2 +- docs/quick_tour.md | 19 ++-- pkg/simple-client/SimpleClient.php | 34 ++++++-- .../SimpleClientContainerExtension.php | 8 +- .../Tests/Functional/SimpleClientTest.php | 86 +++++++++---------- 7 files changed, 91 insertions(+), 63 deletions(-) diff --git a/docs/client/quick_tour.md b/docs/client/quick_tour.md index e7ca402cb..d7ebb27fe 100644 --- a/docs/client/quick_tour.md +++ b/docs/client/quick_tour.md @@ -89,7 +89,7 @@ use Interop\Queue\PsrProcessor; /** @var \Enqueue\SimpleClient\SimpleClient $client */ -$client->bind('a_bar_topic', 'a_processor_name', function(PsrMessage $psrMessage) { +$client->bindTopic('a_bar_topic', function(PsrMessage $psrMessage) { // processing logic here return PsrProcessor::ACK; diff --git a/docs/client/rpc_call.md b/docs/client/rpc_call.md index 4afb1e722..01420fba3 100644 --- a/docs/client/rpc_call.md +++ b/docs/client/rpc_call.md @@ -19,7 +19,6 @@ use Interop\Queue\PsrContext; use Enqueue\Consumption\Result; use Enqueue\Consumption\ChainExtension; use Enqueue\Consumption\Extension\ReplyExtension; -use Enqueue\Client\Config; use Enqueue\SimpleClient\SimpleClient; /** @var \Interop\Queue\PsrContext $context */ @@ -27,7 +26,7 @@ use Enqueue\SimpleClient\SimpleClient; // composer require enqueue/amqp-ext # or enqueue/amqp-bunny, enqueue/amqp-lib $client = new SimpleClient('amqp:'); -$client->bind(Config::COMMAND_TOPIC, 'square', function (PsrMessage $message, PsrContext $context) use (&$requestMessage) { +$client->bindCommand('square', function (PsrMessage $message, PsrContext $context) use (&$requestMessage) { $number = (int) $message->getBody(); return Result::reply($context->createMessage($number ^ 2)); diff --git a/docs/laravel/quick_tour.md b/docs/laravel/quick_tour.md index e14f4604c..653367805 100644 --- a/docs/laravel/quick_tour.md +++ b/docs/laravel/quick_tour.md @@ -72,7 +72,7 @@ use Interop\Queue\PsrMessage; use Interop\Queue\PsrProcessor; $app->resolving(SimpleClient::class, function (SimpleClient $client, $app) { - $client->bind('enqueue_test', 'a_processor', function(PsrMessage $message) { + $client->bindTopic('enqueue_test', function(PsrMessage $message) { // do stuff here return PsrProcessor::ACK; diff --git a/docs/quick_tour.md b/docs/quick_tour.md index 1c293cefd..0d4467687 100644 --- a/docs/quick_tour.md +++ b/docs/quick_tour.md @@ -175,17 +175,16 @@ $client = new SimpleClient('amqp:'); // composer require enqueue/fs $client = new SimpleClient('file://foo/bar'); - -$client->setupBroker(); - -$client->sendEvent('a_foo_topic', 'message'); - -$client->bind('a_foo_topic', 'fooProcessor', function(PsrMessage $message) { +$client->bindTopic('a_foo_topic', function(PsrMessage $message) { echo $message->getBody().PHP_EOL; // your event processor logic here }); +$client->setupBroker(); + +$client->sendEvent('a_foo_topic', 'message'); + // this is a blocking call, it'll consume message until it is interrupted $client->consume(); ``` @@ -207,18 +206,18 @@ $client = new SimpleClient('amqp:'); // composer require enqueue/fs //$client = new SimpleClient('file://foo/bar'); -$client->setupBroker(); - -$client->bind(Config::COMMAND_TOPIC, 'bar_command', function(PsrMessage $message) { +$client->bindCommand('bar_command', function(PsrMessage $message) { // your bar command processor logic here }); -$client->bind(Config::COMMAND_TOPIC, 'baz_reply_command', function(PsrMessage $message, PsrContext $context) { +$client->bindCommand('baz_reply_command', function(PsrMessage $message, PsrContext $context) { // your baz reply command processor logic here return Result::reply($context->createMessage('theReplyBody')); }); +$client->setupBroker(); + // It is sent to one consumer. $client->sendCommand('bar_command', 'aMessageData'); diff --git a/pkg/simple-client/SimpleClient.php b/pkg/simple-client/SimpleClient.php index 18f1887d1..719631aab 100644 --- a/pkg/simple-client/SimpleClient.php +++ b/pkg/simple-client/SimpleClient.php @@ -11,6 +11,8 @@ use Enqueue\Client\Meta\TopicMetaRegistry; use Enqueue\Client\ProcessorRegistryInterface; use Enqueue\Client\ProducerInterface; +use Enqueue\Client\Route; +use Enqueue\Client\RouteCollection; use Enqueue\Client\RouterProcessor; use Enqueue\Consumption\CallbackProcessor; use Enqueue\Consumption\ExtensionInterface; @@ -90,7 +92,7 @@ public function __construct($config, ContainerBuilder $container = null) /** * @param callable|PsrProcessor $processor */ - public function bind(string $topic, string $processorName, $processor): void + public function bindTopic(string $topic, $processor, string $processorName = null): void { if (is_callable($processor)) { $processor = new CallbackProcessor($processor); @@ -100,11 +102,28 @@ public function bind(string $topic, string $processorName, $processor): void throw new \LogicException('The processor must be either callable or instance of PsrProcessor'); } - $queueName = $this->getConfig()->getDefaultProcessorQueueName(); + $processorName = $processorName ?: uniqid(get_class($processor)); - $this->getRouterProcessor()->add($topic, $queueName, $processorName); - $this->getTopicMetaRegistry()->addProcessor($topic, $processorName); - $this->getQueueMetaRegistry()->addProcessor($queueName, $processorName); + $this->getRouteCollection()->add(new Route($topic, Route::TOPIC, $processorName)); + $this->getProcessorRegistry()->add($processorName, $processor); + } + + /** + * @param callable|PsrProcessor $processor + */ + public function bindCommand(string $command, $processor, string $processorName = null): void + { + if (is_callable($processor)) { + $processor = new CallbackProcessor($processor); + } + + if (false == $processor instanceof PsrProcessor) { + throw new \LogicException('The processor must be either callable or instance of PsrProcessor'); + } + + $processorName = $processorName ?: uniqid(get_class($processor)); + + $this->getRouteCollection()->add(new Route($command, Route::COMMAND, $processorName)); $this->getProcessorRegistry()->add($processorName, $processor); } @@ -204,6 +223,11 @@ public function getRouterProcessor(): RouterProcessor return $this->container->get('enqueue.client.router_processor'); } + private function getRouteCollection(): RouteCollection + { + return $this->container->get('enqueue.client.route_collection'); + } + private function buildContainer($config, ContainerBuilder $container): ContainerInterface { $extension = new SimpleClientContainerExtension(); diff --git a/pkg/simple-client/SimpleClientContainerExtension.php b/pkg/simple-client/SimpleClientContainerExtension.php index 6c862a64c..ec5146cc3 100644 --- a/pkg/simple-client/SimpleClientContainerExtension.php +++ b/pkg/simple-client/SimpleClientContainerExtension.php @@ -11,6 +11,7 @@ use Enqueue\Client\Meta\QueueMetaRegistry; use Enqueue\Client\Meta\TopicMetaRegistry; use Enqueue\Client\Producer; +use Enqueue\Client\RouteCollection; use Enqueue\Client\RouterProcessor; use Enqueue\ConnectionFactoryFactory; use Enqueue\Consumption\ChainExtension as ConsumptionChainExtension; @@ -40,7 +41,7 @@ public function load(array $configs, ContainerBuilder $container): void $container->register('enqueue.client.driver_factory', DriverFactory::class) ->addArgument(new Reference('enqueue.client.config')) - ->addArgument(new Reference('enqueue.client.meta.queue_meta_registry')) + ->addArgument(new Reference('enqueue.client.route_collection')) ; $transportFactory = (new TransportFactory('default')); @@ -63,6 +64,11 @@ public function load(array $configs, ContainerBuilder $container): void ]) ; + $container->register('enqueue.client.route_collection', RouteCollection::class) + ->setPublic(true) + ->addArgument([]) + ; + $container->register('enqueue.client.rpc_factory', RpcFactory::class) ->setPublic(true) ->setArguments([ diff --git a/pkg/simple-client/Tests/Functional/SimpleClientTest.php b/pkg/simple-client/Tests/Functional/SimpleClientTest.php index 29216e615..b5615ac71 100644 --- a/pkg/simple-client/Tests/Functional/SimpleClientTest.php +++ b/pkg/simple-client/Tests/Functional/SimpleClientTest.php @@ -75,7 +75,7 @@ public function testProduceAndConsumeOneMessage(array $config) $client = new SimpleClient($config); - $client->bind('foo_topic', 'foo_processor', function (PsrMessage $message) use (&$actualMessage) { + $client->bindTopic('foo_topic', function (PsrMessage $message) use (&$actualMessage) { $actualMessage = $message; return Result::ACK; @@ -95,48 +95,48 @@ public function testProduceAndConsumeOneMessage(array $config) $this->assertSame('Hello there!', $actualMessage->getBody()); } - /** - * @dataProvider transportConfigDataProvider - * - * @param mixed $config - */ - public function testProduceAndRouteToTwoConsumes($config) - { - $received = 0; - - $config['client'] = [ - 'prefix' => str_replace('.', '', uniqid('enqueue', true)), - 'app_name' => 'simple_client', - 'router_topic' => 'test', - 'router_queue' => 'test', - 'default_processor_queue' => 'test', - ]; - - $client = new SimpleClient($config); - - $client->bind('foo_topic', 'foo_processor1', function () use (&$received) { - ++$received; - - return Result::ACK; - }); - $client->bind('foo_topic', 'foo_processor2', function () use (&$received) { - ++$received; - - return Result::ACK; - }); - - $client->setupBroker(); - $this->purgeQueue($client); - - $client->sendEvent('foo_topic', 'Hello there!'); - - $client->consume(new ChainExtension([ - new LimitConsumptionTimeExtension(new \DateTime('+2sec')), - new LimitConsumedMessagesExtension(3), - ])); - - $this->assertSame(2, $received); - } +// /** +// * @dataProvider transportConfigDataProvider +// * +// * @param mixed $config +// */ +// public function testProduceAndRouteToTwoConsumes($config) +// { +// $received = 0; +// +// $config['client'] = [ +// 'prefix' => str_replace('.', '', uniqid('enqueue', true)), +// 'app_name' => 'simple_client', +// 'router_topic' => 'test', +// 'router_queue' => 'test', +// 'default_processor_queue' => 'test', +// ]; +// +// $client = new SimpleClient($config); +// +// $client->bindTopic('foo_topic', function () use (&$received) { +// ++$received; +// +// return Result::ACK; +// }); +// $client->bindTopic('foo_topic', function () use (&$received) { +// ++$received; +// +// return Result::ACK; +// }); +// +// $client->setupBroker(); +// $this->purgeQueue($client); +// +// $client->sendEvent('foo_topic', 'Hello there!'); +// +// $client->consume(new ChainExtension([ +// new LimitConsumptionTimeExtension(new \DateTime('+2sec')), +// new LimitConsumedMessagesExtension(3), +// ])); +// +// $this->assertSame(2, $received); +// } protected function purgeQueue(SimpleClient $client): void { From c075ec2ca0519c57efce1f554d7356c50f8ce5d2 Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Fri, 21 Sep 2018 15:56:41 +0300 Subject: [PATCH 21/31] upd drivers --- .../SetRouterPropertiesExtension.php | 1 - pkg/enqueue/Client/Driver/AmqpDriver.php | 4 +- pkg/enqueue/Client/Driver/GenericDriver.php | 50 +++++---- pkg/enqueue/Client/Driver/RabbitMqDriver.php | 4 +- .../Client/Driver/RabbitMqStompDriver.php | 22 ++-- pkg/enqueue/Client/Driver/StompDriver.php | 4 +- pkg/enqueue/Client/DriverPreSend.php | 7 +- pkg/enqueue/Client/Message.php | 10 +- pkg/enqueue/Client/PostSend.php | 7 +- pkg/enqueue/Client/Producer.php | 27 ++--- pkg/enqueue/Client/Resources.php | 12 ++ pkg/enqueue/Client/TraceableProducer.php | 3 +- .../SetRouterPropertiesExtensionTest.php | 1 - .../Client/Driver/GenericDriverTestsTrait.php | 104 ++++++++++++++---- .../Tests/Client/DriverFactoryTest.php | 8 ++ .../Tests/Client/DriverPreSendTest.php | 1 - pkg/enqueue/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/pkg/enqueue/Client/ConsumptionExtension/SetRouterPropertiesExtension.php b/pkg/enqueue/Client/ConsumptionExtension/SetRouterPropertiesExtension.php index d294a9d87..27ef6f389 100644 --- a/pkg/enqueue/Client/ConsumptionExtension/SetRouterPropertiesExtension.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/AmqpDriver.php b/pkg/enqueue/Client/Driver/AmqpDriver.php index 9b62ddcec..875cc80ed 100644 --- a/pkg/enqueue/Client/Driver/AmqpDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/GenericDriver.php b/pkg/enqueue/Client/Driver/GenericDriver.php index 64088d6b3..26a82777a 100644 --- a/pkg/enqueue/Client/Driver/GenericDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/RabbitMqDriver.php b/pkg/enqueue/Client/Driver/RabbitMqDriver.php index 2d5611b8a..5e94b165f 100644 --- a/pkg/enqueue/Client/Driver/RabbitMqDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/RabbitMqStompDriver.php b/pkg/enqueue/Client/Driver/RabbitMqStompDriver.php index 4a4d69738..4dd72f635 100644 --- a/pkg/enqueue/Client/Driver/RabbitMqStompDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/StompDriver.php b/pkg/enqueue/Client/Driver/StompDriver.php index aedd95b6d..8ef90462f 100644 --- a/pkg/enqueue/Client/Driver/StompDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/DriverPreSend.php b/pkg/enqueue/Client/DriverPreSend.php index 8a298cb5a..d940fc023 100644 --- a/pkg/enqueue/Client/DriverPreSend.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Message.php b/pkg/enqueue/Client/Message.php index d58371b38..6f1186dcc 100644 --- a/pkg/enqueue/Client/Message.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/PostSend.php b/pkg/enqueue/Client/PostSend.php index 2e0ae627a..5f575e2a6 100644 --- a/pkg/enqueue/Client/PostSend.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Producer.php b/pkg/enqueue/Client/Producer.php index 7a4d18f9e..e236cd1dc 100644 --- a/pkg/enqueue/Client/Producer.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Resources.php b/pkg/enqueue/Client/Resources.php index 09ee43083..71f8c534d 100644 --- a/pkg/enqueue/Client/Resources.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/TraceableProducer.php b/pkg/enqueue/Client/TraceableProducer.php index 40ff580d4..86ff9f8c6 100644 --- a/pkg/enqueue/Client/TraceableProducer.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/pkg/enqueue/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php index 6dc723c37..f85ace3c3 100644 --- a/pkg/enqueue/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php b/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php index 4c03a1467..8e1d3fe98 100644 --- a/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/DriverFactoryTest.php b/pkg/enqueue/Tests/Client/DriverFactoryTest.php index a51adb342..f17ce38a0 100644 --- a/pkg/enqueue/Tests/Client/DriverFactoryTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/DriverPreSendTest.php b/pkg/enqueue/Tests/Client/DriverPreSendTest.php index 62d493b7b..aa9ff397f 100644 --- a/pkg/enqueue/Tests/Client/DriverPreSendTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/PostSendTest.php b/pkg/enqueue/Tests/Client/PostSendTest.php index fe78c14a1..902403446 100644 --- a/pkg/enqueue/Tests/Client/PostSendTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/ProducerSendCommandTest.php b/pkg/enqueue/Tests/Client/ProducerSendCommandTest.php index 057d22730..67b948537 100644 --- a/pkg/enqueue/Tests/Client/ProducerSendCommandTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/ProducerSendEventTest.php b/pkg/enqueue/Tests/Client/ProducerSendEventTest.php index a5d45d3dc..13cddbc31 100644 --- a/pkg/enqueue/Tests/Client/ProducerSendEventTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/TraceableProducerTest.php b/pkg/enqueue/Tests/Client/TraceableProducerTest.php index 42c9c8b8f..82364cacc 100644 --- a/pkg/enqueue/Tests/Client/TraceableProducerTest.php +++ b/pkg/enqueue/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 2b9cb8a5853b4143a91aa69cfea794635b75987a Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Fri, 21 Sep 2018 18:20:09 +0300 Subject: [PATCH 22/31] upd client. --- pkg/enqueue/Client/Config.php | 3 - pkg/enqueue/Client/Driver/GenericDriver.php | 20 +-- pkg/enqueue/Client/DriverInterface.php | 2 + .../Client/Driver/GenericDriverTestsTrait.php | 150 ++++++++++++++++-- 4 files changed, 149 insertions(+), 26 deletions(-) diff --git a/pkg/enqueue/Client/Config.php b/pkg/enqueue/Client/Config.php index 4e7dd398c..f85d25de4 100644 --- a/pkg/enqueue/Client/Config.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/GenericDriver.php b/pkg/enqueue/Client/Driver/GenericDriver.php index 26a82777a..c048b3fbf 100644 --- a/pkg/enqueue/Client/Driver/GenericDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/DriverInterface.php b/pkg/enqueue/Client/DriverInterface.php index e67155631..96144a5d9 100644 --- a/pkg/enqueue/Client/DriverInterface.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php b/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php index 8e1d3fe98..daafc6610 100644 --- a/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php +++ b/pkg/enqueue/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 e25feb1403be8fe4c2b9f9746845a44520a215f0 Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Fri, 21 Sep 2018 20:17:23 +0300 Subject: [PATCH 23/31] client fixes --- .../ConsumptionExtension/SetRouterPropertiesExtension.php | 8 +++++--- pkg/enqueue/Client/Driver/GenericDriver.php | 2 +- pkg/enqueue/Client/Producer.php | 4 ---- .../SetRouterPropertiesExtensionTest.php | 2 ++ pkg/enqueue/Tests/Client/ProducerSendCommandTest.php | 4 ++-- pkg/enqueue/Tests/Client/ProducerSendEventTest.php | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pkg/enqueue/Client/ConsumptionExtension/SetRouterPropertiesExtension.php b/pkg/enqueue/Client/ConsumptionExtension/SetRouterPropertiesExtension.php index 27ef6f389..a87a098f5 100644 --- a/pkg/enqueue/Client/ConsumptionExtension/SetRouterPropertiesExtension.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/GenericDriver.php b/pkg/enqueue/Client/Driver/GenericDriver.php index c048b3fbf..2cb8ca437 100644 --- a/pkg/enqueue/Client/Driver/GenericDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Producer.php b/pkg/enqueue/Client/Producer.php index e236cd1dc..977b4b577 100644 --- a/pkg/enqueue/Client/Producer.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php b/pkg/enqueue/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php index f85ace3c3..686ece9c5 100644 --- a/pkg/enqueue/Tests/Client/ConsumptionExtension/SetRouterPropertiesExtensionTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/ProducerSendCommandTest.php b/pkg/enqueue/Tests/Client/ProducerSendCommandTest.php index 67b948537..21b1f5b27 100644 --- a/pkg/enqueue/Tests/Client/ProducerSendCommandTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/ProducerSendEventTest.php b/pkg/enqueue/Tests/Client/ProducerSendEventTest.php index 13cddbc31..757c61234 100644 --- a/pkg/enqueue/Tests/Client/ProducerSendEventTest.php +++ b/pkg/enqueue/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 39924b90331cd74ec8a4f9cd6b11abfdf28c8afb Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Fri, 21 Sep 2018 23:20:12 +0300 Subject: [PATCH 24/31] remove stom examples. --- pkg/stomp/examples/consume.php | 43 ---------------------------------- pkg/stomp/examples/publish.php | 39 ------------------------------ 2 files changed, 82 deletions(-) delete mode 100644 pkg/stomp/examples/consume.php delete mode 100644 pkg/stomp/examples/publish.php diff --git a/pkg/stomp/examples/consume.php b/pkg/stomp/examples/consume.php deleted file mode 100644 index a8af3db3a..000000000 --- a/pkg/stomp/examples/consume.php +++ /dev/null @@ -1,43 +0,0 @@ -createContext(); - - $destination = $context->createQueue('destination'); - $destination->setDurable(true); - $destination->setAutoDelete(false); - - $consumer = $context->createConsumer($destination); - - while (true) { - if ($message = $consumer->receive()) { - $consumer->acknowledge($message); - - var_dump($message->getBody()); - var_dump($message->getProperties()); - var_dump($message->getHeaders()); - echo '-------------------------------------'.PHP_EOL; - } - } -} catch (ErrorFrameException $e) { - var_dump($e->getFrame()); -} diff --git a/pkg/stomp/examples/publish.php b/pkg/stomp/examples/publish.php deleted file mode 100644 index afc08d590..000000000 --- a/pkg/stomp/examples/publish.php +++ /dev/null @@ -1,39 +0,0 @@ -createContext(); - - $destination = $context->createQueue('destination'); - $destination->setDurable(true); - $destination->setAutoDelete(false); - - $producer = $context->createProducer(); - - $i = 1; - while (true) { - $message = $context->createMessage('payload: '.$i++); - $producer->send($destination, $message); - usleep(1000); - } -} catch (ErrorFrameException $e) { - var_dump($e->getFrame()); -} From 19ace56a3bac5b2d7c2cbde5527db4d99bb08b3d Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Fri, 21 Sep 2018 23:22:37 +0300 Subject: [PATCH 25/31] [client] migrate simple client to new concept. adopt route collection. --- pkg/enqueue/Client/Driver/DbalDriver.php | 10 ++ pkg/enqueue/Client/Driver/FsDriver.php | 16 +- pkg/enqueue/Client/Driver/MongodbDriver.php | 10 ++ pkg/enqueue/Client/Driver/RedisDriver.php | 27 ++++ pkg/enqueue/Client/Driver/SqsDriver.php | 9 ++ pkg/enqueue/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 + .../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 ++- pkg/simple-client/SimpleClient.php | 39 +++-- .../SimpleClientContainerExtension.php | 12 -- .../Tests/Functional/SimpleClientTest.php | 141 +++++++++++------- 19 files changed, 277 insertions(+), 141 deletions(-) create mode 100644 pkg/enqueue/Client/Driver/RedisDriver.php diff --git a/pkg/enqueue/Client/Driver/DbalDriver.php b/pkg/enqueue/Client/Driver/DbalDriver.php index 8b1f32655..165bcf723 100644 --- a/pkg/enqueue/Client/Driver/DbalDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/FsDriver.php b/pkg/enqueue/Client/Driver/FsDriver.php index 376e4191e..1ce7032f3 100644 --- a/pkg/enqueue/Client/Driver/FsDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/MongodbDriver.php b/pkg/enqueue/Client/Driver/MongodbDriver.php index 19f2c57d3..ed31727c3 100644 --- a/pkg/enqueue/Client/Driver/MongodbDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/RedisDriver.php b/pkg/enqueue/Client/Driver/RedisDriver.php new file mode 100644 index 000000000..264409e75 --- /dev/null +++ b/pkg/enqueue/Client/Driver/RedisDriver.php @@ -0,0 +1,27 @@ +createQueue($this->getConfig()->getRouterQueueName()); + } +} diff --git a/pkg/enqueue/Client/Driver/SqsDriver.php b/pkg/enqueue/Client/Driver/SqsDriver.php index 622c6f7cf..db94ad148 100644 --- a/pkg/enqueue/Client/Driver/SqsDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Resources.php b/pkg/enqueue/Client/Resources.php index 71f8c534d..56fc76472 100644 --- a/pkg/enqueue/Client/Resources.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/AmqpDriverTest.php b/pkg/enqueue/Tests/Client/Driver/AmqpDriverTest.php index 91a7f528f..e9c1d7709 100644 --- a/pkg/enqueue/Tests/Client/Driver/AmqpDriverTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/FsDriverTest.php b/pkg/enqueue/Tests/Client/Driver/FsDriverTest.php index f65cab0d1..bbc083be2 100644 --- a/pkg/enqueue/Tests/Client/Driver/FsDriverTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/GenericDriverTest.php b/pkg/enqueue/Tests/Client/Driver/GenericDriverTest.php index 31f92a548..ac4cd7a80 100644 --- a/pkg/enqueue/Tests/Client/Driver/GenericDriverTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php b/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php index daafc6610..78d212255 100644 --- a/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/GpsDriverTest.php b/pkg/enqueue/Tests/Client/Driver/GpsDriverTest.php index 15b8e63bd..8b48a3683 100644 --- a/pkg/enqueue/Tests/Client/Driver/GpsDriverTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/RabbitMqDriverTest.php b/pkg/enqueue/Tests/Client/Driver/RabbitMqDriverTest.php index 8f27232d3..295ac7267 100644 --- a/pkg/enqueue/Tests/Client/Driver/RabbitMqDriverTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/RabbitMqStompDriverTest.php b/pkg/enqueue/Tests/Client/Driver/RabbitMqStompDriverTest.php index 8a866a8aa..c055d8baa 100644 --- a/pkg/enqueue/Tests/Client/Driver/RabbitMqStompDriverTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/RdKafkaDriverTest.php b/pkg/enqueue/Tests/Client/Driver/RdKafkaDriverTest.php index 3c89fe117..899830e5c 100644 --- a/pkg/enqueue/Tests/Client/Driver/RdKafkaDriverTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/SqsDriverTest.php b/pkg/enqueue/Tests/Client/Driver/SqsDriverTest.php index 3888326c7..bf8bef1c9 100644 --- a/pkg/enqueue/Tests/Client/Driver/SqsDriverTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/StompDriverTest.php b/pkg/enqueue/Tests/Client/Driver/StompDriverTest.php index 556d593ff..b68e63e08 100644 --- a/pkg/enqueue/Tests/Client/Driver/StompDriverTest.php +++ b/pkg/enqueue/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'; + } } diff --git a/pkg/simple-client/SimpleClient.php b/pkg/simple-client/SimpleClient.php index 719631aab..7112ad362 100644 --- a/pkg/simple-client/SimpleClient.php +++ b/pkg/simple-client/SimpleClient.php @@ -7,8 +7,6 @@ use Enqueue\Client\DelegateProcessor; use Enqueue\Client\DriverInterface; use Enqueue\Client\Message; -use Enqueue\Client\Meta\QueueMetaRegistry; -use Enqueue\Client\Meta\TopicMetaRegistry; use Enqueue\Client\ProcessorRegistryInterface; use Enqueue\Client\ProducerInterface; use Enqueue\Client\Route; @@ -146,21 +144,28 @@ public function sendEvent(string $topic, $message): void public function consume(ExtensionInterface $runtimeExtension = null): void { $this->setupBroker(); + $processor = $this->getDelegateProcessor(); - $queueConsumer = $this->getQueueConsumer(); + $consumer = $this->getQueueConsumer(); + + $boundQueues = []; + + $routerQueue = $this->getDriver()->createQueue($this->getConfig()->getRouterQueueName()); + $consumer->bind($routerQueue, $processor); + $boundQueues[$routerQueue->getQueueName()] = true; - $defaultQueueName = $this->getConfig()->getDefaultProcessorQueueName(); - $defaultTransportQueueName = $this->getDriver()->createQueue($defaultQueueName); - $queueConsumer->bind($defaultTransportQueueName, $processor); + foreach ($this->getRouteCollection()->all() as $route) { + $queue = $this->getDriver()->createRouteQueue($route); + if (array_key_exists($queue->getQueueName(), $boundQueues)) { + continue; + } - $routerQueueName = $this->getConfig()->getRouterQueueName(); - if ($routerQueueName != $defaultQueueName) { - $routerTransportQueueName = $this->getDriver()->createQueue($routerQueueName); + $consumer->bind($queue, $processor); - $queueConsumer->bind($routerTransportQueueName, $processor); + $boundQueues[$queue->getQueueName()] = true; } - $queueConsumer->consume($runtimeExtension); + $consumer->consume($runtimeExtension); } public function getContext(): PsrContext @@ -183,16 +188,6 @@ public function getDriver(): DriverInterface return $this->container->get('enqueue.client.default.driver'); } - public function getTopicMetaRegistry(): TopicMetaRegistry - { - return $this->container->get('enqueue.client.meta.topic_meta_registry'); - } - - public function getQueueMetaRegistry(): QueueMetaRegistry - { - return $this->container->get('enqueue.client.meta.queue_meta_registry'); - } - public function getProducer(bool $setupBroker = false): ProducerInterface { $setupBroker && $this->setupBroker(); @@ -223,7 +218,7 @@ public function getRouterProcessor(): RouterProcessor return $this->container->get('enqueue.client.router_processor'); } - private function getRouteCollection(): RouteCollection + public function getRouteCollection(): RouteCollection { return $this->container->get('enqueue.client.route_collection'); } diff --git a/pkg/simple-client/SimpleClientContainerExtension.php b/pkg/simple-client/SimpleClientContainerExtension.php index ec5146cc3..6ca86b86b 100644 --- a/pkg/simple-client/SimpleClientContainerExtension.php +++ b/pkg/simple-client/SimpleClientContainerExtension.php @@ -8,8 +8,6 @@ use Enqueue\Client\ConsumptionExtension\SetRouterPropertiesExtension; use Enqueue\Client\DelegateProcessor; use Enqueue\Client\DriverFactory; -use Enqueue\Client\Meta\QueueMetaRegistry; -use Enqueue\Client\Meta\TopicMetaRegistry; use Enqueue\Client\Producer; use Enqueue\Client\RouteCollection; use Enqueue\Client\RouterProcessor; @@ -84,14 +82,6 @@ public function load(array $configs, ContainerBuilder $container): void ]) ; - $container->register('enqueue.client.meta.topic_meta_registry', TopicMetaRegistry::class) - ->setPublic(true) - ->setArguments([[]]); - - $container->register('enqueue.client.meta.queue_meta_registry', QueueMetaRegistry::class) - ->setPublic(true) - ->setArguments([new Reference('enqueue.client.config'), []]); - $container->register('enqueue.client.processor_registry', ArrayProcessorRegistry::class) ->setPublic(true) ; @@ -113,8 +103,6 @@ public function load(array $configs, ContainerBuilder $container): void ->setArguments([new Reference('enqueue.client.default.driver'), []]); $container->getDefinition('enqueue.client.processor_registry') ->addMethodCall('add', ['enqueue.client.router_processor', new Reference('enqueue.client.router_processor')]); - $container->getDefinition('enqueue.client.meta.queue_meta_registry') - ->addMethodCall('addProcessor', [$config['client']['router_queue'], 'enqueue.client.router_processor']); // extensions $extensions = []; diff --git a/pkg/simple-client/Tests/Functional/SimpleClientTest.php b/pkg/simple-client/Tests/Functional/SimpleClientTest.php index b5615ac71..25fdc4396 100644 --- a/pkg/simple-client/Tests/Functional/SimpleClientTest.php +++ b/pkg/simple-client/Tests/Functional/SimpleClientTest.php @@ -20,11 +20,11 @@ public function transportConfigDataProvider() { yield 'amqp_dsn' => [[ 'transport' => getenv('AMQP_DSN'), - ]]; + ], '+1sec']; yield 'dbal_dsn' => [[ 'transport' => getenv('DOCTRINE_DSN'), - ]]; + ], '+1sec']; yield 'rabbitmq_stomp' => [[ 'transport' => [ @@ -32,28 +32,28 @@ public function transportConfigDataProvider() 'lazy' => false, 'management_plugin_installed' => true, ], - ]]; + ], '+1sec']; yield 'predis_dsn' => [[ 'transport' => [ 'dsn' => getenv('PREDIS_DSN'), 'lazy' => false, ], - ]]; + ], '+1sec']; yield 'fs_dsn' => [[ 'transport' => 'file://'.sys_get_temp_dir(), - ]]; + ], '+1sec']; yield 'sqs' => [[ 'transport' => [ 'dsn' => getenv('SQS_DSN'), ], - ]]; + ], '+1sec']; yield 'mongodb_dsn' => [[ 'transport' => getenv('MONGO_DSN'), - ]]; + ], '+1sec']; } /** @@ -61,7 +61,7 @@ public function transportConfigDataProvider() * * @param mixed $config */ - public function testProduceAndConsumeOneMessage(array $config) + public function testSendEventWithOneSubscriber(array $config, string $timeLimit) { $actualMessage = null; @@ -86,8 +86,9 @@ public function testProduceAndConsumeOneMessage(array $config) $client->sendEvent('foo_topic', 'Hello there!'); + $client->getQueueConsumer()->setReceiveTimeout(200); $client->consume(new ChainExtension([ - new LimitConsumptionTimeExtension(new \DateTime('+30sec')), + new LimitConsumptionTimeExtension(new \DateTime($timeLimit)), new LimitConsumedMessagesExtension(2), ])); @@ -95,48 +96,86 @@ public function testProduceAndConsumeOneMessage(array $config) $this->assertSame('Hello there!', $actualMessage->getBody()); } -// /** -// * @dataProvider transportConfigDataProvider -// * -// * @param mixed $config -// */ -// public function testProduceAndRouteToTwoConsumes($config) -// { -// $received = 0; -// -// $config['client'] = [ -// 'prefix' => str_replace('.', '', uniqid('enqueue', true)), -// 'app_name' => 'simple_client', -// 'router_topic' => 'test', -// 'router_queue' => 'test', -// 'default_processor_queue' => 'test', -// ]; -// -// $client = new SimpleClient($config); -// -// $client->bindTopic('foo_topic', function () use (&$received) { -// ++$received; -// -// return Result::ACK; -// }); -// $client->bindTopic('foo_topic', function () use (&$received) { -// ++$received; -// -// return Result::ACK; -// }); -// -// $client->setupBroker(); -// $this->purgeQueue($client); -// -// $client->sendEvent('foo_topic', 'Hello there!'); -// -// $client->consume(new ChainExtension([ -// new LimitConsumptionTimeExtension(new \DateTime('+2sec')), -// new LimitConsumedMessagesExtension(3), -// ])); -// -// $this->assertSame(2, $received); -// } + /** + * @dataProvider transportConfigDataProvider + * + * @param mixed $config + */ + public function testSendEventWithTwoSubscriber(array $config, string $timeLimit) + { + $received = 0; + + $config['client'] = [ + 'prefix' => str_replace('.', '', uniqid('enqueue', true)), + 'app_name' => 'simple_client', + 'router_topic' => 'test', + 'router_queue' => 'test', + 'default_processor_queue' => 'test', + ]; + + $client = new SimpleClient($config); + + $client->bindTopic('foo_topic', function () use (&$received) { + ++$received; + + return Result::ACK; + }); + $client->bindTopic('foo_topic', function () use (&$received) { + ++$received; + + return Result::ACK; + }); + + $client->setupBroker(); + $this->purgeQueue($client); + + $client->sendEvent('foo_topic', 'Hello there!'); + $client->getQueueConsumer()->setReceiveTimeout(200); + $client->consume(new ChainExtension([ + new LimitConsumptionTimeExtension(new \DateTime($timeLimit)), + new LimitConsumedMessagesExtension(3), + ])); + + $this->assertSame(2, $received); + } + + /** + * @dataProvider transportConfigDataProvider + * + * @param mixed $config + */ + public function testSendCommand(array $config, string $timeLimit) + { + $received = 0; + + $config['client'] = [ + 'prefix' => str_replace('.', '', uniqid('enqueue', true)), + 'app_name' => 'simple_client', + 'router_topic' => 'test', + 'router_queue' => 'test', + 'default_processor_queue' => 'test', + ]; + + $client = new SimpleClient($config); + + $client->bindCommand('foo_command', function () use (&$received) { + ++$received; + + return Result::ACK; + }); + + $client->setupBroker(); + $this->purgeQueue($client); + + $client->sendCommand('foo_command', 'Hello there!'); + $client->getQueueConsumer()->setReceiveTimeout(200); + $client->consume(new ChainExtension([ + new LimitConsumptionTimeExtension(new \DateTime($timeLimit)), + new LimitConsumedMessagesExtension(1), + ])); + + $this->assertSame(1, $received); + } protected function purgeQueue(SimpleClient $client): void { From cffa1b6b7c390109ec0a62b95877372a035d996c Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Mon, 24 Sep 2018 11:16:14 +0300 Subject: [PATCH 26/31] [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. --- pkg/enqueue/Client/Driver/AmqpDriver.php | 31 ++++++++++--------- pkg/enqueue/Client/Driver/DbalDriver.php | 10 ------ pkg/enqueue/Client/Driver/FsDriver.php | 9 ------ pkg/enqueue/Client/Driver/GenericDriver.php | 9 +++--- pkg/enqueue/Client/Driver/GpsDriver.php | 12 ++++++- pkg/enqueue/Client/Driver/MongodbDriver.php | 10 ------ .../Client/Driver/RabbitMqStompDriver.php | 4 +-- pkg/enqueue/Client/Driver/RdKafkaDriver.php | 11 +++++++ pkg/enqueue/Client/Driver/RedisDriver.php | 9 ------ pkg/enqueue/Client/Driver/SqsDriver.php | 9 ------ pkg/enqueue/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/pkg/enqueue/Client/Driver/AmqpDriver.php b/pkg/enqueue/Client/Driver/AmqpDriver.php index 875cc80ed..0d77d1194 100644 --- a/pkg/enqueue/Client/Driver/AmqpDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/DbalDriver.php b/pkg/enqueue/Client/Driver/DbalDriver.php index 165bcf723..8b1f32655 100644 --- a/pkg/enqueue/Client/Driver/DbalDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/FsDriver.php b/pkg/enqueue/Client/Driver/FsDriver.php index 1ce7032f3..7931ea3a1 100644 --- a/pkg/enqueue/Client/Driver/FsDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/GenericDriver.php b/pkg/enqueue/Client/Driver/GenericDriver.php index 2cb8ca437..634eaf44e 100644 --- a/pkg/enqueue/Client/Driver/GenericDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/GpsDriver.php b/pkg/enqueue/Client/Driver/GpsDriver.php index 01dc943d6..572faacb5 100644 --- a/pkg/enqueue/Client/Driver/GpsDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/MongodbDriver.php b/pkg/enqueue/Client/Driver/MongodbDriver.php index ed31727c3..19f2c57d3 100644 --- a/pkg/enqueue/Client/Driver/MongodbDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/RabbitMqStompDriver.php b/pkg/enqueue/Client/Driver/RabbitMqStompDriver.php index 4dd72f635..ace520929 100644 --- a/pkg/enqueue/Client/Driver/RabbitMqStompDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/RdKafkaDriver.php b/pkg/enqueue/Client/Driver/RdKafkaDriver.php index 01b9e2716..f83677deb 100644 --- a/pkg/enqueue/Client/Driver/RdKafkaDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/RedisDriver.php b/pkg/enqueue/Client/Driver/RedisDriver.php index 264409e75..641d3bff3 100644 --- a/pkg/enqueue/Client/Driver/RedisDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/SqsDriver.php b/pkg/enqueue/Client/Driver/SqsDriver.php index db94ad148..622c6f7cf 100644 --- a/pkg/enqueue/Client/Driver/SqsDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/StompDriver.php b/pkg/enqueue/Client/Driver/StompDriver.php index 8ef90462f..95cc8b761 100644 --- a/pkg/enqueue/Client/Driver/StompDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/GenericDriverTest.php b/pkg/enqueue/Tests/Client/Driver/GenericDriverTest.php index ac4cd7a80..31f92a548 100644 --- a/pkg/enqueue/Tests/Client/Driver/GenericDriverTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php b/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php index 78d212255..5d5d4c823 100644 --- a/pkg/enqueue/Tests/Client/Driver/GenericDriverTestsTrait.php +++ b/pkg/enqueue/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 9904f7f70d924469cfbda6596219cde317f724a4 Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Thu, 27 Sep 2018 13:45:31 +0300 Subject: [PATCH 27/31] [job-queue] sync with latest design changes. --- .../CalculateRootJobStatusProcessor.php | 20 ++++--------------- pkg/job-queue/Commands.php | 8 ++++++++ pkg/job-queue/DependentJobProcessor.php | 5 +---- pkg/job-queue/JobProcessor.php | 2 +- .../CalculateRootJobStatusProcessorTest.php | 5 +++-- .../Tests/DependentJobProcessorTest.php | 2 +- pkg/job-queue/Tests/JobProcessorTest.php | 4 ++-- pkg/job-queue/Topics.php | 1 - 8 files changed, 20 insertions(+), 27 deletions(-) create mode 100644 pkg/job-queue/Commands.php diff --git a/pkg/job-queue/CalculateRootJobStatusProcessor.php b/pkg/job-queue/CalculateRootJobStatusProcessor.php index 8ccac2cae..3f467ef17 100644 --- a/pkg/job-queue/CalculateRootJobStatusProcessor.php +++ b/pkg/job-queue/CalculateRootJobStatusProcessor.php @@ -2,8 +2,8 @@ namespace Enqueue\JobQueue; +use Enqueue\Client\CommandSubscriberInterface; use Enqueue\Client\ProducerInterface; -use Enqueue\Client\TopicSubscriberInterface; use Enqueue\Consumption\Result; use Enqueue\JobQueue\Doctrine\JobStorage; use Enqueue\Util\JSON; @@ -12,7 +12,7 @@ use Interop\Queue\PsrProcessor; use Psr\Log\LoggerInterface; -class CalculateRootJobStatusProcessor implements PsrProcessor, TopicSubscriberInterface +class CalculateRootJobStatusProcessor implements PsrProcessor, CommandSubscriberInterface { /** * @var JobStorage @@ -34,12 +34,6 @@ class CalculateRootJobStatusProcessor implements PsrProcessor, TopicSubscriberIn */ private $logger; - /** - * @param JobStorage $jobStorage - * @param CalculateRootJobStatusService $calculateRootJobStatusCase - * @param ProducerInterface $producer - * @param LoggerInterface $logger - */ public function __construct( JobStorage $jobStorage, CalculateRootJobStatusService $calculateRootJobStatusCase, @@ -52,9 +46,6 @@ public function __construct( $this->logger = $logger; } - /** - * {@inheritdoc} - */ public function process(PsrMessage $message, PsrContext $context) { $data = JSON::decode($message->getBody()); @@ -83,11 +74,8 @@ public function process(PsrMessage $message, PsrContext $context) return Result::ACK; } - /** - * {@inheritdoc} - */ - public static function getSubscribedTopics() + public static function getSubscribedCommand() { - return [Topics::CALCULATE_ROOT_JOB_STATUS]; + return Commands::CALCULATE_ROOT_JOB_STATUS; } } diff --git a/pkg/job-queue/Commands.php b/pkg/job-queue/Commands.php new file mode 100644 index 000000000..57966c30c --- /dev/null +++ b/pkg/job-queue/Commands.php @@ -0,0 +1,8 @@ +producer->sendEvent(Topics::CALCULATE_ROOT_JOB_STATUS, [ + $this->producer->sendEvent(Commands::CALCULATE_ROOT_JOB_STATUS, [ 'jobId' => $job->getId(), ]); } diff --git a/pkg/job-queue/Tests/CalculateRootJobStatusProcessorTest.php b/pkg/job-queue/Tests/CalculateRootJobStatusProcessorTest.php index 2c86a01db..27ac92203 100644 --- a/pkg/job-queue/Tests/CalculateRootJobStatusProcessorTest.php +++ b/pkg/job-queue/Tests/CalculateRootJobStatusProcessorTest.php @@ -6,6 +6,7 @@ use Enqueue\Consumption\Result; use Enqueue\JobQueue\CalculateRootJobStatusProcessor; use Enqueue\JobQueue\CalculateRootJobStatusService; +use Enqueue\JobQueue\Commands; use Enqueue\JobQueue\Doctrine\JobStorage; use Enqueue\JobQueue\Job; use Enqueue\JobQueue\Topics; @@ -28,8 +29,8 @@ public function testCouldBeConstructedWithRequiredArguments() public function testShouldReturnSubscribedTopicNames() { $this->assertEquals( - [Topics::CALCULATE_ROOT_JOB_STATUS], - CalculateRootJobStatusProcessor::getSubscribedTopics() + Commands::CALCULATE_ROOT_JOB_STATUS, + CalculateRootJobStatusProcessor::getSubscribedCommand() ); } diff --git a/pkg/job-queue/Tests/DependentJobProcessorTest.php b/pkg/job-queue/Tests/DependentJobProcessorTest.php index a61918c19..8f1258fa3 100644 --- a/pkg/job-queue/Tests/DependentJobProcessorTest.php +++ b/pkg/job-queue/Tests/DependentJobProcessorTest.php @@ -18,7 +18,7 @@ class DependentJobProcessorTest extends \PHPUnit\Framework\TestCase public function testShouldReturnSubscribedTopicNames() { $this->assertEquals( - [Topics::ROOT_JOB_STOPPED], + Topics::ROOT_JOB_STOPPED, DependentJobProcessor::getSubscribedTopics() ); } diff --git a/pkg/job-queue/Tests/JobProcessorTest.php b/pkg/job-queue/Tests/JobProcessorTest.php index d9ce75e98..483e8ef2d 100644 --- a/pkg/job-queue/Tests/JobProcessorTest.php +++ b/pkg/job-queue/Tests/JobProcessorTest.php @@ -3,11 +3,11 @@ namespace Enqueue\JobQueue\Tests; use Enqueue\Client\ProducerInterface; +use Enqueue\JobQueue\Commands; use Enqueue\JobQueue\Doctrine\JobStorage; use Enqueue\JobQueue\DuplicateJobException; use Enqueue\JobQueue\Job; use Enqueue\JobQueue\JobProcessor; -use Enqueue\JobQueue\Topics; use PHPUnit\Framework\TestCase; class JobProcessorTest extends TestCase @@ -170,7 +170,7 @@ public function testCreateChildJobShouldCreateAndSaveJobAndPublishRecalculateRoo $producer ->expects($this->once()) ->method('sendEvent') - ->with(Topics::CALCULATE_ROOT_JOB_STATUS, ['jobId' => 12345]) + ->with(Commands::CALCULATE_ROOT_JOB_STATUS, ['jobId' => 12345]) ; $processor = new JobProcessor($storage, $producer); diff --git a/pkg/job-queue/Topics.php b/pkg/job-queue/Topics.php index 664fa0b1c..61b424b25 100644 --- a/pkg/job-queue/Topics.php +++ b/pkg/job-queue/Topics.php @@ -4,6 +4,5 @@ class Topics { - const CALCULATE_ROOT_JOB_STATUS = 'enqueue.message_queue.job.calculate_root_job_status'; const ROOT_JOB_STOPPED = 'enqueue.message_queue.job.root_job_stopped'; } From eca10ef3bc6c3e962e451858ce30d375c7637b47 Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Thu, 27 Sep 2018 13:56:38 +0300 Subject: [PATCH 28/31] [async-commands][async-events] Sync with latest design chagnes in core. --- pkg/async-command/Resources/config/services.yml | 3 +-- pkg/async-command/RunCommandProcessor.php | 6 +++--- .../Tests/RunCommandProcessorTest.php | 6 +++--- pkg/async-event-dispatcher/AsyncProcessor.php | 15 +++++++-------- pkg/async-event-dispatcher/Commands.php | 8 ++++++++ .../DependencyInjection/AsyncEventsPass.php | 13 ++++++------- .../Resources/config/services.yml | 9 ++++----- 7 files changed, 32 insertions(+), 28 deletions(-) create mode 100644 pkg/async-event-dispatcher/Commands.php diff --git a/pkg/async-command/Resources/config/services.yml b/pkg/async-command/Resources/config/services.yml index 9f6543f97..84ada4226 100644 --- a/pkg/async-command/Resources/config/services.yml +++ b/pkg/async-command/Resources/config/services.yml @@ -1,8 +1,7 @@ services: enqueue.async_command.run_command_processor: class: 'Enqueue\AsyncCommand\RunCommandProcessor' - public: public arguments: - '%kernel.project_dir%' tags: - - { name: 'enqueue.client.processor' } + - { name: 'enqueue.command_subscriber', client: 'default' } diff --git a/pkg/async-command/RunCommandProcessor.php b/pkg/async-command/RunCommandProcessor.php index bdb1dfcd3..3e34759e2 100644 --- a/pkg/async-command/RunCommandProcessor.php +++ b/pkg/async-command/RunCommandProcessor.php @@ -45,9 +45,9 @@ public function process(PsrMessage $message, PsrContext $context): Result public static function getSubscribedCommand(): array { return [ - 'processorName' => Commands::RUN_COMMAND, - 'queueName' => Commands::RUN_COMMAND, - 'queueNameHardcoded' => true, + 'command' => Commands::RUN_COMMAND, + 'queue' => Commands::RUN_COMMAND, + 'prefix_queue' => false, 'exclusive' => true, ]; } diff --git a/pkg/async-command/Tests/RunCommandProcessorTest.php b/pkg/async-command/Tests/RunCommandProcessorTest.php index 2db650611..c16069998 100644 --- a/pkg/async-command/Tests/RunCommandProcessorTest.php +++ b/pkg/async-command/Tests/RunCommandProcessorTest.php @@ -43,9 +43,9 @@ public function testShouldSubscribeOnRunCommand() $subscription = RunCommandProcessor::getSubscribedCommand(); $this->assertSame([ - 'processorName' => Commands::RUN_COMMAND, - 'queueName' => Commands::RUN_COMMAND, - 'queueNameHardcoded' => true, + 'command' => Commands::RUN_COMMAND, + 'queue' => Commands::RUN_COMMAND, + 'prefix_queue' => false, 'exclusive' => true, ], $subscription); } diff --git a/pkg/async-event-dispatcher/AsyncProcessor.php b/pkg/async-event-dispatcher/AsyncProcessor.php index a82a28205..000d3056d 100644 --- a/pkg/async-event-dispatcher/AsyncProcessor.php +++ b/pkg/async-event-dispatcher/AsyncProcessor.php @@ -2,13 +2,14 @@ namespace Enqueue\AsyncEventDispatcher; +use Enqueue\Client\CommandSubscriberInterface; use Enqueue\Consumption\Result; use Interop\Queue\PsrContext; use Interop\Queue\PsrMessage; use Interop\Queue\PsrProcessor; use Symfony\Component\EventDispatcher\EventDispatcherInterface; -class AsyncProcessor implements PsrProcessor +class AsyncProcessor implements PsrProcessor, CommandSubscriberInterface { /** * @var Registry @@ -20,10 +21,6 @@ class AsyncProcessor implements PsrProcessor */ private $dispatcher; - /** - * @param Registry $registry - * @param EventDispatcherInterface $dispatcher - */ public function __construct(Registry $registry, EventDispatcherInterface $dispatcher) { $this->registry = $registry; @@ -39,9 +36,6 @@ public function __construct(Registry $registry, EventDispatcherInterface $dispat $this->dispatcher = $dispatcher; } - /** - * {@inheritdoc} - */ public function process(PsrMessage $message, PsrContext $context) { if (false == $eventName = $message->getProperty('event_name')) { @@ -57,4 +51,9 @@ public function process(PsrMessage $message, PsrContext $context) return self::ACK; } + + public static function getSubscribedCommand() + { + return Commands::DISPATCH_ASYNC_EVENTS; + } } diff --git a/pkg/async-event-dispatcher/Commands.php b/pkg/async-event-dispatcher/Commands.php new file mode 100644 index 000000000..a00ed6fa9 --- /dev/null +++ b/pkg/async-event-dispatcher/Commands.php @@ -0,0 +1,8 @@ +hasDefinition('enqueue.events.async_listener')) { return; @@ -45,8 +42,9 @@ public function process(ContainerBuilder $container) ; $container->getDefinition('enqueue.events.async_processor') - ->addTag('enqueue.client.processor', [ - 'topicName' => 'event.'.$event, + ->addTag('enqueue.processor', [ + 'topic' => 'event.'.$event, + 'client' => 'default', ]) ; @@ -78,8 +76,9 @@ public function process(ContainerBuilder $container) ; $container->getDefinition('enqueue.events.async_processor') - ->addTag('enqueue.client.processor', [ + ->addTag('enqueue.processor', [ 'topicName' => 'event.'.$event, + 'client' => 'default', ]) ; diff --git a/pkg/async-event-dispatcher/Resources/config/services.yml b/pkg/async-event-dispatcher/Resources/config/services.yml index e2916058f..81ae3f305 100644 --- a/pkg/async-event-dispatcher/Resources/config/services.yml +++ b/pkg/async-event-dispatcher/Resources/config/services.yml @@ -33,11 +33,10 @@ services: - '@enqueue.events.event_dispatcher' tags: - - name: 'enqueue.client.processor' - topicName: '__command__' - processorName: '%enqueue_events_queue%' - queueName: '%enqueue_events_queue%' - queueNameHardcoded: true + name: 'enqueue.processor' + command: 'symfony.dispatch_async_events' + queue: '%enqueue_events_queue%' + queue_prefixed: false exclusive: true enqueue.events.php_serializer_event_transofrmer: From ed707fe527cf4906d63c7904bdcd2ba163540bb0 Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Thu, 27 Sep 2018 14:04:31 +0300 Subject: [PATCH 29/31] Update enqueue architecture. Get rid of meta (queue|topic), rely one routes. --- pkg/enqueue/Client/ArrayProcessorRegistry.php | 11 +- pkg/enqueue/Client/ChainExtension.php | 1 + .../Client/CommandSubscriberInterface.php | 29 +- pkg/enqueue/Client/Driver/GenericDriver.php | 4 +- pkg/enqueue/Client/DriverInterface.php | 2 +- pkg/enqueue/Client/Meta/QueueMeta.php | 57 ---- pkg/enqueue/Client/Meta/QueueMetaRegistry.php | 95 ------- pkg/enqueue/Client/Meta/TopicMeta.php | 57 ---- pkg/enqueue/Client/Meta/TopicMetaRegistry.php | 80 ------ .../Client/ProcessorRegistryInterface.php | 7 +- .../Client/TopicSubscriberInterface.php | 3 +- pkg/enqueue/Consumption/ChainExtension.php | 5 +- .../Symfony/Client/ConsumeMessagesCommand.php | 58 ++-- .../ContainerAwareProcessorRegistry.php | 61 ---- .../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 ----- pkg/enqueue/Symfony/Client/RoutesCommand.php | 131 +++++++++ .../DependencyInjection/ClientFactory.php | 48 ++++ .../DependencyInjection/TransportFactory.php | 32 +-- .../Tests/Client/ChainExtensionTest.php | 7 + .../Client/Meta/QueueMetaRegistryTest.php | 146 ---------- .../Tests/Client/Meta/QueueMetaTest.php | 39 --- .../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 ------ .../Symfony/Client/RoutesCommandTest.php | 262 ++++++++++++++++++ .../DependencyInjection/ClientFactoryTest.php | 61 ++++ .../TransportFactoryTest.php | 65 +---- 46 files changed, 1489 insertions(+), 1423 deletions(-) delete mode 100644 pkg/enqueue/Client/Meta/QueueMeta.php delete mode 100644 pkg/enqueue/Client/Meta/QueueMetaRegistry.php delete mode 100644 pkg/enqueue/Client/Meta/TopicMeta.php delete mode 100644 pkg/enqueue/Client/Meta/TopicMetaRegistry.php delete mode 100644 pkg/enqueue/Symfony/Client/ContainerAwareProcessorRegistry.php create mode 100644 pkg/enqueue/Symfony/Client/ContainerProcessorRegistry.php rename pkg/enqueue/Symfony/{ => Client}/DependencyInjection/AnalyzeRouteCollectionPass.php (98%) create mode 100644 pkg/enqueue/Symfony/Client/DependencyInjection/BuildClientExtensionsPass.php rename pkg/enqueue/Symfony/{ => Client}/DependencyInjection/BuildCommandSubscriberRoutesPass.php (96%) create mode 100644 pkg/enqueue/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php rename pkg/enqueue/Symfony/{ => Client}/DependencyInjection/BuildProcessorRegistryPass.php (69%) rename pkg/enqueue/Symfony/{ => Client}/DependencyInjection/BuildProcessorRoutesPass.php (87%) rename pkg/enqueue/Symfony/{ => Client}/DependencyInjection/BuildTopicSubscriberRoutesPass.php (98%) delete mode 100644 pkg/enqueue/Symfony/Client/Meta/QueuesCommand.php delete mode 100644 pkg/enqueue/Symfony/Client/Meta/TopicsCommand.php create mode 100644 pkg/enqueue/Symfony/Client/RoutesCommand.php create mode 100644 pkg/enqueue/Symfony/DependencyInjection/ClientFactory.php delete mode 100644 pkg/enqueue/Tests/Client/Meta/QueueMetaRegistryTest.php delete mode 100644 pkg/enqueue/Tests/Client/Meta/QueueMetaTest.php delete mode 100644 pkg/enqueue/Tests/Client/Meta/TopicMetaRegistryTest.php delete mode 100644 pkg/enqueue/Tests/Client/Meta/TopicMetaTest.php delete mode 100644 pkg/enqueue/Tests/Symfony/Client/ContainerAwareProcessorRegistryTest.php rename pkg/enqueue/Tests/Symfony/{ => Client}/DependencyInjection/AnalyzeRouteCollectionPassTest.php (97%) create mode 100644 pkg/enqueue/Tests/Symfony/Client/DependencyInjection/BuildClientExtensionsPassTest.php rename pkg/enqueue/Tests/Symfony/{ => Client}/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php (89%) create mode 100644 pkg/enqueue/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php rename pkg/enqueue/Tests/Symfony/{ => Client}/DependencyInjection/BuildProcessorRegistryPassTest.php (70%) rename pkg/enqueue/Tests/Symfony/{ => Client}/DependencyInjection/BuildProcessorRoutesPassTest.php (97%) rename pkg/enqueue/Tests/Symfony/{ => Client}/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php (98%) delete mode 100644 pkg/enqueue/Tests/Symfony/Client/Meta/QueuesCommandTest.php delete mode 100644 pkg/enqueue/Tests/Symfony/Client/Meta/TopicsCommandTest.php create mode 100644 pkg/enqueue/Tests/Symfony/Client/RoutesCommandTest.php create mode 100644 pkg/enqueue/Tests/Symfony/DependencyInjection/ClientFactoryTest.php diff --git a/pkg/enqueue/Client/ArrayProcessorRegistry.php b/pkg/enqueue/Client/ArrayProcessorRegistry.php index d6cb300d0..bbe1cfe84 100644 --- a/pkg/enqueue/Client/ArrayProcessorRegistry.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/ChainExtension.php b/pkg/enqueue/Client/ChainExtension.php index 603b2b633..ba0fc888d 100644 --- a/pkg/enqueue/Client/ChainExtension.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/CommandSubscriberInterface.php b/pkg/enqueue/Client/CommandSubscriberInterface.php index 99f6b274b..5bbe78efe 100644 --- a/pkg/enqueue/Client/CommandSubscriberInterface.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Driver/GenericDriver.php b/pkg/enqueue/Client/Driver/GenericDriver.php index 634eaf44e..e7b2e9343 100644 --- a/pkg/enqueue/Client/Driver/GenericDriver.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/DriverInterface.php b/pkg/enqueue/Client/DriverInterface.php index 96144a5d9..23d06613c 100644 --- a/pkg/enqueue/Client/DriverInterface.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/Meta/QueueMeta.php b/pkg/enqueue/Client/Meta/QueueMeta.php deleted file mode 100644 index bee32bd81..000000000 --- a/pkg/enqueue/Client/Meta/QueueMeta.php +++ /dev/null @@ -1,57 +0,0 @@ -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/pkg/enqueue/Client/Meta/QueueMetaRegistry.php b/pkg/enqueue/Client/Meta/QueueMetaRegistry.php deleted file mode 100644 index 29c2d69a3..000000000 --- a/pkg/enqueue/Client/Meta/QueueMetaRegistry.php +++ /dev/null @@ -1,95 +0,0 @@ - [ - * '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/pkg/enqueue/Client/Meta/TopicMeta.php b/pkg/enqueue/Client/Meta/TopicMeta.php deleted file mode 100644 index abb0e33ed..000000000 --- a/pkg/enqueue/Client/Meta/TopicMeta.php +++ /dev/null @@ -1,57 +0,0 @@ -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/pkg/enqueue/Client/Meta/TopicMetaRegistry.php b/pkg/enqueue/Client/Meta/TopicMetaRegistry.php deleted file mode 100644 index efceb9a11..000000000 --- a/pkg/enqueue/Client/Meta/TopicMetaRegistry.php +++ /dev/null @@ -1,80 +0,0 @@ - [ - * '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/pkg/enqueue/Client/ProcessorRegistryInterface.php b/pkg/enqueue/Client/ProcessorRegistryInterface.php index c83c1130d..f8894a9b8 100644 --- a/pkg/enqueue/Client/ProcessorRegistryInterface.php +++ b/pkg/enqueue/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/pkg/enqueue/Client/TopicSubscriberInterface.php b/pkg/enqueue/Client/TopicSubscriberInterface.php index 4723c670f..634a7ace2 100644 --- a/pkg/enqueue/Client/TopicSubscriberInterface.php +++ b/pkg/enqueue/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/pkg/enqueue/Consumption/ChainExtension.php b/pkg/enqueue/Consumption/ChainExtension.php index d7a24adc7..ae71f247c 100644 --- a/pkg/enqueue/Consumption/ChainExtension.php +++ b/pkg/enqueue/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/pkg/enqueue/Symfony/Client/ConsumeMessagesCommand.php b/pkg/enqueue/Symfony/Client/ConsumeMessagesCommand.php index 6d4987bb6..f38fc86cb 100644 --- a/pkg/enqueue/Symfony/Client/ConsumeMessagesCommand.php +++ b/pkg/enqueue/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/pkg/enqueue/Symfony/Client/ContainerAwareProcessorRegistry.php b/pkg/enqueue/Symfony/Client/ContainerAwareProcessorRegistry.php deleted file mode 100644 index 2b42cec18..000000000 --- a/pkg/enqueue/Symfony/Client/ContainerAwareProcessorRegistry.php +++ /dev/null @@ -1,61 +0,0 @@ -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/pkg/enqueue/Symfony/Client/ContainerProcessorRegistry.php b/pkg/enqueue/Symfony/Client/ContainerProcessorRegistry.php new file mode 100644 index 000000000..c2d06c8c1 --- /dev/null +++ b/pkg/enqueue/Symfony/Client/ContainerProcessorRegistry.php @@ -0,0 +1,29 @@ +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/pkg/enqueue/Symfony/DependencyInjection/AnalyzeRouteCollectionPass.php b/pkg/enqueue/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php similarity index 98% rename from pkg/enqueue/Symfony/DependencyInjection/AnalyzeRouteCollectionPass.php rename to pkg/enqueue/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php index b5bd11f48..c8b140cfd 100644 --- a/pkg/enqueue/Symfony/DependencyInjection/AnalyzeRouteCollectionPass.php +++ b/pkg/enqueue/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPass.php @@ -1,6 +1,6 @@ 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/pkg/enqueue/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPass.php b/pkg/enqueue/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php similarity index 96% rename from pkg/enqueue/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPass.php rename to pkg/enqueue/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php index e9e16a3a8..3e8791a47 100644 --- a/pkg/enqueue/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPass.php +++ b/pkg/enqueue/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPass.php @@ -1,6 +1,6 @@ $params) { if (is_string($params)) { $routeCollection->add(new Route($params, Route::COMMAND, $serviceId, ['processor_service_id' => $serviceId])); diff --git a/pkg/enqueue/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php b/pkg/enqueue/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php new file mode 100644 index 000000000..8482225f2 --- /dev/null +++ b/pkg/enqueue/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPass.php @@ -0,0 +1,65 @@ +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/pkg/enqueue/Symfony/DependencyInjection/BuildProcessorRegistryPass.php b/pkg/enqueue/Symfony/Client/DependencyInjection/BuildProcessorRegistryPass.php similarity index 69% rename from pkg/enqueue/Symfony/DependencyInjection/BuildProcessorRegistryPass.php rename to pkg/enqueue/Symfony/Client/DependencyInjection/BuildProcessorRegistryPass.php index 4620cef7c..d69935d64 100644 --- a/pkg/enqueue/Symfony/DependencyInjection/BuildProcessorRegistryPass.php +++ b/pkg/enqueue/Symfony/Client/DependencyInjection/BuildProcessorRegistryPass.php @@ -1,10 +1,12 @@ 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/pkg/enqueue/Symfony/DependencyInjection/BuildProcessorRoutesPass.php b/pkg/enqueue/Symfony/Client/DependencyInjection/BuildProcessorRoutesPass.php similarity index 87% rename from pkg/enqueue/Symfony/DependencyInjection/BuildProcessorRoutesPass.php rename to pkg/enqueue/Symfony/Client/DependencyInjection/BuildProcessorRoutesPass.php index 7a6853b52..415b87a92 100644 --- a/pkg/enqueue/Symfony/DependencyInjection/BuildProcessorRoutesPass.php +++ b/pkg/enqueue/Symfony/Client/DependencyInjection/BuildProcessorRoutesPass.php @@ -1,6 +1,6 @@ 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/pkg/enqueue/Symfony/Client/Meta/TopicsCommand.php b/pkg/enqueue/Symfony/Client/Meta/TopicsCommand.php deleted file mode 100644 index 63b28bc14..000000000 --- a/pkg/enqueue/Symfony/Client/Meta/TopicsCommand.php +++ /dev/null @@ -1,70 +0,0 @@ -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/pkg/enqueue/Symfony/Client/RoutesCommand.php b/pkg/enqueue/Symfony/Client/RoutesCommand.php new file mode 100644 index 000000000..e4abc20a3 --- /dev/null +++ b/pkg/enqueue/Symfony/Client/RoutesCommand.php @@ -0,0 +1,131 @@ +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/pkg/enqueue/Symfony/DependencyInjection/ClientFactory.php b/pkg/enqueue/Symfony/DependencyInjection/ClientFactory.php new file mode 100644 index 000000000..a2c09afe2 --- /dev/null +++ b/pkg/enqueue/Symfony/DependencyInjection/ClientFactory.php @@ -0,0 +1,48 @@ +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/pkg/enqueue/Symfony/DependencyInjection/TransportFactory.php b/pkg/enqueue/Symfony/DependencyInjection/TransportFactory.php index 4f1eab760..f54a34fba 100644 --- a/pkg/enqueue/Symfony/DependencyInjection/TransportFactory.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/ChainExtensionTest.php b/pkg/enqueue/Tests/Client/ChainExtensionTest.php index 9268c7b94..a48da5f81 100644 --- a/pkg/enqueue/Tests/Client/ChainExtensionTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Client/Meta/QueueMetaRegistryTest.php b/pkg/enqueue/Tests/Client/Meta/QueueMetaRegistryTest.php deleted file mode 100644 index 787290fe6..000000000 --- a/pkg/enqueue/Tests/Client/Meta/QueueMetaRegistryTest.php +++ /dev/null @@ -1,146 +0,0 @@ - [], - '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/pkg/enqueue/Tests/Client/Meta/QueueMetaTest.php b/pkg/enqueue/Tests/Client/Meta/QueueMetaTest.php deleted file mode 100644 index fde6dc52f..000000000 --- a/pkg/enqueue/Tests/Client/Meta/QueueMetaTest.php +++ /dev/null @@ -1,39 +0,0 @@ -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/pkg/enqueue/Tests/Client/Meta/TopicMetaRegistryTest.php b/pkg/enqueue/Tests/Client/Meta/TopicMetaRegistryTest.php deleted file mode 100644 index ce074b6b1..000000000 --- a/pkg/enqueue/Tests/Client/Meta/TopicMetaRegistryTest.php +++ /dev/null @@ -1,124 +0,0 @@ - [], - '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/pkg/enqueue/Tests/Client/Meta/TopicMetaTest.php b/pkg/enqueue/Tests/Client/Meta/TopicMetaTest.php deleted file mode 100644 index 565a8f821..000000000 --- a/pkg/enqueue/Tests/Client/Meta/TopicMetaTest.php +++ /dev/null @@ -1,57 +0,0 @@ -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/pkg/enqueue/Tests/Symfony/Client/ConsumeMessagesCommandTest.php b/pkg/enqueue/Tests/Symfony/Client/ConsumeMessagesCommandTest.php index d2a08df69..69af29fdf 100644 --- a/pkg/enqueue/Tests/Symfony/Client/ConsumeMessagesCommandTest.php +++ b/pkg/enqueue/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/pkg/enqueue/Tests/Symfony/Client/ContainerAwareProcessorRegistryTest.php b/pkg/enqueue/Tests/Symfony/Client/ContainerAwareProcessorRegistryTest.php deleted file mode 100644 index 1d641d429..000000000 --- a/pkg/enqueue/Tests/Symfony/Client/ContainerAwareProcessorRegistryTest.php +++ /dev/null @@ -1,84 +0,0 @@ -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/pkg/enqueue/Tests/Symfony/DependencyInjection/AnalyzeRouteCollectionPassTest.php b/pkg/enqueue/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php similarity index 97% rename from pkg/enqueue/Tests/Symfony/DependencyInjection/AnalyzeRouteCollectionPassTest.php rename to pkg/enqueue/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php index be844d84f..c2f7386af 100644 --- a/pkg/enqueue/Tests/Symfony/DependencyInjection/AnalyzeRouteCollectionPassTest.php +++ b/pkg/enqueue/Tests/Symfony/Client/DependencyInjection/AnalyzeRouteCollectionPassTest.php @@ -1,9 +1,9 @@ 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/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php b/pkg/enqueue/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php similarity index 89% rename from pkg/enqueue/Tests/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php rename to pkg/enqueue/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php index 3e4d69a71..a93efe3d0 100644 --- a/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php +++ b/pkg/enqueue/Tests/Symfony/Client/DependencyInjection/BuildCommandSubscriberRoutesPassTest.php @@ -1,11 +1,11 @@ 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/pkg/enqueue/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php b/pkg/enqueue/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php new file mode 100644 index 000000000..68ad2ab3e --- /dev/null +++ b/pkg/enqueue/Tests/Symfony/Client/DependencyInjection/BuildConsumptionExtensionsPassTest.php @@ -0,0 +1,240 @@ +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/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php b/pkg/enqueue/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php similarity index 70% rename from pkg/enqueue/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php rename to pkg/enqueue/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php index 70ad5dfeb..59796521f 100644 --- a/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildProcessorRegistryPassTest.php +++ b/pkg/enqueue/Tests/Symfony/Client/DependencyInjection/BuildProcessorRegistryPassTest.php @@ -1,14 +1,15 @@ 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/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildProcessorRoutesPassTest.php b/pkg/enqueue/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php similarity index 97% rename from pkg/enqueue/Tests/Symfony/DependencyInjection/BuildProcessorRoutesPassTest.php rename to pkg/enqueue/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php index 517f98bbc..b0d7af5eb 100644 --- a/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildProcessorRoutesPassTest.php +++ b/pkg/enqueue/Tests/Symfony/Client/DependencyInjection/BuildProcessorRoutesPassTest.php @@ -1,10 +1,10 @@ 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/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php b/pkg/enqueue/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php similarity index 98% rename from pkg/enqueue/Tests/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php rename to pkg/enqueue/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php index af3b0b178..0ad70a21a 100644 --- a/pkg/enqueue/Tests/Symfony/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php +++ b/pkg/enqueue/Tests/Symfony/Client/DependencyInjection/BuildTopicSubscriberRoutesPassTest.php @@ -1,11 +1,11 @@ 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/pkg/enqueue/Tests/Symfony/Client/Meta/TopicsCommandTest.php b/pkg/enqueue/Tests/Symfony/Client/Meta/TopicsCommandTest.php deleted file mode 100644 index 4efdd2f66..000000000 --- a/pkg/enqueue/Tests/Symfony/Client/Meta/TopicsCommandTest.php +++ /dev/null @@ -1,91 +0,0 @@ -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/pkg/enqueue/Tests/Symfony/Client/RoutesCommandTest.php b/pkg/enqueue/Tests/Symfony/Client/RoutesCommandTest.php new file mode 100644 index 000000000..6100b64e2 --- /dev/null +++ b/pkg/enqueue/Tests/Symfony/Client/RoutesCommandTest.php @@ -0,0 +1,262 @@ +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 = <<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/pkg/enqueue/Tests/Symfony/DependencyInjection/ClientFactoryTest.php b/pkg/enqueue/Tests/Symfony/DependencyInjection/ClientFactoryTest.php new file mode 100644 index 000000000..bb4cfe9a4 --- /dev/null +++ b/pkg/enqueue/Tests/Symfony/DependencyInjection/ClientFactoryTest.php @@ -0,0 +1,61 @@ +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/pkg/enqueue/Tests/Symfony/DependencyInjection/TransportFactoryTest.php b/pkg/enqueue/Tests/Symfony/DependencyInjection/TransportFactoryTest.php index f53775194..8c2cc3240 100644 --- a/pkg/enqueue/Tests/Symfony/DependencyInjection/TransportFactoryTest.php +++ b/pkg/enqueue/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 7d50b6b5017a19eabfef7dc29628e079e9d126de Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Thu, 27 Sep 2018 14:11:45 +0300 Subject: [PATCH 30/31] [bundle] Sync Symfony bundle with arch changes. --- .../Compiler/AddTopicMetaPass.php | 66 ---- .../Compiler/BuildClientExtensionsPass.php | 40 -- .../BuildConsumptionExtensionsPass.php | 36 -- .../Compiler/BuildProcessorRegistryPass.php | 36 -- .../Compiler/BuildQueueMetaRegistryPass.php | 41 -- .../BuildTopicMetaSubscribersPass.php | 40 -- .../ExtractProcessorTagSubscriptionsTrait.php | 156 -------- .../DependencyInjection/EnqueueExtension.php | 82 +--- pkg/enqueue-bundle/EnqueueBundle.php | 31 +- .../Resources/config/client.yml | 229 ++++------- .../delay_redelivered_message_extension.yml | 6 +- .../doctrine_clear_identity_map_extension.yml | 2 +- .../doctrine_ping_connection_extension.yml | 2 +- .../exclusive_command_extension.yml | 7 +- .../flush_spool_producer_extension.yml | 6 +- .../config/extensions/reply_extension.yml | 2 +- .../config/extensions/signal_extension.yml | 2 +- pkg/enqueue-bundle/Resources/config/job.yml | 10 +- .../Resources/config/services.yml | 9 +- .../Tests/Functional/App/AsyncListener.php | 3 +- .../App/TestCommandSubscriberProcessor.php | 2 +- ...estExclusiveCommandSubscriberProcessor.php | 7 +- .../App/TestTopicSubscriberProcessor.php | 28 ++ .../Tests/Functional/App/config/config.yml | 40 +- .../Functional/App/config/custom-config.yml | 16 +- .../Client/ConsumeMessagesCommandTest.php | 19 - .../Tests/Functional/Client/DriverTest.php | 19 - .../Client/ProduceMessageCommandTest.php | 19 - .../Tests/Functional/Client/ProducerTest.php | 60 +-- .../Functional/Client/SpoolProducerTest.php | 13 +- .../Commands/RunCommandProcessorTest.php | 20 - .../Functional/ConsumeMessagesCommandTest.php | 18 - .../Tests/Functional/ContextTest.php | 2 +- .../Functional/Events/AsyncListenerTest.php | 15 +- .../Functional/Events/AsyncSubscriberTest.php | 15 +- .../Tests/Functional/QueueRegistryTest.php | 18 - .../Tests/Functional/QueuesCommandTest.php | 46 --- .../Tests/Functional/RoutesCommandTest.php | 51 +++ .../Tests/Functional/TopicRegistryTest.php | 18 - .../Tests/Functional/TopicsCommandTest.php | 47 --- .../Tests/Functional/UseCasesTest.php | 83 ++-- .../Tests/Functional/WebTestCase.php | 2 +- .../Compiler/AddTopicMetaPassTest.php | 76 ---- .../BuildClientExtensionsPassTest.php | 129 ------- .../BuildConsumptionExtensionsPassTest.php | 111 ------ .../BuildProcessorRegistryPassTest.php | 293 -------------- .../BuildQueueMetaRegistryPassTest.php | 252 ------------ .../BuildTopicMetaSubscribersPassTest.php | 364 ------------------ .../Compiler/Mock/EmptyCommandSubscriber.php | 13 - ...ButQueueNameHardCodedCommandSubscriber.php | 18 - .../Mock/ExclusiveCommandSubscriber.php | 18 - .../Mock/InvalidCommandSubscriber.php | 13 - .../Compiler/Mock/InvalidTopicSubscriber.php | 13 - .../Mock/OnlyCommandNameSubscriber.php | 13 - .../Mock/OnlyTopicNameTopicSubscriber.php | 13 - .../Mock/ProcessorNameCommandSubscriber.php | 16 - .../Mock/ProcessorNameTopicSubscriber.php | 17 - .../Mock/QueueNameTopicSubscriber.php | 17 - .../WithoutProcessorNameTopicSubscriber.php | 18 - .../DependencyInjection/ConfigurationTest.php | 4 +- .../EnqueueExtensionTest.php | 31 +- .../Profiler/MessageQueueCollectorTest.php | 2 +- 62 files changed, 348 insertions(+), 2447 deletions(-) delete mode 100644 pkg/enqueue-bundle/DependencyInjection/Compiler/AddTopicMetaPass.php delete mode 100644 pkg/enqueue-bundle/DependencyInjection/Compiler/BuildClientExtensionsPass.php delete mode 100644 pkg/enqueue-bundle/DependencyInjection/Compiler/BuildConsumptionExtensionsPass.php delete mode 100644 pkg/enqueue-bundle/DependencyInjection/Compiler/BuildProcessorRegistryPass.php delete mode 100644 pkg/enqueue-bundle/DependencyInjection/Compiler/BuildQueueMetaRegistryPass.php delete mode 100644 pkg/enqueue-bundle/DependencyInjection/Compiler/BuildTopicMetaSubscribersPass.php delete mode 100644 pkg/enqueue-bundle/DependencyInjection/Compiler/ExtractProcessorTagSubscriptionsTrait.php create mode 100644 pkg/enqueue-bundle/Tests/Functional/App/TestTopicSubscriberProcessor.php delete mode 100644 pkg/enqueue-bundle/Tests/Functional/Client/ConsumeMessagesCommandTest.php delete mode 100644 pkg/enqueue-bundle/Tests/Functional/Client/DriverTest.php delete mode 100644 pkg/enqueue-bundle/Tests/Functional/Client/ProduceMessageCommandTest.php delete mode 100644 pkg/enqueue-bundle/Tests/Functional/Commands/RunCommandProcessorTest.php delete mode 100644 pkg/enqueue-bundle/Tests/Functional/ConsumeMessagesCommandTest.php delete mode 100644 pkg/enqueue-bundle/Tests/Functional/QueueRegistryTest.php delete mode 100644 pkg/enqueue-bundle/Tests/Functional/QueuesCommandTest.php create mode 100644 pkg/enqueue-bundle/Tests/Functional/RoutesCommandTest.php delete mode 100644 pkg/enqueue-bundle/Tests/Functional/TopicRegistryTest.php delete mode 100644 pkg/enqueue-bundle/Tests/Functional/TopicsCommandTest.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/AddTopicMetaPassTest.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildClientExtensionsPassTest.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildConsumptionExtensionsPassTest.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildProcessorRegistryPassTest.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildQueueMetaRegistryPassTest.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildTopicMetaSubscribersPassTest.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/EmptyCommandSubscriber.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/ExclusiveButQueueNameHardCodedCommandSubscriber.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/ExclusiveCommandSubscriber.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/InvalidCommandSubscriber.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/InvalidTopicSubscriber.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/OnlyCommandNameSubscriber.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/OnlyTopicNameTopicSubscriber.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/ProcessorNameCommandSubscriber.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/ProcessorNameTopicSubscriber.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/QueueNameTopicSubscriber.php delete mode 100644 pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/WithoutProcessorNameTopicSubscriber.php diff --git a/pkg/enqueue-bundle/DependencyInjection/Compiler/AddTopicMetaPass.php b/pkg/enqueue-bundle/DependencyInjection/Compiler/AddTopicMetaPass.php deleted file mode 100644 index 45becabef..000000000 --- a/pkg/enqueue-bundle/DependencyInjection/Compiler/AddTopicMetaPass.php +++ /dev/null @@ -1,66 +0,0 @@ -topicsMeta = []; - } - - /** - * @param string $topicName - * @param string $topicDescription - * @param array $topicSubscribers - * - * @return $this - */ - public function add($topicName, $topicDescription = '', array $topicSubscribers = []) - { - $this->topicsMeta[$topicName] = []; - - if ($topicDescription) { - $this->topicsMeta[$topicName]['description'] = $topicDescription; - } - - if ($topicSubscribers) { - $this->topicsMeta[$topicName]['processors'] = $topicSubscribers; - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function process(ContainerBuilder $container) - { - $metaRegistryId = TopicMetaRegistry::class; - - if (false == $container->hasDefinition($metaRegistryId)) { - return; - } - - $metaRegistry = $container->getDefinition($metaRegistryId); - - $metaRegistry->replaceArgument(0, array_merge_recursive($metaRegistry->getArgument(0), $this->topicsMeta)); - } - - /** - * @return static - */ - public static function create() - { - return new static(); - } -} diff --git a/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildClientExtensionsPass.php b/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildClientExtensionsPass.php deleted file mode 100644 index 5f83e83e2..000000000 --- a/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildClientExtensionsPass.php +++ /dev/null @@ -1,40 +0,0 @@ -hasDefinition('enqueue.client.extensions')) { - return; - } - - $tags = $container->findTaggedServiceIds('enqueue.client.extension'); - - $groupByPriority = []; - foreach ($tags as $serviceId => $tagAttributes) { - foreach ($tagAttributes as $tagAttribute) { - $priority = isset($tagAttribute['priority']) ? (int) $tagAttribute['priority'] : 0; - - $groupByPriority[$priority][] = new Reference($serviceId); - } - } - - krsort($groupByPriority, SORT_NUMERIC); - - $flatExtensions = []; - foreach ($groupByPriority as $extension) { - $flatExtensions = array_merge($flatExtensions, $extension); - } - - $container->getDefinition('enqueue.client.extensions')->replaceArgument(0, $flatExtensions); - } -} diff --git a/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildConsumptionExtensionsPass.php b/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildConsumptionExtensionsPass.php deleted file mode 100644 index 20f2a3817..000000000 --- a/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildConsumptionExtensionsPass.php +++ /dev/null @@ -1,36 +0,0 @@ -findTaggedServiceIds('enqueue.consumption.extension'); - - $groupByPriority = []; - foreach ($tags as $serviceId => $tagAttributes) { - foreach ($tagAttributes as $tagAttribute) { - $priority = isset($tagAttribute['priority']) ? (int) $tagAttribute['priority'] : 0; - - $groupByPriority[$priority][] = new Reference($serviceId); - } - } - - krsort($groupByPriority, SORT_NUMERIC); - - $flatExtensions = []; - foreach ($groupByPriority as $extension) { - $flatExtensions = array_merge($flatExtensions, $extension); - } - - $container->getDefinition('enqueue.consumption.extensions')->replaceArgument(0, $flatExtensions); - } -} diff --git a/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildProcessorRegistryPass.php b/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildProcessorRegistryPass.php deleted file mode 100644 index ffff5b4ed..000000000 --- a/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildProcessorRegistryPass.php +++ /dev/null @@ -1,36 +0,0 @@ -hasDefinition($processorRegistryId)) { - return; - } - - $processorIds = []; - foreach ($container->findTaggedServiceIds($processorTagName) as $serviceId => $tagAttributes) { - $subscriptions = $this->extractSubscriptions($container, $serviceId, $tagAttributes); - - foreach ($subscriptions as $subscription) { - $processorIds[$subscription['processorName']] = $serviceId; - } - } - - $processorRegistryDef = $container->getDefinition($processorRegistryId); - $processorRegistryDef->setArguments([$processorIds]); - } -} diff --git a/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildQueueMetaRegistryPass.php b/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildQueueMetaRegistryPass.php deleted file mode 100644 index 5e82f324d..000000000 --- a/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildQueueMetaRegistryPass.php +++ /dev/null @@ -1,41 +0,0 @@ -hasDefinition($queueMetaRegistryId)) { - return; - } - - $queueMetaRegistry = $container->getDefinition($queueMetaRegistryId); - - $configs = []; - foreach ($container->findTaggedServiceIds($processorTagName) as $serviceId => $tagAttributes) { - $subscriptions = $this->extractSubscriptions($container, $serviceId, $tagAttributes); - - foreach ($subscriptions as $subscription) { - $configs[$subscription['queueName']]['processors'][] = $subscription['processorName']; - - if ($subscription['queueNameHardcoded']) { - $configs[$subscription['queueName']]['transportName'] = $subscription['queueName']; - } - } - } - - $queueMetaRegistry->replaceArgument(1, $configs); - } -} diff --git a/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildTopicMetaSubscribersPass.php b/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildTopicMetaSubscribersPass.php deleted file mode 100644 index 2f5195fa6..000000000 --- a/pkg/enqueue-bundle/DependencyInjection/Compiler/BuildTopicMetaSubscribersPass.php +++ /dev/null @@ -1,40 +0,0 @@ -hasDefinition(TopicMetaRegistry::class)) { - return; - } - - $topicsSubscribers = []; - foreach ($container->findTaggedServiceIds($processorTagName) as $serviceId => $tagAttributes) { - $subscriptions = $this->extractSubscriptions($container, $serviceId, $tagAttributes); - - foreach ($subscriptions as $subscription) { - $topicsSubscribers[$subscription['topicName']][] = $subscription['processorName']; - } - } - - $addTopicMetaPass = AddTopicMetaPass::create(); - foreach ($topicsSubscribers as $topicName => $subscribers) { - $addTopicMetaPass->add($topicName, '', $subscribers); - } - - $addTopicMetaPass->process($container); - } -} diff --git a/pkg/enqueue-bundle/DependencyInjection/Compiler/ExtractProcessorTagSubscriptionsTrait.php b/pkg/enqueue-bundle/DependencyInjection/Compiler/ExtractProcessorTagSubscriptionsTrait.php deleted file mode 100644 index dbb737044..000000000 --- a/pkg/enqueue-bundle/DependencyInjection/Compiler/ExtractProcessorTagSubscriptionsTrait.php +++ /dev/null @@ -1,156 +0,0 @@ -getParameter(trim($value, '%')); - } catch (ParameterNotFoundException $e) { - return $value; - } - }; - - $processorDefinition = $container->getDefinition($processorServiceId); - $processorClass = $processorDefinition->getClass(); - if (false == $processorDefinition->getFactory() && false == class_exists($processorClass)) { - throw new \LogicException(sprintf('The processor class "%s" could not be found.', $processorClass)); - } - - $defaultQueueName = $resolve($container->getParameter('enqueue.client.default_queue_name')); - - $data = []; - if ($processorClass && is_subclass_of($processorClass, CommandSubscriberInterface::class)) { - /** @var CommandSubscriberInterface $processorClass */ - $params = $processorClass::getSubscribedCommand(); - if (is_string($params)) { - if (empty($params)) { - throw new \LogicException('The command name must not be empty.'); - } - - $data[] = [ - 'commandName' => $params, - 'queueName' => $defaultQueueName, - 'queueNameHardcoded' => false, - 'processorName' => $processorServiceId, - 'exclusive' => false, - ]; - } elseif (is_array($params)) { - if (empty($params['commandName'])) { - throw new \LogicException('The commandName must be set.'); - } - - if (false == $commandName = $resolve($params['commandName'])) { - throw new \LogicException('The commandName must not be empty.'); - } - - $data[] = [ - 'commandName' => $commandName, - 'queueName' => $resolve($params['queueName'] ?? $defaultQueueName), - 'queueNameHardcoded' => $params['queueNameHardcoded'] ?? false, - 'processorName' => $params['processorName'] ?? $commandName, - 'exclusive' => $params['exclusive'] ?? false, - ]; - } else { - throw new \LogicException(sprintf( - 'Command subscriber configuration is invalid. "%s"', - json_encode($processorClass::getSubscribedCommand()) - )); - } - } - - if ($processorClass && is_subclass_of($processorClass, TopicSubscriberInterface::class)) { - /** @var TopicSubscriberInterface $processorClass */ - $topics = $processorClass::getSubscribedTopics(); - if (!is_array($topics)) { - throw new \LogicException(sprintf( - 'Topic subscriber configuration is invalid for "%s::getSubscribedTopics()": expected array, got %s.', - $processorClass, - gettype($topics) - )); - } - - foreach ($topics as $topicName => $params) { - if (is_string($params)) { - $data[] = [ - 'topicName' => $params, - 'queueName' => $defaultQueueName, - 'queueNameHardcoded' => false, - 'processorName' => $processorServiceId, - 'exclusive' => false, - ]; - } elseif (is_array($params)) { - $data[] = [ - 'topicName' => $topicName, - 'queueName' => $resolve($params['queueName'] ?? $defaultQueueName), - 'queueNameHardcoded' => $params['queueNameHardcoded'] ?? false, - 'processorName' => $resolve($params['processorName']) ?: $processorServiceId, - 'exclusive' => false, - ]; - } else { - throw new \LogicException(sprintf( - 'Topic subscriber configuration is invalid for "%s::getSubscribedTopics()". "%s"', - $processorClass, - json_encode($processorClass::getSubscribedTopics()) - )); - } - } - } - - if (false == ( - $processorClass || - is_subclass_of($processorClass, CommandSubscriberInterface::class) || - is_subclass_of($processorClass, TopicSubscriberInterface::class) - )) { - foreach ($tagAttributes as $tagAttribute) { - if (empty($tagAttribute['commandName']) && empty($tagAttribute['topicName'])) { - throw new \LogicException('Either commandName or topicName attribute must be set'); - } - - $topicName = $tagAttribute['topicName'] ?? null; - $commandName = $tagAttribute['commandName'] ?? null; - - if ($topicName) { - $data[] = [ - 'topicName' => $resolve($tagAttribute['topicName']), - 'queueName' => $resolve($tagAttribute['queueName'] ?? $defaultQueueName), - 'queueNameHardcoded' => $tagAttribute['queueNameHardcoded'], - 'processorName' => $resolve($tagAttribute['processorName'] ?? $processorServiceId), - 'exclusive' => false, - ]; - } - - if ($commandName) { - $data[] = [ - 'commandName' => $resolve($tagAttribute['commandName']), - 'queueName' => $resolve($tagAttribute['queueName'] ?? $defaultQueueName), - 'queueNameHardcoded' => $tagAttribute['queueNameHardcoded'], - 'processorName' => $resolve($tagAttribute['processorName'] ?? $processorServiceId), - 'exclusive' => $tagAttribute['exclusive'] ?? false, - ]; - } - } - } - - return $data; - } -} diff --git a/pkg/enqueue-bundle/DependencyInjection/EnqueueExtension.php b/pkg/enqueue-bundle/DependencyInjection/EnqueueExtension.php index 9305ef006..ab9dd70ce 100644 --- a/pkg/enqueue-bundle/DependencyInjection/EnqueueExtension.php +++ b/pkg/enqueue-bundle/DependencyInjection/EnqueueExtension.php @@ -5,15 +5,12 @@ use Enqueue\AsyncCommand\DependencyInjection\AsyncCommandExtension; use Enqueue\AsyncEventDispatcher\DependencyInjection\AsyncEventDispatcherExtension; use Enqueue\Client\CommandSubscriberInterface; -use Enqueue\Client\DriverInterface; -use Enqueue\Client\Producer; use Enqueue\Client\TopicSubscriberInterface; use Enqueue\Client\TraceableProducer; use Enqueue\Consumption\QueueConsumer; use Enqueue\JobQueue\Job; +use Enqueue\Symfony\DependencyInjection\ClientFactory; use Enqueue\Symfony\DependencyInjection\TransportFactory; -use Interop\Queue\PsrConnectionFactory; -use Interop\Queue\PsrContext; use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\DependencyInjection\Alias; @@ -43,9 +40,10 @@ public function load(array $configs, ContainerBuilder $container): void $loader->load('extensions/flush_spool_producer_extension.yml'); $loader->load('extensions/exclusive_command_extension.yml'); - $transportFactory->createDriver($container, $config['transport']); + $clientFactory = new ClientFactory('default'); + $clientFactory->createDriver($container, $config['transport']); - $configDef = $container->getDefinition('enqueue.client.config'); + $configDef = $container->getDefinition('enqueue.client.default.config'); $configDef->setArguments([ $config['client']['prefix'], $config['client']['app_name'], @@ -57,34 +55,38 @@ public function load(array $configs, ContainerBuilder $container): void $config['transport'], ]); + $container->setParameter('enqueue.client.default.router_processor', $config['client']['router_processor']); $container->setParameter('enqueue.client.router_queue_name', $config['client']['router_queue']); $container->setParameter('enqueue.client.default_queue_name', $config['client']['default_processor_queue']); if ($config['client']['traceable_producer']) { - $container->register(TraceableProducer::class, TraceableProducer::class) - ->setDecoratedService(Producer::class) + $container->register('enqueue.client.default.traceable_producer', TraceableProducer::class) + ->setDecoratedService('enqueue.client.default.producer') ->setPublic(true) - ->addArgument(new Reference(sprintf('%s.inner', TraceableProducer::class))) + ->addArgument(new Reference('enqueue.client.default.traceable_producer.inner')) ; + + // todo do alias only for default transport + $container->setAlias(TraceableProducer::class, new Alias('enqueue.client.default.traceable_producer')); } if ($config['client']['redelivered_delay_time']) { $loader->load('extensions/delay_redelivered_message_extension.yml'); - $container->getDefinition('enqueue.client.delay_redelivered_message_extension') + $container->getDefinition('enqueue.client.default.delay_redelivered_message_extension') ->replaceArgument(1, $config['client']['redelivered_delay_time']) ; } } - // configure queue consumer + // todo configure queue consumer $container->getDefinition(QueueConsumer::class) ->replaceArgument(2, $config['consumption']['idle_timeout']) ->replaceArgument(3, $config['consumption']['receive_timeout']) ; - if ($container->hasDefinition('enqueue.client.queue_consumer')) { - $container->getDefinition('enqueue.client.queue_consumer') + if ($container->hasDefinition('enqueue.client.default.queue_consumer')) { + $container->getDefinition('enqueue.client.default.queue_consumer') ->replaceArgument(2, $config['consumption']['idle_timeout']) ->replaceArgument(3, $config['consumption']['receive_timeout']) ; @@ -187,60 +189,10 @@ private function setupAutowiringForProcessors(ContainerBuilder $container) { $container->registerForAutoconfiguration(TopicSubscriberInterface::class) ->setPublic(true) - ->addTag('enqueue.client.processor'); + ->addTag('enqueue.topic_subscriber', ['client' => 'default']); $container->registerForAutoconfiguration(CommandSubscriberInterface::class) ->setPublic(true) - ->addTag('enqueue.client.processor'); - } - - private 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; - } - - private 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; - } - - private 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; - } - - private function getName(): string - { - return 'default'; + ->addTag('enqueue.command_subscriber', ['client' => 'default']); } } diff --git a/pkg/enqueue-bundle/EnqueueBundle.php b/pkg/enqueue-bundle/EnqueueBundle.php index 947574869..0fb5cd15d 100644 --- a/pkg/enqueue-bundle/EnqueueBundle.php +++ b/pkg/enqueue-bundle/EnqueueBundle.php @@ -5,15 +5,13 @@ use Enqueue\AsyncEventDispatcher\DependencyInjection\AsyncEventDispatcherExtension; use Enqueue\AsyncEventDispatcher\DependencyInjection\AsyncEventsPass; use Enqueue\AsyncEventDispatcher\DependencyInjection\AsyncTransformersPass; -use Enqueue\Bundle\DependencyInjection\Compiler\BuildClientExtensionsPass; -use Enqueue\Bundle\DependencyInjection\Compiler\BuildConsumptionExtensionsPass; -use Enqueue\Bundle\DependencyInjection\Compiler\BuildQueueMetaRegistryPass; -use Enqueue\Bundle\DependencyInjection\Compiler\BuildTopicMetaSubscribersPass; -use Enqueue\Symfony\DependencyInjection\AnalyzeRouteCollectionPass; -use Enqueue\Symfony\DependencyInjection\BuildCommandSubscriberRoutesPass; -use Enqueue\Symfony\DependencyInjection\BuildProcessorRegistryPass; -use Enqueue\Symfony\DependencyInjection\BuildProcessorRoutesPass; -use Enqueue\Symfony\DependencyInjection\BuildTopicSubscriberRoutesPass; +use Enqueue\Symfony\Client\DependencyInjection\AnalyzeRouteCollectionPass; +use Enqueue\Symfony\Client\DependencyInjection\BuildClientExtensionsPass; +use Enqueue\Symfony\Client\DependencyInjection\BuildCommandSubscriberRoutesPass; +use Enqueue\Symfony\Client\DependencyInjection\BuildConsumptionExtensionsPass; +use Enqueue\Symfony\Client\DependencyInjection\BuildProcessorRegistryPass; +use Enqueue\Symfony\Client\DependencyInjection\BuildProcessorRoutesPass; +use Enqueue\Symfony\Client\DependencyInjection\BuildTopicSubscriberRoutesPass; use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; @@ -22,15 +20,12 @@ class EnqueueBundle extends Bundle { public function build(ContainerBuilder $container): void { - $container->addCompilerPass(new BuildConsumptionExtensionsPass()); - $container->addCompilerPass(new BuildTopicMetaSubscribersPass()); - $container->addCompilerPass(new BuildQueueMetaRegistryPass()); - $container->addCompilerPass(new BuildClientExtensionsPass()); - - $container->addCompilerPass(new BuildTopicSubscriberRoutesPass('default'), 100); - $container->addCompilerPass(new BuildCommandSubscriberRoutesPass('default'), 100); - $container->addCompilerPass(new BuildProcessorRoutesPass('default'), 100); - $container->addCompilerPass(new AnalyzeRouteCollectionPass('default'), 30); + $container->addCompilerPass(new BuildConsumptionExtensionsPass('default')); + $container->addCompilerPass(new BuildClientExtensionsPass('default')); + $container->addCompilerPass(new BuildTopicSubscriberRoutesPass('default'), PassConfig::TYPE_BEFORE_OPTIMIZATION, 100); + $container->addCompilerPass(new BuildCommandSubscriberRoutesPass('default'), PassConfig::TYPE_BEFORE_OPTIMIZATION, 100); + $container->addCompilerPass(new BuildProcessorRoutesPass('default'), PassConfig::TYPE_BEFORE_OPTIMIZATION, 100); + $container->addCompilerPass(new AnalyzeRouteCollectionPass('default'), PassConfig::TYPE_BEFORE_OPTIMIZATION, 30); $container->addCompilerPass(new BuildProcessorRegistryPass('default')); if (class_exists(AsyncEventDispatcherExtension::class)) { diff --git a/pkg/enqueue-bundle/Resources/config/client.yml b/pkg/enqueue-bundle/Resources/config/client.yml index 26d81edc2..ac6c52b5d 100644 --- a/pkg/enqueue-bundle/Resources/config/client.yml +++ b/pkg/enqueue-bundle/Resources/config/client.yml @@ -1,227 +1,130 @@ services: - enqueue.client.driver_factory: - class: 'Enqueue\Client\DriverFactory' - arguments: - - '@enqueue.client.config' - - '@enqueue.client.meta.queue_meta_registry' + enqueue.client.default.context: + class: 'Interop\Queue\PsrContext' + factory: ['@enqueue.client.default.driver', 'getContext'] - enqueue.client.config: - class: 'Enqueue\Client\Config' - public: false - - Enqueue\Client\Producer: - class: 'Enqueue\Client\Producer' - public: true + enqueue.client.default.driver_factory: + class: 'Enqueue\Client\DriverFactory' arguments: - - '@enqueue.client.driver' - - '@enqueue.client.rpc_factory' - - '@enqueue.client.extensions' + - '@enqueue.client.default.config' + - '@enqueue.client.default.route_collection' - Enqueue\Client\ProducerInterface: - public: true - alias: 'Enqueue\Client\Producer' - - # Deprecated. To be removed in 0.10. - enqueue.client.producer: - public: true - alias: 'Enqueue\Client\Producer' + enqueue.client.default.config: + class: 'Enqueue\Client\Config' - # Deprecated. To be removed in 0.10. - enqueue.producer: - public: true - alias: 'enqueue.client.producer' + enqueue.client.default.route_collection: + class: 'Enqueue\Client\RouteCollection' + factory: ['Enqueue\Client\RouteCollection', 'fromArray'] + arguments: + - [] - # Deprecated. To be removed in 0.10. - enqueue.client.producer_v2: + enqueue.client.default.producer: + class: 'Enqueue\Client\Producer' public: true - alias: 'enqueue.client.producer' + arguments: + - '@enqueue.client.default.driver' + - '@enqueue.client.default.rpc_factory' + - '@enqueue.client.default.client_extensions' - Enqueue\Client\SpoolProducer: + enqueue.client.default.spool_producer: class: 'Enqueue\Client\SpoolProducer' public: true arguments: - - '@Enqueue\Client\Producer' - - # Deprecated. To be removed in 0.10. - enqueue.client.spool_producer: - public: true - alias: 'Enqueue\Client\SpoolProducer' - - # Deprecated. To be removed in 0.10. - enqueue.spool_producer: - public: true - alias: 'enqueue.client.spool_producer' + - '@enqueue.client.default.producer' - enqueue.client.extensions: + enqueue.client.default.client_extensions: class: 'Enqueue\Client\ChainExtension' - public: false arguments: - [] - enqueue.client.rpc_factory: + enqueue.client.default.rpc_factory: class: 'Enqueue\Rpc\RpcFactory' - public: false arguments: - - '@enqueue.transport.context' + - '@enqueue.client.default.context' - Enqueue\Client\RouterProcessor: + enqueue.client.default.router_processor: class: 'Enqueue\Client\RouterProcessor' - public: true arguments: - - '@enqueue.client.driver' - - [] - - [] - tags: - - - name: 'enqueue.client.processor' - topicName: '__router__' - queueName: '%enqueue.client.router_queue_name%' - - # Deprecated. To be removed in 0.10. - enqueue.client.router_processor: - public: true - alias: 'Enqueue\Client\RouterProcessor' - - enqueue.client.processor_registry: - class: 'Enqueue\Symfony\Client\ContainerAwareProcessorRegistry' - public: false - calls: - - ['setContainer', ['@service_container']] - - Enqueue\Client\Meta\TopicMetaRegistry: - class: 'Enqueue\Client\Meta\TopicMetaRegistry' - public: true - arguments: [[]] - - # Deprecated. To be removed in 0.10. - enqueue.client.meta.topic_meta_registry: - public: true - alias: 'Enqueue\Client\Meta\TopicMetaRegistry' - - Enqueue\Client\Meta\QueueMetaRegistry: - class: 'Enqueue\Client\Meta\QueueMetaRegistry' - public: true - arguments: ['@enqueue.client.config', []] + - '@enqueue.client.default.driver' - # Deprecated. To be removed in 0.10. - enqueue.client.meta.queue_meta_registry: - public: true - alias: 'Enqueue\Client\Meta\QueueMetaRegistry' + enqueue.client.default.processor_registry: + class: 'Enqueue\Symfony\Client\ContainerProcessorRegistry' - enqueue.client.delegate_processor: + enqueue.client.default.delegate_processor: class: 'Enqueue\Client\DelegateProcessor' - public: false arguments: - - '@enqueue.client.processor_registry' + - '@enqueue.client.default.processor_registry' - enqueue.client.extension.set_router_properties: + enqueue.client.default.set_router_properties_extension: class: 'Enqueue\Client\ConsumptionExtension\SetRouterPropertiesExtension' - public: false arguments: - - '@enqueue.client.driver' + - '@enqueue.client.default.driver' tags: - - { name: 'enqueue.consumption.extension', priority: 100 } + - { name: 'enqueue.consumption_extension', priority: 100, client: 'default' } - enqueue.client.queue_consumer: + enqueue.client.default.queue_consumer: class: 'Enqueue\Consumption\QueueConsumer' - public: false arguments: - - '@enqueue.transport.context' - - '@enqueue.consumption.extensions' + - '@enqueue.client.default.context' + - '@enqueue.client.default.consumption_extensions' - ~ - ~ - Enqueue\Symfony\Client\ConsumeMessagesCommand: - class: 'Enqueue\Symfony\Client\ConsumeMessagesCommand' - public: true + enqueue.client.default.consumption_extensions: + class: 'Enqueue\Consumption\ChainExtension' arguments: - - '@enqueue.client.queue_consumer' - - '@enqueue.client.delegate_processor' - - '@Enqueue\Client\Meta\QueueMetaRegistry' - - '@enqueue.client.driver' - tags: - - { name: 'console.command' } - - # Deprecated. To be removed in 0.10. - enqueue.client.consume_messages_command: - public: true - alias: 'Enqueue\Symfony\Client\ConsumeMessagesCommand' + - [] - Enqueue\Symfony\Client\ProduceMessageCommand: - class: 'Enqueue\Symfony\Client\ProduceMessageCommand' - public: true + enqueue.client.default.consume_messages_command: + class: 'Enqueue\Symfony\Client\ConsumeMessagesCommand' arguments: - - '@Enqueue\Client\Producer' + - '@enqueue.client.default.queue_consumer' + - '@enqueue.client.default.delegate_processor' + - '@enqueue.client.default.driver' tags: - { name: 'console.command' } - # Deprecated. To be removed in 0.10. - enqueue.client.produce_message_command: - public: true - alias: 'Enqueue\Symfony\Client\ProduceMessageCommand' - - Enqueue\Symfony\Client\Meta\TopicsCommand: - class: 'Enqueue\Symfony\Client\Meta\TopicsCommand' - public: true + enqueue.client.default.produce_message_command: + class: 'Enqueue\Symfony\Client\ProduceMessageCommand' arguments: - - '@Enqueue\Client\Meta\TopicMetaRegistry' + - '@enqueue.client.default.producer' tags: - { name: 'console.command' } - # Deprecated. To be removed in 0.10. - enqueue.client.meta.topics_command: - public: true - alias: 'Enqueue\Symfony\Client\Meta\TopicsCommand' - - Enqueue\Symfony\Client\Meta\QueuesCommand: - class: 'Enqueue\Symfony\Client\Meta\QueuesCommand' - public: true + enqueue.client.default.setup_broker_command: + class: 'Enqueue\Symfony\Client\SetupBrokerCommand' arguments: - - '@Enqueue\Client\Meta\QueueMetaRegistry' + - '@enqueue.client.default.driver' tags: - { name: 'console.command' } - # Deprecated. To be removed in 0.10. - enqueue.client.meta.queues_command: - public: true - alias: 'Enqueue\Symfony\Client\Meta\QueuesCommand' - - Enqueue\Symfony\Client\SetupBrokerCommand: - class: 'Enqueue\Symfony\Client\SetupBrokerCommand' - public: true + enqueue.client.default.routes_command: + class: 'Enqueue\Symfony\Client\RoutesCommand' arguments: - - '@enqueue.client.driver' + - '@enqueue.client.default.config' + - '@enqueue.client.default.route_collection' tags: - { name: 'console.command' } - # Deprecated. To be removed in 0.10. - enqueue.client.setup_broker_command: - public: true - alias: 'Enqueue\Symfony\Client\SetupBrokerCommand' - + # todo enqueue.profiler.message_queue_collector: class: 'Enqueue\Bundle\Profiler\MessageQueueCollector' - public: false arguments: - - '@Enqueue\Client\Producer' + - '@enqueue.client.default.producer' tags: - { name: 'data_collector', template: '@Enqueue/Profiler/panel.html.twig', id: 'enqueue.message_queue' } - Enqueue\Symfony\Client\FlushSpoolProducerListener: + enqueue.client.default.flush_spool_producer_listener: class: 'Enqueue\Symfony\Client\FlushSpoolProducerListener' - public: true arguments: - - '@Enqueue\Client\SpoolProducer' + - '@enqueue.client.default.spool_producer' tags: - { name: 'kernel.event_subscriber' } - - # Deprecated. To be removed in 0.10. - enqueue.flush_spool_producer_listener: + + Enqueue\Client\ProducerInterface: public: true - alias: 'Enqueue\Symfony\Client\FlushSpoolProducerListener' - - enqueue.client.default.exclusive_command_extension: - class: 'Enqueue\Client\ConsumptionExtension\ExclusiveCommandExtension' - arguments: '@enqueue.client.default.driver' - tags: - - { name: 'enqueue.client.extension' } + alias: 'enqueue.client.default.producer' + + Enqueue\Client\SpoolProducer: + public: true + alias: 'enqueue.client.default.spool_producer' diff --git a/pkg/enqueue-bundle/Resources/config/extensions/delay_redelivered_message_extension.yml b/pkg/enqueue-bundle/Resources/config/extensions/delay_redelivered_message_extension.yml index 24fff7eb0..fe4f29cce 100644 --- a/pkg/enqueue-bundle/Resources/config/extensions/delay_redelivered_message_extension.yml +++ b/pkg/enqueue-bundle/Resources/config/extensions/delay_redelivered_message_extension.yml @@ -1,9 +1,9 @@ services: - enqueue.client.delay_redelivered_message_extension: + enqueue.client.default.delay_redelivered_message_extension: class: 'Enqueue\Client\ConsumptionExtension\DelayRedeliveredMessageExtension' public: false arguments: - - '@enqueue.client.driver' + - '@enqueue.client.default.driver' - ~ tags: - - { name: 'enqueue.consumption.extension', priority: 10 } \ No newline at end of file + - { name: 'enqueue.consumption_extension', priority: 10, client: 'default' } \ No newline at end of file diff --git a/pkg/enqueue-bundle/Resources/config/extensions/doctrine_clear_identity_map_extension.yml b/pkg/enqueue-bundle/Resources/config/extensions/doctrine_clear_identity_map_extension.yml index 932cb9ba1..30d8e4d8f 100644 --- a/pkg/enqueue-bundle/Resources/config/extensions/doctrine_clear_identity_map_extension.yml +++ b/pkg/enqueue-bundle/Resources/config/extensions/doctrine_clear_identity_map_extension.yml @@ -5,4 +5,4 @@ services: arguments: - '@doctrine' tags: - - { name: 'enqueue.consumption.extension' } + - { name: 'enqueue.consumption_extension', client: 'default' } diff --git a/pkg/enqueue-bundle/Resources/config/extensions/doctrine_ping_connection_extension.yml b/pkg/enqueue-bundle/Resources/config/extensions/doctrine_ping_connection_extension.yml index 7b3383a79..03d2d465d 100644 --- a/pkg/enqueue-bundle/Resources/config/extensions/doctrine_ping_connection_extension.yml +++ b/pkg/enqueue-bundle/Resources/config/extensions/doctrine_ping_connection_extension.yml @@ -5,4 +5,4 @@ services: arguments: - '@doctrine' tags: - - { name: 'enqueue.consumption.extension' } + - { name: 'enqueue.consumption.extension', client: 'default' } diff --git a/pkg/enqueue-bundle/Resources/config/extensions/exclusive_command_extension.yml b/pkg/enqueue-bundle/Resources/config/extensions/exclusive_command_extension.yml index c79b98029..cf8131acc 100644 --- a/pkg/enqueue-bundle/Resources/config/extensions/exclusive_command_extension.yml +++ b/pkg/enqueue-bundle/Resources/config/extensions/exclusive_command_extension.yml @@ -1,9 +1,8 @@ services: - enqueue.client.exclusive_command_extension: + enqueue.client.default.exclusive_command_extension: class: 'Enqueue\Client\ConsumptionExtension\ExclusiveCommandExtension' public: false arguments: - - [] + - '@enqueue.client.default.driver' tags: - - { name: 'enqueue.consumption.extension', priority: 100 } - - { name: 'enqueue.client.extension', priority: 100 } + - { name: 'enqueue.consumption_extension', priority: 100, client: 'default' } diff --git a/pkg/enqueue-bundle/Resources/config/extensions/flush_spool_producer_extension.yml b/pkg/enqueue-bundle/Resources/config/extensions/flush_spool_producer_extension.yml index 80bd3d455..096414166 100644 --- a/pkg/enqueue-bundle/Resources/config/extensions/flush_spool_producer_extension.yml +++ b/pkg/enqueue-bundle/Resources/config/extensions/flush_spool_producer_extension.yml @@ -1,8 +1,8 @@ services: - enqueue.client.flush_spool_producer_extension: + enqueue.client.default.flush_spool_producer_extension: class: 'Enqueue\Client\ConsumptionExtension\FlushSpoolProducerExtension' public: false arguments: - - '@Enqueue\Client\SpoolProducer' + - '@enqueue.client.default.spool_producer' tags: - - { name: 'enqueue.consumption.extension', priority: -100 } \ No newline at end of file + - { name: 'enqueue.consumption.extension', priority: -100, client: 'default' } \ No newline at end of file diff --git a/pkg/enqueue-bundle/Resources/config/extensions/reply_extension.yml b/pkg/enqueue-bundle/Resources/config/extensions/reply_extension.yml index b52c46b8f..20e25af4d 100644 --- a/pkg/enqueue-bundle/Resources/config/extensions/reply_extension.yml +++ b/pkg/enqueue-bundle/Resources/config/extensions/reply_extension.yml @@ -3,4 +3,4 @@ services: class: 'Enqueue\Consumption\Extension\ReplyExtension' public: false tags: - - { name: 'enqueue.consumption.extension' } \ No newline at end of file + - { name: 'enqueue.consumption.extension', client: 'default' } \ No newline at end of file diff --git a/pkg/enqueue-bundle/Resources/config/extensions/signal_extension.yml b/pkg/enqueue-bundle/Resources/config/extensions/signal_extension.yml index e7609eb06..b327ca7f2 100644 --- a/pkg/enqueue-bundle/Resources/config/extensions/signal_extension.yml +++ b/pkg/enqueue-bundle/Resources/config/extensions/signal_extension.yml @@ -3,4 +3,4 @@ services: class: 'Enqueue\Consumption\Extension\SignalExtension' public: false tags: - - { name: 'enqueue.consumption.extension' } \ No newline at end of file + - { name: 'enqueue.consumption.extension', client: 'default' } \ No newline at end of file diff --git a/pkg/enqueue-bundle/Resources/config/job.yml b/pkg/enqueue-bundle/Resources/config/job.yml index 159aecc61..2f55f3f70 100644 --- a/pkg/enqueue-bundle/Resources/config/job.yml +++ b/pkg/enqueue-bundle/Resources/config/job.yml @@ -20,7 +20,7 @@ services: public: true arguments: - '@Enqueue\JobQueue\Doctrine\JobStorage' - - '@Enqueue\Client\Producer' + - '@enqueue.client.default.producer' # Deprecated. To be removed in 0.10. enqueue.job.processor: @@ -55,10 +55,10 @@ services: arguments: - '@Enqueue\JobQueue\Doctrine\JobStorage' - '@Enqueue\JobQueue\CalculateRootJobStatusService' - - '@Enqueue\Client\Producer' + - '@enqueue.client.default.producer' - '@logger' tags: - - { name: 'enqueue.client.processor' } + - { name: 'enqueue.command_subscriber', client: 'default' } # Deprecated. To be removed in 0.10. enqueue.job.calculate_root_job_status_processor: @@ -70,10 +70,10 @@ services: public: true arguments: - '@Enqueue\JobQueue\Doctrine\JobStorage' - - '@Enqueue\Client\Producer' + - '@enqueue.client.default.producer' - '@logger' tags: - - { name: 'enqueue.client.processor' } + - { name: 'enqueue.topic_subscriber', client: 'default' } # Deprecated. To be removed in 0.10. enqueue.job.dependent_job_processor: diff --git a/pkg/enqueue-bundle/Resources/config/services.yml b/pkg/enqueue-bundle/Resources/config/services.yml index 6b626fd30..9b350c9b9 100644 --- a/pkg/enqueue-bundle/Resources/config/services.yml +++ b/pkg/enqueue-bundle/Resources/config/services.yml @@ -3,9 +3,6 @@ parameters: enqueue.queue_consumer.default_receive_timeout: 10000 services: - enqueue.connection_factory_factory: - class: 'Enqueue\ConnectionFactoryFactory' - enqueue.consumption.extensions: class: 'Enqueue\Consumption\ChainExtension' public: false @@ -16,7 +13,7 @@ services: class: 'Enqueue\Consumption\QueueConsumer' public: true arguments: - - '@enqueue.transport.context' + - '@enqueue.transport.default.context' - '@enqueue.consumption.extensions' - '%enqueue.queue_consumer.default_idle_time%' - '%enqueue.queue_consumer.default_receive_timeout%' @@ -43,13 +40,13 @@ services: class: 'Enqueue\Rpc\RpcFactory' public: false arguments: - - '@enqueue.transport.context' + - '@enqueue.transport.default.context' Enqueue\Rpc\RpcClient: class: 'Enqueue\Rpc\RpcClient' public: true arguments: - - '@enqueue.transport.context' + - '@enqueue.transport.default.context' - '@enqueue.transport.rpc_factory' # Deprecated. To be removed in 0.10. diff --git a/pkg/enqueue-bundle/Tests/Functional/App/AsyncListener.php b/pkg/enqueue-bundle/Tests/Functional/App/AsyncListener.php index b55525fc4..2a73d3608 100644 --- a/pkg/enqueue-bundle/Tests/Functional/App/AsyncListener.php +++ b/pkg/enqueue-bundle/Tests/Functional/App/AsyncListener.php @@ -2,6 +2,7 @@ namespace Enqueue\Bundle\Tests\Functional\App; +use Enqueue\AsyncEventDispatcher\Commands; use Enqueue\AsyncEventDispatcher\Registry; use Enqueue\Client\Message; use Enqueue\Client\ProducerInterface; @@ -44,7 +45,7 @@ public function onEvent(Event $event = null, $eventName) $message->setProperty('event_name', $eventName); $message->setProperty('transformer_name', $transformerName); - $this->producer->sendCommand('symfony_events', $message); + $this->producer->sendCommand(Commands::DISPATCH_ASYNC_EVENTS, $message); } } } diff --git a/pkg/enqueue-bundle/Tests/Functional/App/TestCommandSubscriberProcessor.php b/pkg/enqueue-bundle/Tests/Functional/App/TestCommandSubscriberProcessor.php index bc45336d6..2878ec762 100644 --- a/pkg/enqueue-bundle/Tests/Functional/App/TestCommandSubscriberProcessor.php +++ b/pkg/enqueue-bundle/Tests/Functional/App/TestCommandSubscriberProcessor.php @@ -23,6 +23,6 @@ public function process(PsrMessage $message, PsrContext $context) public static function getSubscribedCommand() { - return 'test_command_subscriber'; + return 'theCommand'; } } diff --git a/pkg/enqueue-bundle/Tests/Functional/App/TestExclusiveCommandSubscriberProcessor.php b/pkg/enqueue-bundle/Tests/Functional/App/TestExclusiveCommandSubscriberProcessor.php index 8f0001b92..df0a5719b 100644 --- a/pkg/enqueue-bundle/Tests/Functional/App/TestExclusiveCommandSubscriberProcessor.php +++ b/pkg/enqueue-bundle/Tests/Functional/App/TestExclusiveCommandSubscriberProcessor.php @@ -22,9 +22,10 @@ public function process(PsrMessage $message, PsrContext $context) public static function getSubscribedCommand() { return [ - 'processorName' => 'theExclusiveCommandName', - 'queueName' => 'the_exclusive_command_queue', - 'queueNameHardcoded' => true, + 'command' => 'theExclusiveCommandName', + 'processor' => 'theExclusiveCommandName', + 'queue' => 'the_exclusive_command_queue', + 'prefix_queue' => true, 'exclusive' => true, ]; } diff --git a/pkg/enqueue-bundle/Tests/Functional/App/TestTopicSubscriberProcessor.php b/pkg/enqueue-bundle/Tests/Functional/App/TestTopicSubscriberProcessor.php new file mode 100644 index 000000000..7130eda44 --- /dev/null +++ b/pkg/enqueue-bundle/Tests/Functional/App/TestTopicSubscriberProcessor.php @@ -0,0 +1,28 @@ +calls[] = $message; + + return Result::reply( + $context->createMessage($message->getBody().'Reply') + ); + } + + public static function getSubscribedTopics() + { + return 'theTopic'; + } +} diff --git a/pkg/enqueue-bundle/Tests/Functional/App/config/config.yml b/pkg/enqueue-bundle/Tests/Functional/App/config/config.yml index 1037404e0..ae28909ab 100644 --- a/pkg/enqueue-bundle/Tests/Functional/App/config/config.yml +++ b/pkg/enqueue-bundle/Tests/Functional/App/config/config.yml @@ -30,6 +30,34 @@ enqueue: async_commands: true services: + test_enqueue.client.default.traceable_producer: + alias: 'enqueue.client.default.traceable_producer' + public: true + + test_enqueue.client.default.producer: + alias: 'enqueue.client.default.producer' + public: true + + test_Enqueue\Client\ProducerInterface: + alias: 'Enqueue\Client\ProducerInterface' + public: true + + test_enqueue.client.default.driver: + alias: 'enqueue.client.default.driver' + public: true + + test_enqueue.transport.default.context: + alias: 'enqueue.transport.default.context' + public: true + + test_enqueue.client.default.consume_messages_command: + alias: 'enqueue.client.default.consume_messages_command' + public: true + + test.enqueue.client.default.routes_command: + alias: 'enqueue.client.default.routes_command' + public: true + test_async_listener: class: 'Enqueue\Bundle\Tests\Functional\App\TestAsyncListener' public: true @@ -40,13 +68,19 @@ services: class: 'Enqueue\Bundle\Tests\Functional\App\TestCommandSubscriberProcessor' public: true tags: - - { name: 'enqueue.client.processor' } + - { name: 'enqueue.command_subscriber', client: 'default' } + + test_topic_subscriber_processor: + class: 'Enqueue\Bundle\Tests\Functional\App\TestTopicSubscriberProcessor' + public: true + tags: + - { name: 'enqueue.topic_subscriber', client: 'default' } test_exclusive_command_subscriber_processor: class: 'Enqueue\Bundle\Tests\Functional\App\TestExclusiveCommandSubscriberProcessor' public: true tags: - - { name: 'enqueue.client.processor' } + - { name: 'enqueue.command_subscriber', client: 'default' } test_async_subscriber: class: 'Enqueue\Bundle\Tests\Functional\App\TestAsyncSubscriber' @@ -67,4 +101,4 @@ services: enqueue.events.async_listener: class: 'Enqueue\Bundle\Tests\Functional\App\AsyncListener' public: true - arguments: ['@Enqueue\Client\Producer', '@enqueue.events.registry'] \ No newline at end of file + arguments: ['@enqueue.client.default.producer', '@enqueue.events.registry'] \ No newline at end of file diff --git a/pkg/enqueue-bundle/Tests/Functional/App/config/custom-config.yml b/pkg/enqueue-bundle/Tests/Functional/App/config/custom-config.yml index 179b6e435..0e59a9528 100644 --- a/pkg/enqueue-bundle/Tests/Functional/App/config/custom-config.yml +++ b/pkg/enqueue-bundle/Tests/Functional/App/config/custom-config.yml @@ -15,17 +15,29 @@ framework: default_locale: '%locale%' services: + test_enqueue.client.default.driver: + alias: 'enqueue.client.default.driver' + public: true + + test_enqueue.transport.default.context: + alias: 'enqueue.transport.default.context' + public: true + + test_enqueue.client.default.consume_messages_command: + alias: 'enqueue.client.default.consume_messages_command' + public: true + test.message.processor: class: 'Enqueue\Bundle\Tests\Functional\TestProcessor' public: true tags: - - { name: 'enqueue.client.processor' } + - { name: 'enqueue.topic_subscriber', client: 'default' } test.message.command_processor: class: 'Enqueue\Bundle\Tests\Functional\TestCommandProcessor' public: true tags: - - { name: 'enqueue.client.processor' } + - { name: 'enqueue.command_subscriber', client: 'default' } test.sqs_client: public: true diff --git a/pkg/enqueue-bundle/Tests/Functional/Client/ConsumeMessagesCommandTest.php b/pkg/enqueue-bundle/Tests/Functional/Client/ConsumeMessagesCommandTest.php deleted file mode 100644 index 5af610971..000000000 --- a/pkg/enqueue-bundle/Tests/Functional/Client/ConsumeMessagesCommandTest.php +++ /dev/null @@ -1,19 +0,0 @@ -get(ConsumeMessagesCommand::class); - - $this->assertInstanceOf(ConsumeMessagesCommand::class, $command); - } -} diff --git a/pkg/enqueue-bundle/Tests/Functional/Client/DriverTest.php b/pkg/enqueue-bundle/Tests/Functional/Client/DriverTest.php deleted file mode 100644 index d4b290fb3..000000000 --- a/pkg/enqueue-bundle/Tests/Functional/Client/DriverTest.php +++ /dev/null @@ -1,19 +0,0 @@ -get('enqueue.client.driver'); - - $this->assertInstanceOf(DriverInterface::class, $driver); - } -} diff --git a/pkg/enqueue-bundle/Tests/Functional/Client/ProduceMessageCommandTest.php b/pkg/enqueue-bundle/Tests/Functional/Client/ProduceMessageCommandTest.php deleted file mode 100644 index 0873705e7..000000000 --- a/pkg/enqueue-bundle/Tests/Functional/Client/ProduceMessageCommandTest.php +++ /dev/null @@ -1,19 +0,0 @@ -get(ProduceMessageCommand::class); - - $this->assertInstanceOf(ProduceMessageCommand::class, $command); - } -} diff --git a/pkg/enqueue-bundle/Tests/Functional/Client/ProducerTest.php b/pkg/enqueue-bundle/Tests/Functional/Client/ProducerTest.php index 07bfeb63d..e86eb840d 100644 --- a/pkg/enqueue-bundle/Tests/Functional/Client/ProducerTest.php +++ b/pkg/enqueue-bundle/Tests/Functional/Client/ProducerTest.php @@ -3,11 +3,9 @@ namespace Enqueue\Bundle\Tests\Functional\Client; use Enqueue\Bundle\Tests\Functional\WebTestCase; -use Enqueue\Client\Config; use Enqueue\Client\Message; use Enqueue\Client\Producer; use Enqueue\Client\ProducerInterface; -use Enqueue\Client\RouterProcessor; use Enqueue\Client\TraceableProducer; use Enqueue\Rpc\Promise; @@ -16,39 +14,24 @@ */ class ProducerTest extends WebTestCase { - public function setUp() + public function testCouldBeGetFromContainerByInterface() { - parent::setUp(); + $producer = static::$container->get('test_'.ProducerInterface::class); - static::$container->get(Producer::class)->clearTraces(); + $this->assertInstanceOf(ProducerInterface::class, $producer); } - public function tearDown() + public function testCouldBeGetFromContainerByServiceId() { - static::$container->get(Producer::class)->clearTraces(); + $producer = static::$container->get('test_enqueue.client.default.producer'); - parent::tearDown(); - } - - public function testCouldBeGetFromContainerAsService() - { - $messageProducer = static::$container->get(Producer::class); - - $this->assertInstanceOf(ProducerInterface::class, $messageProducer); - } - - public function testCouldBeGetFromContainerAsShortenAlias() - { - $messageProducer = static::$container->get(Producer::class); - $aliasMessageProducer = static::$container->get('enqueue.producer'); - - $this->assertSame($messageProducer, $aliasMessageProducer); + $this->assertInstanceOf(ProducerInterface::class, $producer); } public function testShouldSendEvent() { /** @var ProducerInterface $producer */ - $producer = static::$container->get(Producer::class); + $producer = static::$container->get('test_enqueue.client.default.producer'); $producer->sendEvent('theTopic', 'theMessage'); @@ -61,13 +44,13 @@ public function testShouldSendEvent() public function testShouldSendCommandWithoutNeedForReply() { /** @var ProducerInterface $producer */ - $producer = static::$container->get(Producer::class); + $producer = static::$container->get('test_enqueue.client.default.producer'); $result = $producer->sendCommand('theCommand', 'theMessage', false); $this->assertNull($result); - $traces = $this->getTraceableProducer()->getTopicTraces(Config::COMMAND_TOPIC); + $traces = $this->getTraceableProducer()->getCommandTraces('theCommand'); $this->assertCount(1, $traces); $this->assertEquals('theMessage', $traces[0]['body']); @@ -76,7 +59,7 @@ public function testShouldSendCommandWithoutNeedForReply() public function testShouldSendMessageInstanceAsCommandWithoutNeedForReply() { /** @var ProducerInterface $producer */ - $producer = static::$container->get(Producer::class); + $producer = static::$container->get('test_enqueue.client.default.producer'); $message = new Message('theMessage'); @@ -84,22 +67,20 @@ public function testShouldSendMessageInstanceAsCommandWithoutNeedForReply() $this->assertNull($result); - $traces = $this->getTraceableProducer()->getTopicTraces(Config::COMMAND_TOPIC); + $traces = $this->getTraceableProducer()->getCommandTraces('theCommand'); $this->assertCount(1, $traces); $this->assertEquals('theMessage', $traces[0]['body']); $this->assertEquals([ - 'enqueue.topic_name' => Config::COMMAND_TOPIC, - 'enqueue.processor_name' => RouterProcessor::class, + 'enqueue.processor_name' => 'test_command_subscriber_processor', 'enqueue.command_name' => 'theCommand', - 'enqueue.processor_queue_name' => 'default', ], $traces[0]['properties']); } public function testShouldSendExclusiveCommandWithNeedForReply() { /** @var ProducerInterface $producer */ - $producer = static::$container->get(Producer::class); + $producer = static::$container->get('test_enqueue.client.default.producer'); $message = new Message('theMessage'); @@ -112,17 +93,15 @@ public function testShouldSendExclusiveCommandWithNeedForReply() $this->assertCount(1, $traces); $this->assertEquals('theMessage', $traces[0]['body']); $this->assertEquals([ - 'enqueue.topic_name' => Config::COMMAND_TOPIC, 'enqueue.processor_name' => 'theExclusiveCommandName', 'enqueue.command_name' => 'theExclusiveCommandName', - 'enqueue.processor_queue_name' => 'the_exclusive_command_queue', ], $traces[0]['properties']); } public function testShouldSendMessageInstanceCommandWithNeedForReply() { /** @var ProducerInterface $producer */ - $producer = static::$container->get(Producer::class); + $producer = static::$container->get('test_enqueue.client.default.producer'); $message = new Message('theMessage'); @@ -135,18 +114,13 @@ public function testShouldSendMessageInstanceCommandWithNeedForReply() $this->assertCount(1, $traces); $this->assertEquals('theMessage', $traces[0]['body']); $this->assertEquals([ - 'enqueue.topic_name' => Config::COMMAND_TOPIC, - 'enqueue.processor_name' => RouterProcessor::class, + 'enqueue.processor_name' => 'test_command_subscriber_processor', 'enqueue.command_name' => 'theCommand', - 'enqueue.processor_queue_name' => 'default', ], $traces[0]['properties']); } - /** - * @return TraceableProducer|object - */ - private function getTraceableProducer() + private function getTraceableProducer(): TraceableProducer { - return static::$container->get(Producer::class); + return static::$container->get('test_enqueue.client.default.traceable_producer'); } } diff --git a/pkg/enqueue-bundle/Tests/Functional/Client/SpoolProducerTest.php b/pkg/enqueue-bundle/Tests/Functional/Client/SpoolProducerTest.php index c8a1eaa27..05192d78a 100644 --- a/pkg/enqueue-bundle/Tests/Functional/Client/SpoolProducerTest.php +++ b/pkg/enqueue-bundle/Tests/Functional/Client/SpoolProducerTest.php @@ -12,19 +12,8 @@ class SpoolProducerTest extends WebTestCase { public function testCouldBeGetFromContainerAsService() { - $producer = static::$container->get(SpoolProducer::class); + $producer = static::$container->get('enqueue.client.default.spool_producer'); $this->assertInstanceOf(SpoolProducer::class, $producer); } - - /** - * @group legacy - */ - public function testCouldBeGetFromContainerAsShortenAlias() - { - $producer = static::$container->get('enqueue.client.spool_producer'); - $aliasProducer = static::$container->get('enqueue.spool_producer'); - - $this->assertSame($producer, $aliasProducer); - } } diff --git a/pkg/enqueue-bundle/Tests/Functional/Commands/RunCommandProcessorTest.php b/pkg/enqueue-bundle/Tests/Functional/Commands/RunCommandProcessorTest.php deleted file mode 100644 index 3f5579e65..000000000 --- a/pkg/enqueue-bundle/Tests/Functional/Commands/RunCommandProcessorTest.php +++ /dev/null @@ -1,20 +0,0 @@ -get('enqueue.async_command.run_command_processor'); - - $this->assertInstanceOf(RunCommandProcessor::class, $processor); - } -} diff --git a/pkg/enqueue-bundle/Tests/Functional/ConsumeMessagesCommandTest.php b/pkg/enqueue-bundle/Tests/Functional/ConsumeMessagesCommandTest.php deleted file mode 100644 index d9313c4c5..000000000 --- a/pkg/enqueue-bundle/Tests/Functional/ConsumeMessagesCommandTest.php +++ /dev/null @@ -1,18 +0,0 @@ -get(ConsumeMessagesCommand::class); - - $this->assertInstanceOf(ConsumeMessagesCommand::class, $command); - } -} diff --git a/pkg/enqueue-bundle/Tests/Functional/ContextTest.php b/pkg/enqueue-bundle/Tests/Functional/ContextTest.php index 44c141acd..6e7e7bd00 100644 --- a/pkg/enqueue-bundle/Tests/Functional/ContextTest.php +++ b/pkg/enqueue-bundle/Tests/Functional/ContextTest.php @@ -11,7 +11,7 @@ class ContextTest extends WebTestCase { public function testCouldBeGetFromContainerAsService() { - $connection = static::$container->get('enqueue.transport.context'); + $connection = static::$container->get('test_enqueue.transport.default.context'); $this->assertInstanceOf(PsrContext::class, $connection); } diff --git a/pkg/enqueue-bundle/Tests/Functional/Events/AsyncListenerTest.php b/pkg/enqueue-bundle/Tests/Functional/Events/AsyncListenerTest.php index e504020ca..6e952ab28 100644 --- a/pkg/enqueue-bundle/Tests/Functional/Events/AsyncListenerTest.php +++ b/pkg/enqueue-bundle/Tests/Functional/Events/AsyncListenerTest.php @@ -3,6 +3,7 @@ namespace Enqueue\Bundle\Tests\Functional\Events; use Enqueue\AsyncEventDispatcher\AsyncListener; +use Enqueue\AsyncEventDispatcher\Commands; use Enqueue\Bundle\Tests\Functional\App\TestAsyncListener; use Enqueue\Bundle\Tests\Functional\WebTestCase; use Enqueue\Client\TraceableProducer; @@ -50,13 +51,13 @@ public function testShouldSendMessageToExpectedCommandInsteadOfCallingRealListen $dispatcher->dispatch('test_async', $event); /** @var TraceableProducer $producer */ - $producer = static::$container->get('enqueue.producer'); + $producer = static::$container->get('test_enqueue.client.default.producer'); - $traces = $producer->getCommandTraces('symfony_events'); + $traces = $producer->getCommandTraces(Commands::DISPATCH_ASYNC_EVENTS); $this->assertCount(1, $traces); - $this->assertEquals('symfony_events', $traces[0]['command']); + $this->assertEquals(Commands::DISPATCH_ASYNC_EVENTS, $traces[0]['command']); $this->assertEquals('{"subject":"theSubject","arguments":{"fooArg":"fooVal"}}', $traces[0]['body']); } @@ -70,9 +71,9 @@ public function testShouldSendMessageForEveryDispatchCall() $dispatcher->dispatch('test_async', new GenericEvent('theSubject', ['fooArg' => 'fooVal'])); /** @var TraceableProducer $producer */ - $producer = static::$container->get('enqueue.producer'); + $producer = static::$container->get('test_enqueue.client.default.producer'); - $traces = $producer->getCommandTraces('symfony_events'); + $traces = $producer->getCommandTraces(Commands::DISPATCH_ASYNC_EVENTS); $this->assertCount(3, $traces); } @@ -90,9 +91,9 @@ public function testShouldSendMessageIfDispatchedFromInsideListener() $dispatcher->dispatch($eventName); /** @var TraceableProducer $producer */ - $producer = static::$container->get('enqueue.producer'); + $producer = static::$container->get('test_enqueue.client.default.producer'); - $traces = $producer->getCommandTraces('symfony_events'); + $traces = $producer->getCommandTraces(Commands::DISPATCH_ASYNC_EVENTS); $this->assertCount(1, $traces); } diff --git a/pkg/enqueue-bundle/Tests/Functional/Events/AsyncSubscriberTest.php b/pkg/enqueue-bundle/Tests/Functional/Events/AsyncSubscriberTest.php index dc653a300..6e00eafca 100644 --- a/pkg/enqueue-bundle/Tests/Functional/Events/AsyncSubscriberTest.php +++ b/pkg/enqueue-bundle/Tests/Functional/Events/AsyncSubscriberTest.php @@ -3,6 +3,7 @@ namespace Enqueue\Bundle\Tests\Functional\Events; use Enqueue\AsyncEventDispatcher\AsyncListener; +use Enqueue\AsyncEventDispatcher\Commands; use Enqueue\Bundle\Tests\Functional\App\TestAsyncSubscriber; use Enqueue\Bundle\Tests\Functional\WebTestCase; use Enqueue\Client\TraceableProducer; @@ -50,13 +51,13 @@ public function testShouldSendMessageToExpectedTopicInsteadOfCallingRealSubscrib $dispatcher->dispatch('test_async_subscriber', $event); /** @var TraceableProducer $producer */ - $producer = static::$container->get('enqueue.producer'); + $producer = static::$container->get('test_enqueue.client.default.producer'); - $traces = $producer->getCommandTraces('symfony_events'); + $traces = $producer->getCommandTraces(Commands::DISPATCH_ASYNC_EVENTS); $this->assertCount(1, $traces); - $this->assertEquals('symfony_events', $traces[0]['command']); + $this->assertEquals(Commands::DISPATCH_ASYNC_EVENTS, $traces[0]['command']); $this->assertEquals('{"subject":"theSubject","arguments":{"fooArg":"fooVal"}}', $traces[0]['body']); } @@ -70,9 +71,9 @@ public function testShouldSendMessageForEveryDispatchCall() $dispatcher->dispatch('test_async_subscriber', new GenericEvent('theSubject', ['fooArg' => 'fooVal'])); /** @var TraceableProducer $producer */ - $producer = static::$container->get('enqueue.producer'); + $producer = static::$container->get('test_enqueue.client.default.producer'); - $traces = $producer->getCommandTraces('symfony_events'); + $traces = $producer->getCommandTraces(Commands::DISPATCH_ASYNC_EVENTS); $this->assertCount(3, $traces); } @@ -90,9 +91,9 @@ public function testShouldSendMessageIfDispatchedFromInsideListener() $dispatcher->dispatch($eventName); /** @var TraceableProducer $producer */ - $producer = static::$container->get('enqueue.producer'); + $producer = static::$container->get('test_enqueue.client.default.producer'); - $traces = $producer->getCommandTraces('symfony_events'); + $traces = $producer->getCommandTraces(Commands::DISPATCH_ASYNC_EVENTS); $this->assertCount(1, $traces); } diff --git a/pkg/enqueue-bundle/Tests/Functional/QueueRegistryTest.php b/pkg/enqueue-bundle/Tests/Functional/QueueRegistryTest.php deleted file mode 100644 index d608b8f05..000000000 --- a/pkg/enqueue-bundle/Tests/Functional/QueueRegistryTest.php +++ /dev/null @@ -1,18 +0,0 @@ -get(QueueMetaRegistry::class); - - $this->assertInstanceOf(QueueMetaRegistry::class, $connection); - } -} diff --git a/pkg/enqueue-bundle/Tests/Functional/QueuesCommandTest.php b/pkg/enqueue-bundle/Tests/Functional/QueuesCommandTest.php deleted file mode 100644 index d13862e29..000000000 --- a/pkg/enqueue-bundle/Tests/Functional/QueuesCommandTest.php +++ /dev/null @@ -1,46 +0,0 @@ -get(QueuesCommand::class); - - $this->assertInstanceOf(QueuesCommand::class, $command); - } - - public function testShouldDisplayRegisteredQueues() - { - $command = static::$container->get(QueuesCommand::class); - - $tester = new CommandTester($command); - $tester->execute([]); - - $display = $tester->getDisplay(); - - $this->assertContains(' default ', $display); - $this->assertContains('enqueue.app.default', $display); - $this->assertContains(RouterProcessor::class, $display); - } - - public function testShouldDisplayRegisteredCommand() - { - $command = static::$container->get(QueuesCommand::class); - - $tester = new CommandTester($command); - $tester->execute([]); - - $display = $tester->getDisplay(); - - $this->assertContains('test_command_subscriber', $display); - } -} diff --git a/pkg/enqueue-bundle/Tests/Functional/RoutesCommandTest.php b/pkg/enqueue-bundle/Tests/Functional/RoutesCommandTest.php new file mode 100644 index 000000000..c8fd7b7a2 --- /dev/null +++ b/pkg/enqueue-bundle/Tests/Functional/RoutesCommandTest.php @@ -0,0 +1,51 @@ +get('test.enqueue.client.default.routes_command'); + + $this->assertInstanceOf(RoutesCommand::class, $command); + } + + public function testShouldDisplayRegisteredTopics() + { + /** @var RoutesCommand $command */ + $command = static::$container->get('test.enqueue.client.default.routes_command'); + + $tester = new CommandTester($command); + $tester->execute([]); + + $expected = <<<'OUTPUT' +| topic | theTopic | default (prefixed) | test_topic_subscriber_processor | (hidden) | +OUTPUT; + + $this->assertSame(0, $tester->getStatusCode()); + $this->assertContains($expected, $tester->getDisplay()); + } + + public function testShouldDisplayCommands() + { + /** @var RoutesCommand $command */ + $command = static::$container->get('test.enqueue.client.default.routes_command'); + + $tester = new CommandTester($command); + $tester->execute([]); + + $expected = <<<'OUTPUT' +| command | theCommand | default (prefixed) | test_command_subscriber_processor | (hidden) | +OUTPUT; + + $this->assertSame(0, $tester->getStatusCode()); + $this->assertContains($expected, $tester->getDisplay()); + } +} diff --git a/pkg/enqueue-bundle/Tests/Functional/TopicRegistryTest.php b/pkg/enqueue-bundle/Tests/Functional/TopicRegistryTest.php deleted file mode 100644 index bd15aa7d5..000000000 --- a/pkg/enqueue-bundle/Tests/Functional/TopicRegistryTest.php +++ /dev/null @@ -1,18 +0,0 @@ -get(TopicMetaRegistry::class); - - $this->assertInstanceOf(TopicMetaRegistry::class, $connection); - } -} diff --git a/pkg/enqueue-bundle/Tests/Functional/TopicsCommandTest.php b/pkg/enqueue-bundle/Tests/Functional/TopicsCommandTest.php deleted file mode 100644 index af6361a74..000000000 --- a/pkg/enqueue-bundle/Tests/Functional/TopicsCommandTest.php +++ /dev/null @@ -1,47 +0,0 @@ -get(TopicsCommand::class); - - $this->assertInstanceOf(TopicsCommand::class, $command); - } - - public function testShouldDisplayRegisteredTopics() - { - $command = static::$container->get(TopicsCommand::class); - - $tester = new CommandTester($command); - $tester->execute([]); - - $display = $tester->getDisplay(); - - $this->assertContains('__router__', $display); - $this->assertContains(RouterProcessor::class, $display); - } - - public function testShouldDisplayCommands() - { - $command = static::$container->get(TopicsCommand::class); - - $tester = new CommandTester($command); - $tester->execute([]); - - $display = $tester->getDisplay(); - - $this->assertContains(Config::COMMAND_TOPIC, $display); - $this->assertContains('test_command_subscriber', $display); - } -} diff --git a/pkg/enqueue-bundle/Tests/Functional/UseCasesTest.php b/pkg/enqueue-bundle/Tests/Functional/UseCasesTest.php index 2656d3efc..7014ba8f5 100644 --- a/pkg/enqueue-bundle/Tests/Functional/UseCasesTest.php +++ b/pkg/enqueue-bundle/Tests/Functional/UseCasesTest.php @@ -4,11 +4,8 @@ use Enqueue\Bundle\Tests\Functional\App\CustomAppKernel; use Enqueue\Client\DriverInterface; -use Enqueue\Client\Producer; use Enqueue\Client\ProducerInterface; use Enqueue\Stomp\StompDestination; -use Enqueue\Symfony\Client\ConsumeMessagesCommand; -use Enqueue\Symfony\Consumption\ContainerAwareConsumeMessagesCommand; use Interop\Queue\PsrContext; use Interop\Queue\PsrMessage; use Interop\Queue\PsrQueue; @@ -117,7 +114,7 @@ public function provideEnqueueConfigs() yield 'mongodb_dsn' => [[ 'transport' => getenv('MONGO_DSN'), ]]; - +// // yield 'gps' => [[ // 'transport' => [ // 'dsn' => getenv('GPS_DSN'), @@ -173,7 +170,7 @@ public function testClientConsumeCommandMessagesFromExplicitlySetQueue(array $en { $this->customSetUp($enqueueConfig); - $command = static::$container->get(ConsumeMessagesCommand::class); + $command = static::$container->get('test_enqueue.client.default.consume_messages_command'); $processor = static::$container->get('test.message.command_processor'); $expectedBody = __METHOD__.time(); @@ -200,7 +197,7 @@ public function testClientConsumeMessagesFromExplicitlySetQueue(array $enqueueCo $expectedBody = __METHOD__.time(); - $command = static::$container->get(ConsumeMessagesCommand::class); + $command = static::$container->get('test_enqueue.client.default.consume_messages_command'); $processor = static::$container->get('test.message.processor'); $this->getMessageProducer()->sendEvent(TestProcessor::TOPIC, $expectedBody); @@ -216,39 +213,39 @@ public function testClientConsumeMessagesFromExplicitlySetQueue(array $enqueueCo $this->assertEquals($expectedBody, $processor->message->getBody()); } - /** - * @dataProvider provideEnqueueConfigs - */ - public function testTransportConsumeMessagesCommandShouldConsumeMessage(array $enqueueConfig) - { - $this->customSetUp($enqueueConfig); - - if ($this->getTestQueue() instanceof StompDestination) { - $this->markTestSkipped('The test fails with the exception Stomp\Exception\ErrorFrameException: Error "precondition_failed". '. - 'It happens because of the destination options are different from the one used while creating the dest. Nothing to do about it' - ); - } - - $expectedBody = __METHOD__.time(); - - $command = static::$container->get(ContainerAwareConsumeMessagesCommand::class); - $command->setContainer(static::$container); - $processor = static::$container->get('test.message.processor'); - - $this->getMessageProducer()->sendEvent(TestProcessor::TOPIC, $expectedBody); - - $tester = new CommandTester($command); - $tester->execute([ - '--message-limit' => 1, - '--time-limit' => '+2sec', - '--receive-timeout' => 1000, - '--queue' => [$this->getTestQueue()->getQueueName()], - 'processor-service' => 'test.message.processor', - ]); - - $this->assertInstanceOf(PsrMessage::class, $processor->message); - $this->assertEquals($expectedBody, $processor->message->getBody()); - } +// /** +// * @dataProvider provideEnqueueConfigs +// */ +// public function testTransportConsumeMessagesCommandShouldConsumeMessage(array $enqueueConfig) +// { +// $this->customSetUp($enqueueConfig); +// +// if ($this->getTestQueue() instanceof StompDestination) { +// $this->markTestSkipped('The test fails with the exception Stomp\Exception\ErrorFrameException: Error "precondition_failed". '. +// 'It happens because of the destination options are different from the one used while creating the dest. Nothing to do about it' +// ); +// } +// +// $expectedBody = __METHOD__.time(); +// +// $command = static::$container->get('test_enqueue.client.default.consume_messages_command'); +// $command->setContainer(static::$container); +// $processor = static::$container->get('test.message.processor'); +// +// $this->getMessageProducer()->sendEvent(TestProcessor::TOPIC, $expectedBody); +// +// $tester = new CommandTester($command); +// $tester->execute([ +// '--message-limit' => 1, +// '--time-limit' => '+2sec', +// '--receive-timeout' => 1000, +// '--queue' => [$this->getTestQueue()->getQueueName()], +// 'processor-service' => 'test.message.processor', +// ]); +// +// $this->assertInstanceOf(PsrMessage::class, $processor->message); +// $this->assertEquals($expectedBody, $processor->message->getBody()); +// } /** * @return string @@ -270,7 +267,7 @@ protected function customSetUp(array $enqueueConfig) static::$container = static::$kernel->getContainer(); /** @var DriverInterface $driver */ - $driver = static::$container->get('enqueue.client.driver'); + $driver = static::$container->get('test_enqueue.client.default.driver'); $context = $this->getPsrContext(); $driver->setupBroker(); @@ -287,7 +284,7 @@ protected function customSetUp(array $enqueueConfig) protected function getTestQueue() { /** @var DriverInterface $driver */ - $driver = static::$container->get('enqueue.client.driver'); + $driver = static::$container->get('test_enqueue.client.default.driver'); return $driver->createQueue('test'); } @@ -310,7 +307,7 @@ protected static function createKernel(array $options = []) */ private function getMessageProducer() { - return static::$container->get(Producer::class); + return static::$container->get('enqueue.client.default.producer'); } /** @@ -318,6 +315,6 @@ private function getMessageProducer() */ private function getPsrContext() { - return static::$container->get('enqueue.transport.context'); + return static::$container->get('test_enqueue.transport.default.context'); } } diff --git a/pkg/enqueue-bundle/Tests/Functional/WebTestCase.php b/pkg/enqueue-bundle/Tests/Functional/WebTestCase.php index d597d83cd..0cf1a0723 100644 --- a/pkg/enqueue-bundle/Tests/Functional/WebTestCase.php +++ b/pkg/enqueue-bundle/Tests/Functional/WebTestCase.php @@ -33,7 +33,7 @@ protected function setUp() } /** @var TraceableProducer $producer */ - $producer = static::$container->get(TraceableProducer::class); + $producer = static::$container->get('test_enqueue.client.default.traceable_producer'); $producer->clearTraces(); } diff --git a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/AddTopicMetaPassTest.php b/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/AddTopicMetaPassTest.php deleted file mode 100644 index 94537fd89..000000000 --- a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/AddTopicMetaPassTest.php +++ /dev/null @@ -1,76 +0,0 @@ -assertClassImplements(CompilerPassInterface::class, AddTopicMetaPass::class); - } - - public function testCouldBeConstructedWithoutAntArguments() - { - new AddTopicMetaPass(); - } - - public function testCouldBeConstructedByCreateFactoryMethod() - { - $pass = AddTopicMetaPass::create(); - - $this->assertInstanceOf(AddTopicMetaPass::class, $pass); - } - - public function testShouldReturnSelfOnAdd() - { - $pass = AddTopicMetaPass::create(); - - $this->assertSame($pass, $pass->add('aTopic')); - } - - public function testShouldDoNothingIfContainerDoesNotHaveRegistryService() - { - $container = new ContainerBuilder(); - - $pass = AddTopicMetaPass::create() - ->add('fooTopic') - ->add('barTopic') - ; - - $pass->process($container); - } - - public function testShouldAddTopicsInRegistryKeepingPreviouslyAdded() - { - $container = new ContainerBuilder(); - - $registry = new Definition(null, [[ - 'bazTopic' => [], - ]]); - $container->setDefinition(TopicMetaRegistry::class, $registry); - - $pass = AddTopicMetaPass::create() - ->add('fooTopic') - ->add('barTopic') - ; - $pass->process($container); - - $expectedTopics = [ - 'bazTopic' => [], - 'fooTopic' => [], - 'barTopic' => [], - ]; - - $this->assertSame($expectedTopics, $registry->getArgument(0)); - } -} diff --git a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildClientExtensionsPassTest.php b/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildClientExtensionsPassTest.php deleted file mode 100644 index e98184b84..000000000 --- a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildClientExtensionsPassTest.php +++ /dev/null @@ -1,129 +0,0 @@ -assertClassImplements(CompilerPassInterface::class, BuildClientExtensionsPass::class); - } - - public function testCouldBeConstructedWithoutAnyArguments() - { - new BuildClientExtensionsPass(); - } - - public function testShouldReplaceFirstArgumentOfExtensionsServiceConstructorWithTagsExtensions() - { - $container = new ContainerBuilder(); - - $extensions = new Definition(); - $extensions->addArgument([]); - $container->setDefinition('enqueue.client.extensions', $extensions); - - $extension = new Definition(); - $extension->addTag('enqueue.client.extension'); - $container->setDefinition('foo_extension', $extension); - - $extension = new Definition(); - $extension->addTag('enqueue.client.extension'); - $container->setDefinition('bar_extension', $extension); - - $pass = new BuildClientExtensionsPass(); - $pass->process($container); - - $this->assertEquals( - [new Reference('foo_extension'), new Reference('bar_extension')], - $extensions->getArgument(0) - ); - } - - public function testShouldOrderExtensionsByPriority() - { - $container = new ContainerBuilder(); - - $extensions = new Definition(); - $extensions->addArgument([]); - $container->setDefinition('enqueue.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(); - $pass->process($container); - - $orderedExtensions = $extensions->getArgument(0); - - $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.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(); - $pass->process($container); - - $orderedExtensions = $extensions->getArgument(0); - - $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 testShouldDoesNothingIfClientExtensionServiceIsNotDefined() - { - $container = $this->createMock(ContainerBuilder::class); - $container - ->expects($this->once()) - ->method('hasDefinition') - ->with('enqueue.client.extensions') - ->willReturn(false) - ; - $container - ->expects($this->never()) - ->method('findTaggedServiceIds') - ; - - $pass = new BuildClientExtensionsPass(); - $pass->process($container); - } -} diff --git a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildConsumptionExtensionsPassTest.php b/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildConsumptionExtensionsPassTest.php deleted file mode 100644 index a7614bc96..000000000 --- a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildConsumptionExtensionsPassTest.php +++ /dev/null @@ -1,111 +0,0 @@ -assertClassImplements(CompilerPassInterface::class, BuildConsumptionExtensionsPass::class); - } - - public function testCouldBeConstructedWithoutAnyArguments() - { - new BuildConsumptionExtensionsPass(); - } - - public function testShouldReplaceFirstArgumentOfExtensionsServiceConstructorWithTagsExtensions() - { - $container = new ContainerBuilder(); - - $extensions = new Definition(); - $extensions->addArgument([]); - $container->setDefinition('enqueue.consumption.extensions', $extensions); - - $extension = new Definition(); - $extension->addTag('enqueue.consumption.extension'); - $container->setDefinition('foo_extension', $extension); - - $extension = new Definition(); - $extension->addTag('enqueue.consumption.extension'); - $container->setDefinition('bar_extension', $extension); - - $pass = new BuildConsumptionExtensionsPass(); - $pass->process($container); - - $this->assertEquals( - [new Reference('foo_extension'), new Reference('bar_extension')], - $extensions->getArgument(0) - ); - } - - public function testShouldOrderExtensionsByPriority() - { - $container = new ContainerBuilder(); - - $extensions = new Definition(); - $extensions->addArgument([]); - $container->setDefinition('enqueue.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(); - $pass->process($container); - - $orderedExtensions = $extensions->getArgument(0); - - $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.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(); - $pass->process($container); - - $orderedExtensions = $extensions->getArgument(0); - - $this->assertEquals(new Reference('bar_extension'), $orderedExtensions[0]); - $this->assertEquals(new Reference('foo_extension'), $orderedExtensions[1]); - $this->assertEquals(new Reference('baz_extension'), $orderedExtensions[2]); - } -} diff --git a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildProcessorRegistryPassTest.php b/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildProcessorRegistryPassTest.php deleted file mode 100644 index 9c7a00cdd..000000000 --- a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildProcessorRegistryPassTest.php +++ /dev/null @@ -1,293 +0,0 @@ -createContainerBuilder(); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor', [ - 'topicName' => 'topic', - 'processorName' => 'processor-name', - ]); - $container->setDefinition('processor-id', $processor); - - $processorRegistry = new Definition(); - $processorRegistry->setArguments([]); - $container->setDefinition('enqueue.client.processor_registry', $processorRegistry); - - $pass = new BuildProcessorRegistryPass(); - $pass->process($container); - - $expectedValue = [ - 'processor-name' => 'processor-id', - ]; - - $this->assertEquals($expectedValue, $processorRegistry->getArgument(0)); - } - - public function testThrowIfProcessorClassNameCouldNotBeFound() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition('notExistingClass'); - $processor->addTag('enqueue.client.processor', [ - 'processorName' => 'processor', - ]); - $container->setDefinition('processor', $processor); - - $processorRegistry = new Definition(); - $processorRegistry->setArguments([]); - $container->setDefinition('enqueue.client.processor_registry', $processorRegistry); - - $pass = new BuildProcessorRegistryPass(); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('The class "notExistingClass" could not be found.'); - $pass->process($container); - } - - public function testShouldThrowExceptionIfTopicNameIsNotSet() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor', $processor); - - $processorRegistry = new Definition(); - $processorRegistry->setArguments([]); - $container->setDefinition('enqueue.client.processor_registry', $processorRegistry); - - $pass = new BuildProcessorRegistryPass(); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('Topic name is not set on message processor tag but it is required.'); - $pass->process($container); - } - - public function testShouldSetServiceIdAdProcessorIdIfIsNotSetInTag() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor', [ - 'topicName' => 'topic', - ]); - $container->setDefinition('processor-id', $processor); - - $processorRegistry = new Definition(); - $processorRegistry->setArguments([]); - $container->setDefinition('enqueue.client.processor_registry', $processorRegistry); - - $pass = new BuildProcessorRegistryPass(); - $pass->process($container); - - $expectedValue = [ - 'processor-id' => 'processor-id', - ]; - - $this->assertEquals($expectedValue, $processorRegistry->getArgument(0)); - } - - public function testShouldBuildRouteFromSubscriberIfOnlyTopicNameSpecified() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(OnlyTopicNameTopicSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-id', $processor); - - $processorRegistry = new Definition(); - $processorRegistry->setArguments([]); - $container->setDefinition('enqueue.client.processor_registry', $processorRegistry); - - $pass = new BuildProcessorRegistryPass(); - $pass->process($container); - - $expectedValue = [ - 'processor-id' => 'processor-id', - ]; - - $this->assertEquals($expectedValue, $processorRegistry->getArgument(0)); - } - - public function testShouldBuildRouteFromWithoutProcessorNameTopicSubscriber() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(WithoutProcessorNameTopicSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-id', $processor); - - $processorRegistry = new Definition(); - $processorRegistry->setArguments([]); - $container->setDefinition('enqueue.client.processor_registry', $processorRegistry); - - $pass = new BuildProcessorRegistryPass(); - $pass->process($container); - - $expectedValue = [ - 'processor-id' => 'processor-id', - ]; - - $this->assertEquals($expectedValue, $processorRegistry->getArgument(0)); - } - - public function testShouldBuildRouteFromSubscriberIfProcessorNameSpecified() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(ProcessorNameTopicSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-id', $processor); - - $processorRegistry = new Definition(); - $processorRegistry->setArguments([]); - $container->setDefinition('enqueue.client.processor_registry', $processorRegistry); - - $pass = new BuildProcessorRegistryPass(); - $pass->process($container); - - $expectedValue = [ - 'subscriber-processor-name' => 'processor-id', - ]; - - $this->assertEquals($expectedValue, $processorRegistry->getArgument(0)); - } - - public function testShouldThrowExceptionWhenTopicSubscriberConfigurationIsInvalid() - { - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('Topic subscriber configuration is invalid'); - - $container = $this->createContainerBuilder(); - - $processor = new Definition(InvalidTopicSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-id', $processor); - - $processorRegistry = new Definition(); - $processorRegistry->setArguments([]); - $container->setDefinition('enqueue.client.processor_registry', $processorRegistry); - - $pass = new BuildProcessorRegistryPass(); - $pass->process($container); - } - - public function testShouldBuildRouteFromOnlyNameCommandSubscriber() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(OnlyCommandNameSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-id', $processor); - - $processorRegistry = new Definition(); - $processorRegistry->setArguments([]); - $container->setDefinition('enqueue.client.processor_registry', $processorRegistry); - - $pass = new BuildProcessorRegistryPass(); - $pass->process($container); - - $expectedValue = [ - 'the-command-name' => 'processor-id', - ]; - - $this->assertEquals($expectedValue, $processorRegistry->getArgument(0)); - } - - public function testShouldBuildRouteFromProcessorNameCommandSubscriber() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(ProcessorNameCommandSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-id', $processor); - - $processorRegistry = new Definition(); - $processorRegistry->setArguments([]); - $container->setDefinition('enqueue.client.processor_registry', $processorRegistry); - - $pass = new BuildProcessorRegistryPass(); - $pass->process($container); - - $expectedValue = [ - 'the-command-name' => 'processor-id', - ]; - - $this->assertEquals($expectedValue, $processorRegistry->getArgument(0)); - } - - public function testShouldThrowExceptionWhenProcessorNameEmpty() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(EmptyCommandSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-id', $processor); - - $processorRegistry = new Definition(); - $processorRegistry->setArguments([]); - $container->setDefinition('enqueue.client.processor_registry', $processorRegistry); - - $pass = new BuildProcessorRegistryPass(); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('The processor name (it is also the command name) must not be empty.'); - - $pass->process($container); - } - - public function testShouldThrowExceptionWhenCommandSubscriberConfigurationIsInvalid() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(InvalidCommandSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-id', $processor); - - $processorRegistry = new Definition(); - $processorRegistry->setArguments([]); - $container->setDefinition('enqueue.client.processor_registry', $processorRegistry); - - $pass = new BuildProcessorRegistryPass(); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('Command subscriber configuration is invalid. "12345"'); - - $pass->process($container); - } - - /** - * @return ContainerBuilder - */ - private function createContainerBuilder() - { - $container = new ContainerBuilder(); - $container->setParameter('enqueue.client.default_queue_name', 'aDefaultQueueName'); - - return $container; - } -} diff --git a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildQueueMetaRegistryPassTest.php b/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildQueueMetaRegistryPassTest.php deleted file mode 100644 index 46c315a78..000000000 --- a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildQueueMetaRegistryPassTest.php +++ /dev/null @@ -1,252 +0,0 @@ -createContainerBuilder(); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor', [ - 'processorName' => 'processor', - ]); - $container->setDefinition('processor', $processor); - - $pass = new BuildQueueMetaRegistryPass(); - $pass->process($container); - } - - public function testThrowIfProcessorClassNameCouldNotBeFound() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition('notExistingClass'); - $processor->addTag('enqueue.client.processor', [ - 'processorName' => 'processor', - ]); - $container->setDefinition('processor', $processor); - - $registry = new Definition(); - $registry->setArguments([null, []]); - $container->setDefinition(QueueMetaRegistry::class, $registry); - - $pass = new BuildQueueMetaRegistryPass(); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('The class "notExistingClass" could not be found.'); - $pass->process($container); - } - - public function testShouldBuildQueueMetaRegistry() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor', [ - 'processorName' => 'theProcessorName', - 'topicName' => 'aTopicName', - ]); - $container->setDefinition('processor', $processor); - - $registry = new Definition(); - $registry->setArguments([null, []]); - $container->setDefinition(QueueMetaRegistry::class, $registry); - - $pass = new BuildQueueMetaRegistryPass(); - $pass->process($container); - - $expectedQueues = [ - 'aDefaultQueueName' => ['processors' => ['theProcessorName']], - ]; - - $this->assertEquals($expectedQueues, $registry->getArgument(1)); - } - - public function testShouldSetServiceIdAdProcessorIdIfIsNotSetInTag() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor', [ - 'topicName' => 'aTopicName', - ]); - $container->setDefinition('processor-service-id', $processor); - - $registry = new Definition(); - $registry->setArguments([null, []]); - $container->setDefinition(QueueMetaRegistry::class, $registry); - - $pass = new BuildQueueMetaRegistryPass(); - $pass->process($container); - - $expectedQueues = [ - 'aDefaultQueueName' => ['processors' => ['processor-service-id']], - ]; - - $this->assertEquals($expectedQueues, $registry->getArgument(1)); - } - - public function testShouldSetQueueIfSetInTag() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor', [ - 'queueName' => 'theClientQueueName', - 'topicName' => 'aTopicName', - ]); - $container->setDefinition('processor-service-id', $processor); - - $registry = new Definition(); - $registry->setArguments([null, []]); - $container->setDefinition(QueueMetaRegistry::class, $registry); - - $pass = new BuildQueueMetaRegistryPass(); - $pass->process($container); - - $expectedQueues = [ - 'theClientQueueName' => ['processors' => ['processor-service-id']], - ]; - - $this->assertEquals($expectedQueues, $registry->getArgument(1)); - } - - public function testShouldBuildQueueFromSubscriberIfOnlyTopicNameSpecified() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(OnlyTopicNameTopicSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-service-id', $processor); - - $registry = new Definition(); - $registry->setArguments([null, []]); - $container->setDefinition(QueueMetaRegistry::class, $registry); - - $pass = new BuildQueueMetaRegistryPass(); - $pass->process($container); - - $expectedQueues = [ - 'aDefaultQueueName' => ['processors' => ['processor-service-id']], - ]; - - $this->assertEquals($expectedQueues, $registry->getArgument(1)); - } - - public function testShouldBuildQueueFromSubscriberIfProcessorNameSpecified() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(ProcessorNameTopicSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-service-id', $processor); - - $registry = new Definition(); - $registry->setArguments([null, []]); - $container->setDefinition(QueueMetaRegistry::class, $registry); - - $pass = new BuildQueueMetaRegistryPass(); - $pass->process($container); - - $expectedQueues = [ - 'aDefaultQueueName' => ['processors' => ['subscriber-processor-name']], - ]; - - $this->assertEquals($expectedQueues, $registry->getArgument(1)); - } - - public function testShouldBuildQueueFromSubscriberIfQueueNameSpecified() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(QueueNameTopicSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-service-id', $processor); - - $registry = new Definition(); - $registry->setArguments([null, []]); - $container->setDefinition(QueueMetaRegistry::class, $registry); - - $pass = new BuildQueueMetaRegistryPass(); - $pass->process($container); - - $expectedQueues = [ - 'subscriber-queue-name' => ['processors' => ['processor-service-id']], - ]; - - $this->assertEquals($expectedQueues, $registry->getArgument(1)); - } - - public function testShouldBuildQueueFromCommandSubscriber() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(ProcessorNameCommandSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-service-id', $processor); - - $registry = new Definition(); - $registry->setArguments([null, []]); - $container->setDefinition(QueueMetaRegistry::class, $registry); - - $pass = new BuildQueueMetaRegistryPass(); - $pass->process($container); - - $expectedQueues = [ - 'the-command-queue-name' => ['processors' => ['the-command-name']], - ]; - - $this->assertEquals($expectedQueues, $registry->getArgument(1)); - } - - public function testShouldBuildQueueFromOnlyCommandNameSubscriber() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(OnlyCommandNameSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-service-id', $processor); - - $registry = new Definition(); - $registry->setArguments([null, []]); - $container->setDefinition(QueueMetaRegistry::class, $registry); - - $pass = new BuildQueueMetaRegistryPass(); - $pass->process($container); - - $expectedQueues = [ - 'aDefaultQueueName' => ['processors' => ['the-command-name']], - ]; - - $this->assertEquals($expectedQueues, $registry->getArgument(1)); - } - - /** - * @return ContainerBuilder - */ - private function createContainerBuilder() - { - $container = new ContainerBuilder(); - $container->setParameter('enqueue.client.default_queue_name', 'aDefaultQueueName'); - - return $container; - } -} diff --git a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildTopicMetaSubscribersPassTest.php b/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildTopicMetaSubscribersPassTest.php deleted file mode 100644 index 7e166b06d..000000000 --- a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/BuildTopicMetaSubscribersPassTest.php +++ /dev/null @@ -1,364 +0,0 @@ -createContainerBuilder(); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor', [ - 'topicName' => 'topic', - 'processorName' => 'processor-name', - ]); - $container->setDefinition('processor-id', $processor); - - $topicMetaRegistry = new Definition(); - $topicMetaRegistry->setArguments([[]]); - $container->setDefinition(TopicMetaRegistry::class, $topicMetaRegistry); - - $pass = new BuildTopicMetaSubscribersPass(); - $pass->process($container); - - $expectedValue = [ - 'topic' => ['processors' => ['processor-name']], - ]; - - $this->assertEquals($expectedValue, $topicMetaRegistry->getArgument(0)); - } - - public function testThrowIfProcessorClassNameCouldNotBeFound() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition('notExistingClass'); - $processor->addTag('enqueue.client.processor', [ - 'processorName' => 'processor', - ]); - $container->setDefinition('processor', $processor); - - $topicMetaRegistry = new Definition(); - $topicMetaRegistry->setArguments([[]]); - $container->setDefinition(TopicMetaRegistry::class, $topicMetaRegistry); - - $pass = new BuildTopicMetaSubscribersPass(); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('The class "notExistingClass" could not be found.'); - $pass->process($container); - } - - public function testShouldBuildTopicMetaSubscribersForOneTagAndSameMetaInRegistry() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor', [ - 'topicName' => 'topic', - 'processorName' => 'barProcessorName', - ]); - $container->setDefinition('processor-id', $processor); - - $topicMetaRegistry = new Definition(); - $topicMetaRegistry->setArguments([[ - 'topic' => ['description' => 'aDescription', 'processors' => ['fooProcessorName']], - ]]); - $container->setDefinition(TopicMetaRegistry::class, $topicMetaRegistry); - - $pass = new BuildTopicMetaSubscribersPass(); - $pass->process($container); - - $expectedValue = [ - 'topic' => [ - 'description' => 'aDescription', - 'processors' => ['fooProcessorName', 'barProcessorName'], - ], - ]; - - $this->assertEquals($expectedValue, $topicMetaRegistry->getArgument(0)); - } - - public function testShouldBuildTopicMetaSubscribersForOneTagAndSameMetaInPlusAnotherRegistry() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor', [ - 'topicName' => 'fooTopic', - 'processorName' => 'barProcessorName', - ]); - $container->setDefinition('processor-id', $processor); - - $topicMetaRegistry = new Definition(); - $topicMetaRegistry->setArguments([[ - 'fooTopic' => ['description' => 'aDescription', 'processors' => ['fooProcessorName']], - 'barTopic' => ['description' => 'aBarDescription'], - ]]); - $container->setDefinition(TopicMetaRegistry::class, $topicMetaRegistry); - - $pass = new BuildTopicMetaSubscribersPass(); - $pass->process($container); - - $expectedValue = [ - 'fooTopic' => [ - 'description' => 'aDescription', - 'processors' => ['fooProcessorName', 'barProcessorName'], - ], - 'barTopic' => ['description' => 'aBarDescription'], - ]; - - $this->assertEquals($expectedValue, $topicMetaRegistry->getArgument(0)); - } - - public function testShouldBuildTopicMetaSubscribersForTwoTagAndEmptyRegistry() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor', [ - 'topicName' => 'fooTopic', - 'processorName' => 'fooProcessorName', - ]); - $container->setDefinition('processor-id', $processor); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor', [ - 'topicName' => 'fooTopic', - 'processorName' => 'barProcessorName', - ]); - $container->setDefinition('another-processor-id', $processor); - - $topicMetaRegistry = new Definition(); - $topicMetaRegistry->setArguments([[]]); - $container->setDefinition(TopicMetaRegistry::class, $topicMetaRegistry); - - $pass = new BuildTopicMetaSubscribersPass(); - $pass->process($container); - - $expectedValue = [ - 'fooTopic' => [ - 'processors' => ['fooProcessorName', 'barProcessorName'], - ], - ]; - - $this->assertEquals($expectedValue, $topicMetaRegistry->getArgument(0)); - } - - public function testShouldBuildTopicMetaSubscribersForTwoTagSameMetaRegistry() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor', [ - 'topicName' => 'fooTopic', - 'processorName' => 'fooProcessorName', - ]); - $container->setDefinition('processor-id', $processor); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor', [ - 'topicName' => 'fooTopic', - 'processorName' => 'barProcessorName', - ]); - $container->setDefinition('another-processor-id', $processor); - - $topicMetaRegistry = new Definition(); - $topicMetaRegistry->setArguments([[ - 'fooTopic' => ['description' => 'aDescription', 'processors' => ['bazProcessorName']], - ]]); - $container->setDefinition(TopicMetaRegistry::class, $topicMetaRegistry); - - $pass = new BuildTopicMetaSubscribersPass(); - $pass->process($container); - - $expectedValue = [ - 'fooTopic' => [ - 'description' => 'aDescription', - 'processors' => ['bazProcessorName', 'fooProcessorName', 'barProcessorName'], - ], - ]; - - $this->assertEquals($expectedValue, $topicMetaRegistry->getArgument(0)); - } - - public function testThrowIfTopicNameNotSetOnTagAsAttribute() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor', []); - $container->setDefinition('processor', $processor); - - $topicMetaRegistry = new Definition(); - $topicMetaRegistry->setArguments([[]]); - $container->setDefinition(TopicMetaRegistry::class, $topicMetaRegistry); - - $pass = new BuildTopicMetaSubscribersPass(); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('Topic name is not set on message processor tag but it is required.'); - $pass->process($container); - } - - public function testShouldSetServiceIdAdProcessorIdIfIsNotSetInTag() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(\stdClass::class); - $processor->addTag('enqueue.client.processor', [ - 'topicName' => 'topic', - ]); - $container->setDefinition('processor-id', $processor); - - $topicMetaRegistry = new Definition(); - $topicMetaRegistry->setArguments([[]]); - $container->setDefinition(TopicMetaRegistry::class, $topicMetaRegistry); - - $pass = new BuildTopicMetaSubscribersPass(); - $pass->process($container); - - $expectedValue = [ - 'topic' => ['processors' => ['processor-id']], - ]; - - $this->assertEquals($expectedValue, $topicMetaRegistry->getArgument(0)); - } - - public function testShouldBuildMetaFromSubscriberIfOnlyTopicNameSpecified() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(OnlyTopicNameTopicSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-id', $processor); - - $topicMetaRegistry = new Definition(); - $topicMetaRegistry->setArguments([[]]); - $container->setDefinition(TopicMetaRegistry::class, $topicMetaRegistry); - - $pass = new BuildTopicMetaSubscribersPass(); - $pass->process($container); - - $expectedValue = [ - 'topic-subscriber-name' => ['processors' => ['processor-id']], - ]; - - $this->assertEquals($expectedValue, $topicMetaRegistry->getArgument(0)); - } - - public function testShouldBuildMetaFromSubscriberIfProcessorNameSpecified() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(ProcessorNameTopicSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-id', $processor); - - $topicMetaRegistry = new Definition(); - $topicMetaRegistry->setArguments([[]]); - $container->setDefinition(TopicMetaRegistry::class, $topicMetaRegistry); - - $pass = new BuildTopicMetaSubscribersPass(); - $pass->process($container); - - $expectedValue = [ - 'topic-subscriber-name' => ['processors' => ['subscriber-processor-name']], - ]; - - $this->assertEquals($expectedValue, $topicMetaRegistry->getArgument(0)); - } - - public function testShouldThrowExceptionWhenTopicSubscriberConfigurationIsInvalid() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(InvalidTopicSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-id', $processor); - - $topicMetaRegistry = new Definition(); - $topicMetaRegistry->setArguments([[]]); - $container->setDefinition(TopicMetaRegistry::class, $topicMetaRegistry); - - $pass = new BuildTopicMetaSubscribersPass(); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('Topic subscriber configuration is invalid'); - - $pass->process($container); - } - - public function testShouldBuildMetaFromCommandSubscriberIfOnlyCommandNameSpecified() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(OnlyCommandNameSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-id', $processor); - - $topicMetaRegistry = new Definition(); - $topicMetaRegistry->setArguments([[]]); - $container->setDefinition(TopicMetaRegistry::class, $topicMetaRegistry); - - $pass = new BuildTopicMetaSubscribersPass(); - $pass->process($container); - - $expectedValue = [ - Config::COMMAND_TOPIC => ['processors' => ['the-command-name']], - ]; - - $this->assertEquals($expectedValue, $topicMetaRegistry->getArgument(0)); - } - - public function testShouldBuildMetaFromCommandSubscriberIfProcessorNameSpecified() - { - $container = $this->createContainerBuilder(); - - $processor = new Definition(ProcessorNameCommandSubscriber::class); - $processor->addTag('enqueue.client.processor'); - $container->setDefinition('processor-id', $processor); - - $topicMetaRegistry = new Definition(); - $topicMetaRegistry->setArguments([[]]); - $container->setDefinition(TopicMetaRegistry::class, $topicMetaRegistry); - - $pass = new BuildTopicMetaSubscribersPass(); - $pass->process($container); - - $expectedValue = [ - Config::COMMAND_TOPIC => ['processors' => ['the-command-name']], - ]; - - $this->assertEquals($expectedValue, $topicMetaRegistry->getArgument(0)); - } - - /** - * @return ContainerBuilder - */ - private function createContainerBuilder() - { - $container = new ContainerBuilder(); - $container->setParameter('enqueue.client.default_queue_name', 'aDefaultQueueName'); - - return $container; - } -} diff --git a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/EmptyCommandSubscriber.php b/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/EmptyCommandSubscriber.php deleted file mode 100644 index 27fb16138..000000000 --- a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/EmptyCommandSubscriber.php +++ /dev/null @@ -1,13 +0,0 @@ - 'the-exclusive-command-name', - 'queueName' => 'the-queue-name', - 'queueNameHardCoded' => false, - 'exclusive' => true, - ]; - } -} diff --git a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/ExclusiveCommandSubscriber.php b/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/ExclusiveCommandSubscriber.php deleted file mode 100644 index 742758a81..000000000 --- a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/ExclusiveCommandSubscriber.php +++ /dev/null @@ -1,18 +0,0 @@ - 'the-exclusive-command-name', - 'queueName' => 'the-queue-name', - 'queueNameHardcoded' => true, - 'exclusive' => true, - ]; - } -} diff --git a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/InvalidCommandSubscriber.php b/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/InvalidCommandSubscriber.php deleted file mode 100644 index 44b32a4de..000000000 --- a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/InvalidCommandSubscriber.php +++ /dev/null @@ -1,13 +0,0 @@ - 'the-command-name', - 'queueName' => 'the-command-queue-name', - ]; - } -} diff --git a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/ProcessorNameTopicSubscriber.php b/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/ProcessorNameTopicSubscriber.php deleted file mode 100644 index 9574ee4e4..000000000 --- a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/ProcessorNameTopicSubscriber.php +++ /dev/null @@ -1,17 +0,0 @@ - [ - 'processorName' => 'subscriber-processor-name', - ], - ]; - } -} diff --git a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/QueueNameTopicSubscriber.php b/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/QueueNameTopicSubscriber.php deleted file mode 100644 index 6ed163d13..000000000 --- a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/QueueNameTopicSubscriber.php +++ /dev/null @@ -1,17 +0,0 @@ - [ - 'queueName' => 'subscriber-queue-name', - ], - ]; - } -} diff --git a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/WithoutProcessorNameTopicSubscriber.php b/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/WithoutProcessorNameTopicSubscriber.php deleted file mode 100644 index fa30d9f78..000000000 --- a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/Compiler/Mock/WithoutProcessorNameTopicSubscriber.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'queueName' => 'a_queue_name', - 'queueNameHardcoded' => true, - ], - ]; - } -} diff --git a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/ConfigurationTest.php b/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/ConfigurationTest.php index 828720297..5c4a20b73 100644 --- a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/ConfigurationTest.php +++ b/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/ConfigurationTest.php @@ -48,7 +48,7 @@ public function testShouldUseDefaultConfigurationIfNothingIsConfiguredAtAll() 'extensions' => [ 'doctrine_ping_connection_extension' => false, 'doctrine_clear_identity_map_extension' => false, - 'signal_extension' => true, + 'signal_extension' => function_exists('pcntl_signal_dispatch'), 'reply_extension' => true, ], ], $config); @@ -75,7 +75,7 @@ public function testShouldUseDefaultTransportIfIfTransportIsConfiguredAtAll() 'extensions' => [ 'doctrine_ping_connection_extension' => false, 'doctrine_clear_identity_map_extension' => false, - 'signal_extension' => true, + 'signal_extension' => function_exists('pcntl_signal_dispatch'), 'reply_extension' => true, ], ], $config); diff --git a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/EnqueueExtensionTest.php b/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/EnqueueExtensionTest.php index ab26ee093..b779e50dc 100644 --- a/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/EnqueueExtensionTest.php +++ b/pkg/enqueue-bundle/Tests/Unit/DependencyInjection/EnqueueExtensionTest.php @@ -87,8 +87,7 @@ public function testShouldLoadClientServicesWhenEnabled() ]], $container); self::assertTrue($container->hasDefinition('enqueue.client.default.driver')); - self::assertTrue($container->hasDefinition('enqueue.client.config')); - self::assertTrue($container->hasDefinition(Producer::class)); + self::assertTrue($container->hasDefinition('enqueue.client.default.config')); self::assertTrue($container->hasAlias(ProducerInterface::class)); } @@ -103,7 +102,7 @@ public function testShouldUseProducerByDefault() 'transport' => 'null', ]], $container); - $producer = $container->getDefinition(Producer::class); + $producer = $container->getDefinition('enqueue.client.default.producer'); self::assertEquals(Producer::class, $producer->getClass()); } @@ -120,7 +119,7 @@ public function testShouldUseMessageProducerIfTraceableProducerOptionSetToFalseE 'transport' => 'null:', ]], $container); - $producer = $container->getDefinition(Producer::class); + $producer = $container->getDefinition('enqueue.client.default.producer'); self::assertEquals(Producer::class, $producer->getClass()); } @@ -135,16 +134,16 @@ public function testShouldUseTraceableMessageProducerIfDebugEnabled() 'client' => null, ]], $container); - $producer = $container->getDefinition(TraceableProducer::class); + $producer = $container->getDefinition('enqueue.client.default.traceable_producer'); self::assertEquals(TraceableProducer::class, $producer->getClass()); self::assertEquals( - [Producer::class, null, 0], + ['enqueue.client.default.producer', null, 0], $producer->getDecoratedService() ); self::assertInstanceOf(Reference::class, $producer->getArgument(0)); - $innerServiceName = sprintf('%s.inner', TraceableProducer::class); + $innerServiceName = 'enqueue.client.default.traceable_producer.inner'; self::assertEquals( $innerServiceName, @@ -162,7 +161,7 @@ public function testShouldNotUseTraceableMessageProducerIfDebugDisabledAndNotSet 'transport' => 'null:', ]], $container); - $this->assertFalse($container->hasDefinition(TraceableProducer::class)); + $this->assertFalse($container->hasDefinition('enqueue.client.default.traceable_producer')); } public function testShouldUseTraceableMessageProducerIfDebugDisabledButTraceableProducerOptionSetToTrueExplicitly() @@ -178,16 +177,16 @@ public function testShouldUseTraceableMessageProducerIfDebugDisabledButTraceable 'transport' => 'null:', ]], $container); - $producer = $container->getDefinition(TraceableProducer::class); + $producer = $container->getDefinition('enqueue.client.default.traceable_producer'); self::assertEquals(TraceableProducer::class, $producer->getClass()); self::assertEquals( - [Producer::class, null, 0], + ['enqueue.client.default.producer', null, 0], $producer->getDecoratedService() ); self::assertInstanceOf(Reference::class, $producer->getArgument(0)); - $innerServiceName = sprintf('%s.inner', TraceableProducer::class); + $innerServiceName = 'enqueue.client.default.traceable_producer.inner'; self::assertEquals( $innerServiceName, @@ -208,7 +207,7 @@ public function testShouldLoadDelayRedeliveredMessageExtensionIfRedeliveredDelay ], ]], $container); - $extension = $container->getDefinition('enqueue.client.delay_redelivered_message_extension'); + $extension = $container->getDefinition('enqueue.client.default.delay_redelivered_message_extension'); self::assertEquals(12345, $extension->getArgument(1)); } @@ -226,7 +225,7 @@ public function testShouldNotLoadDelayRedeliveredMessageExtensionIfRedeliveredDe ], ]], $container); - $this->assertFalse($container->hasDefinition('enqueue.client.delay_redelivered_message_extension')); + $this->assertFalse($container->hasDefinition('enqueue.client.default.delay_redelivered_message_extension')); } public function testShouldLoadJobServicesIfEnabled() @@ -441,7 +440,7 @@ public function testShouldConfigureQueueConsumer() $this->assertSame(123, $def->getArgument(2)); $this->assertSame(456, $def->getArgument(3)); - $def = $container->getDefinition('enqueue.client.queue_consumer'); + $def = $container->getDefinition('enqueue.client.default.queue_consumer'); $this->assertSame(123, $def->getArgument(2)); $this->assertSame(456, $def->getArgument(3)); } @@ -459,11 +458,11 @@ public function testShouldLoadProcessAutoconfigureChildDefinition() $autoconfigured = $container->getAutoconfiguredInstanceof(); self::assertArrayHasKey(CommandSubscriberInterface::class, $autoconfigured); - self::assertTrue($autoconfigured[CommandSubscriberInterface::class]->hasTag('enqueue.client.processor')); + self::assertTrue($autoconfigured[CommandSubscriberInterface::class]->hasTag('enqueue.command_subscriber')); self::assertTrue($autoconfigured[CommandSubscriberInterface::class]->isPublic()); self::assertArrayHasKey(TopicSubscriberInterface::class, $autoconfigured); - self::assertTrue($autoconfigured[TopicSubscriberInterface::class]->hasTag('enqueue.client.processor')); + self::assertTrue($autoconfigured[TopicSubscriberInterface::class]->hasTag('enqueue.topic_subscriber')); self::assertTrue($autoconfigured[TopicSubscriberInterface::class]->isPublic()); } diff --git a/pkg/enqueue-bundle/Tests/Unit/Profiler/MessageQueueCollectorTest.php b/pkg/enqueue-bundle/Tests/Unit/Profiler/MessageQueueCollectorTest.php index b32cf8c28..bdc21bf29 100644 --- a/pkg/enqueue-bundle/Tests/Unit/Profiler/MessageQueueCollectorTest.php +++ b/pkg/enqueue-bundle/Tests/Unit/Profiler/MessageQueueCollectorTest.php @@ -68,7 +68,7 @@ public function testShouldReturnSentMessageArrayTakenFromTraceableProducer() 'messageId' => null, ], [ - 'topic' => '__command__', + 'topic' => null, 'command' => 'barCommand', 'body' => 'barMessage', 'headers' => [], From dfc14121100802933fa2adcaa29118984f1d6e0b Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Thu, 27 Sep 2018 14:58:22 +0300 Subject: [PATCH 31/31] [simple-client] Do not use Symfony Container --- pkg/simple-client/SimpleClient.php | 207 ++++++++++++------ .../SimpleClientContainerExtension.php | 164 -------------- .../Tests/Functional/SimpleClientTest.php | 2 +- pkg/simple-client/composer.json | 4 +- 4 files changed, 144 insertions(+), 233 deletions(-) delete mode 100644 pkg/simple-client/SimpleClientContainerExtension.php diff --git a/pkg/simple-client/SimpleClient.php b/pkg/simple-client/SimpleClient.php index 7112ad362..bdcd9a63d 100644 --- a/pkg/simple-client/SimpleClient.php +++ b/pkg/simple-client/SimpleClient.php @@ -3,35 +3,59 @@ namespace Enqueue\SimpleClient; use Enqueue\Client\ArrayProcessorRegistry; +use Enqueue\Client\ChainExtension as ClientChainExtensions; use Enqueue\Client\Config; +use Enqueue\Client\ConsumptionExtension\DelayRedeliveredMessageExtension; +use Enqueue\Client\ConsumptionExtension\SetRouterPropertiesExtension; use Enqueue\Client\DelegateProcessor; +use Enqueue\Client\DriverFactory; use Enqueue\Client\DriverInterface; use Enqueue\Client\Message; -use Enqueue\Client\ProcessorRegistryInterface; +use Enqueue\Client\Producer; use Enqueue\Client\ProducerInterface; use Enqueue\Client\Route; use Enqueue\Client\RouteCollection; use Enqueue\Client\RouterProcessor; +use Enqueue\ConnectionFactoryFactory; use Enqueue\Consumption\CallbackProcessor; +use Enqueue\Consumption\ChainExtension as ConsumptionChainExtension; use Enqueue\Consumption\ExtensionInterface; +use Enqueue\Consumption\QueueConsumer; use Enqueue\Consumption\QueueConsumerInterface; use Enqueue\Rpc\Promise; -use Interop\Queue\PsrContext; +use Enqueue\Rpc\RpcFactory; +use Enqueue\Symfony\DependencyInjection\TransportFactory; use Interop\Queue\PsrProcessor; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\NodeInterface; +use Symfony\Component\Config\Definition\Processor; final class SimpleClient { /** - * @var ContainerInterface + * @var DriverInterface */ - private $container; + private $driver; /** - * @var array|string + * @var Producer */ - private $config; + private $producer; + + /** + * @var QueueConsumer + */ + private $queueConsumer; + + /** + * @var ArrayProcessorRegistry + */ + private $processorRegistry; + + /** + * @var DelegateProcessor + */ + private $delegateProcessor; /** * The config could be a transport DSN (string) or an array, here's an example of a few DSNs:. @@ -78,13 +102,11 @@ final class SimpleClient * ] * * - * @param string|array $config - * @param ContainerBuilder|null $container + * @param string|array $config */ - public function __construct($config, ContainerBuilder $container = null) + public function __construct($config) { - $this->container = $this->buildContainer($config, $container ?: new ContainerBuilder()); - $this->config = $config; + $this->build(['enqueue' => $config]); } /** @@ -102,8 +124,8 @@ public function bindTopic(string $topic, $processor, string $processorName = nul $processorName = $processorName ?: uniqid(get_class($processor)); - $this->getRouteCollection()->add(new Route($topic, Route::TOPIC, $processorName)); - $this->getProcessorRegistry()->add($processorName, $processor); + $this->driver->getRouteCollection()->add(new Route($topic, Route::TOPIC, $processorName)); + $this->processorRegistry->add($processorName, $processor); } /** @@ -121,8 +143,8 @@ public function bindCommand(string $command, $processor, string $processorName = $processorName = $processorName ?: uniqid(get_class($processor)); - $this->getRouteCollection()->add(new Route($command, Route::COMMAND, $processorName)); - $this->getProcessorRegistry()->add($processorName, $processor); + $this->driver->getRouteCollection()->add(new Route($command, Route::COMMAND, $processorName)); + $this->processorRegistry->add($processorName, $processor); } /** @@ -130,7 +152,7 @@ public function bindCommand(string $command, $processor, string $processorName = */ public function sendCommand(string $command, $message, bool $needReply = false): ?Promise { - return $this->getProducer()->sendCommand($command, $message, $needReply); + return $this->producer->sendCommand($command, $message, $needReply); } /** @@ -138,61 +160,48 @@ public function sendCommand(string $command, $message, bool $needReply = false): */ public function sendEvent(string $topic, $message): void { - $this->getProducer()->sendEvent($topic, $message); + $this->producer->sendEvent($topic, $message); } public function consume(ExtensionInterface $runtimeExtension = null): void { $this->setupBroker(); - $processor = $this->getDelegateProcessor(); - $consumer = $this->getQueueConsumer(); - $boundQueues = []; - $routerQueue = $this->getDriver()->createQueue($this->getConfig()->getRouterQueueName()); - $consumer->bind($routerQueue, $processor); + $routerQueue = $this->getDriver()->createQueue($this->getDriver()->getConfig()->getRouterQueueName()); + $this->queueConsumer->bind($routerQueue, $this->delegateProcessor); $boundQueues[$routerQueue->getQueueName()] = true; - foreach ($this->getRouteCollection()->all() as $route) { + foreach ($this->driver->getRouteCollection()->all() as $route) { $queue = $this->getDriver()->createRouteQueue($route); if (array_key_exists($queue->getQueueName(), $boundQueues)) { continue; } - $consumer->bind($queue, $processor); + $this->queueConsumer->bind($queue, $this->delegateProcessor); $boundQueues[$queue->getQueueName()] = true; } - $consumer->consume($runtimeExtension); - } - - public function getContext(): PsrContext - { - return $this->container->get('enqueue.transport.context'); + $this->queueConsumer->consume($runtimeExtension); } public function getQueueConsumer(): QueueConsumerInterface { - return $this->container->get('enqueue.client.queue_consumer'); - } - - public function getConfig(): Config - { - return $this->container->get('enqueue.client.config'); + return $this->queueConsumer; } public function getDriver(): DriverInterface { - return $this->container->get('enqueue.client.default.driver'); + return $this->driver; } public function getProducer(bool $setupBroker = false): ProducerInterface { $setupBroker && $this->setupBroker(); - return $this->container->get('enqueue.client.producer'); + return $this->producer; } public function setupBroker(): void @@ -200,37 +209,105 @@ public function setupBroker(): void $this->getDriver()->setupBroker(); } - /** - * @return ArrayProcessorRegistry - */ - public function getProcessorRegistry(): ProcessorRegistryInterface + public function build(array $configs): void { - return $this->container->get('enqueue.client.processor_registry'); - } + $configProcessor = new Processor(); + $simpleClientConfig = $configProcessor->process($this->createConfiguration(), $configs); - public function getDelegateProcessor(): DelegateProcessor - { - return $this->container->get('enqueue.client.delegate_processor'); - } + if (isset($simpleClientConfig['transport']['factory_service'])) { + throw new \LogicException('transport.factory_service option is not supported by simple client'); + } + if (isset($simpleClientConfig['transport']['factory_class'])) { + throw new \LogicException('transport.factory_class option is not supported by simple client'); + } + if (isset($simpleClientConfig['transport']['connection_factory_class'])) { + throw new \LogicException('transport.connection_factory_class option is not supported by simple client'); + } - public function getRouterProcessor(): RouterProcessor - { - return $this->container->get('enqueue.client.router_processor'); - } + $connectionFactoryFactory = new ConnectionFactoryFactory(); + $connection = $connectionFactoryFactory->create($simpleClientConfig['transport']); - public function getRouteCollection(): RouteCollection - { - return $this->container->get('enqueue.client.route_collection'); - } + $clientExtensions = new ClientChainExtensions([]); - private function buildContainer($config, ContainerBuilder $container): ContainerInterface - { - $extension = new SimpleClientContainerExtension(); - $container->registerExtension($extension); - $container->loadFromExtension($extension->getAlias(), $config); + $config = new Config( + $simpleClientConfig['client']['prefix'], + $simpleClientConfig['client']['app_name'], + $simpleClientConfig['client']['router_topic'], + $simpleClientConfig['client']['router_queue'], + $simpleClientConfig['client']['default_processor_queue'], + 'enqueue.client.router_processor', + $simpleClientConfig['transport'] + ); + $routeCollection = new RouteCollection([]); + $driverFactory = new DriverFactory($config, $routeCollection); + + $driver = $driverFactory->create( + $connection, + $simpleClientConfig['transport']['dsn'], + $simpleClientConfig['transport'] + ); + + $rpcFactory = new RpcFactory($driver->getContext()); + + $producer = new Producer($driver, $rpcFactory, $clientExtensions); - $container->compile(); + $processorRegistry = new ArrayProcessorRegistry([]); - return $container; + $delegateProcessor = new DelegateProcessor($processorRegistry); + + // consumption extensions + $consumptionExtensions = []; + if ($simpleClientConfig['client']['redelivered_delay_time']) { + $consumptionExtensions[] = new DelayRedeliveredMessageExtension($driver, $simpleClientConfig['client']['redelivered_delay_time']); + } + + $consumptionExtensions[] = new SetRouterPropertiesExtension($driver); + + $consumptionChainExtension = new ConsumptionChainExtension($consumptionExtensions); + $queueConsumer = new QueueConsumer($driver->getContext(), $consumptionChainExtension); + + $routerProcessor = new RouterProcessor($driver); + + $processorRegistry->add($config->getRouterProcessorName(), $routerProcessor); + + $this->driver = $driver; + $this->producer = $producer; + $this->queueConsumer = $queueConsumer; + $this->delegateProcessor = $delegateProcessor; + $this->processorRegistry = $processorRegistry; + } + + private function createConfiguration(): NodeInterface + { + $tb = new TreeBuilder(); + $rootNode = $tb->root('enqueue'); + + $rootNode + ->beforeNormalization() + ->ifEmpty()->then(function () { + return ['transport' => ['dsn' => 'null:']]; + }); + + $transportNode = $rootNode->children()->arrayNode('transport'); + (new TransportFactory('default'))->addConfiguration($transportNode); + + $rootNode->children() + ->arrayNode('client') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('prefix')->defaultValue('enqueue')->end() + ->scalarNode('app_name')->defaultValue('app')->end() + ->scalarNode('router_topic')->defaultValue(Config::DEFAULT_PROCESSOR_QUEUE_NAME)->cannotBeEmpty()->end() + ->scalarNode('router_queue')->defaultValue(Config::DEFAULT_PROCESSOR_QUEUE_NAME)->cannotBeEmpty()->end() + ->scalarNode('default_processor_queue')->defaultValue(Config::DEFAULT_PROCESSOR_QUEUE_NAME)->cannotBeEmpty()->end() + ->integerNode('redelivered_delay_time')->min(0)->defaultValue(0)->end() + ->end() + ->end() + ->arrayNode('extensions')->addDefaultsIfNotSet()->children() + ->booleanNode('signal_extension')->defaultValue(function_exists('pcntl_signal_dispatch'))->end() + ->end()->end() + ; + + return $tb->buildTree(); } } diff --git a/pkg/simple-client/SimpleClientContainerExtension.php b/pkg/simple-client/SimpleClientContainerExtension.php deleted file mode 100644 index 6ca86b86b..000000000 --- a/pkg/simple-client/SimpleClientContainerExtension.php +++ /dev/null @@ -1,164 +0,0 @@ -process($this->createConfiguration(), $configs); - - $container->register('enqueue.connection_factory_factory', ConnectionFactoryFactory::class); - - $container->register('enqueue.client.driver_factory', DriverFactory::class) - ->addArgument(new Reference('enqueue.client.config')) - ->addArgument(new Reference('enqueue.client.route_collection')) - ; - - $transportFactory = (new TransportFactory('default')); - $transportFactory->createConnectionFactory($container, $config['transport']); - $transportFactory->createContext($container, $config['transport']); - - $driverId = $transportFactory->createDriver($container, $config['transport']); - $container->getDefinition($driverId)->setPublic(true); - - $container->register('enqueue.client.config', Config::class) - ->setPublic(true) - ->setArguments([ - $config['client']['prefix'], - $config['client']['app_name'], - $config['client']['router_topic'], - $config['client']['router_queue'], - $config['client']['default_processor_queue'], - 'enqueue.client.router_processor', - $config['transport'], - ]) - ; - - $container->register('enqueue.client.route_collection', RouteCollection::class) - ->setPublic(true) - ->addArgument([]) - ; - - $container->register('enqueue.client.rpc_factory', RpcFactory::class) - ->setPublic(true) - ->setArguments([ - new Reference('enqueue.transport.default.context'), - ]) - ; - - $container->register('enqueue.client.producer', Producer::class) - ->setPublic(true) - ->setArguments([ - new Reference('enqueue.client.default.driver'), - new Reference('enqueue.client.rpc_factory'), - ]) - ; - - $container->register('enqueue.client.processor_registry', ArrayProcessorRegistry::class) - ->setPublic(true) - ; - - $container->register('enqueue.client.delegate_processor', DelegateProcessor::class) - ->setPublic(true) - ->setArguments([new Reference('enqueue.client.processor_registry')]); - - $container->register('enqueue.client.queue_consumer', QueueConsumer::class) - ->setPublic(true) - ->setArguments([ - new Reference('enqueue.transport.default.context'), - new Reference('enqueue.consumption.extensions'), - ]); - - // router - $container->register('enqueue.client.router_processor', RouterProcessor::class) - ->setPublic(true) - ->setArguments([new Reference('enqueue.client.default.driver'), []]); - $container->getDefinition('enqueue.client.processor_registry') - ->addMethodCall('add', ['enqueue.client.router_processor', new Reference('enqueue.client.router_processor')]); - - // extensions - $extensions = []; - if ($config['client']['redelivered_delay_time']) { - $container->register('enqueue.client.delay_redelivered_message_extension', DelayRedeliveredMessageExtension::class) - ->setPublic(true) - ->setArguments([ - new Reference('enqueue.client.default.driver'), - $config['client']['redelivered_delay_time'], - ]); - - $extensions[] = new Reference('enqueue.client.delay_redelivered_message_extension'); - } - - $container->register('enqueue.client.extension.set_router_properties', SetRouterPropertiesExtension::class) - ->setPublic(true) - ->setArguments([new Reference('enqueue.client.default.driver')]); - - $extensions[] = new Reference('enqueue.client.extension.set_router_properties'); - - $container->register('enqueue.consumption.extensions', ConsumptionChainExtension::class) - ->setPublic(true) - ->setArguments([$extensions]); - } - - private function createConfiguration(): NodeInterface - { - $tb = new TreeBuilder(); - $rootNode = $tb->root('enqueue'); - - $rootNode - ->beforeNormalization() - ->ifEmpty()->then(function () { - return ['transport' => ['dsn' => 'null:']]; - }); - - $transportNode = $rootNode->children()->arrayNode('transport'); - (new TransportFactory('default'))->addConfiguration($transportNode); - - $rootNode->children() - ->arrayNode('client') - ->addDefaultsIfNotSet() - ->children() - ->scalarNode('prefix')->defaultValue('enqueue')->end() - ->scalarNode('app_name')->defaultValue('app')->end() - ->scalarNode('router_topic')->defaultValue(Config::DEFAULT_PROCESSOR_QUEUE_NAME)->cannotBeEmpty()->end() - ->scalarNode('router_queue')->defaultValue(Config::DEFAULT_PROCESSOR_QUEUE_NAME)->cannotBeEmpty()->end() - ->scalarNode('default_processor_queue')->defaultValue(Config::DEFAULT_PROCESSOR_QUEUE_NAME)->cannotBeEmpty()->end() - ->integerNode('redelivered_delay_time')->min(0)->defaultValue(0)->end() - ->end() - ->end() - ->arrayNode('extensions')->addDefaultsIfNotSet()->children() - ->booleanNode('signal_extension')->defaultValue(function_exists('pcntl_signal_dispatch'))->end() - ->end()->end() - ; - - return $tb->buildTree(); - } -} diff --git a/pkg/simple-client/Tests/Functional/SimpleClientTest.php b/pkg/simple-client/Tests/Functional/SimpleClientTest.php index 25fdc4396..d486ccf62 100644 --- a/pkg/simple-client/Tests/Functional/SimpleClientTest.php +++ b/pkg/simple-client/Tests/Functional/SimpleClientTest.php @@ -184,7 +184,7 @@ protected function purgeQueue(SimpleClient $client): void $queue = $driver->createQueue($driver->getConfig()->getDefaultProcessorQueueName()); try { - $client->getContext()->purgeQueue($queue); + $client->getDriver()->getContext()->purgeQueue($queue); } catch (PurgeQueueNotSupportedException $e) { } } diff --git a/pkg/simple-client/composer.json b/pkg/simple-client/composer.json index aea1a2089..4f6c07a55 100644 --- a/pkg/simple-client/composer.json +++ b/pkg/simple-client/composer.json @@ -8,11 +8,9 @@ "require": { "php": "^7.1.3", "enqueue/enqueue": "0.9.x-dev", - "symfony/dependency-injection": "^3.4|^4", "queue-interop/amqp-interop": "0.8.x-dev", "queue-interop/queue-interop": "0.7.x-dev", - "symfony/config": "^3.4|^4", - "symfony/console": "^3.4|^4" + "symfony/config": "^3.4|^4" }, "require-dev": { "phpunit/phpunit": "~5.5",