diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a45409f..a4627463 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,26 @@ v2.0.0 (201x-xx-xx) ================================================================================ +- Accepted values for some client options have changed, this is the new list of + accepted values: + + - `aggregate`: callable returning an aggregate connection. + - `cluster`: string value (`predis`, `redis`), callable returning an aggregate + connection. + - `replication`: string value (`predis`, `sentinel`), callable returning an + aggregate connection. + - `commands`: command factory, named array mapping command IDs to PHP classes, + callable returning a command factory or a named array. + - `connections`: connection factory, callable returning a connection factory, + named array mapping connection schemes to PHP classes. + - `prefix`: string value, command processor, callable. + - `exceptions`: boolean. + + Note that both the `cluster` and `replication` options now return a closure + acting as initializer instead of an aggregate connection instance. + +- Client option classes now live in the `Predis\Configuration\Option` namespace. + - Classes for Redis commands have been moved into the new `Predis\Command\Redis` namespace and each class name mirrors the respective Redis command ID. diff --git a/README.md b/README.md index b396ee52..eba0fc25 100644 --- a/README.md +++ b/README.md @@ -154,12 +154,12 @@ $client = new Predis\Client($parameters, ['prefix' => 'sample:']); Options are managed using a mini DI-alike container and their values can be lazily initialized only when needed. The client options supported by default in Predis are: - - `prefix`: prefix string automatically applied to keys found in commands. + - `prefix`: prefix string applied to every key found in commands. - `exceptions`: whether the client should throw or return responses upon Redis errors. - `connections`: list of connection backends or a connection factory instance. - - `cluster`: specifies a cluster backend (`predis`, `redis` or callable object). - - `replication`: specifies a replication backend (`TRUE`, `sentinel` or callable object). - - `aggregate`: overrides `cluster` and `replication` to provide a custom connections aggregator. + - `cluster`: specifies a cluster backend (`predis`, `redis` or callable). + - `replication`: specifies a replication backend (`predis`, `sentinel` or callable). + - `aggregate`: configures the client with a custom aggregate connection (callable). - `parameters`: list of default connection parameters for aggregate connections. - `commands`: specifies a command factory instance to use through the library. @@ -337,8 +337,11 @@ class BrandNewRedisCommand extends Predis\Command\Command } // Inject your command in the current command factory: -$client = new Predis\Client(); -$client->getCommandFactory()->defineCommand('newcmd', 'BrandNewRedisCommand'); +$client = new Predis\Client($parameters, [ + 'commands' => [ + 'newcmd' => 'BrandNewRedisCommand', + ], +]); $response = $client->newcmd(); ``` @@ -384,8 +387,11 @@ LUA; } // Inject the script command in the current command factory: -$client = new Predis\Client(); -$client->getCommandFactory()->defineCommand('lpushrand', 'ListPushRandomValue'); +$client = new Predis\Client($parameters, [ + 'commands' => [ + 'lpushrand' => 'ListPushRandomValue', + ], +]); $response = $client->lpushrand('random_values', $seed = mt_rand()); ``` diff --git a/examples/lua_scripting_abstraction.php b/examples/lua_scripting_abstraction.php index bdf55a21..5d39f746 100644 --- a/examples/lua_scripting_abstraction.php +++ b/examples/lua_scripting_abstraction.php @@ -49,12 +49,9 @@ LUA; } $client = new Predis\Client($single_server, array( - 'commands' => function ($options) { - $commands = $options->getDefault('commands'); - $commands->defineCommand('increxby', 'IncrementExistingKeysBy'); - - return $commands; - }, + 'commands' => array( + 'increxby' => 'IncrementExistingKeysBy', + ), )); $client->mset('foo', 10, 'foobar', 100); diff --git a/examples/replication_complex.php b/examples/replication_complex.php index 5969e000..138d5a33 100644 --- a/examples/replication_complex.php +++ b/examples/replication_complex.php @@ -52,12 +52,9 @@ $parameters = array( ); $options = array( - 'commands' => function ($options) { - $commands = $options->getDefault('commands'); - $commands->defineCommand('hmgetall', 'HashMultipleGetAll'); - - return $commands; - }, + 'commands' => array( + 'hmgetall' => 'HashMultipleGetAll', + ), 'replication' => function () { $strategy = new ReplicationStrategy(); $strategy->setScriptReadOnly(HashMultipleGetAll::BODY); diff --git a/examples/replication_simple.php b/examples/replication_simple.php index 91b6db9c..ebf5eda1 100644 --- a/examples/replication_simple.php +++ b/examples/replication_simple.php @@ -26,7 +26,7 @@ $parameters = array( 'tcp://127.0.0.1:6380?database=15&alias=slave', ); -$options = array('replication' => true); +$options = array('replication' => 'predis'); $client = new Predis\Client($parameters, $options); diff --git a/src/Client.php b/src/Client.php index 4f1206de..3b53c8d6 100644 --- a/src/Client.php +++ b/src/Client.php @@ -19,6 +19,7 @@ use Predis\Configuration\OptionsInterface; use Predis\Connection\AggregateConnectionInterface; use Predis\Connection\ConnectionInterface; use Predis\Connection\ParametersInterface; +use Predis\Connection\Replication\SentinelReplication; use Predis\Monitor\Consumer as MonitorConsumer; use Predis\Pipeline\Pipeline; use Predis\PubSub\Consumer as PubSubConsumer; @@ -102,73 +103,68 @@ class Client implements ClientInterface, \IteratorAggregate */ protected function createConnection($parameters) { + $options = $this->getOptions(); + if ($parameters instanceof ConnectionInterface) { return $parameters; } if ($parameters instanceof ParametersInterface || is_string($parameters)) { - return $this->options->connections->create($parameters); + return $options->connections->create($parameters); } if (is_array($parameters)) { if (!isset($parameters[0])) { - return $this->options->connections->create($parameters); + return $options->connections->create($parameters); } - $options = $this->options; - - if ($options->defined('aggregate')) { - $initializer = $this->getConnectionInitializerWrapper($options->aggregate); - $connection = $initializer($parameters, $options); + if ($options->defined('cluster')) { + return $this->createAggregateConnection($parameters, 'cluster'); } elseif ($options->defined('replication')) { - $replication = $options->replication; - - if ($replication instanceof AggregateConnectionInterface) { - $connection = $replication; - $options->connections->aggregate($connection, $parameters); - } else { - $initializer = $this->getConnectionInitializerWrapper($replication); - $connection = $initializer($parameters, $options); - } + return $this->createAggregateConnection($parameters, 'replication'); + } elseif ($options->defined('aggregate')) { + return $this->createAggregateConnection($parameters, 'aggregate'); } else { - $connection = $options->cluster; - $options->connections->aggregate($connection, $parameters); + throw new \InvalidArgumentException( + 'Array of connection parameters requires `cluster`, `replication` or `aggregate` client option' + ); } - - return $connection; } if (is_callable($parameters)) { - $initializer = $this->getConnectionInitializerWrapper($parameters); - $connection = $initializer($this->options); + $connection = call_user_func($parameters, $options); + + if (!$connection instanceof ConnectionInterface) { + throw new \InvalidArgumentException('Callable parameters must return a valid connection'); + } return $connection; } - throw new \InvalidArgumentException('Invalid type for connection parameters.'); + throw new \InvalidArgumentException('Invalid type for connection parameters'); } /** - * Wraps a callable to make sure that its returned value represents a valid - * connection type. + * Creates an aggregate connection. * - * @param mixed $callable + * @param mixed $parameters Connection parameters. + * @param string $option Option for aggregate connections (`aggregate`, `cluster`, `replication`). * * @return \Closure */ - protected function getConnectionInitializerWrapper($callable) + protected function createAggregateConnection($parameters, $option) { - return function () use ($callable) { - $connection = call_user_func_array($callable, func_get_args()); + $options = $this->getOptions(); - if (!$connection instanceof ConnectionInterface) { - throw new \UnexpectedValueException( - 'The callable connection initializer returned an invalid type.' - ); - } + $initializer = $options->$option; + $connection = $initializer($parameters); - return $connection; - }; + // TODO: this is dirty but we must skip the redis-sentinel backend for now. + if ($option !== 'aggregate' && !$connection instanceof SentinelReplication) { + $options->connections->aggregate($connection, $parameters); + } + + return $connection; } /** diff --git a/src/Configuration/ClusterOption.php b/src/Configuration/ClusterOption.php deleted file mode 100644 index 9c94d45e..00000000 --- a/src/Configuration/ClusterOption.php +++ /dev/null @@ -1,76 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Predis\Configuration; - -use Predis\Connection\Cluster\ClusterInterface; -use Predis\Connection\Cluster\PredisCluster; -use Predis\Connection\Cluster\RedisCluster; - -/** - * Configures an aggregate connection used for clustering - * multiple Redis nodes using various implementations with - * different algorithms or strategies. - * - * @author Daniele Alessandri - */ -class ClusterOption implements OptionInterface -{ - /** - * Creates a new cluster connection from on a known descriptive name. - * - * @param OptionsInterface $options Instance of the client options. - * @param string $id Descriptive identifier of the cluster type (`predis`, `redis-cluster`) - * - * @return ClusterInterface|null - */ - protected function createByDescription(OptionsInterface $options, $id) - { - switch ($id) { - case 'predis': - case 'predis-cluster': - return new PredisCluster(); - - case 'redis': - case 'redis-cluster': - return new RedisCluster($options->connections); - - default: - return; - } - } - - /** - * {@inheritdoc} - */ - public function filter(OptionsInterface $options, $value) - { - if (is_string($value)) { - $value = $this->createByDescription($options, $value); - } - - if (!$value instanceof ClusterInterface) { - throw new \InvalidArgumentException( - "An instance of type 'Predis\Connection\Cluster\ClusterInterface' was expected." - ); - } - - return $value; - } - - /** - * {@inheritdoc} - */ - public function getDefault(OptionsInterface $options) - { - return new PredisCluster(); - } -} diff --git a/src/Configuration/CommandsOption.php b/src/Configuration/CommandsOption.php deleted file mode 100644 index caa00934..00000000 --- a/src/Configuration/CommandsOption.php +++ /dev/null @@ -1,64 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Predis\Configuration; - -use Predis\Command\FactoryInterface; -use Predis\Command\RedisFactory; - -/** - * Configures a connection factory to be used by the client. - * - * @author Daniele Alessandri - */ -class CommandsOption implements OptionInterface -{ - /** - * Sets the commands processors to be used by the command factory. - * - * @param OptionsInterface $options Client options. - * @param FactoryInterface $factory Command factory. - */ - protected function setProcessors(OptionsInterface $options, FactoryInterface $factory) - { - if (isset($options->prefix)) { - // NOTE: directly using __get('prefix') is actually a workaround for - // HHVM 2.3.0. It's correct and respects the options interface, it's - // just ugly. We will remove this hack when HHVM will fix re-entrant - // calls to __get() once and for all. - - $factory->setProcessor($options->__get('prefix')); - } - } - - /** - * {@inheritdoc} - */ - public function filter(OptionsInterface $options, $value) - { - if (!$value instanceof FactoryInterface) { - throw new \InvalidArgumentException('Invalid value for the commands option.'); - } - - return $value; - } - - /** - * {@inheritdoc} - */ - public function getDefault(OptionsInterface $options) - { - $commands = new RedisFactory(); - $this->setProcessors($options, $commands); - - return $commands; - } -} diff --git a/src/Configuration/Option/Aggregate.php b/src/Configuration/Option/Aggregate.php new file mode 100644 index 00000000..78005afe --- /dev/null +++ b/src/Configuration/Option/Aggregate.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Configuration\Option; + +use Predis\Configuration\OptionInterface; +use Predis\Configuration\OptionsInterface; +use Predis\Connection\AggregateConnectionInterface; + +/** + * Configures an aggregate connection used for clustering + * multiple Redis nodes using various implementations with + * different algorithms or strategies. + * + * @author Daniele Alessandri + */ +class Aggregate implements OptionInterface +{ + /** + * Wraps a callable to ensure that the returned value is a valid connection. + * + * @param OptionsInterface $options Client options. + * @param mixed $callable Callable initializer. + * + * @return \Closure + */ + protected function getConnectionInitializer(OptionsInterface $options, $callable) + { + if (!is_callable($callable)) { + $class = get_class($this); + + throw new \InvalidArgumentException("$class expects a valid callable"); + } + + return function ($parameters = null) use ($callable, $options) { + $connection = call_user_func($callable, $options, $parameters); + + if (!$connection instanceof AggregateConnectionInterface) { + $class = get_class($this); + + throw new \InvalidArgumentException("$class expects a valid connection type returned by callable initializer"); + } + + return $connection; + }; + } + + /** + * {@inheritdoc} + */ + public function filter(OptionsInterface $options, $value) + { + return $this->getConnectionInitializer($options, $value); + } + + /** + * {@inheritdoc} + */ + public function getDefault(OptionsInterface $options) + { + return; + } +} diff --git a/src/Configuration/Option/Cluster.php b/src/Configuration/Option/Cluster.php new file mode 100644 index 00000000..79cd3503 --- /dev/null +++ b/src/Configuration/Option/Cluster.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Configuration\Option; + +use Predis\Configuration\OptionsInterface; +use Predis\Connection\Cluster\PredisCluster; +use Predis\Connection\Cluster\RedisCluster; + +/** + * Configures an aggregate connection used for clustering + * multiple Redis nodes using various implementations with + * different algorithms or strategies. + * + * @author Daniele Alessandri + */ +class Cluster extends Aggregate +{ + /** + * Returns a connection initializer from a descriptive name. + * + * @param OptionsInterface $options Client options. + * @param string $description Identifier of a cluster backend (`predis`, `redis`) + * + * @return callable + */ + protected function getConnectionInitializerByDescription(OptionsInterface $options, $description) + { + if ($description === 'predis') { + $callback = $this->getDefault($options); + } elseif ($description === 'redis') { + $callback = function ($options) { + return new RedisCluster($options->connections); + }; + } else { + throw new \InvalidArgumentException( + 'String value for the cluster option must be either `predis` or `redis`' + ); + } + + return $this->getConnectionInitializer($options, $callback); + } + + /** + * {@inheritdoc} + */ + public function filter(OptionsInterface $options, $value) + { + if (is_string($value)) { + return $this->getConnectionInitializerByDescription($options, $value); + } else { + return $this->getConnectionInitializer($options, $value); + } + } + + /** + * {@inheritdoc} + */ + public function getDefault(OptionsInterface $options) + { + return function ($options) { + return new PredisCluster(); + }; + } +} diff --git a/src/Configuration/Option/Commands.php b/src/Configuration/Option/Commands.php new file mode 100644 index 00000000..8c9f86ca --- /dev/null +++ b/src/Configuration/Option/Commands.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Configuration\Option; + +use Predis\Command\FactoryInterface; +use Predis\Command\RedisFactory; +use Predis\Configuration\OptionInterface; +use Predis\Configuration\OptionsInterface; + +/** + * Configures a connection factory to be used by the client. + * + * @author Daniele Alessandri + */ +class Commands implements OptionInterface +{ + /** + * {@inheritdoc} + */ + public function filter(OptionsInterface $options, $value) + { + if (is_callable($value)) { + $value = call_user_func($value, $options); + } + + if (is_array($value)) { + $commands = $this->getDefault($options); + + foreach ($value as $commandID => $classFQN) { + $commands->defineCommand($commandID, $classFQN); + } + + return $commands; + } + + if (!$value instanceof FactoryInterface) { + $class = get_class($this); + + throw new \InvalidArgumentException("$class expects a valid command factory"); + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function getDefault(OptionsInterface $options) + { + $commands = new RedisFactory(); + + if (isset($options->prefix)) { + $commands->setProcessor($options->prefix); + } + + return $commands; + } +} diff --git a/src/Configuration/ConnectionFactoryOption.php b/src/Configuration/Option/Connections.php similarity index 75% rename from src/Configuration/ConnectionFactoryOption.php rename to src/Configuration/Option/Connections.php index bf8479c9..fd42a81a 100644 --- a/src/Configuration/ConnectionFactoryOption.php +++ b/src/Configuration/Option/Connections.php @@ -9,8 +9,10 @@ * file that was distributed with this source code. */ -namespace Predis\Configuration; +namespace Predis\Configuration\Option; +use Predis\Configuration\OptionInterface; +use Predis\Configuration\OptionsInterface; use Predis\Connection\Factory; use Predis\Connection\FactoryInterface; @@ -20,13 +22,17 @@ use Predis\Connection\FactoryInterface; * * @author Daniele Alessandri */ -class ConnectionFactoryOption implements OptionInterface +class Connections implements OptionInterface { /** * {@inheritdoc} */ public function filter(OptionsInterface $options, $value) { + if (is_callable($value)) { + $value = call_user_func($value, $options); + } + if ($value instanceof FactoryInterface) { return $value; } elseif (is_array($value)) { @@ -38,9 +44,9 @@ class ConnectionFactoryOption implements OptionInterface return $factory; } else { - throw new \InvalidArgumentException( - 'Invalid value provided for the connections option.' - ); + $class = get_class($this); + + throw new \InvalidArgumentException("$class expects a valid connection factory"); } } diff --git a/src/Configuration/ExceptionsOption.php b/src/Configuration/Option/Exceptions.php similarity index 81% rename from src/Configuration/ExceptionsOption.php rename to src/Configuration/Option/Exceptions.php index 337733e4..d8ab1a71 100644 --- a/src/Configuration/ExceptionsOption.php +++ b/src/Configuration/Option/Exceptions.php @@ -9,7 +9,10 @@ * file that was distributed with this source code. */ -namespace Predis\Configuration; +namespace Predis\Configuration\Option; + +use Predis\Configuration\OptionInterface; +use Predis\Configuration\OptionsInterface; /** * Configures whether consumers (such as the client) should throw exceptions on @@ -17,7 +20,7 @@ namespace Predis\Configuration; * * @author Daniele Alessandri */ -class ExceptionsOption implements OptionInterface +class Exceptions implements OptionInterface { /** * {@inheritdoc} diff --git a/src/Configuration/PrefixOption.php b/src/Configuration/Option/Prefix.php similarity index 72% rename from src/Configuration/PrefixOption.php rename to src/Configuration/Option/Prefix.php index 5827cdc3..41cc9f7b 100644 --- a/src/Configuration/PrefixOption.php +++ b/src/Configuration/Option/Prefix.php @@ -9,10 +9,12 @@ * file that was distributed with this source code. */ -namespace Predis\Configuration; +namespace Predis\Configuration\Option; use Predis\Command\Processor\KeyPrefixProcessor; use Predis\Command\Processor\ProcessorInterface; +use Predis\Configuration\OptionInterface; +use Predis\Configuration\OptionsInterface; /** * Configures a command processor that apply the specified prefix string to a @@ -20,18 +22,22 @@ use Predis\Command\Processor\ProcessorInterface; * * @author Daniele Alessandri */ -class PrefixOption implements OptionInterface +class Prefix implements OptionInterface { /** * {@inheritdoc} */ public function filter(OptionsInterface $options, $value) { + if (is_callable($value)) { + $value = call_user_func($value, $options); + } + if ($value instanceof ProcessorInterface) { return $value; } - return new KeyPrefixProcessor($value); + return new KeyPrefixProcessor((string) $value); } /** diff --git a/src/Configuration/Option/Replication.php b/src/Configuration/Option/Replication.php new file mode 100644 index 00000000..2074ccd4 --- /dev/null +++ b/src/Configuration/Option/Replication.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Configuration\Option; + +use Predis\Configuration\OptionsInterface; +use Predis\Connection\Replication\MasterSlaveReplication; +use Predis\Connection\Replication\SentinelReplication; + +/** + * Configures an aggregate connection used for master/slave replication among + * multiple Redis nodes. + * + * @author Daniele Alessandri + */ +class Replication extends Aggregate +{ + /** + * Returns a connection initializer from a descriptive name. + * + * @param OptionsInterface $options Client options. + * @param string $description Identifier of a replication backend (`predis`, `sentinel`) + * + * @return callable + */ + protected function getConnectionInitializerByDescription(OptionsInterface $options, $description) + { + if ($description === 'predis') { + $callback = $this->getDefault($options); + } elseif ($description === 'sentinel') { + $callback = function ($options, $sentinels) { + return new SentinelReplication($options->service, $sentinels, $options->connections); + }; + } else { + throw new \InvalidArgumentException( + 'String value for the replication option must be either `predis` or `sentinel`' + ); + } + + return $this->getConnectionInitializer($options, $callback); + } + + /** + * {@inheritdoc} + */ + public function filter(OptionsInterface $options, $value) + { + if (is_string($value)) { + return $this->getConnectionInitializerByDescription($options, $value); + } else { + return $this->getConnectionInitializer($options, $value); + } + } + + /** + * {@inheritdoc} + */ + public function getDefault(OptionsInterface $options) + { + return function ($options) { + $connection = new MasterSlaveReplication(); + + if ($options->autodiscovery) { + $connection->setConnectionFactory($options->connections); + $connection->setAutoDiscovery(true); + } + + return $connection; + }; + } +} diff --git a/src/Configuration/Options.php b/src/Configuration/Options.php index 79b377fb..4cbaa285 100644 --- a/src/Configuration/Options.php +++ b/src/Configuration/Options.php @@ -21,8 +21,8 @@ namespace Predis\Configuration; */ class Options implements OptionsInterface { + protected $options = array(); protected $input; - protected $options; protected $handlers; /** @@ -43,12 +43,13 @@ class Options implements OptionsInterface protected function getHandlers() { return array( - 'cluster' => 'Predis\Configuration\ClusterOption', - 'connections' => 'Predis\Configuration\ConnectionFactoryOption', - 'exceptions' => 'Predis\Configuration\ExceptionsOption', - 'prefix' => 'Predis\Configuration\PrefixOption', - 'commands' => 'Predis\Configuration\CommandsOption', - 'replication' => 'Predis\Configuration\ReplicationOption', + 'aggregate' => 'Predis\Configuration\Option\Aggregate', + 'cluster' => 'Predis\Configuration\Option\Cluster', + 'replication' => 'Predis\Configuration\Option\Replication', + 'connections' => 'Predis\Configuration\Option\Connections', + 'commands' => 'Predis\Configuration\Option\Commands', + 'exceptions' => 'Predis\Configuration\Option\Exceptions', + 'prefix' => 'Predis\Configuration\Option\Prefix', ); } @@ -100,14 +101,12 @@ class Options implements OptionsInterface $value = $this->input[$option]; unset($this->input[$option]); - if (is_object($value) && method_exists($value, '__invoke')) { - $value = $value($this, $option); - } - if (isset($this->handlers[$option])) { $handler = $this->handlers[$option]; $handler = new $handler(); $value = $handler->filter($this, $value); + } elseif (is_object($value) && method_exists($value, '__invoke')) { + $value = $value($this); } return $this->options[$option] = $value; diff --git a/src/Configuration/ReplicationOption.php b/src/Configuration/ReplicationOption.php deleted file mode 100644 index 73946beb..00000000 --- a/src/Configuration/ReplicationOption.php +++ /dev/null @@ -1,75 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Predis\Configuration; - -use Predis\Connection\Replication\MasterSlaveReplication; -use Predis\Connection\Replication\ReplicationInterface; -use Predis\Connection\Replication\SentinelReplication; - -/** - * Configures an aggregate connection used for master/slave replication among - * multiple Redis nodes. - * - * @author Daniele Alessandri - */ -class ReplicationOption implements OptionInterface -{ - /** - * {@inheritdoc} - * - * @todo There's more code than needed due to a bug in filter_var() as - * discussed here https://bugs.php.net/bug.php?id=49510 and different - * behaviours when encountering NULL values on PHP 5.3. - */ - public function filter(OptionsInterface $options, $value) - { - if ($value instanceof ReplicationInterface) { - return $value; - } - - if (is_bool($value) || $value === null) { - return $value ? $this->getDefault($options) : null; - } - - if ($value === 'sentinel') { - return function ($sentinels, $options) { - return new SentinelReplication($options->service, $sentinels, $options->connections); - }; - } - - if ( - !is_object($value) && - null !== $asbool = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) - ) { - return $asbool ? $this->getDefault($options) : null; - } - - throw new \InvalidArgumentException( - "An instance of type 'Predis\Connection\Replication\ReplicationInterface' was expected." - ); - } - - /** - * {@inheritdoc} - */ - public function getDefault(OptionsInterface $options) - { - $replication = new MasterSlaveReplication(); - - if ($options->autodiscovery) { - $replication->setConnectionFactory($options->connections); - $replication->setAutoDiscovery(true); - } - - return $replication; - } -} diff --git a/tests/Predis/ClientTest.php b/tests/Predis/ClientTest.php index 4a2d3dae..ff8cd67b 100644 --- a/tests/Predis/ClientTest.php +++ b/tests/Predis/ClientTest.php @@ -92,8 +92,10 @@ class ClientTest extends PredisTestCase /** * @group disconnected + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Array of connection parameters requires `cluster`, `replication` or `aggregate` client option */ - public function testConstructorWithArrayOfArrayArgument() + public function testConstructorThrowsExceptionWithArrayOfParametersArgumentAndMissingOption() { $arg1 = array( array('host' => 'localhost', 'port' => 7000), @@ -101,8 +103,23 @@ class ClientTest extends PredisTestCase ); $client = new Client($arg1); + } - $this->assertInstanceOf('Predis\Connection\Cluster\ClusterInterface', $client->getConnection()); + /** + * @group disconnected + */ + public function testConstructorWithArrayOfArrayArgumentAndClusterOption() + { + $arg1 = array( + array('host' => 'localhost', 'port' => 7000), + array('host' => 'localhost', 'port' => 7001), + ); + + $client = new Client($arg1, array( + 'aggregate' => $this->getAggregateInitializer($arg1), + )); + + $this->assertInstanceOf('Predis\Connection\AggregateConnectionInterface', $client->getConnection()); } /** @@ -122,9 +139,13 @@ class ClientTest extends PredisTestCase */ public function testConstructorWithArrayOfStringArgument() { - $client = new Client($arg1 = array('tcp://localhost:7000', 'tcp://localhost:7001')); + $arg1 = array('tcp://localhost:7000', 'tcp://localhost:7001'); - $this->assertInstanceOf('Predis\Connection\Cluster\ClusterInterface', $client->getConnection()); + $client = new Client($arg1, array( + 'aggregate' => $this->getAggregateInitializer($arg1), + )); + + $this->assertInstanceOf('Predis\Connection\AggregateConnectionInterface', $client->getConnection()); } /** @@ -132,14 +153,16 @@ class ClientTest extends PredisTestCase */ public function testConstructorWithArrayOfConnectionsArgument() { - $connection1 = $this->getMock('Predis\Connection\NodeConnectionInterface'); - $connection2 = $this->getMock('Predis\Connection\NodeConnectionInterface'); + $arg1 = array( + $this->getMock('Predis\Connection\NodeConnectionInterface'), + $this->getMock('Predis\Connection\NodeConnectionInterface'), + ); - $client = new Client(array($connection1, $connection2)); + $client = new Client($arg1, array( + 'aggregate' => $this->getAggregateInitializer($arg1), + )); - $this->assertInstanceOf('Predis\Connection\Cluster\ClusterInterface', $cluster = $client->getConnection()); - $this->assertSame($connection1, $cluster->getConnectionById(0)); - $this->assertSame($connection2, $cluster->getConnectionById(1)); + $this->assertInstanceOf('Predis\Connection\AggregateConnectionInterface', $client->getConnection()); } /** @@ -212,8 +235,8 @@ class ClientTest extends PredisTestCase /** * @group disconnected - * @expectedException \UnexpectedValueException - * @expectedExceptionMessage The callable connection initializer returned an invalid type. + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Callable parameters must return a valid connection */ public function testConstructorWithCallableConnectionInitializerThrowsExceptionOnInvalidReturnType() { @@ -249,7 +272,7 @@ class ClientTest extends PredisTestCase public function testConstructorWithArrayAndOptionReplication() { $arg1 = array('tcp://host1?alias=master', 'tcp://host2?alias=slave'); - $arg2 = array('replication' => true); + $arg2 = array('replication' => 'predis'); $client = new Client($arg1, $arg2); $this->assertInstanceOf('Predis\Connection\Replication\ReplicationInterface', $connection = $client->getConnection()); @@ -260,28 +283,28 @@ class ClientTest extends PredisTestCase /** * @group disconnected */ - public function testConstructorWithArrayAndOptionAggregate() + public function testClusterOptionHasPrecedenceOverReplicationOptionAndAggregateOption() { $arg1 = array('tcp://host1', 'tcp://host2'); - $connection = $this->getMock('Predis\Connection\ConnectionInterface'); - - $fnaggregate = $this->getMock('stdClass', array('__invoke')); - $fnaggregate->expects($this->once()) - ->method('__invoke') - ->with($arg1) - ->will($this->returnValue($connection)); + $connection = $this->getMock('Predis\Connection\AggregateConnectionInterface'); $fncluster = $this->getMock('stdClass', array('__invoke')); - $fncluster->expects($this->never())->method('__invoke'); + $fncluster->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface'), $arg1) + ->will($this->returnValue($connection)); $fnreplication = $this->getMock('stdClass', array('__invoke')); $fnreplication->expects($this->never())->method('__invoke'); + $fnaggregate = $this->getMock('stdClass', array('__invoke')); + $fnaggregate->expects($this->never())->method('__invoke'); + $arg2 = array( - 'aggregate' => function () use ($fnaggregate) { return $fnaggregate; }, - 'cluster' => function () use ($fncluster) { return $fncluster; }, - 'replication' => function () use ($fnreplication) { return $fnreplication; }, + 'cluster' => $fncluster, + 'replication' => $fnreplication, + 'aggregate' => $fnaggregate, ); $client = new Client($arg1, $arg2); @@ -291,22 +314,56 @@ class ClientTest extends PredisTestCase /** * @group disconnected - * @expectedException \UnexpectedValueException - * @expectedExceptionMessage The callable connection initializer returned an invalid type. */ - public function testConstructorWithArrayAndOptionAggregateThrowsExceptionOnInvalidReturnType() + public function testReplicationOptionHasPrecedenceOverAggregateOption() { $arg1 = array('tcp://host1', 'tcp://host2'); + $connection = $this->getMock('Predis\Connection\AggregateConnectionInterface'); + + $fnreplication = $this->getMock('stdClass', array('__invoke')); + $fnreplication->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface'), $arg1) + ->will($this->returnValue($connection)); + + $fnaggregate = $this->getMock('stdClass', array('__invoke')); + $fnaggregate->expects($this->never())->method('__invoke'); + + $arg2 = array( + 'replication' => $fnreplication, + 'aggregate' => $fnaggregate, + ); + + $client = new Client($arg1, $arg2); + + $this->assertSame($connection, $client->getConnection()); + } + + /** + * @group disconnected + */ + public function testAggregateOptionDoesNotTriggerAggregationInClient() + { + $arg1 = array('tcp://host1', 'tcp://host2'); + + $connection = $this->getMock('Predis\Connection\AggregateConnectionInterface'); + $fnaggregate = $this->getMock('stdClass', array('__invoke')); $fnaggregate->expects($this->once()) ->method('__invoke') - ->with($arg1) - ->will($this->returnValue(false)); + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface'), $arg1) + ->will($this->returnValue($connection)); - $arg2 = array('aggregate' => function () use ($fnaggregate) { return $fnaggregate; }); + $connections = $this->getMock('Predis\Connection\FactoryInterface'); + $connections->expects($this->never()) + ->method('aggregate'); - new Client($arg1, $arg2); + $arg2 = array('aggregate' => $fnaggregate, 'connections' => $connections); + + $client = new Client($arg1, $arg2); + + $this->assertSame($connection, $client->getConnection()); } /** @@ -577,7 +634,7 @@ class ClientTest extends PredisTestCase */ public function testGetConnectionFromAggregateConnectionWithAlias() { - $client = new Client(array('tcp://host1?alias=node01', 'tcp://host2?alias=node02')); + $client = new Client(array('tcp://host1?alias=node01', 'tcp://host2?alias=node02'), array('cluster' => 'predis')); $this->assertInstanceOf('Predis\Connection\Cluster\ClusterInterface', $cluster = $client->getConnection()); $this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $node01 = $client->getConnectionById('node01')); @@ -604,7 +661,7 @@ class ClientTest extends PredisTestCase */ public function testCreateClientWithConnectionFromAggregateConnection() { - $client = new Client(array('tcp://host1?alias=node01', 'tcp://host2?alias=node02'), array('prefix' => 'pfx:')); + $client = new Client(array('tcp://host1?alias=node01', 'tcp://host2?alias=node02'), array('prefix' => 'pfx:', 'cluster' => 'predis')); $this->assertInstanceOf('Predis\Connection\Cluster\ClusterInterface', $cluster = $client->getConnection()); $this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $node01 = $client->getConnectionById('node01')); @@ -623,7 +680,7 @@ class ClientTest extends PredisTestCase public function testGetClientForReturnsInstanceOfSubclass() { $nodes = array('tcp://host1?alias=node01', 'tcp://host2?alias=node02'); - $client = $this->getMock('Predis\Client', array('dummy'), array($nodes), 'SubclassedClient'); + $client = $this->getMock('Predis\Client', array('dummy'), array($nodes, array('cluster' => 'predis')), 'SubclassedClient'); $this->assertInstanceOf('SubclassedClient', $client->getClientFor('node02')); } @@ -891,4 +948,24 @@ class ClientTest extends PredisTestCase return $uriString; } + + /** + * Returns a mock callable simulating an aggregate connection initializer. + * + * @param mixed $parameters Expected connection parameters + * + * @return callable + */ + protected function getAggregateInitializer($parameters) + { + $connection = $this->getMock('Predis\Connection\AggregateConnectionInterface'); + + $callable = $this->getMock('stdClass', array('__invoke')); + $callable->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface'), $parameters) + ->will($this->returnValue($connection)); + + return $callable; + } } diff --git a/tests/Predis/Configuration/ClusterOptionTest.php b/tests/Predis/Configuration/ClusterOptionTest.php deleted file mode 100644 index 2d8ac019..00000000 --- a/tests/Predis/Configuration/ClusterOptionTest.php +++ /dev/null @@ -1,90 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Predis\Configuration; - -use PredisTestCase; - -/** - * - */ -class ClusterOptionTest extends PredisTestCase -{ - /** - * @group disconnected - */ - public function testDefaultOptionValue() - { - $option = new ClusterOption(); - $options = $this->getMock('Predis\Configuration\OptionsInterface'); - - $this->assertInstanceOf('Predis\Connection\Cluster\PredisCluster', $option->getDefault($options)); - } - - /** - * @group disconnected - */ - public function testAcceptsInstanceOfClusterInterface() - { - $option = new ClusterOption(); - $options = $this->getMock('Predis\Configuration\OptionsInterface'); - $cluster = $this->getMock('Predis\Connection\Cluster\ClusterInterface'); - - $this->assertSame($cluster, $option->filter($options, $cluster)); - } - - /** - * @group disconnected - */ - public function testAcceptsPredefinedShortNameString() - { - $option = new ClusterOption(); - - $options = $this->getMock('Predis\Configuration\OptionsInterface'); - $options->expects($this->any()) - ->method('__get') - ->with('connections') - ->will($this->returnValue( - $this->getMock('Predis\Connection\FactoryInterface') - )); - - $this->assertInstanceOf('Predis\Connection\Cluster\PredisCluster', $option->filter($options, 'predis')); - $this->assertInstanceOf('Predis\Connection\Cluster\PredisCluster', $option->filter($options, 'predis-cluster')); - - $this->assertInstanceOf('Predis\Connection\Cluster\RedisCluster', $option->filter($options, 'redis')); - $this->assertInstanceOf('Predis\Connection\Cluster\RedisCluster', $option->filter($options, 'redis-cluster')); - } - - /** - * @group disconnected - * @expectedException \InvalidArgumentException - */ - public function testThrowsExceptionOnInvalidInstanceType() - { - $option = new ClusterOption(); - $options = $this->getMock('Predis\Configuration\OptionsInterface'); - $class = $this->getMock('Predis\Connection\NodeConnectionInterface'); - - $option->filter($options, $class); - } - - /** - * @group disconnected - * @expectedException \InvalidArgumentException - */ - public function testThrowsExceptionOnInvalidShortNameString() - { - $option = new ClusterOption(); - $options = $this->getMock('Predis\Configuration\OptionsInterface'); - - $option->filter($options, 'unknown'); - } -} diff --git a/tests/Predis/Configuration/CommandsOptionTest.php b/tests/Predis/Configuration/CommandsOptionTest.php deleted file mode 100644 index 5a02d7ba..00000000 --- a/tests/Predis/Configuration/CommandsOptionTest.php +++ /dev/null @@ -1,100 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Predis\Configuration; - -use Predis\Command\RedisFactory; -use Predis\Command\Processor\KeyPrefixProcessor; -use PredisTestCase; - -/** - * - */ -class CommandsOptionTest extends PredisTestCase -{ - /** - * @group disconnected - */ - public function testDefaultOptionValue() - { - $option = new CommandsOption(); - $options = $this->getMock('Predis\Configuration\OptionsInterface'); - - $commands = $option->getDefault($options); - - $this->assertInstanceOf('Predis\Command\FactoryInterface', $commands); - $this->assertNull($commands->getProcessor()); - } - - /** - * @group disconnected - */ - public function testAcceptsCommandFactoryInstanceAsValue() - { - $option = new CommandsOption(); - $options = $this->getMock('Predis\Configuration\OptionsInterface'); - - $value = new RedisFactory(); - - $commands = $option->filter($options, $value); - - $this->assertSame($commands, $value); - $this->assertNull($commands->getProcessor()); - } - - /** - * @group disconnected - */ - public function testAppliesPrefixOnDefaultOptionValue() - { - $option = new CommandsOption(); - $options = $this->getMock('Predis\Configuration\OptionsInterface'); - - $options->expects($this->once()) - ->method('__isset') - ->with('prefix') - ->will($this->returnValue(true)); - $options->expects($this->once()) - ->method('__get') - ->with('prefix') - ->will($this->returnValue(new KeyPrefixProcessor('prefix:'))); - - $commands = $option->getDefault($options); - - $this->assertInstanceOf('Predis\Command\FactoryInterface', $commands); - $this->assertInstanceOf('Predis\Command\Processor\KeyPrefixProcessor', $commands->getProcessor()); - $this->assertSame('prefix:', $commands->getProcessor()->getPrefix()); - } - - /** - * @group disconnected - * @expectedException InvalidArgumentException - */ - public function testThrowsExceptionOnStringValue() - { - $option = new CommandsOption(); - $options = $this->getMock('Predis\Configuration\OptionsInterface'); - - $option->filter($options, '3.2'); - } - - /** - * @group disconnected - * @expectedException InvalidArgumentException - */ - public function testThrowsExceptionOnInvalidValue() - { - $option = new CommandsOption(); - $options = $this->getMock('Predis\Configuration\OptionsInterface'); - - $option->filter($options, new \stdClass()); - } -} diff --git a/tests/Predis/Configuration/Option/AggregateTest.php b/tests/Predis/Configuration/Option/AggregateTest.php new file mode 100644 index 00000000..350e8945 --- /dev/null +++ b/tests/Predis/Configuration/Option/AggregateTest.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Configuration\Option; + +use PredisTestCase; + +/** + * + */ +class AggregateTest extends PredisTestCase +{ + /** + * @group disconnected + */ + public function testDefaultOptionValue() + { + $option = new Aggregate(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + + $this->assertNull($option->getDefault($options)); + } + + /** + * @group disconnected + */ + public function testAcceptsCallableAsConnectionInitializer() + { + $option = new Aggregate(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $connection = $this->getMock('Predis\Connection\AggregateConnectionInterface'); + + $callable = $this->getMock('stdClass', array('__invoke')); + $callable->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface')) + ->will($this->returnValue($connection)); + + $this->assertInstanceOf('Closure', $initializer = $option->filter($options, $callable)); + $this->assertSame($connection, $initializer($parameters = array())); + } + + /** + * @group disconnected + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Predis\Configuration\Option\Aggregate expects a valid connection type returned by callable initializer + */ + public function testThrowsExceptionOnInvalidReturnTypeOfConnectionInitializer() + { + $option = new Aggregate(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $connection = $this->getMock('Predis\Connection\NodeConnectionInterface'); + + $callable = $this->getMock('stdClass', array('__invoke')); + $callable->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface')) + ->will($this->returnValue($connection)); + + $this->assertInstanceOf('Closure', $initializer = $option->filter($options, $callable)); + + $initializer($parameters = array()); + } + + /** + * @group disconnected + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Predis\Configuration\Option\Aggregate expects a valid callable + */ + public function testThrowsExceptionOnInstanceOfAggregateConnectionInterface() + { + $option = new Aggregate(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $cluster = $this->getMock('Predis\Connection\AggregateConnectionInterface'); + + $option->filter($options, $cluster); + } +} diff --git a/tests/Predis/Configuration/Option/ClusterTest.php b/tests/Predis/Configuration/Option/ClusterTest.php new file mode 100644 index 00000000..ff740b9e --- /dev/null +++ b/tests/Predis/Configuration/Option/ClusterTest.php @@ -0,0 +1,140 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Configuration\Option; + +use PredisTestCase; + +/** + * + */ +class ClusterTest extends PredisTestCase +{ + /** + * @group disconnected + */ + public function testDefaultOptionValue() + { + $option = new Cluster(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + + $this->assertInstanceOf('Closure', $initializer = $option->getDefault($options)); + $this->assertInstanceOf('Predis\Connection\Cluster\PredisCluster', $initializer($options)); + } + + /** + * @group disconnected + */ + public function testAcceptsCallableAsConnectionInitializer() + { + $option = new Cluster(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $connection = $this->getMock('Predis\Connection\AggregateConnectionInterface'); + + $callable = $this->getMock('stdClass', array('__invoke')); + $callable->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface')) + ->will($this->returnValue($connection)); + + $this->assertInstanceOf('Closure', $initializer = $option->filter($options, $callable)); + $this->assertSame($connection, $initializer($parameters = array())); + } + + /** + * @group disconnected + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Predis\Configuration\Option\Cluster expects a valid connection type returned by callable initializer + */ + public function testThrowsExceptionOnInvalidReturnTypeOfConnectionInitializer() + { + $option = new Cluster(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $connection = $this->getMock('Predis\Connection\NodeConnectionInterface'); + + $callable = $this->getMock('stdClass', array('__invoke')); + $callable->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface')) + ->will($this->returnValue($connection)); + + $this->assertInstanceOf('Closure', $initializer = $option->filter($options, $callable)); + + $initializer($parameters = array()); + } + + /** + * @group disconnected + */ + public function testAcceptsShortNameStringPredis() + { + $option = new Cluster(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $options->expects($this->never()) + ->method('__get') + ->with('connections'); + + $this->assertInstanceOf('Closure', $initializer = $option->filter($options, 'predis')); + $this->assertInstanceOf('Predis\Connection\Cluster\PredisCluster', $initializer($parameters = array())); + } + + /** + * @group disconnected + */ + public function testAcceptsShortNameStringRedis() + { + $option = new Cluster(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $options->expects($this->once()) + ->method('__get') + ->with('connections') + ->will($this->returnValue( + $this->getMock('Predis\Connection\FactoryInterface') + )); + + $this->assertInstanceOf('Closure', $initializer = $option->filter($options, 'redis')); + $this->assertInstanceOf('Predis\Connection\Cluster\RedisCluster', $initializer($parameters = array())); + } + + /** + * @group disconnected + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage String value for the cluster option must be either `predis` or `redis` + */ + public function testThrowsExceptionOnInvalidShortNameString() + { + $option = new Cluster(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + + $option->filter($options, 'unknown'); + } + + /** + * @group disconnected + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Predis\Configuration\Option\Cluster expects a valid callable + */ + public function testThrowsExceptionOnInstanceOfClusterInterface() + { + $option = new Cluster(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $connection = $this->getMock('Predis\Connection\Cluster\ClusterInterface'); + + $option->filter($options, $connection); + } +} diff --git a/tests/Predis/Configuration/Option/CommandsTest.php b/tests/Predis/Configuration/Option/CommandsTest.php new file mode 100644 index 00000000..49035201 --- /dev/null +++ b/tests/Predis/Configuration/Option/CommandsTest.php @@ -0,0 +1,178 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Configuration\Option; + +use Predis\Command\Processor\KeyPrefixProcessor; +use Predis\Command\RedisFactory; +use PredisTestCase; + +/** + * + */ +class CommandsTest extends PredisTestCase +{ + /** + * @group disconnected + */ + public function testDefaultOptionValue() + { + $option = new Commands(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + + $commands = $option->getDefault($options); + + $this->assertInstanceOf('Predis\Command\FactoryInterface', $commands); + $this->assertNull($commands->getProcessor()); + } + + /** + * @group disconnected + */ + public function testAppliesPrefixOnDefaultOptionValue() + { + $option = new Commands(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $options->expects($this->once()) + ->method('__isset') + ->with('prefix') + ->will($this->returnValue(true)); + $options->expects($this->once()) + ->method('__get') + ->with('prefix') + ->will($this->returnValue(new KeyPrefixProcessor('prefix:'))); + + $commands = $option->getDefault($options); + + $this->assertInstanceOf('Predis\Command\FactoryInterface', $commands); + $this->assertInstanceOf('Predis\Command\Processor\KeyPrefixProcessor', $commands->getProcessor()); + $this->assertSame('prefix:', $commands->getProcessor()->getPrefix()); + } + + /** + * @group disconnected + */ + public function testAcceptsCommandFactoryInstanceAsValue() + { + $option = new Commands(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + + $input = new RedisFactory(); + + $commands = $option->filter($options, $input); + + $this->assertSame($commands, $input); + $this->assertNull($commands->getProcessor()); + } + + /** + * @group disconnected + */ + public function testAcceptsDictionaryOfCommandsAsValue() + { + $option = new Commands(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + + $input = array( + 'FOO' => 'Predis\Command\RawCommand', + 'BAR' => 'Predis\Command\RawCommand', + ); + + $commands = $option->filter($options, $input); + + $this->assertInstanceOf('Predis\Command\FactoryInterface', $commands); + $this->assertSame('Predis\Command\RawCommand', $commands->getCommandClass('FOO')); + $this->assertSame('Predis\Command\RawCommand', $commands->getCommandClass('BAR')); + } + + /** + * @group disconnected + */ + public function testAcceptsCallableReturningCommandFactoryInstance() + { + $option = new Commands(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $commands = $this->getMock('Predis\Command\FactoryInterface'); + + $callable = $this->getMock('stdClass', array('__invoke')); + $callable->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface')) + ->will($this->returnValue($commands)); + + $this->assertSame($commands, $option->filter($options, $callable)); + } + + /** + * @group disconnected + */ + public function testAcceptsCallableReturningDictionaryOfCommandsAsValue() + { + $option = new Commands(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + + $dictionary = array( + 'FOO' => 'Predis\Command\RawCommand', + 'BAR' => 'Predis\Command\RawCommand', + ); + + $callable = $this->getMock('stdClass', array('__invoke')); + $callable->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface')) + ->will($this->returnValue($dictionary)); + + $commands = $option->filter($options, $callable); + + $this->assertInstanceOf('Predis\Command\FactoryInterface', $commands); + $this->assertSame('Predis\Command\RawCommand', $commands->getCommandClass('FOO')); + $this->assertSame('Predis\Command\RawCommand', $commands->getCommandClass('BAR')); + } + + /** + * @group disconnected + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Predis\Configuration\Option\Commands expects a valid command factory + */ + public function testThrowsExceptionOnInvalidTypeReturnedByCallable() + { + $option = new Commands(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + + $callable = $this->getMock('stdClass', array('__invoke')); + $callable->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface')) + ->will($this->returnValue(new \stdClass())); + + $option->filter($options, $callable); + } + + /** + * @group disconnected + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Predis\Configuration\Option\Commands expects a valid command factory + */ + public function testThrowsExceptionOnInvalidValue() + { + $option = new Commands(); + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + + $option->filter($options, new \stdClass()); + } +} diff --git a/tests/Predis/Configuration/ConnectionFactoryOptionTest.php b/tests/Predis/Configuration/Option/ConnectionsTest.php similarity index 66% rename from tests/Predis/Configuration/ConnectionFactoryOptionTest.php rename to tests/Predis/Configuration/Option/ConnectionsTest.php index d4863a4b..17eaf164 100644 --- a/tests/Predis/Configuration/ConnectionFactoryOptionTest.php +++ b/tests/Predis/Configuration/Option/ConnectionsTest.php @@ -9,21 +9,22 @@ * file that was distributed with this source code. */ -namespace Predis\Configuration; +namespace Predis\Configuration\Option; use PredisTestCase; /** * */ -class ConnectionFactoryOptionTest extends PredisTestCase +class ConnectionsTest extends PredisTestCase { /** * @group disconnected */ public function testDefaultOptionValue() { - $option = new ConnectionFactoryOption(); + $option = new Connections(); + $options = $this->getMock('Predis\Configuration\OptionsInterface'); $this->assertInstanceOf('Predis\Connection\Factory', $option->getDefault($options)); @@ -44,7 +45,7 @@ class ConnectionFactoryOptionTest extends PredisTestCase ->method('define') ->with($this->matchesRegularExpression('/^tcp|redis$/'), $class); - $option = $this->getMock('Predis\Configuration\ConnectionFactoryOption', array('getDefault')); + $option = $this->getMock('Predis\Configuration\Option\Connections', array('getDefault')); $option->expects($this->once()) ->method('getDefault') ->with($options) @@ -74,7 +75,7 @@ class ConnectionFactoryOptionTest extends PredisTestCase ->with('parameters') ->will($this->returnValue($parameters)); - $option = new ConnectionFactoryOption(); + $option = new Connections(); $factory = $option->getDefault($options); $this->assertSame($parameters, $factory->getDefaultParameters()); @@ -85,22 +86,42 @@ class ConnectionFactoryOptionTest extends PredisTestCase */ public function testAcceptsConnectionFactoryInstance() { - $options = $this->getMock('Predis\Configuration\OptionsInterface'); - $value = $this->getMock('Predis\Connection\FactoryInterface'); - - $option = $this->getMock('Predis\Configuration\ConnectionFactoryOption', array('getDefault')); + $option = $this->getMock('Predis\Configuration\Option\Connections', array('getDefault')); $option->expects($this->never())->method('getDefault'); - $this->assertSame($value, $option->filter($options, $value)); + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $factory = $this->getMock('Predis\Connection\FactoryInterface'); + + $this->assertSame($factory, $option->filter($options, $factory)); } /** * @group disconnected - * @expectedException InvalidArgumentException + */ + public function testAcceptsCallableReturningConnectionFactoryInstance() + { + $option = new Connections(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $factory = $this->getMock('Predis\Connection\FactoryInterface'); + + $callable = $this->getMock('stdClass', array('__invoke')); + $callable->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface')) + ->will($this->returnValue($factory)); + + $this->assertSame($factory, $option->filter($options, $callable)); + } + + /** + * @group disconnected + * @expectedException \InvalidArgumentException + * @expectedException Predis\Configuration\Option\Connections expects a valid command factory */ public function testThrowsExceptionOnInvalidArguments() { - $option = new ConnectionFactoryOption(); + $option = new Connections(); $options = $this->getMock('Predis\Configuration\OptionsInterface'); $option->filter($options, new \stdClass()); diff --git a/tests/Predis/Configuration/ExceptionsOptionTest.php b/tests/Predis/Configuration/Option/ExceptionsTest.php similarity index 88% rename from tests/Predis/Configuration/ExceptionsOptionTest.php rename to tests/Predis/Configuration/Option/ExceptionsTest.php index b7d7780a..46b044d4 100644 --- a/tests/Predis/Configuration/ExceptionsOptionTest.php +++ b/tests/Predis/Configuration/Option/ExceptionsTest.php @@ -9,21 +9,21 @@ * file that was distributed with this source code. */ -namespace Predis\Configuration; +namespace Predis\Configuration\Option; use PredisTestCase; /** * */ -class ExceptionsOptionTest extends PredisTestCase +class ExceptionsTest extends PredisTestCase { /** * @group disconnected */ public function testDefaultOptionValue() { - $option = new ExceptionsOption(); + $option = new Exceptions(); $options = $this->getMock('Predis\Configuration\OptionsInterface'); $this->assertTrue($option->getDefault($options)); @@ -34,7 +34,7 @@ class ExceptionsOptionTest extends PredisTestCase */ public function testAcceptsDifferentValuesAndFiltersThemAsBooleans() { - $option = new ExceptionsOption(); + $option = new Exceptions(); $options = $this->getMock('Predis\Configuration\OptionsInterface'); $this->assertFalse($option->filter($options, null)); @@ -57,7 +57,7 @@ class ExceptionsOptionTest extends PredisTestCase */ public function testReturnsFalesOnValuesNotParsableAsBooleans() { - $option = new ExceptionsOption(); + $option = new Exceptions(); $options = $this->getMock('Predis\Configuration\OptionsInterface'); $this->assertFalse($option->filter($options, new \stdClass())); diff --git a/tests/Predis/Configuration/Option/PrefixTest.php b/tests/Predis/Configuration/Option/PrefixTest.php new file mode 100644 index 00000000..835fc743 --- /dev/null +++ b/tests/Predis/Configuration/Option/PrefixTest.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Configuration\Option; + +use PredisTestCase; + +/** + * + */ +class PrefixTest extends PredisTestCase +{ + /** + * @group disconnected + */ + public function testDefaultOptionValue() + { + $option = new Prefix(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + + $this->assertNull($option->getDefault($options)); + } + + /** + * @group disconnected + */ + public function testAcceptsStringAndReturnsCommandProcessor() + { + $option = new Prefix(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + + $prefix = $option->filter($options, $value = 'prefix:'); + + $this->assertInstanceOf('Predis\Command\Processor\ProcessorInterface', $prefix); + $this->assertInstanceOf('Predis\Command\Processor\KeyPrefixProcessor', $prefix); + $this->assertSame($value, $prefix->getPrefix()); + } + + /** + * @group disconnected + */ + public function testAcceptsCommandProcessorInstance() + { + $option = new Prefix(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $processor = $this->getMock('Predis\Command\Processor\ProcessorInterface'); + + $prefix = $option->filter($options, $processor); + + $this->assertSame($processor, $prefix); + } + + /** + * @group disconnected + */ + public function testAcceptsCallableReturningProcessorInterface() + { + $option = new Prefix(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $processor = $this->getMock('Predis\Command\Processor\ProcessorInterface'); + + $callable = $this->getMock('stdClass', array('__invoke')); + $callable->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface')) + ->will($this->returnValue($processor)); + + $prefix = $option->filter($options, $callable); + + $this->assertSame($processor, $prefix); + } + + /** + * @group disconnected + */ + public function testAcceptsCallableReturningStringPrefix() + { + $option = new Prefix(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + + $callable = $this->getMock('stdClass', array('__invoke')); + $callable->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface')) + ->will($this->returnValue('pfx:')); + + $prefix = $option->filter($options, $callable); + + $this->assertInstanceOf('Predis\Command\Processor\ProcessorInterface', $prefix); + $this->assertInstanceOf('Predis\Command\Processor\KeyPrefixProcessor', $prefix); + $this->assertSame('pfx:', $prefix->getPrefix()); + } + + /** + * @group disconnected + */ + public function testAcceptsObjectAsPrefixAndCastsToString() + { + $option = new Prefix(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + + $input = $this->getMock('stdClass', array('__toString')); + $input->expects($this->once()) + ->method('__toString') + ->will($this->returnValue('pfx:')); + + $prefix = $option->filter($options, $input); + + $this->assertInstanceOf('Predis\Command\Processor\ProcessorInterface', $prefix); + $this->assertInstanceOf('Predis\Command\Processor\KeyPrefixProcessor', $prefix); + $this->assertSame('pfx:', $prefix->getPrefix()); + } +} diff --git a/tests/Predis/Configuration/Option/ReplicationTest.php b/tests/Predis/Configuration/Option/ReplicationTest.php new file mode 100644 index 00000000..d9af6bee --- /dev/null +++ b/tests/Predis/Configuration/Option/ReplicationTest.php @@ -0,0 +1,190 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Configuration\Option; + +use PredisTestCase; + +/** + * + */ +class ReplicationTest extends PredisTestCase +{ + /** + * @group disconnected + */ + public function testDefaultOptionValue() + { + $option = new Replication(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + + $this->assertInstanceOf('Closure', $initializer = $option->getDefault($options)); + $this->assertInstanceOf('Predis\Connection\Replication\MasterSlaveReplication', $initializer($options)); + } + + /** + * @group disconnected + */ + public function testConfiguresAutomaticDiscoveryWhenAutodiscoveryOptionIsPresent() + { + $option = new Replication(); + + $connectionFactory = $this->getMock('Predis\Connection\FactoryInterface'); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $options->expects($this->at(0)) + ->method('__get') + ->with('autodiscovery') + ->will($this->returnValue(true)); + $options->expects($this->at(1)) + ->method('__get') + ->with('connections') + ->will($this->returnValue($connectionFactory)); + + $this->assertInstanceOf('Closure', $initializer = $option->getDefault($options)); + $this->assertInstanceOf('Predis\Connection\Replication\MasterSlaveReplication', $connection = $initializer($options)); + + // TODO: I know, I know... + $reflection = new \ReflectionProperty($connection, 'autoDiscovery'); + $reflection->setAccessible(true); + + $this->assertTrue($reflection->getValue($connection)); + } + + /** + * @group disconnected + */ + public function testAcceptsCallableAsConnectionInitializer() + { + $option = new Replication(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $connection = $this->getMock('Predis\Connection\AggregateConnectionInterface'); + + $callable = $this->getMock('stdClass', array('__invoke')); + $callable->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface')) + ->will($this->returnValue($connection)); + + $this->assertInstanceOf('Closure', $initializer = $option->filter($options, $callable)); + $this->assertSame($connection, $initializer($parameters = array())); + } + + /** + * @group disconnected + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Predis\Configuration\Option\Replication expects a valid connection type returned by callable initializer + */ + public function testThrowsExceptionOnInvalidReturnTypeOfConnectionInitializer() + { + $option = new Replication(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $connection = $this->getMock('Predis\Connection\NodeConnectionInterface'); + + $callable = $this->getMock('stdClass', array('__invoke')); + $callable->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface')) + ->will($this->returnValue($connection)); + + $this->assertInstanceOf('Closure', $initializer = $option->filter($options, $callable)); + + $initializer($parameters = array()); + } + + /** + * @group disconnected + */ + public function testAcceptsShortNameStringPredis() + { + $option = new Replication(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + + $this->assertInstanceOf('Closure', $initializer = $option->filter($options, 'predis')); + $this->assertInstanceOf('Predis\Connection\Replication\MasterSlaveReplication', $initializer($parameters = array())); + } + + /** + * @group disconnected + */ + public function testAcceptsShortNameStringRedis() + { + $option = new Replication(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $options->expects($this->at(0)) + ->method('__get') + ->with('service') + ->will($this->returnValue('mymaster')); + $options->expects($this->at(1)) + ->method('__get') + ->with('connections') + ->will($this->returnValue( + $this->getMock('Predis\Connection\FactoryInterface') + )); + + $parameters = array( + $this->getMock('Predis\Connection\NodeConnectionInterface'), + ); + + $this->assertInstanceOf('Closure', $initializer = $option->filter($options, 'sentinel')); + $this->assertInstanceOf('Predis\Connection\Replication\SentinelReplication', $connection = $initializer($parameters)); + + $this->assertSame($parameters[0], $connection->getSentinelConnection()); + } + + /** + * @group disconnected + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage String value for the replication option must be either `predis` or `sentinel` + */ + public function testThrowsExceptionOnInvalidShortNameString() + { + $option = new Replication(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + + $option->filter($options, 'unknown'); + } + + /** + * @group disconnected + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Predis\Configuration\Option\Replication expects a valid callable + */ + public function testThrowsExceptionOnBooleanValue() + { + $option = new Replication(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + + $option->filter($options, true); + } + + /** + * @group disconnected + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Predis\Configuration\Option\Replication expects a valid callable + */ + public function testThrowsExceptionOnInstanceOfReplicationInterface() + { + $option = new Replication(); + + $options = $this->getMock('Predis\Configuration\OptionsInterface'); + $connection = $this->getMock('Predis\Connection\Cluster\ClusterInterface'); + + $option->filter($options, $connection); + } +} diff --git a/tests/Predis/Configuration/OptionsTest.php b/tests/Predis/Configuration/OptionsTest.php index 0461ce17..e80e4f6c 100644 --- a/tests/Predis/Configuration/OptionsTest.php +++ b/tests/Predis/Configuration/OptionsTest.php @@ -25,12 +25,13 @@ class OptionsTest extends PredisTestCase { $options = new Options(); - $this->assertInstanceOf('Predis\Connection\FactoryInterface', $options->connections); - $this->assertInstanceOf('Predis\Command\FactoryInterface', $options->commands); - $this->assertInstanceOf('Predis\Connection\Cluster\ClusterInterface', $options->cluster); - $this->assertInstanceOf('Predis\Connection\Replication\ReplicationInterface', $options->replication); $this->assertTrue($options->exceptions); $this->assertNull($options->prefix); + $this->assertNull($options->aggregate); + $this->assertInstanceOf('Closure', $options->cluster); + $this->assertInstanceOf('Closure', $options->replication); + $this->assertInstanceOf('Predis\Command\FactoryInterface', $options->commands); + $this->assertInstanceOf('Predis\Connection\FactoryInterface', $options->connections); } /** @@ -38,21 +39,38 @@ class OptionsTest extends PredisTestCase */ public function testConstructorWithArrayArgument() { + $connection = $this->getMock('Predis\Connection\AggregateConnectionInterface'); + + $callable = $this->getMock('stdClass', array('__invoke')); + $callable->expects($this->any()) + ->method('__invoke') + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface')) + ->will($this->returnValue($connection)); + $options = new Options(array( 'exceptions' => false, 'prefix' => 'prefix:', 'commands' => $this->getMock('Predis\Command\FactoryInterface'), 'connections' => $this->getMock('Predis\Connection\FactoryInterface'), - 'cluster' => $this->getMock('Predis\Connection\Cluster\ClusterInterface'), - 'replication' => $this->getMock('Predis\Connection\Replication\ReplicationInterface'), + 'cluster' => $callable, + 'replication' => $callable, + 'aggregate' => $callable, )); $this->assertInternalType('bool', $options->exceptions); - $this->assertInstanceOf('Predis\Command\FactoryInterface', $options->commands); + $this->assertInstanceOf('Predis\Command\Processor\ProcessorInterface', $options->prefix); + $this->assertInstanceOf('Predis\Command\FactoryInterface', $options->commands); $this->assertInstanceOf('Predis\Connection\FactoryInterface', $options->connections); - $this->assertInstanceOf('Predis\Connection\Cluster\ClusterInterface', $options->cluster); - $this->assertInstanceOf('Predis\Connection\Replication\ReplicationInterface', $options->replication); + + $this->assertInstanceOf('Closure', $initializer = $options->aggregate); + $this->assertSame($connection, $initializer($options, array())); + + $this->assertInstanceOf('Closure', $initializer = $options->cluster); + $this->assertSame($connection, $initializer($options, array())); + + $this->assertInstanceOf('Closure', $initializer = $options->replication); + $this->assertSame($connection, $initializer($options, array())); } /** @@ -144,7 +162,7 @@ class OptionsTest extends PredisTestCase $callable = $this->getMock('stdClass', array('__invoke')); $callable->expects($this->once()) ->method('__invoke') - ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface'), 'commands') + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface')) ->will($this->returnValue($commands)); $options = new Options(array( @@ -166,7 +184,7 @@ class OptionsTest extends PredisTestCase $callable = $this->getMock('stdClass', array('__invoke')); $callable->expects($this->once()) ->method('__invoke') - ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface'), 'custom') + ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface')) ->will($this->returnValue($custom)); $options = new Options(array( @@ -190,7 +208,7 @@ class OptionsTest extends PredisTestCase }, true, false); try { - $options = new Options(array('prefix' => 'pfx')); + $options = new Options(array('custom' => 'value')); $pfx = $options->prefix; } catch (\Exception $_) { spl_autoload_unregister($autoload); diff --git a/tests/Predis/Configuration/PrefixOptionTest.php b/tests/Predis/Configuration/PrefixOptionTest.php deleted file mode 100644 index a4e67909..00000000 --- a/tests/Predis/Configuration/PrefixOptionTest.php +++ /dev/null @@ -1,60 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Predis\Configuration; - -use PredisTestCase; - -/** - * - */ -class PrefixOptionTest extends PredisTestCase -{ - /** - * @group disconnected - */ - public function testDefaultOptionValue() - { - $option = new PrefixOption(); - $options = $this->getMock('Predis\Configuration\OptionsInterface'); - - $this->assertNull($option->getDefault($options)); - } - - /** - * @group disconnected - */ - public function testAcceptsStringAndReturnsCommandProcessor() - { - $option = new PrefixOption(); - $options = $this->getMock('Predis\Configuration\OptionsInterface'); - - $return = $option->filter($options, $value = 'prefix:'); - - $this->assertInstanceOf('Predis\Command\Processor\ProcessorInterface', $return); - $this->assertInstanceOf('Predis\Command\Processor\KeyPrefixProcessor', $return); - $this->assertSame($value, $return->getPrefix()); - } - - /** - * @group disconnected - */ - public function testAcceptsCommandProcessorInstance() - { - $option = new PrefixOption(); - $options = $this->getMock('Predis\Configuration\OptionsInterface'); - $processor = $this->getMock('Predis\Command\Processor\ProcessorInterface'); - - $return = $option->filter($options, $processor); - - $this->assertSame($processor, $return); - } -} diff --git a/tests/Predis/Configuration/ReplicationOptionTest.php b/tests/Predis/Configuration/ReplicationOptionTest.php deleted file mode 100644 index 8483acab..00000000 --- a/tests/Predis/Configuration/ReplicationOptionTest.php +++ /dev/null @@ -1,95 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Predis\Configuration; - -use PredisTestCase; - -/** - * - */ -class ReplicationOptionTest extends PredisTestCase -{ - /** - * @group disconnected - */ - public function testDefaultOptionValue() - { - $option = new ReplicationOption(); - $options = $this->getMock('Predis\Configuration\OptionsInterface'); - - $this->assertInstanceOf('Predis\Connection\Replication\ReplicationInterface', $option->getDefault($options)); - $this->assertInstanceOf('Predis\Connection\Replication\MasterSlaveReplication', $option->getDefault($options)); - } - - /** - * @group disconnected - */ - public function testAcceptsValuesThatCanBeInterpretedAsBooleans() - { - $option = new ReplicationOption(); - $options = $this->getMock('Predis\Configuration\OptionsInterface'); - - $this->assertNull($option->filter($options, null)); - - $this->assertInstanceOf('Predis\Connection\Replication\ReplicationInterface', $option->filter($options, true)); - $this->assertNull($option->filter($options, false)); - - $this->assertInstanceOf('Predis\Connection\Replication\ReplicationInterface', $option->filter($options, 1)); - $this->assertNull($option->filter($options, 0)); - - $this->assertInstanceOf('Predis\Connection\Replication\ReplicationInterface', $option->filter($options, 'true')); - $this->assertNull($option->filter($options, 'false')); - - $this->assertInstanceOf('Predis\Connection\Replication\ReplicationInterface', $option->filter($options, 'on')); - $this->assertNull($option->filter($options, 'off')); - } - - /** - * @group disconnected - */ - public function testConfiguresAutomaticDiscoveryWhenAutodiscoveryOptionIsPresent() - { - $option = new ReplicationOption(); - $options = $this->getMock('Predis\Configuration\OptionsInterface'); - $connFactory = $this->getMock('Predis\Connection\FactoryInterface'); - - $options->expects($this->at(0)) - ->method('__get') - ->with('autodiscovery') - ->will($this->returnValue(true)); - $options->expects($this->at(1)) - ->method('__get') - ->with('connections') - ->will($this->returnValue($connFactory)); - - $replication = $option->getDefault($options); - - // TODO: I know, I know... - $reflection = new \ReflectionProperty($replication, 'autoDiscovery'); - $reflection->setAccessible(true); - - $this->assertTrue($reflection->getValue($replication)); - } - - /** - * @group disconnected - * @expectedException \InvalidArgumentException - */ - public function testThrowsExceptionOnInvalidInstanceType() - { - $option = new ReplicationOption(); - $options = $this->getMock('Predis\Configuration\OptionsInterface'); - $value = $this->getMock('Predis\Connection\NodeConnectionInterface'); - - $option->filter($options, $value); - } -}