memcache-pp
Version:
Memcache for node
1,250 lines (1,052 loc) • 47.8 kB
JavaScript
require('chai').should();
var _ = require('lodash'),
chance = require('chance').Chance(),
expect = require('chai').expect,
misc = require('../lib/misc'),
Promise = require('bluebird');
var Client = require('../lib/client');
describe('Client', function() {
var keys = [];
// We want a method for generating keys which will store them so we can
// do cleanup later and not litter memcache with a bunch of garbage data
var getKey = function(opts) {
var key = opts ? chance.word(opts) : chance.guid();
keys.push(key);
return key;
};
describe('initialization', function() {
it('with defaults', function() {
var cache = new Client();
cache.should.have.property('reconnect');
cache.reconnect.should.be.a('boolean');
cache.should.have.property('hosts');
cache.hosts.should.be.an('array');
});
it('initiates connection', function(done) {
var cache = new Client();
cache.should.have.property('connections');
cache.connections.should.be.an('object');
_.sample(cache.connections).client.on('connect', function() {
done();
});
});
it('has a disconnect method', function(done) {
var cache = new Client();
cache.should.have.property('disconnect');
cache.disconnect.should.be.a('function');
_.sample(cache.connections).client.on('connect', function() {
cache.disconnect()
.then(function() {
cache.connections.should.be.an('object');
Object.keys(cache.connections).should.have.length(0);
}).then(done);
});
});
it('can disconnect from a specific client with string', function(done) {
var cache = new Client({ hosts: ['localhost:11211', '127.0.0.1:11211'] });
cache.should.have.property('disconnect');
cache.disconnect.should.be.a('function');
cache.disconnect('127.0.0.1:11211')
.then(function() {
cache.connections.should.be.an('object');
Object.keys(cache.connections).should.have.length(1);
cache.hosts.should.have.length(1);
Object.keys(cache.connections)[0].should.equal('localhost:11211');
cache.hosts[0].should.equal('localhost:11211');
}).then(done);
});
it('can disconnect from a specific client with array', function(done) {
var cache = new Client({ hosts: ['localhost:11211', '127.0.0.1:11211'] });
cache.should.have.property('disconnect');
cache.disconnect.should.be.a('function');
cache.disconnect(['127.0.0.1:11211'])
.then(function() {
cache.connections.should.be.an('object');
Object.keys(cache.connections).should.have.length(1);
Object.keys(cache.connections)[0].should.equal('localhost:11211');
}).then(done);
});
it('throws an error if attempting to disconnect from a bogus host', function() {
var cache = new Client({
hosts: ['localhost:11211', '127.0.0.1:11211'],
onNetError: function() {}
});
cache.should.have.property('disconnect');
cache.disconnect.should.be.a('function');
expect(function() { cache.disconnect(['badserver:11211']); }).to.throw('Cannot disconnect from server unless connected');
});
it('has a dictionary of connections', function() {
var cache = new Client();
cache.should.have.property('hosts');
cache.connections.should.be.an('object');
});
it('has a hashring of connections', function() {
var cache = new Client();
cache.should.have.property('ring');
cache.ring.should.be.an.instanceof(require('hashring'));
});
it('with default port with single connection', function() {
var cache = new Client('localhost');
cache.connections['localhost'].should.have.property('port');
cache.connections['localhost'].port.should.equal('11211');
});
it('with default port with multiple connections', function() {
var cache = new Client(['localhost']);
cache.connections['localhost'].should.have.property('port');
cache.connections['localhost'].port.should.equal('11211');
});
/**
* Only comment this out when we have an Elasticache autodiscovery cluster to test against.
* Ideally one day this can be mocked, but for now just selectively enabling it
it('supports autodiscovery', function() {
var cache = new Client({ hosts: ['test-memcache.di6cba.cfg.use1.cache.amazonaws.com'], autodiscover: true });
var val = chance.word();
return cache.set('test', val)
.then(function() {
return cache.get('test');
})
.then(function(v) {
val.should.equal(v);
});
});
*/
it('throws on autodiscovery failure', function() {
var cache = new Client({
hosts: ['badserver:11211'],
autodiscover: true,
onNetError: function() {}
});
var val = chance.word();
return cache.set('test', val)
.then(function() { throw new Error('should not get here'); })
.catch(function(err) {
err.should.be.ok;
err.should.be.an.instanceof(Error);
err.message.should.match(/Autodiscovery failed/);
})
.then(function() {
// try again to ensure that subsequent ops also fail
return cache.set('test', val);
})
.then(function() { throw new Error('should not get here'); })
.catch(function(err) {
err.should.be.ok;
err.should.be.an.instanceof(Error);
err.message.should.match(/Autodiscovery failed/);
});
});
});
describe('set and get', function() {
var cache;
beforeEach(function() {
cache = new Client();
});
it('exists', function() {
cache.should.have.property('set');
});
describe('should throw an error if called', function() {
it('without a key', function() {
expect(function() { cache.set(); }).to.throw('AssertionError: Cannot "set" without key!');
});
it('with a key that is too long', function() {
expect(function() { cache.set(chance.string({length: 251}), chance.word()); }).to.throw('less than 250 characters');
});
it('with a non-string key', function() {
expect(function() { cache.set({blah: 'test'}, 'val'); }).to.throw('AssertionError: Key needs to be of type "string"');
expect(function() { cache.set([1, 2], 'val'); }).to.throw('AssertionError: Key needs to be of type "string"');
expect(function() { cache.set(_.noop, 'val'); }).to.throw('AssertionError: Key needs to be of type "string"');
});
});
it('should work', function() {
var key = getKey(), val = chance.word();
return cache.set(key, val)
.then(function() { return cache.get(key); })
.then(function(v) { val.should.equal(v); });
});
it.skip('works with values with newlines', function() {
var key = getKey(), val = 'value\nwith newline';
return cache.set(key, val)
.then(function() { return cache.get(key); })
.then(function(v) { val.should.equal(v); });
});
it('works with very large values', function() {
var key = getKey(), val = chance.word({ length: 1000000 });
return cache.set(key, val)
.then(function() { return cache.get(key); })
.then(function(v) { val.should.equal(v); });
});
describe('compression', function() {
it('does not throw an error if compression specified', function() {
var key = getKey(), val = chance.word({ length: 1000 });
return cache.set(key, val, { compressed: true });
});
it('works of its own accord', function() {
var val = chance.word({ length: 1000 });
return misc.compress(Buffer.from(val))
.then(function(v) { return misc.decompress(v); })
.then(function(d) { d.toString().should.equal(val); });
});
it('get works with compression', function() {
var key = getKey(), val = chance.word({ length: 1000 });
return cache.set(key, val, { compressed: true })
.then(function() { return cache.get(key, { compressed: true }); })
.then(function(v) { val.should.equal(v); });
});
it('get works with compression without explicit get compressed flag', function() {
var key = getKey(), val = chance.word({ length: 1000 });
return cache.set(key, val, { compressed: true })
.then(function() { return cache.get(key); })
.then(function(v) { val.should.equal(v); });
});
it('getMulti works with compression', function() {
var key1 = getKey(), key2 = getKey(),
val1 = chance.word(), val2 = chance.word();
return Promise.all([cache.set(key1, val1, { compressed: true }), cache.set(key2, val2, { compressed: true })])
.then(function() {
return cache.getMulti([key1, key2], { compressed: true });
})
.then(function(vals) {
vals.should.be.an('object');
vals[key1].should.equal(val1);
vals[key2].should.equal(val2);
});
});
it.skip('get works with a callback', function(done) {
var key = getKey(), val = chance.word({ length: 1000 });
return cache.set(key, val, { compressed: true })
.then(function() {
cache.get(key, { compressed: true }, function(err, v) {
val.should.equal(v);
done(err);
});
});
});
it('get for key that should be compressed but is not returns null', function() {
var key = getKey(), val = chance.word({ length: 1000 });
return cache.set(key, val)
.then(function() { return cache.get(key, { compressed: true }); })
.then(function(v) { expect(v).to.be.null; });
});
});
it('does not throw an error when setting a value number', function() {
var key = chance.guid(), val = chance.natural();
expect(function() { cache.set(key, val); }).to.not.throw();
});
it('get for val set as number returns number', function() {
var key = getKey(), val = chance.integer();
return cache.set(key, val)
.then(function() { return cache.get(key); })
.then(function(v) {
expect(v).to.be.a('number');
v.should.equal(val);
});
});
it('get for val set as floating number returns number', function() {
var key = getKey(), val = chance.floating();
return cache.set(key, val)
.then(function() { return cache.get(key); })
.then(function(v) {
expect(v).to.be.a.number;
v.should.equal(val);
});
});
it('get for val set as object returns object', function() {
var key = getKey(), val = { num: chance.integer() };
return cache.set(key, val)
.then(function() { return cache.get(key); })
.then(function(v) {
expect(v).to.be.an.object;
(v.num).should.equal(val.num);
});
});
it('get for val set as Buffer returns Buffer', function() {
var key = getKey(), val = Buffer.from('blah blah test');
return cache.set(key, val)
.then(function() { return cache.get(key); })
.then(function(v) {
expect(v).to.be.an.instanceof(Buffer);
(v.toString()).should.equal(val.toString());
});
});
it('get for val set as null returns null', function() {
var key = getKey(), val = null;
return cache.set(key, val)
.then(function() { return cache.get(key); })
.then(function(v) {
expect(v).to.be.null;
});
});
it('get for val set as array returns array', function() {
var key = getKey(), val = [ chance.integer(), chance.integer() ];
return cache.set(key, val)
.then(function() { return cache.get(key); })
.then(function(v) {
expect(v).to.be.an.array;
expect(v).to.deep.equal(val);
});
});
it('throws error with enormous values (over memcache limit)', function() {
// Limit is 1048577, 1 byte more throws error. We'll go up a few just to be safe
var key = getKey(), val = chance.word({ length: 1048590 });
return cache.set(key, val)
.then(function() {
throw new Error('this code should never get hit');
})
.catch(function(err) {
err.should.be.ok;
err.should.be.an.instanceof(Error);
err.should.deep.equal(new Error('Value too large to set in memcache'));
});
});
it('works fine with special characters', function() {
var key = getKey(),
val = chance.string({ pool: 'ÀÈÌÒÙàèìòÁÉÍÓÚáéíóúÂÊÎÔÛâêîôûÃÑÕãñõÄËÏÖÜŸäëïöüÿæ☃', length: 1000 });
return cache.set(key, val)
.then(function() { return cache.get(key); })
.then(function(v) { val.should.equal(v); });
});
it('works with callbacks as well', function(done) {
var key = getKey(), val = chance.word();
cache.set(key, val, function(err) {
if (err !== null) {
done(err);
}
cache.get(key, function(err, v) {
if (err !== null) {
done(err);
}
val.should.equal(v);
done();
});
});
});
it('multiple should not conflict', function() {
var key1 = getKey(), key2 = getKey(), key3 = getKey(),
val1 = chance.word(), val2 = chance.word(), val3 = chance.word();
var item1 = cache.set(key1, val1)
.then(function() { return cache.get(key1); })
.then(function(v) { val1.should.equal(v); });
var item2 = cache.set(key2, val2)
.then(function() { return cache.get(key2); })
.then(function(v) { val2.should.equal(v); });
var item3 = cache.set(key3, val3)
.then(function() { return cache.get(key3); })
.then(function(v) { val3.should.equal(v); });
return Promise.all([item1, item2, item3]);
});
it('many multiple operations should not conflict', function() {
var key = getKey(), key1 = getKey(), key2 = getKey(), key3 = getKey(),
val1 = chance.word(), val2 = chance.word(), val3 = chance.word();
return cache.set(key, val1)
.then(function() {
return Promise.all([
cache.delete(key),
cache.set(key1, val1),
cache.set(key2, val2),
cache.set(key3, val3)
]);
})
.then(function() {
return Promise.all([cache.get(key1), cache.get(key2), cache.get(key3)]);
})
.then(function(v) {
v[0].should.equal(val1);
v[1].should.equal(val2);
v[2].should.equal(val3);
return Promise.all([
cache.get(key1),
cache.deleteMulti([key1, key3])
]);
})
.then(function(v) {
v[0].should.equal(val1);
});
});
describe('get to key that does not exist returns null', function() {
it('with Promise', function() {
return cache.get(chance.guid())
.then(function(v) { expect(v).to.be.null; });
});
it('with Callback', function(done) {
cache.get(chance.word(), function(err, response) {
expect(response).to.be.null;
done(err);
});
});
});
describe('getMulti', function() {
it('exists', function() {
cache.should.have.property('getMulti');
});
it('works', function() {
var key1 = getKey(), key2 = getKey(),
val1 = chance.word(), val2 = chance.word();
return Promise.all([cache.set(key1, val1), cache.set(key2, val2)])
.then(function() {
return cache.getMulti([key1, key2]);
})
.then(function(vals) {
vals.should.be.an('object');
vals[key1].should.equal(val1);
vals[key2].should.equal(val2);
});
});
it('get with array of keys delegates to getMulti', function() {
var key1 = getKey(), key2 = getKey(),
val1 = chance.word(), val2 = chance.word();
return Promise.all([cache.set(key1, val1), cache.set(key2, val2)])
.then(function() {
return cache.get([key1, key2]);
})
.then(function(vals) {
vals.should.be.an('object');
vals[key1].should.equal(val1);
vals[key2].should.equal(val2);
});
});
it('works if some values not found', function() {
var key1 = getKey(), key2 = getKey(),
val = chance.word();
return cache.set(key1, val)
.then(function() {
return cache.getMulti([key1, key2]);
})
.then(function(vals) {
vals.should.be.an('object');
vals[key1].should.equal(val);
expect(vals[key2]).to.equal(null);
});
});
it('works if all values not found', function() {
var key = getKey(), key2 = getKey(), key3 = getKey(),
val = chance.word();
return cache.set(key, val)
.then(function() {
return cache.getMulti([key2, key3]);
})
.then(function(vals) {
vals.should.be.an('object');
_.size(vals).should.equal(2);
expect(vals[key2]).to.equal(null);
expect(vals[key3]).to.equal(null);
});
});
it('works if all values not found with callback', function(done) {
var key = getKey(), key2 = getKey(), key3 = getKey(),
val = chance.word();
cache.set(key, val)
.then(function() {
cache.getMulti([key2, key3], function(err, vals) {
vals.should.be.an('object');
_.size(vals).should.equal(2);
expect(vals[key2]).to.equal(null);
expect(vals[key3]).to.equal(null);
done(err);
});
});
});
});
describe('works with expiration', function() {
it('expires', function() {
var key = getKey(), val = chance.word();
return cache.set(key, val, 1)
.then(function() {
return cache.get(key);
})
.then(function(v) {
val.should.equal(v);
})
.delay(1001)
.then(function() {
return cache.get(key);
})
.then(function(v) {
expect(v).to.be.null;
});
});
});
});
describe('cas and gets', function() {
var cache;
beforeEach(function() {
cache = new Client();
});
it('exists', function() {
cache.should.have.property('gets');
});
it('should return a cas value', function() {
var key = getKey(), val = chance.word();
return cache.set(key, val)
.then(function() {
return cache.gets(key);
})
.spread(function(v, cas) {
val.should.equal(v);
expect(cas).to.exist;
});
});
it('should store new value when given a matching cas', function() {
var key = getKey(), val = chance.word(), updatedVal = chance.word();
return cache.set(key, val)
.then(function() {
return cache.gets(key);
})
.spread(function(v, cas) {
return cache.cas(key, updatedVal, cas);
}).then(function(success) {
expect(success).to.be.true;
return cache.get(key);
}).then(function(v) {
expect(v).to.equal(updatedVal);
});
});
it('should not store the new value when given an invalid cas value', function() {
var key = getKey(), val = chance.word(), updatedVal = chance.word();
return cache.set(key, val)
.then(function() {
return cache.gets(key);
})
.spread(function(v, cas) {
var invalidCas;
do {
invalidCas = chance.string({pool: '0123456789', length: 15});
} while (invalidCas === cas);
return cache.cas(key, updatedVal, invalidCas);
}).then(function(success) {
expect(success).to.be.false;
});
});
it('should not store a value when given an invalid key value', function() {
var key = getKey(), invalidKey = getKey(),
val = chance.word(), updatedVal = chance.word();
return cache.set(key, val)
.then(function() {
return cache.gets(key);
})
.spread(function(v, cas) {
return cache.cas(invalidKey, updatedVal, cas);
}).then(function(success) {
expect(success).to.be.false;
});
});
});
// @todo should have cleanup jobs to delete keys we set in memcache
describe('delete', function() {
var cache;
beforeEach(function() {
cache = new Client();
});
it('exists', function() {
cache.should.have.property('delete');
cache.delete.should.be.a('function');
});
it('works', function() {
var key = getKey();
return cache.set(key, 'myvalue')
.then(function() {
return cache.delete(key);
})
.then(function() {
return cache.get(key);
})
.then(function(v) {
expect(v).to.be.null;
});
});
it('does not blow up if deleting key that does not exist', function() {
var key = chance.guid();
return cache.delete(key);
});
});
describe('deleteMulti', function() {
var cache;
beforeEach(function() {
cache = new Client();
});
it('exists', function() {
cache.should.have.property('deleteMulti');
cache.deleteMulti.should.be.a('function');
});
it('works', function() {
var key1 = getKey(), key2 = getKey();
return Promise.all([cache.set(key1, 'myvalue'), cache.set(key2, 'myvalue')])
.then(function() {
return cache.deleteMulti([key1, key2]);
})
.then(function(d) {
d.should.be.an.object;
_.values(d).indexOf(null).should.equal(-1);
_.every(d).should.be.true;
return Promise.all([cache.get(key1), cache.get(key2)]);
})
.spread(function(v1, v2) {
expect(v1).to.be.null;
expect(v2).to.be.null;
return;
});
});
});
// @todo these are placeholders for now until I can figure out a good way
// to adequeately test these.
describe('Client buffer', function() {
it('works');
it('can be flushed');
});
describe('Connection buffer', function() {
it('works');
it('can be flushed');
});
describe('Helpers', function() {
describe('splitHost()', function() {
it('exists', function() {
var client = new Client();
client.should.have.property('splitHost');
});
it('works with no port', function() {
var client = new Client();
var hostName = chance.word();
var host = client.splitHost(hostName);
host.should.have.property('host');
host.should.have.property('port');
host.host.should.equal(hostName);
host.port.should.equal('11211');
});
it('works with just a port', function() {
var client = new Client();
var port = chance.natural({ max: 65536 }).toString();
var host = client.splitHost(':' + port);
host.should.have.property('host');
host.should.have.property('port');
host.host.should.equal('localhost');
host.port.should.equal(port);
});
it('works with both a host and port', function() {
var client = new Client();
var hostName = chance.word();
var port = chance.natural({ max: 65536 }).toString();
var host = client.splitHost(hostName + ':' + port);
host.should.have.property('host');
host.should.have.property('port');
host.host.should.equal(hostName);
host.port.should.equal(port);
});
});
});
describe('Options', function() {
it('can be disabled', function() {
var client = new Client({ disabled: true });
var key = getKey(), val = chance.word();
return client.set(key, val)
.then(function() {
return client.get(key);
})
.then(function(v) {
expect(v).to.be.null;
});
});
});
describe('incr', function() {
var cache;
beforeEach(function() {
cache = new Client();
});
it('exists', function() {
cache.should.have.property('incr');
});
describe('should throw an error if called', function() {
it('without a key', function() {
expect(function() { cache.incr(); }).to.throw('AssertionError: Cannot "incr" without key!');
});
it('with a key that is too long', function() {
expect(function() { cache.incr(chance.string({length: 251})); }).to.throw('less than 250 characters');
});
it('with a non-string key', function() {
expect(function() { cache.incr({blah: 'test'}); }).to.throw('AssertionError: Key needs to be of type "string"');
expect(function() { cache.incr([1, 2]); }).to.throw('AssertionError: Key needs to be of type "string"');
expect(function() { cache.incr(_.noop); }).to.throw('AssertionError: Key needs to be of type "string"');
});
it('with a val that is not a number', function() {
expect(function() { cache.incr(chance.string(), chance.word()); }).to.throw('AssertionError: Cannot incr in memcache with a non number value');
});
});
describe('should work', function() {
it('without an increment value', function() {
var key = getKey(), val = chance.natural();
return cache.set(key, val)
.then(function() {
return cache.incr(key);
})
.then(function(v) {
v.should.equal(val + 1);
});
});
it('with an increment value', function() {
var key = getKey(), val = chance.natural({ max: 20000000}), incr = chance.natural({ max: 1000 });
return cache.set(key, val)
.then(function() {
return cache.incr(key, incr);
})
.then(function(v) {
v.should.equal(val + incr);
});
});
});
});
describe('decr', function() {
var cache;
beforeEach(function() {
cache = new Client();
});
it('exists', function() {
cache.should.have.property('decr');
});
describe('should throw an error if called', function() {
it('without a key', function() {
expect(function() { cache.decr(); }).to.throw('AssertionError: Cannot "decr" without key!');
});
it('with a key that is too long', function() {
expect(function() { cache.decr(chance.string({length: 251})); }).to.throw('less than 250 characters');
});
it('with a non-string key', function() {
expect(function() { cache.decr({blah: 'test'}); }).to.throw('AssertionError: Key needs to be of type "string"');
expect(function() { cache.decr([1, 2]); }).to.throw('AssertionError: Key needs to be of type "string"');
expect(function() { cache.decr(_.noop); }).to.throw('AssertionError: Key needs to be of type "string"');
});
it('with a val that is not a number', function() {
expect(function() { cache.decr(chance.string(), chance.word()); }).to.throw('AssertionError: Cannot decr in memcache with a non number value');
});
});
describe('should work', function() {
it('without a decrement value', function() {
var key = getKey(), val = chance.natural();
return cache.set(key, val)
.then(function() {
return cache.decr(key);
})
.then(function(v) {
v.should.equal(val - 1);
});
});
it('with a decrement value', function() {
var key = getKey(), val = chance.natural({ max: 20000000}), decr = chance.natural({ max: 1000 });
return cache.set(key, val)
.then(function() {
return cache.decr(key, decr);
})
.then(function(v) {
v.should.equal(val - decr);
});
});
});
});
describe('flush', function() {
var cache;
beforeEach(function() {
cache = new Client();
});
it('exists', function() {
cache.should.have.property('flush');
});
describe('should work', function() {
it('removes all data', function () {
var key = getKey(), val = chance.natural();
return cache.set(key, val)
.then(function() {
return cache.get(key);
})
.then(function(v) {
expect(v).to.equal(val);
return cache.flush();
})
.then(function () {
return cache.get(key);
})
.then(function (v) {
expect(v).to.equal(null);
});
});
it('removes all data after a specified seconds', function () {
var key = getKey(), val = chance.natural();
return cache.set(key, val)
.then(function() {
return cache.get(key);
})
.then(function(v) {
expect(v).to.equal(val);
return cache.flush(1);
})
.then(function () {
return cache.get(key);
})
.then(function (v) {
expect(v).to.equal(v);
})
.delay(1001)
.then(function() {
return cache.get(key);
})
.then(function (v) {
expect(v).to.equal(null);
});
});
});
});
describe('add', function() {
var cache;
beforeEach(function() {
cache = new Client();
});
it('exists', function() {
cache.should.have.property('add');
});
describe('should throw an error if called', function() {
it('without a key', function() {
expect(function() { cache.add(); }).to.throw('AssertionError: Cannot "add" without key!');
});
it('with a key that is too long', function() {
expect(function() { cache.add(chance.string({length: 251})); }).to.throw('less than 250 characters');
});
it('with a non-string key', function() {
expect(function() { cache.add({blah: 'test'}); }).to.throw('AssertionError: Key needs to be of type "string"');
expect(function() { cache.add([1, 2]); }).to.throw('AssertionError: Key needs to be of type "string"');
expect(function() { cache.add(_.noop); }).to.throw('AssertionError: Key needs to be of type "string"');
});
});
describe('should work', function() {
it('with a brand new key', function() {
var key = getKey(), val = chance.natural();
return cache.add(key, val)
.then(function() {
return cache.get(key);
})
.then(function(v) {
v.should.equal(val);
});
});
it('should behave properly when add over existing key', function() {
var key = getKey(), val = chance.natural();
return cache.add(key, val)
.then(function() {
return cache.add(key, val);
})
.catch(function(err) {
expect(err.toString()).to.contain('it already exists');
});
});
});
});
describe('replace', function() {
var cache;
beforeEach(function() {
cache = new Client();
});
it('exists', function() {
cache.should.have.property('replace');
});
describe('should throw an error if called', function() {
it('without a key', function() {
expect(function() { cache.replace(); }).to.throw('AssertionError: Cannot "replace" without key!');
});
it('with a key that is too long', function() {
expect(function() { cache.replace(chance.string({length: 251})); }).to.throw('less than 250 characters');
});
it('with a non-string key', function() {
expect(function() { cache.replace({blah: 'test'}); }).to.throw('AssertionError: Key needs to be of type "string"');
expect(function() { cache.replace([1, 2]); }).to.throw('AssertionError: Key needs to be of type "string"');
expect(function() { cache.replace(_.noop); }).to.throw('AssertionError: Key needs to be of type "string"');
});
});
describe('should work', function() {
it('as normal', function() {
var key = getKey(), val = chance.natural(), val2 = chance.natural();
return cache.set(key, val)
.then(function() {
return cache.replace(key, val2);
})
.then(function() {
return cache.get(key);
})
.then(function(v) {
v.should.equal(val2);
});
});
it('should behave properly when replace over non-existent key', function() {
var key = getKey(), val = chance.natural();
return cache.replace(key, val)
.catch(function(err) {
expect(err.toString()).to.contain('does not exist');
});
});
});
});
describe('append', function() {
var cache;
beforeEach(function() {
cache = new Client();
});
it('exists', function() {
cache.should.have.property('append');
});
describe('should throw an error if called', function() {
it('without a key', function() {
expect(function() { cache.append(); }).to.throw('AssertionError: Cannot "append" without key!');
});
it('with a key that is too long', function() {
expect(function() { cache.append(chance.string({length: 251})); }).to.throw('less than 250 characters');
});
it('with a non-string key', function() {
expect(function() { cache.append({blah: 'test'}); }).to.throw('AssertionError: Key needs to be of type "string"');
expect(function() { cache.append([1, 2]); }).to.throw('AssertionError: Key needs to be of type "string"');
expect(function() { cache.append(_.noop); }).to.throw('AssertionError: Key needs to be of type "string"');
});
});
describe('should work', function() {
it('as normal', function() {
var key = getKey(), val = chance.string(), val2 = chance.string();
return cache.set(key, val)
.then(function() {
return cache.append(key, val2);
})
.then(function() {
return cache.get(key);
})
.then(function(v) {
v.should.equal(val + val2);
});
});
it('should behave properly when append over non-existent key', function() {
var key = getKey(), val = chance.natural();
return cache.append(key, val)
.catch(function(err) {
expect(err.toString()).to.contain('does not exist');
});
});
});
});
describe('prepend', function() {
var cache;
beforeEach(function() {
cache = new Client();
});
it('exists', function() {
cache.should.have.property('prepend');
});
describe('should throw an error if called', function() {
it('without a key', function() {
expect(function() { cache.prepend(); }).to.throw('AssertionError: Cannot "prepend" without key!');
});
it('with a key that is too long', function() {
expect(function() { cache.prepend(chance.string({length: 251})); }).to.throw('less than 250 characters');
});
it('with a non-string key', function() {
expect(function() { cache.prepend({blah: 'test'}); }).to.throw('AssertionError: Key needs to be of type "string"');
expect(function() { cache.prepend([1, 2]); }).to.throw('AssertionError: Key needs to be of type "string"');
expect(function() { cache.prepend(_.noop); }).to.throw('AssertionError: Key needs to be of type "string"');
});
});
describe('should work', function() {
it('as normal', function() {
var key = getKey(), val = chance.string(), val2 = chance.string();
return cache.set(key, val)
.then(function() {
return cache.prepend(key, val2);
})
.then(function() {
return cache.get(key);
})
.then(function(v) {
v.should.equal(val2 + val);
});
});
it('should behave properly when prepend over non-existent key', function() {
var key = getKey(), val = chance.natural();
return cache.prepend(key, val)
.catch(function(err) {
expect(err.toString()).to.contain('does not exist');
});
});
});
});
describe('items', function () {
var cache;
beforeEach(function() {
cache = new Client();
});
it('exists', function() {
cache.should.have.property('items');
});
describe('should work', function() {
it('gets slab stats', function (done) {
cache.set('test', 'test').then(function() {
return cache.items();
}).then(function (items) {
expect(items.length).to.be.above(0);
expect(items[0].slab_id).to.exist;
expect(items[0].server).to.exist;
expect(items[0].data.number).to.exist;
expect(items[0].data.age).to.exist;
done();
});
});
});
});
describe('cachedump', function () {
var cache;
beforeEach(function() {
cache = new Client();
});
it('exists', function() {
cache.should.have.property('items');
});
describe('should work', function() {
it('gets cache metadata', function (done) {
var key = getKey();
// guarantee that we will at least have one result
cache.set(key, 'test').then(function() {
return cache.items();
}).then(function(items) {
return new Promise(function(res) {
setTimeout(function() { res(items); }, 1000);
});
}).then(function (items) {
return cache.cachedump(items[0].slab_id);
}).then(function (data) {
expect(data[0].key).to.be.defined;
done();
});
});
it('gets cache metadata with limit', function (done) {
var key = getKey();
cache.set(key, 'test').then(function() {
return cache.items();
}).then(function (items) {
return cache.cachedump(items[0].slab_id, 1);
}).then(function (data) {
expect(data.length).to.equal(1);
done();
});
});
});
});
describe('version', function () {
var cache;
beforeEach(function() {
cache = new Client();
});
it('exists', function() {
cache.should.have.property('version');
});
describe('should work', function() {
it('gets version', function () {
return cache.version().then(function(v) {
expect(v).to.be.a.string;
});
});
});
});
after(function() {
var cache = new Client();
// Clean up all of the keys we created
return cache.deleteMulti(keys);
});
});