Skip to content
Open
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
43 changes: 1 addition & 42 deletions lib/keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,52 +93,11 @@ exports.ttl = function (mockInstance, key, callback) {
mockInstance._callCallback(callback, null, result);
};

/* Converting pattern into regex */
function patternToRegex(pattern) {

function process_plain(start, length) {
var plain = pattern.substr(start, length);
plain = plain.replace(/(\(|\)|\\|\.|\^|\$|\||\+)/gi
, function (spec) {
return '\\' + spec
});
plain = plain.replace('*', '.*');
plain = plain.replace('?', '.');
return plain;
}

var current_position = 0;
var parts = [];
var group_regex = /\[([^\]]+?)\]/ig;

var matches;
while (matches = group_regex.exec(pattern)) {
if (matches.index > 0) {
parts.push(process_plain(current_position, matches.index - current_position));
}
var groups = matches[1].split('');
for (var i in groups) {
groups[i] = groups[i].replace(/(\(|\)|\\|\.|\^|\$|\||\?|\+|\*)/gi
, function (spec) {
return '\\' + spec
});
}

var group = '(' + groups.join('|') + ')'
parts.push(group);
current_position = matches.index + matches[0].length;
}
if (current_position != pattern.length) {
parts.push(process_plain(current_position, pattern.length - current_position));
}
return new RegExp(parts.join(''));
}

/**
* Keys
*/
exports.keys = function (mockInstance, pattern, callback) {
var regex = patternToRegex(pattern);
var regex = mockInstance._patternToRegex(pattern);

var keys = [];
for (var key in mockInstance.storage) {
Expand Down
44 changes: 44 additions & 0 deletions lib/redis-mock.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,46 @@ function RedisMock() {
});
}
};
/* Converting pattern into regex */
this._patternToRegex = function (pattern) {

function process_plain(start, length) {
var plain = pattern.substr(start, length);
plain = plain.replace(/(\(|\)|\\|\.|\^|\$|\||\+)/gi
, function (spec) {
return '\\' + spec
});
plain = plain.replace('*', '.*');
plain = plain.replace('?', '.');
return plain;
}

var current_position = 0;
var parts = [];
var group_regex = /\[([^\]]+?)\]/ig;

var matches;
while (matches = group_regex.exec(pattern)) {
if (matches.index > 0) {
parts.push(process_plain(current_position, matches.index - current_position));
}
var groups = matches[1].split('');
for (var i in groups) {
groups[i] = groups[i].replace(/(\(|\)|\\|\.|\^|\$|\||\?|\+|\*)/gi
, function (spec) {
return '\\' + spec
});
}

var group = '(' + groups.join('|') + ')'
parts.push(group);
current_position = matches.index + matches[0].length;
}
if (current_position != pattern.length) {
parts.push(process_plain(current_position, pattern.length - current_position));
}
return new RegExp(parts.join(''));
};
}

/**
Expand Down Expand Up @@ -412,6 +452,10 @@ RedisClient.prototype.flushall = RedisClient.prototype.FLUSHALL = function (call

serverfunctions.flushall.call(this, MockInstance, callback);
}
RedisClient.prototype.scan = RedisClient.prototype.SCAN = function (args, callback) {

serverfunctions.scan.call(this, MockInstance, args, callback);
}

RedisMock.prototype.createClient = function (port_arg, host_arg, options) {

Expand Down
43 changes: 43 additions & 0 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,46 @@ exports.flushdb = flushdb = function (mockInstance, callback) {
* Exact the same as flushdb because multiple db is not supported yet
*/
exports.flushall = flushdb;

/**
* scan
*/
// TODO: Support expanded arguments
exports.scan = scan = function (mockInstance, args, callback) {
var cursor = args[0];
args = args.slice(1);

/* Simulate random max returned elements (10 - 35 keys) */
var count = Math.floor(Math.random() * 25 + cursor + 10)
var pattern = '*'

/* Parse arguments array */
for (var i = 0; i < args.length; i++) {
var arg = args[i];
if (typeof arg === 'string') arg = arg.toLowerCase();
if (i + 1 < args.length) {
if (arg === 'count') {
count = Number(args[i + 1]);
i++;
}
if (arg === 'match') {
pattern = args[i + 1];
i++;
}
}
}
var regex = mockInstance._patternToRegex(pattern);
var keys = [];

for (var key in mockInstance.storage) {
if (regex.test(key)) {
keys.push(key);
}
}

var matched = keys.slice(cursor, count + cursor);
var newCursor = Math.min(count, matched.length);
/* Give back 0 for cursor if all keys are included */
if (count + cursor >= keys.length) newCursor = 0;
return mockInstance._callCallback(callback, null, [newCursor, matched]);
}
121 changes: 121 additions & 0 deletions test/redis-mock.server.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,124 @@ describe("flushdb", function () {

});

describe("scan", function () {

it("should return cursor and keys", function (done) {

var r = redismock.createClient();

r.set("foo", "bar", function (err, result) {

r.scan([0], function (err, result) {

should(result instanceof Array).be.exactly(true);
should(result.length).be.exactly(2);
should(isNaN(result[0])).be.exactly(false);
should(result[0]).be.exactly(0);
should(result[1] instanceof Array).be.exactly(true);
should(result[1].length).be.exactly(1);

r.end();
done();

});

});

});

it("should work with patterns", function (done) {

var r = redismock.createClient();

r.set("foo", "a", function (err, result) {

r.set("family", "b", function (err, result) {

r.set("burger", "food", function (err, result) {

r.scan([0, 'MATCH', 'f*'], function (err, result) {

should(result instanceof Array).be.exactly(true);
should(result.length).be.exactly(2);
should(isNaN(result[0])).be.exactly(false);
should(result[1] instanceof Array).be.exactly(true);
should(result[1].length).be.exactly(2);

r.end();
done();

});

});

});

});

});

it("should work with count", function (done) {

var r = redismock.createClient();

r.set("foo", "a", function (err, result) {

r.set("family", "b", function (err, result) {

r.set("burger", "food", function (err, result) {

r.scan([0, 'COUNT', 1], function (err, result) {

should(result instanceof Array).be.exactly(true);
should(result.length).be.exactly(2);
should(isNaN(result[0])).be.exactly(false);
should(result[1] instanceof Array).be.exactly(true);
should(result[1].length).be.exactly(1);

r.end();
done();

});

});

});

});

});

it("should work with count, cursor, and patterns", function (done) {

var r = redismock.createClient();

r.set("foo", "a", function (err, result) {

r.set("family", "b", function (err, result) {

r.set("burger", "food", function (err, result) {

r.scan([1, 'COUNT', 1, 'MATCH', 'f*'], function (err, result) {

should(result instanceof Array).be.exactly(true);
should(result.length).be.exactly(2);
should(isNaN(result[0])).be.exactly(false);
should(result[1] instanceof Array).be.exactly(true);
should(result[1].length).be.exactly(1);
should(result[1][0]).be.exactly('family');

r.end();
done();

});

});

});

});

});

});