Added support for container commands (#1049)

* Added support for container commands FUNCTION LOAD, FUNCTION DELETE and FCALL

* Changed ContainerInterface and AbstractContainer

* Re-implement logic of abstract methods

---------

Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
This commit is contained in:
Vladyslav Vildanov
2023-01-31 18:48:48 +02:00
committed by GitHub
parent 38e955bc26
commit 98222f47b0
22 changed files with 1051 additions and 0 deletions
+31
View File
@@ -17,6 +17,8 @@ use InvalidArgumentException;
use IteratorAggregate;
use Predis\Command\CommandInterface;
use Predis\Command\RawCommand;
use Predis\Command\Redis\Container\ContainerFactory;
use Predis\Command\Redis\Container\ContainerInterface;
use Predis\Command\ScriptCommand;
use Predis\Configuration\Options;
use Predis\Configuration\OptionsInterface;
@@ -31,6 +33,7 @@ use Predis\Response\ResponseInterface;
use Predis\Response\ServerException;
use Predis\Transaction\MultiExec as MultiExecTransaction;
use ReturnTypeWillChange;
use RuntimeException;
use Traversable;
/**
@@ -310,6 +313,34 @@ class Client implements ClientInterface, IteratorAggregate
return $this->commands->create($commandID, $arguments);
}
/**
* @param $name
* @return ContainerInterface
*/
public function __get($name)
{
return ContainerFactory::create($this, $name);
}
/**
* @param $name
* @param $value
* @return mixed
*/
public function __set($name, $value)
{
throw new RuntimeException('Not allowed');
}
/**
* @param $name
* @return mixed
*/
public function __isset($name)
{
throw new RuntimeException('Not allowed');
}
/**
* {@inheritdoc}
*/
+5
View File
@@ -17,6 +17,7 @@ use Predis\Command\Argument\Geospatial\FromInterface;
use Predis\Command\Argument\Server\LimitOffsetCount;
use Predis\Command\Argument\Server\To;
use Predis\Command\CommandInterface;
use Predis\Command\Redis\Container\FunctionContainer;
/**
* Interface defining a client-side context such as a pipeline or transaction.
@@ -55,6 +56,7 @@ use Predis\Command\CommandInterface;
* @method $this decr($key)
* @method $this decrby($key, $decrement)
* @method $this failover(?To $to = null, bool $abort = false, int $timeout = -1)
* @method $this fcall(string $function, array $keys, ...$args)
* @method $this get($key)
* @method $this getbit($key, $offset)
* @method $this getex(string $key, $modifier = '', $value = false)
@@ -200,6 +202,9 @@ use Predis\Command\CommandInterface;
* @method $this georadiusbymember($key, $member, $radius, $unit, array $options = null)
* @method $this geosearch(string $key, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $withCoord = false, bool $withDist = false, bool $withHash = false)
* @method $this geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false)
*
* Container commands
* @property FunctionContainer $function
*/
interface ClientContextInterface
{
+5
View File
@@ -18,6 +18,7 @@ use Predis\Command\Argument\Server\LimitOffsetCount;
use Predis\Command\Argument\Server\To;
use Predis\Command\CommandInterface;
use Predis\Command\FactoryInterface;
use Predis\Command\Redis\Container\FunctionContainer;
use Predis\Configuration\OptionsInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Response\Status;
@@ -64,6 +65,7 @@ use Predis\Response\Status;
* @method int decr(string $key)
* @method int decrby(string $key, int $decrement)
* @method Status failover(?To $to = null, bool $abort = false, int $timeout = -1)
* @method mixed fcall(string $function, array $keys, ...$args)
* @method string|null get(string $key)
* @method int getbit(string $key, $offset)
* @method int|null getex(string $key, $modifier = '', $value = false)
@@ -218,6 +220,9 @@ use Predis\Response\Status;
* @method array georadiusbymember(string $key, $member, $radius, $unit, array $options = null)
* @method array geosearch(string $key, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $withCoord = false, bool $withDist = false, bool $withHash = false)
* @method int geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false)
*
* Container commands
* @property FunctionContainer $function
*/
interface ClientInterface
{
@@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis\Container;
use Predis\ClientInterface;
abstract class AbstractContainer implements ContainerInterface
{
/**
* @var ClientInterface
*/
protected $client;
public function __construct(ClientInterface $client)
{
$this->client = $client;
}
/**
* {@inheritDoc}
*/
public function __call($subcommandID, $arguments)
{
array_unshift($arguments, strtoupper($subcommandID));
return $this->client->executeCommand(
$this->client->createCommand($this->getContainerCommandId(), $arguments)
);
}
abstract public function getContainerCommandId(): string;
}
@@ -0,0 +1,54 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis\Container;
use Predis\ClientInterface;
use UnexpectedValueException;
class ContainerFactory
{
private const CONTAINER_NAMESPACE = "Predis\Command\Redis\Container";
/**
* Mappings for class names that corresponds to PHP reserved words.
*
* @var array
*/
private static $specialMappings = [
'FUNCTION' => FunctionContainer::class,
];
/**
* Creates container command.
*
* @param ClientInterface $client
* @param string $containerCommandID
* @return ContainerInterface
*/
public static function create(ClientInterface $client, string $containerCommandID): ContainerInterface
{
$containerCommandID = strtoupper($containerCommandID);
if (class_exists($containerClass = self::CONTAINER_NAMESPACE . '\\' . $containerCommandID)) {
return new $containerClass($client);
}
if (array_key_exists($containerCommandID, self::$specialMappings)) {
$containerClass = self::$specialMappings[$containerCommandID];
return new $containerClass($client);
}
throw new UnexpectedValueException('Given command is not supported.');
}
}
@@ -0,0 +1,33 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis\Container;
interface ContainerInterface
{
/**
* Creates Redis container command with subcommand as virtual method name
* and sends a request to the server.
*
* @param $subcommandID
* @param $arguments
* @return mixed
*/
public function __call($subcommandID, $arguments);
/**
* Returns containerCommandId of specific container command.
*
* @return string
*/
public function getContainerCommandId(): string;
}
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis\Container;
use Predis\Response\Status;
/**
* @method string load(string $functionCode, bool $replace = 'false')
* @method Status delete(string $libraryName)
*/
class FunctionContainer extends AbstractContainer
{
public function getContainerCommandId(): string
{
return 'functions';
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Keys;
/**
* @see https://redis.io/commands/fcall/
*
* Invoke a function.
*/
class FCALL extends RedisCommand
{
use Keys;
protected static $keysArgumentPositionOffset = 1;
public function getId()
{
return 'FCALL';
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Strategy\StrategyResolverInterface;
use Predis\Command\Strategy\SubcommandStrategyResolver;
/**
* @see https://redis.io/commands/?name=function
*
* Container command corresponds to any FUNCTION *.
* Represents any FUNCTION command with subcommand as first argument.
*/
class FUNCTIONS extends RedisCommand
{
/**
* @var StrategyResolverInterface
*/
private $strategyResolver;
public function __construct()
{
$this->strategyResolver = new SubcommandStrategyResolver();
}
public function getId()
{
return 'FUNCTION';
}
public function setArguments(array $arguments)
{
$strategy = $this->strategyResolver->resolve('functions', $arguments[0]);
$arguments = $strategy->processArguments($arguments);
parent::setArguments($arguments);
$this->filterArguments();
}
}
+4
View File
@@ -12,6 +12,8 @@
namespace Predis\Command;
use Predis\Command\Redis\FUNCTIONS;
/**
* Command factory for mainline Redis servers.
*
@@ -29,6 +31,8 @@ class RedisFactory extends Factory
'ECHO' => 'Predis\Command\Redis\ECHO_',
'EVAL' => 'Predis\Command\Redis\EVAL_',
'OBJECT' => 'Predis\Command\Redis\OBJECT_',
// Class name corresponds to PHP reserved word "function", added mapping to bypass restrictions
'FUNCTION' => FUNCTIONS::class,
];
}
@@ -0,0 +1,26 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Strategy\ContainerCommands\Functions;
use Predis\Command\Strategy\SubcommandStrategyInterface;
class DeleteStrategy implements SubcommandStrategyInterface
{
/**
* {@inheritDoc}
*/
public function processArguments(array $arguments): array
{
return $arguments;
}
}
@@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Strategy\ContainerCommands\Functions;
use Predis\Command\Strategy\SubcommandStrategyInterface;
class LoadStrategy implements SubcommandStrategyInterface
{
/**
* {@inheritdoc}
*/
public function processArguments(array $arguments): array
{
if (count($arguments) <= 2) {
return $arguments;
}
$processedArguments = [$arguments[0]];
$replace = array_pop($arguments);
if (is_bool($replace) && $replace) {
$processedArguments[] = 'REPLACE';
} elseif (!is_bool($replace)) {
$processedArguments[] = $replace;
}
$processedArguments[] = $arguments[1];
return $processedArguments;
}
}
@@ -0,0 +1,25 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Strategy;
interface StrategyResolverInterface
{
/**
* Resolves subcommand strategy.
*
* @param string $commandId
* @param string $subcommandId
* @return SubcommandStrategyInterface
*/
public function resolve(string $commandId, string $subcommandId): SubcommandStrategyInterface;
}
@@ -0,0 +1,24 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Strategy;
interface SubcommandStrategyInterface
{
/**
* Process arguments for given subcommand.
*
* @param array $arguments
* @return array
*/
public function processArguments(array $arguments): array;
}
@@ -0,0 +1,37 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Strategy;
use InvalidArgumentException;
class SubcommandStrategyResolver implements StrategyResolverInterface
{
private const CONTAINER_COMMANDS_NAMESPACE = 'Predis\Command\Strategy\ContainerCommands';
/**
* {@inheritDoc}
*/
public function resolve(string $commandId, string $subcommandId): SubcommandStrategyInterface
{
$subcommandStrategyClass = ucfirst(strtolower($subcommandId)) . 'Strategy';
$commandDirectoryName = ucfirst(strtolower($commandId));
if (class_exists(
$containerCommandClass = self::CONTAINER_COMMANDS_NAMESPACE . '\\' . $commandDirectoryName . '\\' . $subcommandStrategyClass
)) {
return new $containerCommandClass();
}
throw new InvalidArgumentException('Non-existing container command given');
}
}
@@ -0,0 +1,90 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis\Container;
use PHPUnit\Framework\TestCase;
use Predis\ClientInterface;
use Predis\Command\CommandInterface;
class AbstractContainerTest extends TestCase
{
/**
* @var AbstractContainer
*/
private $testClass;
/**
* @var array
*/
private $arguments;
/**
* @var array
*/
private $expectedValue;
/**
* @var \PHPUnit\Framework\MockObject\MockObject|ClientInterface
*/
private $mockClient;
/**
* @var \PHPUnit\Framework\MockObject\MockObject|CommandInterface
*/
private $mockCommand;
protected function setUp(): void
{
$this->arguments = ['arg1', 'arg2'];
$this->expectedValue = ['value'];
$this->mockCommand = $this->getMockBuilder(CommandInterface::class)->getMock();
$this->mockClient = $this->getMockBuilder(ClientInterface::class)->getMock();
$this->testClass = new class($this->mockClient) extends AbstractContainer {
public function getContainerCommandId(): string
{
return 'test';
}
};
}
/**
* @return void
*/
public function testGetContainerId(): void
{
$this->assertSame('test', $this->testClass->getContainerCommandId());
}
/**
* @return void
*/
public function testCallReturnsValidCommandResponse(): void
{
$modifiedArguments = ['TEST', ['arg1', 'arg2']];
$this->mockClient
->expects($this->once())
->method('createCommand')
->with($this->equalTo('test'), $modifiedArguments)
->willReturn($this->mockCommand);
$this->mockClient
->expects($this->once())
->method('executeCommand')
->with($this->mockCommand)
->willReturn($this->expectedValue);
$this->assertSame($this->expectedValue, $this->testClass->test($this->arguments));
}
}
@@ -0,0 +1,64 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis\Container;
use PHPUnit\Framework\TestCase;
use Predis\ClientInterface;
use UnexpectedValueException;
class ContainerFactoryTest extends TestCase
{
/**
* @var \PHPUnit\Framework\MockObject\MockObject|ClientInterface
*/
private $mockClient;
/**
* @var ContainerFactory
*/
private $factory;
/**
* @var ContainerInterface
*/
private $expectedContainer;
protected function setUp(): void
{
$this->mockClient = $this->getMockBuilder(ClientInterface::class)->getMock();
$this->expectedContainer = new FunctionContainer($this->mockClient);
$this->factory = new ContainerFactory();
}
/**
* @return void
*/
public function testCreatesReturnsExistingCommandContainerClass(): void
{
$this->assertEquals(
$this->expectedContainer,
$this->factory::create($this->mockClient, 'function')
);
}
/**
* @return void
*/
public function testThrowsExceptionOnNonExistingCommand(): void
{
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('Given command is not supported.');
$this->factory::create($this->mockClient, 'foobar');
}
}
+135
View File
@@ -0,0 +1,135 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Response\ServerException;
/**
* @group commands
* @group realm-scripting
*/
class FCALL_Test extends PredisCommandTestCase
{
/**
* {@inheritdoc}
*/
protected function getExpectedCommand(): string
{
return FCALL::class;
}
/**
* {@inheritdoc}
*/
protected function getExpectedId(): string
{
return 'FCALL';
}
/**
* @group disconnected
* @dataProvider argumentsProvider
*/
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
{
$command = $this->getCommand();
$command->setArguments($actualArguments);
$this->assertSame($expectedArguments, $command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse(): void
{
$this->assertSame(1, $this->getCommand()->parseResponse(1));
}
/**
* @group connected
* @dataProvider functionsProvider
* @param string $function
* @param array $functionArguments
* @param $expectedResponse
* @return void
* @requiresRedisVersion >= 7.0.0
*/
public function testInvokeGivenFunction(
string $function,
array $functionArguments,
$expectedResponse
): void {
$redis = $this->getClient();
$this->assertSame('mylib', $redis->function->load($function));
$actualResponse = $redis->fcall(...$functionArguments);
$this->assertSame($expectedResponse, $actualResponse);
$this->assertEquals('OK', $redis->function->delete('mylib'));
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 7.0.0
*/
public function testThrowsExceptionOnNonExistingFunctionGiven(): void
{
$redis = $this->getClient();
$this->expectException(ServerException::class);
$this->expectExceptionMessage('ERR Function not found');
$redis->fcall('function', []);
}
public function argumentsProvider(): array
{
return [
'with default arguments' => [
['function', []],
['function', 0],
],
'with provided keys' => [
['function', ['key1', 'key2']],
['function', 2, 'key1', 'key2'],
],
'with provided keys and arguments' => [
['function', ['key1', 'key2'], 'arg1', 'arg2'],
['function', 2, 'key1', 'key2', 'arg1', 'arg2'],
],
];
}
public function functionsProvider(): array
{
return [
'with default arguments' => [
"#!lua name=mylib \n redis.register_function('myfunc', function(keys, args) return 'hello' end)",
['myfunc', []],
'hello',
],
'with provided keys' => [
"#!lua name=mylib \n redis.register_function('myfunc', function(keys, args) return keys[1] end)",
['myfunc', ['key1']],
'key1',
],
'with provided keys and arguments' => [
"#!lua name=mylib \n redis.register_function('myfunc', function(keys, args) return keys[1] .. ' ' .. args[1] end)",
['myfunc', ['key1'], 'arg1'],
'key1 arg1',
],
];
}
}
@@ -0,0 +1,182 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Response\ServerException;
/**
* @group commands
* @group realm-scripting
*/
class FUNCTIONS_Test extends PredisCommandTestCase
{
/**
* @var string
*/
private $libName = 'mylib';
/**
* {@inheritdoc}
*/
protected function getExpectedCommand(): string
{
return FUNCTIONS::class;
}
/**
* {@inheritdoc}
*/
protected function getExpectedId(): string
{
return 'FUNCTION';
}
/**
* @group disconnected
*/
public function testLoadFilterArguments(): void
{
$arguments = ['LOAD', 'function-code', true];
$expected = ['LOAD', 'function-code', 'REPLACE'];
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSameValues($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testDeleteFilterArguments(): void
{
$arguments = ['DELETE', 'libraryName'];
$expected = ['DELETE', 'libraryName'];
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSameValues($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse(): void
{
$this->assertSame(1, $this->getCommand()->parseResponse(1));
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 7.0.0
*/
public function testLoadFunctionAddFunctionIntoGivenLibrary(): void
{
$redis = $this->getClient();
$actualResponse = $redis->function->load(
"#!lua name={$this->libName} \n redis.register_function('myfunc', function(keys, args) return args[1] end)"
);
$this->assertSame('mylib', $actualResponse);
$this->assertSame('arg1', $redis->fcall('myfunc', [], 'arg1'));
$this->assertEquals('OK', $redis->function->delete($this->libName));
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 7.0.0
*/
public function testLoadFunctionOverridesExistingFunctionWithReplaceArgumentGiven(): void
{
$redis = $this->getClient();
$actualResponse = $redis->function->load(
"#!lua name={$this->libName} \n redis.register_function('myfunc', function(keys, args) return args[1] end)"
);
$this->assertSame($this->libName, $actualResponse);
$this->assertSame('arg1', $redis->fcall('myfunc', [], 'arg1'));
$overriddenResponse = $redis->function->load(
"#!lua name={$this->libName} \n redis.register_function('myfunc', function(keys, args) return args[2] end)",
true
);
$this->assertSame($this->libName, $overriddenResponse);
$this->assertSame('arg2', $redis->fcall('myfunc', [], 'arg1', 'arg2'));
$this->assertEquals('OK', $redis->function->delete($this->libName));
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 7.0.0
*/
public function testLoadFunctionThrowsErrorOnAlreadyExistingLibraryGiven(): void
{
$redis = $this->getClient();
$actualResponse = $redis->function->load(
"#!lua name={$this->libName} \n redis.register_function('myfunc', function(keys, args) return args[1] end)"
);
$this->assertSame($this->libName, $actualResponse);
$this->expectException(ServerException::class);
$this->expectExceptionMessage("ERR Library '{$this->libName}' already exists");
try {
$redis->function->load(
"#!lua name={$this->libName} \n redis.register_function('myfunc', function(keys, args) return args[1] end)"
);
} finally {
$this->assertEquals('OK', $redis->function->delete($this->libName));
}
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 7.0.0
*/
public function testDeleteFunctionRemovesAlreadyExistingLibrary(): void
{
$redis = $this->getClient();
$actualResponse = $redis->function->load(
"#!lua name={$this->libName} \n redis.register_function('myfunc', function(keys, args) return args[1] end)"
);
$this->assertSame($this->libName, $actualResponse);
$this->assertEquals('OK', $redis->function->delete($this->libName));
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 7.0.0
*/
public function testDeleteFunctionThrowsErrorOnNonExistingLibrary(): void
{
$redis = $this->getClient();
$this->expectException(ServerException::class);
$this->expectExceptionMessage('ERR Library not found');
$redis->function->delete($this->libName);
}
}
@@ -0,0 +1,33 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Strategy\ContainerCommands\Functions;
use PredisTestCase;
class DeleteStrategyTest extends PredisTestCase
{
/**
* @var DeleteStrategy
*/
private $strategy;
protected function setUp(): void
{
$this->strategy = new DeleteStrategy();
}
public function testProcessArguments(): void
{
$this->assertSame(['arg1', 'arg2'], $this->strategy->processArguments(['arg1', 'arg2']));
}
}
@@ -0,0 +1,59 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Strategy\ContainerCommands\Functions;
use PredisTestCase;
class LoadStrategyTest extends PredisTestCase
{
/**
* @var LoadStrategy
*/
private $strategy;
protected function setUp(): void
{
$this->strategy = new LoadStrategy();
}
/**
* @dataProvider argumentsProvider
* @param array $actualArguments
* @param array $expectedArguments
* @return void
*/
public function testProcessArgumentsReturnsCorrectArguments(
array $actualArguments,
array $expectedArguments
): void {
$this->assertSame($expectedArguments, $this->strategy->processArguments($actualArguments));
}
public function argumentsProvider(): array
{
return [
'with less then or equal 2 arguments' => [
['arg1', 'arg2'],
['arg1', 'arg2'],
],
'with last argument equals true' => [
['arg1', 'arg2', true],
['arg1', 'REPLACE', 'arg2'],
],
'with last argument equals false' => [
['arg1', 'arg2', false],
['arg1', 'arg2'],
],
];
}
}
@@ -0,0 +1,51 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Strategy;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use Predis\Command\Strategy\ContainerCommands\Functions\LoadStrategy;
class SubcommandStrategyResolverTest extends TestCase
{
/**
* @var StrategyResolverInterface
*/
private $resolver;
protected function setUp(): void
{
$this->resolver = new SubcommandStrategyResolver();
}
/**
* @return void
*/
public function testResolveCorrectStrategy(): void
{
$expectedStrategy = new LoadStrategy();
$this->assertEquals($expectedStrategy, $this->resolver->resolve('functions', 'load'));
}
/**
* @return void
*/
public function testResolveThrowsExceptionOnNonExistingStrategy(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Non-existing container command given');
$this->resolver->resolve('foo', 'bar');
}
}