Repository: basho/riak-nodejs-client
Pull request #124 (2016-01-06 16:19:49)

READY: remove duplicated Joi validation code


Patch:
diff --git a/lib/commands/commandbase.js b/lib/commands/commandbase.js
index fcec04f..78667fa 100644
--- a/lib/commands/commandbase.js
+++ b/lib/commands/commandbase.js
@@ -37,7 +37,7 @@ function CommandBase(pbRequestName, pbResponseName, callback) {
     var self = this;
     Joi.validate(callback, schema, function(err, option) {
        if (err) {
-           throw 'callback is required and must be a function.';
+           throw new Error('callback is required and must be a function.');
        }
        self.callback = callback;
     });
@@ -50,6 +50,16 @@ function CommandBase(pbRequestName, pbResponseName, callback) {
     // This is to facilitate debugging what pb messages this Command represents
     this.PbRequestName = pbRequestName;
     this.PbResponseName = pbResponseName;
+
+    this.validateOptions = function (options, schema, joi_opts) {
+        var self = this;
+        Joi.validate(options, schema, joi_opts, function(err, options) {
+            if (err) {
+                throw err;
+            }
+            self.options = options;
+        });
+    };
 }
 
 /**
@@ -120,7 +130,7 @@ CommandBase.prototype.getPbReqBuilder = function() {
  * @return {Object} a protocol buffer message builder
  */
 CommandBase.prototype.constructPbRequest = function() {
-    throw 'Not supported yet!';
+    throw new Error('Not supported yet!');
 };
 
 /**
@@ -132,7 +142,7 @@ CommandBase.prototype.constructPbRequest = function() {
  * @return {Boolean} true if not streaming or the last response has been received, false otherwise.
  */
 CommandBase.prototype.onSuccess = function(pbResponseMessage) {
-    throw 'Not supported yet!';
+    throw new Error('Not supported yet!');
 };
 
 /**
diff --git a/lib/commands/crdt/fetchcounter.js b/lib/commands/crdt/fetchcounter.js
index 1842b5a..e774a3a 100644
--- a/lib/commands/crdt/fetchcounter.js
+++ b/lib/commands/crdt/fetchcounter.js
@@ -44,13 +44,7 @@ var Joi = require('joi');
  */
 function FetchCounter(options, callback) {
     CommandBase.call(this, 'DtFetchReq', 'DtFetchResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
 }
 
 inherits(FetchCounter, CommandBase);
diff --git a/lib/commands/crdt/fetchmap.js b/lib/commands/crdt/fetchmap.js
index 56f4b60..f229d2c 100644
--- a/lib/commands/crdt/fetchmap.js
+++ b/lib/commands/crdt/fetchmap.js
@@ -49,13 +49,7 @@ var MapField = require('../../protobuf/riakprotobuf').getProtoFor('MapField');
  */
 function FetchMap(options, callback) {
     CommandBase.call(this, 'DtFetchReq', 'DtFetchResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
 }
 
 inherits(FetchMap, CommandBase);
diff --git a/lib/commands/crdt/fetchset.js b/lib/commands/crdt/fetchset.js
index f215368..7bc78c4 100644
--- a/lib/commands/crdt/fetchset.js
+++ b/lib/commands/crdt/fetchset.js
@@ -47,14 +47,7 @@ var Joi = require('joi');
 
 function FetchSet(options, callback) {
     CommandBase.call(this, 'DtFetchReq', 'DtFetchResp', callback);
-
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
 }
 
 inherits(FetchSet, CommandBase);
diff --git a/lib/commands/crdt/updatecounter.js b/lib/commands/crdt/updatecounter.js
index 88633fb..adb0f0c 100644
--- a/lib/commands/crdt/updatecounter.js
+++ b/lib/commands/crdt/updatecounter.js
@@ -48,14 +48,7 @@ var CounterOp = require('../../protobuf/riakprotobuf').getProtoFor('CounterOp');
  */
 function UpdateCounter(options, callback) {
     CommandBase.call(this, 'DtUpdateReq', 'DtUpdateResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-
-    });
+    this.validateOptions(options, schema);
 }
 
 inherits(UpdateCounter, CommandBase);
diff --git a/lib/commands/crdt/updatemap.js b/lib/commands/crdt/updatemap.js
index 016f46f..676276a 100644
--- a/lib/commands/crdt/updatemap.js
+++ b/lib/commands/crdt/updatemap.js
@@ -72,16 +72,10 @@ var FetchMap = require('./fetchmap');
  */
 function UpdateMap(options, callback) {
     CommandBase.call(this, 'DtUpdateReq', 'DtUpdateResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        if (options.op._hasRemoves() && options.context === null) {
-            throw 'When doing any removes a context must be provided.';
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
+    if (this.options.op._hasRemoves() && this.options.context === null) {
+        throw new Error('When doing any removes a context must be provided.');
+    }
 }
 
 inherits(UpdateMap, CommandBase);
diff --git a/lib/commands/crdt/updateset.js b/lib/commands/crdt/updateset.js
index 74e97d2..49c76e5 100644
--- a/lib/commands/crdt/updateset.js
+++ b/lib/commands/crdt/updateset.js
@@ -55,13 +55,7 @@ var SetOp = rpb.getProtoFor('SetOp');
  */
 function UpdateSet(options, callback) {
     CommandBase.call(this, 'DtUpdateReq', 'DtUpdateResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
 }
 
 inherits(UpdateSet, CommandBase);
diff --git a/lib/commands/kv/deletevalue.js b/lib/commands/kv/deletevalue.js
index 3b690b6..51dd127 100644
--- a/lib/commands/kv/deletevalue.js
+++ b/lib/commands/kv/deletevalue.js
@@ -44,18 +44,7 @@ var Joi = require('joi');
  */
 function DeleteValue(options, callback) {
     CommandBase.call(this, 'RpbDelReq', 'RpbDelResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-
-        if (err) {
-            throw err;
-        }
-
-        self.options = options;
-
-    });
-
-    this.remainingRetries = 1;
+    this.validateOptions(options, schema);
 }
 
 inherits(DeleteValue, CommandBase);
diff --git a/lib/commands/kv/fetchbucketprops.js b/lib/commands/kv/fetchbucketprops.js
index c1c8d42..45814f3 100644
--- a/lib/commands/kv/fetchbucketprops.js
+++ b/lib/commands/kv/fetchbucketprops.js
@@ -34,13 +34,7 @@ var Joi = require('joi');
  */
 function FetchBucketProps(options, callback) {
     FetchPropsBase.call(this, 'RpbGetBucketReq','RpbGetBucketResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
 }
 
 inherits(FetchBucketProps, FetchPropsBase);
diff --git a/lib/commands/kv/fetchbuckettypeprops.js b/lib/commands/kv/fetchbuckettypeprops.js
index 7907447..d2e7ed4 100644
--- a/lib/commands/kv/fetchbuckettypeprops.js
+++ b/lib/commands/kv/fetchbuckettypeprops.js
@@ -32,13 +32,7 @@ var Joi = require('joi');
  */
 function FetchBucketTypeProps(options, callback) {
     FetchPropsBase.call(this, 'RpbGetBucketTypeReq','RpbGetBucketResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
 }
 
 inherits(FetchBucketTypeProps, FetchPropsBase);
diff --git a/lib/commands/kv/fetchpreflist.js b/lib/commands/kv/fetchpreflist.js
index 6b2ec72..2fc5d27 100644
--- a/lib/commands/kv/fetchpreflist.js
+++ b/lib/commands/kv/fetchpreflist.js
@@ -40,13 +40,7 @@ var Joi = require('joi');
 
 function FetchPreflist(options, callback) {
     CommandBase.call(this, 'RpbGetBucketKeyPreflistReq', 'RpbGetBucketKeyPreflistResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
 }
 
 inherits(FetchPreflist, CommandBase);
diff --git a/lib/commands/kv/fetchvalue.js b/lib/commands/kv/fetchvalue.js
index 68fe65f..8ee1693 100644
--- a/lib/commands/kv/fetchvalue.js
+++ b/lib/commands/kv/fetchvalue.js
@@ -53,19 +53,12 @@ var Joi = require('joi');
  */
 function FetchValue(options, callback) {
     CommandBase.call(this, 'RpbGetReq', 'RpbGetResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
 }
 
 inherits(FetchValue, CommandBase);
 
 FetchValue.prototype.constructPbRequest = function() {
-
     var protobuf = this.getPbReqBuilder();
 
     protobuf.setBucket(new Buffer(this.options.bucket));
@@ -82,7 +75,6 @@ FetchValue.prototype.constructPbRequest = function() {
     protobuf.setTimeout(this.options.timeout);
 
     return protobuf;
-
 };
 
 FetchValue.prototype._parseResponse = function(rpbGetResp) {
diff --git a/lib/commands/kv/listbuckets.js b/lib/commands/kv/listbuckets.js
index 9888018..4eb34c3 100644
--- a/lib/commands/kv/listbuckets.js
+++ b/lib/commands/kv/listbuckets.js
@@ -37,13 +37,7 @@ var Joi = require('joi');
  */
 function ListBuckets(options, callback) {
     CommandBase.call(this, 'RpbListBucketsReq', 'RpbListBucketsResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
     if (!this.options.stream) {
         this.buckets = [];
     }
diff --git a/lib/commands/kv/listkeys.js b/lib/commands/kv/listkeys.js
index 5856ce4..957cd69 100644
--- a/lib/commands/kv/listkeys.js
+++ b/lib/commands/kv/listkeys.js
@@ -42,13 +42,7 @@ var Joi = require('joi');
  */
 function ListKeys(options, callback) {
     CommandBase.call(this, 'RpbListKeysReq', 'RpbListKeysResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
     if (!this.options.stream) {
         this.keys = [];
     }
@@ -57,7 +51,6 @@ function ListKeys(options, callback) {
 inherits(ListKeys, CommandBase);
 
 ListKeys.prototype.constructPbRequest = function() {
-
     var protobuf = this.getPbReqBuilder();
 
     protobuf.setBucket(new Buffer(this.options.bucket));
@@ -65,7 +58,6 @@ ListKeys.prototype.constructPbRequest = function() {
     protobuf.setTimeout(this.options.timeout);
 
     return protobuf;
-
 };
 
 ListKeys.prototype.onSuccess = function(rpbListKeysResp) {
diff --git a/lib/commands/kv/riakobject.js b/lib/commands/kv/riakobject.js
index 0ed23f2..363cd71 100644
--- a/lib/commands/kv/riakobject.js
+++ b/lib/commands/kv/riakobject.js
@@ -27,7 +27,6 @@ function RiakObject() {
 }
 
 RiakObject.prototype = {
-    
     /**
      * Set the key.
      * @method setKey
@@ -331,7 +330,6 @@ RiakObject.prototype = {
     getLastModified : function() {
         return this.lastModified;
     }
-    
 };
 
 module.exports = RiakObject;
@@ -345,7 +343,6 @@ module.exports.isIntIndex = function(indexName) {
 };
 
 module.exports.createFromRpbContent = function(rpbContent, convertToJs) {
-
     var ro = new RiakObject();
     
     var value = rpbContent.getValue();
@@ -428,7 +425,6 @@ module.exports.createFromRpbContent = function(rpbContent, convertToJs) {
     }
     
     return ro;
-    
 };
 
 /**
diff --git a/lib/commands/kv/secondaryindexquery.js b/lib/commands/kv/secondaryindexquery.js
index 6d5ec30..f297976 100644
--- a/lib/commands/kv/secondaryindexquery.js
+++ b/lib/commands/kv/secondaryindexquery.js
@@ -52,18 +52,12 @@ var RiakObject = require('./riakobject');
  */
 function SecondaryIndexQuery(options, callback) {
     CommandBase.call(this, 'RpbIndexReq', 'RpbIndexResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        // I tried making this work in the Joi schema and finally gave up. It seems
-        // alternatives can't work with multiple conditionals.
-        if (options.indexKey === null && (options.rangeStart === null || options.rangeEnd === null) ) {
-            throw ('either \'indexKey\' or \'rangeStart\' + \'rangeEnd\' are required');
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
+
+    if (this.options.indexKey === null && (this.options.rangeStart === null || this.options.rangeEnd === null)) {
+        throw new Error("either 'indexKey' or 'rangeStart' + 'rangeEnd' are required");
+    }
+
     if (!this.options.stream) {
         this.responses = [];
     }
diff --git a/lib/commands/kv/storebucketprops.js b/lib/commands/kv/storebucketprops.js
index b0a6fb5..1ca3b73 100644
--- a/lib/commands/kv/storebucketprops.js
+++ b/lib/commands/kv/storebucketprops.js
@@ -38,13 +38,7 @@ var StorePropsBase = require('./storepropsbase');
  */
 function StoreBucketProps(options, callback) {
     StorePropsBase.call(this, options, 'RpbSetBucketReq', 'RpbSetBucketResp', callback);
-    var self = this;
-    Joi.validate(options, schema, { allowUnknown: true }, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema, { allowUnknown: true });
 }
 
 inherits(StoreBucketProps, StorePropsBase);
diff --git a/lib/commands/kv/storebuckettypeprops.js b/lib/commands/kv/storebuckettypeprops.js
index d654a00..15e787e 100644
--- a/lib/commands/kv/storebuckettypeprops.js
+++ b/lib/commands/kv/storebuckettypeprops.js
@@ -37,13 +37,7 @@ var StorePropsBase = require('./storepropsbase');
  */
 function StoreBucketTypeProps(options, callback) {
     StorePropsBase.call(this, options, 'RpbSetBucketTypeReq', 'RpbSetBucketResp', callback);
-    var self = this;
-    Joi.validate(options, schema, { allowUnknown: true }, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema, { allowUnknown: true });
 }
 
 inherits(StoreBucketTypeProps, StorePropsBase);
diff --git a/lib/commands/kv/storepropsbase.js b/lib/commands/kv/storepropsbase.js
index 83d8abf..a29bcc1 100644
--- a/lib/commands/kv/storepropsbase.js
+++ b/lib/commands/kv/storepropsbase.js
@@ -53,13 +53,7 @@ var RpbModFun = rpb.getProtoFor('RpbModFun');
  */
 function StorePropsBase(options, pbRequestName, pbResponseName, callback) {
     CommandBase.call(this, pbRequestName, pbResponseName, callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
 }
 
 inherits(StorePropsBase, CommandBase);
diff --git a/lib/commands/kv/storevalue.js b/lib/commands/kv/storevalue.js
index abc4a3b..d60c9cb 100644
--- a/lib/commands/kv/storevalue.js
+++ b/lib/commands/kv/storevalue.js
@@ -53,16 +53,12 @@ var Joi = require('joi');
  */
 function StoreValue(options, callback) {
     CommandBase.call(this, 'RpbPutReq', 'RpbPutResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-        if (!options.bucket && (RiakObject.isRiakObject(options.value) && !options.value.bucket)) {
-            throw 'Must supply bucket directly or via a RiakObject';
-        }
-    });
+    this.validateOptions(options, schema);
+    var hasBucket = this.options.bucket ||
+        (RiakObject.isRiakObject(options.value) && this.options.value.bucket);
+    if (!hasBucket) {
+        throw new Error('Must supply bucket directly or via a RiakObject');
+    }
 }
 
 inherits(StoreValue, CommandBase);
diff --git a/lib/commands/mapreduce/mapreduce.js b/lib/commands/mapreduce/mapreduce.js
index 3c4208a..1618a2a 100644
--- a/lib/commands/mapreduce/mapreduce.js
+++ b/lib/commands/mapreduce/mapreduce.js
@@ -53,14 +53,10 @@ var Joi = require('joi');
  */
 function MapReduce(query, callback, stream) {
     CommandBase.call(this, 'RpbMapRedReq', 'RpbMapRedResp', callback);
-    var self = this;
-    Joi.validate({ query: query, stream: stream }, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.query = options.query;
-        self.stream = options.stream;
-    });
+    var options = { query: query, stream: stream };
+    this.validateOptions(options, schema);
+    this.query = this.options.query;
+    this.stream = this.options.stream;
     if (!this.stream) {
         this.response = {};
     }
diff --git a/lib/commands/ts/bykeybase.js b/lib/commands/ts/bykeybase.js
index 03c31d2..0acc110 100644
--- a/lib/commands/ts/bykeybase.js
+++ b/lib/commands/ts/bykeybase.js
@@ -29,13 +29,7 @@ var tsutils = require('./utils');
  */
 function ByKeyBase(options, pbRequestName, pbResponseName, callback) {
     CommandBase.call(this, pbRequestName, pbResponseName, callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
 }
 
 inherits(ByKeyBase, CommandBase);
diff --git a/lib/commands/ts/listkeys.js b/lib/commands/ts/listkeys.js
index 04702bd..5c47d89 100644
--- a/lib/commands/ts/listkeys.js
+++ b/lib/commands/ts/listkeys.js
@@ -38,13 +38,7 @@ var tsutils = require('./utils');
  */
 function ListKeys(options, callback) {
     CommandBase.call(this, 'TsListKeysReq', 'TsListKeysResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function (err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
     if (!this.options.stream) {
         this.keys = [];
     }
diff --git a/lib/commands/ts/query.js b/lib/commands/ts/query.js
index e920355..bf42649 100644
--- a/lib/commands/ts/query.js
+++ b/lib/commands/ts/query.js
@@ -42,13 +42,7 @@ var TsInterpolation = rpb.getProtoFor('TsInterpolation');
  */
 function Query(options, callback) {
     CommandBase.call(this, 'TsQueryReq', 'TsQueryResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
 }
 
 inherits(Query, CommandBase);
diff --git a/lib/commands/ts/store.js b/lib/commands/ts/store.js
index 953b458..1c96ed1 100644
--- a/lib/commands/ts/store.js
+++ b/lib/commands/ts/store.js
@@ -49,13 +49,7 @@ var TsCell = rpb.getProtoFor('TsCell');
  */
 function Store(options, callback) {
     CommandBase.call(this, 'TsPutReq', 'TsPutResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
 }
 
 util.inherits(Store, CommandBase);
diff --git a/lib/commands/yokozuna/deleteindex.js b/lib/commands/yokozuna/deleteindex.js
index 41648bf..1c9582d 100644
--- a/lib/commands/yokozuna/deleteindex.js
+++ b/lib/commands/yokozuna/deleteindex.js
@@ -33,13 +33,7 @@ var Joi = require('joi');
  */
 function DeleteIndex(options, callback) {
     CommandBase.call(this, 'RpbYokozunaIndexDeleteReq' , 'RpbDelResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
 }
 
 inherits(DeleteIndex, CommandBase);
diff --git a/lib/commands/yokozuna/fetchindex.js b/lib/commands/yokozuna/fetchindex.js
index 9d2571b..a93e408 100644
--- a/lib/commands/yokozuna/fetchindex.js
+++ b/lib/commands/yokozuna/fetchindex.js
@@ -35,13 +35,7 @@ var Joi = require('joi');
  */
 function FetchIndex(options, callback) {
     CommandBase.call(this, 'RpbYokozunaIndexGetReq' , 'RpbYokozunaIndexGetResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
 }
 
 inherits(FetchIndex, CommandBase);
diff --git a/lib/commands/yokozuna/fetchschema.js b/lib/commands/yokozuna/fetchschema.js
index 405ff1a..85b70c2 100644
--- a/lib/commands/yokozuna/fetchschema.js
+++ b/lib/commands/yokozuna/fetchschema.js
@@ -35,13 +35,7 @@ var Joi = require('joi');
  */
 function FetchSchema(options, callback) {
     CommandBase.call(this, 'RpbYokozunaSchemaGetReq' , 'RpbYokozunaSchemaGetResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
 }
 
 inherits(FetchSchema, CommandBase);
diff --git a/lib/commands/yokozuna/search.js b/lib/commands/yokozuna/search.js
index e58484a..d52d3ef 100644
--- a/lib/commands/yokozuna/search.js
+++ b/lib/commands/yokozuna/search.js
@@ -50,13 +50,7 @@ var Joi = require('joi');
  */
 function Search(options, callback)  {
     CommandBase.call(this, 'RpbSearchQueryReq', 'RpbSearchQueryResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
 }
 
 inherits(Search, CommandBase);
diff --git a/lib/commands/yokozuna/storeindex.js b/lib/commands/yokozuna/storeindex.js
index 3e62a56..6dbe756 100644
--- a/lib/commands/yokozuna/storeindex.js
+++ b/lib/commands/yokozuna/storeindex.js
@@ -38,13 +38,7 @@ var RpbYokozunaIndex = require('../../protobuf/riakprotobuf').getProtoFor('RpbYo
  */
 function StoreIndex(options, callback) {
     CommandBase.call(this, 'RpbYokozunaIndexPutReq' , 'RpbPutResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
 }
 
 inherits(StoreIndex, CommandBase);
diff --git a/lib/commands/yokozuna/storeschema.js b/lib/commands/yokozuna/storeschema.js
index da81dc2..3e9c946 100644
--- a/lib/commands/yokozuna/storeschema.js
+++ b/lib/commands/yokozuna/storeschema.js
@@ -36,13 +36,7 @@ var RpbYokozunaSchema = require('../../protobuf/riakprotobuf').getProtoFor('RpbY
  */
 function StoreSchema(options, callback) {
     CommandBase.call(this, 'RpbYokozunaSchemaPutReq' , 'RpbPutResp', callback);
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.options = options;
-    });
+    this.validateOptions(options, schema);
 }
 
 inherits(StoreSchema, CommandBase);
diff --git a/lib/core/authreq.js b/lib/core/authreq.js
index 10b5e1c..c7e30c6 100644
--- a/lib/core/authreq.js
+++ b/lib/core/authreq.js
@@ -23,23 +23,15 @@ var logger = require('winston');
  * @extends CommandBase
  */
 function AuthReq(options, callback) {
-
     CommandBase.call(this, 'RpbAuthReq', 'RpbAuthResp', callback);
-
-    var self = this;
-    Joi.validate(options, schema, function(err, options) {
-        if (err) {
-            throw err;
-        }
-        self.user = options.user;
-        self.password = options.password;
-    });
+    this.validateOptions(options, schema);
+    this.user = this.options.user;
+    this.password = this.options.password;
 }
 
 inherits(AuthReq, CommandBase);
 
 AuthReq.prototype.constructPbRequest = function() {
-
     var protobuf = this.getPbReqBuilder();
 
     protobuf.setUser(new Buffer(this.user));
@@ -62,4 +54,3 @@ var schema = Joi.object().keys({
 });
 
 module.exports = AuthReq;
-
diff --git a/lib/core/riakcluster.js b/lib/core/riakcluster.js
index 7438827..dce1e36 100644
--- a/lib/core/riakcluster.js
+++ b/lib/core/riakcluster.js
@@ -64,7 +64,6 @@ function RiakCluster(options) {
         if (err) {
             throw err;
         }
-
         self.nodes = options.nodes;
         self.executionAttempts = options.executionAttempts;
         self.state = State.CREATED;
@@ -337,7 +336,7 @@ RiakCluster.prototype._onRetryCommand = function(command, lastNode) {
 
 RiakCluster.prototype._stateCheck = function(allowedStates) {
     if (allowedStates.indexOf(this.state) === -1) {
-        throw 'RiakCluster: Illegal State; required: ' + allowedStates + ' current: ' + this.state; 
+        throw new Error('RiakCluster: Illegal State; required: ' + allowedStates + ' current: ' + this.state);
     }
 };
 
diff --git a/lib/core/riaknode.js b/lib/core/riaknode.js
index 297da56..049b949 100644
--- a/lib/core/riaknode.js
+++ b/lib/core/riaknode.js
@@ -71,11 +71,9 @@ function RiakNode(options) {
     }
 
     Joi.validate(options, schema, function (err, options) {
-
         if (err) {
             throw err;
         }
-
         self.remoteAddress = options.remoteAddress;
         self.remotePort = options.remotePort;
         self.minConnections = options.minConnections;
@@ -364,7 +362,7 @@ RiakNode.prototype._connectionClosed = function (conn) {
 
 RiakNode.prototype._stateCheck = function (allowedStates) {
     if (allowedStates.indexOf(this.state) === -1) {
-        throw "RiakNode: Illegal State; required: " + allowedStates + " current: " + this.state;
+        throw new Error("RiakNode: Illegal State; required: " + allowedStates + " current: " + this.state);
     }
 };
 


Test patch:
diff --git a/test/unit/core/authreq.js b/test/unit/core/authreq.js
new file mode 100644
index 0000000..22a6197
--- /dev/null
+++ b/test/unit/core/authreq.js
@@ -0,0 +1,18 @@
+var assert = require('assert');
+var AuthReq = require('../../../lib/core/authreq');
+
+describe('AuthReq', function() {
+    it('sets user and password from options', function(done) {
+        var user = 'user';
+        var password = 'password';
+        var opts = {
+            user: user,
+            password: password
+        };
+        var cb = function (e, r) { };
+        var r = new AuthReq(opts, cb);
+        assert.strictEqual(r.user, user);
+        assert.strictEqual(r.password, password);
+        done();
+    });
+});
diff --git a/test/unit/crdt/updatemap.js b/test/unit/crdt/updatemap.js
index 79be8e2..140fb0a 100644
--- a/test/unit/crdt/updatemap.js
+++ b/test/unit/crdt/updatemap.js
@@ -12,11 +12,8 @@ var assert = require('assert');
 var logger = require('winston');
 
 describe('UpdateMap', function() {
-
     describe('UpdateMap', function() {
-
         it('should build a DtUpdateReq correctly', function(done) {
-
            var mapOp = new UpdateMap.MapOperation();
 
             mapOp.incrementCounter('counter_1', 50)
@@ -109,7 +106,6 @@ describe('UpdateMap', function() {
                 assert(registerRemoved);
                 assert(flagRemoved);
                 assert(mapRemoved);
-
             };
 
             var verifyUpdates = function(updates, expectMapUpdate) {
@@ -174,7 +170,6 @@ describe('UpdateMap', function() {
                 }
 
                 return mapUpdate;
-
             };
 
             verifyRemoves(mapOp.removes);
@@ -182,14 +177,51 @@ describe('UpdateMap', function() {
             verifyRemoves(innerMapUpdate.map_op.removes);
             verifyUpdates(innerMapUpdate.map_op.updates, false);
 
-            // perform happy dance
-
             done();
+        });
 
+        it('should require context for any remove operations', function(done) {
+           var mapOp = new UpdateMap.MapOperation();
+            mapOp.incrementCounter('counter_1', 50)
+                .removeCounter('counter_2')
+                .addToSet('set_1', 'set_value_1')
+                .removeFromSet('set_2', 'set_value_2')
+                .removeSet('set_3')
+                .setRegister('register_1', new Buffer('register_value_1'))
+                .removeRegister('register_2')
+                .setFlag('flag_1', true)
+                .removeFlag('flag_2')
+                .removeMap('map_3');
+            mapOp.map('map_2').incrementCounter('counter_1', 50)
+                .removeCounter('counter_2')
+                .addToSet('set_1', new Buffer('set_value_1'))
+                .removeFromSet('set_2', new Buffer('set_value_2'))
+                .removeSet('set_3')
+                .setRegister('register_1', new Buffer('register_value_1'))
+                .removeRegister('register_2')
+                .setFlag('flag_1', true)
+                .removeFlag('flag_2')
+                .removeMap('map_3');
+            var b = new UpdateMap.Builder()
+                .withBucketType('maps')
+                .withBucket('myBucket')
+                .withKey('map_1')
+                .withMapOperation(mapOp)
+                .withCallback(function(){});
+            assert.throws(
+                function () {
+                    b.build();
+                },
+                function (err) {
+                    assert.strictEqual(err.message,
+                        'When doing any removes a context must be provided.');
+                    return true;
+                }
+            );
+            done();
         });
 
         it('should take a DtUpdateResp and call the users callback with the response', function(done) {
-
             var dtUpdateResp = new DtUpdateResp();
             dtUpdateResp.setKey(new Buffer('riak_generated_key'));
             dtUpdateResp.setContext(new Buffer('1234'));
@@ -230,7 +262,6 @@ describe('UpdateMap', function() {
                 mapEntries.push(mapEntry);
 
                 return mapEntries;
-
             };
 
             Array.prototype.push.apply(dtUpdateResp.map_value, createMapEntries());
@@ -274,7 +305,6 @@ describe('UpdateMap', function() {
                 .build();
 
             update.onSuccess(dtUpdateResp);
-
         });
 
         it ('should take a RpbErrorResp and call the users callback with the error message', function(done) {
@@ -303,9 +333,7 @@ describe('UpdateMap', function() {
     });
 
     describe('MapOperation', function() {
-
         it('should add counter increments', function(done) {
-
             var op = new UpdateMap.MapOperation();
 
             op.incrementCounter('counter_1', 5);
@@ -321,7 +349,6 @@ describe('UpdateMap', function() {
         });
 
         it('should invalidate counter increment on remove', function(done) {
-
             var op = new UpdateMap.MapOperation();
 
             op.incrementCounter('counter_1', 5);
@@ -332,7 +359,6 @@ describe('UpdateMap', function() {
             assert.equal(op.counters.remove[0], 'counter_1');
             assert(op._hasRemoves());
             done();
-
         });
 
         it('should invalidate counter remove on increment', function(done) {
@@ -344,11 +370,9 @@ describe('UpdateMap', function() {
             assert.equal(op.counters.increment[0].key, 'counter_1');
             assert.equal(op.counters.increment[0].increment, 5);
             done();
-
         });
 
         it('should add set adds', function(done) {
-
             var op = new UpdateMap.MapOperation();
 
             op.addToSet('set_1', 'value_1');
@@ -363,11 +387,9 @@ describe('UpdateMap', function() {
             assert.equal(op.sets.adds[1].key, 'set_2');
             assert.equal(op.sets.adds[1].add[0], 'value_1');
             done();
-
         });
 
         it('should add set removes', function(done) {
-
             var op = new UpdateMap.MapOperation();
 
             op.removeFromSet('set_1', 'value_1');
@@ -383,11 +405,9 @@ describe('UpdateMap', function() {
             assert.equal(op.sets.removes[1].remove[0], 'value_1');
             assert(op._hasRemoves());
             done();
-
         });
 
         it('should invalidate adding to a set on a remove set', function(done) {
-
            var op = new UpdateMap.MapOperation();
 
            op.addToSet('set_1', 'value_1');
@@ -402,11 +422,9 @@ describe('UpdateMap', function() {
            assert.equal(op.sets.remove[1], 'set_3');
            assert(op._hasRemoves());
            done();
-
         });
 
         it('should invalidate removing from a set on a remove set', function(done) {
-
            var op = new UpdateMap.MapOperation();
 
            op.removeFromSet('set_1', 'value_1');
@@ -421,11 +439,9 @@ describe('UpdateMap', function() {
            assert.equal(op.sets.remove[1], 'set_3');
            assert(op._hasRemoves());
            done();
-
         });
 
         it('should invalidate removing a set on an add to set', function(done) {
-
             var op = new UpdateMap.MapOperation();
 
             op.removeSet('set_1');
@@ -438,11 +454,9 @@ describe('UpdateMap', function() {
             assert(op._hasRemoves());
 
             done();
-
         });
 
         it('should invalidate removing a set on a remove from set', function(done) {
-
             var op = new UpdateMap.MapOperation();
 
             op.removeSet('set_1');
@@ -455,11 +469,9 @@ describe('UpdateMap', function() {
             assert(op._hasRemoves());
 
             done();
-
         });
 
         it('should add registers', function(done) {
-
            var op = new UpdateMap.MapOperation();
 
            op.setRegister('register_1', new Buffer('value_1'));
@@ -475,11 +487,9 @@ describe('UpdateMap', function() {
            assert.equal(op.registers.set[0].key, 'register_1');
            assert.equal(op.registers.set[0].value.toString('utf8'), 'value_3');
            done();
-
         });
 
         it('should invalidate register sets on removes', function(done) {
-
             var op = new UpdateMap.MapOperation();
 
             op.setRegister('register_1', new Buffer('value_1'));
@@ -494,11 +504,9 @@ describe('UpdateMap', function() {
             assert.equal(op.registers.remove[0], 'register_1');
             assert(op._hasRemoves());
             done();
-
         });
 
         if('should invalidate register removes on register sets', function(done) {
-
             var op = new UpdateMap.MapOperation();
             op.removeRegister('register_1');
             op.removeRegister('register_2');
@@ -512,11 +520,9 @@ describe('UpdateMap', function() {
             assert.equal(op.registers.remove[0], 'register_2');
             assert(op._hasRemoves());
             done();
-
         });
 
         it('should set flags', function(done) {
-
             var op = new UpdateMap.MapOperation();
 
             op.setFlag('flag_1', true);
@@ -532,11 +538,9 @@ describe('UpdateMap', function() {
             assert.equal(op.flags.set.length, 2);
             assert.equal(op.flags.set[0].state, false);
             done();
-
         });
 
         it('should invalidate a flag set on a flag remove', function(done) {
-
             var op = new UpdateMap.MapOperation();
 
             op.setFlag('flag_1', true);
@@ -548,11 +552,9 @@ describe('UpdateMap', function() {
             assert.equal(op.flags.remove[0], 'flag_2');
             assert(op._hasRemoves());
             done();
-
         });
 
         it('should invalidate a remove flag on a set flag', function(done) {
-
             var op = new UpdateMap.MapOperation();
 
             op.removeFlag('flag_1');
@@ -564,11 +566,9 @@ describe('UpdateMap', function() {
             assert.equal(op.flags.set.length, 1);
             assert(op._hasRemoves());
             done();
-
         });
 
         it('should nest maps', function(done) {
-
             var op = new UpdateMap.MapOperation();
             op.map('map_1')
                     .setFlag('some_flag', true)
@@ -585,11 +585,9 @@ describe('UpdateMap', function() {
             assert.equal(op.maps.modify[0].map.registers.remove.length, 1);
             assert(op._hasRemoves());
             done();
-
         });
 
         it('should detect removes in deeply nested maps', function(done) {
-
             var op = new UpdateMap.MapOperation();
             var map_1 = op.map('map_1');
             map_1.incrementCounter('counter_1', 5);
@@ -621,7 +619,6 @@ describe('UpdateMap', function() {
             assert(op._hasRemoves());
 
             done();
-
         });
 
         it('should invalidate a map modify on a map remove', function(done) {
@@ -634,11 +631,9 @@ describe('UpdateMap', function() {
             assert(op._hasRemoves());
             assert.equal(op.maps.remove[0], 'map_1');
             done();
-
         });
 
         it('should invalidate a map remove on a map modify', function(done) {
-
             var op = new UpdateMap.MapOperation();
             op.removeMap('map_1');
             op.removeMap('map_2');
@@ -648,9 +643,6 @@ describe('UpdateMap', function() {
             assert.equal(op.maps.modify.length, 1);
             assert(op._hasRemoves());
             done();
-
         });
-
     });
 });
-
diff --git a/test/unit/joi.js b/test/unit/joi.js
new file mode 100644
index 0000000..67b5baa
--- /dev/null
+++ b/test/unit/joi.js
@@ -0,0 +1,19 @@
+'use strict';
+
+var Joi = require('joi');
+var assert = require('assert');
+
+describe('Joi', function() {
+    it('returns object being validated on success', function(done) {
+        var cb = function (err, rslt) { };
+        var schema = Joi.func().required();
+        var self = this;
+        Joi.validate(cb, schema, function(err, value) {
+            if (err) {
+                throw new Error('callback is required and must be a function.');
+            }
+            assert.strictEqual(value, cb);
+            done();
+        });
+    });
+});
diff --git a/test/unit/kv/secondaryindexquery.js b/test/unit/kv/secondaryindexquery.js
index bb393cb..d129bc6 100644
--- a/test/unit/kv/secondaryindexquery.js
+++ b/test/unit/kv/secondaryindexquery.js
@@ -11,7 +11,6 @@ var assert = require('assert');
 describe('SecondaryIndexQuery', function() {
     describe('Build', function() {
         it('should build a RpbIndexReq correctly for a single key query', function(done) {
-            
             var siq = new SecondaryIndexQuery.Builder()
                     .withBucket('MyBucket')
                     .withIndexName('email_bin')
@@ -39,8 +38,6 @@ describe('SecondaryIndexQuery', function() {
             assert.equal(protobuf.getQtype(), RpbIndexReq.IndexQueryType.eq);
             assert.equal(protobuf.getMaxResults(), 20);
             done();
-            
-            
         });
 
         it('should build a RpbIndexReq correctly for a single _bin key query without string2int conversion', function(done) {
@@ -69,7 +66,6 @@ describe('SecondaryIndexQuery', function() {
         });
 
         it('should build a RpbIndexReq correctly for a range query', function(done) {
-            
             var siq = new SecondaryIndexQuery.Builder()
                     .withBucket('MyBucket')
                     .withIndexName('id_int')
@@ -101,7 +97,6 @@ describe('SecondaryIndexQuery', function() {
         });
         
         it('maxResults should override paginationSort', function(done) {
-            
             var siq = new SecondaryIndexQuery.Builder()
                     .withBucket('MyBucket')
                     .withIndexName('id_int')
@@ -127,26 +122,27 @@ describe('SecondaryIndexQuery', function() {
             
             assert.equal(protobuf.getPaginationSort(), true);
             done();
-            
         });
         
         it('requires either an indexKey or rangeStart + rangeEnd', function(done) {
-           
            assert.throws(
-                function() { var siq = new SecondaryIndexQuery.Builder()
-                            .withBucket('MyBucket')
-                            .withIndexName('id_int')
-                            .withCallback(function(){})
-                            .build();
+                function() {
+                    var siq = new SecondaryIndexQuery.Builder()
+                        .withBucket('MyBucket')
+                        .withIndexName('id_int')
+                        .withCallback(function(){})
+                        .build();
                 },
-                'either \'indxKey\' or \'rangeStart\' + \'rangeEnd\' are required'
+                function (err) {
+                    assert.strictEqual(err.message,
+                        "either 'indexKey' or 'rangeStart' + 'rangeEnd' are required");
+                    return true;
+                }
             );
             done();
-            
         });
         
         it('indexKey overrides range if both supplied', function(done) {
-           
             var siq = new SecondaryIndexQuery.Builder()
                     .withBucket('MyBucket')
                     .withIndexName('id_int')
@@ -162,13 +158,10 @@ describe('SecondaryIndexQuery', function() {
             assert.equal(protobuf.getRangeMax(), null);
             assert.equal(protobuf.getQtype(), RpbIndexReq.IndexQueryType.eq);
             done();
-            
         });
         
         it('should take multiple RpbIndexResp with just object keys and call the users callback with the response', function(done) {
-            
             var callback = function(err, response) {
-                
                 assert.equal(response.values.length, 100);
                 assert.equal(response.values[0].indexKey, null);
                 assert.equal(response.values[0].objectKey, 'object_key');
@@ -199,9 +192,7 @@ describe('SecondaryIndexQuery', function() {
         });
         
         it('should take multiple RpbIndexResp with term/key pairs and call the users callback with the response', function(done) {
-            
             var callback = function(err, response) {
-                
                 assert.equal(response.values.length, 100);
                 assert.equal(response.values[0].indexKey, 'index_key');
                 assert.equal(response.values[0].objectKey, 'object_key');
@@ -236,11 +227,9 @@ describe('SecondaryIndexQuery', function() {
         });
         
         it('should take multiple RpbIndexResp with just object keys and stream the response', function(done) {
-            
             var count = 0;
             var timesCalled = 0;
             var callback = function(err, response) {
-                
                 timesCalled++;
                 count += response.values.length;
                 
@@ -278,7 +267,6 @@ describe('SecondaryIndexQuery', function() {
         });
         
         it('should take multiple RpbIndexResp with term/key pairs and stream the response', function(done) {
-            
             var count = 0;
             var timesCalled = 0;
             var callback = function(err, response) {
@@ -343,10 +331,6 @@ describe('SecondaryIndexQuery', function() {
                     .build();
             
             siq.onRiakError(rpbErrorResp);
-           
-           
        });
-        
     });
 });
-    
diff --git a/test/unit/kv/storevalue.js b/test/unit/kv/storevalue.js
index 6d2e5d5..1aab31e 100644
--- a/test/unit/kv/storevalue.js
+++ b/test/unit/kv/storevalue.js
@@ -90,7 +90,23 @@ describe('StoreValue', function() {
             done();
         });
         
-        it('Should take the key, bucket, type and vclock from a RiakObject', function(done) {
+        it('should require a bucket from options or RiakObject', function(done) {
+            var b = new StoreValue.Builder()
+                .withContent('test content')
+                .withCallback(function(){});
+            assert.throws(
+                function () {
+                    b.build();
+                },
+                function (err) {
+                    assert.strictEqual(err.message, 'Must supply bucket directly or via a RiakObject');
+                    return true;
+                }
+            );
+            done();
+        });
+
+        it('should take the key, bucket, type and vclock from a RiakObject', function(done) {
             var value = 'this is a value';
             var riakObject = new RiakObject()
                     .setKey('key')