diff --git a/.travis.yml b/.travis.yml index 5001b38..7daef6b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,4 @@ language: php - dist: bionic php: @@ -8,21 +7,27 @@ php: - 7.4 env: - - REDIS_HOST=localhost + - REDIS_HOST=localhost REBLOOM_VERSION=2.2.0 # Redis 5.0.7 + - REDIS_HOST=localhost REBLOOM_VERSION=2.2.4 # Redis 6.0.5 + - REDIS_HOST=localhost REBLOOM_VERSION=edge + +jobs: + allow_failures: + - env: REDIS_HOST=localhost REBLOOM_VERSION=edge services: - docker before_install: - - docker pull redislabs/rebloom:2.2.0 - - docker run -d -p 127.0.0.1:6379:6379 --name redis redislabs/rebloom:2.2.0 - - yes | pecl install igbinary redis || true + - docker pull redislabs/rebloom:${REBLOOM_VERSION} + - docker run -d -p 127.0.0.1:6379:6379 --name redis redislabs/rebloom:${REBLOOM_VERSION} + - yes | pecl upgrade igbinary redis || true - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini before_script: - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter - chmod +x ./cc-test-reporter - - if [ $(phpenv version-name) = "7.4" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then ./cc-test-reporter before-build; fi + - if [ $(phpenv version-name) = "7.4" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$REBLOOM_VERSION" == "2.2.4" ]; then ./cc-test-reporter before-build; fi script: - composer install @@ -32,4 +37,4 @@ script: after_script: - docker stop redis - docker rm redis - - if [ $(phpenv version-name) = "7.4" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then ./cc-test-reporter after-build --coverage-input-type clover --id $CC_TEST_REPORTER_ID --exit-code $TRAVIS_TEST_RESULT; fi + - if [ $(phpenv version-name) = "7.4" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$REBLOOM_VERSION" == "2.2.4" ]; then ./cc-test-reporter after-build --coverage-input-type clover --id $CC_TEST_REPORTER_ID --exit-code $TRAVIS_TEST_RESULT; fi diff --git a/Dockerfile b/Dockerfile index 26a4fac..e90e3a5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,4 +14,3 @@ RUN pecl install redis && \ RUN wget https://github.com/composer/composer/releases/download/1.9.1/composer.phar -q &&\ mv composer.phar /usr/bin/composer && \ chmod +x /usr/bin/composer - diff --git a/README.md b/README.md index ab6eb5f..7bab107 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,15 @@ Use [Redis Bloom](https://oss.redislabs.com/redisbloom/) with PHP! -![Code Climate maintainability](https://img.shields.io/codeclimate/coverage-letter/palicao/phpRebloom?label=maintainability&logo=code-climate) -![Code Climate coverage](https://img.shields.io/codeclimate/coverage/palicao/phpRebloom?logo=code-climate) +[![Code Climate maintainability](https://img.shields.io/codeclimate/coverage-letter/palicao/phpRebloom?label=maintainability&logo=code-climate)](https://codeclimate.com/github/palicao/phpRebloom) +[![Code Climate coverage](https://img.shields.io/codeclimate/coverage/palicao/phpRebloom?logo=code-climate)](https://codeclimate.com/github/palicao/phpRebloom) [![Build Status](https://travis-ci.com/palicao/phpRebloom.svg?branch=master)](https://travis-ci.com/palicao/phpRebloom) [![Latest Stable Version](https://img.shields.io/packagist/v/palicao/php-rebloom.svg)](https://packagist.org/packages/palicao/php-rebloom) -![PHP version](https://img.shields.io/packagist/php-v/palicao/php-rebloom/0.1.0) -![GitHub](https://img.shields.io/github/license/palicao/phpRebloom) +[![PHP version](https://img.shields.io/packagist/php-v/palicao/php-rebloom/0.1.0)]((https://packagist.org/packages/palicao/php-rebloom)) +[![GitHub](https://img.shields.io/github/license/palicao/phpRebloom)](https://github.com/palicao/phpRebloom/blob/master/LICENSE) -Disclaimer: this is a very lightweight library. For a battery-included experience, -please checkout: https://github.com/averias/phpredis-bloom +Disclaimer: this is a very lightweight library. For a battery-included experience, +please checkout: https://github.com/averias/phpredis-bloom ## Install `composer require palicao/php-rebloom` diff --git a/docker-compose.yml b/docker-compose.yml index 24ee35b..3238978 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,6 +11,4 @@ services: - .:/app redis: container_name: redis - image: redislabs/rebloom:2.2.0 - - + image: redislabs/rebloom:2.2.4 diff --git a/src/BaseFrequencyCounter.php b/src/BaseFrequencyCounter.php index fd3ec82..989a59f 100644 --- a/src/BaseFrequencyCounter.php +++ b/src/BaseFrequencyCounter.php @@ -38,4 +38,4 @@ protected function parseResult($result): bool { return $result === 'OK' ? true : (bool) $result; } -} \ No newline at end of file +} diff --git a/src/Exception/RedisAuthenticationException.php b/src/Exception/RedisAuthenticationException.php new file mode 100644 index 0000000..15ca34e --- /dev/null +++ b/src/Exception/RedisAuthenticationException.php @@ -0,0 +1,10 @@ +redis = $redis; - $this->connectionParams = $connectionParams; } /** * @throws RedisClientException + * @throws RedisAuthenticationException */ private function connectIfNeeded(): void { @@ -62,9 +63,13 @@ private function connectIfNeeded(): void $this->redis->getLastError() ?? 'unknown error' )); } + + $this->authenticate($params->getUsername(), $params->getPassword()); } /** + * @noinspection PhpDocRedundantThrowsInspection + * * @param array $params * @return mixed * @throws RedisException @@ -73,9 +78,40 @@ private function connectIfNeeded(): void public function executeCommand(array $params) { $this->connectIfNeeded(); - // UNDOCUMENTED FEATURE: option 8 is REDIS_OPT_REPLY_LITERAL + $value = (PHP_VERSION_ID < 70300) ? '1' : 1; - $this->redis->setOption(8, $value); + $this->redis->setOption(Redis::OPT_REPLY_LITERAL, $value); + return $this->redis->rawCommand(...$params); } + + /** + * @param string|null $username + * @param string|null $password + * @throws RedisAuthenticationException + */ + private function authenticate(?string $username, ?string $password): void + { + try { + if ($password) { + if ($username) { + // Calling auth() with an array throws a TypeError in some cases + /** @noinspection PhpMethodParametersCountMismatchInspection */ + $result = $this->redis->rawCommand('AUTH', $username, $password); + } else { + /** @psalm-suppress PossiblyNullArgument */ + $result = $this->redis->auth($password); + } + if ($result === false) { + throw new RedisAuthenticationException(sprintf( + 'Failure authenticating user %s', $username ?: 'default' + )); + } + } + } /** @noinspection PhpRedundantCatchClauseInspection */ catch (RedisException $e) { + throw new RedisAuthenticationException(sprintf( + 'Failure authenticating user %s: %s', $username ?: 'default', $e->getMessage() + )); + } + } } diff --git a/src/RedisConnectionParams.php b/src/RedisConnectionParams.php index 316407c..8d15d99 100644 --- a/src/RedisConnectionParams.php +++ b/src/RedisConnectionParams.php @@ -23,11 +23,25 @@ final class RedisConnectionParams /** @var float */ private $readTimeout; - public function __construct(string $host = '127.0.0.1', int $port = 6379) + /** @var string|null */ + private $username; + + /** @var string|null */ + private $password; + + /** + * @param string $host + * @param int $port + * @param string|null $username Only supported by Redis 6 + * @param string|null $password + */ + public function __construct(string $host = '127.0.0.1', int $port = 6379, string $username = null, string $password = null) { $this->persistentConnection = false; $this->host = $host; $this->port = $port; + $this->username = $username; + $this->password = $password; $this->timeout = 0; $this->retryInterval = 0; $this->readTimeout = 0.0; @@ -106,4 +120,14 @@ public function getReadTimeout(): float { return $this->readTimeout; } + + public function getUsername(): ?string + { + return $this->username; + } + + public function getPassword(): ?string + { + return $this->password; + } } diff --git a/tests/Integration/AuthRedis5Test.php b/tests/Integration/AuthRedis5Test.php new file mode 100644 index 0000000..1e01fad --- /dev/null +++ b/tests/Integration/AuthRedis5Test.php @@ -0,0 +1,54 @@ + 5) { + self::markTestSkipped('This test is supposed to run on Redis version 5 or lower'); + } + parent::setUp(); + } + + public function testAuthWithPasswordSuccess(): void + { + $this->redisClient->executeCommand(['CONFIG', 'SET', 'requirepass', 'pass123']); + $this->redis->close(); + $connectionParams = new RedisConnectionParams(self::getHost(), self::getPort(), null, 'pass123'); + $authorizedRedisClient = new RedisClient(new Redis(), $connectionParams); + $bloomFilter = new BloomFilter($authorizedRedisClient); + $result = $bloomFilter->reserve('reserveTest', .0001, 100); + self::assertTrue($result); + $authorizedRedisClient->executeCommand(['CONFIG', 'SET', 'requirepass', '']); + } + + public function testAuthWithPasswordFailure(): void + { + $this->expectException(RedisAuthenticationException::class); + $this->redisClient->executeCommand(['CONFIG', 'SET', 'requirepass', 'pass123']); + $this->redis->close(); + $connectionParams = new RedisConnectionParams(self::getHost(), self::getPort(), null, 'foobar'); + $nonAuthorizedRedisClient = new RedisClient(new Redis(), $connectionParams); + $bloomFilter = new BloomFilter($nonAuthorizedRedisClient); + try { + $bloomFilter->reserve('reserveTest', .0001, 100); + } catch (RedisAuthenticationException $exception) { + throw $exception; + } finally { + $connectionParams = new RedisConnectionParams(self::getHost(), self::getPort(), null, 'pass123'); + $authorizedRedisClient = new RedisClient(new Redis(), $connectionParams); + $authorizedRedisClient->executeCommand(['CONFIG', 'SET', 'requirepass', '']); + } + } +} diff --git a/tests/Integration/AuthRedis6Test.php b/tests/Integration/AuthRedis6Test.php new file mode 100644 index 0000000..d62fde6 --- /dev/null +++ b/tests/Integration/AuthRedis6Test.php @@ -0,0 +1,85 @@ +redisClient->executeCommand(['ACL', 'SETUSER', 'default', '>pass123', '+@all']); + $this->redis->close(); + $connectionParams = new RedisConnectionParams(self::getHost(), self::getPort(), null, 'pass123'); + $authorizedRedisClient = new RedisClient(new Redis(), $connectionParams); + $bloomFilter = new BloomFilter($authorizedRedisClient); + $result = $bloomFilter->reserve('reserveTest', .0001, 100); + self::assertTrue($result); + $authorizedRedisClient->executeCommand(['ACL', 'SETUSER', 'default', 'nopass']); + } + + public function testAuthWithPasswordFailure(): void + { + $this->expectException(RedisAuthenticationException::class); + $this->redisClient->executeCommand(['ACL', 'SETUSER', 'default', '>pass123', '+@all']); + $this->redis->close(); + $connectionParams = new RedisConnectionParams(self::getHost(), self::getPort(), null, 'foobar'); + $nonAuthorizedRedisClient = new RedisClient(new Redis(), $connectionParams); + $bloomFilter = new BloomFilter($nonAuthorizedRedisClient); + try { + $bloomFilter->reserve('reserveTest', .0001, 100); + } catch (RedisAuthenticationException $exception) { + throw $exception; + } finally { + $connectionParams = new RedisConnectionParams(self::getHost(), self::getPort(), null, 'pass123'); + $authorizedRedisClient = new RedisClient(new Redis(), $connectionParams); + $authorizedRedisClient->executeCommand(['ACL', 'SETUSER', 'default', 'nopass']); + } + } + + public function testAuthWithUsernameAndPasswordSuccess(): void + { + $this->redisClient->executeCommand(['ACL', 'SETUSER', 'username', 'on', '>pass123', '~*', '+@all']); + $this->redis->close(); + $connectionParams = new RedisConnectionParams(self::getHost(), self::getPort(), 'username', 'pass123'); + $authorizedRedisClient = new RedisClient(new Redis(), $connectionParams); + $bloomFilter = new BloomFilter($authorizedRedisClient); + $result = $bloomFilter->reserve('reserveTest', .0001, 100); + self::assertTrue($result); + $authorizedRedisClient->executeCommand(['ACL', 'DELUSER', 'username']); + } + + public function testAuthWithUsernameAndPasswordFailure(): void + { + $this->expectException(RedisAuthenticationException::class); + $this->redisClient->executeCommand(['ACL', 'SETUSER', 'username', 'on', '>pass123', '~*', '+@all']); + $this->redis->close(); + $connectionParams = new RedisConnectionParams(self::getHost(), self::getPort(), 'username', 'foobar'); + $nonAuthorizedRedisClient = new RedisClient(new Redis(), $connectionParams); + $bloomFilter = new BloomFilter($nonAuthorizedRedisClient); + try { + $bloomFilter->reserve('reserveTest', .0001, 100); + } catch (RedisAuthenticationException $exception) { + throw $exception; + } finally { + $connectionParams = new RedisConnectionParams(self::getHost(), self::getPort(), 'username', 'pass123'); + $authorizedRedisClient = new RedisClient(new Redis(), $connectionParams); + $authorizedRedisClient->executeCommand(['ACL', 'DELUSER', 'username']); + } + } +} diff --git a/tests/Integration/BloomFilterIntegrationTest.php b/tests/Integration/BloomFilterIntegrationTest.php index 40e072c..c151317 100644 --- a/tests/Integration/BloomFilterIntegrationTest.php +++ b/tests/Integration/BloomFilterIntegrationTest.php @@ -26,21 +26,21 @@ public function testReserveCreatesKey(): void { $this->sut->reserve('reserveTest', .0001, 100); $result = (bool)$this->redis->exists('reserveTest'); - $this->assertTrue($result); + self::assertTrue($result); } public function testInsert(): void { $this->sut->insert('insertTest', 'foo', .0001, 100); - $this->assertTrue($this->sut->exists('insertTest', 'foo')); - $this->assertFalse($this->sut->exists('insertTest', 'bar')); + self::assertTrue($this->sut->exists('insertTest', 'foo')); + self::assertFalse($this->sut->exists('insertTest', 'bar')); } public function testInsertWithoutErrorAndCapacity(): void { $this->sut->insert('insert2Test', 'foo'); - $this->assertTrue($this->sut->exists('insert2Test', 'foo')); - $this->assertFalse($this->sut->exists('insert2Test', 'bar')); + self::assertTrue($this->sut->exists('insert2Test', 'foo')); + self::assertFalse($this->sut->exists('insert2Test', 'bar')); } public function testErrorOutOfBounds(): void @@ -52,20 +52,20 @@ public function testErrorOutOfBounds(): void public function testInsertMany(): void { $this->sut->insertMany('insertManyTest', ['pear', 'orange', 'banana'], .0001, 100); - $this->assertTrue($this->sut->exists('insertManyTest', 'orange')); - $this->assertFalse($this->sut->exists('insertManyTest', 'pineapple')); + self::assertTrue($this->sut->exists('insertManyTest', 'orange')); + self::assertFalse($this->sut->exists('insertManyTest', 'pineapple')); } public function testInsertManyEmpty(): void { - $this->assertEquals([], $this->sut->insertMany('insertManyTest', [])); + self::assertEquals([], $this->sut->insertMany('insertManyTest', [])); } public function testInsertManyWithoutErrorAndCapacity(): void { $this->sut->insertMany('insertMany2Test', ['pear', 'orange', 'banana']); - $this->assertTrue($this->sut->exists('insertMany2Test', 'orange')); - $this->assertFalse($this->sut->exists('insertMany2Test', 'pineapple')); + self::assertTrue($this->sut->exists('insertMany2Test', 'orange')); + self::assertFalse($this->sut->exists('insertMany2Test', 'pineapple')); } public function testInsertIfKeyExists(): void @@ -73,7 +73,7 @@ public function testInsertIfKeyExists(): void $key = 'insertIfKeyExistsTest'; $this->sut->reserve($key, .0001, 100); $this->sut->insertIfKeyExists($key, 'kiwi'); - $this->assertTrue($this->sut->exists($key, 'kiwi')); + self::assertTrue($this->sut->exists($key, 'kiwi')); } public function testInsertManyIfKeyExists(): void @@ -81,8 +81,8 @@ public function testInsertManyIfKeyExists(): void $key = 'insertManyIfKeyExistsTest'; $this->sut->reserve($key, .0001, 100); $this->sut->insertManyIfKeyExists($key, ['pear', 'orange', 'banana']); - $this->assertTrue($this->sut->exists($key, 'orange')); - $this->assertFalse($this->sut->exists($key, 'pineapple')); + self::assertTrue($this->sut->exists($key, 'orange')); + self::assertFalse($this->sut->exists($key, 'pineapple')); } public function testInsertIfKeyExistsOnNonExistingKey(): void @@ -101,9 +101,9 @@ public function testManyExists(): void { $key = 'manyExistsTest'; $this->sut->insertMany($key, ['pear', 'orange', 'banana']); - $this->assertEquals([true, true], $this->sut->manyExist($key, ['orange', 'banana'])); - $this->assertEquals([false, false], $this->sut->manyExist($key, ['pineapple', 'strawberry'])); - $this->assertEquals([false, true], $this->sut->manyExist($key, ['watermelon', 'orange'])); + self::assertEquals([true, true], $this->sut->manyExist($key, ['orange', 'banana'])); + self::assertEquals([false, false], $this->sut->manyExist($key, ['pineapple', 'strawberry'])); + self::assertEquals([false, true], $this->sut->manyExist($key, ['watermelon', 'orange'])); } public function testCopy(): void @@ -115,6 +115,6 @@ public function testCopy(): void 'copyTo', ['pear', 'orange', 'banana', 'pineapple', 'strawberry', 'watermelon'] ); - $this->assertEquals($expected, $returned); + self::assertEquals($expected, $returned); } } diff --git a/tests/Integration/CountMinSketchIntegrationTest.php b/tests/Integration/CountMinSketchIntegrationTest.php index 4d4c950..943e306 100644 --- a/tests/Integration/CountMinSketchIntegrationTest.php +++ b/tests/Integration/CountMinSketchIntegrationTest.php @@ -28,22 +28,22 @@ public function testInitByDimensions(): void { $key = 'initByDim'; $result = $this->sut->initByDimensions($key, 3000, 40); - $this->assertTrue($result); + self::assertTrue($result); $info = $this->sut->info($key); - $this->assertEquals($key, $info->getKey()); - $this->assertEquals(3000, $info->getWidth()); - $this->assertEquals(40, $info->getDepth()); + self::assertEquals($key, $info->getKey()); + self::assertEquals(3000, $info->getWidth()); + self::assertEquals(40, $info->getDepth()); } public function testInitByProbability(): void { $key = 'initByProb'; $result = $this->sut->initByProbability($key, .001, .01); - $this->assertTrue($result); + self::assertTrue($result); $info = $this->sut->info($key); - $this->assertEquals($key, $info->getKey()); - $this->assertEquals(2000, $info->getWidth()); - $this->assertEquals(7, $info->getDepth()); + self::assertEquals($key, $info->getKey()); + self::assertEquals(2000, $info->getWidth()); + self::assertEquals(7, $info->getDepth()); } public function testIncrementBy(): void @@ -53,11 +53,11 @@ public function testIncrementBy(): void $result1 = $this->sut->incrementBy($key, new Pair('a', 100), new Pair('b', 200)); $result2 = $this->sut->incrementBy($key, new Pair('a', 20), new Pair('b', 10)); - $this->assertTrue($result1); - $this->assertTrue($result2); + self::assertTrue($result1); + self::assertTrue($result2); $expected = [new Pair('a', 120), new Pair('b', 210)]; - $this->assertEquals($expected, $this->sut->query($key, 'a', 'b')); + self::assertEquals($expected, $this->sut->query($key, 'a', 'b')); } public function testIncrementByOnNonExistingKey(): void @@ -83,10 +83,10 @@ public function testMerge(): void $this->sut->initByDimensions('destination', 3000, 40); $result = $this->sut->merge('destination', ['source1' => 3, 'source2' => 5]); - $this->assertTrue($result); + self::assertTrue($result); $expected = [new Pair('a', 130), new Pair('b', 60), new Pair('c', 150)]; - $this->assertEquals($expected, $this->sut->query('destination', 'a', 'b', 'c')); + self::assertEquals($expected, $this->sut->query('destination', 'a', 'b', 'c')); } public function testMergeNonExistingKey(): void @@ -104,7 +104,6 @@ public function testInfo(): void $expected = new CountMinSketchInfo($key, 3000, 40, 30); $result = $this->sut->info($key); - $this->assertEquals($expected, $result); + self::assertEquals($expected, $result); } - -} \ No newline at end of file +} diff --git a/tests/Integration/CuckooFilterIntegrationTest.php b/tests/Integration/CuckooFilterIntegrationTest.php index ed97651..cbc63b8 100644 --- a/tests/Integration/CuckooFilterIntegrationTest.php +++ b/tests/Integration/CuckooFilterIntegrationTest.php @@ -24,19 +24,19 @@ public function setUp(): void public function testReserveAndInfo(): void { $key = 'reserve'; - $this->assertTrue($this->sut->reserve($key, 200, 10, 20, 2)); - $this->assertTrue((bool)$this->redis->exists($key)); + self::assertTrue($this->sut->reserve($key, 200, 10, 20, 2)); + self::assertTrue((bool)$this->redis->exists($key)); $info = $this->sut->info($key); - $this->assertEquals($key, $info->getKey(), 'Wrong key'); - $this->assertEquals(376, $info->getSize(), 'Wrong size'); - $this->assertEquals(10, $info->getBucketSize(), 'Wrong bucket size'); - $this->assertEquals(0, $info->getDeletedItems(), 'Wrong deleted items'); - $this->assertEquals(0, $info->getInsertedItems(), 'Wrong inserted items'); - $this->assertEquals(2, $info->getExpansionRate(), 'Wrong expansion rate'); - $this->assertEquals(20, $info->getMaxIterations(), 'Wrong max iterations'); - $this->assertEquals(32, $info->getNumBuckets(), 'Wrong num buckets'); - $this->assertEquals(1, $info->getNumFilters(), 'Wrong num filters'); + self::assertEquals($key, $info->getKey(), 'Wrong key'); + self::assertEquals(376, $info->getSize(), 'Wrong size'); + self::assertEquals(10, $info->getBucketSize(), 'Wrong bucket size'); + self::assertEquals(0, $info->getDeletedItems(), 'Wrong deleted items'); + self::assertEquals(0, $info->getInsertedItems(), 'Wrong inserted items'); + self::assertEquals(2, $info->getExpansionRate(), 'Wrong expansion rate'); + self::assertEquals(20, $info->getMaxIterations(), 'Wrong max iterations'); + self::assertEquals(32, $info->getNumBuckets(), 'Wrong num buckets'); + self::assertEquals(1, $info->getNumFilters(), 'Wrong num filters'); } @@ -44,23 +44,23 @@ public function testInsert(): void { $this->sut->insert('insertTest', 'horse'); $result = $this->sut->exists('insertTest', 'horse'); - $this->assertTrue($result); + self::assertTrue($result); } public function testInsertMany(): void { $key = 'insertManyTest'; $result = $this->sut->insertMany($key, ['horse', 'cow']); - $this->assertEquals([true, true], $result); - $this->assertTrue($this->sut->exists($key, 'horse')); - $this->assertFalse($this->sut->exists($key, 'monkey')); + self::assertEquals([true, true], $result); + self::assertTrue($this->sut->exists($key, 'horse')); + self::assertFalse($this->sut->exists($key, 'monkey')); } public function testInsertIfKeyExists(): void { $key = 'testInsertIfKeyExists'; $this->sut->reserve($key, 10); - $this->assertTrue($this->sut->insertIfKeyExists($key, 'foo')); + self::assertTrue($this->sut->insertIfKeyExists($key, 'foo')); } public function testInsertIfKeyExistsOnNonExistingKey(): void @@ -80,26 +80,26 @@ public function testDelete(): void $key = 'deleteTest'; $this->sut->insertMany($key, ['horse', 'bat', 'rat']); $this->sut->delete($key, 'bat'); - $this->assertTrue($this->sut->exists($key, 'horse')); - $this->assertFalse($this->sut->exists($key, 'bat')); - $this->assertTrue($this->sut->exists($key, 'rat')); + self::assertTrue($this->sut->exists($key, 'horse')); + self::assertFalse($this->sut->exists($key, 'bat')); + self::assertTrue($this->sut->exists($key, 'rat')); } public function testCount(): void { $key = 'countTest'; $this->sut->insertMany($key, ['horse', 'bat', 'rat', 'horse', 'bat', 'horse']); - $this->assertEquals(3, $this->sut->count($key, 'horse')); - $this->assertEquals(2, $this->sut->count($key, 'bat')); - $this->assertEquals(1, $this->sut->count($key, 'rat')); + self::assertEquals(3, $this->sut->count($key, 'horse')); + self::assertEquals(2, $this->sut->count($key, 'bat')); + self::assertEquals(1, $this->sut->count($key, 'rat')); } public function testCopy(): void { $this->sut->insertMany('copyFrom', ['cow', 'donkey', 'fish']); $this->sut->copy('copyFrom', 'copyTo'); - $this->assertTrue($this->sut->exists('copyTo', 'fish')); - $this->assertFalse($this->sut->exists('copyTo', 'monkey')); + self::assertTrue($this->sut->exists('copyTo', 'fish')); + self::assertFalse($this->sut->exists('copyTo', 'monkey')); } public function testInfoOnNonExistingKey(): void diff --git a/tests/Integration/IntegrationTestCase.php b/tests/Integration/IntegrationTestCase.php index 21eac04..1c7a7f9 100644 --- a/tests/Integration/IntegrationTestCase.php +++ b/tests/Integration/IntegrationTestCase.php @@ -20,13 +20,32 @@ abstract class IntegrationTestCase extends TestCase public function setUp(): void { - $host = getenv('REDIS_HOST') ?: 'redis'; - $port = getenv('REDIS_PORT') ? (int)getenv('REDIS_PORT') : 6379; + $host = self::getHost(); + $port = self::getPort(); $this->redis = new Redis(); $this->redis->connect($host, $port); - $connectionParams = new RedisConnectionParams($host, $port); $this->redisClient = new RedisClient($this->redis, $connectionParams); $this->redisClient->executeCommand(['FLUSHDB']); } + + protected static function getHost(): string + { + return getenv('REDIS_HOST') ?: 'redis'; + } + + protected static function getPort(): int + { + return getenv('REDIS_PORT') ? (int)getenv('REDIS_PORT') : 6379; + } + + protected static function getRedisMajorVersion(): int + { + $redis = new Redis(); + $redis->connect(self::getHost(), self::getPort()); + /** @var array $info */ + $info = $redis->info('SERVER'); + [$version] = explode('.', $info['redis_version']); + return (int) $version; + } } diff --git a/tests/Integration/TopKIntegrationTest.php b/tests/Integration/TopKIntegrationTest.php index b8aa0be..d416d49 100644 --- a/tests/Integration/TopKIntegrationTest.php +++ b/tests/Integration/TopKIntegrationTest.php @@ -25,7 +25,7 @@ public function setUp(): void public function testReserve(): void { $result = $this->sut->reserve('testReserve', 50, 2000, 7, .925); - $this->assertTrue($result); + self::assertTrue($result); } public function testAdd(): void @@ -33,7 +33,7 @@ public function testAdd(): void $key = 'addTest'; $this->sut->reserve($key, 2, 10, 10, .925); $result = $this->sut->add($key, 'foo', 'bar', 'baz', 'baz', 'bar', 'baz'); - $this->assertEquals([false, false, false, 'foo', false, false], $result); + self::assertEquals([false, false, false, 'foo', false, false], $result); } public function testIncrementBy(): void @@ -45,7 +45,7 @@ public function testIncrementBy(): void new Pair('foo', 10), new Pair('bar', 10) ); - $this->assertEquals([false, false], $result1); + self::assertEquals([false, false], $result1); $result2 = $this->sut->incrementBy( $key, @@ -53,7 +53,7 @@ public function testIncrementBy(): void new Pair('baz', 25), new Pair('nope', 1) ); - $this->assertEquals([false, 'foo', false], $result2); + self::assertEquals([false, 'foo', false], $result2); } public function testAddOnNonExistingKey(): void @@ -74,7 +74,7 @@ public function testQuery(): void $this->sut->reserve($key, 2, 10, 10, .925); $this->sut->add($key, 'foo', 'bar', 'baz', 'baz', 'bar', 'baz'); $result = $this->sut->query($key, 'foo', 'bar', 'baz', 'bom'); - $this->assertEqualsCanonicalizing(['bar', 'baz'], $result); + self::assertEqualsCanonicalizing(['bar', 'baz'], $result); } public function testQueryNonExistingKey(): void @@ -89,7 +89,7 @@ public function testCount(): void $this->sut->reserve($key, 2, 10, 10, .925); $this->sut->add($key, 'foo', 'bar', 'baz', 'baz', 'bar', 'baz'); $result = $this->sut->count($key, 'foo', 'bar', 'baz', 'bom'); - $this->assertEqualsCanonicalizing([ + self::assertEqualsCanonicalizing([ new Pair('foo', 1), new Pair('bar', 2), new Pair('baz', 3), @@ -109,7 +109,7 @@ public function testList(): void $this->sut->reserve($key, 2, 10, 10, .925); $this->sut->add($key, 'foo', 'bar', 'baz', 'baz', 'bar', 'baz'); $result = $this->sut->list($key); - $this->assertEqualsCanonicalizing([new Pair('bar', 0), new Pair('baz', 1)], $result); + self::assertEqualsCanonicalizing([new Pair('bar', 0), new Pair('baz', 1)], $result); } public function testListNonExistingKey(): void @@ -123,11 +123,11 @@ public function testInfo(): void $key = 'infoTest'; $this->sut->reserve($key, 2, 10, 12, .925); $result = $this->sut->info($key); - $this->assertEquals($key, $result->getKey()); - $this->assertEquals(2, $result->getTopK()); - $this->assertEquals(10, $result->getWidth()); - $this->assertEquals(12, $result->getDepth()); - $this->assertEquals(.925, $result->getDecay()); + self::assertEquals($key, $result->getKey()); + self::assertEquals(2, $result->getTopK()); + self::assertEquals(10, $result->getWidth()); + self::assertEquals(12, $result->getDepth()); + self::assertEquals(.925, $result->getDecay()); } public function testInfoOnNonExistingKey(): void diff --git a/tests/Unit/RedisClientTest.php b/tests/Unit/RedisClientTest.php index fb7b4c8..fd4cf66 100644 --- a/tests/Unit/RedisClientTest.php +++ b/tests/Unit/RedisClientTest.php @@ -16,8 +16,8 @@ class RedisClientTest extends TestCase public function testExecuteCommand(): void { $redisMock = $this->createMock(Redis::class); - $redisMock->expects($this->once())->method('isConnected')->willReturn(false); - $redisMock->expects($this->once())->method('connect')->with( + $redisMock->expects(self::once())->method('isConnected')->willReturn(false); + $redisMock->expects(self::once())->method('connect')->with( '127.0.0.1', 6379, 3, @@ -25,7 +25,7 @@ public function testExecuteCommand(): void 1, 2.2 ); - $redisMock->expects($this->once())->method('rawCommand')->with('MY', 'command'); + $redisMock->expects(self::once())->method('rawCommand')->with('MY', 'command'); $connectionParams = new RedisConnectionParams(); $connectionParams->setRetryInterval(1) ->setReadTimeout(2.2) @@ -37,12 +37,12 @@ public function testExecuteCommand(): void public function testPersistentConnection(): void { $redisMock = $this->createMock(Redis::class); - $redisMock->expects($this->once())->method('isConnected')->willReturn(false); - $redisMock->expects($this->once())->method('pconnect')->with( + $redisMock->expects(self::once())->method('isConnected')->willReturn(false); + $redisMock->expects(self::once())->method('pconnect')->with( '127.0.0.1', 6379, 0, - $this->isType(IsType::TYPE_STRING), + self::isType(IsType::TYPE_STRING), 0, 0.0 ); @@ -55,9 +55,9 @@ public function testPersistentConnection(): void public function testDontConnectIfNotNecessary(): void { $redisMock = $this->createMock(Redis::class); - $redisMock->expects($this->once())->method('isConnected')->willReturn(true); - $redisMock->expects($this->never())->method('connect'); - $redisMock->expects($this->never())->method('pconnect'); + $redisMock->expects(self::once())->method('isConnected')->willReturn(true); + $redisMock->expects(self::never())->method('connect'); + $redisMock->expects(self::never())->method('pconnect'); $connectionParams = new RedisConnectionParams(); $sut = new RedisClient($redisMock, $connectionParams); $sut->executeCommand(['MY', 'command']); @@ -67,7 +67,7 @@ public function testFailureToConnectThrowsException(): void { $this->expectException(RedisClientException::class); $redisMock = $this->createMock(Redis::class); - $redisMock->expects($this->once())->method('connect')->willReturn(false); + $redisMock->expects(self::once())->method('connect')->willReturn(false); $connectionParams = new RedisConnectionParams(); $sut = new RedisClient($redisMock, $connectionParams); $sut->executeCommand(['MY', 'command']);