UNPKG

actionhero

Version:

actionhero.js is a multi-transport API Server with integrated cluster capabilities and delayed tasks

321 lines (285 loc) 9.89 kB
var fs = require('fs'); var os = require('os'); var path = require('path'); var should = require('should'); var actionheroPrototype = require(__dirname + '/../../actionhero.js').actionheroPrototype; var actionhero = new actionheroPrototype(); var api; describe('Core: Cache', function(){ before(function(done){ actionhero.start(function(err, a){ api = a; done(); }) }); after(function(done){ actionhero.stop(function(){ done(); }); }); it('cache methods should exist', function(done){ api.cache.should.be.an.instanceOf(Object); api.cache.save.should.be.an.instanceOf(Function); api.cache.load.should.be.an.instanceOf(Function); api.cache.destroy.should.be.an.instanceOf(Function); done(); }); it('cache.save', function(done){ api.cache.save('testKey','abc123',null,function(err, resp){ should.not.exist(err); resp.should.equal(true); done(); }); }); it('cache.load', function(done){ api.cache.load('testKey',function(err, resp){ resp.should.equal('abc123'); done(); }); }); it('cache.load failures', function(done){ api.cache.load('something else',function(err, resp){ String(err).should.equal('Error: Object not found'); should.equal(null, resp); done(); }); }); it('cache.destroy', function(done){ api.cache.destroy('testKey',function(err, resp){ resp.should.equal(true); done(); }); }); it('cache.destroy failure', function(done){ api.cache.destroy('testKey',function(err, resp){ resp.should.equal(false); done(); }); }); it('cache.save with expire time', function(done){ api.cache.save('testKey','abc123',10,function(err, resp){ resp.should.equal(true); done(); }); }); it('cache.load with expired items should not return them', function(done){ api.cache.save('testKey_slow', 'abc123', 10, function(err, saveResp){ saveResp.should.equal(true); setTimeout(function(){ api.cache.load('testKey_slow', function(err, loadResp){ String(err).should.equal('Error: Object expired') should.equal(null, loadResp); done(); }); }, 20); }); }); it('cache.load with negative expire times will never load', function(done){ api.cache.save('testKeyInThePast', 'abc123', -1, function(err, saveResp){ saveResp.should.equal(true); api.cache.load('testKeyInThePast', function(err, loadResp){ (String(err).indexOf('Error: Object') >= 0).should.equal(true) should.equal(null, loadResp); done(); }); }); }); it('cache.save does not need to pass expireTime', function(done){ api.cache.save('testKeyForNullExpireTime', 'abc123', function(err, saveResp){ saveResp.should.equal(true); api.cache.load('testKeyForNullExpireTime', function(err, loadResp){ loadResp.should.equal('abc123'); done(); }); }); }); it('cache.load without changing the expireTime will re-apply the redis expire', function(done){ var key = 'testKey' api.cache.save(key, 'val', 1000, function(){ api.cache.load(key, function(err, loadResp){ loadResp.should.equal('val'); setTimeout(function(){ api.cache.load(key, function(err, loadResp){ String(err).should.equal('Error: Object not found') should.equal(null, loadResp); done(); }); }, 1001); }); }); }); it('cache.load with options that extending expireTime should return cached item', function(done){ var expireTime = 400 var timeout = 320 //save the initial key api.cache.save('testKey_slow', 'abc123', expireTime, function(err, saveResp){ saveResp.should.equal(true) //wait for `timeout` and try to load the key setTimeout(function(){ api.cache.load('testKey_slow', {expireTimeMS: expireTime}, function(err, loadResp){ loadResp.should.equal('abc123') //wait another `timeout` and load the key again within the extended expire time setTimeout(function(){ api.cache.load('testKey_slow', function(err, loadResp){ loadResp.should.equal('abc123') //wait another `timeout` and the key load should fail without the extension setTimeout(function(){ api.cache.load('testKey_slow', function(err, loadResp){ String(err).should.equal('Error: Object not found') should.equal(null, loadResp) done() }) },timeout) }); },timeout) }) },timeout) }) }) it('cache.save works with arrays', function(done){ api.cache.save('array_key', [1, 2, 3], function(err, saveResp){ saveResp.should.equal(true); api.cache.load('array_key', function(err, loadResp){ loadResp[0].should.equal(1); loadResp[1].should.equal(2); loadResp[2].should.equal(3); done(); }); }); }); it('cache.save works with objects', function(done){ var data = {}; data.thing = 'stuff'; data.otherThing = [1, 2, 3]; api.cache.save('obj_key', data, function(err, saveResp){ saveResp.should.equal(true); api.cache.load('obj_key', function(err, loadResp){ loadResp.thing.should.equal('stuff'); loadResp.otherThing[0].should.equal(1); loadResp.otherThing[1].should.equal(2); loadResp.otherThing[2].should.equal(3); done(); }); }); }); it('can clear the cache entirely', function(done){ api.cache.save('thingA', 123, function(){ api.cache.size(function(err, count){ (count > 0).should.equal(true); api.cache.clear(function(){ api.cache.size(function(err, count){ count.should.equal(0); done(); }); }); }); }); }); describe('locks', function(){ var key = 'testKey'; afterEach(function(done){ api.cache.lockName = api.id; api.cache.unlock(key, function(){ done(); }); }) it('things can be locked, checked, and unlocked aribitrarily', function(done){ api.cache.lock(key, 100, function(err, lockOk){ lockOk.should.equal(true); api.cache.checkLock(key, null, function(err, lockOk){ lockOk.should.equal(true); api.cache.unlock(key, function(err, lockOk){ lockOk.should.equal(true); done(); }); }); }); }); it('locks have a TTL and the default will be assumed from config', function(done){ api.cache.lock(key, null, function(err, lockOk){ lockOk.should.equal(true); api.redis.client.ttl(api.cache.lockPrefix + key, function(err, ttl){ (ttl >= 9).should.equal(true); (ttl <= 10).should.equal(true); done(); }); }); }); it('you can save an item if you do hold the lock', function(done){ api.cache.lock(key, null, function(err, lockOk){ lockOk.should.equal(true); api.cache.save(key, 'value', function(err, success){ success.should.equal(true); done(); }); }); }); it('you cannot save a locked item if you do not hold the lock', function(done){ api.cache.lock(key, null, function(err, lockOk){ lockOk.should.equal(true); api.cache.lockName = 'otherId'; api.cache.save(key, 'value', function(err){ String(err).should.equal('Error: Object Locked') done(); }); }); }); it('you cannot destroy a locked item if you do not hold the lock', function(done){ api.cache.lock(key, null, function(err, lockOk){ lockOk.should.equal(true); api.cache.lockName = 'otherId'; api.cache.destroy(key, function(err){ String(err).should.equal('Error: Object Locked') done(); }); }); }); it('you can opt to retry to obtaina lock if a lock is held (READ)', function(done){ api.cache.lock(key, 1, function(err, lockOk){ // will be rounded up to 1s lockOk.should.equal(true); api.cache.save(key, 'value', function(err, success){ success.should.equal(true); api.cache.lockName = 'otherId'; api.cache.checkLock(key, null, function(err, lockOk){ lockOk.should.equal(false); var start = new Date().getTime(); api.cache.load(key, {retry: 2000}, function(err, data){ data.should.equal('value'); var delta = new Date().getTime() - start; (delta >= 1000).should.equal(true) done(); }); }); }); }); }); }); describe('cache dump files', function(){ if (typeof os.tmpdir !== 'function'){ os.tmpdir = os.tmpDir } // resolution for node v0.8.x var file = os.tmpdir() + path.sep + "cacheDump"; it('can read write the cache to a dump file', function(done){ api.cache.clear(function(){ api.cache.save('thingA', 123, function(){ api.cache.dumpWrite(file, function(error, count){ count.should.equal(1); var body = JSON.parse(String(fs.readFileSync(file))); var content = JSON.parse(body['actionhero:cache:thingA']); content.value.should.equal(123); done(); }); }); }); }); it('can laod the cache from a dump file', function(done){ api.cache.clear(function(){ api.cache.dumpRead(file, function(error, count){ count.should.equal(1); api.cache.load('thingA', function(err, value){ value.should.equal(123); done(); }); }); }); }); }) })