From 6c0e31ddc8a27fe27b363332e558426968e6b77e Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Mon, 27 Aug 2018 13:28:14 +0300 Subject: [PATCH 1/7] [client] Improve client extension. --- pkg/enqueue/Client/ChainExtension.php | 28 ++- .../ExclusiveCommandExtension.php | 31 +-- pkg/enqueue/Client/EmptyExtensionTrait.php | 22 ++ .../Client/Extension/PrepareBodyExtension.php | 59 +++++ pkg/enqueue/Client/ExtensionInterface.php | 30 +-- pkg/enqueue/Client/PostSend.php | 49 ++++ pkg/enqueue/Client/PreDriverSend.php | 49 ++++ pkg/enqueue/Client/PreSend.php | 69 ++++++ pkg/enqueue/Client/Producer.php | 143 +++++------ .../Extension/PrepareBodyExtensionTest.php | 142 +++++++++++ pkg/enqueue/Tests/Client/ProducerTest.php | 227 ------------------ 11 files changed, 491 insertions(+), 358 deletions(-) create mode 100644 pkg/enqueue/Client/EmptyExtensionTrait.php create mode 100644 pkg/enqueue/Client/Extension/PrepareBodyExtension.php create mode 100644 pkg/enqueue/Client/PostSend.php create mode 100644 pkg/enqueue/Client/PreDriverSend.php create mode 100644 pkg/enqueue/Client/PreSend.php create mode 100644 pkg/enqueue/Tests/Client/Extension/PrepareBodyExtensionTest.php diff --git a/pkg/enqueue/Client/ChainExtension.php b/pkg/enqueue/Client/ChainExtension.php index c202e98e0..01bee2227 100644 --- a/pkg/enqueue/Client/ChainExtension.php +++ b/pkg/enqueue/Client/ChainExtension.php @@ -17,23 +17,31 @@ public function __construct(array $extensions) $this->extensions = $extensions; } - /** - * {@inheritdoc} - */ - public function onPreSend($topic, Message $message) + public function onPreSendEvent(PreSend $event): void { foreach ($this->extensions as $extension) { - $extension->onPreSend($topic, $message); + $extension->onPreSendEvent($event); } } - /** - * {@inheritdoc} - */ - public function onPostSend($topic, Message $message) + public function onPreSendCommand(PreSend $event): void + { + foreach ($this->extensions as $extension) { + $extension->onPreSendCommand($event); + } + } + + public function onPreDriverSend(PreDriverSend $context): void + { + foreach ($this->extensions as $extension) { + $extension->onPreDriverSend($context); + } + } + + public function onPostSend(PostSend $event): void { foreach ($this->extensions as $extension) { - $extension->onPostSend($topic, $message); + $extension->onPostSend($event); } } } diff --git a/pkg/enqueue/Client/ConsumptionExtension/ExclusiveCommandExtension.php b/pkg/enqueue/Client/ConsumptionExtension/ExclusiveCommandExtension.php index a9afde004..add6ef751 100644 --- a/pkg/enqueue/Client/ConsumptionExtension/ExclusiveCommandExtension.php +++ b/pkg/enqueue/Client/ConsumptionExtension/ExclusiveCommandExtension.php @@ -3,15 +3,16 @@ namespace Enqueue\Client\ConsumptionExtension; use Enqueue\Client\Config; +use Enqueue\Client\EmptyExtensionTrait as ClientEmptyExtensionTrait; use Enqueue\Client\ExtensionInterface as ClientExtensionInterface; -use Enqueue\Client\Message; +use Enqueue\Client\PreSend; use Enqueue\Consumption\Context; -use Enqueue\Consumption\EmptyExtensionTrait; +use Enqueue\Consumption\EmptyExtensionTrait as ConsumptionEmptyExtensionTrait; use Enqueue\Consumption\ExtensionInterface as ConsumptionExtensionInterface; class ExclusiveCommandExtension implements ConsumptionExtensionInterface, ClientExtensionInterface { - use EmptyExtensionTrait; + use ConsumptionEmptyExtensionTrait, ClientEmptyExtensionTrait; /** * @var string[] @@ -60,26 +61,14 @@ public function onPreReceived(Context $context) } } - /** - * {@inheritdoc} - */ - public function onPreSend($topic, Message $message) + public function onPreSendCommand(PreSend $context): void { - if (Config::COMMAND_TOPIC != $topic) { - return; - } + $message = $context->getMessage(); + $command = $context->getCommandOrTopic(); - $commandName = $message->getProperty(Config::PARAMETER_COMMAND_NAME); - if (array_key_exists($commandName, $this->processorNameToQueueNameMap)) { - $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $commandName); - $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $this->processorNameToQueueNameMap[$commandName]); + if (array_key_exists($command, $this->processorNameToQueueNameMap)) { + $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $command); + $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $this->processorNameToQueueNameMap[$command]); } } - - /** - * {@inheritdoc} - */ - public function onPostSend($topic, Message $message) - { - } } diff --git a/pkg/enqueue/Client/EmptyExtensionTrait.php b/pkg/enqueue/Client/EmptyExtensionTrait.php new file mode 100644 index 000000000..baf7f56e3 --- /dev/null +++ b/pkg/enqueue/Client/EmptyExtensionTrait.php @@ -0,0 +1,22 @@ +prepareBody($context->getMessage()); + } + + public function onPreSendCommand(PreSend $context): void + { + $this->prepareBody($context->getMessage()); + } + + private function prepareBody(Message $message): void + { + $body = $message->getBody(); + $contentType = $message->getContentType(); + + if (is_scalar($body) || null === $body) { + $contentType = $contentType ?: 'text/plain'; + $body = (string) $body; + } elseif (is_array($body)) { + // only array of scalars is allowed. + array_walk_recursive($body, function ($value) { + if (!is_scalar($value) && null !== $value) { + throw new \LogicException(sprintf( + 'The message\'s body must be an array of scalars. Found not scalar in the array: %s', + is_object($value) ? get_class($value) : gettype($value) + )); + } + }); + + $contentType = $contentType ?: 'application/json'; + $body = JSON::encode($body); + } elseif ($body instanceof \JsonSerializable) { + $contentType = $contentType ?: 'application/json'; + $body = JSON::encode($body); + } else { + throw new \InvalidArgumentException(sprintf( + 'The message\'s body must be either null, scalar, array or object (implements \JsonSerializable). Got: %s', + is_object($body) ? get_class($body) : gettype($body) + )); + } + + $message->setContentType($contentType); + $message->setBody($body); + } +} diff --git a/pkg/enqueue/Client/ExtensionInterface.php b/pkg/enqueue/Client/ExtensionInterface.php index 4f9fd66ea..deb7d3fdc 100644 --- a/pkg/enqueue/Client/ExtensionInterface.php +++ b/pkg/enqueue/Client/ExtensionInterface.php @@ -4,19 +4,21 @@ interface ExtensionInterface { - /** - * @param string $topic - * @param Message $message - * - * @return - */ - public function onPreSend($topic, Message $message); + public function onPreSendEvent(PreSend $context): void; - /** - * @param string $topic - * @param Message $message - * - * @return - */ - public function onPostSend($topic, Message $message); + public function onPreSendCommand(PreSend $context): void; + + public function onPreDriverSend(PreDriverSend $context): void; + + public function onPostSend(PostSend $context): void; + +// /** +// * @deprecated +// */ +// public function onPreSend($topic, Message $message); +// +// /** +// * @deprecated +// */ +// public function onPostSend($topic, Message $message); } diff --git a/pkg/enqueue/Client/PostSend.php b/pkg/enqueue/Client/PostSend.php new file mode 100644 index 000000000..f99d78138 --- /dev/null +++ b/pkg/enqueue/Client/PostSend.php @@ -0,0 +1,49 @@ +message = $message; + $this->producer = $producer; + $this->driver = $driver; + } + + public function getMessage(): Message + { + return $this->message; + } + + public function getProducer(): ProducerInterface + { + return $this->producer; + } + + public function getDriver(): DriverInterface + { + return $this->driver; + } + + public function isEvent(): bool + { + return Config::COMMAND_TOPIC !== $this->message->getProperty(Config::PARAMETER_TOPIC_NAME); + } + + public function getCommand(): string + { + return $this->message->getProperty(Config::PARAMETER_COMMAND_NAME); + } + + public function getTopic(): string + { + return $this->message->getProperty(Config::PARAMETER_TOPIC_NAME); + } +} diff --git a/pkg/enqueue/Client/PreDriverSend.php b/pkg/enqueue/Client/PreDriverSend.php new file mode 100644 index 000000000..1ea580440 --- /dev/null +++ b/pkg/enqueue/Client/PreDriverSend.php @@ -0,0 +1,49 @@ +message = $message; + $this->producer = $producer; + $this->driver = $driver; + } + + public function getMessage(): Message + { + return $this->message; + } + + public function getProducer(): ProducerInterface + { + return $this->producer; + } + + public function getDriver(): DriverInterface + { + return $this->driver; + } + + public function isEvent(): bool + { + return Config::COMMAND_TOPIC !== $this->message->getProperty(Config::PARAMETER_TOPIC_NAME); + } + + public function getCommand(): string + { + return $this->message->getProperty(Config::PARAMETER_COMMAND_NAME); + } + + public function getTopic(): string + { + return $this->message->getProperty(Config::PARAMETER_TOPIC_NAME); + } +} diff --git a/pkg/enqueue/Client/PreSend.php b/pkg/enqueue/Client/PreSend.php new file mode 100644 index 000000000..b37ce5d78 --- /dev/null +++ b/pkg/enqueue/Client/PreSend.php @@ -0,0 +1,69 @@ +message = $message; + $this->commandOrTopic = $commandOrTopic; + $this->producer = $producer; + $this->driver = $driver; + + $this->originalMessage = clone $message; + } + + public function getCommandOrTopic(): string + { + return $this->commandOrTopic; + } + + public function changeCommandOrTopic(string $commandOrTopic): void + { + $this->commandOrTopic = $commandOrTopic; + } + + public function changeBody($body, string $contentType = null): void + { + $this->message->setBody($body); + + if (null !== $contentType) { + $this->message->setContentType($contentType); + } + } + + public function getMessage(): Message + { + return $this->message; + } + + public function getOriginalMessage(): Message + { + return $this->originalMessage; + } + + public function getProducer(): ProducerInterface + { + return $this->producer; + } + + public function getDriver(): DriverInterface + { + return $this->driver; + } +} diff --git a/pkg/enqueue/Client/Producer.php b/pkg/enqueue/Client/Producer.php index 8fda47ff6..15957832f 100644 --- a/pkg/enqueue/Client/Producer.php +++ b/pkg/enqueue/Client/Producer.php @@ -2,8 +2,8 @@ namespace Enqueue\Client; +use Enqueue\Client\Extension\PrepareBodyExtension; use Enqueue\Rpc\RpcFactory; -use Enqueue\Util\JSON; use Enqueue\Util\UUID; class Producer implements ProducerInterface @@ -37,72 +37,40 @@ public function __construct( ) { $this->driver = $driver; $this->rpcFactory = $rpcFactory; - $this->extension = $extension ?: new ChainExtension([]); + + $prepareBodyExtension = new PrepareBodyExtension(); + $this->extension = new ChainExtension([$extension, $prepareBodyExtension]) ?: new ChainExtension([$prepareBodyExtension]); } - /** - * {@inheritdoc} - */ public function sendEvent($topic, $message) { if (false == $message instanceof Message) { - $body = $message; - $message = new Message(); - $message->setBody($body); - } - - $this->prepareBody($message); - - $message->setProperty(Config::PARAMETER_TOPIC_NAME, $topic); - - if (!$message->getMessageId()) { - $message->setMessageId(UUID::generate()); + $message = new Message($message); } - if (!$message->getTimestamp()) { - $message->setTimestamp(time()); - } + $preSend = new PreSend($topic, $message, $this, $this->driver); + $this->extension->onPreSendEvent($preSend); - if (!$message->getPriority()) { - $message->setPriority(MessagePriority::NORMAL); - } + $topic = $preSend->getCommandOrTopic(); + $message = $preSend->getMessage(); - if (Message::SCOPE_MESSAGE_BUS == $message->getScope()) { - if ($message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) { - throw new \LogicException(sprintf('The %s property must not be set for messages that are sent to message bus.', Config::PARAMETER_PROCESSOR_QUEUE_NAME)); - } - if ($message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) { - throw new \LogicException(sprintf('The %s property must not be set for messages that are sent to message bus.', Config::PARAMETER_PROCESSOR_NAME)); - } - - $this->extension->onPreSend($topic, $message); - $this->driver->sendToRouter($message); - $this->extension->onPostSend($topic, $message); - } elseif (Message::SCOPE_APP == $message->getScope()) { - if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) { - $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $this->driver->getConfig()->getRouterProcessorName()); - } - if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) { - $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $this->driver->getConfig()->getRouterQueueName()); - } + $message->setProperty(Config::PARAMETER_TOPIC_NAME, $topic); - $this->extension->onPreSend($topic, $message); - $this->driver->sendToProcessor($message); - $this->extension->onPostSend($topic, $message); - } else { - throw new \LogicException(sprintf('The message scope "%s" is not supported.', $message->getScope())); - } + $this->doSend($message); } - /** - * {@inheritdoc} - */ public function sendCommand($command, $message, $needReply = false) { if (false == $message instanceof Message) { $message = new Message($message); } + $preSend = new PreSend($command, $message, $this, $this->driver); + $this->extension->onPreSendEvent($preSend); + + $command = $preSend->getCommandOrTopic(); + $message = $preSend->getMessage(); + $deleteReplyQueue = false; $replyTo = $message->getReplyTo(); @@ -119,9 +87,10 @@ public function sendCommand($command, $message, $needReply = false) $message->setProperty(Config::PARAMETER_TOPIC_NAME, Config::COMMAND_TOPIC); $message->setProperty(Config::PARAMETER_COMMAND_NAME, $command); + $message->setProperty(Config::PARAMETER_TOPIC_NAME, Config::COMMAND_TOPIC); $message->setScope(Message::SCOPE_APP); - $this->sendEvent(Config::COMMAND_TOPIC, $message); + $this->doSend($message); if ($needReply) { $promise = $this->rpcFactory->createPromise($replyTo, $message->getCorrelationId(), 60000); @@ -139,49 +108,51 @@ public function send($topic, $message) $this->sendEvent($topic, $message); } - /** - * @param Message $message - */ - private function prepareBody(Message $message) + private function doSend(Message $message) { - $body = $message->getBody(); - $contentType = $message->getContentType(); - - if (is_scalar($body) || null === $body) { - $contentType = $contentType ?: 'text/plain'; - $body = (string) $body; - } elseif (is_array($body)) { - if ($contentType && 'application/json' !== $contentType) { - throw new \LogicException(sprintf('Content type "application/json" only allowed when body is array')); + if (false === is_string($message->getBody())) { + throw new \LogicException(sprintf( + 'The message body must be string at this stage, got "%s". Make sure you passed string as message or there is an extension that converts custom input to string.', + is_object($message->getBody()) ? get_class($message->getBody()) : gettype($message->getBody()) + )); + } + + if (!$message->getMessageId()) { + $message->setMessageId(UUID::generate()); + } + + if (!$message->getTimestamp()) { + $message->setTimestamp(time()); + } + + if (!$message->getPriority()) { + $message->setPriority(MessagePriority::NORMAL); + } + + if (Message::SCOPE_MESSAGE_BUS == $message->getScope()) { + if ($message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) { + throw new \LogicException(sprintf('The %s property must not be set for messages that are sent to message bus.', Config::PARAMETER_PROCESSOR_QUEUE_NAME)); + } + if ($message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) { + throw new \LogicException(sprintf('The %s property must not be set for messages that are sent to message bus.', Config::PARAMETER_PROCESSOR_NAME)); } - // only array of scalars is allowed. - array_walk_recursive($body, function ($value) { - if (!is_scalar($value) && null !== $value) { - throw new \LogicException(sprintf( - 'The message\'s body must be an array of scalars. Found not scalar in the array: %s', - is_object($value) ? get_class($value) : gettype($value) - )); - } - }); - - $contentType = 'application/json'; - $body = JSON::encode($body); - } elseif ($body instanceof \JsonSerializable) { - if ($contentType && 'application/json' !== $contentType) { - throw new \LogicException(sprintf('Content type "application/json" only allowed when body is array')); + $this->extension->onPreDriverSend(new PreDriverSend($message, $this, $this->driver)); + $this->driver->sendToRouter($message); + } elseif (Message::SCOPE_APP == $message->getScope()) { + if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) { + $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $this->driver->getConfig()->getRouterProcessorName()); + } + if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)) { + $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $this->driver->getConfig()->getRouterQueueName()); } - $contentType = 'application/json'; - $body = JSON::encode($body); + $this->extension->onPreDriverSend(new PreDriverSend($message, $this, $this->driver)); + $this->driver->sendToRouter($message); } else { - throw new \InvalidArgumentException(sprintf( - 'The message\'s body must be either null, scalar, array or object (implements \JsonSerializable). Got: %s', - is_object($body) ? get_class($body) : gettype($body) - )); + throw new \LogicException(sprintf('The message scope "%s" is not supported.', $message->getScope())); } - $message->setContentType($contentType); - $message->setBody($body); + $this->extension->onPostSend(new PostSend($message, $this, $this->driver)); } } diff --git a/pkg/enqueue/Tests/Client/Extension/PrepareBodyExtensionTest.php b/pkg/enqueue/Tests/Client/Extension/PrepareBodyExtensionTest.php new file mode 100644 index 000000000..e99e3e39e --- /dev/null +++ b/pkg/enqueue/Tests/Client/Extension/PrepareBodyExtensionTest.php @@ -0,0 +1,142 @@ +assertTrue($rc->implementsInterface(ExtensionInterface::class)); + } + + public function testCouldConstructedWithoutAnyArguments() + { + new PrepareBodyExtension(); + } + + /** + * @dataProvider provideMessages + * + * @param mixed $body + * @param null|mixed $contentType + */ + public function testShouldSendStringUnchangedAndAddPlainTextContentTypeIfEmpty( + $body, + $contentType, + string $expectedBody, + string $expectedContentType + ) { + $message = new Message($body); + $message->setContentType($contentType); + + $context = $this->createDummyPreSendContext('aTopic', $message); + + $extension = new PrepareBodyExtension(); + + $extension->onPreSendEvent($context); + + $this->assertSame($expectedBody, $message->getBody()); + $this->assertSame($expectedContentType, $message->getContentType()); + } + + public function testThrowIfBodyIsObject() + { + $message = new Message(new \stdClass()); + + $context = $this->createDummyPreSendContext('aTopic', $message); + + $extension = new PrepareBodyExtension(); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('The message\'s body must be either null, scalar, array or object (implements \JsonSerializable). Got: stdClass'); + + $extension->onPreSendEvent($context); + } + + public function testThrowIfBodyIsArrayWithObjectsInsideOnSend() + { + $message = new Message(['foo' => new \stdClass()]); + + $context = $this->createDummyPreSendContext('aTopic', $message); + + $extension = new PrepareBodyExtension(); + + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('The message\'s body must be an array of scalars. Found not scalar in the array: stdClass'); + + $extension->onPreSendEvent($context); + } + + public function testShouldThrowExceptionIfBodyIsArrayWithObjectsInSubArraysInsideOnSend() + { + $message = new Message(['foo' => ['bar' => new \stdClass()]]); + + $context = $this->createDummyPreSendContext('aTopic', $message); + + $extension = new PrepareBodyExtension(); + + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('The message\'s body must be an array of scalars. Found not scalar in the array: stdClass'); + + $extension->onPreSendEvent($context); + } + + public static function provideMessages() + { + yield ['theBody', null, 'theBody', 'text/plain']; + + yield ['theBody', 'foo/bar', 'theBody', 'foo/bar']; + + yield [12345, null, '12345', 'text/plain']; + + yield [12345, 'foo/bar', '12345', 'foo/bar']; + + yield [12.345, null, '12.345', 'text/plain']; + + yield [12.345, 'foo/bar', '12.345', 'foo/bar']; + + yield [true, null, '1', 'text/plain']; + + yield [true, 'foo/bar', '1', 'foo/bar']; + + yield [null, null, '', 'text/plain']; + + yield [null, 'foo/bar', '', 'foo/bar']; + + yield [['foo' => 'fooVal'], null, '{"foo":"fooVal"}', 'application/json']; + + yield [['foo' => 'fooVal'], 'foo/bar', '{"foo":"fooVal"}', 'foo/bar']; + + yield [new JsonSerializableObject(), null, '{"foo":"fooVal"}', 'application/json']; + + yield [new JsonSerializableObject(), 'foo/bar', '{"foo":"fooVal"}', 'foo/bar']; + } + + private function createDummyPreSendContext($commandOrTopic, $message): PreSend + { + return new PreSend( + $commandOrTopic, + $message, + $this->createMock(ProducerInterface::class), + $this->createMock(DriverInterface::class) + ); + } +} + +class JsonSerializableObject implements \JsonSerializable +{ + public function jsonSerialize() + { + return ['foo' => 'fooVal']; + } +} diff --git a/pkg/enqueue/Tests/Client/ProducerTest.php b/pkg/enqueue/Tests/Client/ProducerTest.php index c9a30fcde..be8554cdd 100644 --- a/pkg/enqueue/Tests/Client/ProducerTest.php +++ b/pkg/enqueue/Tests/Client/ProducerTest.php @@ -9,7 +9,6 @@ use Enqueue\Client\MessagePriority; use Enqueue\Client\Producer; use Enqueue\Client\ProducerInterface; -use Enqueue\Null\NullQueue; use Enqueue\Rpc\RpcFactory; use Enqueue\Test\ClassExtensionTrait; use PHPUnit\Framework\TestCase; @@ -154,207 +153,6 @@ public function testShouldSendMessageWithCustomTimestamp() self::assertSame('theCustomTimestamp', $message->getTimestamp()); } - public function testShouldSendStringAsPlainText() - { - $driver = $this->createDriverStub(); - $driver - ->expects($this->once()) - ->method('sendToRouter') - ->willReturnCallback(function (Message $message) { - self::assertSame('theStringMessage', $message->getBody()); - self::assertSame('text/plain', $message->getContentType()); - }) - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent('topic', 'theStringMessage'); - } - - public function testShouldSendArrayAsJsonString() - { - $driver = $this->createDriverStub(); - $driver - ->expects($this->once()) - ->method('sendToRouter') - ->willReturnCallback(function (Message $message) { - self::assertSame('{"foo":"fooVal"}', $message->getBody()); - self::assertSame('application/json', $message->getContentType()); - }) - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent('topic', ['foo' => 'fooVal']); - } - - public function testShouldConvertMessageArrayBodyJsonString() - { - $message = new Message(); - $message->setBody(['foo' => 'fooVal']); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->once()) - ->method('sendToRouter') - ->willReturnCallback(function (Message $message) { - self::assertSame('{"foo":"fooVal"}', $message->getBody()); - self::assertSame('application/json', $message->getContentType()); - }) - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent('topic', $message); - } - - public function testSendShouldForceScalarsToStringAndSetTextContentType() - { - $queue = new NullQueue(''); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->once()) - ->method('sendToRouter') - ->willReturnCallback(function (Message $message) { - self::assertEquals('text/plain', $message->getContentType()); - - self::assertInternalType('string', $message->getBody()); - self::assertEquals('12345', $message->getBody()); - }) - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent($queue, 12345); - } - - public function testSendShouldForceMessageScalarsBodyToStringAndSetTextContentType() - { - $queue = new NullQueue(''); - - $message = new Message(); - $message->setBody(12345); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->once()) - ->method('sendToRouter') - ->willReturnCallback(function (Message $message) { - self::assertEquals('text/plain', $message->getContentType()); - - self::assertInternalType('string', $message->getBody()); - self::assertEquals('12345', $message->getBody()); - }) - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent($queue, $message); - } - - public function testSendShouldForceNullToEmptyStringAndSetTextContentType() - { - $queue = new NullQueue(''); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->once()) - ->method('sendToRouter') - ->willReturnCallback(function (Message $message) { - self::assertEquals('text/plain', $message->getContentType()); - - self::assertInternalType('string', $message->getBody()); - self::assertEquals('', $message->getBody()); - }) - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent($queue, null); - } - - public function testSendShouldForceNullBodyToEmptyStringAndSetTextContentType() - { - $queue = new NullQueue(''); - - $message = new Message(); - $message->setBody(null); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->once()) - ->method('sendToRouter') - ->willReturnCallback(function (Message $message) { - self::assertEquals('text/plain', $message->getContentType()); - - self::assertInternalType('string', $message->getBody()); - self::assertEquals('', $message->getBody()); - }) - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent($queue, $message); - } - - public function testShouldThrowExceptionIfBodyIsObjectOnSend() - { - $driver = $this->createDriverStub(); - $driver - ->expects($this->never()) - ->method('sendToRouter') - ; - $driver - ->expects($this->never()) - ->method('sendToProcessor') - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The message\'s body must be either null, scalar, array or object (implements \JsonSerializable). Got: stdClass'); - - $producer->sendEvent('topic', new \stdClass()); - } - - public function testShouldThrowExceptionIfBodyIsArrayWithObjectsInsideOnSend() - { - $queue = new NullQueue('queue'); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->never()) - ->method('sendToRouter') - ; - $driver - ->expects($this->never()) - ->method('sendToProcessor') - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('The message\'s body must be an array of scalars. Found not scalar in the array: stdClass'); - - $producer->sendEvent($queue, ['foo' => new \stdClass()]); - } - - public function testShouldThrowExceptionIfBodyIsArrayWithObjectsInSubArraysInsideOnSend() - { - $queue = new NullQueue('queue'); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->never()) - ->method('sendToRouter') - ; - $driver - ->expects($this->never()) - ->method('sendToProcessor') - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('The message\'s body must be an array of scalars. Found not scalar in the array: stdClass'); - - $producer->sendEvent($queue, ['foo' => ['bar' => new \stdClass()]]); - } - public function testShouldSendJsonSerializableObjectAsJsonStringToMessageBus() { $object = new JsonSerializableObject(); @@ -444,31 +242,6 @@ public function testThrowIfTryToSendMessageToMessageBusWithProcessorQueueNamePro $producer->sendEvent('topic', $message); } - public function testThrowIfNotApplicationJsonContentTypeSetWithJsonSerializableBody() - { - $object = new JsonSerializableObject(); - - $message = new Message(); - $message->setBody($object); - $message->setContentType('foo/bar'); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->never()) - ->method('sendToRouter') - ; - $driver - ->expects($this->never()) - ->method('sendToProcessor') - ; - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('Content type "application/json" only allowed when body is array'); - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent('topic', $message); - } - public function testShouldSendMessageToApplicationRouter() { $message = new Message(); From 2c84a240cbe6faf27bc2bc355f74ae8eaeef6d07 Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Mon, 27 Aug 2018 23:19:51 +0300 Subject: [PATCH 2/7] rename onPreDriverSend to onDriverPreSend. split getCommandOrTopic into two getters. --- pkg/enqueue/Client/ChainExtension.php | 4 ++-- .../ExclusiveCommandExtension.php | 2 +- .../{PreDriverSend.php => DriverPreSend.php} | 2 +- pkg/enqueue/Client/EmptyExtensionTrait.php | 2 +- pkg/enqueue/Client/ExtensionInterface.php | 2 +- pkg/enqueue/Client/PreSend.php | 16 +++++++++++++--- pkg/enqueue/Client/Producer.php | 8 ++++---- 7 files changed, 23 insertions(+), 13 deletions(-) rename pkg/enqueue/Client/{PreDriverSend.php => DriverPreSend.php} (98%) diff --git a/pkg/enqueue/Client/ChainExtension.php b/pkg/enqueue/Client/ChainExtension.php index 01bee2227..842f83b5d 100644 --- a/pkg/enqueue/Client/ChainExtension.php +++ b/pkg/enqueue/Client/ChainExtension.php @@ -31,10 +31,10 @@ public function onPreSendCommand(PreSend $event): void } } - public function onPreDriverSend(PreDriverSend $context): void + public function onDriverPreSend(DriverPreSend $context): void { foreach ($this->extensions as $extension) { - $extension->onPreDriverSend($context); + $extension->onDriverPreSend($context); } } diff --git a/pkg/enqueue/Client/ConsumptionExtension/ExclusiveCommandExtension.php b/pkg/enqueue/Client/ConsumptionExtension/ExclusiveCommandExtension.php index add6ef751..63fe96c42 100644 --- a/pkg/enqueue/Client/ConsumptionExtension/ExclusiveCommandExtension.php +++ b/pkg/enqueue/Client/ConsumptionExtension/ExclusiveCommandExtension.php @@ -64,7 +64,7 @@ public function onPreReceived(Context $context) public function onPreSendCommand(PreSend $context): void { $message = $context->getMessage(); - $command = $context->getCommandOrTopic(); + $command = $context->getCommand(); if (array_key_exists($command, $this->processorNameToQueueNameMap)) { $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, $command); diff --git a/pkg/enqueue/Client/PreDriverSend.php b/pkg/enqueue/Client/DriverPreSend.php similarity index 98% rename from pkg/enqueue/Client/PreDriverSend.php rename to pkg/enqueue/Client/DriverPreSend.php index 1ea580440..076123d94 100644 --- a/pkg/enqueue/Client/PreDriverSend.php +++ b/pkg/enqueue/Client/DriverPreSend.php @@ -2,7 +2,7 @@ namespace Enqueue\Client; -class PreDriverSend +class DriverPreSend { private $message; diff --git a/pkg/enqueue/Client/EmptyExtensionTrait.php b/pkg/enqueue/Client/EmptyExtensionTrait.php index baf7f56e3..2b4f97634 100644 --- a/pkg/enqueue/Client/EmptyExtensionTrait.php +++ b/pkg/enqueue/Client/EmptyExtensionTrait.php @@ -12,7 +12,7 @@ public function onPreSendCommand(PreSend $context): void { } - public function onPreDriverSend(PreDriverSend $context): void + public function onDriverPreSend(DriverPreSend $context): void { } diff --git a/pkg/enqueue/Client/ExtensionInterface.php b/pkg/enqueue/Client/ExtensionInterface.php index deb7d3fdc..1a6b27d03 100644 --- a/pkg/enqueue/Client/ExtensionInterface.php +++ b/pkg/enqueue/Client/ExtensionInterface.php @@ -8,7 +8,7 @@ public function onPreSendEvent(PreSend $context): void; public function onPreSendCommand(PreSend $context): void; - public function onPreDriverSend(PreDriverSend $context): void; + public function onDriverPreSend(DriverPreSend $context): void; public function onPostSend(PostSend $context): void; diff --git a/pkg/enqueue/Client/PreSend.php b/pkg/enqueue/Client/PreSend.php index b37ce5d78..6ebbd753b 100644 --- a/pkg/enqueue/Client/PreSend.php +++ b/pkg/enqueue/Client/PreSend.php @@ -28,14 +28,24 @@ public function __construct( $this->originalMessage = clone $message; } - public function getCommandOrTopic(): string + public function getCommand(): string { return $this->commandOrTopic; } - public function changeCommandOrTopic(string $commandOrTopic): void + public function getTopic(): string { - $this->commandOrTopic = $commandOrTopic; + return $this->commandOrTopic; + } + + public function changeCommand(string $newCommand): void + { + $this->commandOrTopic = $newCommand; + } + + public function changeTopic(string $newTopic): void + { + $this->commandOrTopic = $newTopic; } public function changeBody($body, string $contentType = null): void diff --git a/pkg/enqueue/Client/Producer.php b/pkg/enqueue/Client/Producer.php index 15957832f..a99610ad5 100644 --- a/pkg/enqueue/Client/Producer.php +++ b/pkg/enqueue/Client/Producer.php @@ -51,7 +51,7 @@ public function sendEvent($topic, $message) $preSend = new PreSend($topic, $message, $this, $this->driver); $this->extension->onPreSendEvent($preSend); - $topic = $preSend->getCommandOrTopic(); + $topic = $preSend->getTopic(); $message = $preSend->getMessage(); $message->setProperty(Config::PARAMETER_TOPIC_NAME, $topic); @@ -68,7 +68,7 @@ public function sendCommand($command, $message, $needReply = false) $preSend = new PreSend($command, $message, $this, $this->driver); $this->extension->onPreSendEvent($preSend); - $command = $preSend->getCommandOrTopic(); + $command = $preSend->getCommand(); $message = $preSend->getMessage(); $deleteReplyQueue = false; @@ -137,7 +137,7 @@ private function doSend(Message $message) throw new \LogicException(sprintf('The %s property must not be set for messages that are sent to message bus.', Config::PARAMETER_PROCESSOR_NAME)); } - $this->extension->onPreDriverSend(new PreDriverSend($message, $this, $this->driver)); + $this->extension->onPreDriverSend(new DriverPreSend($message, $this, $this->driver)); $this->driver->sendToRouter($message); } elseif (Message::SCOPE_APP == $message->getScope()) { if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) { @@ -147,7 +147,7 @@ private function doSend(Message $message) $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $this->driver->getConfig()->getRouterQueueName()); } - $this->extension->onPreDriverSend(new PreDriverSend($message, $this, $this->driver)); + $this->extension->onPreDriverSend(new DriverPreSend($message, $this, $this->driver)); $this->driver->sendToRouter($message); } else { throw new \LogicException(sprintf('The message scope "%s" is not supported.', $message->getScope())); From dd550310629f9c4cb5d561b3486def77d1e00063 Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Thu, 30 Aug 2018 13:05:11 +0300 Subject: [PATCH 3/7] Add tests. --- pkg/enqueue/Client/ChainExtension.php | 6 +- .../ExclusiveCommandExtension.php | 2 +- pkg/enqueue/Client/DriverPreSend.php | 2 +- pkg/enqueue/Client/PostSend.php | 2 +- pkg/enqueue/Client/PreSend.php | 2 +- pkg/enqueue/Client/Producer.php | 26 +- .../Tests/Client/ChainExtensionTest.php | 104 +++- .../ExclusiveCommandExtensionTest.php | 26 +- .../Extension/PrepareBodyExtensionTest.php | 9 +- .../Tests/Client/ProducerSendCommandTest.php | 508 ++++++++++++++++ .../Tests/Client/ProducerSendEventTest.php | 546 ++++++++++++++++++ pkg/enqueue/Tests/Client/ProducerTest.php | 388 +------------ .../CustomPrepareBodyClientExtension.php | 22 + .../Tests/Mocks/JsonSerializableObject.php | 11 + pkg/job-queue/Tests/JobProcessorTest.php | 12 +- pkg/test/ClassExtensionTrait.php | 10 + 16 files changed, 1251 insertions(+), 425 deletions(-) create mode 100644 pkg/enqueue/Tests/Client/ProducerSendCommandTest.php create mode 100644 pkg/enqueue/Tests/Client/ProducerSendEventTest.php create mode 100644 pkg/enqueue/Tests/Mocks/CustomPrepareBodyClientExtension.php create mode 100644 pkg/enqueue/Tests/Mocks/JsonSerializableObject.php diff --git a/pkg/enqueue/Client/ChainExtension.php b/pkg/enqueue/Client/ChainExtension.php index 842f83b5d..603b2b633 100644 --- a/pkg/enqueue/Client/ChainExtension.php +++ b/pkg/enqueue/Client/ChainExtension.php @@ -2,7 +2,7 @@ namespace Enqueue\Client; -class ChainExtension implements ExtensionInterface +final class ChainExtension implements ExtensionInterface { /** * @var ExtensionInterface[] @@ -14,7 +14,9 @@ class ChainExtension implements ExtensionInterface */ public function __construct(array $extensions) { - $this->extensions = $extensions; + array_walk($extensions, function (ExtensionInterface $extension) { + $this->extensions[] = $extension; + }); } public function onPreSendEvent(PreSend $event): void diff --git a/pkg/enqueue/Client/ConsumptionExtension/ExclusiveCommandExtension.php b/pkg/enqueue/Client/ConsumptionExtension/ExclusiveCommandExtension.php index 63fe96c42..824a24448 100644 --- a/pkg/enqueue/Client/ConsumptionExtension/ExclusiveCommandExtension.php +++ b/pkg/enqueue/Client/ConsumptionExtension/ExclusiveCommandExtension.php @@ -10,7 +10,7 @@ use Enqueue\Consumption\EmptyExtensionTrait as ConsumptionEmptyExtensionTrait; use Enqueue\Consumption\ExtensionInterface as ConsumptionExtensionInterface; -class ExclusiveCommandExtension implements ConsumptionExtensionInterface, ClientExtensionInterface +final class ExclusiveCommandExtension implements ConsumptionExtensionInterface, ClientExtensionInterface { use ConsumptionEmptyExtensionTrait, ClientEmptyExtensionTrait; diff --git a/pkg/enqueue/Client/DriverPreSend.php b/pkg/enqueue/Client/DriverPreSend.php index 076123d94..8a298cb5a 100644 --- a/pkg/enqueue/Client/DriverPreSend.php +++ b/pkg/enqueue/Client/DriverPreSend.php @@ -2,7 +2,7 @@ namespace Enqueue\Client; -class DriverPreSend +final class DriverPreSend { private $message; diff --git a/pkg/enqueue/Client/PostSend.php b/pkg/enqueue/Client/PostSend.php index f99d78138..2e0ae627a 100644 --- a/pkg/enqueue/Client/PostSend.php +++ b/pkg/enqueue/Client/PostSend.php @@ -2,7 +2,7 @@ namespace Enqueue\Client; -class PostSend +final class PostSend { private $message; diff --git a/pkg/enqueue/Client/PreSend.php b/pkg/enqueue/Client/PreSend.php index 6ebbd753b..afd64012f 100644 --- a/pkg/enqueue/Client/PreSend.php +++ b/pkg/enqueue/Client/PreSend.php @@ -2,7 +2,7 @@ namespace Enqueue\Client; -class PreSend +final class PreSend { private $message; diff --git a/pkg/enqueue/Client/Producer.php b/pkg/enqueue/Client/Producer.php index a99610ad5..2e74df0ec 100644 --- a/pkg/enqueue/Client/Producer.php +++ b/pkg/enqueue/Client/Producer.php @@ -6,12 +6,12 @@ use Enqueue\Rpc\RpcFactory; use Enqueue\Util\UUID; -class Producer implements ProducerInterface +final class Producer implements ProducerInterface { /** * @var DriverInterface */ - protected $driver; + private $driver; /** * @var ExtensionInterface @@ -23,13 +23,6 @@ class Producer implements ProducerInterface */ private $rpcFactory; - /** - * @param DriverInterface $driver - * @param ExtensionInterface|null $extension - * @param RpcFactory $rpcFactory - * - * @internal param RpcClient $rpcClient - */ public function __construct( DriverInterface $driver, RpcFactory $rpcFactory, @@ -38,8 +31,10 @@ public function __construct( $this->driver = $driver; $this->rpcFactory = $rpcFactory; - $prepareBodyExtension = new PrepareBodyExtension(); - $this->extension = new ChainExtension([$extension, $prepareBodyExtension]) ?: new ChainExtension([$prepareBodyExtension]); + $this->extension = $extension ? + new ChainExtension([$extension, new PrepareBodyExtension()]) : + new ChainExtension([new PrepareBodyExtension()]) + ; } public function sendEvent($topic, $message) @@ -66,7 +61,7 @@ public function sendCommand($command, $message, $needReply = false) } $preSend = new PreSend($command, $message, $this, $this->driver); - $this->extension->onPreSendEvent($preSend); + $this->extension->onPreSendCommand($preSend); $command = $preSend->getCommand(); $message = $preSend->getMessage(); @@ -87,7 +82,6 @@ public function sendCommand($command, $message, $needReply = false) $message->setProperty(Config::PARAMETER_TOPIC_NAME, Config::COMMAND_TOPIC); $message->setProperty(Config::PARAMETER_COMMAND_NAME, $command); - $message->setProperty(Config::PARAMETER_TOPIC_NAME, Config::COMMAND_TOPIC); $message->setScope(Message::SCOPE_APP); $this->doSend($message); @@ -137,7 +131,7 @@ private function doSend(Message $message) throw new \LogicException(sprintf('The %s property must not be set for messages that are sent to message bus.', Config::PARAMETER_PROCESSOR_NAME)); } - $this->extension->onPreDriverSend(new DriverPreSend($message, $this, $this->driver)); + $this->extension->onDriverPreSend(new DriverPreSend($message, $this, $this->driver)); $this->driver->sendToRouter($message); } elseif (Message::SCOPE_APP == $message->getScope()) { if (false == $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)) { @@ -147,8 +141,8 @@ private function doSend(Message $message) $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, $this->driver->getConfig()->getRouterQueueName()); } - $this->extension->onPreDriverSend(new DriverPreSend($message, $this, $this->driver)); - $this->driver->sendToRouter($message); + $this->extension->onDriverPreSend(new DriverPreSend($message, $this, $this->driver)); + $this->driver->sendToProcessor($message); } else { throw new \LogicException(sprintf('The message scope "%s" is not supported.', $message->getScope())); } diff --git a/pkg/enqueue/Tests/Client/ChainExtensionTest.php b/pkg/enqueue/Tests/Client/ChainExtensionTest.php index 3b1d82f9a..9268c7b94 100644 --- a/pkg/enqueue/Tests/Client/ChainExtensionTest.php +++ b/pkg/enqueue/Tests/Client/ChainExtensionTest.php @@ -3,8 +3,13 @@ namespace Enqueue\Tests\Client; use Enqueue\Client\ChainExtension; +use Enqueue\Client\DriverInterface; +use Enqueue\Client\DriverPreSend; use Enqueue\Client\ExtensionInterface; use Enqueue\Client\Message; +use Enqueue\Client\PostSend; +use Enqueue\Client\PreSend; +use Enqueue\Client\ProducerInterface; use Enqueue\Test\ClassExtensionTrait; use PHPUnit\Framework\TestCase; @@ -17,53 +22,128 @@ public function testShouldImplementExtensionInterface() $this->assertClassImplements(ExtensionInterface::class, ChainExtension::class); } + public function testShouldBeFinal() + { + $this->assertClassFinal(ChainExtension::class); + } + public function testCouldBeConstructedWithExtensionsArray() { new ChainExtension([$this->createExtension(), $this->createExtension()]); } - public function testShouldProxyOnPreSendToAllInternalExtensions() + public function testThrowIfArrayContainsNotExtension() + { + $this->expectException(\TypeError::class); + $this->expectExceptionMessage('Argument 1 passed to'); + + new ChainExtension([$this->createExtension(), new \stdClass()]); + } + + public function testShouldProxyOnPreSendEventToAllInternalExtensions() + { + $preSend = new PreSend( + 'aCommandOrTopic', + new Message(), + $this->createMock(ProducerInterface::class), + $this->createMock(DriverInterface::class) + ); + + $fooExtension = $this->createExtension(); + $fooExtension + ->expects($this->once()) + ->method('onPreSendEvent') + ->with($this->identicalTo($preSend)) + ; + $barExtension = $this->createExtension(); + $barExtension + ->expects($this->once()) + ->method('onPreSendEvent') + ->with($this->identicalTo($preSend)) + ; + + $extensions = new ChainExtension([$fooExtension, $barExtension]); + + $extensions->onPreSendEvent($preSend); + } + + public function testShouldProxyOnPreSendCommandToAllInternalExtensions() + { + $preSend = new PreSend( + 'aCommandOrTopic', + new Message(), + $this->createMock(ProducerInterface::class), + $this->createMock(DriverInterface::class) + ); + + $fooExtension = $this->createExtension(); + $fooExtension + ->expects($this->once()) + ->method('onPreSendCommand') + ->with($this->identicalTo($preSend)) + ; + $barExtension = $this->createExtension(); + $barExtension + ->expects($this->once()) + ->method('onPreSendCommand') + ->with($this->identicalTo($preSend)) + ; + + $extensions = new ChainExtension([$fooExtension, $barExtension]); + + $extensions->onPreSendCommand($preSend); + } + + public function testShouldProxyOnDriverPreSendToAllInternalExtensions() { - $message = new Message(); + $driverPreSend = new DriverPreSend( + new Message(), + $this->createMock(ProducerInterface::class), + $this->createMock(DriverInterface::class) + ); $fooExtension = $this->createExtension(); $fooExtension ->expects($this->once()) - ->method('onPreSend') - ->with('topic', $this->identicalTo($message)) + ->method('onDriverPreSend') + ->with($this->identicalTo($driverPreSend)) ; $barExtension = $this->createExtension(); $barExtension ->expects($this->once()) - ->method('onPreSend') - ->with('topic', $this->identicalTo($message)) + ->method('onDriverPreSend') + ->with($this->identicalTo($driverPreSend)) ; $extensions = new ChainExtension([$fooExtension, $barExtension]); - $extensions->onPreSend('topic', $message); + $extensions->onDriverPreSend($driverPreSend); } - public function testShouldProxyOnPostSendToAllInternalExtensions() + public function testShouldProxyOnPostSentToAllInternalExtensions() { - $message = new Message(); + $postSend = new PostSend( + new Message(), + $this->createMock(ProducerInterface::class), + $this->createMock(DriverInterface::class) + ); $fooExtension = $this->createExtension(); $fooExtension ->expects($this->once()) ->method('onPostSend') - ->with('topic', $this->identicalTo($message)) + ->with($this->identicalTo($postSend)) ; $barExtension = $this->createExtension(); $barExtension ->expects($this->once()) ->method('onPostSend') - ->with('topic', $this->identicalTo($message)) + ->with($this->identicalTo($postSend)) ; $extensions = new ChainExtension([$fooExtension, $barExtension]); - $extensions->onPostSend('topic', $message); + $extensions->onPostSend($postSend); } /** diff --git a/pkg/enqueue/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php b/pkg/enqueue/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php index acbcb41ab..c5eb0bba2 100644 --- a/pkg/enqueue/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php +++ b/pkg/enqueue/Tests/Client/ConsumptionExtension/ExclusiveCommandExtensionTest.php @@ -4,8 +4,11 @@ use Enqueue\Client\Config; use Enqueue\Client\ConsumptionExtension\ExclusiveCommandExtension; +use Enqueue\Client\DriverInterface; use Enqueue\Client\ExtensionInterface as ClientExtensionInterface; use Enqueue\Client\Message; +use Enqueue\Client\PreSend; +use Enqueue\Client\ProducerInterface; use Enqueue\Consumption\Context; use Enqueue\Consumption\ExtensionInterface as ConsumptionExtensionInterface; use Enqueue\Null\NullContext; @@ -24,6 +27,11 @@ public function testShouldImplementConsumptionExtensionInterface() $this->assertClassImplements(ConsumptionExtensionInterface::class, ExclusiveCommandExtension::class); } + public function testShouldBeFinal() + { + $this->assertClassFinal(ExclusiveCommandExtension::class); + } + public function testShouldImplementClientExtensionInterface() { $this->assertClassImplements(ClientExtensionInterface::class, ExclusiveCommandExtension::class); @@ -145,7 +153,7 @@ public function testShouldSetCommandPropertiesIfCurrentQueueInTheMap() ], $message->getProperties()); } - public function testShouldDoNothingOnPreSendIfTopicNotCommandOne() + public function testShouldDoNothingOnPreSendEvent() { $message = new Message(); @@ -153,7 +161,7 @@ public function testShouldDoNothingOnPreSendIfTopicNotCommandOne() 'aFooQueueName' => 'aFooProcessorName', ]); - $extension->onPreSend('aTopic', $message); + $extension->onPreSendEvent($this->createDummyPreSend('aTopic', $message)); $this->assertEquals([], $message->getProperties()); } @@ -167,7 +175,7 @@ public function testShouldDoNothingIfCommandNotExclusive() 'aFooQueueName' => 'aFooProcessorName', ]); - $extension->onPreSend(Config::COMMAND_TOPIC, $message); + $extension->onPreSendCommand($this->createDummyPreSend('theBarProcessorName', $message)); $this->assertEquals([ 'enqueue.command_name' => 'theBarProcessorName', @@ -183,7 +191,7 @@ public function testShouldForceExclusiveCommandQueue() 'aFooQueueName' => 'aFooProcessorName', ]); - $extension->onPreSend(Config::COMMAND_TOPIC, $message); + $extension->onPreSendCommand($this->createDummyPreSend('aFooProcessorName', $message)); $this->assertEquals([ 'enqueue.command_name' => 'aFooProcessorName', @@ -191,4 +199,14 @@ public function testShouldForceExclusiveCommandQueue() 'enqueue.processor_queue_name' => 'aFooQueueName', ], $message->getProperties()); } + + private function createDummyPreSend(string $commandOrTopic, Message $message): PreSend + { + return new PreSend( + $commandOrTopic, + $message, + $this->createMock(ProducerInterface::class), + $this->createMock(DriverInterface::class) + ); + } } diff --git a/pkg/enqueue/Tests/Client/Extension/PrepareBodyExtensionTest.php b/pkg/enqueue/Tests/Client/Extension/PrepareBodyExtensionTest.php index e99e3e39e..b1fd98509 100644 --- a/pkg/enqueue/Tests/Client/Extension/PrepareBodyExtensionTest.php +++ b/pkg/enqueue/Tests/Client/Extension/PrepareBodyExtensionTest.php @@ -8,6 +8,7 @@ use Enqueue\Client\Message; use Enqueue\Client\PreSend; use Enqueue\Client\ProducerInterface; +use Enqueue\Tests\Mocks\JsonSerializableObject; use PHPUnit\Framework\TestCase; class PrepareBodyExtensionTest extends TestCase @@ -132,11 +133,3 @@ private function createDummyPreSendContext($commandOrTopic, $message): PreSend ); } } - -class JsonSerializableObject implements \JsonSerializable -{ - public function jsonSerialize() - { - return ['foo' => 'fooVal']; - } -} diff --git a/pkg/enqueue/Tests/Client/ProducerSendCommandTest.php b/pkg/enqueue/Tests/Client/ProducerSendCommandTest.php new file mode 100644 index 000000000..057d22730 --- /dev/null +++ b/pkg/enqueue/Tests/Client/ProducerSendCommandTest.php @@ -0,0 +1,508 @@ +createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ->with(self::identicalTo($message)) + ; + $driver + ->expects($this->never()) + ->method('sendToRouter') + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendCommand('command', $message); + + $expectedProperties = [ + 'enqueue.processor_name' => 'a_router_processor_name', + 'enqueue.topic_name' => '__command__', + 'enqueue.command_name' => 'command', + 'enqueue.processor_queue_name' => 'a_router_queue', + ]; + + self::assertEquals($expectedProperties, $message->getProperties()); + } + + public function testShouldSendCommandWithReply() + { + $message = new Message(); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ->with(self::identicalTo($message)) + ; + $driver + ->expects($this->never()) + ->method('sendToRouter') + ; + + $expectedPromiseMock = $this->createMock(Promise::class); + + $rpcFactoryMock = $this->createRpcFactoryMock(); + $rpcFactoryMock + ->expects($this->once()) + ->method('createReplyTo') + ->willReturn('theReplyQueue') + ; + $rpcFactoryMock + ->expects($this->once()) + ->method('createPromise') + ->with( + 'theReplyQueue', + $this->logicalNot($this->isEmpty()), + 60000 + ) + ->willReturn($expectedPromiseMock) + ; + + $producer = new Producer($driver, $rpcFactoryMock); + $actualPromise = $producer->sendCommand('command', $message, true); + + $this->assertSame($expectedPromiseMock, $actualPromise); + + self::assertEquals('theReplyQueue', $message->getReplyTo()); + self::assertNotEmpty($message->getCorrelationId()); + } + + public function testShouldSendCommandWithReplyAndCustomReplyQueueAndCorrelationId() + { + $message = new Message(); + $message->setReplyTo('theCustomReplyQueue'); + $message->setCorrelationId('theCustomCorrelationId'); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ->with(self::identicalTo($message)) + ; + $driver + ->expects($this->never()) + ->method('sendToRouter') + ; + + $expectedPromiseMock = $this->createMock(Promise::class); + + $rpcFactoryMock = $this->createRpcFactoryMock(); + $rpcFactoryMock + ->expects($this->never()) + ->method('createReplyTo') + ; + $rpcFactoryMock + ->expects($this->once()) + ->method('createPromise') + ->with( + 'theCustomReplyQueue', + 'theCustomCorrelationId', + 60000 + ) + ->willReturn($expectedPromiseMock) + ; + + $producer = new Producer($driver, $rpcFactoryMock); + $actualPromise = $producer->sendCommand('command', $message, true); + + $this->assertSame($expectedPromiseMock, $actualPromise); + + self::assertEquals('theCustomReplyQueue', $message->getReplyTo()); + self::assertSame('theCustomCorrelationId', $message->getCorrelationId()); + } + + public function testShouldOverwriteExpectedMessageProperties() + { + $message = new Message(); + $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topicShouldBeOverwritten'); + $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'topicShouldBeOverwritten'); + $message->setScope('topicShouldBeOverwritten'); + + $driver = $this->createDriverStub(); + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendCommand('expectedCommand', $message); + + $expectedProperties = [ + 'enqueue.processor_name' => 'a_router_processor_name', + 'enqueue.topic_name' => '__command__', + 'enqueue.command_name' => 'expectedCommand', + 'enqueue.processor_queue_name' => 'a_router_queue', + ]; + + self::assertEquals($expectedProperties, $message->getProperties()); + self::assertSame(Message::SCOPE_APP, $message->getScope()); + } + + public function testShouldSendCommandWithNormalPriorityByDefault() + { + $message = new Message(); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ->with(self::identicalTo($message)) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendCommand('command', $message); + + self::assertSame(MessagePriority::NORMAL, $message->getPriority()); + } + + public function testShouldSendCommandWithCustomPriority() + { + $message = new Message(); + $message->setPriority(MessagePriority::HIGH); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ->with(self::identicalTo($message)) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendCommand('command', $message); + + self::assertSame(MessagePriority::HIGH, $message->getPriority()); + } + + public function testShouldSendCommandWithGeneratedMessageId() + { + $message = new Message(); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ->with(self::identicalTo($message)) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendCommand('command', $message); + + self::assertNotEmpty($message->getMessageId()); + } + + public function testShouldSendCommandWithCustomMessageId() + { + $message = new Message(); + $message->setMessageId('theCustomMessageId'); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ->with(self::identicalTo($message)) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendCommand('command', $message); + + self::assertSame('theCustomMessageId', $message->getMessageId()); + } + + public function testShouldSendCommandWithGeneratedTimestamp() + { + $message = new Message(); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ->with(self::identicalTo($message)) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendCommand('command', $message); + + self::assertNotEmpty($message->getTimestamp()); + } + + public function testShouldSendCommandWithCustomTimestamp() + { + $message = new Message(); + $message->setTimestamp('theCustomTimestamp'); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ->with(self::identicalTo($message)) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendCommand('command', $message); + + self::assertSame('theCustomTimestamp', $message->getTimestamp()); + } + + public function testShouldSerializeMessageToJsonByDefault() + { + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ->willReturnCallback(function (Message $message) { + $this->assertSame('{"foo":"fooVal"}', $message->getBody()); + }) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendCommand('command', ['foo' => 'fooVal']); + } + + public function testShouldSerializeMessageByCustomExtension() + { + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ->willReturnCallback(function (Message $message) { + $this->assertSame('theCommandBodySerializedByCustomExtension', $message->getBody()); + }) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock(), new CustomPrepareBodyClientExtension()); + $producer->sendCommand('command', ['foo' => 'fooVal']); + } + + public function testShouldSendCommandToApplicationRouter() + { + $message = new Message(); + $message->setBody('aBody'); + $message->setScope(Message::SCOPE_APP); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ->willReturnCallback(function (Message $message) { + self::assertSame('aBody', $message->getBody()); + self::assertSame('a_router_processor_name', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)); + self::assertSame('a_router_queue', $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)); + }) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendCommand('command', $message); + } + + public function testShouldSendCommandWithCustomProcessorAndQueueNamePropertiesSetToApplicationRouter() + { + $message = new Message(); + $message->setBody('aBody'); + $message->setScope(Message::SCOPE_APP); + $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aCustomProcessor'); + $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aCustomProcessorQueue'); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ->willReturnCallback(function (Message $message) { + self::assertSame('aBody', $message->getBody()); + self::assertSame('aCustomProcessor', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)); + self::assertSame('aCustomProcessorQueue', $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)); + }) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendCommand('command', $message); + } + + public function testShouldCallPreSendCommandExtensionMethodWhenSendToBus() + { + $message = new Message(); + $message->setBody('aBody'); + $message->setScope(Message::SCOPE_MESSAGE_BUS); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ; + + $extension = $this->createMock(ExtensionInterface::class); + + $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension); + + $extension + ->expects($this->at(0)) + ->method('onPreSendCommand') + ->willReturnCallback(function (PreSend $context) use ($message, $producer, $driver) { + $this->assertSame($message, $context->getMessage()); + $this->assertSame($producer, $context->getProducer()); + $this->assertSame($driver, $context->getDriver()); + $this->assertSame('command', $context->getCommand()); + + $this->assertEquals($message, $context->getOriginalMessage()); + $this->assertNotSame($message, $context->getOriginalMessage()); + }); + + $extension + ->expects($this->never()) + ->method('onPreSendEvent') + ; + + $producer->sendCommand('command', $message); + } + + public function testShouldCallPreSendCommandExtensionMethodWhenSendToApplicationRouter() + { + $message = new Message(); + $message->setBody('aBody'); + $message->setScope(Message::SCOPE_APP); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ; + + $extension = $this->createMock(ExtensionInterface::class); + + $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension); + + $extension + ->expects($this->at(0)) + ->method('onPreSendCommand') + ->willReturnCallback(function (PreSend $context) use ($message, $producer, $driver) { + $this->assertSame($message, $context->getMessage()); + $this->assertSame($producer, $context->getProducer()); + $this->assertSame($driver, $context->getDriver()); + $this->assertSame('command', $context->getCommand()); + + $this->assertEquals($message, $context->getOriginalMessage()); + $this->assertNotSame($message, $context->getOriginalMessage()); + }); + + $extension + ->expects($this->never()) + ->method('onPreSendEvent') + ; + + $producer->sendCommand('command', $message); + } + + public function testShouldCallPreDriverSendExtensionMethod() + { + $message = new Message(); + $message->setBody('aBody'); + $message->setScope(Message::SCOPE_APP); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ; + + $extension = $this->createMock(ExtensionInterface::class); + + $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension); + + $extension + ->expects($this->at(0)) + ->method('onDriverPreSend') + ->willReturnCallback(function (DriverPreSend $context) use ($message, $producer, $driver) { + $this->assertSame($message, $context->getMessage()); + $this->assertSame($producer, $context->getProducer()); + $this->assertSame($driver, $context->getDriver()); + $this->assertSame('command', $context->getCommand()); + + $this->assertTrue($context->isEvent()); + }); + + $producer->sendCommand('command', $message); + } + + public function testShouldCallPostSendExtensionMethod() + { + $message = new Message(); + $message->setBody('aBody'); + $message->setScope(Message::SCOPE_APP); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ; + + $extension = $this->createMock(ExtensionInterface::class); + + $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension); + + $extension + ->expects($this->at(0)) + ->method('onDriverPreSend') + ->willReturnCallback(function (PostSend $context) use ($message, $producer, $driver) { + $this->assertSame($message, $context->getMessage()); + $this->assertSame($producer, $context->getProducer()); + $this->assertSame($driver, $context->getDriver()); + $this->assertSame('command', $context->getCommand()); + + $this->assertFalse($context->isEvent()); + }); + + $producer->sendCommand('command', $message); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function createRpcFactoryMock(): RpcFactory + { + return $this->createMock(RpcFactory::class); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function createDriverStub(): DriverInterface + { + $config = new Config( + 'a_prefix', + 'an_app', + 'a_router_topic', + 'a_router_queue', + 'a_default_processor_queue', + 'a_router_processor_name' + ); + + $driverMock = $this->createMock(DriverInterface::class); + $driverMock + ->expects($this->any()) + ->method('getConfig') + ->willReturn($config) + ; + + return $driverMock; + } +} diff --git a/pkg/enqueue/Tests/Client/ProducerSendEventTest.php b/pkg/enqueue/Tests/Client/ProducerSendEventTest.php new file mode 100644 index 000000000..a5d45d3dc --- /dev/null +++ b/pkg/enqueue/Tests/Client/ProducerSendEventTest.php @@ -0,0 +1,546 @@ +createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToRouter') + ->with(self::identicalTo($message)) + ; + $driver + ->expects($this->never()) + ->method('sendToProcessor') + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendEvent('topic', $message); + + $expectedProperties = [ + 'enqueue.topic_name' => 'topic', + ]; + + self::assertEquals($expectedProperties, $message->getProperties()); + } + + public function testShouldOverwriteTopicProperty() + { + $message = new Message(); + $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'topicShouldBeOverwritten'); + + $driver = $this->createDriverStub(); + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendEvent('expectedTopic', $message); + + $expectedProperties = [ + 'enqueue.topic_name' => 'expectedTopic', + ]; + + self::assertEquals($expectedProperties, $message->getProperties()); + } + + public function testShouldSendEventWithNormalPriorityByDefault() + { + $message = new Message(); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToRouter') + ->with(self::identicalTo($message)) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendEvent('topic', $message); + + self::assertSame(MessagePriority::NORMAL, $message->getPriority()); + } + + public function testShouldSendEventWithCustomPriority() + { + $message = new Message(); + $message->setPriority(MessagePriority::HIGH); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToRouter') + ->with(self::identicalTo($message)) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendEvent('topic', $message); + + self::assertSame(MessagePriority::HIGH, $message->getPriority()); + } + + public function testShouldSendEventWithGeneratedMessageId() + { + $message = new Message(); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToRouter') + ->with(self::identicalTo($message)) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendEvent('topic', $message); + + self::assertNotEmpty($message->getMessageId()); + } + + public function testShouldSendEventWithCustomMessageId() + { + $message = new Message(); + $message->setMessageId('theCustomMessageId'); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToRouter') + ->with(self::identicalTo($message)) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendEvent('topic', $message); + + self::assertSame('theCustomMessageId', $message->getMessageId()); + } + + public function testShouldSendEventWithGeneratedTimestamp() + { + $message = new Message(); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToRouter') + ->with(self::identicalTo($message)) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendEvent('topic', $message); + + self::assertNotEmpty($message->getTimestamp()); + } + + public function testShouldSendEventWithCustomTimestamp() + { + $message = new Message(); + $message->setTimestamp('theCustomTimestamp'); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToRouter') + ->with(self::identicalTo($message)) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendEvent('topic', $message); + + self::assertSame('theCustomTimestamp', $message->getTimestamp()); + } + + public function testShouldSerializeMessageToJsonByDefault() + { + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToRouter') + ->willReturnCallback(function (Message $message) { + $this->assertSame('{"foo":"fooVal"}', $message->getBody()); + }) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendEvent('topic', ['foo' => 'fooVal']); + } + + public function testShouldSerializeMessageByCustomExtension() + { + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToRouter') + ->willReturnCallback(function (Message $message) { + $this->assertSame('theEventBodySerializedByCustomExtension', $message->getBody()); + }) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock(), new CustomPrepareBodyClientExtension()); + $producer->sendEvent('topic', ['foo' => 'fooVal']); + } + + public function testThrowIfSendEventToMessageBusWithProcessorNamePropertySet() + { + $message = new Message(); + $message->setBody(''); + $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aProcessor'); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->never()) + ->method('sendToRouter') + ; + $driver + ->expects($this->never()) + ->method('sendToProcessor') + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('The enqueue.processor_name property must not be set for messages that are sent to message bus.'); + $producer->sendEvent('topic', $message); + } + + public function testThrowIfSendEventToMessageBusWithProcessorQueueNamePropertySet() + { + $message = new Message(); + $message->setBody(''); + $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aProcessorQueue'); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->never()) + ->method('sendToRouter') + ; + $driver + ->expects($this->never()) + ->method('sendToProcessor') + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('The enqueue.processor_queue_name property must not be set for messages that are sent to message bus.'); + $producer->sendEvent('topic', $message); + } + + public function testShouldSendEventToApplicationRouter() + { + $message = new Message(); + $message->setBody('aBody'); + $message->setScope(Message::SCOPE_APP); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->never()) + ->method('sendToRouter') + ; + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ->willReturnCallback(function (Message $message) { + self::assertSame('aBody', $message->getBody()); + self::assertSame('a_router_processor_name', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)); + self::assertSame('a_router_queue', $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)); + }) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendEvent('topic', $message); + } + + public function testShouldSendEventWithCustomProcessorAndQueueNamePropertiesSetToApplicationRouter() + { + $message = new Message(); + $message->setBody('aBody'); + $message->setScope(Message::SCOPE_APP); + $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aCustomProcessor'); + $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aCustomProcessorQueue'); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->never()) + ->method('sendToRouter') + ; + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ->willReturnCallback(function (Message $message) { + self::assertSame('aBody', $message->getBody()); + self::assertSame('aCustomProcessor', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)); + self::assertSame('aCustomProcessorQueue', $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)); + }) + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + $producer->sendEvent('topic', $message); + } + + public function testThrowIfUnSupportedScopeGivenOnSend() + { + $message = new Message(); + $message->setScope('iDontKnowScope'); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->never()) + ->method('sendToRouter') + ; + $driver + ->expects($this->never()) + ->method('sendToProcessor') + ; + + $producer = new Producer($driver, $this->createRpcFactoryMock()); + + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('The message scope "iDontKnowScope" is not supported.'); + $producer->sendEvent('topic', $message); + } + + public function testShouldCallPreSendEventExtensionMethodWhenSendToBus() + { + $message = new Message(); + $message->setBody('aBody'); + $message->setScope(Message::SCOPE_MESSAGE_BUS); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToRouter') + ; + + $extension = $this->createMock(ExtensionInterface::class); + + $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension); + + $extension + ->expects($this->at(0)) + ->method('onPreSendEvent') + ->willReturnCallback(function (PreSend $context) use ($message, $producer, $driver) { + $this->assertSame($message, $context->getMessage()); + $this->assertSame($producer, $context->getProducer()); + $this->assertSame($driver, $context->getDriver()); + $this->assertSame('topic', $context->getTopic()); + + $this->assertEquals($message, $context->getOriginalMessage()); + $this->assertNotSame($message, $context->getOriginalMessage()); + }); + + $extension + ->expects($this->never()) + ->method('onPreSendCommand') + ; + + $producer->sendEvent('topic', $message); + } + + public function testShouldCallPreSendEventExtensionMethodWhenSendToApplicationRouter() + { + $message = new Message(); + $message->setBody('aBody'); + $message->setScope(Message::SCOPE_APP); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ; + + $extension = $this->createMock(ExtensionInterface::class); + + $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension); + + $extension + ->expects($this->at(0)) + ->method('onPreSendEvent') + ->willReturnCallback(function (PreSend $context) use ($message, $producer, $driver) { + $this->assertSame($message, $context->getMessage()); + $this->assertSame($producer, $context->getProducer()); + $this->assertSame($driver, $context->getDriver()); + $this->assertSame('topic', $context->getTopic()); + + $this->assertEquals($message, $context->getOriginalMessage()); + $this->assertNotSame($message, $context->getOriginalMessage()); + }); + + $extension + ->expects($this->never()) + ->method('onPreSendCommand') + ; + + $producer->sendEvent('topic', $message); + } + + public function testShouldCallPreDriverSendExtensionMethodWhenSendToMessageBus() + { + $message = new Message(); + $message->setBody('aBody'); + $message->setScope(Message::SCOPE_MESSAGE_BUS); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToRouter') + ; + + $extension = $this->createMock(ExtensionInterface::class); + + $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension); + + $extension + ->expects($this->at(0)) + ->method('onDriverPreSend') + ->willReturnCallback(function (DriverPreSend $context) use ($message, $producer, $driver) { + $this->assertSame($message, $context->getMessage()); + $this->assertSame($producer, $context->getProducer()); + $this->assertSame($driver, $context->getDriver()); + $this->assertSame('topic', $context->getTopic()); + + $this->assertTrue($context->isEvent()); + }); + + $producer->sendEvent('topic', $message); + } + + public function testShouldCallPreDriverSendExtensionMethodWhenSendToApplicationRouter() + { + $message = new Message(); + $message->setBody('aBody'); + $message->setScope(Message::SCOPE_APP); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ; + + $extension = $this->createMock(ExtensionInterface::class); + + $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension); + + $extension + ->expects($this->at(0)) + ->method('onDriverPreSend') + ->willReturnCallback(function (DriverPreSend $context) use ($message, $producer, $driver) { + $this->assertSame($message, $context->getMessage()); + $this->assertSame($producer, $context->getProducer()); + $this->assertSame($driver, $context->getDriver()); + $this->assertSame('topic', $context->getTopic()); + + $this->assertTrue($context->isEvent()); + }); + + $producer->sendEvent('topic', $message); + } + + public function testShouldCallPostSendExtensionMethodWhenSendToMessageBus() + { + $message = new Message(); + $message->setBody('aBody'); + $message->setScope(Message::SCOPE_MESSAGE_BUS); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToRouter') + ; + + $extension = $this->createMock(ExtensionInterface::class); + + $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension); + + $extension + ->expects($this->at(0)) + ->method('onPostSend') + ->willReturnCallback(function (PostSend $context) use ($message, $producer, $driver) { + $this->assertSame($message, $context->getMessage()); + $this->assertSame($producer, $context->getProducer()); + $this->assertSame($driver, $context->getDriver()); + $this->assertSame('topic', $context->getTopic()); + + $this->assertTrue($context->isEvent()); + }); + + $producer->sendEvent('topic', $message); + } + + public function testShouldCallPostSendExtensionMethodWhenSendToApplicationRouter() + { + $message = new Message(); + $message->setBody('aBody'); + $message->setScope(Message::SCOPE_APP); + + $driver = $this->createDriverStub(); + $driver + ->expects($this->once()) + ->method('sendToProcessor') + ; + + $extension = $this->createMock(ExtensionInterface::class); + + $producer = new Producer($driver, $this->createRpcFactoryMock(), $extension); + + $extension + ->expects($this->at(0)) + ->method('onDriverPreSend') + ->willReturnCallback(function (PostSend $context) use ($message, $producer, $driver) { + $this->assertSame($message, $context->getMessage()); + $this->assertSame($producer, $context->getProducer()); + $this->assertSame($driver, $context->getDriver()); + $this->assertSame('topic', $context->getTopic()); + + $this->assertTrue($context->isEvent()); + }); + + $producer->sendEvent('topic', $message); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function createRpcFactoryMock(): RpcFactory + { + return $this->createMock(RpcFactory::class); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function createDriverStub(): DriverInterface + { + $config = new Config( + 'a_prefix', + 'an_app', + 'a_router_topic', + 'a_router_queue', + 'a_default_processor_queue', + 'a_router_processor_name' + ); + + $driverMock = $this->createMock(DriverInterface::class); + $driverMock + ->expects($this->any()) + ->method('getConfig') + ->willReturn($config) + ; + + return $driverMock; + } +} diff --git a/pkg/enqueue/Tests/Client/ProducerTest.php b/pkg/enqueue/Tests/Client/ProducerTest.php index be8554cdd..4fbf4baea 100644 --- a/pkg/enqueue/Tests/Client/ProducerTest.php +++ b/pkg/enqueue/Tests/Client/ProducerTest.php @@ -2,11 +2,8 @@ namespace Enqueue\Tests\Client; -use Enqueue\Client\Config; use Enqueue\Client\DriverInterface; use Enqueue\Client\ExtensionInterface; -use Enqueue\Client\Message; -use Enqueue\Client\MessagePriority; use Enqueue\Client\Producer; use Enqueue\Client\ProducerInterface; use Enqueue\Rpc\RpcFactory; @@ -22,393 +19,38 @@ public function testShouldImplementProducerInterface() self::assertClassImplements(ProducerInterface::class, Producer::class); } - public function testCouldBeConstructedWithDriverAsFirstArgument() + public function testShouldBeFinal() { - new Producer($this->createDriverStub(), $this->createRpcFactory()); + self::assertClassFinal(Producer::class); } - public function testShouldSendMessageToRouter() + public function testCouldBeConstructedWithRequiredArguments() { - $message = new Message(); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->once()) - ->method('sendToRouter') - ->with(self::identicalTo($message)) - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent('topic', $message); - - $expectedProperties = [ - 'enqueue.topic_name' => 'topic', - ]; - - self::assertEquals($expectedProperties, $message->getProperties()); - } - - public function testShouldSendMessageWithNormalPriorityByDefault() - { - $message = new Message(); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->once()) - ->method('sendToRouter') - ->with(self::identicalTo($message)) - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent('topic', $message); - - self::assertSame(MessagePriority::NORMAL, $message->getPriority()); - } - - public function testShouldSendMessageWithCustomPriority() - { - $message = new Message(); - $message->setPriority(MessagePriority::HIGH); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->once()) - ->method('sendToRouter') - ->with(self::identicalTo($message)) - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent('topic', $message); - - self::assertSame(MessagePriority::HIGH, $message->getPriority()); - } - - public function testShouldSendMessageWithGeneratedMessageId() - { - $message = new Message(); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->once()) - ->method('sendToRouter') - ->with(self::identicalTo($message)) - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent('topic', $message); - - self::assertNotEmpty($message->getMessageId()); - } - - public function testShouldSendMessageWithCustomMessageId() - { - $message = new Message(); - $message->setMessageId('theCustomMessageId'); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->once()) - ->method('sendToRouter') - ->with(self::identicalTo($message)) - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent('topic', $message); - - self::assertSame('theCustomMessageId', $message->getMessageId()); + new Producer($this->createDriverMock(), $this->createRpcFactoryMock()); } - public function testShouldSendMessageWithGeneratedTimestamp() + public function testCouldBeConstructedWithOptionalArguments() { - $message = new Message(); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->once()) - ->method('sendToRouter') - ->with(self::identicalTo($message)) - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent('topic', $message); - - self::assertNotEmpty($message->getTimestamp()); - } - - public function testShouldSendMessageWithCustomTimestamp() - { - $message = new Message(); - $message->setTimestamp('theCustomTimestamp'); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->once()) - ->method('sendToRouter') - ->with(self::identicalTo($message)) - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent('topic', $message); - - self::assertSame('theCustomTimestamp', $message->getTimestamp()); - } - - public function testShouldSendJsonSerializableObjectAsJsonStringToMessageBus() - { - $object = new JsonSerializableObject(); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->once()) - ->method('sendToRouter') - ->willReturnCallback(function (Message $message) { - self::assertSame('{"foo":"fooVal"}', $message->getBody()); - self::assertSame('application/json', $message->getContentType()); - }) - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent('topic', $object); - } - - public function testShouldSendMessageJsonSerializableBodyAsJsonStringToMessageBus() - { - $object = new JsonSerializableObject(); - - $message = new Message(); - $message->setBody($object); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->once()) - ->method('sendToRouter') - ->willReturnCallback(function (Message $message) { - self::assertSame('{"foo":"fooVal"}', $message->getBody()); - self::assertSame('application/json', $message->getContentType()); - }) - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent('topic', $message); - } - - public function testThrowIfTryToSendMessageToMessageBusWithProcessorNamePropertySet() - { - $object = new JsonSerializableObject(); - - $message = new Message(); - $message->setBody($object); - $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aProcessor'); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->never()) - ->method('sendToRouter') - ; - $driver - ->expects($this->never()) - ->method('sendToProcessor') - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('The enqueue.processor_name property must not be set for messages that are sent to message bus.'); - $producer->sendEvent('topic', $message); - } - - public function testThrowIfTryToSendMessageToMessageBusWithProcessorQueueNamePropertySet() - { - $object = new JsonSerializableObject(); - - $message = new Message(); - $message->setBody($object); - $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aProcessorQueue'); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->never()) - ->method('sendToRouter') - ; - $driver - ->expects($this->never()) - ->method('sendToProcessor') - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('The enqueue.processor_queue_name property must not be set for messages that are sent to message bus.'); - $producer->sendEvent('topic', $message); - } - - public function testShouldSendMessageToApplicationRouter() - { - $message = new Message(); - $message->setBody('aBody'); - $message->setScope(Message::SCOPE_APP); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->never()) - ->method('sendToRouter') - ; - $driver - ->expects($this->once()) - ->method('sendToProcessor') - ->willReturnCallback(function (Message $message) { - self::assertSame('aBody', $message->getBody()); - self::assertSame('a_router_processor_name', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)); - self::assertSame('a_router_queue', $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)); - }) - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent('topic', $message); - } - - public function testShouldSendToCustomMessageToApplicationRouter() - { - $message = new Message(); - $message->setBody('aBody'); - $message->setScope(Message::SCOPE_APP); - $message->setProperty(Config::PARAMETER_PROCESSOR_NAME, 'aCustomProcessor'); - $message->setProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME, 'aCustomProcessorQueue'); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->never()) - ->method('sendToRouter') - ; - $driver - ->expects($this->once()) - ->method('sendToProcessor') - ->willReturnCallback(function (Message $message) { - self::assertSame('aBody', $message->getBody()); - self::assertSame('aCustomProcessor', $message->getProperty(Config::PARAMETER_PROCESSOR_NAME)); - self::assertSame('aCustomProcessorQueue', $message->getProperty(Config::PARAMETER_PROCESSOR_QUEUE_NAME)); - }) - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - $producer->sendEvent('topic', $message); - } - - public function testThrowIfUnSupportedScopeGivenOnSend() - { - $message = new Message(); - $message->setScope('iDontKnowScope'); - - $driver = $this->createDriverStub(); - $driver - ->expects($this->never()) - ->method('sendToRouter') - ; - $driver - ->expects($this->never()) - ->method('sendToProcessor') - ; - - $producer = new Producer($driver, $this->createRpcFactory()); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('The message scope "iDontKnowScope" is not supported.'); - $producer->sendEvent('topic', $message); - } - - public function testShouldCallPreSendPostSendExtensionMethodsWhenSendToRouter() - { - $message = new Message(); - $message->setBody('aBody'); - $message->setScope(Message::SCOPE_MESSAGE_BUS); - - $extension = $this->createMock(ExtensionInterface::class); - $extension - ->expects($this->at(0)) - ->method('onPreSend') - ->with($this->identicalTo('topic'), $this->identicalTo($message)) - ; - $extension - ->expects($this->at(1)) - ->method('onPostSend') - ->with($this->identicalTo('topic'), $this->identicalTo($message)) - ; - - $driver = $this->createDriverStub(); - $driver - ->expects($this->once()) - ->method('sendToRouter') - ; - - $producer = new Producer($driver, $this->createRpcFactory(), $extension); - $producer->sendEvent('topic', $message); - } - - public function testShouldCallPreSendPostSendExtensionMethodsWhenSendToProcessor() - { - $message = new Message(); - $message->setBody('aBody'); - $message->setScope(Message::SCOPE_APP); - - $extension = $this->createMock(ExtensionInterface::class); - $extension - ->expects($this->at(0)) - ->method('onPreSend') - ->with($this->identicalTo('topic'), $this->identicalTo($message)) - ; - $extension - ->expects($this->at(1)) - ->method('onPostSend') - ->with($this->identicalTo('topic'), $this->identicalTo($message)) - ; - - $driver = $this->createDriverStub(); - $driver - ->expects($this->once()) - ->method('sendToProcessor') - ; - - $producer = new Producer($driver, $this->createRpcFactory(), $extension); - $producer->sendEvent('topic', $message); + new Producer( + $this->createDriverMock(), + $this->createRpcFactoryMock(), + $this->createMock(ExtensionInterface::class) + ); } /** - * @return \PHPUnit_Framework_MockObject_MockObject|RpcFactory + * @return \PHPUnit_Framework_MockObject_MockObject */ - private function createRpcFactory() + private function createRpcFactoryMock(): RpcFactory { return $this->createMock(RpcFactory::class); } /** - * @return \PHPUnit_Framework_MockObject_MockObject|DriverInterface + * @return \PHPUnit_Framework_MockObject_MockObject */ - private function createDriverStub() - { - $config = new Config( - 'a_prefix', - 'an_app', - 'a_router_topic', - 'a_router_queue', - 'a_default_processor_queue', - 'a_router_processor_name' - ); - - $driverMock = $this->createMock(DriverInterface::class); - $driverMock - ->expects($this->any()) - ->method('getConfig') - ->willReturn($config) - ; - - return $driverMock; - } -} - -class JsonSerializableObject implements \JsonSerializable -{ - public function jsonSerialize() + private function createDriverMock(): DriverInterface { - return ['foo' => 'fooVal']; + return $this->createMock(DriverInterface::class); } } diff --git a/pkg/enqueue/Tests/Mocks/CustomPrepareBodyClientExtension.php b/pkg/enqueue/Tests/Mocks/CustomPrepareBodyClientExtension.php new file mode 100644 index 000000000..4d68fa1f3 --- /dev/null +++ b/pkg/enqueue/Tests/Mocks/CustomPrepareBodyClientExtension.php @@ -0,0 +1,22 @@ +getMessage()->setBody('theCommandBodySerializedByCustomExtension'); + } + + public function onPreSendEvent(PreSend $context): void + { + $context->getMessage()->setBody('theEventBodySerializedByCustomExtension'); + } +} diff --git a/pkg/enqueue/Tests/Mocks/JsonSerializableObject.php b/pkg/enqueue/Tests/Mocks/JsonSerializableObject.php new file mode 100644 index 000000000..5b74106dc --- /dev/null +++ b/pkg/enqueue/Tests/Mocks/JsonSerializableObject.php @@ -0,0 +1,11 @@ + 'fooVal']; + } +} diff --git a/pkg/job-queue/Tests/JobProcessorTest.php b/pkg/job-queue/Tests/JobProcessorTest.php index bb2386895..d9ce75e98 100644 --- a/pkg/job-queue/Tests/JobProcessorTest.php +++ b/pkg/job-queue/Tests/JobProcessorTest.php @@ -2,7 +2,7 @@ namespace Enqueue\JobQueue\Tests; -use Enqueue\Client\Producer; +use Enqueue\Client\ProducerInterface; use Enqueue\JobQueue\Doctrine\JobStorage; use Enqueue\JobQueue\DuplicateJobException; use Enqueue\JobQueue\Job; @@ -539,18 +539,18 @@ public function testInterruptRootJobShouldUpdateJobAndSetInterruptedTrueAndStopp } /** - * @return \PHPUnit_Framework_MockObject_MockObject|JobStorage + * @return \PHPUnit_Framework_MockObject_MockObject */ - private function createJobStorage() + private function createJobStorage(): JobStorage { return $this->createMock(JobStorage::class); } /** - * @return \PHPUnit_Framework_MockObject_MockObject|Producer + * @return \PHPUnit_Framework_MockObject_MockObject */ - private function createProducerMock() + private function createProducerMock(): ProducerInterface { - return $this->createMock(Producer::class); + return $this->createMock(ProducerInterface::class); } } diff --git a/pkg/test/ClassExtensionTrait.php b/pkg/test/ClassExtensionTrait.php index b9dea0c8b..e2bd84069 100644 --- a/pkg/test/ClassExtensionTrait.php +++ b/pkg/test/ClassExtensionTrait.php @@ -23,4 +23,14 @@ public function assertClassImplements($expected, $actual) sprintf('Failed assert that class %s implements %s interface.', $actual, $expected) ); } + + public function assertClassFinal($actual) + { + $rc = new \ReflectionClass($actual); + + $this->assertTrue( + $rc->isFinal(), + sprintf('Failed assert that class %s is final.', $actual) + ); + } } From 86de6aaf7833edd24865865a7d14a6ce3a3d2315 Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Thu, 30 Aug 2018 13:14:36 +0300 Subject: [PATCH 4/7] add tests for pre send object. --- pkg/enqueue/Tests/Client/PreSendTest.php | 126 +++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 pkg/enqueue/Tests/Client/PreSendTest.php diff --git a/pkg/enqueue/Tests/Client/PreSendTest.php b/pkg/enqueue/Tests/Client/PreSendTest.php new file mode 100644 index 000000000..d1e5595ba --- /dev/null +++ b/pkg/enqueue/Tests/Client/PreSendTest.php @@ -0,0 +1,126 @@ +createProducerMock(), + $this->createDriverMock() + ); + } + + public function testShouldAllowGetArgumentSetInConstructor() + { + $expectedCommandOrTopic = 'theCommandOrTopic'; + $expectedMessage = new Message(); + $expectedProducer = $this->createProducerMock(); + $expectedDriver = $this->createDriverMock(); + + $context = new PreSend( + $expectedCommandOrTopic, + $expectedMessage, + $expectedProducer, + $expectedDriver + ); + + $this->assertSame($expectedCommandOrTopic, $context->getTopic()); + $this->assertSame($expectedCommandOrTopic, $context->getCommand()); + $this->assertSame($expectedMessage, $context->getMessage()); + $this->assertSame($expectedProducer, $context->getProducer()); + $this->assertSame($expectedDriver, $context->getDriver()); + + $this->assertEquals($expectedMessage, $context->getOriginalMessage()); + $this->assertNotSame($expectedMessage, $context->getOriginalMessage()); + } + + public function testCouldChangeTopic() + { + $context = new PreSend( + 'aCommandOrTopic', + new Message(), + $this->createProducerMock(), + $this->createDriverMock() + ); + + //guard + $this->assertSame('aCommandOrTopic', $context->getTopic()); + + $context->changeTopic('theChangedTopic'); + + $this->assertSame('theChangedTopic', $context->getTopic()); + } + + public function testCouldChangeCommand() + { + $context = new PreSend( + 'aCommandOrTopic', + new Message(), + $this->createProducerMock(), + $this->createDriverMock() + ); + + //guard + $this->assertSame('aCommandOrTopic', $context->getCommand()); + + $context->changeCommand('theChangedCommand'); + + $this->assertSame('theChangedCommand', $context->getCommand()); + } + + public function testCouldChangeBody() + { + $context = new PreSend( + 'aCommandOrTopic', + new Message('aBody'), + $this->createProducerMock(), + $this->createDriverMock() + ); + + //guard + $this->assertSame('aBody', $context->getMessage()->getBody()); + $this->assertNull($context->getMessage()->getContentType()); + + $context->changeBody('theChangedBody'); + $this->assertSame('theChangedBody', $context->getMessage()->getBody()); + $this->assertNull($context->getMessage()->getContentType()); + + $context->changeBody('theChangedBodyAgain', 'foo/bar'); + $this->assertSame('theChangedBodyAgain', $context->getMessage()->getBody()); + $this->assertSame('foo/bar', $context->getMessage()->getContentType()); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function createDriverMock(): DriverInterface + { + return $this->createMock(DriverInterface::class); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function createProducerMock(): ProducerInterface + { + return $this->createMock(ProducerInterface::class); + } +} From f521084537fbcd061cccf0e5f64b36739e033b13 Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Thu, 30 Aug 2018 13:58:48 +0300 Subject: [PATCH 5/7] add tests --- .../Tests/Client/DriverPreSendTest.php | 94 +++++++++++++++++++ pkg/enqueue/Tests/Client/PostSendTest.php | 94 +++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 pkg/enqueue/Tests/Client/DriverPreSendTest.php create mode 100644 pkg/enqueue/Tests/Client/PostSendTest.php diff --git a/pkg/enqueue/Tests/Client/DriverPreSendTest.php b/pkg/enqueue/Tests/Client/DriverPreSendTest.php new file mode 100644 index 000000000..62d493b7b --- /dev/null +++ b/pkg/enqueue/Tests/Client/DriverPreSendTest.php @@ -0,0 +1,94 @@ +createProducerMock(), + $this->createDriverMock() + ); + } + + public function testShouldAllowGetArgumentSetInConstructor() + { + $expectedMessage = new Message(); + $expectedProducer = $this->createProducerMock(); + $expectedDriver = $this->createDriverMock(); + + $context = new DriverPreSend( + $expectedMessage, + $expectedProducer, + $expectedDriver + ); + + $this->assertSame($expectedMessage, $context->getMessage()); + $this->assertSame($expectedProducer, $context->getProducer()); + $this->assertSame($expectedDriver, $context->getDriver()); + } + + public function testShouldAllowGetCommand() + { + $message = new Message(); + $message->setProperty(Config::PARAMETER_TOPIC_NAME, Config::COMMAND_TOPIC); + $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'theCommand'); + + $context = new DriverPreSend( + $message, + $this->createProducerMock(), + $this->createDriverMock() + ); + + $this->assertFalse($context->isEvent()); + $this->assertSame('theCommand', $context->getCommand()); + } + + public function testShouldAllowGetTopic() + { + $message = new Message(); + $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'theTopic'); + + $context = new DriverPreSend( + $message, + $this->createProducerMock(), + $this->createDriverMock() + ); + + $this->assertTrue($context->isEvent()); + $this->assertSame('theTopic', $context->getTopic()); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function createDriverMock(): DriverInterface + { + return $this->createMock(DriverInterface::class); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function createProducerMock(): ProducerInterface + { + return $this->createMock(ProducerInterface::class); + } +} diff --git a/pkg/enqueue/Tests/Client/PostSendTest.php b/pkg/enqueue/Tests/Client/PostSendTest.php new file mode 100644 index 000000000..fe78c14a1 --- /dev/null +++ b/pkg/enqueue/Tests/Client/PostSendTest.php @@ -0,0 +1,94 @@ +createProducerMock(), + $this->createDriverMock() + ); + } + + public function testShouldAllowGetArgumentSetInConstructor() + { + $expectedMessage = new Message(); + $expectedProducer = $this->createProducerMock(); + $expectedDriver = $this->createDriverMock(); + + $context = new PostSend( + $expectedMessage, + $expectedProducer, + $expectedDriver + ); + + $this->assertSame($expectedMessage, $context->getMessage()); + $this->assertSame($expectedProducer, $context->getProducer()); + $this->assertSame($expectedDriver, $context->getDriver()); + } + + public function testShouldAllowGetCommand() + { + $message = new Message(); + $message->setProperty(Config::PARAMETER_TOPIC_NAME, Config::COMMAND_TOPIC); + $message->setProperty(Config::PARAMETER_COMMAND_NAME, 'theCommand'); + + $context = new PostSend( + $message, + $this->createProducerMock(), + $this->createDriverMock() + ); + + $this->assertFalse($context->isEvent()); + $this->assertSame('theCommand', $context->getCommand()); + } + + public function testShouldAllowGetTopic() + { + $message = new Message(); + $message->setProperty(Config::PARAMETER_TOPIC_NAME, 'theTopic'); + + $context = new PostSend( + $message, + $this->createProducerMock(), + $this->createDriverMock() + ); + + $this->assertTrue($context->isEvent()); + $this->assertSame('theTopic', $context->getTopic()); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function createDriverMock(): DriverInterface + { + return $this->createMock(DriverInterface::class); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function createProducerMock(): ProducerInterface + { + return $this->createMock(ProducerInterface::class); + } +} From 6855c7cfda84b2445d59f60f865d3a68ab023577 Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Thu, 30 Aug 2018 14:30:50 +0300 Subject: [PATCH 6/7] fix travis. --- .php_cs.dist | 1 + .travis.yml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.php_cs.dist b/.php_cs.dist index 99a418499..0a8583087 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -21,6 +21,7 @@ return PhpCsFixer\Config::create() 'psr4' => true, 'strict_param' => true, )) + ->setCacheFile(getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__.'/var/.php_cs.cache') ->setFinder( PhpCsFixer\Finder::create() ->in(__DIR__) diff --git a/.travis.yml b/.travis.yml index fb3a4e46a..17251f01d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,6 +38,7 @@ matrix: cache: directories: - $HOME/.composer/cache + - $HOME/.php-cs-fixer before_install: - echo "extension = mongodb.so" >> $HOME/.phpenv/versions/$(phpenv version-name)/etc/php.ini @@ -53,7 +54,7 @@ install: script: - IFS=$'\n'; COMMIT_SCA_FILES=($(git diff --name-only --diff-filter=ACMRTUXB "${TRAVIS_COMMIT_RANGE}")); unset IFS - - if [ "$PHP_CS_FIXER" = true ]; then ./bin/php-cs-fixer --no-interaction --dry-run --diff -v --path-mode=intersection -- "${COMMIT_SCA_FILES[@]} fix; fi + - if [ "$PHP_CS_FIXER" = true ]; then ./bin/php-cs-fixer --no-interaction --dry-run --diff -v --path-mode=intersection fix -- "${COMMIT_SCA_FILES[@]}" ; fi - if [ "$PHPSTAN" = true ]; then docker run --workdir="/mqdev" -v "`pwd`:/mqdev" --rm enqueue/dev:latest php -d memory_limit=1024M bin/phpstan analyse -l 1 -c phpstan.neon -- "${COMMIT_SCA_FILES[@]}" ; fi - if [ "$UNIT_TESTS" = true ]; then bin/phpunit --exclude-group=functional; fi - if [ "$FUNCTIONAL_TESTS" = true ]; then bin/test.sh --exclude-group=rdkafka; fi From 722f8c80de5dc358ab996a63ca769f8ca6a4d358 Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Thu, 30 Aug 2018 19:33:53 +0300 Subject: [PATCH 7/7] fix php-cs-fixer --- .php_cs.dist => .php_cs.php | 2 +- .travis.yml | 6 +++--- bin/git-find-changed-php-files.sh | 15 +++++++++++++++ bin/pre-commit | 6 +++++- 4 files changed, 24 insertions(+), 5 deletions(-) rename .php_cs.dist => .php_cs.php (99%) create mode 100755 bin/git-find-changed-php-files.sh diff --git a/.php_cs.dist b/.php_cs.php similarity index 99% rename from .php_cs.dist rename to .php_cs.php index 0a8583087..5746bcbc4 100644 --- a/.php_cs.dist +++ b/.php_cs.php @@ -26,4 +26,4 @@ PhpCsFixer\Finder::create() ->in(__DIR__) ) -; \ No newline at end of file +; diff --git a/.travis.yml b/.travis.yml index 17251f01d..7101e8329 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,9 +53,9 @@ install: - if [ "$PREPARE_CONTAINER" = true ]; then bin/dev -b; fi script: - - IFS=$'\n'; COMMIT_SCA_FILES=($(git diff --name-only --diff-filter=ACMRTUXB "${TRAVIS_COMMIT_RANGE}")); unset IFS - - if [ "$PHP_CS_FIXER" = true ]; then ./bin/php-cs-fixer --no-interaction --dry-run --diff -v --path-mode=intersection fix -- "${COMMIT_SCA_FILES[@]}" ; fi - - if [ "$PHPSTAN" = true ]; then docker run --workdir="/mqdev" -v "`pwd`:/mqdev" --rm enqueue/dev:latest php -d memory_limit=1024M bin/phpstan analyse -l 1 -c phpstan.neon -- "${COMMIT_SCA_FILES[@]}" ; fi + - PKG_PHP_CHANGED_FILES=`./bin/git-find-changed-php-files.sh "${TRAVIS_COMMIT_RANGE}"` + - if [ "$PHP_CS_FIXER" = true ] && [ ! -z "${PKG_PHP_CHANGED_FILES}" ]; then ./bin/php-cs-fixer fix --config=.php_cs.php --no-interaction --dry-run --diff -v --path-mode=intersection -- ${PKG_PHP_CHANGED_FILES[@]} ; fi + - if [ "$PHPSTAN" = true ] && [ ! -z "${PKG_PHP_CHANGED_FILES}" ]; then docker run --workdir="/mqdev" -v "`pwd`:/mqdev" --rm enqueue/dev:latest php -d memory_limit=1024M bin/phpstan analyse -l 1 -c phpstan.neon -- ${PKG_PHP_CHANGED_FILES[@]} ; fi - if [ "$UNIT_TESTS" = true ]; then bin/phpunit --exclude-group=functional; fi - if [ "$FUNCTIONAL_TESTS" = true ]; then bin/test.sh --exclude-group=rdkafka; fi - if [ "RDKAFKA_TESTS" = true ]; then bin/test.sh --group=rdkafka; fi diff --git a/bin/git-find-changed-php-files.sh b/bin/git-find-changed-php-files.sh new file mode 100755 index 000000000..e256b5f24 --- /dev/null +++ b/bin/git-find-changed-php-files.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +if (( "$#" != 1 )) +then + echo "Git range must be provided" + exit 1 +fi + + +IFS=' +' +ALL_CHANGED_FILES=$(git diff --name-only --diff-filter=ACMRTUXB "$1"); +PKG_PHP_CHANGED_FILES=$(echo "$ALL_CHANGED_FILES" | grep -E "^pkg\/" | grep -E ".*?\.php$"); + +echo "$PKG_PHP_CHANGED_FILES"; diff --git a/bin/pre-commit b/bin/pre-commit index d10bd7ce2..7c3936ca3 100755 --- a/bin/pre-commit +++ b/bin/pre-commit @@ -68,6 +68,10 @@ function getFilesToFix() return (bool) preg_match('/\.(php|twig|translations\/*.yml)$/', $file); }); + $stagedFiles = array_filter($stagedFiles, function ($file) { + return (bool) preg_match('/^pkg\//', $file); + }); + return $stagedFiles; } @@ -104,7 +108,7 @@ function runPhpCsFixer() $returnCode = null; exec(sprintf( - '%s %s fix %s --dry-run', + '%s %s fix %s --dry-run --config=.php_cs.php', $phpBin, $phpCsFixerBin, $projectRootDir.'/'.$file