bastian
Version:
Handles partial cache misses, generically, using Redis as a cache store
237 lines (200 loc) • 5.27 kB
JavaScript
var async = require('async');
var Redis = require('redis');
var test = require('tape');
var request = require('./helper/request-single.js');
var Bastian = require('../index.js');
test('get(): Normal successful usage', function(t) {
var redis = Redis.createClient();
var cache = new Bastian(redis);
cache.on('error', function(err) {
t.error(err);
});
redis.del([
'TEST-cuisine-en-US-v4:1',
'TEST-cuisine-en-US-v4:2',
'TEST-cuisine-es-MX-v4:1',
'TEST-cuisine-es-MX-v4:2',
]);
var testRun = 0;
var cuisineService = {
getItem: function(id, language, callback) {
testRun += 1;
const VERSION = 'v4';
cache.get({
keyPrefix: 'TEST-cuisine-' + language + '-' + VERSION,
id: id,
expiration: 60 * 60 * 24,
handler: function(id, cb) {
var options = {
url: 'http://restaurant.api.opentable.com/' + VERSION + '/restaurants/?id=' + id,
json: true,
headers: {
"Accept-Language": language
}
};
t.comment(options.url);
if (testRun === 1) {
t.equal(id, 1);
} else if (testRun === 2) {
t.equal(id, 2);
} else if (testRun === 3) {
t.equal(id, 1);
} else if (testRun === 4) {
t.error(new Error('should not execute as the data for this request is in cache'));
}
request(options, function(err, response, body) {
if (err) {
return cb(err);
}
if (response.statusCode > 299) {
return cb(response.statusCode);
}
cb(null, body);
});
}
}, callback);
}
};
async.series([
function(callback) {
cuisineService.getItem(1, 'en-US', function(err, data) {
t.error(err, 'no error');
t.equal(data.id, 1);
callback(err, data);
});
},
function(callback) {
cuisineService.getItem(2, 'en-US', function(err, data) {
t.error(err, 'no error');
t.equal(data.id, 2);
callback(err, data);
});
},
function(callback) {
cuisineService.getItem(1, 'es-MX', function(err, data) {
t.error(err, 'no error');
t.equal(data.id, 1);
callback(err, data);
});
},
function(callback) {
cuisineService.getItem(1, 'en-US', function(err, data) {
t.error(err, 'no error');
t.equal(data.id, 1);
callback(err, data);
});
}
], function(err, result) {
t.error(err, 'no error');
t.ok(result[0]);
t.ok(result[1]);
t.ok(result[2]);
t.ok(result[3]);
redis.quit();
t.end(err);
});
});
test('get(): No redis, go directly to handler', function(t) {
var cache = new Bastian();
cache.get({
keyPrefix: 'no-store',
id: 1,
handler: function(id, cb) {
t.equal(id, 1);
cb(null, id * 10);
}
}, function(err, data) {
t.deepEqual(data, 10, 'received data');
t.end(err);
});
});
test('get(): When handler fails, overall operation should fail', function(t) {
var cache = new Bastian();
cache.get({
keyPrefix: 'no-store',
id: 1,
handler: function(id, cb) {
cb(new Error('uh oh'));
}
}, function(err, data) {
t.ok(err instanceof Error, 'end in error, no data in cache or handler');
t.end();
});
});
test('get(): When no ID is provided, operation should run normally', function(t) {
var cache = new Bastian();
cache.get({
keyPrefix: 'no-data',
handler: function(id, cb) {
t.notOk(id);
setImmediate(function() {
cb(null, 'ok');
});
}
}, function(err, data) {
t.deepEqual(data, 'ok', 'asked for no id, received data');
t.end(err);
});
});
test('get(): When Redis.GET fails, still run the handler', function(t) {
var failureGetRedis = {
get: function(data, cb) {
setImmediate(function() {
cb(new Error('mget failed'));
});
}
};
var cache = new Bastian(failureGetRedis);
var didEmitError = false;
cache.on('error', function(err) {
didEmitError = true;
t.ok(err, 'does have error');
});
cache.get({
keyPrefix: 'no-data',
id: 100,
handler: function(id, cb) {
cb(null, 'good stuff');
}
}, function(err, data) {
t.ok(didEmitError, 'did emit error');
t.end(err);
});
});
test('get(): With redis, no expiration', function(t) {
var redis = Redis.createClient();
redis.del([
'no-expire:1',
]);
var cache = new Bastian(redis);
cache.get({
keyPrefix: 'no-expire',
id: 1,
handler: function(id, cb) {
t.equal(id, 1);
cb(null, id * 10);
}
}, function(err, data) {
t.deepEqual(data, 10, 'received data');
redis.quit();
t.end(err);
});
});
test('get(): With redis, no id', function(t) {
var redis = Redis.createClient();
redis.del([
'no-expire',
]);
var cache = new Bastian(redis);
cache.get({
keyPrefix: 'no-expire',
handler: function(id, cb) {
t.equal(id, null, 'id should be null');
cb(null, 100);
}
}, function(err, data) {
t.deepEqual(data, 100, 'received data');
redis.quit();
t.end(err);
});
});