diff --git a/.codespellrc b/.codespellrc
index 0e95e105..70f77356 100644
--- a/.codespellrc
+++ b/.codespellrc
@@ -3,4 +3,4 @@ skip=./.git
check-hidden=
check-filenames=
builtin=clear,rare,informal,usage,code,names
-ignore-words-list=master,masters,slave,slaves,whitelist,cas,exat,smove,SUGGET,sugget
+ignore-words-list=master,masters,slave,slaves,whitelist,cas,exat,smove,SUGGET,sugget,ro
diff --git a/.editorconfig b/.editorconfig
index 0764720b..9cffd80e 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -14,7 +14,7 @@ block_comment_end = */
[*.php]
max_line_length = 150
-[*.{md,yml,yaml,neon}]
+[*.{md,yml,yaml,neon,sh}]
indent_size = 2
[tests/**.php]
diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml
index b6d481b0..1e22c899 100644
--- a/.github/workflows/linters.yml
+++ b/.github/workflows/linters.yml
@@ -136,7 +136,7 @@ jobs:
EXPECTED="LICENSE,README.md,autoload.php,composer.json"
CURRENT="$(
git archive HEAD \
- | tar --list --exclude="src" --exclude="src/*" --exclude="bin" --exclude="bin/*" \
+ | tar --list --exclude="src" --exclude="src/*" --exclude="bin" --exclude="bin/*" --exclude="docker" --exclude="docker/*" \
| paste --serial --delimiters=","
)"
echo "CURRENT =${CURRENT}"
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index b5259142..44f3b7f3 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -78,3 +78,46 @@ jobs:
run: |
wget "https://github.com/php-coveralls/php-coveralls/releases/download/v2.5.3/php-coveralls.phar"
php ./php-coveralls.phar -v
+
+ predis-cluster:
+
+ name: PHP ${{ matrix.php }} (Redis Cluster latest)
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ php:
+ - '7.2'
+ - '7.3'
+ - '7.4'
+ - '8.0'
+ - '8.1'
+ - '8.2'
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Run redis cluster
+ uses: isbang/compose-action@v1.4.1
+ with:
+ compose-file: "./docker/unstable_cluster/docker-compose.yml"
+
+ - name: Setup PHP with Composer and extensions
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ extensions: relay
+ coverage: ${{ (matrix.php == '8.1') && 'xdebug' || 'none' }}
+
+ - name: Install Composer dependencies
+ uses: ramsey/composer-install@v2
+ with:
+ dependency-versions: highest
+ composer-options: ${{ matrix.php == '8.0' && '--ignore-platform-reqs' || '' }}
+
+ - name: Run tests against cluster
+ run: |
+ sleep 5 # Timeout to make sure that docker image is setup
+ vendor/bin/phpunit --group cluster
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fd468fff..bfb2d3c9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,12 +1,8 @@
## Changelog
-## Unreleased
+## v2.2.0 (2023-06-14)
-### Changed
-- Added `PrefixableCommandInterface` (#1262)
-
-### Removed
-- Removed Webdis and Phpiredis support (#1291)
+Predis v2.2.0 introduces official support for [Redis Stack](https://redis.io/docs/stack/) as well as a [Relay](https://github.com/cachewerk/relay) integration for substantially [faster read performance](https://github.com/predis/predis/wiki/Using-Relay).
## v2.2.0-RC1 (2023-05-09)
@@ -16,11 +12,15 @@
- Added support for Redis `JSON`, `Bloom`, `Search` and `TimeSeries` module (#1253)
- Added support for `ACL SETUSER, GETUSER, DRYRUN` commands (#1193)
+### Changed
+- Minor code style and type-hint changes (#1311)
+
### Fixed
- Fixed prefixes for `XTRIM` and `XREVRANGE` commands (#1230)
-- Fix `fclose()` being called on invalid stream resource (#1199)
-- Fix `BitByte` and `ExpireOptions` traits skip processing on null values (#1169)
-- Fix missing `@return` annotations (#1265)
+- Fixed `fclose()` being called on invalid stream resource (#1199)
+- Fixed `BitByte` and `ExpireOptions` traits skip processing on null values (#1169)
+- Fixed missing `@return` annotations (#1265)
+- Fixed `GETDEL` prefixing (#1306)
## v2.1.2 (2023-03-02)
diff --git a/docker/unstable_cluster/Dockerfile b/docker/unstable_cluster/Dockerfile
new file mode 100644
index 00000000..a434dddc
--- /dev/null
+++ b/docker/unstable_cluster/Dockerfile
@@ -0,0 +1,7 @@
+FROM redis/redis-stack-server:latest as rss
+
+COPY create_cluster.sh /create_cluster.sh
+RUN ls -R /opt/redis-stack
+RUN chmod a+x /create_cluster.sh
+
+ENTRYPOINT [ "/create_cluster.sh"]
diff --git a/docker/unstable_cluster/create_cluster.sh b/docker/unstable_cluster/create_cluster.sh
new file mode 100644
index 00000000..b000a2ad
--- /dev/null
+++ b/docker/unstable_cluster/create_cluster.sh
@@ -0,0 +1,47 @@
+#! /bin/bash
+
+mkdir -p /nodes
+touch /nodes/nodemap
+if [ -z ${START_PORT} ]; then
+ START_PORT=6372
+fi
+if [ -z ${END_PORT} ]; then
+ END_PORT=6377
+fi
+if [ ! -z "$3" ]; then
+ START_PORT=$2
+ START_PORT=$3
+fi
+echo "STARTING: ${START_PORT}"
+echo "ENDING: ${END_PORT}"
+
+for PORT in `seq ${START_PORT} ${END_PORT}`; do
+ mkdir -p /nodes/$PORT
+ if [[ -e /redis.conf ]]; then
+ cp /redis.conf /nodes/$PORT/redis.conf
+ else
+ touch /nodes/$PORT/redis.conf
+ fi
+ cat << EOF >> /nodes/$PORT/redis.conf
+port ${PORT}
+cluster-enabled yes
+daemonize yes
+logfile /redis.log
+dir /nodes/$PORT
+EOF
+
+ set -x
+ /opt/redis-stack/bin/redis-server /nodes/$PORT/redis.conf
+ sleep 1
+ if [ $? -ne 0 ]; then
+ echo "Redis failed to start, exiting."
+ continue
+ fi
+ echo 127.0.0.1:$PORT >> /nodes/nodemap
+done
+if [ -z "${REDIS_PASSWORD}" ]; then
+ echo yes | /opt/redis-stack/bin/redis-cli --cluster create `seq -f 127.0.0.1:%g ${START_PORT} ${END_PORT}` --cluster-replicas 1
+else
+ echo yes | opt/redis-stack/bin/redis-cli -a ${REDIS_PASSWORD} --cluster create `seq -f 127.0.0.1:%g ${START_PORT} ${END_PORT}` --cluster-replicas 1
+fi
+tail -f /redis.log
diff --git a/docker/unstable_cluster/docker-compose.yml b/docker/unstable_cluster/docker-compose.yml
new file mode 100644
index 00000000..2829da39
--- /dev/null
+++ b/docker/unstable_cluster/docker-compose.yml
@@ -0,0 +1,17 @@
+version: "3.9"
+services:
+ cluster:
+ container_name: redis-cluster
+ build:
+ context: .
+ dockerfile: Dockerfile
+ ports:
+ - "6372:6372"
+ - "6373:6373"
+ - "6374:6374"
+ - "6375:6375"
+ - "6376:6376"
+ - "6377:6378"
+ volumes:
+ - "./redis.conf:/redis.conf:ro"
+
diff --git a/docker/unstable_cluster/redis.conf b/docker/unstable_cluster/redis.conf
new file mode 100644
index 00000000..71928253
--- /dev/null
+++ b/docker/unstable_cluster/redis.conf
@@ -0,0 +1,9 @@
+# Redis Cluster config file will be shared across all nodes.
+# Do not change the following configurations that are already set:
+# port, cluster-enabled, daemonize, logfile, dir
+protected-mode no
+loadmodule /opt/redis-stack/lib/redisearch.so
+loadmodule /opt/redis-stack/lib/redisgraph.so
+loadmodule /opt/redis-stack/lib/redistimeseries.so
+loadmodule /opt/redis-stack/lib/rejson.so
+loadmodule /opt/redis-stack/lib/redisbloom.so
diff --git a/phpunit.relay.xml b/phpunit.relay.xml
index bfe4f52a..81d4cc49 100644
--- a/phpunit.relay.xml
+++ b/phpunit.relay.xml
@@ -22,6 +22,7 @@
relay-incompatible
realm-stack
ext-curl
+ cluster
@@ -36,5 +37,9 @@
+
+
+
+
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 662e1fd4..a9332630 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -31,6 +31,7 @@
realm-stack
ext-relay
ext-curl
+ cluster
@@ -49,5 +50,9 @@
+
+
+
+
diff --git a/src/ClientContextInterface.php b/src/ClientContextInterface.php
index f2ed60b0..4ce72a5c 100644
--- a/src/ClientContextInterface.php
+++ b/src/ClientContextInterface.php
@@ -38,11 +38,12 @@ use Predis\Command\Argument\TimeSeries\MGetArguments;
use Predis\Command\Argument\TimeSeries\MRangeArguments;
use Predis\Command\Argument\TimeSeries\RangeArguments;
use Predis\Command\CommandInterface;
-use Predis\Command\Redis\Container\ACL;
-use Predis\Command\Redis\Container\FunctionContainer;
-use Predis\Command\Redis\Container\Json\JSONDEBUG;
-use Predis\Command\Redis\Container\Search\FTCONFIG;
-use Predis\Command\Redis\Container\Search\FTCURSOR;
+use Predis\Command\Container\ACL;
+use Predis\Command\Container\CLIENT;
+use Predis\Command\Container\FUNCTIONS;
+use Predis\Command\Container\Json\JSONDEBUG;
+use Predis\Command\Container\Search\FTCONFIG;
+use Predis\Command\Container\Search\FTCURSOR;
/**
* Interface defining a client-side context such as a pipeline or transaction.
@@ -58,8 +59,8 @@ use Predis\Command\Redis\Container\Search\FTCURSOR;
* @method $this move($key, $db)
* @method $this object($subcommand, $key)
* @method $this persist($key)
- * @method $this pexpire($key, $milliseconds)
- * @method $this pexpireat($key, $timestamp)
+ * @method $this pexpire($key, $milliseconds, string $option = null)
+ * @method $this pexpireat($key, $timestamp, string $option = null)
* @method $this pttl($key)
* @method $this randomkey()
* @method $this rename($key, $target)
@@ -320,21 +321,18 @@ use Predis\Command\Redis\Container\Search\FTCURSOR;
* @method $this select($database)
* @method $this bgrewriteaof()
* @method $this bgsave()
- * @method $this client($subcommand, ...$argument = null)
* @method $this config($subcommand, $argument = null)
* @method $this dbsize()
* @method $this flushall()
* @method $this flushdb()
- * @method $this info($section = null)
+ * @method $this info(string ...$section = null)
* @method $this lastsave()
* @method $this save()
* @method $this slaveof($host, $port)
* @method $this slowlog($subcommand, $argument = null)
* @method $this spublish(string $shardChannel, string $message)
- * @method $this ssubscribe(string ...$shardChannels)
- * @method $this sunsubscribe(string ...$shardChannels)
* @method $this time()
- * @method $this command()
+ * @method $this command($subcommand, $argument = null)
* @method $this geoadd($key, $longitude, $latitude, $member)
* @method $this geohash($key, array $members)
* @method $this geopos($key, array $members)
@@ -345,11 +343,12 @@ use Predis\Command\Redis\Container\Search\FTCURSOR;
* @method $this geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false)
*
* Container commands
- * @property FunctionContainer $function
- * @property FTCONFIG $ftconfig
- * @property FTCURSOR $ftcursor
- * @property JSONDEBUG $jsondebug
- * @property ACL $acl
+ * @property CLIENT $client
+ * @property FUNCTIONS $function
+ * @property FTCONFIG $ftconfig
+ * @property FTCURSOR $ftcursor
+ * @property JSONDEBUG $jsondebug
+ * @property ACL $acl
*/
interface ClientContextInterface
{
diff --git a/src/ClientInterface.php b/src/ClientInterface.php
index 785c7e53..6e8762a7 100644
--- a/src/ClientInterface.php
+++ b/src/ClientInterface.php
@@ -38,12 +38,13 @@ use Predis\Command\Argument\TimeSeries\MGetArguments;
use Predis\Command\Argument\TimeSeries\MRangeArguments;
use Predis\Command\Argument\TimeSeries\RangeArguments;
use Predis\Command\CommandInterface;
+use Predis\Command\Container\ACL;
+use Predis\Command\Container\CLIENT;
+use Predis\Command\Container\FUNCTIONS;
+use Predis\Command\Container\Json\JSONDEBUG;
+use Predis\Command\Container\Search\FTCONFIG;
+use Predis\Command\Container\Search\FTCURSOR;
use Predis\Command\FactoryInterface;
-use Predis\Command\Redis\Container\ACL;
-use Predis\Command\Redis\Container\FunctionContainer;
-use Predis\Command\Redis\Container\Json\JSONDEBUG;
-use Predis\Command\Redis\Container\Search\FTCONFIG;
-use Predis\Command\Redis\Container\Search\FTCURSOR;
use Predis\Configuration\OptionsInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Response\Status;
@@ -67,8 +68,8 @@ use Predis\Response\Status;
* @method int move(string $key, int $db)
* @method mixed object($subcommand, string $key)
* @method int persist(string $key)
- * @method int pexpire(string $key, int $milliseconds)
- * @method int pexpireat(string $key, int $timestamp)
+ * @method int pexpire(string $key, int $milliseconds, string $option = null)
+ * @method int pexpireat(string $key, int $timestamp, string $option = null)
* @method int pttl(string $key)
* @method string|null randomkey()
* @method mixed rename(string $key, string $target)
@@ -338,21 +339,18 @@ use Predis\Response\Status;
* @method mixed select(int $database)
* @method mixed bgrewriteaof()
* @method mixed bgsave()
- * @method mixed client($subcommand, ...$argument = null)
* @method mixed config($subcommand, $argument = null)
* @method int dbsize()
* @method mixed flushall()
* @method mixed flushdb()
- * @method array info($section = null)
+ * @method array info(string ...$section = null)
* @method int lastsave()
* @method mixed save()
* @method mixed slaveof(string $host, int $port)
* @method mixed slowlog($subcommand, $argument = null)
* @method int spublish(string $shardChannel, string $message)
- * @method array ssubscribe(string ...$shardChannels)
- * @method array sunsubscribe(string ...$shardChannels)
* @method array time()
- * @method array command()
+ * @method array command($subcommand, $argument = null)
* @method int geoadd(string $key, $longitude, $latitude, $member)
* @method array geohash(string $key, array $members)
* @method array geopos(string $key, array $members)
@@ -363,11 +361,12 @@ use Predis\Response\Status;
* @method int geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false)
*
* Container commands
- * @property FunctionContainer $function
- * @property FTCONFIG $ftconfig
- * @property FTCURSOR $ftcursor
- * @property JSONDEBUG $jsondebug
- * @property ACL $acl
+ * @property CLIENT $client
+ * @property FUNCTIONS $function
+ * @property FTCONFIG $ftconfig
+ * @property FTCURSOR $ftcursor
+ * @property JSONDEBUG $jsondebug
+ * @property ACL $acl
*/
interface ClientInterface
{
diff --git a/src/Cluster/ClusterStrategy.php b/src/Cluster/ClusterStrategy.php
index 7ff365f3..2791ea95 100644
--- a/src/Cluster/ClusterStrategy.php
+++ b/src/Cluster/ClusterStrategy.php
@@ -53,6 +53,7 @@ abstract class ClusterStrategy implements StrategyInterface
'SORT' => [$this, 'getKeyFromSortCommand'],
'DUMP' => $getKeyFromFirstArgument,
'RESTORE' => $getKeyFromFirstArgument,
+ 'FLUSHDB' => [$this, 'getFakeKey'],
/* commands operating on string values */
'APPEND' => $getKeyFromFirstArgument,
@@ -163,6 +164,9 @@ abstract class ClusterStrategy implements StrategyInterface
'EVAL' => [$this, 'getKeyFromScriptingCommands'],
'EVALSHA' => [$this, 'getKeyFromScriptingCommands'],
+ /* server */
+ 'INFO' => [$this, 'getFakeKey'],
+
/* commands performing geospatial operations */
'GEOADD' => $getKeyFromFirstArgument,
'GEOHASH' => $getKeyFromFirstArgument,
@@ -221,6 +225,16 @@ abstract class ClusterStrategy implements StrategyInterface
$this->commands[$commandID] = $callback;
}
+ /**
+ * Get fake key for commands with no key argument.
+ *
+ * @return string
+ */
+ protected function getFakeKey(): string
+ {
+ return 'key';
+ }
+
/**
* Extracts the key from the first argument of a command instance.
*
diff --git a/src/Command/Container/AbstractContainer.php b/src/Command/Container/AbstractContainer.php
index 950346ba..703a26ed 100644
--- a/src/Command/Container/AbstractContainer.php
+++ b/src/Command/Container/AbstractContainer.php
@@ -29,7 +29,7 @@ abstract class AbstractContainer implements ContainerInterface
/**
* {@inheritDoc}
*/
- public function __call($subcommandID, $arguments)
+ public function __call(string $subcommandID, array $arguments)
{
array_unshift($arguments, strtoupper($subcommandID));
diff --git a/src/Command/Container/CLIENT.php b/src/Command/Container/CLIENT.php
new file mode 100644
index 00000000..46acbfd1
--- /dev/null
+++ b/src/Command/Container/CLIENT.php
@@ -0,0 +1,31 @@
+setListArguments($arguments);
+ break;
+ case 'NOTOUCH':
+ $arguments[0] = 'NO-TOUCH';
+ $this->setNoTouchArguments($arguments);
+ break;
+ case 'SETINFO':
+ $this->setSetInfoArguments($arguments);
+ break;
+ default:
+ parent::setArguments($arguments);
+ }
+ }
+
+ private function setListArguments(array $arguments): void
+ {
+ $processedArguments = [$arguments[0]];
+
+ if (array_key_exists(1, $arguments) && null !== $arguments[1]) {
+ array_push($processedArguments, 'TYPE', strtoupper($arguments[1]));
+ }
+
+ if (array_key_exists(2, $arguments)) {
+ array_push($processedArguments, 'ID', $arguments[2]);
+ }
+
+ if (count($arguments) > 3) {
+ for ($i = 3, $iMax = count($arguments); $i < $iMax; $i++) {
+ $processedArguments[] = $arguments[$i];
+ }
+ }
+
+ parent::setArguments($processedArguments);
+ }
+
+ private function setNoTouchArguments(array $arguments): void
+ {
+ $processedArguments = [$arguments[0]];
+
+ if (array_key_exists(1, $arguments) && null !== $arguments[1]) {
+ $modifier = ($arguments[1]) ? 'ON' : 'OFF';
+ $processedArguments[] = $modifier;
+ }
+
+ parent::setArguments($processedArguments);
+ }
+
+ private function setSetInfoArguments(array $arguments): void
+ {
+ $processedArguments = [$arguments[0]];
+
+ if (
+ array_key_exists(1, $arguments)
+ && null !== $arguments[1]
+ && array_key_exists(2, $arguments)
+ && null !== $arguments[2]
+ ) {
+ array_push($processedArguments, strtoupper($arguments[1]), $arguments[2]);
+ }
+
+ parent::setArguments($processedArguments);
+ }
+
/**
* {@inheritdoc}
*/
diff --git a/src/Command/Redis/FUNCTIONS.php b/src/Command/Redis/FUNCTIONS.php
index 69465901..7f4fde79 100644
--- a/src/Command/Redis/FUNCTIONS.php
+++ b/src/Command/Redis/FUNCTIONS.php
@@ -41,7 +41,7 @@ class FUNCTIONS extends RedisCommand
public function setArguments(array $arguments)
{
- $strategy = $this->strategyResolver->resolve('functions', $arguments[0]);
+ $strategy = $this->strategyResolver->resolve('functions', strtolower($arguments[0]));
$arguments = $strategy->processArguments($arguments);
parent::setArguments($arguments);
diff --git a/src/Command/Strategy/ContainerCommands/Functions/DumpStrategy.php b/src/Command/Strategy/ContainerCommands/Functions/DumpStrategy.php
new file mode 100644
index 00000000..13f49ab0
--- /dev/null
+++ b/src/Command/Strategy/ContainerCommands/Functions/DumpStrategy.php
@@ -0,0 +1,26 @@
+separator = $separator;
+ }
+
/**
* {@inheritDoc}
*/
public function resolve(string $commandId, string $subcommandId): SubcommandStrategyInterface
{
- $subcommandStrategyClass = ucfirst(strtolower($subcommandId)) . 'Strategy';
- $commandDirectoryName = ucfirst(strtolower($commandId));
+ $subcommandStrategyClass = ucwords($subcommandId) . 'Strategy';
+ $commandDirectoryName = ucwords($commandId);
+
+ if (!is_null($this->separator)) {
+ $subcommandStrategyClass = str_replace($this->separator, '', $subcommandStrategyClass);
+ $commandDirectoryName = str_replace($this->separator, '', $commandDirectoryName);
+ }
if (class_exists(
$containerCommandClass = self::CONTAINER_COMMANDS_NAMESPACE . '\\' . $commandDirectoryName . '\\' . $subcommandStrategyClass
diff --git a/tests/PHPUnit/PredisCommandTestCase.php b/tests/PHPUnit/PredisCommandTestCase.php
index ddf41632..7080173f 100644
--- a/tests/PHPUnit/PredisCommandTestCase.php
+++ b/tests/PHPUnit/PredisCommandTestCase.php
@@ -61,7 +61,11 @@ abstract class PredisCommandTestCase extends PredisTestCase
);
}
- $client = $this->createClient(null, null, $flushdb);
+ if ($this->isClusterTest()) {
+ $client = $this->createClient(null, ['cluster' => 'redis'], $flushdb);
+ } else {
+ $client = $this->createClient(null, null, $flushdb);
+ }
return $client;
}
diff --git a/tests/PHPUnit/PredisTestCase.php b/tests/PHPUnit/PredisTestCase.php
index 1287a694..4ad148c1 100644
--- a/tests/PHPUnit/PredisTestCase.php
+++ b/tests/PHPUnit/PredisTestCase.php
@@ -168,6 +168,10 @@ abstract class PredisTestCase extends \PHPUnit\Framework\TestCase
*/
protected function getDefaultParametersArray(): array
{
+ if ($this->isClusterTest()) {
+ return $this->prepareClusterEndpoints();
+ }
+
return [
'scheme' => 'tcp',
'host' => constant('REDIS_SERVER_HOST'),
@@ -252,6 +256,15 @@ abstract class PredisTestCase extends \PHPUnit\Framework\TestCase
getenv('USE_RELAY') ? ['connections' => 'relay'] : []
);
+ if ($this->isClusterTest()) {
+ $options = array_merge(
+ [
+ 'cluster' => 'redis',
+ ],
+ $options
+ );
+ }
+
$client = new Client($parameters, $options);
$client->connect();
@@ -552,4 +565,36 @@ abstract class PredisTestCase extends \PHPUnit\Framework\TestCase
$this->markTestSkipped($message);
}
}
+
+ /**
+ * Check annotations if it's matches to cluster test scenario.
+ *
+ * @return bool
+ */
+ protected function isClusterTest(): bool
+ {
+ $annotations = TestUtil::parseTestMethodAnnotations(
+ get_class($this),
+ $this->getName(false)
+ );
+
+ return isset($annotations['method']['requiresRedisVersion'], $annotations['method']['group'])
+ && !empty($annotations['method']['requiresRedisVersion'])
+ && in_array('connected', $annotations['method']['group'], true)
+ && in_array('cluster', $annotations['method']['group'], true);
+ }
+
+ /**
+ * Parse comma-separated cluster endpoints and convert them into tcp strings.
+ *
+ * @return array
+ */
+ protected function prepareClusterEndpoints(): array
+ {
+ $endpoints = explode(',', constant('REDIS_CLUSTER_ENDPOINTS'));
+
+ return array_map(static function (string $elem) {
+ return 'tcp://' . $elem;
+ }, $endpoints);
+ }
}
diff --git a/tests/Predis/Cluster/PredisStrategyTest.php b/tests/Predis/Cluster/PredisStrategyTest.php
index ed63ab51..82a74b7c 100644
--- a/tests/Predis/Cluster/PredisStrategyTest.php
+++ b/tests/Predis/Cluster/PredisStrategyTest.php
@@ -106,6 +106,21 @@ class PredisStrategyTest extends PredisTestCase
}
}
+ /**
+ * @group disconnected
+ */
+ public function testFakeKeyCommandsWithOneKey(): void
+ {
+ $strategy = $this->getClusterStrategy();
+ $commands = $this->getCommandFactory();
+ $arguments = [];
+
+ foreach ($this->getExpectedCommands('keys-fake') as $commandID) {
+ $command = $commands->create($commandID, $arguments);
+ $this->assertNotNull($strategy->getSlot($command), $commandID);
+ }
+ }
+
/**
* @group disconnected
*/
@@ -342,6 +357,7 @@ class PredisStrategyTest extends PredisTestCase
'SORT' => 'variable',
'DUMP' => 'keys-first',
'RESTORE' => 'keys-first',
+ 'FLUSHDB' => 'keys-fake',
/* commands operating on string values */
'APPEND' => 'keys-first',
@@ -452,6 +468,9 @@ class PredisStrategyTest extends PredisTestCase
'EVAL' => 'keys-script',
'EVALSHA' => 'keys-script',
+ /* server */
+ 'INFO' => 'keys-fake',
+
/* commands performing geospatial operations */
'GEOADD' => 'keys-first',
'GEOHASH' => 'keys-first',
diff --git a/tests/Predis/Cluster/RedisStrategyTest.php b/tests/Predis/Cluster/RedisStrategyTest.php
index b67b8cc4..b69eab5f 100644
--- a/tests/Predis/Cluster/RedisStrategyTest.php
+++ b/tests/Predis/Cluster/RedisStrategyTest.php
@@ -134,6 +134,21 @@ class RedisStrategyTest extends PredisTestCase
}
}
+ /**
+ * @group disconnected
+ */
+ public function testFakeKeyCommandsWithOneKey(): void
+ {
+ $strategy = $this->getClusterStrategy();
+ $commands = $this->getCommandFactory();
+ $arguments = [];
+
+ foreach ($this->getExpectedCommands('keys-fake') as $commandID) {
+ $command = $commands->create($commandID, $arguments);
+ $this->assertNotNull($strategy->getSlot($command), $commandID);
+ }
+ }
+
/**
* @group disconnected
*/
@@ -365,6 +380,7 @@ class RedisStrategyTest extends PredisTestCase
'SORT' => 'keys-first', // TODO
'DUMP' => 'keys-first',
'RESTORE' => 'keys-first',
+ 'FLUSHDB' => 'keys-fake',
/* commands operating on string values */
'APPEND' => 'keys-first',
@@ -475,6 +491,9 @@ class RedisStrategyTest extends PredisTestCase
'EVAL' => 'keys-script',
'EVALSHA' => 'keys-script',
+ /* server */
+ 'INFO' => 'keys-fake',
+
/* commands performing geospatial operations */
'GEOADD' => 'keys-first',
'GEOHASH' => 'keys-first',
diff --git a/tests/Predis/Command/Redis/CLIENT_Test.php b/tests/Predis/Command/Redis/CLIENT_Test.php
index 0fc13d6a..ec84ff8b 100644
--- a/tests/Predis/Command/Redis/CLIENT_Test.php
+++ b/tests/Predis/Command/Redis/CLIENT_Test.php
@@ -39,8 +39,8 @@ class CLIENT_Test extends PredisCommandTestCase
*/
public function testFilterArgumentsOfClientKill(): void
{
- $arguments = ['kill', '127.0.0.1:45393'];
- $expected = ['kill', '127.0.0.1:45393'];
+ $arguments = ['KILL', '127.0.0.1:45393'];
+ $expected = ['KILL', '127.0.0.1:45393'];
$command = $this->getCommand();
$command->setArguments($arguments);
@@ -49,17 +49,15 @@ class CLIENT_Test extends PredisCommandTestCase
}
/**
+ * @dataProvider listArgumentsProvider
* @group disconnected
*/
- public function testFilterArgumentsOfClientList(): void
+ public function testFilterArgumentsOfClientList(array $actualArguments, array $expectedArguments): void
{
- $arguments = ['list'];
- $expected = ['list'];
-
$command = $this->getCommand();
- $command->setArguments($arguments);
+ $command->setArguments($actualArguments);
- $this->assertSame($expected, $command->getArguments());
+ $this->assertSame($expectedArguments, $command->getArguments());
}
/**
@@ -67,7 +65,7 @@ class CLIENT_Test extends PredisCommandTestCase
*/
public function testFilterArgumentsOfClientGetname(): void
{
- $arguments = $expected = ['getname'];
+ $arguments = $expected = ['GETNAME'];
$command = $this->getCommand();
$command->setArguments($arguments);
@@ -80,7 +78,7 @@ class CLIENT_Test extends PredisCommandTestCase
*/
public function testFilterArgumentsOfClientSetname(): void
{
- $arguments = $expected = ['setname', 'connection-a'];
+ $arguments = $expected = ['SETNAME', 'connection-a'];
$command = $this->getCommand();
$command->setArguments($arguments);
@@ -88,6 +86,30 @@ class CLIENT_Test extends PredisCommandTestCase
$this->assertSame($expected, $command->getArguments());
}
+ /**
+ * @dataProvider noTouchArgumentsProvider
+ * @group disconnected
+ */
+ public function testFilterArgumentsNoTouch(array $actualArguments, array $expectedArguments): void
+ {
+ $command = $this->getCommand();
+ $command->setArguments($actualArguments);
+
+ $this->assertSame($expectedArguments, $command->getArguments());
+ }
+
+ /**
+ * @dataProvider setInfoArgumentsProvider
+ * @group disconnected
+ */
+ public function testFilterArgumentsSetInfo(array $actualArguments, array $expectedArguments): void
+ {
+ $command = $this->getCommand();
+ $command->setArguments($actualArguments);
+
+ $this->assertSame($expectedArguments, $command->getArguments());
+ }
+
/**
* @group disconnected
*/
@@ -131,7 +153,7 @@ BUFFER;
{
$redis = $this->getClient();
- $this->assertIsArray($clients = $redis->client('LIST'));
+ $this->assertIsArray($clients = $redis->client->list());
$this->assertGreaterThanOrEqual(1, count($clients));
$this->assertIsArray($clients[0]);
$this->assertArrayHasKey('addr', $clients[0]);
@@ -171,12 +193,12 @@ BUFFER;
public function testGetsNameOfConnection(): void
{
$redis = $this->getClient();
- $clientName = $redis->client('GETNAME');
+ $clientName = $redis->client->getName();
$this->assertNull($clientName);
$expectedConnectionName = 'foo-bar';
- $this->assertEquals('OK', $redis->client('SETNAME', $expectedConnectionName));
- $this->assertEquals($expectedConnectionName, $redis->client('GETNAME'));
+ $this->assertEquals('OK', $redis->client->setName($expectedConnectionName));
+ $this->assertEquals($expectedConnectionName, $redis->client->getName());
}
/**
@@ -203,8 +225,33 @@ BUFFER;
$redis = $this->getClient();
$expectedConnectionName = 'foo-baz';
- $this->assertEquals('OK', $redis->client('SETNAME', $expectedConnectionName));
- $this->assertEquals($expectedConnectionName, $redis->client('GETNAME'));
+ $this->assertEquals('OK', $redis->client->setName($expectedConnectionName));
+ $this->assertEquals($expectedConnectionName, $redis->client->getName());
+ }
+
+ /**
+ * @group connected
+ * @requiresRedisVersion >= 7.2.0
+ */
+ public function testNoTouchTurnOnControlOnKeys(): void
+ {
+ $redis = $this->getClient();
+
+ $this->assertEquals('OK', $redis->client->noTouch(true));
+ }
+
+ /**
+ * @group connected
+ * @requiresRedisVersion >= 7.2.0
+ */
+ public function testSetInfoToCurrentClientConnection(): void
+ {
+ $redis = $this->getClient();
+
+ $this->assertEquals('OK', $redis->client->setInfo('LIB-NAME', 'lib'));
+ $this->assertEquals('OK', $redis->client->setInfo('LIB-VER', '1.0.0'));
+ $this->assertSame('lib', $redis->client->list()[0]['lib-name']);
+ $this->assertSame('1.0.0', $redis->client->list()[0]['lib-ver']);
}
/**
@@ -231,20 +278,7 @@ BUFFER;
$this->expectException('Predis\Response\ServerException');
$redis = $this->getClient();
- $redis->client('SETNAME', $invalidConnectionName);
- }
-
- /**
- * @group connected
- * @requiresRedisVersion >= 2.4.0
- */
- public function testThrowsExceptioOnWrongModifier(): void
- {
- $this->expectException('Predis\Response\ServerException');
-
- $redis = $this->getClient();
-
- $redis->client('FOO');
+ $redis->client->setName($invalidConnectionName);
}
/**
@@ -258,6 +292,64 @@ BUFFER;
$redis = $this->getClient();
- $redis->client('KILL', '127.0.0.1:65535');
+ $redis->client->kill('127.0.0.1:65535');
+ }
+
+ public function listArgumentsProvider(): array
+ {
+ return [
+ 'with default arguments' => [
+ ['LIST'],
+ ['LIST'],
+ ],
+ 'with TYPE modifier' => [
+ ['LIST', 'MASTER'],
+ ['LIST', 'TYPE', 'MASTER'],
+ ],
+ 'with ID modifier' => [
+ ['LIST', null, 1, 2, 3],
+ ['LIST', 'ID', 1, 2, 3],
+ ],
+ ];
+ }
+
+ public function noTouchArgumentsProvider(): array
+ {
+ return [
+ 'with default arguments' => [
+ ['NOTOUCH'],
+ ['NO-TOUCH'],
+ ],
+ 'with enabled modifier' => [
+ ['NOTOUCH', true],
+ ['NO-TOUCH', 'ON'],
+ ],
+ 'with disabled modifier' => [
+ ['NOTOUCH', false],
+ ['NO-TOUCH', 'OFF'],
+ ],
+ ];
+ }
+
+ public function setInfoArgumentsProvider(): array
+ {
+ return [
+ 'with default arguments' => [
+ ['SETINFO'],
+ ['SETINFO'],
+ ],
+ 'with LIB-NAME modifier' => [
+ ['SETINFO', 'LIB-NAME', 'lib'],
+ ['SETINFO', 'LIB-NAME', 'lib'],
+ ],
+ 'with LIB-VER modifier' => [
+ ['SETINFO', 'LIB-VER', '1.0.0'],
+ ['SETINFO', 'LIB-VER', '1.0.0'],
+ ],
+ 'with only modifier given' => [
+ ['SETINFO', 'LIB-VER'],
+ ['SETINFO'],
+ ],
+ ];
}
}
diff --git a/tests/Predis/Command/Redis/FUNCTIONS_Test.php b/tests/Predis/Command/Redis/FUNCTIONS_Test.php
index ad79f0ef..b13fd80e 100644
--- a/tests/Predis/Command/Redis/FUNCTIONS_Test.php
+++ b/tests/Predis/Command/Redis/FUNCTIONS_Test.php
@@ -70,6 +70,84 @@ class FUNCTIONS_Test extends PredisCommandTestCase
$this->assertSameValues($expected, $command->getArguments());
}
+ /**
+ * @group disconnected
+ */
+ public function testDumpFilterArguments(): void
+ {
+ $arguments = ['DUMP'];
+ $expected = ['DUMP'];
+
+ $command = $this->getCommand();
+ $command->setArguments($arguments);
+
+ $this->assertSameValues($expected, $command->getArguments());
+ }
+
+ /**
+ * @group disconnected
+ */
+ public function testKillFilterArguments(): void
+ {
+ $arguments = ['KILL'];
+ $expected = ['KILL'];
+
+ $command = $this->getCommand();
+ $command->setArguments($arguments);
+
+ $this->assertSameValues($expected, $command->getArguments());
+ }
+
+ /**
+ * @group disconnected
+ */
+ public function testStatsFilterArguments(): void
+ {
+ $arguments = ['STATS'];
+ $expected = ['STATS'];
+
+ $command = $this->getCommand();
+ $command->setArguments($arguments);
+
+ $this->assertSameValues($expected, $command->getArguments());
+ }
+
+ /**
+ * @dataProvider flushArgumentsProvider
+ * @group disconnected
+ */
+ public function testFlushFilterArguments(array $actualArguments, array $expectedResponse): void
+ {
+ $command = $this->getCommand();
+ $command->setArguments($actualArguments);
+
+ $this->assertSameValues($expectedResponse, $command->getArguments());
+ }
+
+ /**
+ * @dataProvider restoreArgumentsProvider
+ * @group disconnected
+ */
+ public function testRestoreFilterArguments(array $actualArguments, array $expectedResponse): void
+ {
+ $command = $this->getCommand();
+ $command->setArguments($actualArguments);
+
+ $this->assertSameValues($expectedResponse, $command->getArguments());
+ }
+
+ /**
+ * @dataProvider listArgumentsProvider
+ * @group disconnected
+ */
+ public function testListFilterArguments(array $actualArguments, array $expectedResponse): void
+ {
+ $command = $this->getCommand();
+ $command->setArguments($actualArguments);
+
+ $this->assertSameValues($expectedResponse, $command->getArguments());
+ }
+
/**
* @group disconnected
*/
@@ -86,7 +164,7 @@ class FUNCTIONS_Test extends PredisCommandTestCase
public function testLoadFunctionAddFunctionIntoGivenLibrary(): void
{
$redis = $this->getClient();
- $redis->executeRaw(['FUNCTION', 'FLUSH']);
+ $redis->function->flush();
$actualResponse = $redis->function->load(
"#!lua name={$this->libName} \n redis.register_function('myfunc', function(keys, args) return args[1] end)"
@@ -123,7 +201,7 @@ class FUNCTIONS_Test extends PredisCommandTestCase
public function testLoadFunctionOverridesExistingFunctionWithReplaceArgumentGiven(): void
{
$redis = $this->getClient();
- $redis->executeRaw(['FUNCTION', 'FLUSH']);
+ $redis->function->flush();
$actualResponse = $redis->function->load(
"#!lua name={$this->libName} \n redis.register_function('myfunc', function(keys, args) return args[1] end)"
@@ -150,7 +228,7 @@ class FUNCTIONS_Test extends PredisCommandTestCase
public function testLoadFunctionThrowsErrorOnAlreadyExistingLibraryGiven(): void
{
$redis = $this->getClient();
- $redis->executeRaw(['FUNCTION', 'FLUSH']);
+ $redis->function->flush();
$actualResponse = $redis->function->load(
"#!lua name={$this->libName} \n redis.register_function('myfunc', function(keys, args) return args[1] end)"
@@ -178,7 +256,7 @@ class FUNCTIONS_Test extends PredisCommandTestCase
public function testDeleteFunctionRemovesAlreadyExistingLibrary(): void
{
$redis = $this->getClient();
- $redis->executeRaw(['FUNCTION', 'FLUSH']);
+ $redis->function->flush();
$actualResponse = $redis->function->load(
"#!lua name={$this->libName} \n redis.register_function('myfunc', function(keys, args) return args[1] end)"
@@ -188,6 +266,112 @@ class FUNCTIONS_Test extends PredisCommandTestCase
$this->assertEquals('OK', $redis->function->delete($this->libName));
}
+ /**
+ * @group connected
+ * @return void
+ * @requiresRedisVersion >= 7.0.0
+ */
+ public function testDumpReturnsSerializedPayloadOfLibrary(): void
+ {
+ $redis = $this->getClient();
+ $redis->function->flush();
+
+ $libName = $redis->function->load(
+ "#!lua name={$this->libName} \n redis.register_function('myfunc', function(keys, args) return args[1] end)"
+ );
+
+ $this->assertSame($this->libName, $libName);
+ $this->assertStringContainsString($libName, $redis->function->dump());
+ }
+
+ /**
+ * @group connected
+ * @return void
+ * @requiresRedisVersion >= 7.0.0
+ */
+ public function testFlushRemovesAllLibraries(): void
+ {
+ $redis = $this->getClient();
+ $redis->function->flush();
+
+ $libName = $redis->function->load(
+ "#!lua name={$this->libName} \n redis.register_function('myfunc', function(keys, args) return args[1] end)"
+ );
+
+ $this->assertEquals($this->libName, $libName);
+ $this->assertEquals('OK', $redis->function->flush());
+ }
+
+ /**
+ * @group connected
+ * @return void
+ * @requiresRedisVersion >= 7.0.0
+ */
+ public function testRestoresLibraryFromSerializedPayload(): void
+ {
+ $redis = $this->getClient();
+ $redis->function->flush();
+
+ $libName = $redis->function->load(
+ "#!lua name={$this->libName} \n redis.register_function('myfunc', function(keys, args) return args[1] end)"
+ );
+ $this->assertEquals($this->libName, $libName);
+
+ $serializedPayload = $redis->function->dump();
+ $this->assertStringContainsString($libName, $serializedPayload);
+
+ $redis->function->flush();
+
+ $this->assertEquals('OK', $redis->function->restore($serializedPayload));
+ }
+
+ /**
+ * @group connected
+ * @group relay-incompatible
+ * @return void
+ * @requiresRedisVersion >= 7.0.0
+ */
+ public function testListReturnsListOfAvailableFunctions(): void
+ {
+ $redis = $this->getClient();
+ $redis->function->flush();
+ $expectedResponse = [
+ [
+ 'library_name', 'mylib', 'engine', 'LUA', 'functions',
+ [
+ ['name', 'myfunc', 'description', null, 'flags', []],
+ ],
+ ],
+ ];
+
+ $libName = $redis->function->load(
+ "#!lua name={$this->libName} \n redis.register_function('myfunc', function(keys, args) return args[1] end)"
+ );
+
+ $this->assertEquals($this->libName, $libName);
+ $this->assertSame($expectedResponse, $redis->function->list());
+ }
+
+ /**
+ * @group connected
+ * @group relay-incompatible
+ * @return void
+ * @requiresRedisVersion >= 7.0.0
+ */
+ public function testStatsReturnsInformationAboutRunningScript(): void
+ {
+ $redis = $this->getClient();
+ $redis->function->flush();
+ $expectedResponse = ['running_script', null, 'engines', ['LUA', ['libraries_count', 1, 'functions_count', 1]]];
+
+ $libName = $redis->function->load(
+ "#!lua name={$this->libName} \n redis.register_function('myfunc', function(keys, args) return args[1] end)"
+ );
+
+ $this->assertEquals($this->libName, $libName);
+ $this->assertSame($expectedResponse, $redis->function->stats());
+ }
+
/**
* @group connected
* @return void
@@ -196,11 +380,77 @@ class FUNCTIONS_Test extends PredisCommandTestCase
public function testDeleteFunctionThrowsErrorOnNonExistingLibrary(): void
{
$redis = $this->getClient();
- $redis->executeRaw(['FUNCTION', 'FLUSH']);
+ $redis->function->flush();
$this->expectException(ServerException::class);
$this->expectExceptionMessage('ERR Library not found');
$redis->function->delete($this->libName);
}
+
+ /**
+ * @group connected
+ * @return void
+ * @requiresRedisVersion >= 7.0.0
+ */
+ public function testKillThrowsExceptionOnNonExistingRunningScript(): void
+ {
+ $redis = $this->getClient();
+ $redis->function->flush();
+
+ $this->expectException(ServerException::class);
+ $this->expectExceptionMessage('NOTBUSY No scripts in execution right now.');
+
+ $redis->function->kill();
+ }
+
+ public function flushArgumentsProvider(): array
+ {
+ return [
+ 'with default arguments' => [
+ ['FLUSH', null],
+ ['FLUSH'],
+ ],
+ 'with mode argument' => [
+ ['FLUSH', 'sync'],
+ ['FLUSH', 'SYNC'],
+ ],
+ ];
+ }
+
+ public function restoreArgumentsProvider(): array
+ {
+ return [
+ 'with default arguments' => [
+ ['RESTORE', 'value', null],
+ ['RESTORE', 'value'],
+ ],
+ 'with mode argument' => [
+ ['RESTORE', 'value', 'append'],
+ ['RESTORE', 'value', 'APPEND'],
+ ],
+ ];
+ }
+
+ public function listArgumentsProvider(): array
+ {
+ return [
+ 'with default arguments' => [
+ ['LIST', null, false],
+ ['LIST'],
+ ],
+ 'with LIBRARYNAME modifier' => [
+ ['LIST', 'libraryname', false],
+ ['LIST', 'LIBRARYNAME', 'libraryname'],
+ ],
+ 'with WITHCODE modifier' => [
+ ['LIST', null, true],
+ ['LIST', 'WITHCODE'],
+ ],
+ 'with all arguments' => [
+ ['LIST', 'libraryname', true],
+ ['LIST', 'LIBRARYNAME', 'libraryname', 'WITHCODE'],
+ ],
+ ];
+ }
}
diff --git a/tests/Predis/Command/Redis/SET_Test.php b/tests/Predis/Command/Redis/SET_Test.php
index f82da5a4..61d470d5 100644
--- a/tests/Predis/Command/Redis/SET_Test.php
+++ b/tests/Predis/Command/Redis/SET_Test.php
@@ -166,4 +166,17 @@ class SET_Test extends PredisCommandTestCase
$this->assertEquals('OK', $redis->set('foo', 'barbar', 'XX'));
$this->assertNull($redis->set('foofoo', 'barbar', 'XX'));
}
+
+ /**
+ * @group connected
+ * @group cluster
+ * @requiresRedisVersion >= 3.0.0
+ * @return void
+ */
+ public function testSetStringValueInClusterMode(): void
+ {
+ $redis = $this->getClient();
+
+ $this->assertEquals('OK', $redis->set('foo', 'bar'));
+ }
}
diff --git a/tests/Predis/Command/Strategy/ContainerCommands/Functions/DumpStrategyTest.php b/tests/Predis/Command/Strategy/ContainerCommands/Functions/DumpStrategyTest.php
new file mode 100644
index 00000000..20b8fb96
--- /dev/null
+++ b/tests/Predis/Command/Strategy/ContainerCommands/Functions/DumpStrategyTest.php
@@ -0,0 +1,37 @@
+strategy = new DumpStrategy();
+ }
+
+ /**
+ * @group disconnected
+ * @return void
+ */
+ public function testProcessArguments(): void
+ {
+ $this->assertSame(['arg1', 'arg2'], $this->strategy->processArguments(['arg1', 'arg2']));
+ }
+}
diff --git a/tests/Predis/Command/Strategy/ContainerCommands/Functions/FlushStrategyTest.php b/tests/Predis/Command/Strategy/ContainerCommands/Functions/FlushStrategyTest.php
new file mode 100644
index 00000000..d5d8ba46
--- /dev/null
+++ b/tests/Predis/Command/Strategy/ContainerCommands/Functions/FlushStrategyTest.php
@@ -0,0 +1,54 @@
+strategy = new FlushStrategy();
+ }
+
+ /**
+ * @dataProvider argumentsProvider
+ * @group disconnected
+ * @param array $actualArguments
+ * @param array $expectedResponse
+ * @return void
+ */
+ public function testProcessArguments(array $actualArguments, array $expectedResponse): void
+ {
+ $this->assertSame($expectedResponse, $this->strategy->processArguments($actualArguments));
+ }
+
+ public function argumentsProvider(): array
+ {
+ return [
+ 'with default arguments' => [
+ ['FLUSH', null],
+ ['FLUSH'],
+ ],
+ 'with mode argument' => [
+ ['FLUSH', 'sync'],
+ ['FLUSH', 'SYNC'],
+ ],
+ ];
+ }
+}
diff --git a/tests/Predis/Command/Strategy/ContainerCommands/Functions/KillStrategyTest.php b/tests/Predis/Command/Strategy/ContainerCommands/Functions/KillStrategyTest.php
new file mode 100644
index 00000000..96da8c5f
--- /dev/null
+++ b/tests/Predis/Command/Strategy/ContainerCommands/Functions/KillStrategyTest.php
@@ -0,0 +1,37 @@
+strategy = new KillStrategy();
+ }
+
+ /**
+ * @group disconnected
+ * @return void
+ */
+ public function testProcessArguments(): void
+ {
+ $this->assertSame(['arg1', 'arg2'], $this->strategy->processArguments(['arg1', 'arg2']));
+ }
+}
diff --git a/tests/Predis/Command/Strategy/ContainerCommands/Functions/ListStrategyTest.php b/tests/Predis/Command/Strategy/ContainerCommands/Functions/ListStrategyTest.php
new file mode 100644
index 00000000..8e30c129
--- /dev/null
+++ b/tests/Predis/Command/Strategy/ContainerCommands/Functions/ListStrategyTest.php
@@ -0,0 +1,62 @@
+strategy = new ListStrategy();
+ }
+
+ /**
+ * @dataProvider argumentsProvider
+ * @group disconnected
+ * @param array $actualArguments
+ * @param array $expectedResponse
+ * @return void
+ */
+ public function testProcessArguments(array $actualArguments, array $expectedResponse): void
+ {
+ $this->assertSame($expectedResponse, $this->strategy->processArguments($actualArguments));
+ }
+
+ public function argumentsProvider(): array
+ {
+ return [
+ 'with default arguments' => [
+ ['LIST', null, false],
+ ['LIST'],
+ ],
+ 'with LIBRARYNAME modifier' => [
+ ['LIST', 'libraryname', false],
+ ['LIST', 'LIBRARYNAME', 'libraryname'],
+ ],
+ 'with WITHCODE modifier' => [
+ ['LIST', null, true],
+ ['LIST', 'WITHCODE'],
+ ],
+ 'with all arguments' => [
+ ['LIST', 'libraryname', true],
+ ['LIST', 'LIBRARYNAME', 'libraryname', 'WITHCODE'],
+ ],
+ ];
+ }
+}
diff --git a/tests/Predis/Command/Strategy/ContainerCommands/Functions/RestoreStrategyTest.php b/tests/Predis/Command/Strategy/ContainerCommands/Functions/RestoreStrategyTest.php
new file mode 100644
index 00000000..839f14e8
--- /dev/null
+++ b/tests/Predis/Command/Strategy/ContainerCommands/Functions/RestoreStrategyTest.php
@@ -0,0 +1,54 @@
+strategy = new RestoreStrategy();
+ }
+
+ /**
+ * @dataProvider argumentsProvider
+ * @group disconnected
+ * @param array $actualArguments
+ * @param array $expectedResponse
+ * @return void
+ */
+ public function testProcessArguments(array $actualArguments, array $expectedResponse): void
+ {
+ $this->assertSame($expectedResponse, $this->strategy->processArguments($actualArguments));
+ }
+
+ public function argumentsProvider(): array
+ {
+ return [
+ 'with default arguments' => [
+ ['RESTORE', 'value', null],
+ ['RESTORE', 'value'],
+ ],
+ 'with mode argument' => [
+ ['RESTORE', 'value', 'append'],
+ ['RESTORE', 'value', 'APPEND'],
+ ],
+ ];
+ }
+}
diff --git a/tests/Predis/Command/Strategy/ContainerCommands/Functions/StatsStrategyTest.php b/tests/Predis/Command/Strategy/ContainerCommands/Functions/StatsStrategyTest.php
new file mode 100644
index 00000000..6d68539b
--- /dev/null
+++ b/tests/Predis/Command/Strategy/ContainerCommands/Functions/StatsStrategyTest.php
@@ -0,0 +1,37 @@
+strategy = new StatsStrategy();
+ }
+
+ /**
+ * @group disconnected
+ * @return void
+ */
+ public function testProcessArguments(): void
+ {
+ $this->assertSame(['arg1', 'arg2'], $this->strategy->processArguments(['arg1', 'arg2']));
+ }
+}
diff --git a/tests/Predis/Command/Strategy/SubcommandStrategyResolverTest.php b/tests/Predis/Command/Strategy/SubcommandStrategyResolverTest.php
index 3532809c..e4497d49 100644
--- a/tests/Predis/Command/Strategy/SubcommandStrategyResolverTest.php
+++ b/tests/Predis/Command/Strategy/SubcommandStrategyResolverTest.php
@@ -19,23 +19,27 @@ use Predis\Command\Strategy\ContainerCommands\Functions\LoadStrategy;
class SubcommandStrategyResolverTest extends TestCase
{
/**
- * @var StrategyResolverInterface
- */
- private $resolver;
-
- protected function setUp(): void
- {
- $this->resolver = new SubcommandStrategyResolver();
- }
-
- /**
+ * @group disconnected
* @return void
*/
public function testResolveCorrectStrategy(): void
{
+ $resolver = new SubcommandStrategyResolver();
$expectedStrategy = new LoadStrategy();
- $this->assertEquals($expectedStrategy, $this->resolver->resolve('functions', 'load'));
+ $this->assertEquals($expectedStrategy, $resolver->resolve('functions', 'load'));
+ }
+
+ /**
+ * @group disconnected
+ * @return void
+ */
+ public function testResolveCorrectlyResolvesStrategyWithGivenWordSeparator(): void
+ {
+ $resolver = new SubcommandStrategyResolver('_');
+ $expectedStrategy = new LoadStrategy();
+
+ $this->assertEquals($expectedStrategy, $resolver->resolve('functions_', 'load_'));
}
/**
@@ -43,9 +47,11 @@ class SubcommandStrategyResolverTest extends TestCase
*/
public function testResolveThrowsExceptionOnNonExistingStrategy(): void
{
+ $resolver = new SubcommandStrategyResolver();
+
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Non-existing container command given');
- $this->resolver->resolve('foo', 'bar');
+ $resolver->resolve('foo', 'bar');
}
}