Files
predis/tests/Predis/Replication/ReplicationStrategyTest.php
T
Daniele Alessandri 62b421f20e Switch from server profiles to commands factory.
This change reduces some unnecessary complexity in the library, Redis
commands do not change much after all. Developers can still implement
their own commands factory, inject new commands or override existing
ones. The "profile" client options has been renamed to "commands" and
it accepts instances of Predis\Command\FactoryInterface.

The test suite checks at runtime the version of the running instance
of Redis for integration tests to adapt itself automatically.
2016-06-04 15:36:21 +02:00

513 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->createCommand($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->createCommand($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->createCommand($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();
$cmdReadSort = $commands->createCommand('SORT', array('key:list'));
$this->assertTrue(
$strategy->isReadOperation($cmdReadSort),
'SORT is expected to be a read operation.'
);
$cmdWriteSort = $commands->createCommand('SORT', array('key:list', array('store' => 'key:stored')));
$this->assertFalse(
$strategy->isReadOperation($cmdWriteSort),
'SORT with STORE is expected to be a write operation.'
);
}
/**
* @group disconnected
*/
public function testBitFieldCommand()
{
$commands = $this->getCommandFactory();
$strategy = new ReplicationStrategy();
$command = $commands->createCommand('BITFIELD', array('key'));
$this->assertTrue(
$strategy->isReadOperation($command),
'BITFIELD with no modifiers is expected to be a read operation.'
);
$command = $commands->createCommand('BITFIELD', array('key', 'GET', 'u4', '0'));
$this->assertTrue(
$strategy->isReadOperation($command),
'BITFIELD with GET only is expected to be a read operation.'
);
$command = $commands->createCommand('BITFIELD', array('key', 'SET', 'u4', '0', 1));
$this->assertFalse(
$strategy->isReadOperation($command),
'BITFIELD with SET is expected to be a write operation.'
);
$command = $commands->createCommand('BITFIELD', array('key', 'INCRBY', 'u4', '0', 1));
$this->assertFalse(
$strategy->isReadOperation($command),
'BITFIELD with INCRBY is expected to be a write operation.'
);
$command = $commands->createCommand('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->createCommand('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->createCommand('GEORADIUS', array('key:geo', 15, 37, 200, 'km'));
$this->assertTrue(
$strategy->isReadOperation($command),
'GEORADIUS is expected to be a read operation.'
);
$command = $commands->createCommand('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->createCommand('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->createCommand('GEORADIUSBYMEMBER', array('key:geo', 15, 37, 200, 'km'));
$this->assertTrue(
$strategy->isReadOperation($command),
'GEORADIUSBYMEMBER is expected to be a read operation.'
);
$command = $commands->createCommand('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->createCommand('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
* @expectedException \Predis\NotSupportedException
* @expectedExceptionMessage The command 'INFO' is not allowed in replication mode.
*/
public function testUsingDisallowedCommandThrowsException()
{
$commands = $this->getCommandFactory();
$strategy = new ReplicationStrategy();
$command = $commands->createCommand('INFO');
$strategy->isReadOperation($command);
}
/**
* @group disconnected
*/
public function testDefaultIsWriteOperation()
{
$strategy = new ReplicationStrategy();
$command = $this->getMock('Predis\Command\CommandInterface');
$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->getMock('Predis\Command\CommandInterface');
$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->getMock('Predis\Command\CommandInterface');
$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->createCommand('SET', array('trigger', false));
$this->assertFalse($strategy->isReadOperation($command));
$command = $commands->createCommand('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->createCommand('EVAL', array($writeScript));
$cmdEvalSHA = $commands->createCommand('EVALSHA', array(sha1($writeScript)));
$this->assertFalse($strategy->isReadOperation($cmdEval));
$this->assertFalse($strategy->isReadOperation($cmdEvalSHA));
$cmdEval = $commands->createCommand('EVAL', array($readScript));
$cmdEvalSHA = $commands->createCommand('EVALSHA', array(sha1($readScript)));
$this->assertTrue($strategy->isReadOperation($cmdEval));
$this->assertTrue($strategy->isReadOperation($cmdEvalSHA));
}
/**
* @group disconnected
*/
public function testSetLuaScriptAsReadOperationWorksWithScriptCommand()
{
$strategy = new ReplicationStrategy();
$command = $this->getMock('Predis\Command\ScriptCommand', array('getScript'));
$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->getMock('Predis\Command\ScriptCommand', 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);
}
}