markin-couchbase
Version:
Markin Fork of Couchbase Node.js Client Library.
847 lines (797 loc) • 27.8 kB
JavaScript
;
var assert = require('assert');
var harness = require('./harness.js');
describe('#crud', function () {
function allTests(H) {
function testBadBasic(fn) {
it('should fail with an invalid key type', function () {
assert.throws(function () {
fn({}, undefined, H.noCallback());
}, TypeError);
});
it('should fail with an invalid hashkey option', function () {
assert.throws(function () {
fn(H.key(), {hashkey: {}}, H.noCallback());
}, TypeError);
});
it('should fail with non object options', function () {
assert.throws(function () {
fn(H.key(), 'opts', H.noCallback());
}, TypeError);
});
it('should fail if no callback is passed', function () {
assert.throws(function () {
fn(H.key(), {});
}, TypeError);
});
}
function testBadDura(fn) {
it('should fail for negative persist_to values', function() {
assert.throws(function() {
fn(H.key(), {persist_to:-1}, H.noCallback());
});
});
it('should fail for negative replicate_to values', function() {
assert.throws(function() {
fn(H.key(), {replicate_to:-1}, H.noCallback());
});
});
it('should fail for very-high persist_to values', function() {
assert.throws(function() {
fn(H.key(), {persist_to:10}, H.noCallback());
});
});
it('should fail for very-high replicate_to values', function() {
assert.throws(function() {
fn(H.key(), {replicate_to:10}, H.noCallback());
});
});
}
function testBadExpiry(fn) {
it('should fail with negative expiry', function () {
assert.throws(function () {
fn(H.key(), -1, H.noCallback());
}, TypeError);
});
it('should fail with null expiry', function () {
assert.throws(function () {
fn(H.key(), null, H.noCallback());
}, TypeError);
});
it('should fail with non-numeric expiry', function () {
assert.throws(function () {
fn(H.key(), '1', H.noCallback());
}, TypeError);
});
}
function testBadCas(fn) {
it('should fail with an invalid cas option', function () {
assert.throws(function () {
fn(H.key(), 1111, H.noCallback());
}, Error);
});
}
function testBadValue(fn) {
it('should fail with an undefined value', function () {
assert.throws(function () {
fn(H.key(), undefined, H.noCallback());
}, TypeError);
});
}
describe('#store', function () {
describe('upsert', function () {
testBadBasic(function (key, options, callback) {
H.b.upsert(key, 'bar', options, callback);
});
testBadExpiry(function (key, expiry, callback) {
H.b.upsert(key, 'bar', {expiry: expiry}, callback);
});
testBadCas(function(key, cas, callback) {
H.b.upsert(key, 'bar', {cas: cas}, callback);
});
testBadValue(function(key, value, callback) {
H.b.upsert(key, value, {}, callback);
});
testBadDura(function(key, options, callback) {
H.b.upsert(key, 'bar', options, callback);
});
});
describe('insert', function () {
testBadBasic(function (key, options, callback) {
H.b.insert(key, 'bar', options, callback);
});
testBadExpiry(function (key, expiry, callback) {
H.b.insert(key, 'bar', {expiry: expiry}, callback);
});
testBadCas(function(key, cas, callback) {
H.b.insert(key, 'bar', {cas: cas}, callback);
});
testBadValue(function(key, value, callback) {
H.b.insert(key, value, {}, callback);
});
testBadDura(function(key, options, callback) {
H.b.insert(key, 'bar', options, callback);
});
});
});
describe('#retrieve', function () {
describe('get', function () {
testBadBasic(function (key, options, callback) {
H.b.get(key, options, callback);
});
});
describe('getMulti', function() {
it('should fail with a non-array keys', function() {
assert.throws(function() {
H.b.getMulti('key', H.noCallback());
}, TypeError);
});
it('should fail for undefined keys', function() {
assert.throws(function() {
H.b.getMulti(undefined, H.noCallback());
}, TypeError);
});
it('should fail with an empty array of keys', function() {
assert.throws(function() {
H.b.getMulti([], H.noCallback());
}, TypeError);
});
it('should fail with a missing callback', function() {
assert.throws(function() {
H.b.getMulti(['test1', 'test2']);
}, TypeError);
});
it('should work normally', function(done) {
var key1 = H.key();
var key2 = H.key();
H.b.insert(key1, 'foo', H.okCallback(function() {
H.b.insert(key2, 'bar', H.okCallback(function() {
H.b.getMulti([key1, key2], function(err, res) {
assert(!err);
assert(res);
assert(res[key1]);
assert(res[key1].cas);
assert(res[key1].value);
assert(!res[key1].error);
assert(res[key2]);
assert(res[key2].cas);
assert(res[key2].value);
assert(!res[key2].error);
done();
});
}));
}));
});
it('should work with partial failures', function(done) {
var key1 = H.key();
var key2 = H.key();
H.b.insert(key1, 'foo', H.okCallback(function() {
H.b.getMulti([key1, key2], function(err, res) {
assert(err === 1);
assert(res);
assert(res[key1]);
assert(res[key1].cas);
assert(res[key1].value);
assert(!res[key1].error);
assert(res[key2]);
assert(!res[key2].cas);
assert(!res[key2].value);
assert(res[key2].error);
done();
});
}));
});
});
describe('getAndLock', function () {
testBadBasic(function (key, options, callback) {
H.b.getAndLock(key, options, callback);
});
it('should fail with an invalid lockTime', function () {
assert.throws(function () {
H.b.getAndLock(H.key(), {lockTime: 0}, H.noCallback());
}, TypeError);
});
it('should fail with a negative lockTime', function () {
assert.throws(function () {
H.b.getAndLock(H.key(), {lockTime: -1}, H.noCallback());
}, TypeError);
});
it('should fail with an non-numeric lockTime', function () {
assert.throws(function () {
H.b.getAndLock(H.key(), {lockTime: '1'}, H.noCallback());
}, TypeError);
});
});
describe('getAndTouch', function () {
testBadBasic(function (key, options, callback) {
H.b.getAndTouch(key, 1, options, callback);
});
testBadExpiry(function (key, expiry, callback) {
H.b.getAndTouch(key, expiry, {}, callback);
});
testBadDura(function(key, options, callback) {
H.b.getAndTouch(key, 1, options, callback);
});
});
describe('getReplica', function () {
testBadBasic(function (key, options, callback) {
H.b.getReplica(key, options, callback);
});
// This test just tries to invoke the function so that it appears to
// work in some way. In reality, we can't consistently test this since
// we can't predetermine the cluster size.
it('should not crash', function(done) {
H.b.getReplica(H.key(), function () {
done();
});
});
});
});
describe('#misc', function () {
describe('remove', function () {
testBadBasic(function (key, options, callback) {
H.b.remove(key, options, callback);
});
testBadCas(function(key, cas, callback) {
H.b.remove(key, {cas: cas}, callback);
});
testBadDura(function(key, options, callback) {
H.b.remove(key, options, callback);
});
it('should fail on missing key', function(done) {
H.b.remove(H.key(), function(err, res) {
assert(err);
assert(!res);
done();
});
});
it('should fail on a locked key', function(done) {
var key = H.key();
H.b.insert(key, 'foo', H.okCallback(function(){
H.b.getAndLock(key, H.okCallback(function() {
H.b.remove(key, function(err, res) {
assert(err);
assert(!res);
done();
});
}));
}));
});
});
describe('unlock', function () {
testBadBasic(function (key, options, callback) {
H.b.unlock(key, {}, options, callback);
});
testBadCas(function(key, cas, callback) {
H.b.unlock(key, cas, callback);
});
it('should fail on missing key', function(done) {
H.b.unlock(H.key(), {}, function(err, res) {
assert(err);
assert(!res);
done();
});
});
it('should fail on an never been locked key', function(done) {
var key = H.key();
H.b.insert(key, 'foo', H.okCallback(function(insertRes) {
H.b.unlock(key, insertRes.cas, function(err, res) {
assert(err);
assert(!res);
done();
});
}));
});
it('should fail on a timed out lock (slow)', function(done) {
this.timeout(3000);
var key = H.key();
H.b.insert(key, 'foo', H.okCallback(function(insertRes) {
H.b.getAndLock(key, {lockTime:1}, H.okCallback(function(lockRes) {
setTimeout(function() {
H.b.unlock(key, lockRes.cas, function(err, res) {
assert(err);
assert(!res);
done();
});
},2000);
}));
}));
});
it('should fail for an incorrect as', function(done) {
var key = H.key();
H.b.insert(key, 'foo', H.okCallback(function(insertRes) {
H.b.getAndLock(key, H.okCallback(function() {
H.b.unlock(key, insertRes.cas, function(err, res) {
assert(err);
assert(!res);
done();
});
}));
}));
});
it('should actually unlock the key', function(done) {
var key = H.key();
H.b.insert(key, 'foo', H.okCallback(function() {
H.b.getAndLock(key, H.okCallback(function(lockRes) {
H.b.unlock(key, lockRes.cas, function() {
H.b.replace(key, 'bar', H.okCallback(function() {
done();
}));
});
}));
}));
});
});
describe('touch', function () {
testBadBasic(function (key, options, callback) {
H.b.touch(key, 1, options, callback);
});
testBadExpiry(function (key, expiry, callback) {
H.b.touch(key, expiry, {}, callback);
});
testBadDura(function(key, options, callback) {
H.b.touch(key, options, callback);
});
});
describe('counter', function() {
testBadBasic(function (key, options, callback) {
H.b.counter(key, 1, options, callback);
});
testBadDura(function(key, options, callback) {
H.b.counter(key, 1, options, callback);
});
it('should fail when passed a 0 delta', function () {
assert.throws(function () {
H.b.counter(H.key(), 0, H.noCallback());
}, TypeError);
});
it('should fail when passed a non-numeric delta', function () {
assert.throws(function () {
H.b.counter(H.key(), {}, H.noCallback());
}, TypeError);
});
it('should fail when passed a negative initial', function () {
assert.throws(function () {
H.b.counter(H.key(), 1, {initial:-1}, H.noCallback());
}, TypeError);
});
it('should fail when passed a non-numeric initial', function () {
assert.throws(function () {
H.b.counter(H.key(), 1, {initial:{}}, H.noCallback());
}, TypeError);
});
it('should increment properly', function(done) {
var key = H.key();
H.b.insert(key, '6', H.okCallback(function() {
H.b.counter(key, 1, H.okCallback(function(res) {
assert(res.value, 7);
H.b.get(key, H.okCallback(function(getRes) {
assert(getRes.value, res.value);
done();
}));
}));
}));
});
it('should add properly', function(done) {
var key = H.key();
H.b.insert(key, '6', H.okCallback(function() {
H.b.counter(key, 3, H.okCallback(function(res) {
assert(res.value, 9);
H.b.get(key, H.okCallback(function(getRes) {
assert(getRes.value, res.value);
done();
}));
}));
}));
});
it('should decrement properly', function(done) {
var key = H.key();
H.b.insert(key, '6', H.okCallback(function() {
H.b.counter(key, -1, H.okCallback(function(res) {
assert(res.value, 5);
H.b.get(key, H.okCallback(function(getRes) {
assert(getRes.value, res.value);
done();
}));
}));
}));
});
it('should subtract properly', function(done) {
var key = H.key();
H.b.insert(key, '6', H.okCallback(function() {
H.b.counter(key, -3, H.okCallback(function(res) {
assert.equal(res.value, 3);
H.b.get(key, H.okCallback(function(getRes) {
assert.equal(getRes.value, res.value);
done();
}));
}));
}));
});
it('should fail on missing key', function(done) {
H.b.counter(H.key(), 1, function(err, res) {
assert(err);
assert(!res);
done();
});
});
it('should fail on a locked key', function(done) {
var key = H.key();
H.b.insert(key, 'foo', H.okCallback(function(){
H.b.getAndLock(key, H.okCallback(function() {
H.b.counter(key, 1, function(err, res) {
assert(err);
assert(!res);
done();
});
}));
}));
});
it('should create with initial set', function(done) {
var key = H.key();
H.b.counter(key, 1, {initial:7}, H.okCallback(function(res) {
assert.equal(res.value, 7);
H.b.get(key, H.okCallback(function(getRes) {
assert.equal(getRes.value, '7');
done();
}));
}));
});
});
describe('append', function() {
testBadBasic(function (key, options, callback) {
H.b.append(key, 'foo', options, callback);
});
testBadDura(function(key, options, callback) {
H.b.append(key, 'foo', options, callback);
});
it('should append to a key', function(done) {
var key = H.key();
H.b.insert(key, 'foo', H.okCallback(function(insertRes) {
H.b.append(key, 'bar', H.okCallback(function(appendRes) {
assert.notDeepEqual(insertRes.cas, appendRes.cas);
H.b.get(key, H.okCallback(function(getRes) {
assert.deepEqual(getRes.cas, appendRes.cas);
assert.equal(getRes.value, 'foobar');
done();
}));
}));
}));
});
it('should fail on missing key', function(done) {
H.b.append(H.key(), 'foo', function(err, res) {
assert(err);
assert(!res);
done();
});
});
it('should fail on a locked key', function(done) {
var key = H.key();
H.b.insert(key, 'foo', H.okCallback(function(){
H.b.getAndLock(key, H.okCallback(function() {
H.b.append(key, 'bar', function(err, res) {
assert(err);
assert(!res);
done();
});
}));
}));
});
});
describe('prepend', function() {
testBadBasic(function (key, options, callback) {
H.b.prepend(key, 'foo', options, callback);
});
testBadDura(function(key, options, callback) {
H.b.prepend(key, 'foo', options, callback);
});
it('should prepend to a key', function(done) {
var key = H.key();
H.b.insert(key, 'foo', H.okCallback(function(insertRes) {
H.b.prepend(key, 'bar', H.okCallback(function(appendRes) {
assert.notDeepEqual(insertRes.cas, appendRes.cas);
H.b.get(key, H.okCallback(function(getRes) {
assert.deepEqual(getRes.cas, appendRes.cas);
assert.equal(getRes.value, 'barfoo');
done();
}));
}));
}));
});
it('should fail on missing key', function(done) {
H.b.prepend(H.key(), 'foo', function(err, res) {
assert(err);
assert(!res);
done();
});
});
it('should fail on a locked key', function(done) {
var key = H.key();
H.b.insert(key, 'foo', H.okCallback(function(){
H.b.getAndLock(key, H.okCallback(function() {
H.b.prepend(key, 'bar', function(err, res) {
assert(err);
assert(!res);
done();
});
}));
}));
});
});
});
it('should successfully round-trip a document', function (done) {
var key = H.key();
var doc = {x: 'hello', y: 'world'};
H.b.insert(key, doc, H.okCallback(function () {
H.b.get(key, H.okCallback(function (res) {
assert.deepEqual(res.value, doc);
done();
}));
}));
});
it('should fail to insert an already existing document', function(done) {
var key = H.key();
H.b.insert(key, 'bar', H.okCallback(function() {
H.b.insert(key, 'foo', function(err, res) {
assert(err);
done();
});
}));
});
it('should successfully upsert a document', function(done) {
var key = H.key();
H.b.upsert(key, 'bar', H.okCallback(function(upsertRes){
H.b.get(key, H.okCallback(function(getRes) {
assert.deepEqual(getRes.cas, upsertRes.cas);
done();
}))
}));
});
it('should fail upsert on a locked key', function(done) {
var key = H.key();
H.b.insert(key, 'foo', H.okCallback(function(){
H.b.getAndLock(key, H.okCallback(function() {
H.b.upsert(key, 'bar', function(err, res) {
assert(err);
assert(!res);
done();
});
}));
}));
});
it('should fail replace on a locked key', function(done) {
var key = H.key();
H.b.insert(key, 'foo', H.okCallback(function(){
H.b.getAndLock(key, H.okCallback(function() {
H.b.replace(key, 'bar', function(err, res) {
assert(err);
assert(!res);
done();
});
}));
}));
});
it('should fail upsert with cas mismatch', function(done) {
var key = H.key();
H.b.insert(key, 'foo', H.okCallback(function(insertRes){
H.b.upsert(key, 'bar', H.okCallback(function(){
H.b.upsert(key, 'wat', {cas:insertRes.cas}, function(err, res) {
assert(err);
assert(!res);
done();
});
}));
}));
});
it('should successfully expire a document (slow)', function (done) {
this.timeout(3000);
var key = H.key();
H.b.insert(key, 'foo', {expiry: 1}, H.okCallback(function () {
H.b.get(key, H.okCallback(function () {
setTimeout(function () {
H.b.get(key, function (err) {
assert(err);
done();
});
}, 2000);
}));
}));
});
function touchTests(touchFn) {
it('should succesfully update expiry (slow)', function(done) {
this.timeout(5000);
var key = H.key();
H.b.insert(key, 'foo', {expiry: 1}, H.okCallback(function () {
touchFn(key, 2, H.okCallback(function (res) {
setTimeout(function () {
H.b.get(key, H.okCallback(function() {
setTimeout(function() {
H.b.get(key, function (err, res) {
assert(err);
assert(!res);
done();
});
}, 3000);
}));
}, 100);
}));
}));
});
it('should fail on missing key', function(done) {
touchFn(H.key(), 1, function(err, res) {
assert(err);
assert(!res);
done();
});
});
it('should fail on a locked key', function(done) {
var key = H.key();
H.b.insert(key, 'foo', H.okCallback(function(){
H.b.getAndLock(key, H.okCallback(function() {
touchFn(key, 1, function(err, res) {
assert(err);
assert(!res);
done();
});
}));
}));
});
}
describe('getAndTouch', function() {
touchTests(function(key, expiry, callback) {
H.b.getAndTouch(key, expiry, callback);
});
});
describe('touch', function() {
touchTests(function(key, expiry, callback) {
H.b.touch(key, expiry, callback);
});
});
it('should require an accurate cas', function (done) {
var key = H.key();
H.b.insert(key, 'bar', H.okCallback(function (insertRes) {
H.b.replace(key, 'foo', H.okCallback(function () {
H.b.replace(key, 'dog', {cas: insertRes.cas}, function (err) {
assert(err);
done();
});
}));
}));
});
it('should work with a valid cas', function (done) {
var key = H.key();
H.b.insert(key, 'bar', H.okCallback(function (insertRes) {
H.b.replace(key, 'dog', {cas: insertRes.cas}, H.okCallback(function () {
done();
}));
}));
});
it('should be able to remove a document', function(done) {
var key = H.key();
H.b.insert(key, 'bar', H.okCallback(function(insertRes) {
H.b.remove(key, H.okCallback(function(removeRes) {
assert.notDeepEqual(insertRes.cas, removeRes.cas);
H.b.get(key, function(getErr, getRes) {
assert(getErr);
assert(!getRes);
done();
});
}));
}));
});
it('getAndLock should get the value', function(done) {
var key = H.key();
H.b.insert(key, 'bar', H.okCallback(function(insertRes) {
H.b.getAndLock(key, H.okCallback(function(lockRes){
assert.notDeepEqual(insertRes.cas, lockRes.cas);
assert.equal(lockRes.value, 'bar');
done();
}));
}));
});
it('get should work on a locked key', function(done) {
var key = H.key();
H.b.insert(key, 'bar', H.okCallback(function() {
H.b.getAndLock(key, H.okCallback(function(){
H.b.get(key, H.okCallback(function(res) {
assert(res.value, 'bar');
done();
}));
}));
}));
});
it('should fail getAndLock for missing key', function(done) {
H.b.getAndLock(H.key(), function(err, res) {
assert(err);
assert(!res);
done();
});
});
it('should fail getAndLock for locked key', function(done) {
var key = H.key();
H.b.insert(key, 'bar', H.okCallback(function(insertRes) {
H.b.getAndLock(key, {lockTime: 1}, H.okCallback(function (lockRes) {
assert.notDeepEqual(insertRes.cas, lockRes.cas);
H.b.getAndLock(key, function(err, res) {
assert(err);
assert(!res);
done();
});
}));
}));
});
it('should set the correct lockTime (slow)', function(done) {
// This test takes at least 2000ms to execute due to Server lock time
// rounding issue (locktime 1000ms takes up to 1999ms to expire).
this.timeout(3000);
var key = H.key();
H.b.insert(key, 'bar', H.okCallback(function(insertRes) {
H.b.getAndLock(key, {lockTime:1}, H.okCallback(function(lockRes){
assert.notDeepEqual(insertRes.cas, lockRes.cas);
H.b.replace(key, 'foo', function(getErr, getRes) {
assert(getErr);
assert(!getRes);
setTimeout(function() {
H.b.replace(key, 'rew', H.okCallback(function(replaceRes) {
assert.notDeepEqual(lockRes.cas, replaceRes.cas);
done();
}));
}, 2000);
});
}));
}));
});
it('should fail to get a missing key', function(done) {
H.b.get(H.key(), function(err, res) {
assert(err);
assert(!res);
done();
});
});
it('durability should not crash', function (done) {
this.timeout(4000);
H.b.insert(H.key(), 'foo', {persist_to: 1, replicate_to: 0}, function() {
done();
});
});
it('durability should propagate errors', function (done) {
H.b.replace(H.key(), 'bar', {persist_to: 1, replicate_to: 0},
function (err, res) {
assert(err);
done();
});
});
it('should return from replace with 0 expiry (slow)', function(done) {
this.timeout(3000);
var key = H.key();
H.b.insert(key, 'bar', {expiry:1}, H.okCallback(function(insertRes) {
H.b.replace(key, 'foo', {expiry:0}, H.okCallback(function (replaceRes) {
setTimeout(function () {
H.b.get(key, H.okCallback(function (getRes) {
assert(getRes);
done();
}));
}, 2000);
}));
}));
});
it('should work with string cas values', function(done) {
var key = H.key();
H.b.upsert(key, 'test1', H.okCallback(function(res) {
var strCas = res.cas.toString();
var jsonCas = JSON.stringify(res.cas);
assert(jsonCas);
assert(strCas);
assert(jsonCas === '"'+strCas+'"');
H.b.upsert(key, 'test2', {cas:'14'}, function(err) {
assert(err);
H.b.upsert(key, 'test3', {cas:strCas}, H.okCallback(function(res) {
done();
}));
});
}));
});
}
describe('#RealBucket', allTests.bind(this, harness));
describe('#MockBucket', allTests.bind(this, harness.mock));
});