UNPKG

memcache-pp

Version:
1,250 lines (1,052 loc) 47.8 kB
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); }); });