diff --git a/src/ClientContextInterface.php b/src/ClientContextInterface.php index 761d7977..7041dda3 100644 --- a/src/ClientContextInterface.php +++ b/src/ClientContextInterface.php @@ -11,6 +11,8 @@ namespace Predis; +use Predis\Command\Argument\Geospatial\ByInterface; +use Predis\Command\Argument\Geospatial\FromInterface; use Predis\Command\CommandInterface; /** @@ -182,6 +184,7 @@ use Predis\Command\CommandInterface; * @method $this geodist($key, $member1, $member2, $unit = null) * @method $this georadius($key, $longitude, $latitude, $radius, $unit, array $options = null) * @method $this georadiusbymember($key, $member, $radius, $unit, array $options = null) + * @method $this geosearch(string $key, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $withCoord = false, bool $withDist = false, bool $withHash = false) * * @author Daniele Alessandri */ diff --git a/src/ClientInterface.php b/src/ClientInterface.php index aec8a059..2134528f 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -11,6 +11,8 @@ namespace Predis; +use Predis\Command\Argument\Geospatial\ByInterface; +use Predis\Command\Argument\Geospatial\FromInterface; use Predis\Command\CommandInterface; use Predis\Command\FactoryInterface; use Predis\Configuration\OptionsInterface; @@ -200,6 +202,7 @@ use Predis\Response\Status; * @method string|null geodist(string $key, $member1, $member2, $unit = null) * @method array georadius(string $key, $longitude, $latitude, $radius, $unit, array $options = null) * @method array georadiusbymember(string $key, $member, $radius, $unit, array $options = null) + * @method array geosearch(string $key, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $withCoord = false, bool $withDist = false, bool $withHash = false) * * @author Daniele Alessandri */ diff --git a/src/Command/Argument/ArrayableArgument.php b/src/Command/Argument/ArrayableArgument.php new file mode 100644 index 00000000..4ca6999b --- /dev/null +++ b/src/Command/Argument/ArrayableArgument.php @@ -0,0 +1,16 @@ +unit = $unit; + } +} diff --git a/src/Command/Argument/Geospatial/ByBox.php b/src/Command/Argument/Geospatial/ByBox.php new file mode 100644 index 00000000..79e69d3b --- /dev/null +++ b/src/Command/Argument/Geospatial/ByBox.php @@ -0,0 +1,33 @@ +width = $width; + $this->height = $height; + $this->setUnit($unit); + } + + /** + * @inheritDoc + */ + public function toArray(): array + { + return [self::KEYWORD, $this->width, $this->height, $this->unit]; + } +} diff --git a/src/Command/Argument/Geospatial/ByInterface.php b/src/Command/Argument/Geospatial/ByInterface.php new file mode 100644 index 00000000..0187330b --- /dev/null +++ b/src/Command/Argument/Geospatial/ByInterface.php @@ -0,0 +1,9 @@ +radius = $radius; + $this->setUnit($unit); + } + + /** + * @inheritDoc + */ + public function toArray(): array + { + return [self::KEYWORD, $this->radius, $this->unit]; + } +} diff --git a/src/Command/Argument/Geospatial/FromInterface.php b/src/Command/Argument/Geospatial/FromInterface.php new file mode 100644 index 00000000..29e286eb --- /dev/null +++ b/src/Command/Argument/Geospatial/FromInterface.php @@ -0,0 +1,9 @@ +longitude = $longitude; + $this->latitude = $latitude; + } + + /** + * @inheritDoc + */ + public function toArray(): array + { + return [self::KEYWORD, $this->longitude, $this->latitude]; + } +} diff --git a/src/Command/Argument/Geospatial/FromMember.php b/src/Command/Argument/Geospatial/FromMember.php new file mode 100644 index 00000000..e0db6d5c --- /dev/null +++ b/src/Command/Argument/Geospatial/FromMember.php @@ -0,0 +1,26 @@ +member = $member; + } + + /** + * @inheritDoc + */ + public function toArray(): array + { + return [self::KEYWORD, $this->member]; + } +} diff --git a/src/Command/Redis/GEOSEARCH.php b/src/Command/Redis/GEOSEARCH.php new file mode 100644 index 00000000..0aa7456f --- /dev/null +++ b/src/Command/Redis/GEOSEARCH.php @@ -0,0 +1,112 @@ +setSorting($arguments); + $arguments = $this->getArguments(); + + $this->setWithCoord($arguments); + $arguments = $this->getArguments(); + + $this->setWithDist($arguments); + $arguments = $this->getArguments(); + + $this->setWithHash($arguments); + $arguments = $this->getArguments(); + + $this->setCount($arguments, $arguments[5] ?? false); + $arguments = $this->getArguments(); + + $this->setFrom($arguments); + $arguments = $this->getArguments(); + + $this->setBy($arguments); + $this->filterArguments(); + } + + public function parseResponse($data) + { + $parsedData = []; + $itemKey = ''; + + foreach ($data as $item) { + if (!is_array($item)) { + $parsedData[] = $item; + continue; + } + + foreach ($item as $key => $itemRow) { + if ($key === 0) { + $itemKey = $itemRow; + continue; + } + + if (is_string($itemRow)) { + $parsedData[$itemKey]['dist'] = round((float)$itemRow, 5); + } elseif (is_int($itemRow)) { + $parsedData[$itemKey]['hash'] = $itemRow; + } else { + $parsedData[$itemKey]['lng'] = round($itemRow[0], 5); + $parsedData[$itemKey]['lat'] = round($itemRow[1], 5); + } + } + } + + return $parsedData; + } +} diff --git a/src/Command/Redis/ZDIFF.php b/src/Command/Redis/ZDIFF.php index fad61d2a..c815ac27 100644 --- a/src/Command/Redis/ZDIFF.php +++ b/src/Command/Redis/ZDIFF.php @@ -3,8 +3,8 @@ namespace Predis\Command\Redis; use Predis\Command\Command as RedisCommand; -use Predis\Command\Traits\WithScores; use Predis\Command\Traits\Keys; +use Predis\Command\Traits\With\WithScores; /** * @link https://redis.io/commands/zdiff/ diff --git a/src/Command/Redis/ZINTER.php b/src/Command/Redis/ZINTER.php index e0e2a9c1..4c526414 100644 --- a/src/Command/Redis/ZINTER.php +++ b/src/Command/Redis/ZINTER.php @@ -2,7 +2,7 @@ namespace Predis\Command\Redis; -use Predis\Command\Traits\WithScores; +use Predis\Command\Traits\With\WithScores; /** * @link https://redis.io/commands/zinter/ diff --git a/src/Command/Redis/ZRANDMEMBER.php b/src/Command/Redis/ZRANDMEMBER.php index faa83d05..bc588070 100644 --- a/src/Command/Redis/ZRANDMEMBER.php +++ b/src/Command/Redis/ZRANDMEMBER.php @@ -3,7 +3,7 @@ namespace Predis\Command\Redis; use Predis\Command\Command as RedisCommand; -use Predis\Command\Traits\WithScores; +use Predis\Command\Traits\With\WithScores; /** * @link https://redis.io/commands/zrandmember/ diff --git a/src/Command/Redis/ZRANGESTORE.php b/src/Command/Redis/ZRANGESTORE.php index 9e1f8748..fff5a9de 100644 --- a/src/Command/Redis/ZRANGESTORE.php +++ b/src/Command/Redis/ZRANGESTORE.php @@ -3,7 +3,7 @@ namespace Predis\Command\Redis; use Predis\Command\Command as RedisCommand; -use Predis\Command\Traits\ByLexByScore; +use Predis\Command\Traits\By\ByLexByScore; use Predis\Command\Traits\Limit; use Predis\Command\Traits\Rev; diff --git a/src/Command/Redis/ZUNION.php b/src/Command/Redis/ZUNION.php index b5a48578..06a5a284 100644 --- a/src/Command/Redis/ZUNION.php +++ b/src/Command/Redis/ZUNION.php @@ -2,7 +2,7 @@ namespace Predis\Command\Redis; -use Predis\Command\Traits\WithScores; +use Predis\Command\Traits\With\WithScores; /** * @link https://redis.io/commands/zunion/ diff --git a/src/Command/Traits/ByLexByScore.php b/src/Command/Traits/By/ByLexByScore.php similarity index 96% rename from src/Command/Traits/ByLexByScore.php rename to src/Command/Traits/By/ByLexByScore.php index 59c84bfa..846c7a06 100644 --- a/src/Command/Traits/ByLexByScore.php +++ b/src/Command/Traits/By/ByLexByScore.php @@ -1,6 +1,6 @@ getByArgumentPositionOffset($arguments); + + if (null === $argumentPositionOffset) { + throw new InvalidArgumentException('Invalid BY argument value given'); + } + + $byArgumentObject = $arguments[$argumentPositionOffset]; + $argumentsBefore = array_slice($arguments, 0, $argumentPositionOffset); + $argumentsAfter = array_slice($arguments, $argumentPositionOffset + 1); + + parent::setArguments(array_merge( + $argumentsBefore, + $byArgumentObject->toArray(), + $argumentsAfter + )); + } + + private function getByArgumentPositionOffset(array $arguments): ?int + { + foreach ($arguments as $i => $value) { + if ($value instanceof ByInterface) { + return $i; + } + } + + return null; + } +} diff --git a/src/Command/Traits/Count.php b/src/Command/Traits/Count.php index 93c61db3..a34e62f7 100644 --- a/src/Command/Traits/Count.php +++ b/src/Command/Traits/Count.php @@ -11,8 +11,9 @@ use UnexpectedValueException; trait Count { private $countModifier = 'COUNT'; + private $anyModifier = 'ANY'; - public function setArguments(array $arguments) + public function setArguments(array $arguments, bool $any = false) { $argumentsLength = count($arguments); @@ -21,18 +22,36 @@ trait Count return; } + if ($arguments[static::$countArgumentPositionOffset] === -1) { + array_splice($arguments, static::$countArgumentPositionOffset, 1, [false]); + parent::setArguments($arguments); + return; + } + if ($arguments[static::$countArgumentPositionOffset] < 1) { throw new UnexpectedValueException('Wrong count argument value or position offset'); } $countArgument = $arguments[static::$countArgumentPositionOffset]; $argumentsBefore = array_slice($arguments, 0, static::$countArgumentPositionOffset); - $argumentsAfter = array_slice($arguments, static::$countArgumentPositionOffset + 1); + $argumentsAfter = array_slice($arguments, static::$countArgumentPositionOffset + 2); + + if (!$any) { + $argumentsAfter = array_slice($arguments, static::$countArgumentPositionOffset + 1); + parent::setArguments(array_merge( + $argumentsBefore, + [$this->countModifier], + [$countArgument], + $argumentsAfter + )); + return; + } parent::setArguments(array_merge( $argumentsBefore, [$this->countModifier], [$countArgument], + [$this->anyModifier], $argumentsAfter )); } diff --git a/src/Command/Traits/From/GeoFrom.php b/src/Command/Traits/From/GeoFrom.php new file mode 100644 index 00000000..7f89cf24 --- /dev/null +++ b/src/Command/Traits/From/GeoFrom.php @@ -0,0 +1,39 @@ +getFromArgumentPositionOffset($arguments); + + if (null === $argumentPositionOffset) { + throw new InvalidArgumentException('Invalid FROM argument value given'); + } + + $fromArgumentObject = $arguments[$argumentPositionOffset]; + $argumentsBefore = array_slice($arguments, 0, $argumentPositionOffset); + $argumentsAfter = array_slice($arguments, $argumentPositionOffset + 1); + + parent::setArguments(array_merge( + $argumentsBefore, + $fromArgumentObject->toArray(), + $argumentsAfter + )); + } + + private function getFromArgumentPositionOffset(array $arguments): ?int + { + foreach ($arguments as $i => $value) { + if ($value instanceof FromInterface) { + return $i; + } + } + + return null; + } +} diff --git a/src/Command/Traits/Sorting.php b/src/Command/Traits/Sorting.php new file mode 100644 index 00000000..4fc18f7e --- /dev/null +++ b/src/Command/Traits/Sorting.php @@ -0,0 +1,45 @@ + 'ASC', + 'desc' => 'DESC', + ]; + + public function setArguments(array $arguments) + { + $argumentsLength = count($arguments); + + if (static::$sortArgumentPositionOffset >= $argumentsLength) { + parent::setArguments($arguments); + return; + } + + $argument = $arguments[static::$sortArgumentPositionOffset]; + + if (null === $argument) { + array_splice($arguments, static::$sortArgumentPositionOffset, 1, [false]); + parent::setArguments($arguments); + return; + } + + if (!in_array(strtoupper($argument), self::$sortingEnum, true)) { + $enumValues = implode(', ', array_keys(self::$sortingEnum)); + throw new UnexpectedValueException("Sorting argument accepts only: {$enumValues} values"); + } + + $argumentsBefore = array_slice($arguments, 0, static::$sortArgumentPositionOffset); + $argumentsAfter = array_slice($arguments, static::$sortArgumentPositionOffset + 1); + + parent::setArguments(array_merge( + $argumentsBefore, + [self::$sortingEnum[$argument]], + $argumentsAfter + )); + } +} diff --git a/src/Command/Traits/With/WithCoord.php b/src/Command/Traits/With/WithCoord.php new file mode 100644 index 00000000..3c9b4a3c --- /dev/null +++ b/src/Command/Traits/With/WithCoord.php @@ -0,0 +1,38 @@ += $argumentsLength + || false === $arguments[static::$withCoordArgumentPositionOffset] + ) { + parent::setArguments($arguments); + return; + } + + $argument = $arguments[static::$withCoordArgumentPositionOffset]; + + if (true === $argument) { + $argument = 'WITHCOORD'; + } else { + throw new UnexpectedValueException("Wrong WITHCOORD argument type"); + } + + $argumentsBefore = array_slice($arguments, 0, static::$withCoordArgumentPositionOffset); + $argumentsAfter = array_slice($arguments, static::$withCoordArgumentPositionOffset + 1); + + parent::setArguments(array_merge($argumentsBefore, [$argument], $argumentsAfter)); + } +} diff --git a/src/Command/Traits/With/WithDist.php b/src/Command/Traits/With/WithDist.php new file mode 100644 index 00000000..221227f9 --- /dev/null +++ b/src/Command/Traits/With/WithDist.php @@ -0,0 +1,34 @@ += $argumentsLength + || false === $arguments[static::$withDistArgumentPositionOffset] + ) { + parent::setArguments($arguments); + return; + } + + $argument = $arguments[static::$withDistArgumentPositionOffset]; + + if (true === $argument) { + $argument = 'WITHDIST'; + } else { + throw new UnexpectedValueException("Wrong WITHDIST argument type"); + } + + $argumentsBefore = array_slice($arguments, 0, static::$withDistArgumentPositionOffset); + $argumentsAfter = array_slice($arguments, static::$withDistArgumentPositionOffset + 1); + + parent::setArguments(array_merge($argumentsBefore, [$argument], $argumentsAfter)); + } +} diff --git a/src/Command/Traits/With/WithHash.php b/src/Command/Traits/With/WithHash.php new file mode 100644 index 00000000..77853d82 --- /dev/null +++ b/src/Command/Traits/With/WithHash.php @@ -0,0 +1,34 @@ += $argumentsLength + || false === $arguments[static::$withHashArgumentPositionOffset] + ) { + parent::setArguments($arguments); + return; + } + + $argument = $arguments[static::$withHashArgumentPositionOffset]; + + if (true === $argument) { + $argument = 'WITHHASH'; + } else { + throw new UnexpectedValueException("Wrong WITHHASH argument type"); + } + + $argumentsBefore = array_slice($arguments, 0, static::$withHashArgumentPositionOffset); + $argumentsAfter = array_slice($arguments, static::$withHashArgumentPositionOffset + 1); + + parent::setArguments(array_merge($argumentsBefore, [$argument], $argumentsAfter)); + } +} diff --git a/src/Command/Traits/WithScores.php b/src/Command/Traits/With/WithScores.php similarity index 97% rename from src/Command/Traits/WithScores.php rename to src/Command/Traits/With/WithScores.php index c2d1ac8c..6c91f2e1 100644 --- a/src/Command/Traits/WithScores.php +++ b/src/Command/Traits/With/WithScores.php @@ -1,6 +1,6 @@ getCommand(); + $command->setArguments($actualArguments); + + $this->assertSameValues($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 coordinatesProvider + * @param array $firstCoordinates + * @param array $secondCoordinates + * @param array $thirdCoordinates + * @param string $key + * @param FromInterface $from + * @param ByInterface $by + * @param string|null $sorting + * @param int $count + * @param bool $any + * @param bool $withCoord + * @param bool $withDist + * @param bool $withHash + * @param array $expectedResponse + * @return void + * @requiresRedisVersion >= 6.2.0 + */ + public function testReturnsSearchedGeospatialCoordinates( + array $firstCoordinates, + array $secondCoordinates, + array $thirdCoordinates, + string $key, + FromInterface $from, + ByInterface $by, + ?string $sorting, + int $count, + bool $any, + bool $withCoord, + bool $withDist, + bool $withHash, + array $expectedResponse + ): void { + $redis = $this->getClient(); + + $redis->geoadd(...$firstCoordinates); + $redis->geoadd(...$secondCoordinates); + $redis->geoadd(...$thirdCoordinates); + + $this->assertSame( + $expectedResponse, + $redis->geosearch($key, $from, $by, $sorting, $count, $any, $withCoord, $withDist, $withHash) + ); + } + + /** + * @group connected + * @dataProvider unexpectedValuesProvider + * @param array $arguments + * @param string $expectedException + * @param string $expectedExceptionMessage + * @return void + * @requiresRedisVersion >= 6.2.0 + */ + public function testThrowsExceptionOnUnexpectedValueProvided( + array $arguments, + string $expectedException, + string $expectedExceptionMessage + ): void { + $redis = $this->getClient(); + + $this->expectException($expectedException); + $this->expectExceptionMessage($expectedExceptionMessage); + + $redis->geosearch(...$arguments); + } + + public function argumentsProvider(): array + { + return [ + 'with default arguments - FROMLONLAT, BYRADIUS' => [ + ['key', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km')], + ['key', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km'] + ], + 'with default arguments - FROMMEMBER, BYBOX' => [ + ['key', new FromMember('member'), new ByBox(1,1, 'km')], + ['key', 'FROMMEMBER', 'member', 'BYBOX', 1, 1, 'km'] + ], + 'with ASC sorting' => [ + ['key', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), 'asc'], + ['key', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'ASC'] + ], + 'with DESC sorting' => [ + ['key', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), 'desc'], + ['key', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'DESC'] + ], + 'with COUNT argument - without ANY option' => [ + ['key', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), null, 20], + ['key', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'COUNT', 20] + ], + 'with COUNT argument - with ANY option' => [ + ['key', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), null, 20, true], + ['key', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'COUNT', 20, 'ANY'] + ], + 'with WITHCOORD argument' => [ + ['key', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), null, -1, false, true], + ['key', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'WITHCOORD'] + ], + 'with WITHDIST argument' => [ + ['key', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), null, -1, false, false, true], + ['key', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'WITHDIST'] + ], + 'with WITHHASH argument' => [ + ['key', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), null, -1, false, false, false, true], + ['key', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'WITHHASH'] + ], + 'with all arguments' => [ + ['key', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), 'asc', 20, true, true, true, true], + ['key', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'ASC', 'COUNT', 20, 'ANY', 'WITHCOORD', 'WITHDIST', 'WITHHASH'] + ] + ]; + } + + public function responsesProvider(): array + { + return [ + 'without WITH modifiers' => [ + ['member1', 'member2', 'member3'], + ['member1', 'member2', 'member3'], + ], + 'with WITHCOORD modifier' => [ + [['member1', [1.1, 2.2]], ['member2', [2.2, 3.3]], ['member3', [3.3, 4.4]]], + [ + 'member1' => ['lng' => 1.1, 'lat' => 2.2], + 'member2' => ['lng' => 2.2, 'lat' => 3.3], + 'member3' => ['lng' => 3.3, 'lat' => 4.4]] + ], + 'with WITHDIST modifier' => [ + [['member1', '111.111'], ['member2', '222.222'], ['member3', '333.333']], + [ + 'member1' => ['dist' => 111.111], + 'member2' => ['dist' => 222.222], + 'member3' => ['dist' => 333.333], + ], + ], + 'with WITHHASH modifier' => [ + [['member1', 1111], ['member2', 2222], ['member3', 3333]], + [ + 'member1' => ['hash' => 1111], + 'member2' => ['hash' => 2222], + 'member3' => ['hash' => 3333], + ], + ], + 'with all WITH modifiers' => [ + [ + ['member1', '111.111', 1111, [1.1, 2.2]], + ['member2', '222.222', 2222, [2.2, 3.3]], + ['member3', '333.333', 3333, [3.3, 4.4]], + ], + [ + 'member1' => [ + 'dist' => 111.111, + 'hash' => 1111, + 'lng' => 1.1, + 'lat' => 2.2, + ], + 'member2' => [ + 'dist' => 222.222, + 'hash' => 2222, + 'lng' => 2.2, + 'lat' => 3.3, + ], + 'member3' => [ + 'dist' => 333.333, + 'hash' => 3333, + 'lng' => 3.3, + 'lat' => 4.4, + ], + ], + ] + ]; + } + + public function coordinatesProvider(): array + { + return [ + 'with default arguments - FROMLONLAT, BYRADIUS - all members' => [ + ['key', 1.1, 2, 'member1'], + ['key', 2.1, 3, 'member2'], + ['key', 3.1, 4, 'member3'], + 'key', + new FromLonLat(1, 4), + new ByRadius(9999, 'km'), + null, + -1, + false, + false, + false, + false, + ['member1', 'member2', 'member3'] + ], + 'with default arguments - FROMLONLAT, BYRADIUS - closest members' => [ + ['key', 1.1, 2, 'member1'], + ['key', 2.1, 3, 'member2'], + ['key', 3.1, 4, 'member3'], + 'key', + new FromLonLat(1, 3), + new ByRadius(200, 'km'), + null, + -1, + false, + false, + false, + false, + ['member2', 'member1'] + ], + 'with default arguments - FROMMEMBER, BYBOX - all members' => [ + ['key', 1.1, 2, 'member1'], + ['key', 2.1, 3, 'member2'], + ['key', 3.1, 4, 'member3'], + 'key', + new FromMember('member2'), + new ByBox(999, 999, 'km'), + null, + -1, + false, + false, + false, + false, + ['member1', 'member2', 'member3'] + ], + 'with default arguments - FROMMEMBER, BYBOX - closest members' => [ + ['key', 1.1, 2, 'member1'], + ['key', 2.1, 3, 'member2'], + ['key', 3.1, 4, 'member3'], + 'key', + new FromMember('member1'), + new ByBox(300, 300, 'km'), + null, + -1, + false, + false, + false, + false, + ['member1', 'member2'] + ], + 'with ASC modifier' => [ + ['key', 1.1, 2, 'member1'], + ['key', 2.1, 3, 'member2'], + ['key', 3.1, 4, 'member3'], + 'key', + new FromLonLat(1, 4), + new ByRadius(9999, 'km'), + 'asc', + -1, + false, + false, + false, + false, + ['member2', 'member1', 'member3'] + ], + 'with DESC modifier' => [ + ['key', 1.1, 2, 'member1'], + ['key', 2.1, 3, 'member2'], + ['key', 3.1, 4, 'member3'], + 'key', + new FromLonLat(1, 4), + new ByRadius(9999, 'km'), + 'desc', + -1, + false, + false, + false, + false, + ['member3', 'member1', 'member2'] + ], + 'with COUNT modifier - without ANY option' => [ + ['key', 1.1, 2, 'member1'], + ['key', 2.1, 3, 'member2'], + ['key', 3.1, 4, 'member3'], + 'key', + new FromLonLat(1, 4), + new ByRadius(9999, 'km'), + null, + 1, + false, + false, + false, + false, + ['member2'] + ], + 'with COUNT modifier - with ANY option' => [ + ['key', 1.1, 2, 'member1'], + ['key', 2.1, 3, 'member2'], + ['key', 3.1, 4, 'member3'], + 'key', + new FromLonLat(1, 4), + new ByRadius(9999, 'km'), + null, + 2, + true, + false, + false, + false, + ['member1', 'member2'] + ], + 'with WITHCOORD modifier' => [ + ['key', 1.1, 2, 'member1'], + ['key', 2.1, 3, 'member2'], + ['key', 3.1, 4, 'member3'], + 'key', + new FromLonLat(1, 4), + new ByRadius(9999, 'km'), + null, + -1, + false, + true, + false, + false, + [ + 'member1' => ['lng' => 1.1, 'lat' => 2.0], + 'member2' => ['lng' => 2.1, 'lat' => 3.0], + 'member3' => ['lng' => 3.1, 'lat' => 4.0], + ] + ], + 'with WITHDIST modifier' => [ + ['key', 1.1, 2, 'member1'], + ['key', 2.1, 3, 'member2'], + ['key', 3.1, 4, 'member3'], + 'key', + new FromLonLat(1, 4), + new ByRadius(9999, 'km'), + null, + -1, + false, + false, + true, + false, + [ + 'member1' => ['dist' => 222.7297], + 'member2' => ['dist' => 165.1798], + 'member3' => ['dist' => 233.006], + ] + ], + 'with WITHHASH modifier' => [ + ['key', 1.1, 2, 'member1'], + ['key', 2.1, 3, 'member2'], + ['key', 3.1, 4, 'member3'], + 'key', + new FromLonLat(1, 4), + new ByRadius(9999, 'km'), + null, + -1, + false, + false, + false, + true, + [ + 'member1' => ['hash' => 3378086406303657], + 'member2' => ['hash' => 3378965307136228], + 'member3' => ['hash' => 3379626601756294], + ] + ], + 'with all arguments' => [ + ['key', 1.1, 2, 'member1'], + ['key', 2.1, 3, 'member2'], + ['key', 3.1, 4, 'member3'], + 'key', + new FromLonLat(1, 4), + new ByRadius(9999, 'km'), + 'asc', + 1, + true, + true, + true, + true, + [ + 'member1' => [ + 'dist' => 222.7297, + 'hash' => 3378086406303657, + 'lng' => 1.1, + 'lat' => 2.0, + ], + ], + ], + ]; + } + + public function unexpectedValuesProvider(): array + { + return [ + 'with wrong FROM argument' => [ + ['key', false, new ByRadius(9999, 'km'), null, -1, false, false], + InvalidArgumentException::class, + 'Invalid FROM argument value given' + ], + 'with wrong BY argument' => [ + ['key', new FromLonLat(1, 4), false, null, -1, false, false], + InvalidArgumentException::class, + 'Invalid BY argument value given' + ], + 'with wrong sorting argument' => [ + ['key', new FromLonLat(1, 4), new ByRadius(9999, 'km'), 'wrong', -1, false, false], + UnexpectedValueException::class, + 'Sorting argument accepts only: asc, desc values' + ], + 'with wrong COUNT argument' => [ + ['key', new FromLonLat(1, 4), new ByRadius(9999, 'km'), null, 0, false, false], + UnexpectedValueException::class, + 'Wrong count argument value or position offset' + ], + 'with wrong WITHCOORD argument' => [ + ['key', new FromLonLat(1, 4), new ByRadius(9999, 'km'), null, 0, false, 'wrong'], + UnexpectedValueException::class, + 'Wrong WITHCOORD argument type' + ], + 'with wrong WITHDIST argument' => [ + ['key', new FromLonLat(1, 4), new ByRadius(9999, 'km'), null, 0, false, false, 'wrong'], + UnexpectedValueException::class, + 'Wrong WITHDIST argument type' + ], + 'with wrong WITHHASH argument' => [ + ['key', new FromLonLat(1, 4), new ByRadius(9999, 'km'), null, 0, false, false, false, 'wrong'], + UnexpectedValueException::class, + 'Wrong WITHHASH argument type' + ], + ]; + } +} diff --git a/tests/Predis/Command/Traits/By/GeoByTest.php b/tests/Predis/Command/Traits/By/GeoByTest.php new file mode 100644 index 00000000..5e043c76 --- /dev/null +++ b/tests/Predis/Command/Traits/By/GeoByTest.php @@ -0,0 +1,66 @@ +testClass = new class extends RedisCommand { + use GeoBy; + + public function getId() + { + return 'test'; + } + }; + } + + /** + * @dataProvider argumentsProvider + * @param array $actualArguments + * @param array $expectedArguments + * @return void + */ + public function testReturnsCorrectArguments(array $actualArguments, array $expectedArguments): void + { + $this->testClass->setArguments($actualArguments); + + $this->assertSame($expectedArguments, $this->testClass->getArguments()); + } + + /** + * @return void + */ + public function testThrowsExceptionOnUnexpectedValue(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid BY argument value given"); + + $this->testClass->setArguments(['test']); + } + + public function argumentsProvider(): array + { + return [ + 'BYRADIUS argument' => [ + ['first argument', new ByRadius(1, 'km'), 'third argument'], + ['first argument', 'BYRADIUS', 1, 'km', 'third argument'] + ], + 'BYBOX argument' => [ + ['first argument', new ByBox(1, 1, 'km'), 'third argument'], + ['first argument', 'BYBOX', 1, 1, 'km', 'third argument'] + ], + ]; + } +} diff --git a/tests/Predis/Command/Traits/ByLexByScoreTest.php b/tests/Predis/Command/Traits/ByLexByScoreTest.php index 6ca51e20..6c66c7ec 100644 --- a/tests/Predis/Command/Traits/ByLexByScoreTest.php +++ b/tests/Predis/Command/Traits/ByLexByScoreTest.php @@ -2,8 +2,9 @@ namespace Predis\Command\Traits; -use PredisTestCase; use Predis\Command\Command as RedisCommand; +use Predis\Command\Traits\By\ByLexByScore; +use PredisTestCase; use UnexpectedValueException; class ByLexByScoreTest extends PredisTestCase diff --git a/tests/Predis/Command/Traits/CountTest.php b/tests/Predis/Command/Traits/CountTest.php index 3744ecb9..c71dd85e 100644 --- a/tests/Predis/Command/Traits/CountTest.php +++ b/tests/Predis/Command/Traits/CountTest.php @@ -30,14 +30,15 @@ class CountTest extends PredisTestCase * @dataProvider argumentsProvider * @param int $offset * @param array $arguments + * @param bool $any * @param array $expectedResponse * @return void */ - public function testReturnsCorrectArguments(int $offset, array $arguments, array $expectedResponse): void + public function testReturnsCorrectArguments(int $offset, bool $any, array $arguments, array $expectedResponse): void { $this->testClass::$countArgumentPositionOffset = $offset; - $this->testClass->setArguments($arguments); + $this->testClass->setArguments($arguments, $any); $this->assertSameValues($expectedResponse, $this->testClass->getArguments()); } @@ -57,14 +58,28 @@ class CountTest extends PredisTestCase return [ 'with count argument' => [ 0, + false, [2], ['COUNT', 2] ], 'without count argument' => [ 2, + false, ['argument1', 'argument2'], ['argument1', 'argument2'] - ] + ], + 'with count argument equal -1' => [ + 0, + false, + [-1], + [false] + ], + 'with any modifier' => [ + 0, + true, + [2], + ['COUNT', 2, 'ANY'], + ], ]; } } diff --git a/tests/Predis/Command/Traits/From/GeoFromTest.php b/tests/Predis/Command/Traits/From/GeoFromTest.php new file mode 100644 index 00000000..294a27d1 --- /dev/null +++ b/tests/Predis/Command/Traits/From/GeoFromTest.php @@ -0,0 +1,66 @@ +testClass = new class extends RedisCommand { + use GeoFrom; + + public function getId() + { + return 'test'; + } + }; + } + + /** + * @dataProvider argumentsProvider + * @param array $actualArguments + * @param array $expectedArguments + * @return void + */ + public function testReturnsCorrectArguments(array $actualArguments, array $expectedArguments): void + { + $this->testClass->setArguments($actualArguments); + + $this->assertSame($expectedArguments, $this->testClass->getArguments()); + } + + /** + * @return void + */ + public function testThrowsExceptionOnUnexpectedValue(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid FROM argument value given"); + + $this->testClass->setArguments(['test']); + } + + public function argumentsProvider(): array + { + return [ + 'FROMLONLAT argument' => [ + ['first argument', new FromLonLat(1.1, 2.2), 'third argument'], + ['first argument', 'FROMLONLAT', 1.1, 2.2, 'third argument'] + ], + 'FROMMEMBER argument' => [ + ['first argument', new FromMember('member1'), 'third argument'], + ['first argument', 'FROMMEMBER', 'member1', 'third argument'] + ], + ]; + } +} diff --git a/tests/Predis/Command/Traits/With/WithCoordTest.php b/tests/Predis/Command/Traits/With/WithCoordTest.php new file mode 100644 index 00000000..8a4aa7a5 --- /dev/null +++ b/tests/Predis/Command/Traits/With/WithCoordTest.php @@ -0,0 +1,83 @@ +testClass = new class extends RedisCommand { + use WithCoord; + + public static $withCoordArgumentPositionOffset = 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::$withCoordArgumentPositionOffset = $offset; + + $this->testClass->setArguments($actualArguments); + + $this->assertSame($expectedArguments, $this->testClass->getArguments()); + } + + /** + * @return void + */ + public function testThrowsExceptionOnUnexpectedValue(): void + { + $this->testClass::$withCoordArgumentPositionOffset = 0; + + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessage("Wrong WITHCOORD argument type"); + + $this->testClass->setArguments(['test']); + } + + public function argumentsProvider(): array + { + return [ + 'WITHCOORD false argument' => [ + 0, + [false, 'second argument', 'third argument'], + [false, 'second argument', 'third argument'] + ], + 'WITHCOORD argument first and there is arguments after' => [ + 0, + [true, 'second argument', 'third argument'], + ['WITHCOORD', 'second argument', 'third argument'] + ], + 'WITHCOORD argument last and there is arguments before' => [ + 2, + ['first argument', 'second argument', true], + ['first argument', 'second argument', 'WITHCOORD'] + ], + 'WITHCOORD argument not the first and not the last' => [ + 1, + ['first argument', true, 'third argument'], + ['first argument', 'WITHCOORD', 'third argument'] + ], + ]; + } +} diff --git a/tests/Predis/Command/Traits/With/WithDistTest.php b/tests/Predis/Command/Traits/With/WithDistTest.php new file mode 100644 index 00000000..0a96d6ec --- /dev/null +++ b/tests/Predis/Command/Traits/With/WithDistTest.php @@ -0,0 +1,83 @@ +testClass = new class extends RedisCommand { + use WithDist; + + public static $withDistArgumentPositionOffset = 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::$withDistArgumentPositionOffset = $offset; + + $this->testClass->setArguments($actualArguments); + + $this->assertSame($expectedArguments, $this->testClass->getArguments()); + } + + /** + * @return void + */ + public function testThrowsExceptionOnUnexpectedValue(): void + { + $this->testClass::$withDistArgumentPositionOffset = 0; + + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessage("Wrong WITHDIST argument type"); + + $this->testClass->setArguments(['test']); + } + + public function argumentsProvider(): array + { + return [ + 'WITHDIST false argument' => [ + 0, + [false, 'second argument', 'third argument'], + [false, 'second argument', 'third argument'] + ], + 'WITHDIST argument first and there is arguments after' => [ + 0, + [true, 'second argument', 'third argument'], + ['WITHDIST', 'second argument', 'third argument'] + ], + 'WITHDIST argument last and there is arguments before' => [ + 2, + ['first argument', 'second argument', true], + ['first argument', 'second argument', 'WITHDIST'] + ], + 'WITHDIST argument not the first and not the last' => [ + 1, + ['first argument', true, 'third argument'], + ['first argument', 'WITHDIST', 'third argument'] + ], + ]; + } +} diff --git a/tests/Predis/Command/Traits/With/WithHashTest.php b/tests/Predis/Command/Traits/With/WithHashTest.php new file mode 100644 index 00000000..7d6c8ed9 --- /dev/null +++ b/tests/Predis/Command/Traits/With/WithHashTest.php @@ -0,0 +1,83 @@ +testClass = new class extends RedisCommand { + use WithHash; + + public static $withHashArgumentPositionOffset = 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::$withHashArgumentPositionOffset = $offset; + + $this->testClass->setArguments($actualArguments); + + $this->assertSame($expectedArguments, $this->testClass->getArguments()); + } + + /** + * @return void + */ + public function testThrowsExceptionOnUnexpectedValue(): void + { + $this->testClass::$withHashArgumentPositionOffset = 0; + + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessage("Wrong WITHHASH argument type"); + + $this->testClass->setArguments(['test']); + } + + public function argumentsProvider(): array + { + return [ + 'WITHHASH false argument' => [ + 0, + [false, 'second argument', 'third argument'], + [false, 'second argument', 'third argument'] + ], + 'WITHHASH argument first and there is arguments after' => [ + 0, + [true, 'second argument', 'third argument'], + ['WITHHASH', 'second argument', 'third argument'] + ], + 'WITHHASH argument last and there is arguments before' => [ + 2, + ['first argument', 'second argument', true], + ['first argument', 'second argument', 'WITHHASH'] + ], + 'WITHHASH argument not the first and not the last' => [ + 1, + ['first argument', true, 'third argument'], + ['first argument', 'WITHHASH', 'third argument'] + ], + ]; + } +} diff --git a/tests/Predis/Command/Traits/WithScoresTest.php b/tests/Predis/Command/Traits/With/WithScoresTest.php similarity index 98% rename from tests/Predis/Command/Traits/WithScoresTest.php rename to tests/Predis/Command/Traits/With/WithScoresTest.php index 411a8b4f..e9ce7862 100644 --- a/tests/Predis/Command/Traits/WithScoresTest.php +++ b/tests/Predis/Command/Traits/With/WithScoresTest.php @@ -1,9 +1,9 @@