From 0e6126ba9dbdbdb1fe4f8ad995556022988885ad Mon Sep 17 00:00:00 2001 From: Vladyslav Vildanov <117659936+vladvildanov@users.noreply.github.com> Date: Mon, 5 Dec 2022 18:33:43 +0200 Subject: [PATCH] Added support for BZMPOP command (#833) --- src/ClientContextInterface.php | 1 + src/ClientInterface.php | 1 + src/Command/Redis/BZMPOP.php | 20 +++ src/Command/Redis/ZMPOP.php | 11 +- tests/Predis/Command/Redis/BZMPOP_Test.php | 199 +++++++++++++++++++++ 5 files changed, 230 insertions(+), 2 deletions(-) create mode 100644 src/Command/Redis/BZMPOP.php create mode 100644 tests/Predis/Command/Redis/BZMPOP_Test.php diff --git a/src/ClientContextInterface.php b/src/ClientContextInterface.php index 37fe9cd4..c4defbcb 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 bzmpop(int $timeout, array $keys, string $modifier = 'min', int $count = 1) * @method $this decr($key) * @method $this decrby($key, $decrement) * @method $this get($key) diff --git a/src/ClientInterface.php b/src/ClientInterface.php index bb53d37b..ca28f8e7 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 bzmpop(int $timeout, array $keys, string $modifier = 'min', int $count = 1) * @method int decr(string $key) * @method int decrby(string $key, int $decrement) * @method string|null get(string $key) diff --git a/src/Command/Redis/BZMPOP.php b/src/Command/Redis/BZMPOP.php new file mode 100644 index 00000000..ac5e46f5 --- /dev/null +++ b/src/Command/Redis/BZMPOP.php @@ -0,0 +1,20 @@ +setNumkeys($arguments); $arguments = $this->getArguments(); - $this->resolveModifier(2, $arguments); - $this->unpackKeysArray(self::$keysArgumentPositionOffset + 1, $arguments); + $this->resolveModifier(static::$modifierArgumentPositionOffset + 1, $arguments); + $this->unpackKeysArray(static::$keysArgumentPositionOffset + 1, $arguments); parent::setArguments($arguments); } diff --git a/tests/Predis/Command/Redis/BZMPOP_Test.php b/tests/Predis/Command/Redis/BZMPOP_Test.php new file mode 100644 index 00000000..6c37b707 --- /dev/null +++ b/tests/Predis/Command/Redis/BZMPOP_Test.php @@ -0,0 +1,199 @@ +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 + * @dataProvider sortedSetsProvider + * @param int $timeout + * @param array $sortedSetDictionary + * @param string $key + * @param string $modifier + * @param int $count + * @param array $expectedResponse + * @param array $expectedModifiedSortedSet + * @return void + * @requiresRedisVersion >= 7.0 + */ + public function testReturnsPoppedElementsFromGivenSortedSet( + int $timeout, + array $sortedSetDictionary, + string $key, + string $modifier, + int $count, + array $expectedResponse, + array $expectedModifiedSortedSet + ): void { + $redis = $this->getClient(); + + $redis->zadd($key, ...$sortedSetDictionary); + $actualResponse = $redis->bzmpop($timeout, [$key], $modifier, $count); + + $this->assertSame($expectedResponse, $actualResponse); + $this->assertSame($expectedModifiedSortedSet, $redis->zrange($key, 0, -1)); + } + + /** + * @group connected + * @dataProvider unexpectedValuesProvider + * @param int $timeout + * @param array $keys + * @param string $modifier + * @param int $count + * @param string $expectedExceptionMessage + * @return void + * @requiresRedisVersion >= 7.0 + */ + public function testThrowsExceptionOnUnexpectedValueGiven( + int $timeout, + array $keys, + string $modifier, + int $count, + string $expectedExceptionMessage + ): void { + $redis = $this->getClient(); + + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessage($expectedExceptionMessage); + + $redis->bzmpop($timeout, $keys, $modifier, $count); + } + + /** + * @group connected + * @requiresRedisVersion >= 7.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('bzmpop_foo', 'bar'); + $redis->bzmpop(1, ['bzmpop_foo']); + } + + public function argumentsProvider(): array + { + return [ + 'with one key' => [ + [10, ['key1'], 'min', 1], + [10, 1, 'key1', 'MIN', 'COUNT', 1] + ], + 'with multiple keys' => [ + [10, ['key1', 'key2', 'key3'], 'max', 1], + [10, 3, 'key1', 'key2', 'key3', 'MAX', 'COUNT', 1] + ], + ]; + } + + public function responsesProvider(): array + { + return [ + 'null-element array' => [ + [null], + [null] + ], + 'two-element array' => [ + ['key', [['member1', 1, 'member2', 2, 'member3', 3]]], + ['key' => ['member1' => 1, 'member2' => 2, 'member3' => 3]] + ], + ]; + } + + public function sortedSetsProvider(): array + { + return [ + 'with MIN modifier' => [ + 1, + [1, 'member1', 2, 'member2', 3, 'member3'], + 'test-bzmpop', + 'min', + 1, + ['test-bzmpop' => ['member1' => '1']], + ['member2', 'member3'], + ], + 'with MAX modifier' => [ + 1, + [1, 'member1', 2, 'member2', 3, 'member3'], + 'test-bzmpop', + 'max', + 1, + ['test-bzmpop' => ['member3' => '3']], + ['member1', 'member2'], + ], + 'with non-default COUNT' => [ + 1, + [1, 'member1', 2, 'member2', 3, 'member3'], + 'test-bzmpop', + 'max', + 2, + ['test-bzmpop' => ['member3' => '3', 'member2' => '2']], + ['member1'], + ] + ]; + } + + public function unexpectedValuesProvider(): array + { + return [ + 'wrong modifier' => [ + 1, + ['key1', 'key2'], + 'wrong modifier', + 1, + 'Wrong type of modifier given' + ], + 'wrong count' => [ + 1, + ['key1', 'key2'], + 'min', + 0, + 'Wrong count argument value or position offset' + ], + ]; + } +}