Bryan Donovan
12 years ago
11 changed files with 909 additions and 0 deletions
@ -0,0 +1 @@ |
|||
node_module |
@ -0,0 +1,6 @@ |
|||
var cache = { |
|||
caching: require('./lib/caching'), |
|||
multi_caching: require('./lib/multi_caching') |
|||
}; |
|||
|
|||
module.exports = cache; |
@ -0,0 +1,47 @@ |
|||
var caching = function(args) { |
|||
args = args || {}; |
|||
var self = {}; |
|||
self.store_name = args.store || 'redis'; |
|||
self.store = require('./stores/' + self.store_name).create(args); |
|||
|
|||
/** |
|||
* Wraps a function in cache. I.e., the first time the function is run, |
|||
* its results are stored in cache so subsequent calls retrieve from cache |
|||
* instead of calling the function. |
|||
* |
|||
* @example |
|||
* |
|||
* var key = 'user_' + user_id; |
|||
* cache.run(key, function(cb) { |
|||
* user_adapter.get(user_id, cb); |
|||
* }, function(err, user) { |
|||
* console.log(user); |
|||
* }); |
|||
*/ |
|||
self.run = function(key, work, cb) { |
|||
self.store.get(key, function(err, result) { |
|||
if (err) { return cb(err); } |
|||
if (result) { |
|||
return cb(null, result); |
|||
} |
|||
|
|||
work(function() { |
|||
var work_args = Array.prototype.slice.call(arguments, 0); |
|||
self.store.set(key, work_args[1], function(err) { |
|||
if (err) { return cb(err); } |
|||
cb.apply(null, work_args); |
|||
}); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
self.get = self.store.get; |
|||
|
|||
self.set = self.store.set; |
|||
|
|||
self.del = self.store.del; |
|||
|
|||
return self; |
|||
}; |
|||
|
|||
module.exports = caching; |
@ -0,0 +1,79 @@ |
|||
var async = require('async'); |
|||
|
|||
/** |
|||
* Module that lets you specify a hiearchy of caches. |
|||
*/ |
|||
var multi_caching = function(caches) { |
|||
var self = {}; |
|||
if (!Array.isArray(caches)) { throw new Error('multi_caching requires an array of caches'); } |
|||
|
|||
function get_from_highest_priority_cache(key, cb) { |
|||
var i = 0; |
|||
async.forEachSeries(caches, function(cache, async_cb) { |
|||
cache.store.get(key, function(err, result) { |
|||
if (err) { return cb(err); } |
|||
if (result) { |
|||
// break out of async loop.
|
|||
return cb(err, result, i); |
|||
} |
|||
|
|||
i += 1; |
|||
async_cb(err); |
|||
}); |
|||
}, cb); |
|||
} |
|||
|
|||
function set_in_multiple_caches(caches, key, value, cb) { |
|||
async.forEach(caches, function(cache, async_cb) { |
|||
cache.store.set(key, value, async_cb); |
|||
}, cb); |
|||
}; |
|||
|
|||
/** |
|||
* Wraps a function in one or more caches. |
|||
* Has same API as regular caching module. |
|||
* |
|||
* If a key doesn't exist in any cache, it gets set in all caches. |
|||
* If a key exists in a high-priority (e.g., first) cache, it gets returned immediately |
|||
* without getting set in other lower-priority caches. |
|||
* If a key doesn't exist in a higher-priority cache but exists in a lower-priority |
|||
* cache, it gets set in all higher-priority caches. |
|||
*/ |
|||
self.run = function(key, work, cb) { |
|||
get_from_highest_priority_cache(key, function(err, result, index) { |
|||
if (err) { return cb(err); } |
|||
if (result) { |
|||
var caches_to_set = caches.slice(0, index); |
|||
set_in_multiple_caches(caches_to_set, key, result, function(err) { |
|||
return cb(err, result); |
|||
}); |
|||
} else { |
|||
work(function() { |
|||
var work_args = Array.prototype.slice.call(arguments, 0); |
|||
|
|||
set_in_multiple_caches(caches, key, work_args[1], function(err) { |
|||
cb.apply(null, work_args); |
|||
}); |
|||
}); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
self.set = function(key, value, cb) { |
|||
set_in_multiple_caches(caches, key, value, cb); |
|||
}; |
|||
|
|||
self.get = function(key, cb) { |
|||
get_from_highest_priority_cache(key, cb); |
|||
}; |
|||
|
|||
self.del = function(key, cb) { |
|||
async.forEach(caches, function(cache, async_cb) { |
|||
cache.store.del(key, async_cb); |
|||
}, cb); |
|||
}; |
|||
|
|||
return self; |
|||
}; |
|||
|
|||
module.exports = multi_caching; |
@ -0,0 +1,38 @@ |
|||
var Lru = require("lru-cache") |
|||
|
|||
var memory_store = function(args) { |
|||
args = args || {}; |
|||
var db = args.db || 'cache'; |
|||
var self = {}; |
|||
var ttl = args.ttl; |
|||
var lru_opts = { |
|||
max: args.max || 500, |
|||
maxAge: ttl ? ttl * 1000 : null |
|||
}; |
|||
|
|||
var lru_cache = new Lru(lru_opts); |
|||
|
|||
self.set = function(key, value, cb) { |
|||
lru_cache.set(key, value); |
|||
cb(null); |
|||
}; |
|||
|
|||
self.get = function(key, cb) { |
|||
cb(null, lru_cache.get(key)); |
|||
}; |
|||
|
|||
self.del = function(key, cb) { |
|||
lru_cache.del(key); |
|||
cb(null); |
|||
}; |
|||
|
|||
return self; |
|||
}; |
|||
|
|||
var methods = { |
|||
create: function(args) { |
|||
return memory_store(args); |
|||
} |
|||
}; |
|||
|
|||
module.exports = methods; |
@ -0,0 +1,68 @@ |
|||
/* |
|||
var redis_store = function(args) { |
|||
args = args || {}; |
|||
var db = args.db || 'cache'; |
|||
var self = {}; |
|||
var ttl = args.ttl; |
|||
var client = djs.backends.redis.client({db: djs.settings.redis.dbs[db]}); |
|||
|
|||
self.set = function(key, value, cb) { |
|||
var val = JSON.stringify(value); |
|||
if (ttl) { |
|||
client.command('setex', {key: key, ttl: ttl, value: val}, cb); |
|||
} else { |
|||
client.command('set', {key: key, value: val}, cb); |
|||
} |
|||
}; |
|||
|
|||
self.get = function(key, cb) { |
|||
client.command('get', {key: key}, function(err, result) { |
|||
if (err) { return cb(err); } |
|||
if (result === undefined) { return cb(null, null); } |
|||
return cb(null, JSON.parse(result)); |
|||
}); |
|||
}; |
|||
|
|||
self.del = function(key, cb) { |
|||
client.command('del', {key: key}, cb); |
|||
}; |
|||
|
|||
return self; |
|||
}; |
|||
*/ |
|||
|
|||
function redis_store(args) { |
|||
args = args || {}; |
|||
var self = {}; |
|||
var ttl = args.ttl; |
|||
self.client = require('redis').createClient(args.port, args.host, args); |
|||
|
|||
self.get = function(key, cb) { |
|||
self.client.get(key, function(err, result) { |
|||
cb(err, JSON.parse(result)); |
|||
}); |
|||
}; |
|||
|
|||
self.set = function(key, value, cb) { |
|||
if (ttl) { |
|||
self.client.setex(key, ttl, JSON.stringify(value), cb); |
|||
} else { |
|||
self.client.set(key, JSON.stringify(value), cb); |
|||
} |
|||
}; |
|||
|
|||
self.del = function(key, cb) { |
|||
self.client.del(key, cb); |
|||
}; |
|||
|
|||
return self; |
|||
} |
|||
|
|||
|
|||
var methods = { |
|||
create: function(args) { |
|||
return redis_store(args); |
|||
} |
|||
}; |
|||
|
|||
module.exports = methods; |
@ -0,0 +1,33 @@ |
|||
{ |
|||
"name": "cashew", |
|||
"version": "0.0.1", |
|||
"description": "Cache module for Node.JS", |
|||
"main": "index.js", |
|||
"scripts": { |
|||
"test": "mocha" |
|||
}, |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "https://github.com/BryanDonovan/node-cashew.git" |
|||
}, |
|||
"keywords": [ |
|||
"cache", |
|||
"redis", |
|||
"lru-cache" |
|||
], |
|||
"author": "Bryan Donovan", |
|||
"license": "BSD", |
|||
"dependencies": { |
|||
"async": "0.1.22", |
|||
"hiredis": "0.1.14", |
|||
"lru-cache": "2.3.0", |
|||
"redis": "0.6.7" |
|||
}, |
|||
"devDependencies": { |
|||
"Faker" : "0.5.6", |
|||
"istanbul": "0.1.29", |
|||
"jshint": "1.0.0", |
|||
"mocha": "1.8.1", |
|||
"sinon": "1.5.2" |
|||
} |
|||
} |
@ -0,0 +1,268 @@ |
|||
var assert = require('assert'); |
|||
var sinon = require('sinon'); |
|||
var redis = require('redis'); |
|||
var Lru = require("lru-cache") |
|||
var support = require('./support'); |
|||
var check_err = support.check_err; |
|||
var caching = require('../index').caching; |
|||
var memory_store = require('../lib/stores/memory'); |
|||
|
|||
function get_widget(name, cb) { |
|||
cb(null, {name: name}); |
|||
} |
|||
|
|||
describe("caching", function() { |
|||
var cache; |
|||
var key; |
|||
var ttl; |
|||
var name; |
|||
var value; |
|||
|
|||
describe("get() and set()", function() { |
|||
context("using redis store", function() { |
|||
beforeEach(function() { |
|||
cache = caching({store: 'redis'}); |
|||
key = support.random.string(20); |
|||
value = support.random.string(); |
|||
}); |
|||
|
|||
it("lets us set and get data in cache", function(done) { |
|||
cache.set(key, value, function(err, result) { |
|||
check_err(err); |
|||
cache.get(key, function(err, result) { |
|||
assert.equal(result, value); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
context("using memory store", function() { |
|||
beforeEach(function() { |
|||
cache = caching({store: 'memory'}); |
|||
key = support.random.string(20); |
|||
value = support.random.string(); |
|||
}); |
|||
|
|||
it("lets us set and get data in cache", function(done) { |
|||
cache.set(key, value, function(err, result) { |
|||
check_err(err); |
|||
cache.get(key, function(err, result) { |
|||
assert.equal(result, value); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
describe("del()", function() { |
|||
['redis', 'memory'].forEach(function(store) { |
|||
context("using " + store + " store", function() { |
|||
beforeEach(function(done) { |
|||
cache = caching({store: store}); |
|||
key = support.random.string(20); |
|||
value = support.random.string(); |
|||
cache.set(key, value, function(err, result) { |
|||
check_err(err); |
|||
done(); |
|||
}); |
|||
}); |
|||
|
|||
it("deletes data from cache", function(done) { |
|||
cache.get(key, function(err, result) { |
|||
assert.equal(result, value); |
|||
|
|||
cache.del(key, function(err, result) { |
|||
check_err(err); |
|||
|
|||
cache.get(key, function(err, result) { |
|||
assert.ok(!result); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
describe("run()", function() { |
|||
context("using redis store", function() { |
|||
var redis_client; |
|||
|
|||
before(function() { |
|||
redis_client = redis.createClient(); |
|||
sinon.stub(redis, 'createClient').returns(redis_client); |
|||
}); |
|||
|
|||
beforeEach(function() { |
|||
cache = caching({store: 'redis'}); |
|||
key = support.random.string(20); |
|||
name = support.random.string(); |
|||
}); |
|||
|
|||
after(function() { |
|||
redis.createClient.restore(); |
|||
}); |
|||
|
|||
it("calls back with the result of the wrapped function", function(done) { |
|||
cache.run(key, function(cb) { |
|||
get_widget(name, cb); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
assert.deepEqual(widget, {name: name}); |
|||
done(); |
|||
}); |
|||
}); |
|||
|
|||
it("caches the result of the function in redis", function(done) { |
|||
cache.run(key, function(cb) { |
|||
get_widget(name, cb); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
|
|||
redis_client.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.deepEqual(JSON.parse(result), {name: name}); |
|||
|
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
it("retrieves data from redis when available", function(done) { |
|||
cache.run(key, function(cb) { |
|||
get_widget(name, cb); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
|
|||
redis_client.get(key, function(err, result) { |
|||
check_err(err); |
|||
|
|||
sinon.spy(redis_client, 'get'); |
|||
|
|||
cache.run(key, function(cb) { |
|||
get_widget(name, cb); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
assert.deepEqual(widget, {name: name}); |
|||
assert.ok(redis_client.get.calledWith(key)); |
|||
redis_client.get.restore(); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
context("when using ttl", function() { |
|||
beforeEach(function() { |
|||
ttl = 50; |
|||
cache = caching({store: 'redis', ttl: ttl}); |
|||
}); |
|||
|
|||
it("expires cached result after ttl seconds", function(done) { |
|||
cache.run(key, function(cb) { |
|||
get_widget(name, cb); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
|
|||
redis_client.ttl(key, function(err, result) { |
|||
check_err(err); |
|||
support.assert_within(result, ttl, 2); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
|
|||
describe("using memory (lru-cache) store", function() { |
|||
var memory_store_stub; |
|||
|
|||
beforeEach(function() { |
|||
ttl = 0.1; |
|||
memory_store_stub = memory_store.create({ttl: ttl}); |
|||
|
|||
sinon.stub(memory_store, 'create').returns(memory_store_stub); |
|||
|
|||
cache = caching({store: 'memory', ttl: ttl}); |
|||
key = support.random.string(20); |
|||
name = support.random.string(); |
|||
}); |
|||
|
|||
afterEach(function() { |
|||
memory_store.create.restore(); |
|||
}); |
|||
|
|||
it("calls back with the result of a function", function(done) { |
|||
cache.run(key, function(cb) { |
|||
get_widget(name, cb); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
assert.deepEqual(widget, {name: name}); |
|||
done(); |
|||
}); |
|||
}); |
|||
|
|||
it("retrieves data from memory when available", function(done) { |
|||
cache.run(key, function(cb) { |
|||
get_widget(name, cb); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
|
|||
memory_store_stub.get(key, function(err, result) { |
|||
check_err(err); |
|||
|
|||
sinon.spy(memory_store_stub, 'get'); |
|||
var func_called = false; |
|||
|
|||
cache.run(key, function(cb) { |
|||
get_widget(name, function(err, result) { |
|||
func_called = true; |
|||
cb(err, result); |
|||
}); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
assert.deepEqual(widget, {name: name}); |
|||
assert.ok(memory_store_stub.get.calledWith(key)); |
|||
assert.ok(!func_called); |
|||
memory_store_stub.get.restore(); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
it("expires cached result after ttl seconds", function(done) { |
|||
cache.run(key, function(cb) { |
|||
get_widget(name, cb); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
|
|||
memory_store_stub.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.deepEqual(widget, {name: name}); |
|||
|
|||
var func_called = false; |
|||
|
|||
setTimeout(function () { |
|||
cache.run(key, function(cb) { |
|||
get_widget(name, function(err, result) { |
|||
func_called = true; |
|||
cb(err, result); |
|||
}); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
assert.ok(func_called); |
|||
assert.deepEqual(widget, {name: name}); |
|||
done(); |
|||
}); |
|||
}, (ttl * 1000 + 10)); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
@ -0,0 +1,5 @@ |
|||
--reporter spec |
|||
--ui bdd |
|||
--globals state,newBlocks,params,type,__coverage__ |
|||
--timeout 6500 |
|||
--slow 200 |
@ -0,0 +1,313 @@ |
|||
var assert = require('assert'); |
|||
var Lru = require("lru-cache") |
|||
var support = require('./support'); |
|||
var check_err = support.check_err; |
|||
var caching = require('../index').caching; |
|||
var multi_caching = require('../index').multi_caching; |
|||
var memory_store = require('../lib/stores/memory'); |
|||
|
|||
function get_widget(name, cb) { |
|||
cb(null, {name: name}); |
|||
} |
|||
|
|||
describe("multi_caching", function() { |
|||
var redis_cache; |
|||
var memory_cache; |
|||
var memory_cache2; |
|||
var multi_cache; |
|||
var key; |
|||
var memory_ttl; |
|||
var redis_ttl; |
|||
var name; |
|||
|
|||
beforeEach(function() { |
|||
memory_ttl = 0.1; |
|||
redis_ttl = 1; |
|||
|
|||
memory_cache = caching({store: 'memory', ttl: memory_ttl}); |
|||
memory_cache2 = caching({store: 'memory', ttl: memory_ttl}); |
|||
redis_cache = caching({store: 'redis', ttl: redis_ttl}); |
|||
|
|||
key = support.random.string(20); |
|||
name = support.random.string(); |
|||
}); |
|||
|
|||
describe("get(), set(), del()", function() { |
|||
var value; |
|||
|
|||
beforeEach(function() { |
|||
multi_cache = multi_caching([memory_cache, redis_cache, memory_cache2]); |
|||
key = support.random.string(20); |
|||
value = support.random.string(); |
|||
}); |
|||
|
|||
describe("set()", function() { |
|||
it("lets us set data in all caches", function(done) { |
|||
multi_cache.set(key, value, function(err, result) { |
|||
check_err(err); |
|||
memory_cache.get(key, function(err, result) { |
|||
assert.equal(result, value); |
|||
|
|||
redis_cache.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.equal(result, value); |
|||
|
|||
memory_cache2.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.equal(result, value); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
describe("get()", function() { |
|||
it("gets data from first cache that has it", function(done) { |
|||
redis_cache.set(key, value, function(err) { |
|||
check_err(err); |
|||
|
|||
multi_cache.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.equal(result, value); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
describe("del()", function() { |
|||
it("lets us delete data in all caches", function(done) { |
|||
multi_cache.set(key, value, function(err, result) { |
|||
check_err(err); |
|||
|
|||
multi_cache.del(key, function(err, result) { |
|||
check_err(err); |
|||
|
|||
memory_cache.get(key, function(err, result) { |
|||
assert.ok(!result); |
|||
|
|||
redis_cache.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.ok(!result); |
|||
|
|||
memory_cache2.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.ok(!result); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
describe("run()", function() { |
|||
describe("using a single cache store", function() { |
|||
beforeEach(function() { |
|||
multi_cache = multi_caching([redis_cache]); |
|||
}); |
|||
|
|||
it("calls back with the result of a function", function(done) { |
|||
multi_cache.run(key, function(cb) { |
|||
get_widget(name, cb); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
assert.deepEqual(widget, {name: name}); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
describe("using two cache stores", function() { |
|||
beforeEach(function() { |
|||
multi_cache = multi_caching([memory_cache, redis_cache]); |
|||
}); |
|||
|
|||
it("calls back with the result of a function", function(done) { |
|||
multi_cache.run(key, function(cb) { |
|||
get_widget(name, cb); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
assert.deepEqual(widget, {name: name}); |
|||
done(); |
|||
}); |
|||
}); |
|||
|
|||
it("sets value in all caches", function(done) { |
|||
multi_cache.run(key, function(cb) { |
|||
get_widget(name, cb); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
assert.deepEqual(widget, {name: name}); |
|||
|
|||
memory_cache.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.deepEqual(result, {name: name}); |
|||
|
|||
redis_cache.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.deepEqual(result, {name: name}); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
context("when value exists in first store but not second", function() { |
|||
it("returns value from first store, does not set it in second", function(done) { |
|||
memory_cache.set(key, {name: name}, function(err) { |
|||
multi_cache.run(key, function(cb) { |
|||
get_widget(name, cb); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
assert.deepEqual(widget, {name: name}); |
|||
|
|||
redis_cache.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.equal(result, null); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
context("when value exists in second store but not first", function() { |
|||
it("returns value from second store, sets it in first store", function(done) { |
|||
redis_cache.set(key, {name: name}, function(err) { |
|||
multi_cache.run(key, function(cb) { |
|||
get_widget(name, cb); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
assert.deepEqual(widget, {name: name}); |
|||
|
|||
memory_cache.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.deepEqual(result, {name: name}); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
describe("using three cache stores", function() { |
|||
beforeEach(function() { |
|||
multi_cache = multi_caching([memory_cache, redis_cache, memory_cache2]); |
|||
}); |
|||
|
|||
it("calls back with the result of a function", function(done) { |
|||
multi_cache.run(key, function(cb) { |
|||
get_widget(name, cb); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
assert.deepEqual(widget, {name: name}); |
|||
done(); |
|||
}); |
|||
}); |
|||
|
|||
it("sets value in all caches", function(done) { |
|||
multi_cache.run(key, function(cb) { |
|||
get_widget(name, cb); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
assert.deepEqual(widget, {name: name}); |
|||
|
|||
memory_cache.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.deepEqual(result, {name: name}); |
|||
|
|||
redis_cache.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.deepEqual(result, {name: name}); |
|||
|
|||
memory_cache2.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.deepEqual(result, {name: name}); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
context("when value exists in first store only", function() { |
|||
it("returns value from first store, does not set it in second or third", function(done) { |
|||
memory_cache.set(key, {name: name}, function(err) { |
|||
multi_cache.run(key, function(cb) { |
|||
get_widget(name, cb); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
assert.deepEqual(widget, {name: name}); |
|||
|
|||
redis_cache.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.equal(result, null); |
|||
|
|||
memory_cache2.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.equal(result, null); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
context("when value exists in second store only", function() { |
|||
it("returns value from second store, sets it in first store, does not set third store", function(done) { |
|||
redis_cache.set(key, {name: name}, function(err) { |
|||
multi_cache.run(key, function(cb) { |
|||
get_widget(name, cb); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
assert.deepEqual(widget, {name: name}); |
|||
|
|||
memory_cache.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.deepEqual(result, {name: name}); |
|||
|
|||
memory_cache2.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.equal(result, null); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
context("when value exists in third store only", function() { |
|||
it("returns value from third store, sets it in first and second stores", function(done) { |
|||
memory_cache2.set(key, {name: name}, function(err) { |
|||
multi_cache.run(key, function(cb) { |
|||
get_widget(name, cb); |
|||
}, function(err, widget) { |
|||
check_err(err); |
|||
assert.deepEqual(widget, {name: name}); |
|||
|
|||
redis_cache.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.deepEqual(result, {name: name}); |
|||
|
|||
memory_cache.get(key, function(err, result) { |
|||
check_err(err); |
|||
assert.deepEqual(result, {name: name}); |
|||
|
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
@ -0,0 +1,51 @@ |
|||
var util = require('util'); |
|||
var assert = require('assert'); |
|||
|
|||
var support = { |
|||
random: { |
|||
string: function(str_len) { |
|||
str_len = str_len || 8; |
|||
var chars = "abcdefghiklmnopqrstuvwxyz"; |
|||
var random_str = ''; |
|||
for (var i = 0; i < str_len; i++) { |
|||
var rnum = Math.floor(Math.random() * chars.length); |
|||
random_str += chars.substring(rnum, rnum + 1); |
|||
} |
|||
return random_str; |
|||
} |
|||
}, |
|||
|
|||
check_err: function(err) { |
|||
if (err) { |
|||
var msg; |
|||
|
|||
if (err instanceof Error) { |
|||
msg = err; |
|||
} else if (err.msg) { |
|||
msg = err.msg; |
|||
} else { |
|||
msg = util.inspect(err); |
|||
} |
|||
|
|||
var error = new Error(msg); |
|||
var stack = app_trace().split('\n'); |
|||
|
|||
stack.unshift(error.message); |
|||
error.stack = stack.join('\n'); |
|||
throw error; |
|||
} |
|||
}, |
|||
|
|||
assert_between: function(actual, lower, upper) { |
|||
assert.ok(actual >= lower, "Expected " + actual + " to be >= " + lower); |
|||
assert.ok(actual <= upper, "Expected " + actual + " to be <= " + upper); |
|||
}, |
|||
|
|||
assert_within: function(actual, expected, delta) { |
|||
var lower = expected - delta; |
|||
var upper = expected + delta; |
|||
this.assert_between(actual, lower, upper); |
|||
} |
|||
}; |
|||
|
|||
module.exports = support; |
Loading…
Reference in new issue