Files
predis/tests/Predis/Replication/ReplicationStrategyTest.php
T
Daniele Alessandri cc2a7657db Rework Predis\Command\FactoryInterface and related classes.
We have renamed most methods to drop the "command" suffix as it is quite
redundant. Due to this change and thanks to variadic methods introduced
with PHP 5.6 we took the opportunity to replace both "supportsCommand()"
and "supportsCommands()" with a single new method "supports()".

Added more stringent typehints for method arguments and typehints for
return values now that we do not need to support anything below PHP 7.2.
Also moved from using array() to [] in source code of class involved.
2020-08-28 14:23:12 +02:00

523 lines
16 KiB
PHP

<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Replication;
use PredisTestCase;
/**
*
*/
class ReplicationStrategyTest extends PredisTestCase
{
/**
* @group disconnected
*/
public function testReadCommands()
{
$commands = $this->getCommandFactory();
$strategy = new ReplicationStrategy();
foreach ($this->getExpectedCommands('read') as $commandId) {
$command = $commands->create($commandId);
$this->assertTrue(
$strategy->isReadOperation($command),
"$commandId is expected to be a read operation."
);
}
}
/**
* @group disconnected
*/
public function testWriteRequests()
{
$commands = $this->getCommandFactory();
$strategy = new ReplicationStrategy();
foreach ($this->getExpectedCommands('write') as $commandId) {
$command = $commands->create($commandId);
$this->assertFalse(
$strategy->isReadOperation($command),
"$commandId is expected to be a write operation."
);
}
}
/**
* @group disconnected
*/
public function testDisallowedCommands()
{
$commands = $this->getCommandFactory();
$strategy = new ReplicationStrategy();
foreach ($this->getExpectedCommands('disallowed') as $commandId) {
$command = $commands->create($commandId);
$this->assertTrue(
$strategy->isDisallowedOperation($command),
"$commandId is expected to be a disallowed operation."
);
}
}
/**
* @group disconnected
*/
public function testSortCommand()
{
$commands = $this->getCommandFactory();
$strategy = new ReplicationStrategy();
$cmdReturnSort = $commands->create('SORT', array('key:list'));
$this->assertFalse(
$strategy->isReadOperation($cmdReturnSort),
'SORT is expected to be a write operation.'
);
$cmdStoreSort = $commands->create('SORT', array('key:list', array('store' => 'key:stored')));
$this->assertFalse(
$strategy->isReadOperation($cmdStoreSort),
'SORT with STORE is expected to be a write operation.'
);
}
/**
* @group disconnected
*/
public function testBitFieldCommand()
{
$commands = $this->getCommandFactory();
$strategy = new ReplicationStrategy();
$command = $commands->create('BITFIELD', array('key'));
$this->assertTrue(
$strategy->isReadOperation($command),
'BITFIELD with no modifiers is expected to be a read operation.'
);
$command = $commands->create('BITFIELD', array('key', 'GET', 'u4', '0'));
$this->assertTrue(
$strategy->isReadOperation($command),
'BITFIELD with GET only is expected to be a read operation.'
);
$command = $commands->create('BITFIELD', array('key', 'SET', 'u4', '0', 1));
$this->assertFalse(
$strategy->isReadOperation($command),
'BITFIELD with SET is expected to be a write operation.'
);
$command = $commands->create('BITFIELD', array('key', 'INCRBY', 'u4', '0', 1));
$this->assertFalse(
$strategy->isReadOperation($command),
'BITFIELD with INCRBY is expected to be a write operation.'
);
$command = $commands->create('BITFIELD', array('key', 'GET', 'u4', '0', 'INCRBY', 'u4', '0', 1));
$this->assertFalse(
$strategy->isReadOperation($command),
'BITFIELD with GET and INCRBY is expected to be a write operation.'
);
$command = $commands->create('BITFIELD', array('key', 'GET', 'u4', '0', 'SET', 'u4', '0', 1));
$this->assertFalse(
$strategy->isReadOperation($command),
'BITFIELD with GET and SET is expected to be a write operation.'
);
}
/**
* @group disconnected
*/
public function testGeoradiusCommand()
{
$commands = $this->getCommandFactory();
$strategy = new ReplicationStrategy();
$command = $commands->create('GEORADIUS', array('key:geo', 15, 37, 200, 'km'));
$this->assertTrue(
$strategy->isReadOperation($command),
'GEORADIUS is expected to be a read operation.'
);
$command = $commands->create('GEORADIUS', array('key:geo', 15, 37, 200, 'km', 'store', 'key:store'));
$this->assertFalse(
$strategy->isReadOperation($command),
'GEORADIUS with STORE is expected to be a write operation.'
);
$command = $commands->create('GEORADIUS', array('key:geo', 15, 37, 200, 'km', 'storedist', 'key:storedist'));
$this->assertFalse(
$strategy->isReadOperation($command),
'GEORADIUS with STOREDIST is expected to be a write operation.'
);
}
/**
* @group disconnected
*/
public function testGeoradiusByMemberCommand()
{
$commands = $this->getCommandFactory();
$strategy = new ReplicationStrategy();
$command = $commands->create('GEORADIUSBYMEMBER', array('key:geo', 15, 37, 200, 'km'));
$this->assertTrue(
$strategy->isReadOperation($command),
'GEORADIUSBYMEMBER is expected to be a read operation.'
);
$command = $commands->create('GEORADIUSBYMEMBER', array('key:geo', 15, 37, 200, 'km', 'store', 'key:store'));
$this->assertFalse(
$strategy->isReadOperation($command),
'GEORADIUSBYMEMBER with STORE is expected to be a write operation.'
);
$command = $commands->create('GEORADIUSBYMEMBER', array('key:geo', 15, 37, 200, 'km', 'storedist', 'key:storedist'));
$this->assertFalse(
$strategy->isReadOperation($command),
'GEORADIUSBYMEMBER with STOREDIST is expected to be a write operation.'
);
}
/**
* @group disconnected
*/
public function testUsingDisallowedCommandThrowsException()
{
$this->expectException('Predis\NotSupportedException');
$this->expectExceptionMessage("The command 'INFO' is not allowed in replication mode");
$commands = $this->getCommandFactory();
$strategy = new ReplicationStrategy();
$command = $commands->create('INFO');
$strategy->isReadOperation($command);
}
/**
* @group disconnected
*/
public function testDefaultIsWriteOperation()
{
$strategy = new ReplicationStrategy();
$command = $this->getMockBuilder('Predis\Command\CommandInterface')->getMock();
$command
->expects($this->any())
->method('getId')
->will($this->returnValue('CMDTEST'));
$this->assertFalse($strategy->isReadOperation($command));
}
/**
* @group disconnected
*/
public function testCanSetCommandAsReadOperation()
{
$strategy = new ReplicationStrategy();
$command = $this->getMockBuilder('Predis\Command\CommandInterface')->getMock();
$command
->expects($this->any())
->method('getId')
->will($this->returnValue('CMDTEST'));
$strategy->setCommandReadOnly('CMDTEST', true);
$this->assertTrue($strategy->isReadOperation($command));
}
/**
* @group disconnected
*/
public function testCanSetCommandAsWriteOperation()
{
$strategy = new ReplicationStrategy();
$command = $this->getMockBuilder('Predis\Command\CommandInterface')->getMock();
$command
->expects($this->any())
->method('getId')
->will($this->returnValue('CMDTEST'));
$strategy->setCommandReadOnly('CMDTEST', false);
$this->assertFalse($strategy->isReadOperation($command));
$strategy->setCommandReadOnly('GET', false);
$this->assertFalse($strategy->isReadOperation($command));
}
/**
* @group disconnected
*/
public function testCanUseCallableToCheckCommand()
{
$commands = $this->getCommandFactory();
$strategy = new ReplicationStrategy();
$strategy->setCommandReadOnly('SET', function ($command) {
return $command->getArgument(1) === true;
});
$command = $commands->create('SET', array('trigger', false));
$this->assertFalse($strategy->isReadOperation($command));
$command = $commands->create('SET', array('trigger', true));
$this->assertTrue($strategy->isReadOperation($command));
}
/**
* @group disconnected
*/
public function testSetLuaScriptAsReadOperation()
{
$commands = $this->getCommandFactory();
$strategy = new ReplicationStrategy();
$writeScript = 'redis.call("set", "foo", "bar")';
$readScript = 'return true';
$strategy->setScriptReadOnly($readScript, true);
$cmdEval = $commands->create('EVAL', array($writeScript));
$cmdEvalSHA = $commands->create('EVALSHA', array(sha1($writeScript)));
$this->assertFalse($strategy->isReadOperation($cmdEval));
$this->assertFalse($strategy->isReadOperation($cmdEvalSHA));
$cmdEval = $commands->create('EVAL', array($readScript));
$cmdEvalSHA = $commands->create('EVALSHA', array(sha1($readScript)));
$this->assertTrue($strategy->isReadOperation($cmdEval));
$this->assertTrue($strategy->isReadOperation($cmdEvalSHA));
}
/**
* @group disconnected
*/
public function testSetLuaScriptAsReadOperationWorksWithScriptCommand()
{
$strategy = new ReplicationStrategy();
$command = $this->getMockBuilder('Predis\Command\ScriptCommand')
->setMethods(array('getScript'))
->getMock();
$command
->expects($this->any())
->method('getScript')
->will($this->returnValue($script = 'return true'));
$strategy->setScriptReadOnly($script, function ($command) {
return $command->getArgument(2) === true;
});
$command->setArguments(array(false));
$this->assertFalse($strategy->isReadOperation($command));
$command->setArguments(array(true));
$this->assertTrue($strategy->isReadOperation($command));
}
/**
* @group disconnected
*/
public function testSetLuaScriptAsReadOperationWorksWithScriptCommandAndCallableCheck()
{
$strategy = new ReplicationStrategy();
$command = $this->getMockBuilder('Predis\Command\ScriptCommand')
->setMethods(array('getScript'))
->getMock(array('getScript'));
$command
->expects($this->any())
->method('getScript')
->will($this->returnValue($script = 'return true'));
$command->setArguments(array('trigger', false));
$strategy->setScriptReadOnly($script, true);
$this->assertTrue($strategy->isReadOperation($command));
}
// ******************************************************************** //
// ---- HELPER METHODS ------------------------------------------------ //
// ******************************************************************** //
/**
* Returns the list of expected supported commands.
*
* @param string $type Optional type of command (based on its keys)
*
* @return array
*/
protected function getExpectedCommands($type = null)
{
$commands = array(
/* commands operating on the connection */
'AUTH' => 'read',
'SELECT' => 'read',
'ECHO' => 'read',
'QUIT' => 'read',
'OBJECT' => 'read',
'TIME' => 'read',
'SHUTDOWN' => 'disallowed',
'INFO' => 'disallowed',
'DBSIZE' => 'disallowed',
'LASTSAVE' => 'disallowed',
'CONFIG' => 'disallowed',
'MONITOR' => 'disallowed',
'SLAVEOF' => 'disallowed',
'SAVE' => 'disallowed',
'BGSAVE' => 'disallowed',
'BGREWRITEAOF' => 'disallowed',
'SLOWLOG' => 'disallowed',
/* commands operating on the key space */
'EXISTS' => 'read',
'DEL' => 'write',
'TYPE' => 'read',
'EXPIRE' => 'write',
'EXPIREAT' => 'write',
'PERSIST' => 'write',
'PEXPIRE' => 'write',
'PEXPIREAT' => 'write',
'TTL' => 'read',
'PTTL' => 'write',
'SORT' => 'variable',
'KEYS' => 'read',
'SCAN' => 'read',
'RANDOMKEY' => 'read',
/* commands operating on string values */
'APPEND' => 'write',
'DECR' => 'write',
'DECRBY' => 'write',
'GET' => 'read',
'GETBIT' => 'read',
'BITCOUNT' => 'read',
'BITPOS' => 'read',
'BITOP' => 'write',
'MGET' => 'read',
'SET' => 'write',
'GETRANGE' => 'read',
'GETSET' => 'write',
'INCR' => 'write',
'INCRBY' => 'write',
'INCRBYFLOAT' => 'write',
'SETBIT' => 'write',
'SETEX' => 'write',
'MSET' => 'write',
'MSETNX' => 'write',
'SETNX' => 'write',
'SETRANGE' => 'write',
'STRLEN' => 'read',
'SUBSTR' => 'read',
'BITFIELD' => 'variable',
/* commands operating on lists */
'LINSERT' => 'write',
'LINDEX' => 'read',
'LLEN' => 'read',
'LPOP' => 'write',
'RPOP' => 'write',
'BLPOP' => 'write',
'BRPOP' => 'write',
'LPUSH' => 'write',
'LPUSHX' => 'write',
'RPUSH' => 'write',
'RPUSHX' => 'write',
'LRANGE' => 'read',
'LREM' => 'write',
'LSET' => 'write',
'LTRIM' => 'write',
/* commands operating on sets */
'SADD' => 'write',
'SCARD' => 'read',
'SISMEMBER' => 'read',
'SMEMBERS' => 'read',
'SSCAN' => 'read',
'SRANDMEMBER' => 'read',
'SPOP' => 'write',
'SREM' => 'write',
'SINTER' => 'read',
'SUNION' => 'read',
'SDIFF' => 'read',
/* commands operating on sorted sets */
'ZADD' => 'write',
'ZCARD' => 'read',
'ZCOUNT' => 'read',
'ZINCRBY' => 'write',
'ZRANGE' => 'read',
'ZRANGEBYSCORE' => 'read',
'ZRANK' => 'read',
'ZREM' => 'write',
'ZREMRANGEBYRANK' => 'write',
'ZREMRANGEBYSCORE' => 'write',
'ZREVRANGE' => 'read',
'ZREVRANGEBYSCORE' => 'read',
'ZREVRANK' => 'read',
'ZSCORE' => 'read',
'ZSCAN' => 'read',
'ZLEXCOUNT' => 'read',
'ZRANGEBYLEX' => 'read',
'ZREMRANGEBYLEX' => 'write',
'ZREVRANGEBYLEX' => 'read',
/* commands operating on hashes */
'HDEL' => 'write',
'HEXISTS' => 'read',
'HGET' => 'read',
'HGETALL' => 'read',
'HMGET' => 'read',
'HINCRBY' => 'write',
'HINCRBYFLOAT' => 'write',
'HKEYS' => 'read',
'HLEN' => 'read',
'HSET' => 'write',
'HSETNX' => 'write',
'HVALS' => 'read',
'HSCAN' => 'read',
'HSTRLEN' => 'read',
/* commands operating on HyperLogLog */
'PFADD' => 'write',
'PFMERGE' => 'write',
'PFCOUNT' => 'read',
/* scripting */
'EVAL' => 'write',
'EVALSHA' => 'write',
/* commands performing geospatial operations */
'GEOADD' => 'write',
'GEOHASH' => 'read',
'GEOPOS' => 'read',
'GEODIST' => 'read',
'GEORADIUS' => 'variable',
'GEORADIUSBYMEMBER' => 'variable',
);
if (isset($type)) {
$commands = array_filter($commands, function ($expectedType) use ($type) {
return $expectedType === $type;
});
}
return array_keys($commands);
}
}