From e60030770e14d939493bd0b6639beb3e790f106a Mon Sep 17 00:00:00 2001 From: Vladyslav Vildanov <117659936+vladvildanov@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:10:53 +0300 Subject: [PATCH] Added support for XGROUP container commands (#1324) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Codestyle changes related to php-cs-fixer update (#1311) * Codestyle changes * Added missing type-hints * Added GETDEL command to KeyPrefixProcessor (#1306) * Added GETDEL command to KeyPrefixProcessor * Added test coverage * Codestyle fixes * Added timeout after FT.CREATE call * Added support for JSON.MERGE command (#1304) * Added support for JSON.MSET command (#1307) * Fixed subcommand test bug (#1313) * Update CHANGELOG.md * Update CHANGELOG.md * Added support for XGROUP container commands * Fixed bug with incorrect multiple words processing (#1325) * Fixed bug with incorrect multiple words processing * Convert subcommand string to lower case * Update SubcommandStrategyResolver.php * Added test coverage * Codestyle fixes --------- Co-authored-by: Till Krüss * Added split words handling * Fixed command id to be lowercase * Fixed test decorator * Added support for FUNCTION DUMP, FUNCTION FLUSH, FUNCTION RESTORE commands (#1332) * Added support for CLIENT NO-EVICT command (#1335) * Added support for FUNCTION KILL, FUNCTION LIST, FUNCTION STATS commands (#1334) * Added support for FUNCTION KILL, FUNCTION LIST, FUNCTION STATS commands * Marked tests as relay-incompatible * Removed old tests * Removed old tests * Move back missing test * Removed blank space * Removed old test --------- Co-authored-by: Till Krüss --- src/ClientContextInterface.php | 2 + src/ClientInterface.php | 2 + src/Command/Container/XGROUP.php | 30 +++ src/Command/Redis/XGROUP.php | 81 +++++++ tests/Predis/Command/Redis/XGROUP_Test.php | 245 +++++++++++++++++++++ 5 files changed, 360 insertions(+) create mode 100644 src/Command/Container/XGROUP.php create mode 100644 src/Command/Redis/XGROUP.php create mode 100644 tests/Predis/Command/Redis/XGROUP_Test.php diff --git a/src/ClientContextInterface.php b/src/ClientContextInterface.php index 4ce72a5c..a52bb369 100644 --- a/src/ClientContextInterface.php +++ b/src/ClientContextInterface.php @@ -44,6 +44,7 @@ use Predis\Command\Container\FUNCTIONS; use Predis\Command\Container\Json\JSONDEBUG; use Predis\Command\Container\Search\FTCONFIG; use Predis\Command\Container\Search\FTCURSOR; +use Predis\Command\Container\XGROUP; /** * Interface defining a client-side context such as a pipeline or transaction. @@ -349,6 +350,7 @@ use Predis\Command\Container\Search\FTCURSOR; * @property FTCURSOR $ftcursor * @property JSONDEBUG $jsondebug * @property ACL $acl + * @property XGROUP $xgroup */ interface ClientContextInterface { diff --git a/src/ClientInterface.php b/src/ClientInterface.php index 6e8762a7..b20db972 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -44,6 +44,7 @@ use Predis\Command\Container\FUNCTIONS; use Predis\Command\Container\Json\JSONDEBUG; use Predis\Command\Container\Search\FTCONFIG; use Predis\Command\Container\Search\FTCURSOR; +use Predis\Command\Container\XGROUP; use Predis\Command\FactoryInterface; use Predis\Configuration\OptionsInterface; use Predis\Connection\ConnectionInterface; @@ -367,6 +368,7 @@ use Predis\Response\Status; * @property FTCURSOR $ftcursor * @property JSONDEBUG $jsondebug * @property ACL $acl + * @property XGROUP $xgroup */ interface ClientInterface { diff --git a/src/Command/Container/XGROUP.php b/src/Command/Container/XGROUP.php new file mode 100644 index 00000000..da44e32f --- /dev/null +++ b/src/Command/Container/XGROUP.php @@ -0,0 +1,30 @@ +setCreateArguments($arguments); + + return; + + case 'SETID': + $this->setSetIdArguments($arguments); + + return; + + default: + parent::setArguments($arguments); + } + } + + /** + * @param array $arguments + * @return void + */ + private function setCreateArguments(array $arguments): void + { + $processedArguments = [$arguments[0], $arguments[1], $arguments[2], $arguments[3]]; + + if (array_key_exists(4, $arguments) && true === $arguments[4]) { + $processedArguments[] = 'MKSTREAM'; + } + + if (array_key_exists(5, $arguments)) { + array_push($processedArguments, 'ENTRIESREAD', $arguments[5]); + } + + parent::setArguments($processedArguments); + } + + /** + * @param array $arguments + * @return void + */ + private function setSetIdArguments(array $arguments): void + { + $processedArguments = [$arguments[0], $arguments[1], $arguments[2], $arguments[3]]; + + if (array_key_exists(4, $arguments)) { + array_push($processedArguments, 'ENTRIESREAD', $arguments[4]); + } + + parent::setArguments($processedArguments); + } +} diff --git a/tests/Predis/Command/Redis/XGROUP_Test.php b/tests/Predis/Command/Redis/XGROUP_Test.php new file mode 100644 index 00000000..29c76162 --- /dev/null +++ b/tests/Predis/Command/Redis/XGROUP_Test.php @@ -0,0 +1,245 @@ +getCommand(); + $command->setArguments($actualArguments); + + $this->assertSame($expectedResponse, $command->getArguments()); + } + + /** + * @group disconnected + * @return void + */ + public function testCreateConsumerFilterArguments(): void + { + $arguments = ['CREATECONSUMER', 'key', 'group', 'consumer']; + $expected = ['CREATECONSUMER', 'key', 'group', 'consumer']; + + $command = $this->getCommand(); + $command->setArguments($arguments); + + $this->assertSameValues($expected, $command->getArguments()); + } + + /** + * @group disconnected + * @return void + */ + public function testDelConsumerFilterArguments(): void + { + $arguments = ['DELCONSUMER', 'key', 'group', 'consumer']; + $expected = ['DELCONSUMER', 'key', 'group', 'consumer']; + + $command = $this->getCommand(); + $command->setArguments($arguments); + + $this->assertSameValues($expected, $command->getArguments()); + } + + /** + * @group disconnected + * @return void + */ + public function testDestroyFilterArguments(): void + { + $arguments = ['DESTROY', 'key', 'group']; + $expected = ['DESTROY', 'key', 'group']; + + $command = $this->getCommand(); + $command->setArguments($arguments); + + $this->assertSameValues($expected, $command->getArguments()); + } + + /** + * @dataProvider setIdArgumentsProvider + * @group disconnected + * @param array $actualArguments + * @param array $expectedResponse + * @return void + */ + public function testSetIdFilterArguments(array $actualArguments, array $expectedResponse): void + { + $command = $this->getCommand(); + $command->setArguments($actualArguments); + + $this->assertSame($expectedResponse, $command->getArguments()); + } + + /** + * @group disconnected + */ + public function testParseResponse(): void + { + $this->assertSame(1, $this->getCommand()->parseResponse(1)); + } + + /** + * @group connected + * @return void + * @requiresRedisVersion >= 5.0.0 + */ + public function testCreatesNewConsumerGroupForExistingStream(): void + { + $redis = $this->getClient(); + + $redis->xadd('key', ['field' => 'value']); + + $this->assertEquals('OK', $redis->xgroup->create('key', 'group', '$')); + } + + /** + * @group connected + * @return void + * @requiresRedisVersion >= 5.0.0 + */ + public function testCreateThrowsExceptionOnNonExistingStream(): void + { + $redis = $this->getClient(); + + $this->expectException(ServerException::class); + $this->expectExceptionMessage( + 'ERR The XGROUP subcommand requires the key to exist.' + ); + + $this->assertEquals('OK', $redis->xgroup->create('key', 'group', '$')); + } + + /** + * @group connected + * @return void + * @requiresRedisVersion >= 6.2.0 + */ + public function testCreatesNewConsumerForExistingConsumerGroup(): void + { + $redis = $this->getClient(); + + $redis->xadd('key', ['field' => 'value']); + + $this->assertEquals('OK', $redis->xgroup->create('key', 'group', '$')); + $this->assertSame(1, $redis->xgroup->createConsumer('key', 'group', 'consumer')); + } + + /** + * @group connected + * @return void + * @requiresRedisVersion >= 6.2.0 + */ + public function testRemovesConsumerFromExistingConsumerGroup(): void + { + $redis = $this->getClient(); + + $redis->xadd('key', ['field' => 'value']); + + $this->assertEquals('OK', $redis->xgroup->create('key', 'group', '$')); + $this->assertSame(1, $redis->xgroup->createConsumer('key', 'group', 'consumer')); + $this->assertSame(0, $redis->xgroup->delConsumer('key', 'group', 'consumer')); + } + + /** + * @group connected + * @return void + * @requiresRedisVersion >= 5.0.0 + */ + public function testDestroyRemovesGivenConsumerGroup(): void + { + $redis = $this->getClient(); + + $redis->xadd('key', ['field' => 'value']); + + $this->assertEquals('OK', $redis->xgroup->create('key', 'group', '$')); + $this->assertEquals(1, $redis->xgroup->destroy('key', 'group')); + } + + /** + * @group connected + * @return void + * @requiresRedisVersion >= 5.0.0 + */ + public function testSetGroupLastDeliveredId(): void + { + $redis = $this->getClient(); + + $redis->xadd('key', ['field' => 'value']); + + $this->assertEquals('OK', $redis->xgroup->create('key', 'group', '$')); + $this->assertEquals('OK', $redis->xgroup->setId('key', 'group', '0')); + } + + public function createArgumentsProvider(): array + { + return [ + 'with default arguments' => [ + ['CREATE', 'key', 'group', '$'], + ['CREATE', 'key', 'group', '$'], + ], + 'with MKSTREAM modifier' => [ + ['CREATE', 'key', 'group', '$', true], + ['CREATE', 'key', 'group', '$', 'MKSTREAM'], + ], + 'with ENTRIESREAD modifier' => [ + ['CREATE', 'key', 'group', '$', false, 'entry'], + ['CREATE', 'key', 'group', '$', 'ENTRIESREAD', 'entry'], + ], + 'with all arguments modifier' => [ + ['CREATE', 'key', 'group', '$', true, 'entry'], + ['CREATE', 'key', 'group', '$', 'MKSTREAM', 'ENTRIESREAD', 'entry'], + ], + ]; + } + + public function setIdArgumentsProvider(): array + { + return [ + 'with default arguments' => [ + ['SETID', 'key', 'group', '$'], + ['SETID', 'key', 'group', '$'], + ], + 'with ENTRIESREAD modifier' => [ + ['SETID', 'key', 'group', '$', 'entry'], + ['SETID', 'key', 'group', '$', 'ENTRIESREAD', 'entry'], + ], + ]; + } +}