relyq
Version:
A reliable redis-backed queue.
228 lines (202 loc) • 7.13 kB
JavaScript
// storage_test.js
require('longjohn').async_trace_limit = -1;
// vendor
var redisPkg = require('redis'),
createRedis = function () { return redisPkg.createClient(6379, 'localhost', { enable_offline_queue: false }) },
Moniker = require('moniker'),
async = require('async'),
_ = require('underscore')
mongo = require('mongodb'),
mongoClient = new mongo.MongoClient(new mongo.Server('localhost', 27017)),
uuid = require('uuid');
mongoClient.open(function (err, mc) {
if (err) {
throw err;
}
mongoClient = mc;
});
// local
var relyq = require('..'),
count = counter();
// Storages to test
var storages = {
'RedisJson': new relyq.RedisJsonQ({ createRedis: createRedis, prefix:prefix('RedisJson'),clean_finish:false}),
'RedisJson2': new relyq.RedisJsonQ({ createRedis: createRedis, prefix: prefix('RedisJson2'), idfield: 'otherid', storage_prefix: prefix('RedisJson2-jobs'), clean_finish:false }),
'Mongo': new relyq.MongoQ({ createRedis: createRedis, mongo: mongoClient, db: 'test', collection: 'relyq.'+Moniker.choose()+'.jobs', prefix: prefix('Mongo'), clean_finish:false }),
'CreateId': new relyq.RedisJsonQ({ createRedis: createRedis, prefix: prefix('CreateId'), idfield: 'omgid', clean_finish:false,getid: function (t) { return t.omgid = t.omgid || uuid.v4(); }}),
'CreateId2': new relyq.MongoQ({ createRedis: createRedis, mongo: mongoClient,clean_finish:false, db: 'test', collection: 'relyq.'+Moniker.choose()+'.jobs', prefix: prefix('CreateId2'), idfield: 'omgid',
getid: function (t) { return t.omgid = t.omgid || count(); }}),
}
_.each(storages, function (q, name) {
exports[name] = createTests(q);
});
// Clean up redis to allow a clean escape!
exports.cleanUp = function cleanUp (test) {
mongoClient.db('test').collection('relyq.jobs').drop(function () {
mongoClient.close(test.done);
});
};
// If we are getting a test.done complaint, turn this on. It helps find errors
process.on('uncaughtException', function (err) {
console.error(err.stack);
});
function createTests(Q) {
var tests = {};
tests.setUp = function (callback) {
Q._redis.ready ? callback() : Q.once('ready', callback)
}
tests.tearDown = function (callback) {
if (Q) {
return async.parallel([
_.bind(Q.todo.clear, Q.todo),
_.bind(Q.doing.clear, Q.doing),
_.bind(Q.done.clear, Q.done),
_.bind(Q.failed.clear, Q.failed)
], callback);
}
callback();
};
function checkByStorageList(test, sQ, exp, ignore) {
var stack = new Error().stack;
return function (callback) {
async.waterfall([
_.bind(sQ.list, sQ),
function (list, cb) {
async.map(list, function (ref, cb2) {
Q.get(ref, function (err, obj) {
cb2(err, _.omit(obj, ignore));
});
}, cb);
},
function (list2, cb) {
list2 = list2.map(function (obj) { return _.omit(obj, 'error') })
exp = exp.map(function (obj) { return _.omit(obj, 'error') })
test.deepEqual(list2, exp, 'checkByStorageList: ' + stack);
cb();
}
], callback);
};
}
var InPlace = /^InPlace/.test(Q.constructor.name);
// -- Tests --
tests.testFull = function (test) {
try {
var task1 = { id: '123', otherid: 'cachoa!', data: { hello: 'dolly' }},
task2 = { id: '321', otherid: '?augment', data: { goodbye: 'dolly' }},
task3 = { id: '213', otherid: 'blahblah', data: {something: 'else' }};
async.series([
_.bind(Q.push, Q, task1),
_.bind(Q.push, Q, task2),
_.bind(Q.push, Q, task3),
_.bind(Q.process, Q),
_.bind(Q.process, Q),
_.bind(Q.process, Q),
_.bind(Q.fail, Q, task2, (InPlace ? undefined : new Error('ahh!'))),
_.bind(Q.finish, Q, task1),
checkByStorageList(test, Q.todo, []),
checkByStorageList(test, Q.doing, [task3]),
checkByStorageList(test, Q.done, [task1]),
checkByStorageList(test, Q.failed, [task2]),
_.bind(Q.remove, Q, 'done', task1),
_.bind(Q.remove, Q, 'failed', task2, true),
checkByStorageList(test, Q.done, []),
checkByStorageList(test, Q.failed, []),
_.bind(Q.get, Q, Q.ref(task2)),
_.bind(Q.get, Q, Q.ref(task1)),
function (cb) {
Q._clean_finish = true;
Q._keep_storage = true;
cb();
},
_.bind(Q.finish, Q, task3),
checkByStorageList(test, Q.done, []),
function (cb) {
Q.get(Q.ref(task3), function (err, obj) {
test.ifError(err);
test.deepEqual(_.omit(obj,'_id'), _.omit(task3,'_id'));
cb();
});
},
function (cb) {
Q._clean_finish = false;
Q._keep_storage = false;
cb();
}
], function (err, results) {
test.ifError(err);
test.deepEqual(results[3], task1);
test.deepEqual(results[4], _.omit(task2, 'error'));
test.deepEqual(results[16], task2);
if (!InPlace) {
test.ok(task2.error && task2.error.match(/^Error: ahh!/));
test.deepEqual(results[17], null);
} else {
test.deepEqual(results[17], task1);
}
Q.finish(task2, function (err) {
test.ok(err instanceof Error);
test.done();
});
});
} catch (e) {
test.done(e);
}
};
if (!InPlace) {
tests.testListen = function (test) {
var i = 0,
atask1 = { id: '456', otherid: 'tucan', data: { hello: 'mother' }},
atask2 = { id: '654', otherid: 'sam', data: { goodbye: 'father' }};
var listener = Q.listen()
.on('error', test.ifError)
.on('task', function (task, done) {
test.deepEqual(task, _.clone(++i===1 ? atask1 : atask2));
var ndone = done;
if (i===2) {
ndone = function () {
done(new Error('omg'));
};
} else {
setTimeout(function () { done(); test.equal(listener._out, 1); }, 30);
}
setTimeout(ndone, i*20);
})
.once('end', function () {
async.series([
checkByStorageList(test, Q.done, [atask1]),
checkByStorageList(test, Q.failed, [_.defaults(atask2, {error:'Error: omg'})]),
], test.done);
});
async.series([
_.bind(Q.push, Q, atask1),
function (cb) {
setTimeout(cb, 10);
},
_.bind(Q.push, Q, atask2),
function (cb) {
setTimeout(cb, 30); // approx time to roundtrip local redis and wait for timeout
},
checkByStorageList(test, Q.done, [atask1]),
], function (err) {
test.ifError(err);
listener.end();
});
};
}
tests.cleanUp = function (test) {
Q.end(function () {
Q = null
test.done()
})
}
return tests;
}
function counter(n) {
n = n || 0;
return function () {
return '' + n++;
};
}
function prefix(name) {
return ['relyq-test', name, Moniker.choose()].join(':');
}