From f26fd899f98a288b48356eb64ffd6dde1e3e7160 Mon Sep 17 00:00:00 2001 From: Vladyslav Vildanov <117659936+vladvildanov@users.noreply.github.com> Date: Tue, 13 Dec 2022 20:26:47 +0200 Subject: [PATCH] Extend Sorted Set support by implementing BZPOPMAX command (#864) * Refactored zinterstore, zunionstore commands and command traits * Merge conflicts resolve, update cluster strategy test with new arguments * Updated assertion in case if command executed faster then duration minimal threshold * Updated Keys trait to handle cases when no numkeys modifier needed * Added support for BZPOPMIN command * Added command link and description * Added support for BZPOPMAX command * Added absract command to fix inheritance chain Co-authored-by: Vladyslav Vildanov --- src/ClientContextInterface.php | 1 + src/ClientInterface.php | 1 + .../Redis/AbstractCommand/BZPOPBase.php | 32 ++++++++ src/Command/Redis/BZPOPMAX.php | 23 ++++++ src/Command/Redis/BZPOPMIN.php | 29 +------ .../Redis/AbstractCommand/BZPOPBaseTest.php | 75 +++++++++++++++++++ tests/Predis/Command/Redis/BZPOPMAX_Test.php | 73 ++++++++++++++++++ tests/Predis/Command/Redis/BZPOPMIN_Test.php | 49 ------------ 8 files changed, 208 insertions(+), 75 deletions(-) create mode 100644 src/Command/Redis/AbstractCommand/BZPOPBase.php create mode 100644 src/Command/Redis/BZPOPMAX.php create mode 100644 tests/Predis/Command/Redis/AbstractCommand/BZPOPBaseTest.php create mode 100644 tests/Predis/Command/Redis/BZPOPMAX_Test.php diff --git a/src/ClientContextInterface.php b/src/ClientContextInterface.php index bdf17242..0b2d2f7a 100644 --- a/src/ClientContextInterface.php +++ b/src/ClientContextInterface.php @@ -40,6 +40,7 @@ use Predis\Command\CommandInterface; * @method $this bitop($operation, $destkey, $key) * @method $this bitfield($key, $subcommand, ...$subcommandArg) * @method $this bitpos($key, $bit, $start = null, $end = null) + * @method $this bzpopmax(array $keys, int $timeout) * @method $this bzpopmin(array $keys, int $timeout) * @method $this bzmpop(int $timeout, array $keys, string $modifier = 'min', int $count = 1) * @method $this decr($key) diff --git a/src/ClientInterface.php b/src/ClientInterface.php index 61c5e059..2d7dd25c 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -49,6 +49,7 @@ use Predis\Response\Status; * @method int bitop($operation, $destkey, $key) * @method array|null bitfield(string $key, $subcommand, ...$subcommandArg) * @method int bitpos(string $key, $bit, $start = null, $end = null) + * @method array bzpopmax(array $keys, int $timeout) * @method array bzpopmin(array $keys, int $timeout) * @method array bzmpop(int $timeout, array $keys, string $modifier = 'min', int $count = 1) * @method int decr(string $key) diff --git a/src/Command/Redis/AbstractCommand/BZPOPBase.php b/src/Command/Redis/AbstractCommand/BZPOPBase.php new file mode 100644 index 00000000..ef87622a --- /dev/null +++ b/src/Command/Redis/AbstractCommand/BZPOPBase.php @@ -0,0 +1,32 @@ +setKeys($arguments, false); + } + + public function parseResponse($data) + { + $key = array_shift($data); + + if (null === $key) { + return [$key]; + } + + return array_combine([$key], [[$data[0] => $data[1]]]); + } +} diff --git a/src/Command/Redis/BZPOPMAX.php b/src/Command/Redis/BZPOPMAX.php new file mode 100644 index 00000000..cc8dba7c --- /dev/null +++ b/src/Command/Redis/BZPOPMAX.php @@ -0,0 +1,23 @@ +setKeys($arguments, false); - } - - public function parseResponse($data) - { - $key = array_shift($data); - - if (null === $key) { - return [$key]; - } - - return array_combine([$key], [[$data[0] => $data[1]]]); - } } diff --git a/tests/Predis/Command/Redis/AbstractCommand/BZPOPBaseTest.php b/tests/Predis/Command/Redis/AbstractCommand/BZPOPBaseTest.php new file mode 100644 index 00000000..18f7d430 --- /dev/null +++ b/tests/Predis/Command/Redis/AbstractCommand/BZPOPBaseTest.php @@ -0,0 +1,75 @@ +testCommand = new class extends BZPOPBase { + + public function getId(): string + { + return 'test'; + } + }; + } + + /** + * @group disconnected + * @dataProvider argumentsProvider + */ + public function testFilterArguments(array $actualArguments, array $expectedArguments): void + { + $this->testCommand->setArguments($actualArguments); + + $this->assertSame($expectedArguments, $this->testCommand->getArguments()); + } + + /** + * @group disconnected + * @dataProvider responsesProvider + */ + public function testParseResponse(array $actualResponse, array $expectedResponse): void + { + $this->assertSame($expectedResponse, $this->testCommand->parseResponse($actualResponse)); + } + + public function argumentsProvider(): array + { + return [ + 'with one key' => [ + [['key1'], 1], + ['key1', 1] + ], + 'with multiple keys' => [ + [['key1', 'key2', 'key3'], 1], + ['key1', 'key2', 'key3', 1] + ], + ]; + } + + public function responsesProvider(): array + { + return [ + 'null-element array' => [ + [null], + [null] + ], + 'three-element array' => [ + ['key', 'member', 'score'], + ['key' => ['member' => 'score']] + ], + ]; + } +} diff --git a/tests/Predis/Command/Redis/BZPOPMAX_Test.php b/tests/Predis/Command/Redis/BZPOPMAX_Test.php new file mode 100644 index 00000000..440bae27 --- /dev/null +++ b/tests/Predis/Command/Redis/BZPOPMAX_Test.php @@ -0,0 +1,73 @@ += 5.0.0 + */ + public function testReturnsPoppedMaxElementFromGivenNonEmptySortedSet(): void + { + $redis = $this->getClient(); + $sortedSetDictionary = [1, 'member1', 2, 'member2', 3, 'member3']; + $expectedResponse = ['test-bzpopmax' => ['member3' => '3']]; + $expectedModifiedSortedSet = ['member1', 'member2']; + + $redis->zadd('test-bzpopmax', ...$sortedSetDictionary); + + $this->assertSame($expectedResponse, $redis->bzpopmax(['empty sorted set','test-bzpopmax'], 0)); + $this->assertSame($expectedModifiedSortedSet, $redis->zrange('test-bzpopmax', 0, -1)); + } + + /** + * @group connected + * @return void + * @requiresRedisVersion >= 5.0.0 + */ + public function testThrowsExceptionOnUnexpectedValueGiven(): void + { + $redis = $this->getClient(); + + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessage('Wrong keys argument type or position offset'); + + $redis->bzpopmax(1, 0); + } + + /** + * @group connected + * @requiresRedisVersion >= 5.0.0 + */ + public function testThrowsExceptionOnWrongType(): void + { + $this->expectException(ServerException::class); + $this->expectExceptionMessage('Operation against a key holding the wrong kind of value'); + + $redis = $this->getClient(); + + $redis->set('bzpopmax_foo', 'bar'); + $redis->bzpopmax(['bzpopmax_foo'], 0); + } +} diff --git a/tests/Predis/Command/Redis/BZPOPMIN_Test.php b/tests/Predis/Command/Redis/BZPOPMIN_Test.php index d58e0142..9a9e749e 100644 --- a/tests/Predis/Command/Redis/BZPOPMIN_Test.php +++ b/tests/Predis/Command/Redis/BZPOPMIN_Test.php @@ -23,27 +23,6 @@ class BZPOPMIN_Test extends PredisCommandTestCase return 'BZPOPMIN'; } - /** - * @group disconnected - * @dataProvider argumentsProvider - */ - public function testFilterArguments(array $actualArguments, array $expectedArguments): void - { - $command = $this->getCommand(); - $command->setArguments($actualArguments); - - $this->assertSame($expectedArguments, $command->getArguments()); - } - - /** - * @group disconnected - * @dataProvider responsesProvider - */ - public function testParseResponse(array $actualResponse, array $expectedResponse): void - { - $this->assertSame($expectedResponse, $this->getCommand()->parseResponse($actualResponse)); - } - /** * @group connected * @return void @@ -91,32 +70,4 @@ class BZPOPMIN_Test extends PredisCommandTestCase $redis->set('bzpopmin_foo', 'bar'); $redis->bzpopmin(['bzpopmin_foo'], 0); } - - public function argumentsProvider(): array - { - return [ - 'with one key' => [ - [['key1'], 1], - ['key1', 1] - ], - 'with multiple keys' => [ - [['key1', 'key2', 'key3'], 1], - ['key1', 'key2', 'key3', 1] - ], - ]; - } - - public function responsesProvider(): array - { - return [ - 'null-element array' => [ - [null], - [null] - ], - 'three-element array' => [ - ['key', 'member', 'score'], - ['key' => ['member' => 'score']] - ], - ]; - } }