Skip to content

Commit

Permalink
init t PR (add supports for redis commands)
Browse files Browse the repository at this point in the history
fix s comment, syntax and style

Improve version of predis

ImAdd Method implementation and tests

update the readme

test tests...

Fix  old test

remove useless predis/client method on the redis mock, add rpoplpush

add  constructorParameters and Scan command on redisMock

Add comments and readme informations

remove useless code

Fix method definition after tests

FiFix scan of redis mock

FiFix style

Reorder Readme list of functions

Add the scan test and fix scan count

Fix TTL test based from sleep...

Fix TTL test based from sleep...

Fix TTL test based from sleep...

Fix TTL test based from sleep...
  • Loading branch information
Romuald PRIOL committed Mar 23, 2018
1 parent c59b026 commit 67c4384
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 33 deletions.
43 changes: 23 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Add this line in your `composer.json` :

Update your vendors :

```
```bash
$ composer update m6web/redis-mock
```

Expand All @@ -28,27 +28,14 @@ It currently mocks these Redis commands :

Redis command | Description
-------------------------------------------------|------------
**DBSIZE** | Returns the number of keys in the selected database
**DEL** *key* *[key ...]* | Deletes one or more keys
**DECR** *key* | Decrements the integer value of a key by one
**DECRBY** *key* *decrement* | Decrements the integer value of a key by `decrement` value
**EXISTS** *key* | Determines if a key exists
**EXPIRE** *key* *seconds* | Sets a key's time to live in seconds
**KEYS** *pattern* | Finds all keys matching the given pattern
**TTL** *key* | Gets the time to live for a key
**TYPE** *key* | Returns the string representation of the type of the value stored at key.
**FLUSHDB** | Flushes the database
**GET** *key* | Gets the value of a key
**INCR** *key* | Increments the integer value of a key by one
**INCRBY** *key* *increment* | Increments the integer value of a key by `increment` value
**DECR** *key* | Decrements the integer value of a key by one
**DECRBY** *key* *decrement* | Decrements the integer value of a key by `decrement` value
**SET** *key* *value* | Sets the string value of a key
**SETEX** *key* *seconds* *value* | Sets the value and expiration of a key
**SETNX** *key* *value* | Sets key to hold value if key does not exist
**SADD** *key* *member* *[member ...]* | Adds one or more members to a set
**SISMEMBER** *key* *member* | Determines if a member is in a set
**SMEMBERS** *key* | Gets all the members in a set
**SUNION** *key* *[key ...]* | Returns the members of the set resulting from the union of all the given sets.
**SINTER** *key* *[key ...]* | Returns the members of the set resulting from the intersection of all the given sets.
**SCARD** *key* | Get cardinality of set (count of members)
**SREM** *key* *member* *[member ...]* | Removes one or more members from a set
**HDEL** *key* *field* | Delete one hash fields
**HEXISTS** *key* *field* | Determines if a hash field exists
**HMGET** *key* *array\<field\>* | Gets the values of multiple hash fields
Expand All @@ -60,6 +47,9 @@ Redis command | Description
**HSET** *key* *field* *value* | Sets the string value of a hash field
**HSETNX** *key* *field* *value* | Sets field in the hash stored at key to value, only if field does not yet exist
**HINCRBY** *key* *field* *increment* | Increments the integer stored at `field` in the hash stored at `key` by `increment`.
**INCR** *key* | Increments the integer value of a key by one
**INCRBY** *key* *increment* | Increments the integer value of a key by `increment` value
**KEYS** *pattern* | Finds all keys matching the given pattern
**LINDEX** *key* *index* | Returns the element at index *index* in the list stored at *key*
**LLEN** *key* | Returns the length of the list stored at *key*
**LPUSH** *key* *value* *[value ...]* | Pushs values at the head of a list
Expand All @@ -69,8 +59,22 @@ Redis command | Description
**LRANGE** *key* *start* *stop* | Gets a range of elements from a list
**MGET** *array\<field\>* | Gets the values of multiple keys
**MSET** *array\<field, value\>* | Sets the string values of multiple keys
**QUIT** | Quit the REDIS
**RPUSH** *key* *value* | Pushs values at the tail of a list
**RPOP** *key* | Pops values at the tail of a list
**SCAN** | Iterates the set of keys in the currently selected Redis database.
**SET** *key* *value* | Sets the string value of a key
**SETEX** *key* *seconds* *value* | Sets the value and expiration of a key
**SETNX** *key* *value* | Sets key to hold value if key does not exist
**SADD** *key* *member* *[member ...]* | Adds one or more members to a set
**SISMEMBER** *key* *member* | Determines if a member is in a set
**SMEMBERS** *key* | Gets all the members in a set
**SUNION** *key* *[key ...]* | Returns the members of the set resulting from the union of all the given sets.
**SINTER** *key* *[key ...]* | Returns the members of the set resulting from the intersection of all the given sets.
**SCARD** *key* | Get cardinality of set (count of members)
**SREM** *key* *member* *[member ...]* | Removes one or more members from a set
**TTL** *key* | Gets the time to live for a key
**TYPE** *key* | Returns the string representation of the type of the value stored at key.
**ZADD** *key* *score* *member* | Adds one member to a sorted set, or update its score if it already exists
**ZCARD** *key* | Returns the sorted set cardinality (number of elements) of the sorted set stored at *key*
**ZRANGE** *key* *start* *stop* *[withscores]* | Returns the specified range of members in a sorted set
Expand All @@ -80,8 +84,7 @@ Redis command | Description
**ZREMRANGEBYSCORE** *key* *min* *max* | Removes all members in a sorted set within the given scores
**ZREVRANGE** *key* *start* *stop* *[withscores]*| Returns the specified range of members in a sorted set, with scores ordered from high to low
**ZREVRANGEBYSCORE** *key* *min* *max* *options* | Returns a range of members in a sorted set, by score, with scores ordered from high to low
**DBSIZE** | Returns the number of keys in the selected database
**FLUSHDB** | Flushes the database


It mocks **MULTI**, **DISCARD** and **EXEC** commands but without any transaction behaviors, they just make the interface fluent and return each command results.
**PIPELINE** and **EXECUTE** pseudo commands (client pipelining) are also mocked.
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@
},
"require-dev": {
"atoum/atoum": "master-dev",
"predis/predis": "~0.8"
"predis/predis": "~1.1"
}
}
81 changes: 81 additions & 0 deletions src/M6Web/Component/RedisMock/RedisMock.php
Original file line number Diff line number Diff line change
Expand Up @@ -1079,4 +1079,85 @@ protected function deleteOnTtlExpired($key)

return false;
}

/**
* Mock the `quit` command
* @see https://redis.io/commands/quit
*/
public function quit(): string
{
return 'OK'; // see the doc : always return `OK` ...
}

/**
* Mock the `monitor` command
* @see https://redis.io/commands/monitor
*/
public function monitor()
{
return;
}

/**
* Mock the `scan` command
* @see https://redis.io/commands/scan
* @param int $cursor
* @param array $options contain options of the command, with values (ex ['MATCH' => 'st*', 'COUNT' => 42] )
*
* @return array
*/
public function scan($cursor = 0, array $options = []): array
{
// Define default options
$match = isset($options['MATCH']) ? $options['MATCH'] : '*';
$count = isset($options['COUNT']) ? $options['COUNT'] : 10;
$maximumValue = $cursor + $count -1;

// List of all keys in the storage (already ordered by index).
$keysArray = array_keys(self::$dataValues[$this->storage]);
$maximumListElement = count($keysArray);

// Next cursor position
$nextCursorPosition = 0;
// Matched values.
$values = [];
// Pattern, for find matched values.
$pattern = str_replace('*', '.*', sprintf('/^%s/', $match));

for($i = $cursor; $i <= $maximumValue; $i++)
{
if (isset($keysArray[$i])){
$nextCursorPosition = $i >= $maximumListElement ? 0 : $i + 1;

if ('*' === $match || 1 === preg_match($pattern, $keysArray[$i])){
$values[] = $keysArray[$i];
}

} else {
// Out of the arrays values, return first element
$nextCursorPosition = 0;
}
}

return [$nextCursorPosition, $values];
}

/**
* Mock the `rpoplpush` command
* @see https://redis.io/commands/rpoplpush
* @param string $sourceList
* @param string $destinationList
* @return RedisMock
*/
public function rpoplpush(string $sourceList, string $destinationList)
{
// RPOP (get last value of the $sourceList)
$rpopValue = $this->rpop($sourceList);

// LPUSH (send the value at the end of the $destinationList)
$this->lpush($destinationList, $rpopValue);

// return the rpop value;
return $rpopValue;
}
}
4 changes: 2 additions & 2 deletions src/M6Web/Component/RedisMock/RedisMockFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ public function __construct()
}
CONSTRUCTOR;

public function getAdapter($classToExtend, $failOnlyAtRuntime = false, $orphanizeConstructor = true, $storage = '')
public function getAdapter($classToExtend, $failOnlyAtRuntime = false, $orphanizeConstructor = true, $storage = '', array $constructorParams = [])
{
list($namespace, $newClassName, $class) = $this->getAdapterClassName($classToExtend, $orphanizeConstructor);

Expand All @@ -227,7 +227,7 @@ public function getAdapter($classToExtend, $failOnlyAtRuntime = false, $orphaniz
}

/** @var RedisMock $instance */
$instance = new $class();
$instance = new $class(...$constructorParams);
// This is our chance to configure explicitly the storage area
// that the consumer of the Mock wants to use, in order to simulate
// separate connections to different Redis servers, despite the static
Expand Down
60 changes: 50 additions & 10 deletions tests/units/RedisMock.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ public function testSetGetDelExists()
->string($redisMock->setex('test4', 2, 'something else'))
->isEqualTo('OK')
->integer($redisMock->ttl('test3'))
->isEqualTo(1)
->isLessThanOrEqualTo(1)
->integer($redisMock->ttl('test4'))
->isEqualTo(2)
->isLessThanOrEqualTo(2)
->string($redisMock->get('test3'))
->isEqualTo('something')
->string($redisMock->get('test4'))
Expand Down Expand Up @@ -173,30 +173,30 @@ public function testExpireTtl()
->integer($redisMock->expire('test', 2))
->isEqualTo(1)
->integer($redisMock->ttl('test'))
->isEqualTo(2);
->isGreaterThan(0);
sleep(1);
$this->assert
->integer($redisMock->ttl('test'))
->isEqualTo(1);
->isLessThanOrEqualTo(1);
sleep(2);
$this->assert
->integer($redisMock->ttl('test'))
->isEqualTo(-2);
->isLessThanOrEqualTo(-2);


$this->assert
->string($redisMock->set('test', 'something', 10))
->isEqualTo('OK')
->integer($redisMock->ttl('test'))
->isEqualTo(10)
->isLessThanOrEqualTo(10)
->integer($redisMock->expire('test', 1))
->isEqualTo(1)
->isLessThanOrEqualTo(1)
->integer($redisMock->ttl('test'))
->isEqualTo(1);
->isLessThanOrEqualTo(1);
sleep(2);
$this->assert
->integer($redisMock->expire('test', 10))
->isEqualTo(0);
->isLessThanOrEqualTo(0);
}

public function testIncr()
Expand Down Expand Up @@ -1549,7 +1549,7 @@ public function testDbsize()

$this->assert
->integer($redisMock->dbsize())
->isEqualTo(1);
-> isGreaterThanOrEqualTo(1);

$redisMock->flushdb();

Expand Down Expand Up @@ -1661,4 +1661,44 @@ public function testTwoSeparateStorage()
->isEqualTo('value2')
;
}

/**
* Check if the scan command works.
* With options. (Scan can have a COUNT, or MATCH options).
*/
public function testScanCommand()
{
$redisMock = new Redis();
$redisMock->lpush('myKey', 'myValue');
$redisMock->lpush('yourKey', 'yourValue');
$redisMock->lpush('ourKi', 'ourValue');
$redisMock->lpush('key4', 'value4');
$redisMock->lpush('key5', 'value5');
$redisMock->lpush('key6', 'value6');
$redisMock->lpush('key7', 'value7');
$redisMock->lpush('key8', 'value8');
$redisMock->lpush('key9', 'value9');
$redisMock->lpush('key10', 'value10');
$redisMock->lpush('key11', 'value11');
$redisMock->lpush('key12', 'value12');

// It must return two values, start cursor after the first value of the list.
$this->assert
->array($redisMock->scan(1, ['COUNT' => 2]))
->isEqualTo([3, [0 => 'yourKey', 1 => 'ourKi']]);


// It must return all the values with match with the regex 'our' (2 keys).
// And the cursor is defined after the default count (10) => the match has not terminate all the list.
$this->assert
->array($redisMock->scan(0, ['MATCH' => '*our*']))
->isEqualTo([10, [0 => 'yourKey', 1 => 'ourKi']]);

// Execute the match at the end of this list, the match not return an element (no one element match with the regex),
// And the list is terminate, return the cursor to the start (0)
$this->assert
->array($redisMock->scan(10, ['MATCH' => '*our*']))
->isEqualTo([0, []]);

}
}

0 comments on commit 67c4384

Please sign in to comment.