Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions lib/Redis/DualRedis.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

class sspmod_redis_Redis_DualRedis
{
public function __construct($oldHost, $newHost)
{
$this->oldHost = $oldHost;
$this->newHost = $newHost;
}

public function get($key)
{
$res = $this->newHost->get($key);
if (is_null($res)) {
$res = $this->oldHost->get($key);
}
return $res;
}

public function set($key, $value)
{
$this->newHost->set($key, $value);
}

public function keys($pattern)
{
return array_unique(array_merge(
$this->newHost->keys($pattern),
$this->oldHost->keys($pattern)
));
}

public function del($key)
{
$this->newHost->del($key);
$this->oldHost->del($key);
}

public function expireat($key, $timestamp)
{
$this->newHost->expireat($key, $timestamp);
}

public function expire($key, $delta)
{
$this->newHost->expire($key, $delta);
}

public function exists($key)
{
return $this->newHost->exists($key) || $this->oldHost->exists($key);
}
}
9 changes: 8 additions & 1 deletion lib/Store/Redis.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ public function __construct()
{
$redisConfig = SimpleSAML_Configuration::getConfig('module_redis.php');

$this->redis = new Predis\Client($redisConfig->getString('host', 'localhost'));
if ($redisConfig->hasValue('host')) {
$this->redis = new Predis\Client($redisConfig->getString('host', 'localhost'));
} else {
$this->redis = new sspmod_redis_Redis_DualRedis(
new Predis\Client($redisConfig->getString('old_host')),
new Predis\Client($redisConfig->getString('new_host'))
);
}
$this->prefix = $redisConfig->getString('prefix', 'simpleSAMLphp');
$this->lifeTime = $redisConfig->getInteger('lifetime', 28800); // 8 hours
}
Expand Down
153 changes: 153 additions & 0 deletions test/Redis/DualRedisTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?php

use Nulpunkt\PhpStub\Stub;

class DualRedisTest extends \PHPUnit_Framework_TestCase
{
private function mockPredisExpectingGetKeyAndReturnsValue($key, $value)
{
$mock = $this->getMock('\Predis\Client', ['get'], [null]);
$mock->expects($this->once())
->method("get")
->with($key)
->will($this->returnValue($value));
return $mock;
}

private function mockPredisExpectingSetKeyValue($key, $value)
{
$mock = $this->getMock('\Predis\Client', ['set'], [null]);
$mock->expects($this->once())
->method("set")
->with($key, $value);
return $mock;
}

private function mockPredisWithKeys($pattern, $keys)
{
$mock = $this->getMock('\Predis\Client', ['keys'], [null]);
$mock->expects($this->once())
->method("keys")
->with($pattern)
->will($this->returnValue($keys));
return $mock;
}

private function mockPredisExpectingDelete($key)
{
$mock = $this->getMock('\Predis\Client', ['del'], [null]);
$mock->expects($this->once())
->method("del")
->with($key);
return $mock;
}

private function mockPredisExpectingExpireat($key, $timestamp)
{
$mock = $this->getMock('\Predis\Client', ['expireat'], [null]);
$mock->expects($this->once())
->method("expireat")
->with($key, $timestamp);
return $mock;
}

private function mockPredisExpectingExpire($key, $delta)
{
$mock = $this->getMock('\Predis\Client', ['expire'], [null]);
$mock->expects($this->once())
->method("expire")
->with($key, $delta);
return $mock;
}

private function mockPredisExpectingExists($key, $boolean)
{
$mock = $this->getMock('\Predis\Client', ['exists'], [null]);
$mock->expects($this->once())
->method("exists")
->with($key)
->will($this->returnValue($boolean));
return $mock;
}

public function testThatGetUsesNewHostIfItHasAValue()
{
$redis = new sspmod_redis_Redis_DualRedis(
null,
$this->mockPredisExpectingGetKeyAndReturnsValue("xyzzy", 42)
);
$this->assertSame(42, $redis->get("xyzzy"));
}

public function testThatGetUsesOldHostIfNewHostDoesNotHaveAValue()
{
$redis = new sspmod_redis_Redis_DualRedis(
$this->mockPredisExpectingGetKeyAndReturnsValue("xyzzy", 420),
$this->mockPredisExpectingGetKeyAndReturnsValue("xyzzy", null)
);
$this->assertSame(420, $redis->get("xyzzy"));
}

public function testThatSetUsesNewHost()
{
$redis = new sspmod_redis_Redis_DualRedis(
null,
$this->mockPredisExpectingSetKeyValue("xyzzy", 42)
);
$redis->set("xyzzy", 42);
}

public function testThatKeysReturnsUniqueKeysFromBothOldAndNewHost()
{
$redis = new sspmod_redis_Redis_DualRedis(
$this->mockPredisWithKeys("foo", ["a", "b"]),
$this->mockPredisWithKeys("foo", ["b", "c", "d"])
);
$this->assertSame(["b", "c", "d", "a"], $redis->keys("foo"));
}

public function testThatDeleteUsesBothHosts()
{
$redis = new sspmod_redis_Redis_DualRedis(
$this->mockPredisExpectingDelete("quux"),
$this->mockPredisExpectingDelete("quux")
);
$redis->del("quux");
}

public function testThatExpireatOnlyUsesNewHost()
{
$redis = new sspmod_redis_Redis_DualRedis(
null,
$this->mockPredisExpectingExpireat("quux", 1234)
);
$redis->expireat("quux", 1234);
}

public function testThatExpireOnlyUsesNewHost()
{
$redis = new sspmod_redis_Redis_DualRedis(
null,
$this->mockPredisExpectingExpire("quux", 4444)
);
$redis->expire("quux", 4444);
}

public function testThatExistsUsesNewHostFirst()
{
$redis = new sspmod_redis_Redis_DualRedis(
null,
$this->mockPredisExpectingExists("quux", true)
);
$this->assertTrue($redis->exists("quux"));
}

public function testThatExistsUsesOldHostSecond()
{
$redis = new sspmod_redis_Redis_DualRedis(
$this->mockPredisExpectingExists("quux", true),
$this->mockPredisExpectingExists("quux", false)
);
$this->assertTrue($redis->exists("quux"));
}
}
64 changes: 54 additions & 10 deletions test/Store/RedisTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ class Client
public static $expireValue = null;
public static $deleteKey = null;

public function __construct($host)
{
}

public function get($key)
{
self::$getKey = $key;
Expand Down Expand Up @@ -48,28 +52,40 @@ class SimpleSAML_Store

class SimpleSAML_Configuration
{
private static $hasHost;

public static function getConfig()
{
return new SimpleSAML_Configuration;
}

public function getString($value)
public static function setHasHost($boolean)
{
self::$hasHost = $boolean;
}

public function getString($key)
{
if ($value == 'host') {
if (in_array($key, ["host", "old_host", "new_host"])) {
return 'localhost';
}
if ($value == 'prefix') {
if ($key == 'prefix') {
return 'simpleSAMLphp';
}
throw new ErrorException('Called with unexpected value');
throw new ErrorException('Called with unexpected key');
}

public function hasValue($key)
{
return self::$hasHost && $key == "host";
}

public function getInteger($key)
{
if ($key == 'lifetime') {
return 288000;
}
throw new ErrorException('Called with unexpected value');
throw new ErrorException('Called with unexpected key');
}
}

Expand All @@ -85,8 +101,20 @@ public function setUp()
Predis\Client::$deleteKey = null;
}

public function testSetKeyInRedis()
public function getHasHostValues()
{
return [
[true],
[false]
];
}

/**
* @dataProvider getHasHostValues
*/
public function testSetKeyInRedis($hasHost)
{
SimpleSAML_Configuration::setHasHost($hasHost);
$store = new sspmod_redis_Store_Redis();
$store->set('test', 'key', ['one', 'two']);

Expand All @@ -100,8 +128,12 @@ public function testSetKeyInRedis()
//$this->assertEquals(1427739616, \Predis\Client::$expireValue);
}

public function testSetKeyWithExpireInRedis()
/**
* @dataProvider getHasHostValues
*/
public function testSetKeyWithExpireInRedis($hasHost)
{
SimpleSAML_Configuration::setHasHost($hasHost);
$store = new sspmod_redis_Store_Redis();
$store->set('test', 'key', ['one', 'two'], 11);

Expand All @@ -111,26 +143,38 @@ public function testSetKeyWithExpireInRedis()
$this->assertEquals(11, Predis\Client::$expireValue);
}

public function testGetExistingKey()
/**
* @dataProvider getHasHostValues
*/
public function testGetExistingKey($hasHost)
{
SimpleSAML_Configuration::setHasHost($hasHost);
$store = new sspmod_redis_Store_Redis();
$res = $store->get('test', 'key');

$this->assertEquals('simpleSAMLphp.test.key', Predis\Client::$getKey);
$this->assertEquals(['ding' => 'bat'], $res);
}

public function testGetNonExistingKey()
/**
* @dataProvider getHasHostValues
*/
public function testGetNonExistingKey($hasHost)
{
SimpleSAML_Configuration::setHasHost($hasHost);
$store = new sspmod_redis_Store_Redis();
$res = $store->get('test', 'nokey');

$this->assertEquals('simpleSAMLphp.test.nokey', Predis\Client::$getKey);
$this->assertNull($res);
}

public function testDeleteKey()
/**
* @dataProvider getHasHostValues
*/
public function testDeleteKey($hasHost)
{
SimpleSAML_Configuration::setHasHost($hasHost);
$store = new sspmod_redis_Store_Redis();
$res = $store->delete('test', 'nokey');

Expand Down