Added support for LMOVEM and BLMOVEM commands (#1709)

* Added support for LMOVEM and BLMOVEM commands

* Removed background testing

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Vladyslav Vildanov
2026-07-22 10:01:21 +03:00
committed by GitHub
parent eafa3ff2bf
commit 422990b631
19 changed files with 808 additions and 34 deletions
+1 -1
View File
@@ -46,7 +46,7 @@ jobs:
run: |
# Mapping of original redis versions to client test containers
declare -A redis_clients_version_mapping=(
["8.10"]="unstable-29454887387-debian"
["8.10"]="8.10-rc2"
["8.8"]="8.8.0"
["8.6"]="8.6.1"
["8.4"]="8.4.0"
+1
View File
@@ -10,6 +10,7 @@
- Added support for `EXCLUDEEMPTY` argument for `TS.MRANGE` and `TS.MREVRANGE` commands
- Added explicit testing for FT.SEARCH timeout policies
- Added support for `TS.QUERYLABELS` command
- Added support for `LMOVEM` and `BLMOVEM` commands
- Added support for `FT.ALIASLIST` command
### Fixed
+2
View File
@@ -237,6 +237,7 @@ use Predis\Command\Redis\VADD;
* @method $this jsontoggle(string $key, string $path)
* @method $this jsontype(string $key, string $path = '$')
* @method $this blmove(string $source, string $destination, string $where, string $to, int $timeout)
* @method $this blmovem(string $source, string $destination, string $from, string $to, int|float $timeout, ?string $quantifier = null, ?int $count = null, ?string $ordering = null)
* @method $this blpop(array|string $keys, $timeout)
* @method $this brpop(array|string $keys, $timeout)
* @method $this brpoplpush($source, $destination, $timeout)
@@ -245,6 +246,7 @@ use Predis\Command\Redis\VADD;
* @method $this linsert($key, $whence, $pivot, $value)
* @method $this llen($key)
* @method $this lmove(string $source, string $destination, string $where, string $to)
* @method $this lmovem(string $source, string $destination, string $from, string $to, ?string $quantifier = null, ?int $count = null, ?string $ordering = null)
* @method $this lmpop(array $keys, string $modifier = 'left', int $count = 1)
* @method $this lpop($key)
* @method $this lpush($key, array $values)
+2
View File
@@ -248,6 +248,7 @@ use Predis\Response\Status;
* @method array jsontoggle(string $key, string $path)
* @method array jsontype(string $key, string $path = '$')
* @method string blmove(string $source, string $destination, string $where, string $to, int $timeout)
* @method array|null blmovem(string $source, string $destination, string $from, string $to, int|float $timeout, ?string $quantifier = null, ?int $count = null, ?string $ordering = null)
* @method array|null blpop(array|string $keys, int|float $timeout)
* @method array|null brpop(array|string $keys, int|float $timeout)
* @method string|null brpoplpush(string $source, string $destination, int|float $timeout)
@@ -256,6 +257,7 @@ use Predis\Response\Status;
* @method int linsert(string $key, $whence, $pivot, $value)
* @method int llen(string $key)
* @method string lmove(string $source, string $destination, string $where, string $to)
* @method array|null lmovem(string $source, string $destination, string $from, string $to, ?string $quantifier = null, ?int $count = null, ?string $ordering = null)
* @method array|null lmpop(array $keys, string $modifier = 'left', int $count = 1)
* @method string|null lpop(string $key)
* @method int lpush(string $key, array $values)
+25
View File
@@ -86,9 +86,11 @@ abstract class ClusterStrategy implements StrategyInterface
'LINSERT' => $getKeyFromFirstArgument,
'LINDEX' => $getKeyFromFirstArgument,
'LLEN' => $getKeyFromFirstArgument,
'LMOVEM' => [$this, 'getKeyFromFirstTwoKeys'],
'LPOP' => $getKeyFromFirstArgument,
'RPOP' => $getKeyFromFirstArgument,
'RPOPLPUSH' => $getKeyFromAllArguments,
'BLMOVEM' => [$this, 'getKeyFromFirstTwoKeys'],
'BLPOP' => [$this, 'getKeyFromBlockingListCommands'],
'BRPOP' => [$this, 'getKeyFromBlockingListCommands'],
'BRPOPLPUSH' => [$this, 'getKeyFromBlockingListCommands'],
@@ -360,6 +362,29 @@ abstract class ClusterStrategy implements StrategyInterface
return $firstKey;
}
/**
* Extracts the key from commands where the first two arguments are keys,
* followed by non-key arguments (e.g. LMOVEM).
*
* @param CommandInterface $command Command instance.
*
* @return string|null
*/
protected function getKeyFromFirstTwoKeys(CommandInterface $command)
{
$arguments = $command->getArguments();
if (!isset($arguments[1])) {
return $arguments[0] ?? null;
}
if (!$this->checkSameSlotForKeys(array_slice($arguments, 0, 2))) {
return null;
}
return $arguments[0];
}
/**
* Extracts the key from BLPOP and BRPOP commands.
*
+38
View File
@@ -0,0 +1,38 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2026 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
/**
* @see https://redis.io/commands/blmovem/
*
* Blocking variant of LMOVEM. When the request cannot be satisfied
* immediately, blocks the connection until it can be or until timeout
* (seconds, 0 blocks indefinitely) elapses, returning null if nothing was
* moved. The timeout argument is positioned after the directions and before
* the optional quantifier block.
*/
class BLMOVEM extends LMOVEM
{
public function getId()
{
return 'BLMOVEM';
}
/**
* {@inheritDoc}
*/
protected function getQuantifierOffset(): int
{
return 5;
}
}
+100
View File
@@ -0,0 +1,100 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2026 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\PrefixableCommand as RedisCommand;
use UnexpectedValueException;
/**
* @see https://redis.io/commands/lmovem/
*
* Atomically moves multiple elements between two lists. Without a quantifier
* a single element is moved (like LMOVE, but the reply is still an array).
* COUNT moves up to count elements, EXACTLY moves exactly count elements or
* nothing at all. The ordering argument controls how elements are pushed at
* the destination: OBO pushes one-by-one (block order reversed), BULK
* preserves the original relative order.
*/
class LMOVEM extends RedisCommand
{
public const COUNT = 'COUNT';
public const EXACTLY = 'EXACTLY';
public const OBO = 'OBO';
public const BULK = 'BULK';
public function getId()
{
return 'LMOVEM';
}
/**
* Returns the position of the optional quantifier block within the
* arguments list (BLMOVEM shifts it by one to fit the timeout argument).
*
* @return int
*/
protected function getQuantifierOffset(): int
{
return 4;
}
public function setArguments(array $arguments)
{
$offset = $this->getQuantifierOffset();
if (count($arguments) <= $offset) {
parent::setArguments($arguments);
return;
}
$processed = array_slice($arguments, 0, $offset);
$quantifier = strtoupper($arguments[$offset]);
if (!in_array($quantifier, [self::COUNT, self::EXACTLY], true)) {
throw new UnexpectedValueException('Quantifier argument accepts only: COUNT, EXACTLY values');
}
if (!isset($arguments[$offset + 1])) {
throw new UnexpectedValueException("{$quantifier} quantifier requires a count argument");
}
if (!isset($arguments[$offset + 2])) {
throw new UnexpectedValueException("{$quantifier} quantifier requires an ordering argument");
}
$ordering = strtoupper($arguments[$offset + 2]);
if (!in_array($ordering, [self::OBO, self::BULK], true)) {
throw new UnexpectedValueException('Ordering argument accepts only: OBO, BULK values');
}
array_push($processed, $quantifier, $arguments[$offset + 1], $ordering);
parent::setArguments($processed);
}
public function prefixKeys($prefix)
{
if ($arguments = $this->getArguments()) {
$arguments[0] = $prefix . $arguments[0];
if (isset($arguments[1])) {
$arguments[1] = $prefix . $arguments[1];
}
$this->setRawArguments($arguments);
}
}
}
@@ -162,6 +162,36 @@ class PredisStrategyTest extends PredisTestCase
}
}
/**
* @group disconnected
*/
public function testKeysForFirstTwoKeysCommands(): void
{
$strategy = $this->getClusterStrategy();
$commands = $this->getCommandFactory();
$arguments = ['{key}:source', '{key}:destination', 'LEFT', 'RIGHT'];
foreach ($this->getExpectedCommands('keys-first-two') as $commandID) {
$command = $commands->create($commandID, $arguments);
$this->assertNotNull($strategy->getSlot($command), $commandID);
}
}
/**
* @group disconnected
*/
public function testReturnsNullOnFirstTwoKeysCommandsWithDifferentSlots(): void
{
$strategy = $this->getClusterStrategy();
$commands = $this->getCommandFactory();
$arguments = ['key:source', 'key:destination', 'LEFT', 'RIGHT'];
foreach ($this->getExpectedCommands('keys-first-two') as $commandID) {
$command = $commands->create($commandID, $arguments);
$this->assertNull($strategy->getSlot($command), $commandID);
}
}
/**
* @group disconnected
*/
@@ -413,9 +443,11 @@ class PredisStrategyTest extends PredisTestCase
'LINSERT' => 'keys-first',
'LINDEX' => 'keys-first',
'LLEN' => 'keys-first',
'LMOVEM' => 'keys-first-two',
'LPOP' => 'keys-first',
'RPOP' => 'keys-first',
'RPOPLPUSH' => 'keys-all',
'BLMOVEM' => 'keys-first-two',
'BLPOP' => 'keys-blockinglist',
'BRPOP' => 'keys-blockinglist',
'BRPOPLPUSH' => 'keys-blockinglist',
@@ -205,6 +205,36 @@ class RedisStrategyTest extends PredisTestCase
}
}
/**
* @group disconnected
*/
public function testKeysForFirstTwoKeysCommands(): void
{
$strategy = $this->getClusterStrategy();
$commands = $this->getCommandFactory();
$arguments = ['{key}:source', '{key}:destination', 'LEFT', 'RIGHT'];
foreach ($this->getExpectedCommands('keys-first-two') as $commandID) {
$command = $commands->create($commandID, $arguments);
$this->assertNotNull($strategy->getSlot($command), $commandID);
}
}
/**
* @group disconnected
*/
public function testReturnsNullOnFirstTwoKeysCommandsWithDifferentSlots(): void
{
$strategy = $this->getClusterStrategy();
$commands = $this->getCommandFactory();
$arguments = ['key:source', 'key:destination', 'LEFT', 'RIGHT'];
foreach ($this->getExpectedCommands('keys-first-two') as $commandID) {
$command = $commands->create($commandID, $arguments);
$this->assertNull($strategy->getSlot($command), $commandID);
}
}
/**
* @group disconnected
*/
@@ -436,9 +466,11 @@ class RedisStrategyTest extends PredisTestCase
'LINSERT' => 'keys-first',
'LINDEX' => 'keys-first',
'LLEN' => 'keys-first',
'LMOVEM' => 'keys-first-two',
'LPOP' => 'keys-first',
'RPOP' => 'keys-first',
'RPOPLPUSH' => 'keys-all',
'BLMOVEM' => 'keys-first-two',
'BLPOP' => 'keys-blockinglist',
'BRPOP' => 'keys-blockinglist',
'BRPOPLPUSH' => 'keys-blockinglist',
+271
View File
@@ -0,0 +1,271 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2026 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\PrefixableCommand;
use Predis\Response\ServerException;
use UnexpectedValueException;
/**
* @group commands
* @group realm-list
*/
class BLMOVEM_Test extends PredisCommandTestCase
{
/**
* {@inheritDoc}
*/
protected function getExpectedCommand(): string
{
return BLMOVEM::class;
}
/**
* {@inheritDoc}
*/
protected function getExpectedId(): string
{
return 'BLMOVEM';
}
/**
* @group disconnected
* @dataProvider argumentsProvider
*/
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
{
$command = $this->getCommand();
$command->setArguments($actualArguments);
$this->assertSameValues($expectedArguments, $command->getArguments());
}
/**
* @group disconnected
* @dataProvider invalidArgumentsProvider
*/
public function testSetArgumentsThrowsExceptionOnInvalidArguments(
array $arguments,
string $expectedExceptionMessage
): void {
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage($expectedExceptionMessage);
$this->getCommand()->setArguments($arguments);
}
/**
* @group disconnected
*/
public function testPrefixKeys(): void
{
/** @var PrefixableCommand $command */
$command = $this->getCommand();
$actualArguments = ['source', 'destination', 'LEFT', 'RIGHT', 0.1, 'COUNT', 3, 'OBO'];
$prefix = 'prefix:';
$expectedArguments = ['prefix:source', 'prefix:destination', 'LEFT', 'RIGHT', 0.1, 'COUNT', 3, 'OBO'];
$command->setArguments($actualArguments);
$command->prefixKeys($prefix);
$this->assertSame($expectedArguments, $command->getArguments());
}
/**
* @group connected
* @dataProvider listsProvider
* @param array $sourceList
* @param array $destinationList
* @param array $commandArguments
* @param array|null $expectedResponse
* @param array $expectedSourceList
* @param array $expectedDestinationList
* @return void
* @requiresRedisVersion >= 8.9.0
*/
public function testMovesElementsBetweenLists(
array $sourceList,
array $destinationList,
array $commandArguments,
?array $expectedResponse,
array $expectedSourceList,
array $expectedDestinationList
): void {
$redis = $this->getClient();
if ($sourceList) {
$redis->rpush('source', $sourceList);
}
if ($destinationList) {
$redis->rpush('destination', $destinationList);
}
$this->assertSame($expectedResponse, $redis->blmovem('source', 'destination', ...$commandArguments));
$this->assertSame($expectedSourceList, $redis->lrange('source', 0, -1));
$this->assertSame($expectedDestinationList, $redis->lrange('destination', 0, -1));
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.9.0
*/
public function testMovesElementsBetweenListsResp3(): void
{
$redis = $this->getResp3Client();
$redis->rpush('source', ['1', '2', '3', '4', '5']);
$redis->rpush('destination', ['6', '7', '8', '9', '10']);
$this->assertSame(
['3', '2', '1'],
$redis->blmovem('source', 'destination', 'LEFT', 'LEFT', 0.1, 'COUNT', 3, 'OBO')
);
$this->assertSame(['4', '5'], $redis->lrange('source', 0, -1));
$this->assertSame(
['3', '2', '1', '6', '7', '8', '9', '10'],
$redis->lrange('destination', 0, -1)
);
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.9.0
*/
public function testThrowsExceptionOnWrongType(): void
{
$redis = $this->getClient();
$this->expectException(ServerException::class);
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
$redis->set('source', 'foo');
$redis->blmovem('source', 'destination', 'LEFT', 'RIGHT', 0.1);
}
public function argumentsProvider(): array
{
return [
'with required arguments only' => [
['source', 'destination', 'LEFT', 'RIGHT', 0.1],
['source', 'destination', 'LEFT', 'RIGHT', 0.1],
],
'with COUNT quantifier' => [
['source', 'destination', 'LEFT', 'LEFT', 0, 'COUNT', 3, 'OBO'],
['source', 'destination', 'LEFT', 'LEFT', 0, 'COUNT', 3, 'OBO'],
],
'with EXACTLY quantifier' => [
['source', 'destination', 'RIGHT', 'RIGHT', 1.5, 'EXACTLY', 2, 'BULK'],
['source', 'destination', 'RIGHT', 'RIGHT', 1.5, 'EXACTLY', 2, 'BULK'],
],
'with lowercase quantifier and ordering' => [
['source', 'destination', 'LEFT', 'RIGHT', 0.1, 'count', 3, 'bulk'],
['source', 'destination', 'LEFT', 'RIGHT', 0.1, 'COUNT', 3, 'BULK'],
],
];
}
public function invalidArgumentsProvider(): array
{
return [
'with invalid quantifier' => [
['source', 'destination', 'LEFT', 'RIGHT', 0.1, 'WRONG', 3, 'OBO'],
'Quantifier argument accepts only: COUNT, EXACTLY values',
],
'with missing count' => [
['source', 'destination', 'LEFT', 'RIGHT', 0.1, 'COUNT'],
'COUNT quantifier requires a count argument',
],
'with missing ordering' => [
['source', 'destination', 'LEFT', 'RIGHT', 0.1, 'EXACTLY', 2],
'EXACTLY quantifier requires an ordering argument',
],
'with invalid ordering' => [
['source', 'destination', 'LEFT', 'RIGHT', 0.1, 'COUNT', 3, 'WRONG'],
'Ordering argument accepts only: OBO, BULK values',
],
];
}
public function listsProvider(): array
{
return [
'moves single element without quantifier' => [
['1', '2', '3', '4', '5'],
[],
['LEFT', 'RIGHT', 0.1],
['1'],
['2', '3', '4', '5'],
['1'],
],
'with COUNT and OBO - reversed block order' => [
['1', '2', '3', '4', '5'],
['6', '7', '8', '9', '10'],
['LEFT', 'LEFT', 0.1, 'COUNT', 3, 'OBO'],
['3', '2', '1'],
['4', '5'],
['3', '2', '1', '6', '7', '8', '9', '10'],
],
'with COUNT and BULK - preserved relative order' => [
['1', '2', '3', '4', '5'],
['6', '7', '8', '9', '10'],
['LEFT', 'LEFT', 0.1, 'COUNT', 3, 'BULK'],
['1', '2', '3'],
['4', '5'],
['1', '2', '3', '6', '7', '8', '9', '10'],
],
'with mixed directions - RIGHT to RIGHT' => [
['1', '2', '3', '4', '5'],
['6', '7', '8', '9', '10'],
['RIGHT', 'RIGHT', 0.1, 'COUNT', 3, 'BULK'],
['3', '4', '5'],
['1', '2'],
['6', '7', '8', '9', '10', '3', '4', '5'],
],
'with COUNT greater than source length - moves fewer immediately' => [
['1', '2'],
[],
['LEFT', 'RIGHT', 0.1, 'COUNT', 5, 'BULK'],
['1', '2'],
[],
['1', '2'],
],
'with EXACTLY and enough elements' => [
['john', 'doe'],
[],
['LEFT', 'RIGHT', 0.1, 'EXACTLY', 2, 'BULK'],
['john', 'doe'],
[],
['john', 'doe'],
],
'with EXACTLY and too few elements - times out moving nothing' => [
['john'],
[],
['LEFT', 'RIGHT', 0.1, 'EXACTLY', 2, 'BULK'],
null,
['john'],
[],
],
'with empty source - times out moving nothing' => [
[],
['6', '7'],
['LEFT', 'RIGHT', 0.1, 'COUNT', 2, 'BULK'],
null,
[],
['6', '7'],
],
];
}
}
+271
View File
@@ -0,0 +1,271 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2026 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\PrefixableCommand;
use Predis\Response\ServerException;
use UnexpectedValueException;
/**
* @group commands
* @group realm-list
*/
class LMOVEM_Test extends PredisCommandTestCase
{
/**
* {@inheritDoc}
*/
protected function getExpectedCommand(): string
{
return LMOVEM::class;
}
/**
* {@inheritDoc}
*/
protected function getExpectedId(): string
{
return 'LMOVEM';
}
/**
* @group disconnected
* @dataProvider argumentsProvider
*/
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
{
$command = $this->getCommand();
$command->setArguments($actualArguments);
$this->assertSameValues($expectedArguments, $command->getArguments());
}
/**
* @group disconnected
* @dataProvider invalidArgumentsProvider
*/
public function testSetArgumentsThrowsExceptionOnInvalidArguments(
array $arguments,
string $expectedExceptionMessage
): void {
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage($expectedExceptionMessage);
$this->getCommand()->setArguments($arguments);
}
/**
* @group disconnected
*/
public function testPrefixKeys(): void
{
/** @var PrefixableCommand $command */
$command = $this->getCommand();
$actualArguments = ['source', 'destination', 'LEFT', 'RIGHT', 'COUNT', 3, 'OBO'];
$prefix = 'prefix:';
$expectedArguments = ['prefix:source', 'prefix:destination', 'LEFT', 'RIGHT', 'COUNT', 3, 'OBO'];
$command->setArguments($actualArguments);
$command->prefixKeys($prefix);
$this->assertSame($expectedArguments, $command->getArguments());
}
/**
* @group connected
* @dataProvider listsProvider
* @param array $sourceList
* @param array $destinationList
* @param array $commandArguments
* @param array|null $expectedResponse
* @param array $expectedSourceList
* @param array $expectedDestinationList
* @return void
* @requiresRedisVersion >= 8.9.0
*/
public function testMovesElementsBetweenLists(
array $sourceList,
array $destinationList,
array $commandArguments,
?array $expectedResponse,
array $expectedSourceList,
array $expectedDestinationList
): void {
$redis = $this->getClient();
if ($sourceList) {
$redis->rpush('source', $sourceList);
}
if ($destinationList) {
$redis->rpush('destination', $destinationList);
}
$this->assertSame($expectedResponse, $redis->lmovem('source', 'destination', ...$commandArguments));
$this->assertSame($expectedSourceList, $redis->lrange('source', 0, -1));
$this->assertSame($expectedDestinationList, $redis->lrange('destination', 0, -1));
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.9.0
*/
public function testMovesElementsBetweenListsResp3(): void
{
$redis = $this->getResp3Client();
$redis->rpush('source', ['1', '2', '3', '4', '5']);
$redis->rpush('destination', ['6', '7', '8', '9', '10']);
$this->assertSame(
['3', '2', '1'],
$redis->lmovem('source', 'destination', 'LEFT', 'LEFT', 'COUNT', 3, 'OBO')
);
$this->assertSame(['4', '5'], $redis->lrange('source', 0, -1));
$this->assertSame(
['3', '2', '1', '6', '7', '8', '9', '10'],
$redis->lrange('destination', 0, -1)
);
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.9.0
*/
public function testThrowsExceptionOnWrongType(): void
{
$redis = $this->getClient();
$this->expectException(ServerException::class);
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
$redis->set('source', 'foo');
$redis->lmovem('source', 'destination', 'LEFT', 'RIGHT');
}
public function argumentsProvider(): array
{
return [
'with required arguments only' => [
['source', 'destination', 'LEFT', 'RIGHT'],
['source', 'destination', 'LEFT', 'RIGHT'],
],
'with COUNT quantifier' => [
['source', 'destination', 'LEFT', 'LEFT', 'COUNT', 3, 'OBO'],
['source', 'destination', 'LEFT', 'LEFT', 'COUNT', 3, 'OBO'],
],
'with EXACTLY quantifier' => [
['source', 'destination', 'RIGHT', 'RIGHT', 'EXACTLY', 2, 'BULK'],
['source', 'destination', 'RIGHT', 'RIGHT', 'EXACTLY', 2, 'BULK'],
],
'with lowercase quantifier and ordering' => [
['source', 'destination', 'LEFT', 'RIGHT', 'count', 3, 'bulk'],
['source', 'destination', 'LEFT', 'RIGHT', 'COUNT', 3, 'BULK'],
],
];
}
public function invalidArgumentsProvider(): array
{
return [
'with invalid quantifier' => [
['source', 'destination', 'LEFT', 'RIGHT', 'WRONG', 3, 'OBO'],
'Quantifier argument accepts only: COUNT, EXACTLY values',
],
'with missing count' => [
['source', 'destination', 'LEFT', 'RIGHT', 'COUNT'],
'COUNT quantifier requires a count argument',
],
'with missing ordering' => [
['source', 'destination', 'LEFT', 'RIGHT', 'EXACTLY', 2],
'EXACTLY quantifier requires an ordering argument',
],
'with invalid ordering' => [
['source', 'destination', 'LEFT', 'RIGHT', 'COUNT', 3, 'WRONG'],
'Ordering argument accepts only: OBO, BULK values',
],
];
}
public function listsProvider(): array
{
return [
'moves single element without quantifier' => [
['1', '2', '3', '4', '5'],
[],
['LEFT', 'RIGHT'],
['1'],
['2', '3', '4', '5'],
['1'],
],
'with COUNT and OBO - reversed block order' => [
['1', '2', '3', '4', '5'],
['6', '7', '8', '9', '10'],
['LEFT', 'LEFT', 'COUNT', 3, 'OBO'],
['3', '2', '1'],
['4', '5'],
['3', '2', '1', '6', '7', '8', '9', '10'],
],
'with COUNT and BULK - preserved relative order' => [
['1', '2', '3', '4', '5'],
['6', '7', '8', '9', '10'],
['LEFT', 'LEFT', 'COUNT', 3, 'BULK'],
['1', '2', '3'],
['4', '5'],
['1', '2', '3', '6', '7', '8', '9', '10'],
],
'with mixed directions - RIGHT to RIGHT' => [
['1', '2', '3', '4', '5'],
['6', '7', '8', '9', '10'],
['RIGHT', 'RIGHT', 'COUNT', 3, 'BULK'],
['3', '4', '5'],
['1', '2'],
['6', '7', '8', '9', '10', '3', '4', '5'],
],
'with COUNT greater than source length - moves fewer' => [
['1', '2'],
[],
['LEFT', 'RIGHT', 'COUNT', 5, 'BULK'],
['1', '2'],
[],
['1', '2'],
],
'with EXACTLY and enough elements' => [
['john', 'doe'],
[],
['LEFT', 'RIGHT', 'EXACTLY', 2, 'BULK'],
['john', 'doe'],
[],
['john', 'doe'],
],
'with EXACTLY and too few elements - moves nothing' => [
['john'],
[],
['LEFT', 'RIGHT', 'EXACTLY', 2, 'BULK'],
null,
['john'],
[],
],
'with empty source - moves nothing' => [
[],
['6', '7'],
['LEFT', 'RIGHT', 'COUNT', 2, 'BULK'],
null,
[],
['6', '7'],
],
];
}
}
@@ -65,7 +65,7 @@ class SDIFFCARD_Test extends PredisCommandTestCase
* @param int $limit
* @param int $expectedCardinality
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testReturnsCorrectCardinalityOfGivenSetDifference(
array $sets,
@@ -85,7 +85,7 @@ class SDIFFCARD_Test extends PredisCommandTestCase
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testReturnsCorrectCardinalityOfGivenSetDifferenceResp3(): void
{
@@ -103,7 +103,7 @@ class SDIFFCARD_Test extends PredisCommandTestCase
* @param array $arguments
* @param string $expectedExceptionMessage
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testThrowsExceptionOnUnexpectedValuesGiven(
array $arguments,
@@ -120,7 +120,7 @@ class SDIFFCARD_Test extends PredisCommandTestCase
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testThrowsExceptionOnWrongType(): void
{
@@ -66,7 +66,7 @@ class SUNIONCARD_Test extends PredisCommandTestCase
* @param int $limit
* @param int $expectedCardinality
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testReturnsCorrectCardinalityOfGivenSetUnion(
array $sets,
@@ -87,7 +87,7 @@ class SUNIONCARD_Test extends PredisCommandTestCase
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testApproxReturnsSameResultAsExactMatchingOnSmallSets(): void
{
@@ -105,7 +105,7 @@ class SUNIONCARD_Test extends PredisCommandTestCase
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testReturnsCorrectCardinalityOfGivenSetUnionResp3(): void
{
@@ -123,7 +123,7 @@ class SUNIONCARD_Test extends PredisCommandTestCase
* @param array $arguments
* @param string $expectedExceptionMessage
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testThrowsExceptionOnUnexpectedValuesGiven(
array $arguments,
@@ -140,7 +140,7 @@ class SUNIONCARD_Test extends PredisCommandTestCase
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testThrowsExceptionOnWrongType(): void
{
@@ -179,7 +179,7 @@ class FTAGGREGATE_Test extends PredisCommandTestCase
* @group connected
* @group relay-resp3
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testAggregatesWithCollectReducer(): void
{
@@ -223,7 +223,7 @@ class FTAGGREGATE_Test extends PredisCommandTestCase
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testAggregatesWithCollectReducerResp3(): void
{
@@ -271,7 +271,7 @@ class FTAGGREGATE_Test extends PredisCommandTestCase
* @group connected
* @group relay-resp3
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testCollectReducerProjectsAllFields(): void
{
@@ -66,7 +66,7 @@ class FTALIASLIST_Test extends PredisCommandTestCase
* @group connected
* @group relay-resp3
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testReturnsAliasesAssociatedWithGivenIndex(): void
{
@@ -83,7 +83,7 @@ class FTALIASLIST_Test extends PredisCommandTestCase
* @group connected
* @group relay-resp3
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testReturnsEmptyListForIndexWithoutAliases(): void
{
@@ -97,7 +97,7 @@ class FTALIASLIST_Test extends PredisCommandTestCase
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testReturnsAliasesAssociatedWithGivenIndexResp3(): void
{
@@ -114,7 +114,7 @@ class FTALIASLIST_Test extends PredisCommandTestCase
* @group connected
* @group relay-resp3
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testThrowsExceptionOnNonExistingIndex(): void
{
@@ -305,7 +305,7 @@ class TSMRANGE_Test extends PredisCommandTestCase
* @group connected
* @group relay-resp3
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testQueryRangeExcludesEmptySeriesWithExcludeEmptyModifier(): void
{
@@ -348,7 +348,7 @@ class TSMRANGE_Test extends PredisCommandTestCase
* @group connected
* @group relay-resp3
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testQueryRangeExcludesEmptySeriesWithExcludeEmptyAndAggregation(): void
{
@@ -380,7 +380,7 @@ class TSMRANGE_Test extends PredisCommandTestCase
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testQueryRangeExcludesEmptySeriesWithExcludeEmptyModifierResp3(): void
{
@@ -305,7 +305,7 @@ class TSMREVRANGE_Test extends PredisCommandTestCase
* @group connected
* @group relay-resp3
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testQueryRangeExcludesEmptySeriesWithExcludeEmptyModifier(): void
{
@@ -344,7 +344,7 @@ class TSMREVRANGE_Test extends PredisCommandTestCase
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testQueryRangeExcludesEmptySeriesWithExcludeEmptyModifierResp3(): void
{
@@ -53,7 +53,7 @@ class TSQUERYLABELS_Test extends PredisCommandTestCase
* @group connected
* @group relay-resp3
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testQueryReturnsLabelNamesMatchingGivenFilterExpression(): void
{
@@ -79,7 +79,7 @@ class TSQUERYLABELS_Test extends PredisCommandTestCase
* @group connected
* @group relay-resp3
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testQueryReturnsLabelValuesMatchingGivenFilterExpression(): void
{
@@ -104,7 +104,7 @@ class TSQUERYLABELS_Test extends PredisCommandTestCase
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testQueryReturnsLabelNamesMatchingGivenFilterExpressionResp3(): void
{
@@ -129,7 +129,7 @@ class TSQUERYLABELS_Test extends PredisCommandTestCase
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testQueryReturnsLabelValuesMatchingGivenFilterExpressionResp3(): void
{
@@ -63,7 +63,7 @@ class TSREAD_Test extends PredisCommandTestCase
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testReadsAllSamplesAtOnce(): void
{
@@ -83,7 +83,7 @@ class TSREAD_Test extends PredisCommandTestCase
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testReadsSamplesUsingCursorPagination(): void
{
@@ -113,7 +113,7 @@ class TSREAD_Test extends PredisCommandTestCase
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testReadsSamplesUsingSpecialTimestampCursors(): void
{
@@ -137,7 +137,7 @@ class TSREAD_Test extends PredisCommandTestCase
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testReadsMissingKeyReturnsEmptyListWithoutError(): void
{
@@ -149,7 +149,7 @@ class TSREAD_Test extends PredisCommandTestCase
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testBlocksForGivenTimeoutWhenNotEnoughSamplesAvailable(): void
{
@@ -172,7 +172,7 @@ class TSREAD_Test extends PredisCommandTestCase
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testReturnsImmediatelyWhenMinCountSamplesAlreadyAvailable(): void
{
@@ -192,7 +192,7 @@ class TSREAD_Test extends PredisCommandTestCase
/**
* @group connected
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testReadsAllSamplesAtOnceResp3(): void
{
@@ -214,7 +214,7 @@ class TSREAD_Test extends PredisCommandTestCase
* @param ReadArguments $arguments
* @param string $expectedExceptionMessage
* @return void
* @requiresRedisVersion >= 8.10.0
* @requiresRedisVersion >= 8.9.0
*/
public function testThrowsExceptionOnCountConstraintViolations(
ReadArguments $arguments,