connect-redis
Advanced tools
Comparing version 3.4.2 to 4.0.0
@@ -1,1 +0,1 @@ | ||
module.exports = require('./lib/connect-redis'); | ||
module.exports = require('./lib/connect-redis') |
@@ -7,370 +7,155 @@ /*! | ||
var debug = require('debug')('connect:redis'); | ||
var util = require('util'); | ||
var noop = function(){}; | ||
module.exports = function(session) { | ||
const Store = session.Store | ||
/** | ||
* One day in seconds. | ||
*/ | ||
// All callbacks should have a noop if none provided for compatibility | ||
// with the most Redis clients. | ||
const noop = () => {} | ||
var oneDay = 86400; | ||
class RedisStore extends Store { | ||
constructor(options = {}) { | ||
super(options) | ||
if (!options.client) { | ||
throw new Error('A client must be directly provided to the RedisStore') | ||
} | ||
function getTTL(store, sess, sid) { | ||
if (typeof store.ttl === 'number' || typeof store.ttl === 'string') return store.ttl; | ||
if (typeof store.ttl === 'function') return store.ttl(store, sess, sid); | ||
if (store.ttl) throw new TypeError('`store.ttl` must be a number or function.'); | ||
this.prefix = options.prefix == null ? 'sess:' : options.prefix | ||
this.scanCount = Number(options.scanCount) || 100 | ||
this.serializer = options.serializer || JSON | ||
this.client = options.client | ||
this.ttl = options.ttl || 86400 // One day in seconds. | ||
this.disableTouch = options.disableTouch || false | ||
} | ||
var maxAge = sess.cookie.maxAge; | ||
return (typeof maxAge === 'number' | ||
? Math.floor(maxAge / 1000) | ||
: oneDay); | ||
} | ||
get(sid, cb = noop) { | ||
let key = this.prefix + sid | ||
/** | ||
* Return the `RedisStore` extending `express`'s session Store. | ||
* | ||
* @param {object} express session | ||
* @return {Function} | ||
* @api public | ||
*/ | ||
this.client.get(key, (er, data) => { | ||
if (er) return cb(er) | ||
if (!data) return cb() | ||
module.exports = function (session) { | ||
let result | ||
try { | ||
result = this.serializer.parse(data) | ||
} catch (er) { | ||
return cb(er) | ||
} | ||
return cb(null, result) | ||
}) | ||
} | ||
/** | ||
* Express's session Store. | ||
*/ | ||
set(sid, sess, cb = noop) { | ||
let args = [this.prefix + sid] | ||
var Store = session.Store; | ||
let value | ||
try { | ||
value = this.serializer.stringify(sess) | ||
} catch (er) { | ||
return cb(er) | ||
} | ||
args.push(value) | ||
args.push('EX', this._getTTL(sess)) | ||
/** | ||
* Initialize RedisStore with the given `options`. | ||
* | ||
* @param {Object} options | ||
* @api public | ||
*/ | ||
function RedisStore (options) { | ||
if (!(this instanceof RedisStore)) { | ||
throw new TypeError('Cannot call RedisStore constructor as a function'); | ||
this.client.set(args, cb) | ||
} | ||
var self = this; | ||
touch(sid, sess, cb = noop) { | ||
if (this.disableTouch) return cb() | ||
options = options || {}; | ||
Store.call(this, options); | ||
this.prefix = options.prefix == null | ||
? 'sess:' | ||
: options.prefix; | ||
delete options.prefix; | ||
this.scanCount = Number(options.scanCount) || 100; | ||
delete options.scanCount; | ||
this.serializer = options.serializer || JSON; | ||
if (options.url) { | ||
options.socket = options.url; | ||
// Since we need to update the expires value on the cookie, | ||
// we update the whole session object. | ||
this.set(sid, sess, cb) | ||
} | ||
// convert to redis connect params | ||
if (options.client) { | ||
this.client = options.client; | ||
} else { | ||
var redis = require('redis'); | ||
if (options.socket) { | ||
this.client = redis.createClient(options.socket, options); | ||
} | ||
else { | ||
this.client = redis.createClient(options); | ||
} | ||
destroy(sid, cb = noop) { | ||
let key = this.prefix + sid | ||
this.client.del(key, cb) | ||
} | ||
// logErrors | ||
if(options.logErrors){ | ||
// if options.logErrors is function, allow it to override. else provide default logger. useful for large scale deployment | ||
// which may need to write to a distributed log | ||
if(typeof options.logErrors != 'function'){ | ||
options.logErrors = function (err) { | ||
console.error('Warning: connect-redis reported a client error: ' + err); | ||
}; | ||
} | ||
this.client.on('error', options.logErrors); | ||
clear(cb = noop) { | ||
this._getAllKeys((err, keys) => { | ||
if (err) return cb(err) | ||
this.client.del(keys, cb) | ||
}) | ||
} | ||
if (options.pass) { | ||
this.client.auth(options.pass, function (err) { | ||
if (err) { | ||
throw err; | ||
} | ||
}); | ||
length(cb = noop) { | ||
this._getAllKeys((err, keys) => { | ||
if (err) return cb(err) | ||
return cb(null, keys.length) | ||
}) | ||
} | ||
this.ttl = options.ttl; | ||
this.disableTTL = options.disableTTL; | ||
ids(cb = noop) { | ||
let prefixLen = this.prefix.length | ||
if (options.unref) this.client.unref(); | ||
if ('db' in options) { | ||
if (typeof options.db !== 'number') { | ||
console.error('Warning: connect-redis expects a number for the "db" option'); | ||
} | ||
self.client.select(options.db); | ||
self.client.on('connect', function () { | ||
self.client.select(options.db); | ||
}); | ||
this._getAllKeys((err, keys) => { | ||
if (err) return cb(err) | ||
keys = keys.map(key => key.substr(prefixLen)) | ||
return cb(null, keys) | ||
}) | ||
} | ||
self.client.on('error', function (er) { | ||
debug('Redis returned err', er); | ||
self.emit('disconnect', er); | ||
}); | ||
all(cb = noop) { | ||
let prefixLen = this.prefix.length | ||
self.client.on('connect', function () { | ||
self.emit('connect'); | ||
}); | ||
} | ||
this._getAllKeys((err, keys) => { | ||
if (err) return cb(err) | ||
if (keys.length === 0) return cb(null, []) | ||
/** | ||
* Inherit from `Store`. | ||
*/ | ||
this.client.mget(keys, (err, sessions) => { | ||
if (err) return cb(err) | ||
util.inherits(RedisStore, Store); | ||
let result | ||
try { | ||
result = sessions.map((data, index) => { | ||
data = this.serializer.parse(data) | ||
data.id = keys[index].substr(prefixLen) | ||
return data | ||
}) | ||
} catch (e) { | ||
err = e | ||
} | ||
return cb(err, result) | ||
}) | ||
}) | ||
} | ||
/** | ||
* Attempt to fetch session by the given `sid`. | ||
* | ||
* @param {String} sid | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
RedisStore.prototype.get = function (sid, fn) { | ||
var store = this; | ||
var psid = store.prefix + sid; | ||
if (!fn) fn = noop; | ||
debug('GET "%s"', sid); | ||
store.client.get(psid, function (er, data) { | ||
if (er) return fn(er); | ||
if (!data) return fn(); | ||
var result; | ||
data = data.toString(); | ||
debug('GOT %s', data); | ||
try { | ||
result = store.serializer.parse(data); | ||
_getTTL(sess) { | ||
let ttl | ||
if (sess && sess.cookie && sess.cookie.expires) { | ||
let ms = Number(new Date(sess.cookie.expires)) - Date.now() | ||
ttl = Math.ceil(ms / 1000) | ||
} else { | ||
ttl = this.ttl | ||
} | ||
catch (er) { | ||
return fn(er); | ||
} | ||
return fn(null, result); | ||
}); | ||
}; | ||
/** | ||
* Commit the given `sess` object associated with the given `sid`. | ||
* | ||
* @param {String} sid | ||
* @param {Session} sess | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
RedisStore.prototype.set = function (sid, sess, fn) { | ||
var store = this; | ||
var args = [store.prefix + sid]; | ||
if (!fn) fn = noop; | ||
try { | ||
var jsess = store.serializer.stringify(sess); | ||
return ttl | ||
} | ||
catch (er) { | ||
return fn(er); | ||
} | ||
args.push(jsess); | ||
if (!store.disableTTL) { | ||
var ttl = getTTL(store, sess, sid); | ||
args.push('EX', ttl); | ||
debug('SET "%s" %s ttl:%s', sid, jsess, ttl); | ||
} else { | ||
debug('SET "%s" %s', sid, jsess); | ||
_getAllKeys(cb = noop) { | ||
let pattern = this.prefix + '*' | ||
this._scanKeys({}, 0, pattern, this.scanCount, cb) | ||
} | ||
store.client.set(args, function (er) { | ||
if (er) return fn(er); | ||
debug('SET complete'); | ||
fn.apply(null, arguments); | ||
}); | ||
}; | ||
_scanKeys(keys = {}, cursor, pattern, count, cb = noop) { | ||
let args = [cursor, 'match', pattern, 'count', count] | ||
this.client.scan(args, (err, data) => { | ||
if (err) return cb(err) | ||
/** | ||
* Destroy the session associated with the given `sid`. | ||
* | ||
* @param {String} sid | ||
* @api public | ||
*/ | ||
let [nextCursorId, scanKeys] = data | ||
for (let key of scanKeys) { | ||
keys[key] = true | ||
} | ||
RedisStore.prototype.destroy = function (sid, fn) { | ||
debug('DEL "%s"', sid); | ||
if (!fn) fn = noop; | ||
// This can be a string or a number. We check both. | ||
if (Number(nextCursorId) !== 0) { | ||
return this._scanKeys(keys, nextCursorId, pattern, count, cb) | ||
} | ||
if (Array.isArray(sid)) { | ||
var multi = this.client.multi(); | ||
var prefix = this.prefix; | ||
sid.forEach(function (s) { | ||
multi.del(prefix + s); | ||
}); | ||
multi.exec(fn); | ||
} else { | ||
sid = this.prefix + sid; | ||
this.client.del(sid, fn); | ||
cb(null, Object.keys(keys)) | ||
}) | ||
} | ||
}; | ||
/** | ||
* Refresh the time-to-live for the session with the given `sid`. | ||
* | ||
* @param {String} sid | ||
* @param {Session} sess | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
RedisStore.prototype.touch = function (sid, sess, fn) { | ||
var store = this; | ||
var psid = store.prefix + sid; | ||
if (!fn) fn = noop; | ||
if (store.disableTTL) return fn(); | ||
var ttl = getTTL(store, sess); | ||
debug('EXPIRE "%s" ttl:%s', sid, ttl); | ||
store.client.expire(psid, ttl, function (er) { | ||
if (er) return fn(er); | ||
debug('EXPIRE complete'); | ||
fn.apply(this, arguments); | ||
}); | ||
}; | ||
/** | ||
* Fetch all sessions' Redis keys using non-blocking SCAN command | ||
* | ||
* @param {Function} fn | ||
* @api private | ||
*/ | ||
function allKeys (store, cb) { | ||
var keysObj = {}; // Use an object to dedupe as scan can return duplicates | ||
var pattern = store.prefix + '*'; | ||
var scanCount = store.scanCount; | ||
debug('SCAN "%s"', pattern); | ||
(function nextBatch (cursorId) { | ||
store.client.scan(cursorId, 'match', pattern, 'count', scanCount, function (err, result) { | ||
if (err) return cb(err); | ||
var nextCursorId = result[0]; | ||
var keys = result[1]; | ||
debug('SCAN complete (next cursor = "%s")', nextCursorId); | ||
keys.forEach(function (key) { | ||
keysObj[key] = 1; | ||
}); | ||
if (nextCursorId != 0) { | ||
// next batch | ||
return nextBatch(nextCursorId); | ||
} | ||
// end of cursor | ||
return cb(null, Object.keys(keysObj)); | ||
}); | ||
})(0); | ||
} | ||
/** | ||
* Fetch all sessions' ids | ||
* | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
RedisStore.prototype.ids = function (fn) { | ||
var store = this; | ||
var prefixLength = store.prefix.length; | ||
if (!fn) fn = noop; | ||
allKeys(store, function (err, keys) { | ||
if (err) return fn(err); | ||
keys = keys.map(function (key) { | ||
return key.substr(prefixLength); | ||
}); | ||
return fn(null, keys); | ||
}); | ||
}; | ||
/** | ||
* Fetch count of all sessions | ||
* | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
RedisStore.prototype.length = function (fn) { | ||
var store = this; | ||
if (!fn) fn = noop; | ||
allKeys(store, function (err, keys) { | ||
if (err) return fn(err); | ||
return fn(null, keys.length); | ||
}); | ||
}; | ||
/** | ||
* Fetch all sessions | ||
* | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
RedisStore.prototype.all = function (fn) { | ||
var store = this; | ||
var prefixLength = store.prefix.length; | ||
if (!fn) fn = noop; | ||
allKeys(store, function (err, keys) { | ||
if (err) return fn(err); | ||
if (keys.length === 0) return fn(null,[]); | ||
store.client.mget(keys, function (err, sessions) { | ||
if (err) return fn(err); | ||
var result; | ||
try { | ||
result = sessions.map(function (data, index) { | ||
data = data.toString(); | ||
data = store.serializer.parse(data); | ||
data.id = keys[index].substr(prefixLength); | ||
return data; | ||
}); | ||
} catch (e) { | ||
err = e; | ||
} | ||
return fn(err, result); | ||
}); | ||
}); | ||
}; | ||
return RedisStore; | ||
}; | ||
return RedisStore | ||
} |
{ | ||
"name": "connect-redis", | ||
"description": "Redis session store for Connect", | ||
"version": "3.4.2", | ||
"version": "4.0.0", | ||
"author": "TJ Holowaychuk <tj@vision-media.ca>", | ||
@@ -15,5 +15,6 @@ "contributors": [ | ||
}, | ||
"dependencies": { | ||
"debug": "^4.1.1", | ||
"redis": "^2.8.0" | ||
"peerDependencies": { | ||
"ioredis": "^4.10.0", | ||
"redis": "^2.8.0", | ||
"redis-mock": "^0.46.0" | ||
}, | ||
@@ -23,11 +24,14 @@ "devDependencies": { | ||
"bluebird": "^3.5.5", | ||
"eslint": "^3.19.0", | ||
"eslint": "^6.2.2", | ||
"eslint-config-prettier": "^6.1.0", | ||
"eslint-plugin-prettier": "^3.1.0", | ||
"express-session": "^1.16.2", | ||
"ioredis": "^4.10.0", | ||
"istanbul": "^0.4.5", | ||
"sinon": "^2.3.4", | ||
"tape": "^4.2.1" | ||
"nyc": "^14.1.1", | ||
"prettier": "^1.18.2", | ||
"redis": "^2.8.0", | ||
"redis-mock": "^0.46.0" | ||
}, | ||
"engines": { | ||
"node": "*" | ||
"node": ">=8.0.0" | ||
}, | ||
@@ -38,7 +42,6 @@ "bugs": { | ||
"scripts": { | ||
"test": "istanbul cover tape \"test/*-test.js\"", | ||
"test-debug": "DEBUG=* istanbul cover tape \"test/*-test.js\"", | ||
"bench": "node bench/redisbench.js", | ||
"lint": "eslint index.js test lib bench" | ||
"test": "nyc tape \"test/*-test.js\"", | ||
"lint": "eslint index.js test lib", | ||
"fmt": "prettier --write \"**/*.{js,md,json,*rc}\"" | ||
} | ||
} |
@@ -1,212 +0,144 @@ | ||
/* eslint-env es6 */ | ||
var test = require('blue-tape'); | ||
var redisSrv = require('./redis-server'); | ||
var session = require('express-session'); | ||
var RedisStore = require('../')(session); | ||
var redis = require('redis'); | ||
var ioRedis = require('ioredis'); | ||
var sinon = require('sinon'); | ||
var P = require('bluebird'); | ||
const test = require('blue-tape') | ||
const redisSrv = require('../test/redis-server') | ||
const session = require('express-session') | ||
const redis = require('redis') | ||
const ioRedis = require('ioredis') | ||
const redisMock = require('redis-mock') | ||
var lifecycleTest = P.coroutine(function *(store, t) { | ||
P.promisifyAll(store); | ||
let RedisStore = require('../')(session) | ||
var ok = yield store.setAsync('123', { cookie: { maxAge: 2000 }, name: 'tj' }); | ||
t.equal(ok, 'OK', '#set() ok'); | ||
let p = (ctx, method) => (...args) => | ||
new Promise((resolve, reject) => { | ||
ctx[method](...args, (err, d) => { | ||
if (err) reject(err) | ||
resolve(d) | ||
}) | ||
}) | ||
var data = yield store.getAsync('123'); | ||
t.deepEqual({ cookie: { maxAge: 2000 }, name: 'tj' }, data, '#get() ok'); | ||
test('setup', redisSrv.connect) | ||
ok = yield store.setAsync('123', { cookie: { maxAge: undefined }, name: 'tj' }); | ||
t.equal(ok, 'OK', '#set() no maxAge ok'); | ||
test('defaults', async t => { | ||
t.throws(() => new RedisStore(), 'client is required') | ||
data = yield store.allAsync(); | ||
t.deepEqual([{ id: '123', cookie: {}, name: 'tj' }], data, '#all() ok'); | ||
var client = redis.createClient(redisSrv.port, 'localhost') | ||
var store = new RedisStore({ client }) | ||
data = yield store.idsAsync(); | ||
t.deepEqual(['123'], data, '#ids() ok'); | ||
t.equal(store.client, client, 'stores client') | ||
t.equal(store.prefix, 'sess:', 'defaults to sess:') | ||
t.equal(store.ttl, 86400, 'defaults to one day') | ||
t.equal(store.scanCount, 100, 'defaults SCAN count to 100') | ||
t.equal(store.serializer, JSON, 'defaults to JSON serialization') | ||
t.equal(store.disableTouch, false, 'defaults to having `touch` enabled') | ||
client.end(false) | ||
}) | ||
data = yield store.lengthAsync(); | ||
t.deepEqual(1, data, '#length() ok'); | ||
test('node_redis', async t => { | ||
var client = redis.createClient(redisSrv.port, 'localhost') | ||
var store = new RedisStore({ client }) | ||
await lifecycleTest(store, t) | ||
client.end(false) | ||
}) | ||
ok = yield store.destroyAsync('123'); | ||
t.equal(ok, 1, '#destroy() ok'); | ||
test('ioredis', async t => { | ||
var client = ioRedis.createClient(redisSrv.port, 'localhost') | ||
var store = new RedisStore({ client }) | ||
await lifecycleTest(store, t) | ||
client.disconnect() | ||
}) | ||
store.client.end(false); | ||
}); | ||
test('redis-mock client', async t => { | ||
var client = redisMock.createClient() | ||
var store = new RedisStore({ client }) | ||
await lifecycleTest(store, t) | ||
}) | ||
test('setup', redisSrv.connect); | ||
test('teardown', redisSrv.disconnect) | ||
test('defaults', function (t) { | ||
var store = new RedisStore(); | ||
t.equal(store.prefix, 'sess:', 'defaults to sess:'); | ||
t.notOk(store.ttl, 'ttl not set'); | ||
t.notOk(store.disableTTL, 'disableTTL not set'); | ||
t.ok(store.client, 'creates client'); | ||
async function lifecycleTest(store, t) { | ||
let res = await p(store, 'set')('123', { foo: 'bar' }) | ||
t.equal(res, 'OK', 'set value') | ||
store.client.end(false); | ||
t.end(); | ||
}); | ||
res = await p(store, 'get')('123') | ||
t.same(res, { foo: 'bar' }, 'get value') | ||
test('basic', function (t) { | ||
t.throws(RedisStore, TypeError, 'constructor not callable as function'); | ||
var store = new RedisStore({ port: redisSrv.port }); | ||
return lifecycleTest(store, t); | ||
}); | ||
res = await p(store.client, 'ttl')('sess:123') | ||
t.ok(res >= 86399, 'check one day ttl') | ||
test('existing client', function (t) { | ||
var client = redis.createClient(redisSrv.port, 'localhost'); | ||
var store = new RedisStore({ client: client }); | ||
return lifecycleTest(store, t); | ||
}); | ||
let ttl = 60 | ||
let expires = new Date(Date.now() + ttl * 1000).toISOString() | ||
res = await p(store, 'set')('456', { cookie: { expires } }) | ||
t.equal(res, 'OK', 'set cookie expires') | ||
test('io redis client', function (t) { | ||
var client = ioRedis.createClient(redisSrv.port, 'localhost'); | ||
var store = new RedisStore({ client: client }); | ||
return lifecycleTest(store, t).then(function () { | ||
t.test('#destroy()', function (p) { | ||
var spy = sinon.spy(ioRedis.prototype, 'sendCommand'); | ||
var sidName = 'randomname'; | ||
store.destroy(sidName); | ||
p.deepEqual( | ||
spy.firstCall.args[0].args, | ||
[store.prefix + sidName] | ||
); | ||
spy.restore(); | ||
p.end(); | ||
}); | ||
}); | ||
}); | ||
res = await p(store.client, 'ttl')('sess:456') | ||
t.ok(res <= 60, 'check expires ttl') | ||
test('options', function (t) { | ||
var store = new RedisStore({ | ||
host: 'localhost', | ||
port: redisSrv.port, | ||
prefix: 'tobi', | ||
ttl: 1000, | ||
disableTTL: true, | ||
db: 1, | ||
scanCount: 32, | ||
unref: true, | ||
pass: 'secret' | ||
}); | ||
ttl = 90 | ||
expires = new Date(Date.now() + ttl * 1000).toISOString() | ||
res = await p(store, 'touch')('456', { cookie: { expires } }) | ||
t.equal(res, 'OK', 'set cookie expires touch') | ||
t.equal(store.prefix, 'tobi', 'uses provided prefix'); | ||
t.equal(store.ttl, 1000, 'ttl set'); | ||
t.ok(store.disableTTL, 'disableTTL set'); | ||
t.ok(store.client, 'creates client'); | ||
t.equal(store.client.address, 'localhost:'+redisSrv.port, 'sets host and port'); | ||
t.equal(store.scanCount, 32, 'sets scan count'); | ||
res = await p(store.client, 'ttl')('sess:456') | ||
t.ok(res >= 60, 'check expires ttl touch') | ||
var socketStore = new RedisStore({ socket: 'word' }); | ||
t.equal(socketStore.client.address, 'word', 'sets socket address'); | ||
socketStore.client.end(false); | ||
res = await p(store, 'length')() | ||
t.equal(res, 2, 'stored two keys length') | ||
var urlStore = new RedisStore({ url: 'redis://127.0.0.1:8888' }); | ||
t.equal(urlStore.client.address, '127.0.0.1:8888', 'sets url address'); | ||
urlStore.client.end(false); | ||
res = await p(store, 'ids')() | ||
res.sort() | ||
t.same(res, ['123', '456'], 'stored two keys ids') | ||
var hostNoPort = new RedisStore({ host: 'host' }); | ||
t.equal(hostNoPort.client.address, 'host:6379', 'sets default port'); | ||
hostNoPort.client.end(false); | ||
res = await p(store, 'all')() | ||
res.sort((a, b) => (a.id > b.id ? 1 : -1)) | ||
t.same( | ||
res, | ||
[{ id: '123', foo: 'bar' }, { id: '456', cookie: { expires } }], | ||
'stored two keys data' | ||
) | ||
return lifecycleTest(store, t); | ||
}); | ||
res = await p(store, 'destroy')('456') | ||
t.equal(res, 1, 'destroyed one') | ||
test('ttl options', P.coroutine(function *(t) { | ||
var store = new RedisStore({ port: redisSrv.port }); | ||
res = await p(store, 'length')() | ||
t.equal(res, 1, 'one key remains') | ||
var sid = '123'; | ||
var data, ok; | ||
sinon.stub(store.client, 'set').callsArgWith(1, null, 'OK'); | ||
P.promisifyAll(store); | ||
res = await p(store, 'clear')() | ||
t.equal(res, 1, 'cleared remaining key') | ||
// Basic (one day) | ||
data = { cookie: {}, name: 'tj' }; | ||
ok = yield store.setAsync(sid, data); | ||
t.equal(ok, 'OK', '#set() ok'); | ||
assertSetCalledWith(t, store, sid, data, ['EX', 86400]); | ||
res = await p(store, 'length')() | ||
t.equal(res, 0, 'no key remains') | ||
// maxAge in cookie | ||
data = { cookie: { maxAge: 2000 }, name: 'tj' }; | ||
ok = yield store.setAsync(sid, data); | ||
t.equal(ok, 'OK', '#set() ok'); | ||
assertSetCalledWith(t, store, sid, data, ['EX', 2]); | ||
let count = 1000 | ||
await load(store, count) | ||
// Floors maxage | ||
data = { cookie: { maxAge: 2500 }, name: 'tj' }; | ||
ok = yield store.setAsync(sid, data); | ||
t.equal(ok, 'OK', '#set() ok'); | ||
assertSetCalledWith(t, store, sid, data, ['EX', 2]); | ||
res = await p(store, 'length')() | ||
t.equal(res, count, 'bulk count') | ||
// store.disableTTL | ||
store.disableTTL = true; | ||
data = { cookie: {}, name: 'tj' }; | ||
ok = yield store.setAsync(sid, data); | ||
t.equal(ok, 'OK', '#set() ok'); | ||
assertSetCalledWith(t, store, sid, data); | ||
store.disableTTL = false; | ||
res = await p(store, 'clear')() | ||
t.equal(res, count, 'bulk clear') | ||
} | ||
// store.ttl: number | ||
store.ttl = 50; | ||
data = { cookie: {}, name: 'tj' }; | ||
ok = yield store.setAsync(sid, data); | ||
t.equal(ok, 'OK', '#set() ok'); | ||
assertSetCalledWith(t, store, sid, data, ['EX', 50]); | ||
store.ttl = null; | ||
function load(store, count) { | ||
return new Promise((resolve, reject) => { | ||
let set = sid => { | ||
store.set( | ||
's' + sid, | ||
{ | ||
cookie: { expires: new Date(Date.now() + 1000) }, | ||
data: 'some data', | ||
}, | ||
err => { | ||
if (err) { | ||
return reject(err) | ||
} | ||
// store.ttl: function | ||
store.ttl = sinon.stub().returns(200); | ||
data = { cookie: {}, name: 'tj' }; | ||
ok = yield store.setAsync(sid, data); | ||
t.equal(ok, 'OK', '#set() ok'); | ||
assertSetCalledWith(t, store, sid, data, ['EX', 200]); | ||
t.ok(store.ttl.called, 'TTL fn was called'); | ||
t.deepEqual(store.ttl.firstCall.args, [store, data, sid]); | ||
store.ttl = null; | ||
if (sid === count) { | ||
return resolve() | ||
} | ||
// store.ttl: string (invalid) | ||
store.ttl = {}; | ||
data = { cookie: {}, name: 'tj' }; | ||
try { | ||
ok = yield store.setAsync(sid, data); | ||
t.ok(false, '#set() should throw with bad TTL'); | ||
} catch (e) { | ||
t.ok(/must be a number or function/i.test(e.message), 'bad TTL type throws error'); | ||
} | ||
store.ttl = null; | ||
store.client.end(false); | ||
})); | ||
function assertSetCalledWith(t, store, sid, data, addl) { | ||
var args = [store.prefix + sid, store.serializer.stringify(data)]; | ||
if (Array.isArray(addl)) args = args.concat(addl); | ||
t.deepEqual(store.client.set.lastCall.args[0], args, '#.set() called with expected params'); | ||
set(sid + 1) | ||
} | ||
) | ||
} | ||
set(1) | ||
}) | ||
} | ||
test('interups', function (t) { | ||
var store = P.promisifyAll(new RedisStore({ port: redisSrv.port, connect_timeout: 500 })); | ||
return store.setAsync('123', { cookie: { maxAge: 2000 }, name: 'tj' }) | ||
.catch(function (er) { | ||
t.ok(/broken/.test(er.message), 'failed connection'); | ||
store.client.end(false); | ||
}); | ||
}); | ||
test('serializer', function (t) { | ||
var serializer = { | ||
stringify: function() { return 'XXX'+JSON.stringify.apply(JSON, arguments); }, | ||
parse: function(x) { | ||
t.ok(x.match(/^XXX/)); | ||
return JSON.parse(x.substring(3)); | ||
} | ||
}; | ||
t.equal(serializer.stringify('UnitTest'), 'XXX"UnitTest"'); | ||
t.equal(serializer.parse(serializer.stringify('UnitTest')), 'UnitTest'); | ||
var store = new RedisStore({ port: redisSrv.port, serializer: serializer }); | ||
return lifecycleTest(store, t); | ||
}); | ||
test('teardown', redisSrv.disconnect); |
@@ -1,17 +0,21 @@ | ||
var P = require('bluebird'); | ||
var spawn = require('child_process').spawn; | ||
var redisSrv; | ||
var port = exports.port = 18543; | ||
const spawn = require('child_process').spawn | ||
const port = (exports.port = 18543) | ||
let redisSrv | ||
exports.connect = function () { | ||
redisSrv = spawn('redis-server', [ | ||
'--port', port, | ||
'--loglevel', 'notice', | ||
], { stdio: 'inherit' }); | ||
return P.delay(1500); | ||
}; | ||
exports.connect = () => | ||
new Promise((resolve, reject) => { | ||
redisSrv = spawn('redis-server', ['--port', port, '--loglevel', 'notice'], { | ||
stdio: 'inherit', | ||
}) | ||
exports.disconnect = function () { | ||
redisSrv.kill('SIGKILL'); | ||
return P.resolve(); | ||
}; | ||
redisSrv.on('error', function(err) { | ||
reject(new Error('Error caught spawning the server:' + err.message)) | ||
}) | ||
setTimeout(resolve, 1500) | ||
}) | ||
exports.disconnect = function() { | ||
redisSrv.kill('SIGKILL') | ||
return Promise.resolve() | ||
} |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
11
119
20384
3
11
264
2
+ Addedcluster-key-slot@1.1.2(transitive)
+ Addeddenque@1.5.1(transitive)
+ Addedioredis@4.28.5(transitive)
+ Addedlodash.defaults@4.2.0(transitive)
+ Addedlodash.flatten@4.4.0(transitive)
+ Addedlodash.isarguments@3.1.0(transitive)
+ Addedp-map@2.1.0(transitive)
+ Addedredis-errors@1.2.0(transitive)
+ Addedredis-mock@0.46.0(transitive)
+ Addedredis-parser@3.0.0(transitive)
+ Addedstandard-as-callback@2.1.0(transitive)
- Removeddebug@^4.1.1
- Removedredis@^2.8.0