diff --git a/composer.json b/composer.json index e309e98..af70c37 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "monospice/laravel-redis-sentinel-drivers", - "description": "Laravel configuration wrapper for highly-available Redis Sentinel replication.", - "keywords": ["redis", "sentinel", "laravel"], + "description": "Redis Sentinel integration for Laravel and Lumen.", + "keywords": ["redis", "sentinel", "laravel", "lumen"], "type": "library", "license": "MIT", "authors": [ @@ -13,6 +13,7 @@ "require": { "php": ">=5.6.4", "illuminate/cache": "^5.4", + "illuminate/contracts": "^5.4", "illuminate/queue": "^5.4", "illuminate/redis": "^5.4", "illuminate/session": "^5.4", @@ -22,6 +23,7 @@ }, "require-dev": { "laravel/framework": "^5.4", + "laravel/lumen-framework": "^5.4", "mockery/mockery": "0.9.*", "phpunit/phpunit": "^4.8" }, diff --git a/src/RedisSentinelServiceProvider.php b/src/RedisSentinelServiceProvider.php index 409c1bb..6c6f08d 100644 --- a/src/RedisSentinelServiceProvider.php +++ b/src/RedisSentinelServiceProvider.php @@ -4,7 +4,6 @@ use Illuminate\Cache\CacheManager; use Illuminate\Cache\RedisStore; -use Illuminate\Foundation\Application; use Illuminate\Queue\QueueManager; use Illuminate\Queue\Connectors\RedisConnector; use Illuminate\Session\CacheBasedSessionHandler; @@ -35,18 +34,18 @@ class RedisSentinelServiceProvider extends ServiceProvider public function boot() { $this->addRedisSentinelCacheDriver($this->app->make('cache')); - $this->addRedisSentinelSessionHandler($this->app->make('session')); $this->addRedisSentinelQueueConnector($this->app->make('queue')); + // Lumen removed session support since version 5.2, so we'll only bind + // the Sentinel session handler if we're running Laravel. + if (! $this->isLumenApplication()) { + $this->addRedisSentinelSessionHandler($this->app->make('session')); + } + // If we want Laravel's Redis API to use Sentinel, we'll remove the // "redis" service from the list of deferred services in the container: if ($this->shouldOverrideLaravelApi()) { - $deferredServices = $this->app->getDeferredServices(); - - unset($deferredServices['redis']); - unset($deferredServices['redis.connection']); - - $this->app->setDeferredServices($deferredServices); + $this->removeDeferredRedisServices(); } } @@ -77,7 +76,7 @@ public function register() /** * Replace the standard Laravel Redis service with the Redis Sentinel - * database driver so all redis operations use Sentinel connections. + * database driver so all Redis operations use Sentinel connections. * * @return void */ @@ -92,6 +91,26 @@ protected function registerOverrides() }); } + /** + * Remove the standard Laravel Redis service from the bound deferred + * services so they don't overwrite Redis Sentinel registrations. + * + * @return void + */ + protected function removeDeferredRedisServices() + { + if ($this->isLumenApplication()) { + return; + } + + $deferredServices = $this->app->getDeferredServices(); + + unset($deferredServices['redis']); + unset($deferredServices['redis.connection']); + + $this->app->setDeferredServices($deferredServices); + } + /** * Add "redis-sentinel" as an available driver option to the Laravel cache * manager. @@ -168,17 +187,36 @@ protected function shouldOverrideLaravelApi() /** * Get the fully-qualified class name of the RedisSentinelManager class - * for the current version of Laravel. + * for the current version of Laravel or Lumen. * * @return string The class name of the appropriate RedisSentinelManager * with its namespace */ protected function getVersionedRedisSentinelManagerClass() { - if (version_compare(Application::VERSION, '5.4.20', 'lt')) { + if ($this->isLumenApplication()) { + $appVersion = substr($this->app->version(), 7, 3); + $frameworkVersion = '5.4'; + } else { + $appVersion = \Illuminate\Foundation\Application::VERSION; + $frameworkVersion = '5.4.20'; + } + + if (version_compare($appVersion, $frameworkVersion, 'lt')) { return Manager\Laravel540RedisSentinelManager::class; } return Manager\Laravel5420RedisSentinelManager::class; } + + /** + * Determine if the current application runs the Lumen framework instead of + * Laravel. + * + * @return bool True if running Lumen + */ + protected function isLumenApplication() + { + return $this->app instanceof \Laravel\Lumen\Application; + } } diff --git a/tests/RedisSentinelFacadeTest.php b/tests/RedisSentinelFacadeTest.php index 87e8990..e98a148 100644 --- a/tests/RedisSentinelFacadeTest.php +++ b/tests/RedisSentinelFacadeTest.php @@ -2,19 +2,29 @@ namespace Monospice\LaravelRedisSentinel\Tests; -use Illuminate\Foundation\Application; use Mockery; use Monospice\LaravelRedisSentinel\RedisSentinel; use Monospice\LaravelRedisSentinel\RedisSentinelManager; +use Monospice\LaravelRedisSentinel\Tests\Support\ApplicationFactory; use PHPUnit_Framework_TestCase as TestCase; class RedisSentinelFacadeTest extends TestCase { + /** + * Run this cleanup after each test. + * + * @return void + */ + public function tearDown() + { + Mockery::close(); + } + public function testResolvesFacadeServiceFromContainer() { $service = RedisSentinelManager::class; + $app = ApplicationFactory::make(); - $app = new Application(); $app->singleton('redis-sentinel', function () use ($service) { return Mockery::mock($service); }); diff --git a/tests/RedisSentinelManagerTest.php b/tests/RedisSentinelManagerTest.php index 1fb8877..9941b1c 100644 --- a/tests/RedisSentinelManagerTest.php +++ b/tests/RedisSentinelManagerTest.php @@ -38,6 +38,16 @@ public function setUp() $this->manager = new RedisSentinelManager($this->versionedManagerMock); } + /** + * Run this cleanup after each test. + * + * @return void + */ + public function tearDown() + { + Mockery::close(); + } + public function testIsInitializable() { $this->assertInstanceOf(RedisSentinelManager::class, $this->manager); diff --git a/tests/RedisSentinelServiceProviderTest.php b/tests/RedisSentinelServiceProviderTest.php index 7e434eb..5bb268f 100644 --- a/tests/RedisSentinelServiceProviderTest.php +++ b/tests/RedisSentinelServiceProviderTest.php @@ -2,24 +2,20 @@ namespace Monospice\LaravelRedisSentinel\Tests; -use Illuminate\Cache\CacheServiceProvider; use Illuminate\Config\Repository as ConfigRepository; use Illuminate\Contracts\Redis\Factory as RedisFactory; -use Illuminate\Foundation\Application; -use Illuminate\Queue\QueueServiceProvider; use Illuminate\Redis\RedisManager; -use Illuminate\Redis\RedisServiceProvider; -use Illuminate\Session\SessionServiceProvider; use Monospice\LaravelRedisSentinel\RedisSentinelManager; use Monospice\LaravelRedisSentinel\RedisSentinelServiceProvider; +use Monospice\LaravelRedisSentinel\Tests\Support\ApplicationFactory; use PHPUnit_Framework_TestCase as TestCase; class RedisSentinelServiceProviderTest extends TestCase { /** - * An instance of the Laravel application container + * An instance of the Laravel or Lumen application container. * - * @var Application + * @var \Illuminate\Contracts\Container\Container */ protected $app; @@ -37,16 +33,9 @@ class RedisSentinelServiceProviderTest extends TestCase */ public function setUp() { - $this->app = new Application(); + $this->app = ApplicationFactory::make(); - $this->app->config = new ConfigRepository( - require(__DIR__ . '/stubs/config.php') - ); - - $this->app->register(new CacheServiceProvider($this->app)); - $this->app->register(new QueueServiceProvider($this->app)); - $this->app->register(new RedisServiceProvider($this->app)); - $this->app->register(new SessionServiceProvider($this->app)); + $this->app->config->set(require(__DIR__ . '/stubs/config.php')); $this->provider = new RedisSentinelServiceProvider($this->app); } @@ -83,6 +72,7 @@ public function testRegisterPreservesStandardRedisApi() public function testRegisterOverridesStandardRedisApi() { + $this->app->config->set('database.redis.driver', 'sentinel'); $this->provider->register(); $this->provider->boot(); @@ -112,6 +102,10 @@ public function testBootExtendsSessionHandlers() $this->provider->register(); $this->provider->boot(); - $this->assertNotNull($this->app->session->driver('redis-sentinel')); + if (ApplicationFactory::isLumen()) { + $this->assertFalse($this->app->bound('session')); + } else { + $this->assertNotNull($this->app->session->driver('redis-sentinel')); + } } } diff --git a/tests/Support/ApplicationFactory.php b/tests/Support/ApplicationFactory.php new file mode 100644 index 0000000..e389178 --- /dev/null +++ b/tests/Support/ApplicationFactory.php @@ -0,0 +1,101 @@ +version(), 7, 3); + } + + return \Illuminate\Foundation\Application::VERSION; + } + + /** + * Bootstrap a Laravel application instance to run tests against. + * + * @param bool $configure If TRUE, configure default application components + * for tests + * + * @return \Illuminate\Foundation\Application The Laravel application + * instance + */ + public static function makeLaravelApplication($configure = true) + { + $app = new \Illuminate\Foundation\Application(); + $app->register(new CacheServiceProvider($app)); + $app->register(new QueueServiceProvider($app)); + $app->register(new SessionServiceProvider($app)); + $app->register(new RedisServiceProvider($app)); + + $app->config = new ConfigRepository(); + + return $app; + } + + /** + * Bootstrap a Lumen application instance to run tests against. + * + * @param bool $configure If TRUE, configure default application components + * for tests + * + * @return \Laravel\Lumen\Application The Lumen application instance + */ + public static function makeLumenApplication($configure = true) + { + // Set the base path so the application doesn't attempt to use the + // package's config directory as the application config directory + // during tests: + $app = new \Laravel\Lumen\Application(__DIR__ . '/..'); + + if ($configure) { + $app->register(RedisServiceProvider::class); + $app->configure('database'); + $app->configure('cache'); + $app->configure('queue'); + } + + return $app; + } + + /** + * Determine if the test is running against a Laravel or Lumen framework. + * + * @return bool True if running the test against Lumen + */ + public static function isLumen() + { + return class_exists('Laravel\Lumen\Application'); + } +} diff --git a/tests/VersionedRedisSentinelManagerTest.php b/tests/VersionedRedisSentinelManagerTest.php index 11fc71a..0c4fea6 100644 --- a/tests/VersionedRedisSentinelManagerTest.php +++ b/tests/VersionedRedisSentinelManagerTest.php @@ -3,10 +3,10 @@ namespace Monospice\LaravelRedisSentinel\Tests; use Closure; -use Illuminate\Foundation\Application; use Illuminate\Redis\RedisManager; use InvalidArgumentException; use Monospice\LaravelRedisSentinel\Manager; +use Monospice\LaravelRedisSentinel\Tests\Support\ApplicationFactory; use PHPUnit_Framework_TestCase as TestCase; use Predis\Connection\Aggregate\SentinelReplication; @@ -36,11 +36,7 @@ public function setUp() $config = require(__DIR__ . '/stubs/config.php'); $config = $config['database']['redis-sentinel']; - if (version_compare(Application::VERSION, '5.4.20', 'lt')) { - $class = Manager\Laravel540RedisSentinelManager::class; - } else { - $class = Manager\Laravel5420RedisSentinelManager::class; - } + $class = $this->getVersionedRedisSentinelManagerClass(); $this->managerClass = $class; $this->manager = new $class('predis', $config); @@ -140,4 +136,28 @@ public function testFailsOnUnsupportedClientDriver() $manager->connection('test_connection'); } + + /** + * Get the fully-qualified class name of the RedisSentinelManager class + * for the current version of Laravel or Lumen under test. + * + * @return string The class name of the appropriate RedisSentinelManager + * with its namespace + */ + protected function getVersionedRedisSentinelManagerClass() + { + $appVersion = ApplicationFactory::getApplicationVersion(); + + if (ApplicationFactory::isLumen()) { + $frameworkVersion = '5.4'; + } else { + $frameworkVersion = '5.4.20'; + } + + if (version_compare($appVersion, $frameworkVersion, 'lt')) { + return Manager\Laravel540RedisSentinelManager::class; + } + + return Manager\Laravel5420RedisSentinelManager::class; + } } diff --git a/tests/stubs/config.php b/tests/stubs/config.php index 994acd3..2bb1536 100644 --- a/tests/stubs/config.php +++ b/tests/stubs/config.php @@ -67,7 +67,8 @@ 'driver' => 'redis-sentinel', 'connection' => 'connection1', 'queue' => 'default', - 'expire' => 60, + 'retry_after' => 90, + 'expire' => 90, // Legacy, Laravel < 5.4.30 ], ], ],