Refactored ZUNIONSTORE, ZINTERSTORE, removed Numkeys trait, improved Withscores response parsing (#858)

This commit is contained in:
Vladyslav Vildanov
2022-12-09 17:37:22 +02:00
committed by GitHub
parent d2b05ff58a
commit f9e77a0562
21 changed files with 665 additions and 454 deletions
+2 -2
View File
@@ -113,7 +113,7 @@ use Predis\Command\CommandInterface;
* @method $this zdiff(array $keys, bool $withScores = false)
* @method $this zdiffstore(string $destination, array $keys)
* @method $this zincrby($key, $increment, $member)
* @method $this zinterstore($destination, array|string $keys, array $options = null)
* @method $this zinterstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
* @method $this zmpop(array $keys, string $modifier = 'min', int $count = 1)
* @method $this zmscore(string $key, string ...$member)
* @method $this zrandmember(string $key, int $count = 1, bool $withScores = false)
@@ -127,7 +127,7 @@ use Predis\Command\CommandInterface;
* @method $this zrevrange($key, $start, $stop, array $options = null)
* @method $this zrevrangebyscore($key, $max, $min, array $options = null)
* @method $this zrevrank($key, $member)
* @method $this zunionstore($destination, array|string $keys, array $options = null)
* @method $this zunionstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
* @method $this zscore($key, $member)
* @method $this zscan($key, $cursor, array $options = null)
* @method $this zrangebylex($key, $start, $stop, array $options = null)
+2 -2
View File
@@ -129,7 +129,7 @@ use Predis\Response\Status;
* @method array zdiff(array $keys, bool $withScores = false)
* @method int zdiffstore(string $destination, array $keys)
* @method string zincrby(string $key, int $increment, string $member)
* @method int zinterstore(string $destination, array|string $keys, array $options = null)
* @method int zinterstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
* @method array zmpop(array $keys, string $modifier = 'min', int $count = 1)
* @method array zmscore(string $key, string ...$member)
* @method array zpopmin(string $key, int $count = 1)
@@ -145,7 +145,7 @@ use Predis\Response\Status;
* @method array zrevrange(string $key, int|string $start, int|string $stop, array $options = null)
* @method array zrevrangebyscore(string $key, int|string $max, int|string $min, array $options = null)
* @method int|null zrevrank(string $key, string $member)
* @method int zunionstore(string $destination, array|string $keys, array $options = null)
* @method int zunionstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
* @method string|null zscore(string $key, string $member)
* @method array zscan(string $key, int $cursor, array $options = null)
* @method array zrangebylex(string $key, string $start, string $stop, array $options = null)
+5 -7
View File
@@ -3,7 +3,6 @@
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Numkeys;
use Predis\Command\Traits\WithScores;
use Predis\Command\Traits\Keys;
@@ -15,13 +14,12 @@ use Predis\Command\Traits\Keys;
*/
class ZDIFF extends RedisCommand
{
use Numkeys {
Numkeys::setArguments as setNumkeys;
}
use WithScores {
WithScores::setArguments as setWithScore;
}
use Keys;
use Keys {
Keys::setArguments as setKeys;
}
protected static $keysArgumentPositionOffset = 0;
@@ -32,9 +30,9 @@ class ZDIFF extends RedisCommand
public function setArguments(array $arguments)
{
$this->setNumkeys($arguments);
$this->setKeys($arguments);
$arguments = $this->getArguments();
$this->unpackKeysArray(self::$keysArgumentPositionOffset + 1, $arguments);
$this->setWithScore($arguments);
}
}
+2 -12
View File
@@ -3,7 +3,6 @@
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Numkeys;
use Predis\Command\Traits\Keys;
/**
@@ -18,9 +17,8 @@ use Predis\Command\Traits\Keys;
*/
class ZDIFFSTORE extends RedisCommand
{
use Keys;
use Numkeys {
setArguments as setNumkeys;
use Keys {
Keys::setArguments as setKeys;
}
public static $keysArgumentPositionOffset = 1;
@@ -29,12 +27,4 @@ class ZDIFFSTORE extends RedisCommand
{
return 'ZDIFFSTORE';
}
public function setArguments(array $arguments)
{
$this->setNumkeys($arguments);
$arguments = $this->getArguments();
$this->unpackKeysArray(self::$keysArgumentPositionOffset + 1, $arguments);
parent::setArguments($arguments);
}
}
+5 -8
View File
@@ -5,7 +5,6 @@ namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Count;
use Predis\Command\Traits\MinMaxModifier;
use Predis\Command\Traits\Numkeys;
use Predis\Command\Traits\Keys;
/**
@@ -16,14 +15,13 @@ use Predis\Command\Traits\Keys;
*/
class ZMPOP extends RedisCommand
{
use Numkeys {
Numkeys::setArguments as setNumkeys;
use Keys {
Keys::setArguments as setKeys;
}
use Count {
Count::setArguments as setCount;
}
use MinMaxModifier;
use Keys;
protected static $keysArgumentPositionOffset = 0;
protected static $countArgumentPositionOffset = 2;
@@ -39,11 +37,10 @@ class ZMPOP extends RedisCommand
$this->setCount($arguments);
$arguments = $this->getArguments();
$this->setNumkeys($arguments);
$arguments = $this->getArguments();
$this->resolveModifier(static::$modifierArgumentPositionOffset, $arguments);
$this->resolveModifier(static::$modifierArgumentPositionOffset + 1, $arguments);
$this->unpackKeysArray(static::$keysArgumentPositionOffset + 1, $arguments);
$this->setKeys($arguments);
$arguments = $this->getArguments();
parent::setArguments($arguments);
}
+22 -41
View File
@@ -12,6 +12,9 @@
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Aggregate;
use Predis\Command\Traits\Keys;
use Predis\Command\Traits\Weights;
/**
* @link http://redis.io/commands/zunionstore
@@ -20,6 +23,20 @@ use Predis\Command\Command as RedisCommand;
*/
class ZUNIONSTORE extends RedisCommand
{
use Keys {
Keys::setArguments as setKeys;
}
use Weights {
Weights::setArguments as setWeights;
}
use Aggregate{
Aggregate::setArguments as setAggregate;
}
protected static $keysArgumentPositionOffset = 1;
protected static $weightsArgumentPositionOffset = 2;
protected static $aggregateArgumentPositionOffset = 3;
/**
* {@inheritdoc}
*/
@@ -33,48 +50,12 @@ class ZUNIONSTORE extends RedisCommand
*/
public function setArguments(array $arguments)
{
$options = array();
$argc = count($arguments);
$this->setAggregate($arguments);
$arguments = $this->getArguments();
if ($argc > 2 && is_array($arguments[$argc - 1])) {
$options = $this->prepareOptions(array_pop($arguments));
}
$this->setWeights($arguments);
$arguments = $this->getArguments();
if (is_array($arguments[1])) {
$arguments = array_merge(
array($arguments[0], count($arguments[1])),
$arguments[1]
);
}
parent::setArguments(array_merge($arguments, $options));
}
/**
* Returns a list of options and modifiers compatible with Redis.
*
* @param array $options List of options.
*
* @return array
*/
private function prepareOptions($options)
{
$opts = array_change_key_case($options, CASE_UPPER);
$finalizedOpts = array();
if (isset($opts['WEIGHTS']) && is_array($opts['WEIGHTS'])) {
$finalizedOpts[] = 'WEIGHTS';
foreach ($opts['WEIGHTS'] as $weight) {
$finalizedOpts[] = $weight;
}
}
if (isset($opts['AGGREGATE'])) {
$finalizedOpts[] = 'AGGREGATE';
$finalizedOpts[] = $opts['AGGREGATE'];
}
return $finalizedOpts;
$this->setKeys($arguments);
}
}
+55
View File
@@ -0,0 +1,55 @@
<?php
namespace Predis\Command\Traits;
use Predis\Command\Command;
use UnexpectedValueException;
/**
* @mixin Command
*/
trait Aggregate
{
/**
* @var string[]
*/
private static $aggregateValuesEnum = [
'min' => 'MIN',
'max' => 'MAX',
'sum' => 'SUM'
];
/**
* @var string
*/
private static $aggregateModifier = 'AGGREGATE';
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);
if (static::$aggregateArgumentPositionOffset >= $argumentsLength) {
parent::setArguments($arguments);
return;
}
$argument = $arguments[static::$aggregateArgumentPositionOffset];
if (is_string($argument) && in_array(strtoupper($argument), self::$aggregateValuesEnum)) {
$argument = self::$aggregateValuesEnum[$argument];
} else {
$enumValues = implode(', ', array_keys(self::$aggregateValuesEnum));
throw new UnexpectedValueException("Aggregate argument accepts only: {$enumValues} values");
}
$argumentsBefore = array_slice($arguments, 0, static::$aggregateArgumentPositionOffset);
$argumentsAfter = array_slice($arguments, static::$aggregateArgumentPositionOffset + 1);
parent::setArguments(array_merge(
$argumentsBefore,
[self::$aggregateModifier],
[$argument],
$argumentsAfter
));
}
}
+11 -6
View File
@@ -10,17 +10,22 @@ use UnexpectedValueException;
*/
trait Keys
{
public function unpackKeysArray(int $keysArgumentOffset, array &$arguments): void
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);
if ($keysArgumentOffset > $argumentsLength || !is_array($arguments[$keysArgumentOffset])) {
if (
static::$keysArgumentPositionOffset > $argumentsLength ||
!is_array($arguments[static::$keysArgumentPositionOffset])
) {
throw new UnexpectedValueException('Wrong keys argument type or position offset');
}
$keysArgument = $arguments[$keysArgumentOffset];
$argumentsBefore = array_slice($arguments, 0, $keysArgumentOffset);
$argumentsAfter = array_slice($arguments, ++$keysArgumentOffset);
$arguments = array_merge($argumentsBefore, $keysArgument, $argumentsAfter);
$keysArgument = $arguments[static::$keysArgumentPositionOffset];
$numkeys = count($keysArgument);
$argumentsBeforeKeys = array_slice($arguments, 0, static::$keysArgumentPositionOffset);
$argumentsAfterKeys = array_slice($arguments, static::$keysArgumentPositionOffset + 1);
parent::setArguments(array_merge($argumentsBeforeKeys, [$numkeys], $keysArgument, $argumentsAfterKeys));
}
}
-33
View File
@@ -1,33 +0,0 @@
<?php
namespace Predis\Command\Traits;
use Predis\Command\Command;
use UnexpectedValueException;
/**
* Resolves numkeys argument from keys and attach it to arguments
*
* @mixin Command
*/
trait Numkeys
{
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);
if (
static::$keysArgumentPositionOffset > $argumentsLength ||
!is_array($arguments[static::$keysArgumentPositionOffset])
) {
throw new UnexpectedValueException('Wrong keys argument type or position offset');
}
$keysArgument = $arguments[static::$keysArgumentPositionOffset];
$numkeys = count($keysArgument);
$argumentsBeforeKeys = array_slice($arguments, 0, static::$keysArgumentPositionOffset);
$argumentsAfterKeys = array_slice($arguments, static::$keysArgumentPositionOffset + 1);
parent::setArguments(array_merge($argumentsBeforeKeys, [$numkeys], [$keysArgument], $argumentsAfterKeys));
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace Predis\Command\Traits;
use Predis\Command\Command;
use UnexpectedValueException;
/**
* @mixin Command
*/
trait Weights
{
/**
* @var string
*/
private static $weightsModifier = 'WEIGHTS';
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);
if (static::$weightsArgumentPositionOffset >= $argumentsLength) {
parent::setArguments($arguments);
return;
}
if (!is_array($arguments[static::$weightsArgumentPositionOffset])) {
throw new UnexpectedValueException('Wrong weights argument type');
}
$weightsArray = $arguments[static::$weightsArgumentPositionOffset];
if (empty($weightsArray)) {
unset($arguments[static::$weightsArgumentPositionOffset]);
parent::setArguments($arguments);
return;
}
$argumentsBefore = array_slice($arguments, 0, static::$weightsArgumentPositionOffset);
$argumentsAfter = array_slice($arguments, static::$weightsArgumentPositionOffset + 1);
parent::setArguments(array_merge(
$argumentsBefore,
[self::$weightsModifier],
$weightsArray,
$argumentsAfter
));
}
}
+1 -1
View File
@@ -32,7 +32,7 @@ trait WithScores
private function isWithScoreModifier(): bool
{
$arguments = parent::getArguments();
$lastArgument = $arguments[count($arguments) - 1];
$lastArgument = (!empty($arguments)) ? $arguments[count($arguments) - 1] : null;
return is_string($lastArgument) && strtoupper($lastArgument) === 'WITHSCORES';
}
+1 -1
View File
@@ -148,7 +148,7 @@ class PredisStrategyTest extends PredisTestCase
{
$strategy = $this->getClusterStrategy();
$commands = $this->getCommandFactory();
$arguments = array('{key}:destination', 2, '{key}:1', '{key}:1', array('aggregate' => 'SUM'));
$arguments = array('{key}:destination', ['{key}:1', '{key}:1'], [], 'sum');
foreach ($this->getExpectedCommands('keys-zaggregated') as $commandID) {
$command = $commands->create($commandID, $arguments);
+1 -1
View File
@@ -104,7 +104,7 @@ class SLOWLOG_Test extends PredisCommandTestCase
$this->assertIsArray($slowlog[0]);
$this->assertGreaterThan(0, $slowlog[0]['id']);
$this->assertGreaterThan(0, $slowlog[0]['timestamp']);
$this->assertGreaterThan(0, $slowlog[0]['duration']);
$this->assertGreaterThanOrEqual(0, $slowlog[0]['duration']);
$this->assertIsArray($slowlog[0]['command']);
$redis->config('set', 'slowlog-log-slower-than', $threshold);
+3 -4
View File
@@ -6,7 +6,6 @@ use Predis\Response\ServerException;
class ZDIFF_test extends PredisCommandTestCase
{
/**
* @inheritDoc
*/
@@ -100,9 +99,9 @@ class ZDIFF_test extends PredisCommandTestCase
public function argumentsProvider(): array
{
return [
'with scores' => [['zset', 5, 'withScores' => true], ['zset', 5, 'WITHSCORES']],
'without scores' => [['zset', 5], ['zset', 5]],
'without scores - false value' => [['zset', 5, 'withScores' => false], ['zset', 5]],
'with scores' => [[['zset'], 5, 'withScores' => true], [1, 'zset', 5, 'WITHSCORES']],
'without scores' => [[['zset'], 5], [1, 'zset', 5]],
'without scores - false value' => [[['zset'], 5, 'withScores' => false], [1, 'zset', 5]],
];
}
+155 -102
View File
@@ -11,6 +11,9 @@
namespace Predis\Command\Redis;
use Predis\Response\ServerException;
use UnexpectedValueException;
/**
* @group commands
* @group realm-zset
@@ -22,7 +25,7 @@ class ZINTERSTORE_Test extends PredisCommandTestCase
*/
protected function getExpectedCommand(): string
{
return 'Predis\Command\Redis\ZINTERSTORE';
return ZINTERSTORE::class;
}
/**
@@ -34,45 +37,15 @@ class ZINTERSTORE_Test extends PredisCommandTestCase
}
/**
* @dataProvider argumentsProvider
* @group disconnected
*/
public function testFilterArguments(): void
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
{
$modifiers = array(
'aggregate' => 'sum',
'weights' => array(10, 100),
);
$arguments = array('zset:destination', 2, 'zset1', 'zset2', $modifiers);
$expected = array(
'zset:destination', 2, 'zset1', 'zset2', 'WEIGHTS', 10, 100, 'AGGREGATE', 'sum',
);
$command = $this->getCommand();
$command->setArguments($arguments);
$command->setArguments($actualArguments);
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testFilterArgumentsSourceKeysAsSingleArray(): void
{
$modifiers = array(
'aggregate' => 'sum',
'weights' => array(10, 100),
);
$arguments = array('zset:destination', array('zset1', 'zset2'), $modifiers);
$expected = array(
'zset:destination', 2, 'zset1', 'zset2', 'WEIGHTS', 10, 100, 'AGGREGATE', 'sum',
);
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
$this->assertSame($expectedArguments, $command->getArguments());
}
/**
@@ -85,77 +58,43 @@ class ZINTERSTORE_Test extends PredisCommandTestCase
/**
* @group connected
* @dataProvider sortedSetsProvider
* @param array $firstSortedSet
* @param array $secondSortedSet
* @param string $destination
* @param array $weights
* @param string $aggregate
* @param int $expectedResponse
* @param array $expectedResultSortedSet
* @return void
* @requiresRedisVersion >= 2.0.0
*/
public function testStoresIntersectionInNewSortedSet(): void
{
public function testStoresIntersectedValuesOnSortedSets(
array $firstSortedSet,
array $secondSortedSet,
string $destination,
array $weights,
string $aggregate,
int $expectedResponse,
array $expectedResultSortedSet
): void {
$redis = $this->getClient();
$redis->zadd('letters:1st', 1, 'a', 2, 'b', 3, 'c');
$redis->zadd('letters:2nd', 1, 'b', 2, 'c', 3, 'd');
$redis->zadd('test-zunionstore1', ...$firstSortedSet);
$redis->zadd('test-zunionstore2', ...$secondSortedSet);
$this->assertSame(2, $redis->zinterstore('letters:out', 2, 'letters:1st', 'letters:2nd'));
$this->assertSame(array('b' => '3', 'c' => '5'), $redis->zrange('letters:out', 0, -1, 'withscores'));
$actualResponse = $redis->zinterstore(
$destination,
['test-zunionstore1', 'test-zunionstore2'],
$weights,
$aggregate
);
$this->assertSame(0, $redis->zinterstore('letters:out', 2, 'letters:1st', 'letters:void'));
$this->assertSame(0, $redis->zinterstore('letters:out', 2, 'letters:void', 'letters:2nd'));
$this->assertSame(0, $redis->zinterstore('letters:out', 2, 'letters:void', 'letters:void'));
}
/**
* @group connected
* @requiresRedisVersion >= 2.0.0
*/
public function testStoresIntersectionWithAggregateModifier(): void
{
$redis = $this->getClient();
$redis->zadd('letters:1st', 1, 'a', 2, 'b', 3, 'c');
$redis->zadd('letters:2nd', 1, 'b', 2, 'c', 3, 'd');
$options = array('aggregate' => 'min');
$this->assertSame(2, $redis->zinterstore('letters:min', 2, 'letters:1st', 'letters:2nd', $options));
$this->assertSame(array('b' => '1', 'c' => '2'), $redis->zrange('letters:min', 0, -1, 'withscores'));
$options = array('aggregate' => 'max');
$this->assertSame(2, $redis->zinterstore('letters:max', 2, 'letters:1st', 'letters:2nd', $options));
$this->assertSame(array('b' => '2', 'c' => '3'), $redis->zrange('letters:max', 0, -1, 'withscores'));
$options = array('aggregate' => 'sum');
$this->assertSame(2, $redis->zinterstore('letters:sum', 2, 'letters:1st', 'letters:2nd', $options));
$this->assertSame(array('b' => '3', 'c' => '5'), $redis->zrange('letters:sum', 0, -1, 'withscores'));
}
/**
* @group connected
* @requiresRedisVersion >= 2.0.0
*/
public function testStoresIntersectionWithWeightsModifier(): void
{
$redis = $this->getClient();
$redis->zadd('letters:1st', 1, 'a', 2, 'b', 3, 'c');
$redis->zadd('letters:2nd', 1, 'b', 2, 'c', 3, 'd');
$options = array('weights' => array(2, 3));
$this->assertSame(2, $redis->zinterstore('letters:out', 2, 'letters:1st', 'letters:2nd', $options));
$this->assertSame(array('b' => '7', 'c' => '12'), $redis->zrange('letters:out', 0, -1, 'withscores'));
}
/**
* @group connected
* @requiresRedisVersion >= 2.0.0
*/
public function testStoresIntersectionWithCombinedModifiers(): void
{
$redis = $this->getClient();
$redis->zadd('letters:1st', 1, 'a', 2, 'b', 3, 'c');
$redis->zadd('letters:2nd', 1, 'b', 2, 'c', 3, 'd');
$options = array('aggregate' => 'max', 'weights' => array(10, 15));
$this->assertSame(2, $redis->zinterstore('letters:out', 2, 'letters:1st', 'letters:2nd', $options));
$this->assertSame(array('b' => '20', 'c' => '30'), $redis->zrange('letters:out', 0, -1, 'withscores'));
$this->assertSame($expectedResponse, $actualResponse);
$this->assertSame(
$expectedResultSortedSet,
$redis->zrange($destination, 0, -1, ['withscores' => true])
);
}
/**
@@ -164,12 +103,126 @@ class ZINTERSTORE_Test extends PredisCommandTestCase
*/
public function testThrowsExceptionOnWrongType(): void
{
$this->expectException('Predis\Response\ServerException');
$this->expectException(ServerException::class);
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
$redis = $this->getClient();
$redis->set('foo', 'bar');
$redis->zinterstore('zset:destination', '1', 'foo');
$redis->zinterstore('zset_interstore:destination', ['foo']);
}
/**
* @dataProvider unexpectedValueProvider
* @param string $destination
* @param $keys
* @param $weights
* @param string $aggregate
* @param string $expectedExceptionMessage
* @return void
*/
public function testThrowsExceptionOnUnexpectedValueGiven(
string $destination,
$keys,
$weights,
string $aggregate,
string $expectedExceptionMessage
): void {
$redis = $this->getClient();
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage($expectedExceptionMessage);
$redis->zinterstore($destination, $keys, $weights, $aggregate);
}
public function argumentsProvider(): array
{
return [
'with required arguments only' => [
['destination', ['key1', 'key2']],
['destination', 2, 'key1', 'key2'],
],
'with weights' => [
['destination', ['key1', 'key2'], [1, 2]],
['destination', 2, 'key1', 'key2', 'WEIGHTS', 1, 2],
],
'with aggregate' => [
['destination', ['key1', 'key2'], [], 'min'],
['destination', 2, 'key1', 'key2', 'AGGREGATE', 'MIN'],
],
'with all arguments' => [
['destination', ['key1', 'key2'], [1, 2], 'min'],
['destination', 2, 'key1', 'key2', 'WEIGHTS', 1, 2, 'AGGREGATE', 'MIN'],
]
];
}
public function sortedSetsProvider(): array
{
return [
'with required arguments' => [
[1, 'member1', 2, 'member2', 3, 'member3'],
[1, 'member1', 2, 'member2'],
'destination',
[],
'sum',
2,
['member1' => '2', 'member2' => '4'],
],
'with weights' => [
[1, 'member1', 2, 'member2', 3, 'member3'],
[1, 'member1', 2, 'member2'],
'destination',
[2, 3],
'sum',
2,
['member1' => '5', 'member2' => '10'],
],
'with aggregate' => [
[1, 'member1', 4, 'member2', 3, 'member3'],
[2, 'member1', 2, 'member2'],
'destination',
[],
'max',
2,
['member1' => '2', 'member2' => '4'],
],
'with all arguments' => [
[1, 'member1', 5, 'member2', 4, 'member3'],
[2, 'member1', 2, 'member2'],
'destination',
[2, 3],
'max',
2,
['member1' => '6', 'member2' => '10'],
],
];
}
public function unexpectedValueProvider(): array
{
return [
'with unexpected keys argument' => [
'destination',
1,
[],
'sum',
'Wrong keys argument type or position offset'
],
'with unexpected weights argument' => [
'destination',
['key1'],
1,
'sum',
'Wrong weights argument type'
],
'with unexpected aggregate argument' => [
'destination',
['key1'],
[],
'wrong',
'Aggregate argument accepts only: min, max, sum values'
],
];
}
}
+152 -117
View File
@@ -11,6 +11,9 @@
namespace Predis\Command\Redis;
use Predis\Response\ServerException;
use UnexpectedValueException;
/**
* @group commands
* @group realm-zset
@@ -22,7 +25,7 @@ class ZUNIONSTORE_Test extends PredisCommandTestCase
*/
protected function getExpectedCommand(): string
{
return 'Predis\Command\Redis\ZUNIONSTORE';
return ZUNIONSTORE::class;
}
/**
@@ -34,45 +37,15 @@ class ZUNIONSTORE_Test extends PredisCommandTestCase
}
/**
* @dataProvider argumentsProvider
* @group disconnected
*/
public function testFilterArguments(): void
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
{
$modifiers = array(
'aggregate' => 'sum',
'weights' => array(10, 100),
);
$arguments = array('zset:destination', 2, 'zset1', 'zset2', $modifiers);
$expected = array(
'zset:destination', 2, 'zset1', 'zset2', 'WEIGHTS', 10, 100, 'AGGREGATE', 'sum',
);
$command = $this->getCommand();
$command->setArguments($arguments);
$command->setArguments($actualArguments);
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testFilterArgumentsSourceKeysAsSingleArray(): void
{
$modifiers = array(
'aggregate' => 'sum',
'weights' => array(10, 100),
);
$arguments = array('zset:destination', array('zset1', 'zset2'), $modifiers);
$expected = array(
'zset:destination', 2, 'zset1', 'zset2', 'WEIGHTS', 10, 100, 'AGGREGATE', 'sum',
);
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
$this->assertSame($expectedArguments, $command->getArguments());
}
/**
@@ -85,94 +58,42 @@ class ZUNIONSTORE_Test extends PredisCommandTestCase
/**
* @group connected
* @dataProvider sortedSetsProvider
* @param array $firstSortedSet
* @param array $secondSortedSet
* @param string $destination
* @param array $weights
* @param string $aggregate
* @param int $expectedResponse
* @param array $expectedResultSortedSet
* @return void
* @requiresRedisVersion >= 2.0.0
*/
public function testStoresUnionInNewSortedSet(): void
{
public function testStoresUnionValuesOnSortedSets(
array $firstSortedSet,
array $secondSortedSet,
string $destination,
array $weights,
string $aggregate,
int $expectedResponse,
array $expectedResultSortedSet
): void {
$redis = $this->getClient();
$redis->zadd('letters:1st', 1, 'a', 2, 'b', 3, 'c');
$redis->zadd('letters:2nd', 1, 'b', 2, 'c', 3, 'd');
$redis->zadd('test-zunionstore1', ...$firstSortedSet);
$redis->zadd('test-zunionstore2', ...$secondSortedSet);
$this->assertSame(4, $redis->zunionstore('letters:out', 2, 'letters:1st', 'letters:2nd'));
$this->assertSame(
array('a' => '1', 'b' => '3', 'd' => '3', 'c' => '5'),
$redis->zrange('letters:out', 0, -1, 'withscores')
$actualResponse = $redis->zunionstore(
$destination,
['test-zunionstore1', 'test-zunionstore2'],
$weights,
$aggregate
);
$this->assertSame(3, $redis->zunionstore('letters:out', 2, 'letters:1st', 'letters:void'));
$this->assertSame(3, $redis->zunionstore('letters:out', 2, 'letters:void', 'letters:2nd'));
$this->assertSame(0, $redis->zunionstore('letters:out', 2, 'letters:void', 'letters:void'));
}
/**
* @group connected
* @requiresRedisVersion >= 2.0.0
*/
public function testStoresUnionWithAggregateModifier(): void
{
$redis = $this->getClient();
$redis->zadd('letters:1st', 1, 'a', 2, 'b', 3, 'c');
$redis->zadd('letters:2nd', 1, 'b', 2, 'c', 3, 'd');
$options = array('aggregate' => 'min');
$this->assertSame(4, $redis->zunionstore('letters:min', 2, 'letters:1st', 'letters:2nd', $options));
$this->assertSame($expectedResponse, $actualResponse);
$this->assertSame(
array('a' => '1', 'b' => '1', 'c' => '2', 'd' => '3'),
$redis->zrange('letters:min', 0, -1, 'withscores')
);
$options = array('aggregate' => 'max');
$this->assertSame(4, $redis->zunionstore('letters:max', 2, 'letters:1st', 'letters:2nd', $options));
$this->assertSame(
array('a' => '1', 'b' => '2', 'c' => '3', 'd' => '3'),
$redis->zrange('letters:max', 0, -1, 'withscores')
);
$options = array('aggregate' => 'sum');
$this->assertSame(4, $redis->zunionstore('letters:sum', 2, 'letters:1st', 'letters:2nd', $options));
$this->assertSame(
array('a' => '1', 'b' => '3', 'd' => '3', 'c' => '5'),
$redis->zrange('letters:sum', 0, -1, 'withscores')
);
}
/**
* @group connected
* @requiresRedisVersion >= 2.0.0
*/
public function testStoresUnionWithWeightsModifier(): void
{
$redis = $this->getClient();
$redis->zadd('letters:1st', 1, 'a', 2, 'b', 3, 'c');
$redis->zadd('letters:2nd', 1, 'b', 2, 'c', 3, 'd');
$options = array('weights' => array(2, 3));
$this->assertSame(4, $redis->zunionstore('letters:out', 2, 'letters:1st', 'letters:2nd', $options));
$this->assertSame(
array('a' => '2', 'b' => '7', 'd' => '9', 'c' => '12'),
$redis->zrange('letters:out', 0, -1, 'withscores')
);
}
/**
* @group connected
* @requiresRedisVersion >= 2.0.0
*/
public function testStoresUnionWithCombinedModifiers(): void
{
$redis = $this->getClient();
$redis->zadd('letters:1st', 1, 'a', 2, 'b', 3, 'c');
$redis->zadd('letters:2nd', 1, 'b', 2, 'c', 3, 'd');
$options = array('aggregate' => 'max', 'weights' => array(10, 15));
$this->assertSame(4, $redis->zunionstore('letters:out', 2, 'letters:1st', 'letters:2nd', $options));
$this->assertSame(
array('a' => '10', 'b' => '20', 'c' => '30', 'd' => '45'),
$redis->zrange('letters:out', 0, -1, 'withscores')
$expectedResultSortedSet,
$redis->zrange($destination, 0, -1, ['withscores' => true])
);
}
@@ -182,12 +103,126 @@ class ZUNIONSTORE_Test extends PredisCommandTestCase
*/
public function testThrowsExceptionOnWrongType(): void
{
$this->expectException('Predis\Response\ServerException');
$this->expectException(ServerException::class);
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
$redis = $this->getClient();
$redis->set('foo', 'bar');
$redis->zunionstore('zset:destination', '1', 'foo');
$redis->zunionstore('zset_unionstore:destination', ['foo']);
}
/**
* @dataProvider unexpectedValueProvider
* @param string $destination
* @param $keys
* @param $weights
* @param string $aggregate
* @param string $expectedExceptionMessage
* @return void
*/
public function testThrowsExceptionOnUnexpectedValueGiven(
string $destination,
$keys,
$weights,
string $aggregate,
string $expectedExceptionMessage
): void {
$redis = $this->getClient();
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage($expectedExceptionMessage);
$redis->zunionstore($destination, $keys, $weights, $aggregate);
}
public function argumentsProvider(): array
{
return [
'with required arguments only' => [
['destination', ['key1', 'key2']],
['destination', 2, 'key1', 'key2'],
],
'with weights' => [
['destination', ['key1', 'key2'], [1, 2]],
['destination', 2, 'key1', 'key2', 'WEIGHTS', 1, 2],
],
'with aggregate' => [
['destination', ['key1', 'key2'], [], 'min'],
['destination', 2, 'key1', 'key2', 'AGGREGATE', 'MIN'],
],
'with all arguments' => [
['destination', ['key1', 'key2'], [1, 2], 'min'],
['destination', 2, 'key1', 'key2', 'WEIGHTS', 1, 2, 'AGGREGATE', 'MIN'],
]
];
}
public function sortedSetsProvider(): array
{
return [
'with required arguments' => [
[1, 'member1', 2, 'member2', 3, 'member3'],
[1, 'member1', 2, 'member2'],
'destination',
[],
'sum',
3,
['member1' => '2', 'member3' => '3', 'member2' => '4'],
],
'with weights' => [
[1, 'member1', 2, 'member2', 3, 'member3'],
[1, 'member1', 2, 'member2'],
'destination',
[2, 3],
'sum',
3,
['member1' => '5', 'member3' => '6', 'member2' => '10'],
],
'with aggregate' => [
[1, 'member1', 4, 'member2', 3, 'member3'],
[2, 'member1', 2, 'member2'],
'destination',
[],
'max',
3,
['member1' => '2', 'member3' => '3', 'member2' => '4'],
],
'with all arguments' => [
[1, 'member1', 5, 'member2', 4, 'member3'],
[2, 'member1', 2, 'member2'],
'destination',
[2, 3],
'max',
3,
['member1' => '6', 'member3' => '8', 'member2' => '10'],
],
];
}
public function unexpectedValueProvider(): array
{
return [
'with unexpected keys argument' => [
'destination',
1,
[],
'sum',
'Wrong keys argument type or position offset'
],
'with unexpected weights argument' => [
'destination',
['key1'],
1,
'sum',
'Wrong weights argument type'
],
'with unexpected aggregate argument' => [
'destination',
['key1'],
[],
'wrong',
'Aggregate argument accepts only: min, max, sum values'
],
];
}
}
@@ -0,0 +1,90 @@
<?php
namespace Predis\Command\Traits;
use Predis\Command\Command as RedisCommand;
use PredisTestCase;
use UnexpectedValueException;
class AggregateTest extends PredisTestCase
{
private $testClass;
protected function setUp(): void
{
$this->testClass = new class extends RedisCommand {
use Aggregate;
public static $aggregateArgumentPositionOffset = 0;
public function getId()
{
return 'test';
}
};
}
/**
* @dataProvider argumentsProvider
* @param int $offset
* @param array $actualArguments
* @param array $expectedArguments
* @return void
*/
public function testReturnsCorrectArguments(int $offset, array $actualArguments, array $expectedArguments): void
{
$this->testClass::$aggregateArgumentPositionOffset = $offset;
$this->testClass->setArguments($actualArguments);
$this->assertSame($expectedArguments, $this->testClass->getArguments());
}
/**
* @dataProvider unexpectedValuesProvider
* @param array $actualArguments
* @return void
*/
public function testThrowsExceptionOnUnexpectedValueGiven(array $actualArguments): void
{
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('Aggregate argument accepts only: min, max, sum values');
$this->testClass->setArguments($actualArguments);
}
public function argumentsProvider(): array
{
return [
'with wrong offset' => [2, ['argument1'], ['argument1']],
'aggregate argument first and there is arguments after' => [
0,
['sum', 'second argument', 'third argument'],
['AGGREGATE', 'SUM', 'second argument', 'third argument']
],
'aggregate argument last and there is arguments before' => [
2,
['first argument', 'second argument', 'min'],
['first argument', 'second argument', 'AGGREGATE', 'MIN']
],
'aggregate argument not the first and not the last' => [
1,
['first argument', 'max', 'third argument'],
['first argument', 'AGGREGATE', 'MAX', 'third argument']
],
'aggregate argument the only argument' => [
0,
['sum'],
['AGGREGATE', 'SUM']
]
];
}
public function unexpectedValuesProvider(): array
{
return [
'with non-string argument' => [[1]],
'with non enum value' => [['wrong']],
];
}
}
+26 -17
View File
@@ -2,6 +2,7 @@
namespace Predis\Command\Traits;
use Predis\Command\Command as RedisCommand;
use PredisTestCase;
use UnexpectedValueException;
@@ -13,26 +14,32 @@ class KeysTest extends PredisTestCase
{
parent::setUp();
$this->testClass = new class {
$this->testClass = new class extends RedisCommand {
use Keys;
public static $keysArgumentPositionOffset = 0;
public function getId()
{
return 'test';
}
};
}
/**
* @dataProvider keysProvider
* @dataProvider argumentsProvider
* @param int $offset
* @param array $actualArguments
* @param array $unpackedArguments
* @param array $expectedArguments
* @return void
*/
public function testUnpackKeysArrayTransformArrayCorrectly(
int $offset,
array $actualArguments,
array $unpackedArguments
): void {
$this->testClass->unpackKeysArray($offset, $actualArguments);
public function testReturnsCorrectArguments(int $offset, array $actualArguments, array $expectedArguments): void
{
$this->testClass::$keysArgumentPositionOffset = $offset;
$this->assertSame($unpackedArguments, $actualArguments);
$this->testClass->setArguments($actualArguments);
$this->assertSame($expectedArguments, $this->testClass->getArguments());
}
/**
@@ -41,36 +48,38 @@ class KeysTest extends PredisTestCase
* @param array $actualArguments
* @return void
*/
public function testUnpackKeysArrayThrowsExceptionOnUnexpectedValueGiven(int $offset, array $actualArguments): void
public function testThrowsExceptionOnUnexpectedValueGiven(int $offset, array $actualArguments): void
{
$this->testClass::$keysArgumentPositionOffset = $offset;
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('Wrong keys argument type or position offset');
$this->testClass->unpackKeysArray($offset, $actualArguments);
$this->testClass->setArguments($actualArguments);
}
public function keysProvider(): array
public function argumentsProvider(): array
{
return [
'keys argument first and there is arguments after' => [
0,
[['key1', 'key2'], 'second argument', 'third argument'],
['key1', 'key2', 'second argument', 'third argument'],
[2, 'key1', 'key2', 'second argument', 'third argument']
],
'keys argument last and there is arguments before' => [
2,
['first argument', 'second argument', ['key1', 'key2']],
['first argument', 'second argument', 'key1', 'key2'],
['first argument', 'second argument', 2, 'key1', 'key2']
],
'keys argument not the first and not the last' => [
1,
['first argument', ['key1', 'key2'], 'third argument'],
['first argument', 'key1', 'key2', 'third argument'],
['first argument', 2, 'key1', 'key2', 'third argument']
],
'keys argument the only argument' => [
0,
[['key1', 'key2']],
['key1', 'key2'],
[2, 'key1', 'key2']
]
];
}
-100
View File
@@ -1,100 +0,0 @@
<?php
namespace Predis\Command\Traits;
use PredisTestCase;
use Predis\Command\Command as RedisCommand;
use UnexpectedValueException;
class NumkeysTest extends PredisTestCase
{
private $testClass;
protected function setUp(): void
{
parent::setUp();
$this->testClass = new class extends RedisCommand {
use Numkeys;
public static $keysArgumentPositionOffset = 0;
public function getId()
{
return 'test';
}
};
}
/**
* @dataProvider argumentsProvider
* @param int $offset
* @param array $actualArguments
* @param array $expectedArguments
* @return void
*/
public function testReturnsCorrectArguments(int $offset, array $actualArguments, array $expectedArguments): void
{
$this->testClass::$keysArgumentPositionOffset = $offset;
$this->testClass->setArguments($actualArguments);
$this->assertSame($expectedArguments, $this->testClass->getArguments());
}
/**
* @dataProvider unexpectedValuesProvider
* @param int $offset
* @param array $actualArguments
* @return void
*/
public function testThrowsExceptionOnUnexpectedValueGiven(int $offset, array $actualArguments): void
{
$this->testClass::$keysArgumentPositionOffset = $offset;
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('Wrong keys argument type or position offset');
$this->testClass->setArguments($actualArguments);
}
public function argumentsProvider(): array
{
return [
'keys argument first and there is arguments after' => [
0,
[['key1', 'key2'], 'second argument', 'third argument'],
[2, ['key1', 'key2'], 'second argument', 'third argument']
],
'keys argument last and there is arguments before' => [
2,
['first argument', 'second argument', ['key1', 'key2']],
['first argument', 'second argument', 2, ['key1', 'key2']]
],
'keys argument not the first and not the last' => [
1,
['first argument', ['key1', 'key2'], 'third argument'],
['first argument', 2, ['key1', 'key2'], 'third argument']
],
'keys argument the only argument' => [
0,
[['key1', 'key2']],
[2, ['key1', 'key2']]
]
];
}
public function unexpectedValuesProvider(): array
{
return [
'keys argument not an array' => [
0,
['key1'],
],
'keys argument position offset higher then arguments quantity' => [
2,
[['key1', 'key2']],
]
];
}
}
@@ -0,0 +1,82 @@
<?php
namespace Predis\Command\Traits;
use Predis\Command\Command as RedisCommand;
use PredisTestCase;
use UnexpectedValueException;
class WeightsTest extends PredisTestCase
{
private $testClass;
protected function setUp(): void
{
$this->testClass = new class extends RedisCommand {
use Weights;
public static $weightsArgumentPositionOffset = 0;
public function getId()
{
return 'test';
}
};
}
/**
* @dataProvider argumentsProvider
* @param int $offset
* @param array $actualArguments
* @param array $expectedArguments
* @return void
*/
public function testReturnsCorrectArguments(int $offset, array $actualArguments, array $expectedArguments): void
{
$this->testClass::$weightsArgumentPositionOffset = $offset;
$this->testClass->setArguments($actualArguments);
$this->assertSame($expectedArguments, $this->testClass->getArguments());
}
/**
* @return void
*/
public function testThrowsExceptionOnUnexpectedValueGiven(): void
{
$actualArguments = [1];
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('Wrong weights argument type');
$this->testClass->setArguments($actualArguments);
}
public function argumentsProvider(): array
{
return [
'with wrong offset' => [2, ['argument1'], ['argument1']],
'weights argument first and there is arguments after' => [
0,
[[1, 2], 'second argument', 'third argument'],
['WEIGHTS', 1, 2, 'second argument', 'third argument']
],
'weights argument last and there is arguments before' => [
2,
['first argument', 'second argument', [1, 2]],
['first argument', 'second argument', 'WEIGHTS', 1, 2]
],
'weights argument not the first and not the last' => [
1,
['first argument', [1, 2], 'third argument'],
['first argument', 'WEIGHTS', 1, 2, 'third argument']
],
'weights argument the only argument' => [
0,
[[1, 2]],
['WEIGHTS', 1, 2]
]
];
}
}
@@ -66,6 +66,7 @@ class WithScoresTest extends PredisTestCase
public function dataProvider(): array
{
return [
'with empty arguments' => [[], [null]],
'without modifier' => [['member1', '1', 'member2', '2'], ['member1', '1', 'member2', '2']],
'with wrong modifier' => [
['member1', '1', 'member2', '2', 'WITHSCOREE'],