diff --git a/.travis.yml b/.travis.yml index 7db9ca1..f11f563 100644 --- a/.travis.yml +++ b/.travis.yml @@ -125,6 +125,7 @@ before_install: - if [ -n "$LUMEN_VERSION" ]; then composer remove --dev --no-update "laravel/framework"; fi - if [ -n "$LUMEN_VERSION" ]; then composer require --no-update "laravel/lumen-framework:$LUMEN_VERSION"; fi - if [ -z "$HORIZON" ]; then composer remove --dev --no-update "laravel/horizon"; fi + - if [[ ${TRAVIS_PHP_VERSION:0:1} == "7" ]]; then printf "\n" | pecl install -f redis; fi install: travis_retry composer install --prefer-dist --no-interaction --no-suggest diff --git a/README.md b/README.md index 3b1c6dc..ee51af7 100644 --- a/README.md +++ b/README.md @@ -59,10 +59,13 @@ Requirements - PHP 5.4 or greater - [Redis][redis] 2.8 or greater (for Sentinel support) - - [Predis][predis] 1.1 or greater (for Sentinel client support) - [Laravel][laravel] or [Lumen][lumen] 5.0 or greater (4.x doesn't support the required Predis version) +Driver options: + - [Predis][predis] 1.1 or greater (for Sentinel client support) + - [PhpRedis][php-redis] 5.3.4 or greater (for Sentinel client support) + **Note:** Laravel 5.4 introduced the ability to use the [PhpRedis][php-redis] extension as a Redis client for the framework. This package does not yet support the PhpRedis option. @@ -132,6 +135,7 @@ QUEUE_CONNECTION=redis-sentinel # Laravel >= 5.7 QUEUE_DRIVER=redis-sentinel # Laravel <= 5.6 REDIS_DRIVER=redis-sentinel +REDIS_CLIENT=predis REDIS_HOST=sentinel1.example.com, sentinel2.example.com, 10.0.0.1, etc. REDIS_PORT=26379 REDIS_SENTINEL_SERVICE=mymaster # or your Redis master group name @@ -409,6 +413,30 @@ for a single connection. The default values are shown below: ], ``` +The PhpRedis client supports extra options. The default values are shown below: + +```php +'options' => [ + ... + + // The default number of attempts to retry the connection if the inititial + // connection has failed. A value of 0 instructs the + // client to throw an exception after the first failed attempt, while a + // value of -1 causes the client to continue to retry commands indefinitely. + 'connector_retry_limit' => 20, + + // The default amount of time (in milliseconds) that the client waits before + // retrying the connection attempt. + 'connector_retry_wait' => 1000, + + // Sets the persistent option in the RedisSentinel class. + 'sentinel_persistent' => null, + + // Sets the read timeout option in the RedisSentinel class. 0 means unlimited. + 'sentinel_read_timeout' => 0, +], +``` + ### Broadcasting, Cache, Session, and Queue Drivers After configuring the Sentinel database connections, we can instruct Laravel to diff --git a/composer.json b/composer.json index 4cedab8..d5fc785 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,12 @@ { "name": "monospice/laravel-redis-sentinel-drivers", "description": "Redis Sentinel integration for Laravel and Lumen.", - "keywords": ["redis", "sentinel", "laravel", "lumen"], + "keywords": [ + "redis", + "sentinel", + "laravel", + "lumen" + ], "type": "library", "license": "MIT", "authors": [ @@ -12,21 +17,21 @@ ], "require": { "php": ">=5.6.4", - "illuminate/cache": "^5.4 || ^6.0 || ^7.0 || ^8.0", - "illuminate/contracts": "^5.4 || ^6.0 || ^7.0 || ^8.0", - "illuminate/queue": "^5.4 || ^6.0 || ^7.0 || ^8.0", - "illuminate/redis": "^5.4 || ^6.0 || ^7.0 || ^8.0", - "illuminate/session": "^5.4 || ^6.0 || ^7.0 || ^8.0", - "illuminate/support": "^5.4 || ^6.0 || ^7.0 || ^8.0", + "illuminate/cache": "^5.4 || ^6.0 || ^7.0 || ^8.0 || ^9.0", + "illuminate/contracts": "^5.4 || ^6.0 || ^7.0 || ^8.0 || ^9.0", + "illuminate/queue": "^5.4 || ^6.0 || ^7.0 || ^8.0 || ^9.0", + "illuminate/redis": "^5.4 || ^6.0 || ^7.0 || ^8.0 || ^9.0", + "illuminate/session": "^5.4 || ^6.0 || ^7.0 || ^8.0 || ^9.0", + "illuminate/support": "^5.4 || ^6.0 || ^7.0 || ^8.0 || ^9.0", "monospice/spicy-identifiers": "^0.1", "predis/predis": "^1.1" }, "require-dev": { - "laravel/framework": "^5.4 || ^6.0 || ^7.0 || ^8.0", - "laravel/horizon": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "laravel/lumen-framework": "^5.4 || ^6.0 || ^7.0 || ^8.0", + "laravel/framework": "^5.4 || ^6.0 || ^7.0 || ^8.0 || ^9.0", + "laravel/horizon": "^1.0 || ^2.0 || ^3.0 || ^4.0 || ^5.8", + "laravel/lumen-framework": "^5.4 || ^6.0 || ^7.0 || ^8.0 || ^9.0", "mockery/mockery": "^1.3", - "phpunit/phpunit": "^5.0" + "phpunit/phpunit": "^5.0 || ^9.5.10" }, "autoload": { "psr-4": { diff --git a/src/Configuration/HostNormalizer.php b/src/Configuration/HostNormalizer.php index 4a012ae..6c220bd 100644 --- a/src/Configuration/HostNormalizer.php +++ b/src/Configuration/HostNormalizer.php @@ -62,7 +62,7 @@ class HostNormalizer public static function normalizeConnections(array $connections) { foreach ($connections as $name => $connection) { - if ($name === 'options' || $name === 'clusters') { + if (in_array($name, ['client', 'options', 'clusters'])) { continue; } diff --git a/src/Connections/PhpRedisConnection.php b/src/Connections/PhpRedisConnection.php new file mode 100644 index 0000000..a6415ad --- /dev/null +++ b/src/Connections/PhpRedisConnection.php @@ -0,0 +1,227 @@ + + * @license See LICENSE file + * @link https://github.com/monospice/laravel-redis-sentinel-drivers + */ +class PhpRedisConnection extends LaravelPhpRedisConnection +{ + /** + * The connection creation callback. + * + * Laravel 5 does not store the connector by default. + * + * @var callable|null + */ + protected $connector; + + /** + * The number of times the client attempts to retry a command when it fails + * to connect to a Redis instance behind Sentinel. + * + * @var int + */ + protected $retryLimit = 20; + + /** + * The time in milliseconds to wait before the client retries a failed + * command. + * + * @var int + */ + protected $retryWait = 1000; + + /** + * Create a new PhpRedis connection. + * + * @param \Redis $client + * @param callable|null $connector + * @param array $sentinelOptions + * @return void + */ + public function __construct($client, callable $connector = null, array $sentinelOptions = []) + { + parent::__construct($client, $connector); + + // Set the connector when it is not set. Used for Laravel 5. + if (! $this->connector) { + $this->connector = $connector; + } + + // Set the retry limit. + if (isset($sentinelOptions['retry_limit']) && is_numeric($sentinelOptions['retry_limit'])) { + $this->retryLimit = (int) $sentinelOptions['retry_limit']; + } + + // Set the retry wait. + if (isset($sentinelOptions['retry_wait']) && is_numeric($sentinelOptions['retry_wait'])) { + $this->retryWait = (int) $sentinelOptions['retry_wait']; + } + } + + /** + * {@inheritdoc} in addition retry on client failure. + * + * @param mixed $cursor + * @param array $options + * @return mixed + */ + public function scan($cursor, $options = []) + { + return $this->retryOnFailure(function () use ($cursor, $options) { + return parent::scan($cursor, $options); + }); + } + + /** + * {@inheritdoc} in addition retry on client failure. + * + * @param string $key + * @param mixed $cursor + * @param array $options + * @return mixed + */ + public function zscan($key, $cursor, $options = []) + { + return $this->retryOnFailure(function () use ($key, $cursor, $options) { + parent::zscan($key, $cursor, $options); + }); + } + + /** + * {@inheritdoc} in addition retry on client failure. + * + * @param string $key + * @param mixed $cursor + * @param array $options + * @return mixed + */ + public function hscan($key, $cursor, $options = []) + { + return $this->retryOnFailure(function () use ($key, $cursor, $options) { + parent::hscan($key, $cursor, $options); + }); + } + + /** + * {@inheritdoc} in addition retry on client failure. + * + * @param string $key + * @param mixed $cursor + * @param array $options + * @return mixed + */ + public function sscan($key, $cursor, $options = []) + { + return $this->retryOnFailure(function () use ($key, $cursor, $options) { + parent::sscan($key, $cursor, $options); + }); + } + + /** + * {@inheritdoc} in addition retry on client failure. + * + * @param callable|null $callback + * @return \Redis|array + */ + public function pipeline(callable $callback = null) + { + return $this->retryOnFailure(function () use ($callback) { + return parent::pipeline($callback); + }); + } + + /** + * {@inheritdoc} in addition retry on client failure. + * + * @param callable|null $callback + * @return \Redis|array + */ + public function transaction(callable $callback = null) + { + return $this->retryOnFailure(function () use ($callback) { + return parent::transaction($callback); + }); + } + + /** + * {@inheritdoc} in addition retry on client failure. + * + * @param array|string $channels + * @param \Closure $callback + * @return void + */ + public function subscribe($channels, Closure $callback) + { + return $this->retryOnFailure(function () use ($channels, $callback) { + return parent::subscribe($channels, $callback); + }); + } + + /** + * {@inheritdoc} in addition retry on client failure. + * + * @param array|string $channels + * @param \Closure $callback + * @return void + */ + public function psubscribe($channels, Closure $callback) + { + return $this->retryOnFailure(function () use ($channels, $callback) { + return parent::psubscribe($channels, $callback); + }); + } + + /** + * {@inheritdoc} in addition retry on client failure. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public function command($method, array $parameters = []) + { + return $this->retryOnFailure(function () use ($method, $parameters) { + return parent::command($method, $parameters); + }); + } + + /** + * Attempt to retry the provided operation when the client fails to connect + * to a Redis server. + * + * @param callable $callback The operation to execute. + * @return mixed The result of the first successful attempt. + */ + protected function retryOnFailure(callable $callback) + { + return PhpRedisConnector::retryOnFailure($callback, $this->retryLimit, $this->retryWait, function () { + $this->client->close(); + + try { + if ($this->connector) { + $this->client = call_user_func($this->connector); + } + } catch (RedisException $e) { + // Ignore when the creation of a new client gets an exception. + // If this exception isn't caught the retry will stop. + } + }); + } +} diff --git a/src/Connectors/PhpRedisConnector.php b/src/Connectors/PhpRedisConnector.php new file mode 100644 index 0000000..4fa18c0 --- /dev/null +++ b/src/Connectors/PhpRedisConnector.php @@ -0,0 +1,279 @@ + + * @license See LICENSE file + * @link http://github.com/monospice/laravel-redis-sentinel-drivers + */ +class PhpRedisConnector extends LaravelPhpRedisConnector +{ + /** + * Holds the current sentinel servers. + * + * @var array + */ + protected $servers; + + /** + * The number of times the client attempts to retry a command when it fails + * to connect to a Redis instance behind Sentinel. + * + * @var int + */ + protected $connectorRetryLimit = 20; + + /** + * The time in milliseconds to wait before the client retries a failed + * command. + * + * @var int + */ + protected $connectorRetryWait = 1000; + + /** + * Configuration options specific to Sentinel connection operation + * + * Some of the Sentinel configuration options can be entered in this class. + * The retry_wait and retry_limit values are passed to the connection. + * + * @var array + */ + protected $sentinelKeys = [ + 'sentinel_timeout' => null, + 'retry_wait' => null, + 'retry_limit' => null, + 'update_sentinels' => null, + + 'sentinel_persistent' => null, + 'sentinel_read_timeout' => null, + ]; + + /** + * Instantiate the connector and check if the required extension is available. + */ + public function __construct() + { + if (! extension_loaded('redis')) { + throw new LogicException('Please make sure the PHP Redis extension is installed and enabled.'); + } + + if (! class_exists(RedisSentinel::class)) { + throw new LogicException('Please make sure the PHP Redis extension is up to date (5.3.4 or greater).'); + } + } + + /** + * Create a new Redis Sentinel connection from the provided configuration + * options + * + * @param array $server The client configuration for the connection + * @param array $options The global client options shared by all Sentinel + * connections + * + * @return PhpRedisConnection The Sentinel connection containing a configured + * PhpRedis Client + */ + public function connect(array $servers, array $options = [ ]) + { + // Set the initial Sentinel servers. + $this->servers = array_map(function ($server) { + return $this->formatServer($server); + }, $servers); + + // Set the connector retry limit. + if (isset($options['connector_retry_limit']) && is_numeric($options['connector_retry_limit'])) { + $this->connectorRetryLimit = (int) $options['connector_retry_limit']; + } + + // Set the connector retry wait. + if (isset($options['connector_retry_wait']) && is_numeric($options['connector_retry_wait'])) { + $this->connectorRetryWait = (int) $options['connector_retry_wait']; + } + + // Merge the global options shared by all Sentinel connections with + // connection-specific options + $clientOpts = array_merge($options, Arr::pull($servers, 'options', [ ])); + + // Extract the array of Sentinel connection options from the rest of + // the client options + $sentinelOpts = array_intersect_key($clientOpts, $this->sentinelKeys); + + // Filter the Sentinel connection options elements from the client + // options array + $clientOpts = array_diff_key($clientOpts, $this->sentinelKeys); + + // Create a client by calling the Sentinel servers + $connector = function () use ($options) { + return $this->createClientWithSentinel($options); + }; + + // Create a connection and retry if this fails. + $connection = self::retryOnFailure(function () use ($connector) { + return $connector(); + }, $this->connectorRetryLimit, $this->connectorRetryWait); + + return new PhpRedisConnection($connection, $connector, $sentinelOpts); + } + + /** + * Create the Redis client instance + * + * @param array $options + * @return Redis + * + * @throws LogicException + */ + protected function createClientWithSentinel(array $options) + { + $serverConfigurations = $this->servers; + $clientConfiguration = isset($options['parameters']) ? $options['parameters'] : []; + + $updateSentinels = isset($options['update_sentinels']) ? $options['update_sentinels'] : false; + $sentinelService = isset($options['service']) ? $options['service'] : 'mymaster'; + $sentinelTimeout = isset($options['sentinel_timeout']) ? $options['sentinel_timeout'] : 0; + $sentinelPersistent = isset($options['sentinel_persistent']) ? $options['sentinel_persistent'] : null; + $sentinelReadTimeout = isset($options['sentinel_read_timeout']) ? $options['sentinel_read_timeout'] : 0; + + // Shuffle the server configurations to perform some loadbalancing. + shuffle($serverConfigurations); + + // Try to connect to any of the servers. + foreach ($serverConfigurations as $idx => $serverConfiguration) { + $host = isset($serverConfiguration['host']) ? $serverConfiguration['host'] : 'localhost'; + $port = isset($serverConfiguration['port']) ? $serverConfiguration['port'] : 26379; + + // Create a connection to the Sentinel instance. Using a retry_interval of 0, retrying + // is done inside the PhpRedisConnection. Cannot seem to get the retry_interval to work: + // https://github.com/phpredis/phpredis/blob/37a90257d09b4efa75230769cf535484116b2b67/library.c#L343 + $sentinel = new RedisSentinel($host, $port, $sentinelTimeout, $sentinelPersistent, 0, $sentinelReadTimeout); + + try { + // Check if the Sentinel server list needs to be updated. + // Put the current server and the other sentinels in the server list. + if ($updateSentinels === true) { + $this->updateSentinels($sentinel, $host, $port, $sentinelService); + } + + // Lookup the master node. + $master = $sentinel->getMasterAddrByName($sentinelService); + if (is_array($master) && ! count($master)) { + throw new RedisException(sprintf('No master found for service "%s".', $sentinelService)); + } + + // Create a PhpRedis client for the selected master node. + return $this->createClient(array_merge($clientConfiguration, $serverConfiguration, [ + 'host' => $master[0], + 'port' => $master[1], + ])); + } catch (RedisException $e) { + // Rethrow the expection when the last server is reached. + if ($idx === count($serverConfigurations) - 1) { + throw $e; + } + } + } + } + + /** + * Update the list With sentinel servers. + * + * @param RedisSentinel $sentinel + * @param string $currentHost + * @param int $currentPort + * @param string $service + * @return void + */ + protected function updateSentinels(RedisSentinel $sentinel, string $currentHost, int $currentPort, string $service) + { + $this->servers = array_merge( + [ + [ + 'host' => $currentHost, + 'port' => $currentPort, + ] + ], array_map(function ($sentinel) { + return [ + 'host' => $sentinel['ip'], + 'port' => $sentinel['port'], + ]; + }, $sentinel->sentinels($service)) + ); + } + + /** + * Format a server. + * + * @param mixed $server + * @return array + * + * @throws RedisException + */ + protected function formatServer($server) + { + if (is_string($server)) { + list($host, $port) = explode(':', $server); + if (! $host || ! $port) { + throw new RedisException('Could not format the server definition.'); + } + + return ['host' => $host, 'port' => (int) $port]; + } + + if (! is_array($server)) { + throw new RedisException('Could not format the server definition.'); + } + + return $server; + } + + /** + * Retry the callback when a RedisException is catched. + * + * @param callable $callback The operation to execute. + * @param int $retryLimit The number of times the retry is performed. + * @param int $retryWait The time in milliseconds to wait before retrying again. + * @param callable $failureCallback The operation to execute when a failure happens. + * @return mixed The result of the first successful attempt. + * + * @throws RedisRetryException After exhausting the allowed number of + * attempts to connect. + */ + public static function retryOnFailure(callable $callback, int $retryLimit, int $retryWait, callable $failureCallback = null) + { + $attempts = 0; + $previousException = null; + + while ($attempts < $retryLimit) { + try { + return $callback(); + } catch (RedisException $exception) { + $previousException = $exception; + + if ($failureCallback) { + call_user_func($failureCallback); + } + + usleep($retryWait * 1000); + + $attempts++; + } + } + + throw new RedisRetryException(sprintf('Reached the (re)connect limit of %d attempts', $attempts), 0, $previousException); + } +} diff --git a/src/Exceptions/RedisRetryException.php b/src/Exceptions/RedisRetryException.php new file mode 100644 index 0000000..571de18 --- /dev/null +++ b/src/Exceptions/RedisRetryException.php @@ -0,0 +1,10 @@ +driver) { case 'predis': return new Connectors\PredisConnector(); + case 'phpredis': + return new Connectors\PhpRedisConnector(); } throw new InvalidArgumentException( diff --git a/tests/Integration/Connections/PhpRedisConnectionTest.php b/tests/Integration/Connections/PhpRedisConnectionTest.php new file mode 100644 index 0000000..87f4d9a --- /dev/null +++ b/tests/Integration/Connections/PhpRedisConnectionTest.php @@ -0,0 +1,178 @@ +subject = $this->makeConnection(); + } + + public function testAllowsTransactionsOnAggregateConnection() + { + if (! extension_loaded('redis')) { + $this->markTestSkipped('The redis extension is not installed. Please install the extension to enable '.__CLASS__); + + return; + } + + $transaction = $this->subject->transaction(); + + $this->assertInstanceOf(Redis::class, $transaction); + } + + public function testExecutesCommandsInTransaction() + { + if (! extension_loaded('redis')) { + $this->markTestSkipped('The redis extension is not installed. Please install the extension to enable '.__CLASS__); + + return; + } + + $result = $this->subject->transaction(function ($trans) { + $trans->set('test-key', 'test value'); + $trans->get('test-key'); + }); + + $this->assertCount(2, $result); + $this->assertTrue($result[0]); + $this->assertEquals('test value', $result[1]); + $this->assertRedisKeyEquals('test-key', 'test value'); + } + + public function testExecutesTransactionsOnMaster() + { + if (! extension_loaded('redis')) { + $this->markTestSkipped('The redis extension is not installed. Please install the extension to enable '.__CLASS__); + + return; + } + + $expectedSubset = ['role' => 'master']; + + $info = $this->subject->transaction(function ($transaction) { + $transaction->info(); + }); + + $this->assertArraySubset($expectedSubset, $info[0]); + } + + public function testAbortsTransactionOnException() + { + if (! extension_loaded('redis')) { + $this->markTestSkipped('The redis extension is not installed. Please install the extension to enable '.__CLASS__); + + return; + } + + $exception = null; + + try { + $this->subject->transaction(function ($trans) { + $trans->set('test-key', 'test value'); + throw new DummyException(); + }); + } catch (DummyException $exception) { + // With PHPUnit, we need to wrap the throwing block to perform + // assertions afterward. + } + + $this->assertNotNull($exception); + $this->assertRedisKeyEquals('test-key', null); + } + + public function testRetriesTransactionWhenConnectionFails() + { + if (! extension_loaded('redis')) { + $this->markTestSkipped('The redis extension is not installed. Please install the extension to enable '.__CLASS__); + + return; + } + + $this->expectException(RedisRetryException::class); + + $this->subject = $this->makeConnection(1, 0); // retry once and immediately + + $this->subject->transaction(function () { + throw new RedisException(); + }); + } + + public function testCanReconnectWhenConnectionFails() + { + if (! extension_loaded('redis')) { + $this->markTestSkipped('The redis extension is not installed. Please install the extension to enable '.__CLASS__); + + return; + } + + $retries = 3; + $attempts = 0; + + $this->subject = $this->makeConnection($retries, 0); // retry immediately + + $this->subject->transaction(function ($trans) use (&$attempts, $retries) { + $attempts++; + + if ($attempts < $retries) { + throw new RedisException(); + } else { + $trans->set('test-key', 'test value'); + } + }); + + $this->assertGreaterThan(1, $attempts, 'First try does not count.'); + $this->assertRedisKeyEquals('test-key', 'test value'); + } + + /** + * Initialize a PhpRedis client using the test connection configuration + * that can verify connectivity failure handling. + * + * @param int|null $retryLimit + * @param int|null $retryWait + * @return PhpRedisConnection A client instance for the subject under test. + */ + protected function makeConnection(int $retryLimit = null, int $retryWait = null) + { + $connector = new PhpRedisConnector(); + + $options = $this->config['options']; + if ($retryLimit !== null) { + $options['retry_limit'] = $retryLimit; + } + + if ($retryWait !== null) { + $options['retry_wait'] = $retryWait; + } + + return $connector->connect($this->config['default'], $options); + } +} diff --git a/tests/Integration/Connectors/PhpRedisConnectorTest.php b/tests/Integration/Connectors/PhpRedisConnectorTest.php new file mode 100644 index 0000000..0f36bfb --- /dev/null +++ b/tests/Integration/Connectors/PhpRedisConnectorTest.php @@ -0,0 +1,67 @@ +markTestSkipped('The redis extension is not installed. Please install the extension to enable '.__CLASS__); + + return; + } + + $connector = new PhpRedisConnector(); + $client = $connector->connect($this->config['default'], $this->config['options']); + + $this->assertInstanceOf(PhpRedisConnection::class, $client); + } + + public function testRetriesTransactionWhenConnectionFails() + { + if (! extension_loaded('redis')) { + $this->markTestSkipped('The redis extension is not installed. Please install the extension to enable '.__CLASS__); + + return; + } + + $this->expectException(RedisRetryException::class); + + $connector = new PhpRedisConnector(); + + $servers = [ + [ + 'host' => '127.0.0.1', + 'port' => 1111, + ], + ]; + + $options = array_merge([ + 'connector_retry_limit' => 3, + 'connector_retry_wait' => 0, + ]); + + $connector = new PhpRedisConnector(); + $connector->connect($servers, $options); + } +} diff --git a/tests/Unit/Configuration/LoaderTest.php b/tests/Unit/Configuration/LoaderTest.php index 5fc8a37..96ed1d9 100644 --- a/tests/Unit/Configuration/LoaderTest.php +++ b/tests/Unit/Configuration/LoaderTest.php @@ -499,9 +499,9 @@ public function testSkipsLoadingHorizonConfigurationIfCached() public function testDisallowsUsingNonexistantConnectionForHorizon() { - $this->config->set('horizon.use', 'not-a-connection'); + $this->expectException(UnexpectedValueException::class); - $this->setExpectedException(UnexpectedValueException::class); + $this->config->set('horizon.use', 'not-a-connection'); $this->loader->loadHorizonConfiguration(); } diff --git a/tests/Unit/Connections/PredisConnectionTest.php b/tests/Unit/Connections/PredisConnectionTest.php index a5e384e..c1c6e30 100644 --- a/tests/Unit/Connections/PredisConnectionTest.php +++ b/tests/Unit/Connections/PredisConnectionTest.php @@ -102,7 +102,7 @@ public function testSetsSentinelConnectionOptionsFromConfig() public function testDisallowsInvalidSentinelOptions() { - $this->setExpectedException(BadMethodCallException::class); + $this->expectException(BadMethodCallException::class); new PredisConnection($this->clientMock, [ 'not_an_option' => null ]); } diff --git a/tests/Unit/Manager/VersionedRedisSentinelManagerTest.php b/tests/Unit/Manager/VersionedRedisSentinelManagerTest.php index d6764b3..0a05005 100644 --- a/tests/Unit/Manager/VersionedRedisSentinelManagerTest.php +++ b/tests/Unit/Manager/VersionedRedisSentinelManagerTest.php @@ -2,7 +2,6 @@ namespace Monospice\LaravelRedisSentinel\Tests\Unit; -use Closure; use Illuminate\Contracts\Container\Container; use Illuminate\Contracts\Redis\Factory as RedisFactory; use Illuminate\Redis\RedisManager; @@ -118,23 +117,23 @@ public function testCreatesSingleClientsWithIndividualConfig() public function testDisallowsRedisClusterConnections() { - $this->setExpectedException(InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $this->subject->connection('clustered_connection'); } public function testFailsOnUndefinedConnection() { - $this->setExpectedException(InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $this->subject->connection('nonexistant_connection'); } public function testFailsOnUnsupportedClientDriver() { - $this->setExpectedException(InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); - $manager = $this->makeSubject('phpredis', [ + $manager = $this->makeSubject('fakeredis', [ 'test_connection' => [ ], ]); diff --git a/tests/Unit/RedisSentinelServiceProviderTest.php b/tests/Unit/RedisSentinelServiceProviderTest.php index 1833453..e2c1fdc 100644 --- a/tests/Unit/RedisSentinelServiceProviderTest.php +++ b/tests/Unit/RedisSentinelServiceProviderTest.php @@ -200,10 +200,11 @@ public function testBootExtendsSessionHandlers() public function testWaitsForBoot() { + $this->expectException(\InvalidArgumentException::class); + $this->app->config->set('redis-sentinel.auto_boot', false); $this->provider->register(); - $this->setExpectedException(\InvalidArgumentException::class); // It didn't auto boot $this->assertNull($this->app->cache->store('redis-sentinel'));