You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
484 lines
20 KiB
484 lines
20 KiB
'use strict'; |
|
|
|
var intercept = require('intercept-stdout'); |
|
var assert = require('assert'); |
|
var Parser = require('../'); |
|
var parsers = ['javascript', 'hiredis']; |
|
|
|
// Mock the not needed return functions |
|
function returnReply () { throw new Error('failed'); } |
|
function returnError () { throw new Error('failed'); } |
|
function returnFatalError () { throw new Error('failed'); } |
|
|
|
describe('parsers', function () { |
|
|
|
describe('general parser functionality', function () { |
|
|
|
it('use default values', function () { |
|
var parser = new Parser({ |
|
returnReply: returnReply, |
|
returnError: returnError |
|
}); |
|
assert.strictEqual(parser.returnError, parser.returnFatalError); |
|
assert.strictEqual(parser.name, 'hiredis'); |
|
}); |
|
|
|
it('auto parser', function () { |
|
var parser = new Parser({ |
|
returnReply: returnReply, |
|
returnError: returnError, |
|
name: 'auto' |
|
}); |
|
assert.strictEqual(parser.name, 'hiredis'); |
|
}); |
|
|
|
it('auto parser v2', function () { |
|
var parser = new Parser({ |
|
returnReply: returnReply, |
|
returnError: returnError, |
|
name: null |
|
}); |
|
assert.strictEqual(parser.name, 'hiredis'); |
|
}); |
|
|
|
it('fail for missing options', function () { |
|
assert.throws(function() { |
|
new Parser({ |
|
returnReply: returnReply, |
|
returnBuffers: true |
|
}); |
|
}, function (err) { |
|
assert.strictEqual(err.message, 'Please provide all return functions while initiating the parser'); |
|
return true; |
|
}); |
|
|
|
}); |
|
|
|
it('unknown parser', function () { |
|
var str = ''; |
|
var unhookIntercept = intercept(function (data) { |
|
str += data; |
|
return ''; |
|
}); |
|
var parser = new Parser({ |
|
returnReply: returnReply, |
|
returnError: returnError, |
|
name: 'something_unknown' |
|
}); |
|
unhookIntercept(); |
|
assert.strictEqual(parser.name, 'hiredis'); |
|
assert(/^Warning: The requested parser "something_unknown" is unkown and the default parser is choosen instead\.\n +at new Parser/.test(str), str); |
|
}); |
|
|
|
it('hiredis and stringNumbers', function () { |
|
var str = ''; |
|
var unhookIntercept = intercept(function (data) { |
|
str += data; |
|
return ''; |
|
}); |
|
var parser = new Parser({ |
|
returnReply: returnReply, |
|
returnError: returnError, |
|
name: 'hiredis', |
|
stringNumbers: true |
|
}); |
|
unhookIntercept(); |
|
assert.strictEqual(parser.name, 'javascript'); |
|
assert(/^Warning: You explicitly required the hiredis parser in combination with the stringNumbers option. .+.\.\n +at new Parser/.test(str), str); |
|
}); |
|
|
|
}); |
|
|
|
parsers.forEach(function (parserName) { |
|
|
|
describe(parserName, function () { |
|
|
|
it('handles multi-bulk reply and check context binding', function () { |
|
var replyCount = 0; |
|
function Abc () {} |
|
Abc.prototype.checkReply = function (reply) { |
|
assert.strictEqual(typeof this.log, 'function'); |
|
assert.deepEqual(reply, [['a']], 'Expecting multi-bulk reply of [["a"]]'); |
|
replyCount++; |
|
}; |
|
Abc.prototype.log = console.log; |
|
var test = new Abc(); |
|
var parser = new Parser({ |
|
returnReply: function (reply) { |
|
test.checkReply(reply); |
|
}, |
|
returnError: returnError, |
|
returnFatalError: returnFatalError, |
|
name: parserName |
|
}); |
|
|
|
parser.execute(new Buffer('*1\r\n*1\r\n$1\r\na\r\n')); |
|
assert.strictEqual(replyCount, 1); |
|
|
|
parser.execute(new Buffer('*1\r\n*1\r')); |
|
parser.execute(new Buffer('\n$1\r\na\r\n')); |
|
assert.strictEqual(replyCount, 2); |
|
|
|
parser.execute(new Buffer('*1\r\n*1\r\n')); |
|
parser.execute(new Buffer('$1\r\na\r\n')); |
|
|
|
assert.equal(replyCount, 3, 'check reply should have been called three times'); |
|
}); |
|
|
|
it('parser error', function () { |
|
var replyCount = 0; |
|
function Abc () {} |
|
Abc.prototype.checkReply = function (reply) { |
|
assert.strictEqual(typeof this.log, 'function'); |
|
assert.strictEqual(reply.message, 'Protocol error, got "a" as reply type byte'); |
|
replyCount++; |
|
}; |
|
Abc.prototype.log = console.log; |
|
var test = new Abc(); |
|
var parser = new Parser({ |
|
returnReply: returnReply, |
|
returnError: returnError, |
|
returnFatalError: function (err) { |
|
test.checkReply(err); |
|
}, |
|
name: parserName |
|
}); |
|
|
|
parser.execute(new Buffer('a*1\r*1\r$1`zasd\r\na')); |
|
assert.equal(replyCount, 1); |
|
}); |
|
|
|
it('parser error resets the buffer', function () { |
|
var replyCount = 0; |
|
var errCount = 0; |
|
function checkReply (reply) { |
|
assert.strictEqual(reply.length, 1); |
|
assert(Buffer.isBuffer(reply[0])); |
|
assert.strictEqual(reply[0].toString(), 'CCC'); |
|
replyCount++; |
|
} |
|
function checkError (err) { |
|
assert.strictEqual(err.message, 'Protocol error, got "b" as reply type byte'); |
|
errCount++; |
|
} |
|
var parser = new Parser({ |
|
returnReply: checkReply, |
|
returnError: returnError, |
|
returnFatalError: checkError, |
|
name: parserName, |
|
returnBuffers: true |
|
}); |
|
|
|
// The chunk contains valid data after the protocol error |
|
parser.execute(new Buffer('*1\r\n+CCC\r\nb$1\r\nz\r\n+abc\r\n')); |
|
assert.strictEqual(replyCount, 1); |
|
assert.strictEqual(errCount, 1); |
|
parser.execute(new Buffer('*1\r\n+CCC\r\n')); |
|
assert.strictEqual(replyCount, 2); |
|
}); |
|
|
|
it('parser error v3 without returnFatalError specified', function () { |
|
var replyCount = 0; |
|
var errCount = 0; |
|
function checkReply (reply) { |
|
assert.strictEqual(reply[0], 'OK'); |
|
replyCount++; |
|
} |
|
function checkError (err) { |
|
assert.strictEqual(err.message, 'Protocol error, got "\\n" as reply type byte'); |
|
errCount++; |
|
} |
|
var parser = new Parser({ |
|
returnReply: checkReply, |
|
returnError: checkError, |
|
name: parserName |
|
}); |
|
|
|
parser.execute(new Buffer('*1\r\n+OK\r\n\n+zasd\r\n')); |
|
assert.strictEqual(replyCount, 1); |
|
assert.strictEqual(errCount, 1); |
|
}); |
|
|
|
it('should handle \\r and \\n characters properly', function () { |
|
// If a string contains \r or \n characters it will always be send as a bulk string |
|
var replyCount = 0; |
|
var entries = ['foo\r', 'foo\r\nbar', '\r\nfoo', 'foo\r\n']; |
|
function checkReply (reply) { |
|
assert.strictEqual(reply, entries[replyCount]); |
|
replyCount++; |
|
} |
|
var parser = new Parser({ |
|
returnReply: checkReply, |
|
returnError: returnError, |
|
returnFatalError: returnFatalError, |
|
name: parserName |
|
}); |
|
|
|
parser.execute(new Buffer('$4\r\nfoo\r\r\n$8\r\nfoo\r\nbar\r\n$5\r\n\r\n')); |
|
assert.strictEqual(replyCount, 2); |
|
parser.execute(new Buffer('foo\r\n$5\r\nfoo\r\n\r\n')); |
|
assert.strictEqual(replyCount, 4); |
|
}); |
|
|
|
it('line breaks in the beginning of the last chunk', function () { |
|
var replyCount = 0; |
|
function checkReply(reply) { |
|
assert.deepEqual(reply, [['a']], 'Expecting multi-bulk reply of [["a"]]'); |
|
replyCount++; |
|
} |
|
var parser = new Parser({ |
|
returnReply: checkReply, |
|
returnError: returnError, |
|
returnFatalError: returnFatalError, |
|
name: parserName |
|
}); |
|
|
|
parser.execute(new Buffer('*1\r\n*1\r\n$1\r\na')); |
|
assert.equal(replyCount, 0); |
|
|
|
parser.execute(new Buffer('\r\n*1\r\n*1\r')); |
|
assert.equal(replyCount, 1); |
|
parser.execute(new Buffer('\n$1\r\na\r\n*1\r\n*1\r\n$1\r\na\r\n')); |
|
|
|
assert.equal(replyCount, 3, 'check reply should have been called three times'); |
|
}); |
|
|
|
it('multiple chunks in a bulk string', function () { |
|
var replyCount = 0; |
|
function checkReply(reply) { |
|
assert.strictEqual(reply, 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij'); |
|
replyCount++; |
|
} |
|
var parser = new Parser({ |
|
returnReply: checkReply, |
|
returnError: returnError, |
|
returnFatalError: returnFatalError, |
|
name: parserName |
|
}); |
|
|
|
parser.execute(new Buffer('$100\r\nabcdefghij')); |
|
parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); |
|
parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); |
|
parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); |
|
assert.strictEqual(replyCount, 0); |
|
parser.execute(new Buffer('\r\n')); |
|
assert.strictEqual(replyCount, 1); |
|
|
|
parser.execute(new Buffer('$100\r')); |
|
parser.execute(new Buffer('\nabcdefghijabcdefghijabcdefghijabcdefghij')); |
|
parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); |
|
parser.execute(new Buffer('abcdefghijabcdefghij')); |
|
assert.strictEqual(replyCount, 1); |
|
parser.execute(new Buffer( |
|
'abcdefghij\r\n' + |
|
'$100\r\nabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij\r\n' + |
|
'$100\r\nabcdefghijabcdefghijabcdefghijabcdefghij' |
|
)); |
|
assert.strictEqual(replyCount, 3); |
|
parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); |
|
parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij\r')); |
|
assert.strictEqual(replyCount, 3); |
|
parser.execute(new Buffer('\n')); |
|
|
|
assert.equal(replyCount, 4, 'check reply should have been called three times'); |
|
}); |
|
|
|
it('multiple chunks with arrays different types', function () { |
|
var replyCount = 0; |
|
var predefined_data = [ |
|
'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij', |
|
'test', |
|
100, |
|
new Error('Error message'), |
|
['The force awakens'] |
|
]; |
|
function checkReply(reply) { |
|
for (var i = 0; i < reply.length; i++) { |
|
if (i < 3) { |
|
assert.strictEqual(reply[i], predefined_data[i]); |
|
} else { |
|
assert.deepEqual(reply[i], predefined_data[i]); |
|
} |
|
} |
|
replyCount++; |
|
} |
|
var parser = new Parser({ |
|
returnReply: checkReply, |
|
returnError: returnError, |
|
returnFatalError: returnFatalError, |
|
name: parserName, |
|
returnBuffers: false |
|
}); |
|
|
|
parser.execute(new Buffer('*5\r\n$100\r\nabcdefghij')); |
|
parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); |
|
parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij')); |
|
parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij\r\n')); |
|
parser.execute(new Buffer('+test\r')); |
|
parser.execute(new Buffer('\n:100')); |
|
parser.execute(new Buffer('\r\n-Error message')); |
|
parser.execute(new Buffer('\r\n*1\r\n$17\r\nThe force')); |
|
assert.strictEqual(replyCount, 0); |
|
parser.execute(new Buffer(' awakens\r\n$5')); |
|
assert.strictEqual(replyCount, 1); |
|
}); |
|
|
|
it('return normal errors', function () { |
|
var replyCount = 0; |
|
function checkReply(reply) { |
|
assert.equal(reply.message, 'Error message'); |
|
replyCount++; |
|
} |
|
var parser = new Parser({ |
|
returnReply: returnError, |
|
returnError: checkReply, |
|
returnFatalError: returnFatalError, |
|
name: parserName |
|
}); |
|
|
|
parser.execute(new Buffer('-Error ')); |
|
parser.execute(new Buffer('message\r\n*3\r\n$17\r\nThe force')); |
|
assert.strictEqual(replyCount, 1); |
|
parser.execute(new Buffer(' awakens\r\n$5')); |
|
assert.strictEqual(replyCount, 1); |
|
}); |
|
|
|
it('return null for empty arrays and empty bulk strings', function () { |
|
var replyCount = 0; |
|
function checkReply(reply) { |
|
assert.equal(reply, null); |
|
replyCount++; |
|
} |
|
var parser = new Parser({ |
|
returnReply: checkReply, |
|
returnError: returnError, |
|
returnFatalError: returnFatalError, |
|
name: parserName |
|
}); |
|
|
|
parser.execute(new Buffer('$-1\r\n*-')); |
|
assert.strictEqual(replyCount, 1); |
|
parser.execute(new Buffer('1')); |
|
assert.strictEqual(replyCount, 1); |
|
parser.execute(new Buffer('\r\n$-')); |
|
assert.strictEqual(replyCount, 2); |
|
}); |
|
|
|
it('return value even if all chunks are only 1 character long', function () { |
|
var replyCount = 0; |
|
function checkReply(reply) { |
|
assert.equal(reply, 1); |
|
replyCount++; |
|
} |
|
var parser = new Parser({ |
|
returnReply: checkReply, |
|
returnError: returnError, |
|
returnFatalError: returnFatalError, |
|
name: parserName |
|
}); |
|
|
|
parser.execute(new Buffer(':')); |
|
assert.strictEqual(replyCount, 0); |
|
parser.execute(new Buffer('1')); |
|
assert.strictEqual(replyCount, 0); |
|
parser.execute(new Buffer('\r')); |
|
assert.strictEqual(replyCount, 0); |
|
parser.execute(new Buffer('\n')); |
|
assert.strictEqual(replyCount, 1); |
|
}); |
|
|
|
it('do not return before \\r\\n', function () { |
|
var replyCount = 0; |
|
function checkReply(reply) { |
|
assert.equal(reply, 1); |
|
replyCount++; |
|
} |
|
var parser = new Parser({ |
|
returnReply: checkReply, |
|
returnError: returnError, |
|
returnFatalError: returnFatalError, |
|
name: parserName |
|
}); |
|
|
|
parser.execute(new Buffer(':1\r\n:')); |
|
assert.strictEqual(replyCount, 1); |
|
parser.execute(new Buffer('1')); |
|
assert.strictEqual(replyCount, 1); |
|
parser.execute(new Buffer('\r')); |
|
assert.strictEqual(replyCount, 1); |
|
parser.execute(new Buffer('\n')); |
|
assert.strictEqual(replyCount, 2); |
|
}); |
|
|
|
it('return data as buffer if requested', function () { |
|
var replyCount = 0; |
|
function checkReply(reply) { |
|
if (Array.isArray(reply)) { |
|
reply = reply[0]; |
|
} |
|
assert(Buffer.isBuffer(reply)); |
|
assert.strictEqual(reply.inspect(), new Buffer('test').inspect()); |
|
replyCount++; |
|
} |
|
var parser = new Parser({ |
|
returnReply: checkReply, |
|
returnError: returnError, |
|
returnFatalError: returnFatalError, |
|
name: parserName, |
|
returnBuffers: true |
|
}); |
|
|
|
parser.execute(new Buffer('+test\r\n')); |
|
assert.strictEqual(replyCount, 1); |
|
parser.execute(new Buffer('$4\r\ntest\r\n')); |
|
assert.strictEqual(replyCount, 2); |
|
parser.execute(new Buffer('*1\r\n$4\r\ntest\r\n')); |
|
assert.strictEqual(replyCount, 3); |
|
}); |
|
|
|
it('handle special case buffer sizes properly', function () { |
|
var replyCount = 0; |
|
var entries = ['test test ', 'test test test test ', 1234]; |
|
function checkReply(reply) { |
|
assert.strictEqual(reply, entries[replyCount]); |
|
replyCount++; |
|
} |
|
var parser = new Parser({ |
|
returnReply: checkReply, |
|
returnError: returnError, |
|
returnFatalError: returnFatalError, |
|
name: parserName |
|
}); |
|
parser.execute(new Buffer('$10\r\ntest ')); |
|
assert.strictEqual(replyCount, 0); |
|
parser.execute(new Buffer('test \r\n$20\r\ntest test test test \r\n:1234\r')); |
|
assert.strictEqual(replyCount, 2); |
|
parser.execute(new Buffer('\n')); |
|
assert.strictEqual(replyCount, 3); |
|
}); |
|
|
|
it('return numbers as strings', function () { |
|
var replyCount = 0; |
|
var entries = ['123', '590295810358705700002', '-99999999999999999']; |
|
function checkReply(reply) { |
|
assert.strictEqual(typeof reply, 'string'); |
|
assert.strictEqual(reply, entries[replyCount]); |
|
replyCount++; |
|
} |
|
var unhookIntercept = intercept(function () { |
|
return ''; |
|
}); |
|
var parser = new Parser({ |
|
returnReply: checkReply, |
|
returnError: returnError, |
|
returnFatalError: returnFatalError, |
|
name: parserName, |
|
stringNumbers: true |
|
}); |
|
unhookIntercept(); |
|
parser.execute(new Buffer(':123\r\n:590295810358705700002\r\n:-99999999999999999\r\n')); |
|
assert.strictEqual(replyCount, 3); |
|
}); |
|
}); |
|
}); |
|
});
|
|
|