From 48a861053e613063f68695cf8b13eff3e4d21987 Mon Sep 17 00:00:00 2001 From: Vladyslav Vildanov <117659936+vladvildanov@users.noreply.github.com> Date: Mon, 24 Mar 2025 17:50:12 +0200 Subject: [PATCH] Added new hash-field expiration commands (#1520) * Added new hash-field expiration commands * Changed argument default value * Marked test as skipped * Removed version restriction * Updated CHANGELOG.md --- .github/workflows/tests.yml | 2 +- CHANGELOG.md | 16 +- src/ClientContextInterface.php | 5 + src/ClientInterface.php | 5 + src/Command/Redis/HGETDEL.php | 35 +++ src/Command/Redis/HGETEX.php | 81 ++++++ src/Command/Redis/HSETEX.php | 117 +++++++++ tests/Predis/Command/Redis/HGETDEL_Test.php | 69 ++++++ tests/Predis/Command/Redis/HGETEX_Test.php | 193 +++++++++++++++ tests/Predis/Command/Redis/HSETEX_Test.php | 257 ++++++++++++++++++++ tests/Predis/Command/Redis/INFO_Test.php | 2 + 11 files changed, 780 insertions(+), 2 deletions(-) create mode 100644 src/Command/Redis/HGETDEL.php create mode 100644 src/Command/Redis/HGETEX.php create mode 100644 src/Command/Redis/HSETEX.php create mode 100644 tests/Predis/Command/Redis/HGETDEL_Test.php create mode 100644 tests/Predis/Command/Redis/HGETEX_Test.php create mode 100644 tests/Predis/Command/Redis/HSETEX_Test.php diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 571353cf..4e64ed68 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -38,7 +38,7 @@ jobs: run: | # Mapping of original redis versions to client test containers declare -A redis_clients_version_mapping=( - ["8.0"]="8.0-M04-pre" + ["8.0"]="8.0-M05-pre" ["7.4"]="7.4.2" ["7.2"]="7.2.7" ["6.2"]="6.2.17" diff --git a/CHANGELOG.md b/CHANGELOG.md index 19f3e7ab..6abc1827 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,21 @@ ## Unreleased ### Fixed -- Fixed PHP 8.4 compatibility with `stream_context_set_option()` +- Fixed PHP 8.4 compatibility with `stream_context_set_option()` (#1503) +- Prevent named arguments runtime failure (#1509) +- Mark GEOSEARCH as read-only to ensure execution on replica (#1481) +- Fix eval_ro cluster support (#1449) + +### Changed +- Use parallel on PHP-CS-Fixer (#1489) +- Update watch command accepting string and string[] (#1476) +- Optimize redis cluster slotmap with compact slot range object (#1493) + +### Added +- Added testing with 8.0 (#1510) +- Added test coverage for compatibility with Redis 8.0 (#1513) +- Added missing FT._LIST and BITFIELD_RO commands (#1521) +- Added new hash-field expiration commands (#1520) ## v2.3.0 (2024-11-21) ### Added diff --git a/src/ClientContextInterface.php b/src/ClientContextInterface.php index 3609baa6..d00e2049 100644 --- a/src/ClientContextInterface.php +++ b/src/ClientContextInterface.php @@ -44,6 +44,8 @@ use Predis\Command\Redis\Container\FunctionContainer; use Predis\Command\Redis\Container\Json\JSONDEBUG; use Predis\Command\Redis\Container\Search\FTCONFIG; use Predis\Command\Redis\Container\Search\FTCURSOR; +use Predis\Command\Redis\HGETEX; +use Predis\Command\Redis\HSETEX; /** * Interface defining a client-side context such as a pipeline or transaction. @@ -164,7 +166,9 @@ use Predis\Command\Redis\Container\Search\FTCURSOR; * @method $this hpexpireat(string $key, int $unixTimeMilliseconds, array $fields, string $flag = null) * @method $this hpexpiretime(string $key, array $fields) * @method $this hget($key, $field) + * @method $this hgetex(string $key, array $fields, string $modifier = HGETEX::NULL) * @method $this hgetall($key) + * @method $this hgetdel(string $key, array $fields) * @method $this hincrby($key, $field, $increment) * @method $this hincrbyfloat($key, $field, $increment) * @method $this hkeys($key) @@ -174,6 +178,7 @@ use Predis\Command\Redis\Container\Search\FTCURSOR; * @method $this hrandfield(string $key, int $count = 1, bool $withValues = false) * @method $this hscan($key, $cursor, ?array $options = null) * @method $this hset($key, $field, $value) + * @method $this hsetex(string $key, array $fieldValueMap, string $setModifier = HSETEX::SET_NULL, string $ttlModifier = HSETEX::TTL_NULL, int|bool $ttlModifierValue = false) * @method $this hsetnx($key, $field, $value) * @method $this httl(string $key, array $fields) * @method $this hpttl(string $key, array $fields) diff --git a/src/ClientInterface.php b/src/ClientInterface.php index d4d8aa69..ce3e1932 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -45,6 +45,8 @@ use Predis\Command\Redis\Container\FunctionContainer; use Predis\Command\Redis\Container\Json\JSONDEBUG; use Predis\Command\Redis\Container\Search\FTCONFIG; use Predis\Command\Redis\Container\Search\FTCURSOR; +use Predis\Command\Redis\HGETEX; +use Predis\Command\Redis\HSETEX; use Predis\Configuration\OptionsInterface; use Predis\Connection\ConnectionInterface; use Predis\Response\Status; @@ -173,7 +175,9 @@ use Predis\Response\Status; * @method array|null hpexpireat(string $key, int $unixTimeMilliseconds, array $fields, string $flag = null) * @method array|null hpexpiretime(string $key, array $fields) * @method string|null hget(string $key, string $field) + * @method array|null hgetex(string $key, array $fields, string $modifier = HGETEX::NULL, int|bool $modifierValue = false) * @method array hgetall(string $key) + * @method array hgetdel(string $key, array $fields) * @method int hincrby(string $key, string $field, int $increment) * @method string hincrbyfloat(string $key, string $field, int|float $increment) * @method array hkeys(string $key) @@ -183,6 +187,7 @@ use Predis\Response\Status; * @method array hrandfield(string $key, int $count = 1, bool $withValues = false) * @method array hscan(string $key, $cursor, ?array $options = null) * @method int hset(string $key, string $field, string $value) + * @method int hsetex(string $key, array $fieldValueMap, string $setModifier = HSETEX::SET_NULL, string $ttlModifier = HSETEX::TTL_NULL, int|bool $ttlModifierValue = false) * @method int hsetnx(string $key, string $field, string $value) * @method array|null httl(string $key, array $fields) * @method array|null hpttl(string $key, array $fields) diff --git a/src/Command/Redis/HGETDEL.php b/src/Command/Redis/HGETDEL.php new file mode 100644 index 00000000..b1518efa --- /dev/null +++ b/src/Command/Redis/HGETDEL.php @@ -0,0 +1,35 @@ + 'EX', + self::PX => 'PX', + self::EXAT => 'EXAT', + self::PXAT => 'PXAT', + self::PERSIST => 'PERSIST', + ]; + + public function getId() + { + return 'HGETEX'; + } + + public function setArguments(array $arguments) + { + $processedArguments = [$arguments[0]]; + + // Only required arguments + if (!array_key_exists(2, $arguments) || $arguments[2] == '') { + array_push($processedArguments, 'FIELDS', count($arguments[1])); + $processedArguments = array_merge($processedArguments, $arguments[1]); + parent::setArguments($processedArguments); + + return; + } + + if (!in_array(strtoupper($arguments[2]), self::$modifierEnum)) { + $enumValues = implode(', ', array_keys(self::$modifierEnum)); + throw new UnexpectedValueException("Modifier argument accepts only: {$enumValues} values"); + } + + // PERSIST requires no additional value + if (strtoupper($arguments[2]) === self::$modifierEnum['persist']) { + $processedArguments[] = self::$modifierEnum['persist']; + array_push($processedArguments, 'FIELDS', count($arguments[1])); + $processedArguments = array_merge($processedArguments, $arguments[1]); + parent::setArguments($processedArguments); + + return; + } + + if (!array_key_exists(3, $arguments) || !is_int($arguments[3])) { + throw new UnexpectedValueException('Modifier value is missing or incorrect type'); + } + + // Order matters so FIELDS should be at the end + array_push($processedArguments, self::$modifierEnum[strtolower($arguments[2])], $arguments[3], 'FIELDS', count($arguments[1])); + $processedArguments = array_merge($processedArguments, $arguments[1]); + + parent::setArguments($processedArguments); + } +} diff --git a/src/Command/Redis/HSETEX.php b/src/Command/Redis/HSETEX.php new file mode 100644 index 00000000..fdf6a690 --- /dev/null +++ b/src/Command/Redis/HSETEX.php @@ -0,0 +1,117 @@ + 'EX', + self::TTL_PX => 'PX', + self::TTL_EXAT => 'EXAT', + self::TTL_PXAT => 'PXAT', + self::TTL_KEEP_TTL => 'KEEPTTL', + ]; + + /** + * @var string[] + */ + private static $setModifierEnum = [ + self::SET_FNX => 'FNX', + self::SET_FXX => 'FXX', + ]; + + public function getId() + { + return 'HSETEX'; + } + + public function setArguments(array $arguments) + { + $processedArguments = [$arguments[0]]; + $flatArray = []; + + // Convert key => value, into key, value + array_walk($arguments[1], function ($value, $key) use (&$flatArray) { + array_push($flatArray, $key, $value); + }); + + // Only required arguments + if (!array_key_exists(2, $arguments)) { + array_push($processedArguments, 'FIELDS', count($flatArray) / 2); + $processedArguments = array_merge($processedArguments, $flatArray); + parent::setArguments($processedArguments); + + return; + } + + if ($arguments[2] !== '') { + if (!in_array(strtoupper($arguments[2]), self::$setModifierEnum)) { + $enumValues = implode(', ', array_keys(self::$setModifierEnum)); + throw new UnexpectedValueException("Modifier argument accepts only: {$enumValues} values"); + } + + $processedArguments[] = self::$setModifierEnum[strtolower($arguments[2])]; + } + + // Required + set modifier + if (!array_key_exists(3, $arguments) || $arguments[3] == '') { + array_push($processedArguments, 'FIELDS', count($flatArray) / 2); + $processedArguments = array_merge($processedArguments, $flatArray); + parent::setArguments($processedArguments); + + return; + } + + if (!in_array(strtoupper($arguments[3]), self::$ttlModifierEnum)) { + $enumValues = implode(', ', array_keys(self::$ttlModifierEnum)); + throw new UnexpectedValueException("Modifier argument accepts only: {$enumValues} values"); + } + + // KEEPTTL requires no additional value + if (strtoupper($arguments[3]) === self::$ttlModifierEnum[self::TTL_KEEP_TTL]) { + $processedArguments[] = self::$ttlModifierEnum[self::TTL_KEEP_TTL]; + array_push($processedArguments, 'FIELDS', count($flatArray) / 2); + $processedArguments = array_merge($processedArguments, $flatArray); + parent::setArguments($processedArguments); + + return; + } + + if (!array_key_exists(4, $arguments) || !is_int($arguments[4])) { + throw new UnexpectedValueException('Modifier value is missing or incorrect type'); + } + + // Order matters so FIELDS should be at the end + array_push($processedArguments, self::$ttlModifierEnum[strtolower($arguments[3])], $arguments[4], 'FIELDS', count($flatArray) / 2); + $processedArguments = array_merge($processedArguments, $flatArray); + + parent::setArguments($processedArguments); + } +} diff --git a/tests/Predis/Command/Redis/HGETDEL_Test.php b/tests/Predis/Command/Redis/HGETDEL_Test.php new file mode 100644 index 00000000..8033a489 --- /dev/null +++ b/tests/Predis/Command/Redis/HGETDEL_Test.php @@ -0,0 +1,69 @@ +getCommand(); + $command->setArguments(['key', ['field1', 'field2']]); + + $this->assertSame(['key', 'FIELDS', 2, 'field1', 'field2'], $command->getArguments()); + } + + /** + * @group disconnected + */ + public function testParseResponse(): void + { + $command = $this->getCommand(); + + $this->assertSame(0, $command->parseResponse(0)); + $this->assertSame(1, $command->parseResponse(1)); + } + + /** + * @group connected + * @return void + * @requiresRedisVersion >= 7.9.0 + */ + public function testReturnsAndRemoveFieldsFromHash(): void + { + $redis = $this->getClient(); + + $redis->hset('hashkey', 'field1', 'value1', 'field2', 'value2', 'field3', 'value3'); + + $this->assertSame(['value1', 'value2'], $redis->hgetdel('hashkey', ['field1', 'field2'])); + $this->assertSame(['field3' => 'value3'], $redis->hgetall('hashkey')); + } +} diff --git a/tests/Predis/Command/Redis/HGETEX_Test.php b/tests/Predis/Command/Redis/HGETEX_Test.php new file mode 100644 index 00000000..85453973 --- /dev/null +++ b/tests/Predis/Command/Redis/HGETEX_Test.php @@ -0,0 +1,193 @@ +getCommand(); + $command->setArguments($actualArguments); + + $this->assertSame($expectedArguments, $command->getArguments()); + } + + /** + * @group disconnected + */ + public function testParseResponse(): void + { + $command = $this->getCommand(); + + $this->assertSame(1, $command->parseResponse(1)); + } + + /** + * @group connected + * @dataProvider hashProvider + * @param array $hash + * @param array $arguments + * @param array $expectedResponse + * @param float $timeout + * @return void + * @requiresRedisVersion >= 7.9.0 + */ + public function testReturnsValueAndSetExpirationTimeForGivenHash( + array $hash, + array $arguments, + array $expectedResponse, + float $timeout + ): void { + $redis = $this->getClient(); + + $redis->hset(...$hash); + + $this->assertSame($expectedResponse, $redis->hgetex(...$arguments)); + $this->sleep($timeout); + $this->assertSame([], $redis->hgetall('hash_key')); + } + + /** + * @group connected + * @return void + * @requiresRedisVersion >= 7.9.0 + */ + public function testRemovesAssociatedTTLFromHash() + { + $redis = $this->getClient(); + + $redis->hsetex( + 'hash_key', ['field1' => 'value1'], + HSETEX::SET_NULL, HSETEX::TTL_EX, 100 + ); + + $this->assertGreaterThan(0, $redis->hexpiretime('hash_key', ['field1'])[0]); + $redis->hgetex('hash_key', ['field1'], HGETEX::PERSIST); + $this->assertEquals(-1, $redis->hexpiretime('hash_key', ['field1'])[0]); + } + + /** + * @group connected + * @dataProvider unexpectedValuesProvider + * @param array $arguments + * @param string $expectedExceptionMessage + * @return void + */ + public function testThrowsExceptionOnUnexpectedValueGiven( + array $arguments, + string $expectedExceptionMessage + ): void { + $redis = $this->getClient(); + + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessage($expectedExceptionMessage); + + $redis->hgetex(...$arguments); + } + + public function argumentsProvider(): array + { + return [ + 'with default arguments' => [ + ['key', ['field1', 'field2']], + ['key', 'FIELDS', 2, 'field1', 'field2'], + ], + 'with EX modifier' => [ + ['key', ['field1', 'field2'], HGETEX::EX, 10], + ['key', 'EX', 10, 'FIELDS', 2, 'field1', 'field2'], + ], + 'with PX modifier' => [ + ['key', ['field1', 'field2'], HGETEX::PX, 10], + ['key', 'PX', 10, 'FIELDS', 2, 'field1', 'field2'], + ], + 'with EXAT modifier' => [ + ['key', ['field1', 'field2'], HGETEX::EXAT, 10], + ['key', 'EXAT', 10, 'FIELDS', 2, 'field1', 'field2'], + ], + 'with PXAT modifier' => [ + ['key', ['field1', 'field2'], HGETEX::PXAT, 10], + ['key', 'PXAT', 10, 'FIELDS', 2, 'field1', 'field2'], + ], + 'with PERSIST modifier' => [ + ['key', ['field1', 'field2'], HGETEX::PERSIST], + ['key', 'PERSIST', 'FIELDS', 2, 'field1', 'field2'], + ], + ]; + } + + public function hashProvider(): array + { + return [ + 'with expiration - EX modifier' => [ + ['hash_key', 'field1', 'value1', 'field2', 'value2'], + ['hash_key', ['field1', 'field2'], HGETEX::EX, 1], + ['value1', 'value2'], + 1.2, + ], + 'with expiration - PX modifier' => [ + ['hash_key', 'field1', 'value1', 'field2', 'value2'], + ['hash_key', ['field1', 'field2'], HGETEX::PX, 100], + ['value1', 'value2'], + 0.2, + ], + 'with expiration - EXAT modifier' => [ + ['hash_key', 'field1', 'value1', 'field2', 'value2'], + ['hash_key', ['field1', 'field2'], HGETEX::EXAT, time() + 1], + ['value1', 'value2'], + 2, + ], + 'with expiration - PXAT modifier' => [ + ['hash_key', 'field1', 'value1', 'field2', 'value2'], + ['hash_key', ['field1', 'field2'], HGETEX::PXAT, (time() * 1000) + 100], + ['value1', 'value2'], + 0.3, + ], + ]; + } + + public function unexpectedValuesProvider(): array + { + return [ + 'with wrong modifier' => [ + ['key', ['field1', 'field2'], 'wrong', 10], + 'Modifier argument accepts only: ex, px, exat, pxat, persist values', + ], + 'with wrong type value' => [ + ['key', ['field1', 'field2'], 'ex', true], + 'Modifier value is missing or incorrect type', + ], + ]; + } +} diff --git a/tests/Predis/Command/Redis/HSETEX_Test.php b/tests/Predis/Command/Redis/HSETEX_Test.php new file mode 100644 index 00000000..ab466b29 --- /dev/null +++ b/tests/Predis/Command/Redis/HSETEX_Test.php @@ -0,0 +1,257 @@ +getCommand(); + $command->setArguments($actualArguments); + + $this->assertSame($expectedArguments, $command->getArguments()); + } + + /** + * @group connected + * @group slow + * @dataProvider hashProvider + * @param array $arguments + * @param int $expectedResponse + * @param float $timeout + * @return void + * @requiresRedisVersion >= 7.9.0 + */ + public function testSetHashWithExpiration( + array $arguments, + int $expectedResponse, + float $timeout + ): void { + $redis = $this->getClient(); + + $this->assertSame($expectedResponse, $redis->hsetex(...$arguments)); + + $this->sleep($timeout); + + $this->assertSame([], $redis->hgetall('hash_key')); + } + + /** + * @group connected + * @return void + * @requiresRedisVersion >= 7.9.0 + */ + public function testSetHashFieldsIfNotExists(): void + { + $redis = $this->getClient(); + + $this->assertSame( + 1, + $redis->hsetex('hash_key', ['field1' => 'value1', 'field2' => 'value2'], HSETEX::SET_FNX) + ); + $this->assertSame( + 0, + $redis->hsetex('hash_key', ['field1' => 'value1', 'field2' => 'value2'], HSETEX::SET_FNX) + ); + } + + /** + * @group connected + * @return void + * @requiresRedisVersion >= 7.9.0 + */ + public function testSetHashFieldsOnlyIfExists(): void + { + $redis = $this->getClient(); + + $this->assertSame( + 0, + $redis->hsetex('hash_key', ['field1' => 'value1', 'field2' => 'value2'], HSETEX::SET_FXX) + ); + $this->assertSame( + 1, + $redis->hsetex('hash_key', ['field1' => 'value1', 'field2' => 'value2'], HSETEX::SET_FNX) + ); + $this->assertSame( + 1, + $redis->hsetex('hash_key', ['field1' => 'value1', 'field2' => 'value2'], HSETEX::SET_FXX) + ); + } + + /** + * @group connected + * @return void + * @requiresRedisVersion >= 7.9.0 + */ + public function testSetHashFieldRetainingTTLValue(): void + { + $redis = $this->getClient(); + + $this->assertSame( + 1, + $redis->hsetex( + 'hash_key', + ['field1' => 'value1', 'field2' => 'value2'], + HSETEX::SET_FNX, + HSETEX::TTL_EX, + 100 + ) + ); + + $this->assertGreaterThan(0, $redis->hexpiretime('hash_key', ['field1'])[0]); + + $this->assertSame( + 1, + $redis->hsetex( + 'hash_key', + ['field1' => 'value1'], + HSETEX::SET_FXX, + HSETEX::TTL_KEEP_TTL + ) + ); + + $this->assertGreaterThan(0, $redis->hexpiretime('hash_key', ['field1'])[0]); + } + + /** + * @group connected + * @dataProvider unexpectedValuesProvider + * @param array $arguments + * @param string $expectedExceptionMessage + * @return void + */ + public function testThrowsExceptionOnUnexpectedValueGiven( + array $arguments, + string $expectedExceptionMessage + ): void { + $redis = $this->getClient(); + + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessage($expectedExceptionMessage); + + $redis->hsetex(...$arguments); + } + + public function argumentsProvider(): array + { + return [ + 'with default arguments' => [ + ['key', ['field1' => 'value1', 'field2' => 'value2']], + ['key', 'FIELDS', 2, 'field1', 'value1', 'field2', 'value2'], + ], + 'with FNX modifier' => [ + ['key', ['field1' => 'value1', 'field2' => 'value2'], HSETEX::SET_FNX], + ['key', 'FNX', 'FIELDS', 2, 'field1', 'value1', 'field2', 'value2'], + ], + 'with FXX modifier' => [ + ['key', ['field1' => 'value1', 'field2' => 'value2'], HSETEX::SET_FXX], + ['key', 'FXX', 'FIELDS', 2, 'field1', 'value1', 'field2', 'value2'], + ], + 'with EX modifier' => [ + ['key', ['field1' => 'value1', 'field2' => 'value2'], HSETEX::SET_NULL, HSETEX::TTL_EX, 10], + ['key', 'EX', 10, 'FIELDS', 2, 'field1', 'value1', 'field2', 'value2'], + ], + 'with PX modifier' => [ + ['key', ['field1' => 'value1', 'field2' => 'value2'], HSETEX::SET_NULL, HSETEX::TTL_PX, 10], + ['key', 'PX', 10, 'FIELDS', 2, 'field1', 'value1', 'field2', 'value2'], + ], + 'with EXAT modifier' => [ + ['key', ['field1' => 'value1', 'field2' => 'value2'], HSETEX::SET_NULL, HSETEX::TTL_EXAT, 10], + ['key', 'EXAT', 10, 'FIELDS', 2, 'field1', 'value1', 'field2', 'value2'], + ], + 'with PXAT modifier' => [ + ['key', ['field1' => 'value1', 'field2' => 'value2'], HSETEX::SET_NULL, HSETEX::TTL_PXAT, 10], + ['key', 'PXAT', 10, 'FIELDS', 2, 'field1', 'value1', 'field2', 'value2'], + ], + 'with KEEPTTL modifier' => [ + ['key', ['field1' => 'value1', 'field2' => 'value2'], HSETEX::SET_NULL, HSETEX::TTL_KEEP_TTL], + ['key', 'KEEPTTL', 'FIELDS', 2, 'field1', 'value1', 'field2', 'value2'], + ], + 'with combined modifiers' => [ + ['key', ['field1' => 'value1', 'field2' => 'value2'], HSETEX::SET_FXX, HSETEX::TTL_PXAT, 10], + ['key', 'FXX', 'PXAT', 10, 'FIELDS', 2, 'field1', 'value1', 'field2', 'value2'], + ], + ]; + } + + public function hashProvider(): array + { + return [ + 'with EX modifier' => [ + ['key', ['field1' => 'value1', 'field2' => 'value2'], HSETEX::SET_NULL, HSETEX::TTL_EX, 1], + 1, + 1.2, + ], + 'with PX modifier' => [ + ['key', ['field1' => 'value1', 'field2' => 'value2'], HSETEX::SET_NULL, HSETEX::TTL_PX, 100], + 1, + 0.2, + ], + 'with EXAT modifier' => [ + ['key', ['field1' => 'value1', 'field2' => 'value2'], HSETEX::SET_NULL, HSETEX::TTL_EXAT, time() + 1], + 1, + 2, + ], + 'with PXAT modifier' => [ + ['key', ['field1' => 'value1', 'field2' => 'value2'], HSETEX::SET_NULL, HSETEX::TTL_PXAT, (time() * 1000) + 100], + 1, + 0.3, + ], + 'with combined modifiers' => [ + ['key', ['field1' => 'value1', 'field2' => 'value2'], HSETEX::SET_FNX, HSETEX::TTL_PX, 100], + 1, + 0.2, + ], + ]; + } + + public function unexpectedValuesProvider(): array + { + return [ + 'with wrong set modifier' => [ + ['key', ['field1', 'field2'], 'wrong'], + 'Modifier argument accepts only: fnx, fxx values', + ], + 'with wrong ttl modifier' => [ + ['key', ['field1', 'field2'], '', 'wrong'], + 'Modifier argument accepts only: ex, px, exat, pxat, keepttl values', + ], + 'with wrong ttl modifier value' => [ + ['key', ['field1', 'field2'], '', HSETEX::TTL_PXAT, 'wrong'], + 'Modifier value is missing or incorrect type', + ], + ]; + } +} diff --git a/tests/Predis/Command/Redis/INFO_Test.php b/tests/Predis/Command/Redis/INFO_Test.php index 548f92fa..f173ef32 100644 --- a/tests/Predis/Command/Redis/INFO_Test.php +++ b/tests/Predis/Command/Redis/INFO_Test.php @@ -319,6 +319,8 @@ BUFFER; */ public function testExposeSearchInformation(): void { + $this->markTestSkipped('Skipped due to a bug in 8.0-M05. Should be removed in the next version.'); + $redis = $this->getClient(); $this->assertArrayHasKey('search', $redis->info('modules')['Modules']);