Repository: auth0/node-auth0
Pull request #50 (2015-11-25 17:18:16)

Improve test coverage and fix bugs in Auth0 module
- Added more tests for ClientsManager
- Added tests for BlacklistedTokensManager
- Fixed bug in BlacklistedTokensManager
- Added dependency incorrectly flagged as "development dependency".
- Updated instructions for SDK initialization in the README file


Patch:
diff --git a/README.md b/README.md
index 4e4b379c0..e7305b58d 100644
--- a/README.md
+++ b/README.md
@@ -6,38 +6,20 @@ Node.js client library for the [Auth0](https://auth0.com) platform.
 
 ## Installation
 
-	npm install auth0@2.0.0-beta.2
+	npm install auth0@2.0.0-alpha.5
 
 ## Usage
 
-Initialize your client class with an API v2 token (you can generate one [here](https://auth0.com/docs/apiv2)).
-
-~~~js
-var token = '{YOUR_API_V2_TOKEN}';
-var auth0 = require('auth0')({
-  token: token
-});
-~~~
-
-By default the code assumes your account is running in the US West region. If you are running in Europe you can specify:
+Initialize your client class with an API v2 token (you can generate one [here](https://auth0.com/docs/apiv2)) and a domain.
 
 ~~~js
 var token = '{YOUR_API_V2_TOKEN}';
 var auth0 = require('auth0')({
   token: token,
-  region: 'eu'
+  domain: 'YOUR_ACCOUNT.auth0.com'
 });
 ~~~
 
-Alternatively you can just set the domain:
-
-~~~js
-var token = '{YOUR_API_V2_TOKEN}';
-var auth0 = require('auth0')({
-  token: token,
-  domain: 'login.eu.auth0.com'
-});
-~~~
 
 ## Promises and Callbacks
 Be aware that all methods can be used with Promises or callbacks. However, when a callback is provided, no Promise will be returned.
@@ -56,7 +38,7 @@ auth0.getUsers(function (err, users) {
 auth0
   .getUsers()
   .then(function (users) {
-    console.log(users);
+    console.log(users);	
   })
   .catch(function (err) {
     // Handle error.
@@ -88,7 +70,7 @@ auth0.createClient(data, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Client created.
 });
 
@@ -98,7 +80,7 @@ auth0.clients.create(data, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Client created.
 });
 ~~~
@@ -111,7 +93,7 @@ auth0.getClient({ client_id: CLIENT_ID }, function (err, client) {
   if (err) {
     // Handle error.
   }
-
+  
   console.log(client);
 });
 
@@ -121,7 +103,7 @@ auth0.clients.get({ client_id: CLIENT_ID }, function (err, client) {
   if (err) {
     // Handle error.
   }
-
+  
   console.log(client);
 });
 ~~~
@@ -134,7 +116,7 @@ auth0.deleteClient({ client_id: CLIENT_ID }, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Client deleted.
 });
 
@@ -144,7 +126,7 @@ auth0.clients.delete({ client_id: CLIENT_ID }, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Client deleted.
 });
 ~~~
@@ -161,7 +143,7 @@ auth0.updateClient(params, data, function (err, client) {
   if (err) {
     // Handle error.
   }
-
+  
   console.log(client.name);  // 'newClientName'
 });
 
@@ -171,7 +153,7 @@ auth0.clients.update(params, data, function (err, client) {
   if (err) {
     // Handle error.
   }
-
+  
   console.log(client.name);  // 'newClientName'
 });
 ~~~
@@ -201,7 +183,7 @@ auth0.createConnection(data, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Conection created.
 });
 
@@ -211,7 +193,7 @@ auth0.connections.create(data, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Conection created.
 });
 ~~~
@@ -224,7 +206,7 @@ auth0.getConnection({ id: CONNECTION_ID }, function (err, connection) {
   if (err) {
     // Handle error.
   }
-
+  
   console.log(connection);
 });
 
@@ -234,7 +216,7 @@ auth0.connections.get({ id: CONNECTION_ID }, function (err, connection) {
   if (err) {
     // Handle error.
   }
-
+  
   console.log(connection);
 });
 ~~~
@@ -247,7 +229,7 @@ auth0.deleteConnection({ id: CONNECTION_ID }, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Conection deleted.
 });
 
@@ -257,7 +239,7 @@ auth0.connections.delete({ id: CONNECTION_ID }, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Conection deleted.
 });
 ~~~
@@ -274,7 +256,7 @@ auth0.updateConnection(params, data, function (err, connection) {
   if (err) {
     // Handle error.
   }
-
+  
   console.log(connection.name);  // 'newConnectionName'
 });
 
@@ -284,7 +266,7 @@ auth0.connections.update(params, data, function (err, connection) {
   if (err) {
     // Handle error.
   }
-
+  
   console.log(connection.name);  // 'newConnectionName'
 });
 ~~~
@@ -315,7 +297,7 @@ auth0.createConnection(data, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Credential created.
 });
 
@@ -325,7 +307,7 @@ auth0.deviceCredentials.create(data, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Credential created.
 });
 ~~~
@@ -340,7 +322,7 @@ auth0.deleteDeviceCredential(params, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Credential deleted.
 });
 
@@ -350,7 +332,7 @@ auth0.deviceCredentials.delete(params, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Credential deleted.
 });
 ~~~
@@ -381,7 +363,7 @@ auth0.createRule(data, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Rule created.
 });
 
@@ -391,7 +373,7 @@ auth0.rules.create(data, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Rule created.
 });
 ~~~
@@ -404,7 +386,7 @@ auth0.getRule({ id: RULE_ID }, function (err, rule) {
   if (err) {
     // Handle error.
   }
-
+  
   console.log(rule);
 });
 
@@ -414,7 +396,7 @@ auth0.rules.get({ id: RULE_ID }, function (err, rule) {
   if (err) {
     // Handle error.
   }
-
+  
   console.log(rule);
 });
 ~~~
@@ -427,7 +409,7 @@ auth0.deleteRule({ id: RULE_ID }, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Rule deleted.
 });
 
@@ -437,7 +419,7 @@ auth0.rules.delete({ id: RULE_ID }, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Rule deleted.
 });
 ~~~
@@ -454,7 +436,7 @@ auth0.updateRule(params, data, function (err, rule) {
   if (err) {
     // Handle error.
   }
-
+  
   console.log(rule.name);  // 'New name'
 });
 
@@ -464,7 +446,7 @@ auth0.rules.update(params, data, function (err, rule) {
   if (err) {
     // Handle error.
   }
-
+  
   console.log(rule.name);  // 'New name'
 });
 ~~~
@@ -476,7 +458,7 @@ Performing CRUD operations on the [Users](https://auth0.com/docs/api/v2#!/Users)
 This method takes an optional object as first argument that may be used to specify pagination settings and the search query.
 
 ~~~js
-// Pagination settings.
+// Pagination settings. 
 var params = {
   per_page: 10,
   page: 2
@@ -501,7 +483,7 @@ auth0.createUser(data, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // User created.
 });
 
@@ -511,7 +493,7 @@ auth0.users.create(data, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // User created.
 });
 ~~~
@@ -541,7 +523,7 @@ auth0.deleteAllUsers(function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Users deleted
 });
 
@@ -551,7 +533,7 @@ auth0.users.deleteAll(function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Users deleted
 });
 ~~~
@@ -564,7 +546,7 @@ auth0.deleteUser({ id: USER_ID }, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // User deleted.
 });
 
@@ -574,7 +556,7 @@ auth0.users.delete({ id: USER_ID }, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // User deleted.
 });
 ~~~
@@ -589,7 +571,7 @@ auth0.updateUser(params, data, function (err, user) {
   if (err) {
     // Handle error.
   }
-
+  
   // Updated user.
   console.log(user);
 });
@@ -600,7 +582,7 @@ auth0.users.update(params, data, function (err, user) {
   if (err) {
     // Handle error.
   }
-
+  
   // Updated user.
   console.log(user);
 });
@@ -624,7 +606,7 @@ auth0.updateUser(params, data, function (err, user) {
   if (err) {
     // Handle error.
   }
-
+  
   // Updated user.
   console.log(user);
 });
@@ -634,7 +616,7 @@ auth0.users.update(params, data, function (err, user) {
   if (err) {
     // Handle error.
   }
-
+  
   // Updated user.
   console.log(user);
 });
@@ -671,7 +653,7 @@ auth0.blacklistToken(token, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Token blacklisted.
 });
 
@@ -681,7 +663,7 @@ auth0.blacklistedTokens.add(token, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Token blacklisted.
 });
 ~~~
@@ -721,7 +703,7 @@ auth0.emailProvider.configure(data, function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Email provider configured.
 });
 ~~~
@@ -734,7 +716,7 @@ auth0.deleteEmailProvider(function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Email provider deleted.
 });
 
@@ -744,7 +726,7 @@ auth0.emailProvider.delete(function (err) {
   if (err) {
     // Handle error.
   }
-
+  
   // Email provider configured.
 });
 ~~~
@@ -757,7 +739,7 @@ auth0.updateEmailProvider(data, function (err, provider) {
   if (err) {
     // Handle error.
   }
-
+  
   // Updated email provider.
   console.log(provider);
 });
@@ -768,7 +750,7 @@ auth0.emailProvider.update(function (err, provider) {
   if (err) {
     // Handle error.
   }
-
+  
   // Updated email provider.
   console.log(provider);
 });
diff --git a/package.json b/package.json
index f4efae643..afe87f0a5 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "auth0",
-  "version": "2.0.0-beta.2",
+  "version": "2.0.0-alpha.5",
   "description": "node.js SDK for Auth0 API v2",
   "main": "index.js",
   "scripts": {
@@ -24,7 +24,8 @@
   "dependencies": {
     "bluebird": "^2.10.2",
     "rest-facade": "^1.2.3",
-    "url-join": "0.0.1"
+    "url-join": "0.0.1",
+    "superagent": "^1.1.0"
   },
   "devDependencies": {
     "chai": "^2.2.0",
@@ -32,7 +33,6 @@
     "istanbul": "^0.4.0",
     "mocha": "^2.2.4",
     "nock": "^3.1.1",
-    "sinon": "^1.17.1",
-    "superagent": "^1.1.0"
+    "sinon": "^1.17.1"
   }
 }
diff --git a/src/BlacklistedTokensManager.js b/src/BlacklistedTokensManager.js
index 58863ebfa..28bbcb591 100644
--- a/src/BlacklistedTokensManager.js
+++ b/src/BlacklistedTokensManager.js
@@ -23,6 +23,14 @@ var BlacklistedTokensManager = function (options) {
     throw new ArgumentError('Must provide client options');
   }
 
+  if (options.baseUrl === null || options.baseUrl === undefined) {
+    throw new ArgumentError('Must provide a base URL for the API');
+  }
+
+  if ('string' !== typeof options.baseUrl || options.baseUrl.length === 0) {
+    throw new ArgumentError('The provided base URL is invalid');
+  }
+
   /**
    * Options object for the Rest Client instace.
    *
@@ -39,7 +47,7 @@ var BlacklistedTokensManager = function (options) {
    *
    * @type {external:RestClient}
    */
-  this.resource = new RestClient(options.baseUrl + '/blacklists/tokens/', clientOptions);
+  this.resource = new RestClient(options.baseUrl + '/blacklists/tokens', clientOptions);
 };
 
 /**
diff --git a/src/utils.js b/src/utils.js
index a88a3c2fb..780824a21 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -1,25 +1,28 @@
-var ApiError = require('./exceptions').ApiError;
-
+/**
+ * @module utils
+ */
 var utils = module.exports = {};
 
+
+/**
+ * Given a JSON string, convert it to its base64 representation.
+ *
+ * @method
+ * @memberOf utils
+ */
 utils.jsonToBase64 = function (json) {
   return (new Buffer(JSON.stringify(json))).toString('base64');
 };
 
-utils.subEntity = function(Parent, name, Constructor){
-  var underlyingFieldName = '__' + name;
-  Object.defineProperty(Parent.prototype, name, {
-    enumerable: true,
-    get: function(){
-      if (!this[underlyingFieldName]){
-        this[underlyingFieldName] = new Constructor(this);
-      }
-
-      return this[underlyingFieldName];
-    }
-  });
-};
 
+/**
+ * Simple wrapper that, given a class, a property name and a method name,
+ * creates a new method in the class that is a wrapper for the given
+ * property method.
+ *
+ * @method
+ * @memberOf utils
+ */
 utils.wrapPropertyMethod = function (Parent, name, propertyMethod) {
   var path = propertyMethod.split('.');
   var property = path.shift();
@@ -34,28 +37,3 @@ utils.wrapPropertyMethod = function (Parent, name, propertyMethod) {
   });
 }
 
-utils.responseHandler = function(onError, onSuccess){
-  return function(err, resp){
-    if (err) {
-      var error = err;
-      if (err.response && err.response.body){
-        var body = err.response.body;
-        error = new ApiError(body.statusCode, body.error, body.message);
-      }
-
-      return onError(error);
-    }
-
-    onSuccess(resp);
-  };
-};
-
-utils.successCallback = function(cb, res){
-  return function(x){
-    if (cb){
-      return cb(null, x);
-    } else {
-      return res(x);
-    }
-  };
-};


Test patch:
diff --git a/test/blacklisted-tokens.tests.js b/test/blacklisted-tokens.tests.js
new file mode 100644
index 000000000..9770b3f37
--- /dev/null
+++ b/test/blacklisted-tokens.tests.js
@@ -0,0 +1,277 @@
+var expect = require('chai').expect;
+var nock = require('nock');
+
+var BlacklistedTokensManager = require('../src/BlacklistedTokensManager');
+var ArgumentError = require('../src/exceptions').ArgumentError;
+
+var API_URL = 'https://tenant.auth0.com';
+
+
+describe('BlacklistedTokensManager', function () {
+  before(function () {
+    this.token = 'TOKEN';
+    this.blacklistedTokens = new BlacklistedTokensManager({
+      headers: { authorization: 'Bearer ' + this.token },
+      baseUrl: API_URL
+    });
+  });
+
+
+  describe('instance', function () {
+    var methods = ['add', 'getAll'];
+
+    methods.forEach(function (method) {
+      it('should have a ' + method + ' method', function () {
+        expect(this.blacklistedTokens[method])
+          .to.exist
+          .to.be.an.instanceOf(Function);
+      })
+    });
+  });
+
+
+  describe('#constructor', function () {
+    it('should error when no options are provided', function () {
+      expect(BlacklistedTokensManager)
+        .to.throw(ArgumentError, 'Must provide client options');
+    });
+
+
+    it('should throw an error when no base URL is provided', function () {
+      var client = BlacklistedTokensManager.bind(null, {});
+
+      expect(client)
+        .to.throw(ArgumentError, 'Must provide a base URL for the API');
+    });
+
+
+    it('should throw an error when the base URL is invalid', function () {
+      var client = BlacklistedTokensManager.bind(null, { baseUrl: '' });
+
+      expect(client)
+        .to.throw(ArgumentError, 'The provided base URL is invalid');
+    });
+  });
+
+
+  describe('#getAll', function () {
+    beforeEach(function () {
+      this.request = nock(API_URL)
+        .get('/blacklists/tokens')
+        .reply(200);
+    })
+
+
+    it('should accept a callback', function (done) {
+      this
+        .blacklistedTokens
+        .getAll(function () {
+        done();
+      });
+    });
+
+
+    it('should return a promise if no callback is given', function (done) {
+      this
+        .blacklistedTokens
+        .getAll()
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+
+    it('should pass any errors to the promise catch handler', function (done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/blacklists/tokens')
+        .reply(500);
+
+      this
+        .blacklistedTokens
+        .getAll()
+        .catch(function (err) {
+          expect(err).to.exist;
+          done();
+        });
+    });
+
+
+    it('should pass the body of the response to the "then" handler', function (done) {
+      nock.cleanAll();
+
+      var data = [{ test: true }];
+      var request = nock(API_URL)
+        .get('/blacklists/tokens')
+        .reply(200, data);
+
+      this
+        .blacklistedTokens
+        .getAll()
+        .then(function (blacklistedTokens) {
+          expect(blacklistedTokens)
+            .to.be.an.instanceOf(Array);
+
+          expect(blacklistedTokens.length)
+            .to.equal(data.length);
+
+          expect(blacklistedTokens[0].test)
+            .to.equal(data[0].test);
+
+          done();
+        });
+    });
+
+
+    it('should perform a GET request to /api/v2/blacklists/tokens', function (done) {
+      var request = this.request;
+
+      this
+        .blacklistedTokens
+        .getAll()
+        .then(function () {
+          expect(request.isDone()).to.be.true;
+          done();
+        });
+    });
+
+
+    it('should include the token in the Authorization header', function (done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/blacklists/tokens')
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(200)
+
+      this
+        .blacklistedTokens
+        .getAll()
+        .then(function () {
+          expect(request.isDone()).to.be.true;
+          done();
+        });
+    });
+
+
+    it('should pass the parameters in the query-string', function (done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/blacklists/tokens')
+        .query({
+          include_fields: true,
+          fields: 'test'
+        })
+        .reply(200)
+
+      this
+        .blacklistedTokens
+        .getAll({ includeFields: true, fields: 'test' })
+        .then(function () {
+          expect(request.isDone()).to.be.true;
+          done();
+        });
+    });
+  });
+
+
+  describe('#add', function () {
+    var tokenData = {
+      aud: '',
+      jti: ''
+    };
+
+    beforeEach(function () {
+      this.request = nock(API_URL)
+        .post('/blacklists/tokens')
+        .reply(200);
+    })
+
+
+    it('should accept a callback', function (done) {
+      this
+        .blacklistedTokens
+        .add(tokenData, function () {
+          done();
+        });
+    });
+
+
+    it('should return a promise if no callback is given', function (done) {
+      this
+        .blacklistedTokens
+        .add(tokenData)
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+
+    it('should pass any errors to the promise catch handler', function (done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/blacklists/tokens')
+        .reply(500);
+
+      this
+        .blacklistedTokens
+        .add(tokenData)
+        .catch(function (err) {
+          expect(err).to.exist;
+          done();
+        });
+    });
+
+
+    it('should perform a POST request to /api/v2/blacklists/tokens', function (done) {
+      var request = this.request;
+
+      this
+        .blacklistedTokens
+        .add(tokenData)
+        .then(function () {
+          expect(request.isDone()).to.be.true;
+          done();
+        });
+    });
+
+
+    it('should pass the token data in the body of the request', function (done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/blacklists/tokens', tokenData)
+        .reply(200);
+
+      this
+        .blacklistedTokens
+        .add(tokenData)
+        .then(function () {
+          expect(request.isDone())
+            .to.be.true;
+
+          done();
+        });
+    });
+
+
+    it('should include the token in the Authorization header', function (done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/blacklists/tokens')
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(200)
+
+      this
+        .blacklistedTokens
+        .add(tokenData)
+        .then(function () {
+          expect(request.isDone()).to.be.true;
+          done();
+        });
+    });
+  });
+
+});
diff --git a/test/client.tests.js b/test/client.tests.js
index 9aed8c9d7..8c479bf80 100644
--- a/test/client.tests.js
+++ b/test/client.tests.js
@@ -4,17 +4,18 @@ var Promise = require('bluebird');
 var ClientsManager = require('../src/ClientsManager');
 
 var ArgumentError = require('../src/exceptions').ArgumentError;
-var BASE_API_URL = 'https://tenant.auth0.com';
+var API_URL = 'https://tenant.auth0.com';
 
 
 describe('ClientsManager', function () {
 
-  beforeEach(function () {
+  before(function () {
+    this.token = 'TOKEN';
     this.clients = new ClientsManager({
       headers: {
         authorization: 'Bearer ' + this.token
       },
-      baseUrl: BASE_API_URL
+      baseUrl: API_URL
     });
   });
 
@@ -24,6 +25,19 @@ describe('ClientsManager', function () {
   });
 
 
+  describe('instance', function () {
+    var methods = ['getAll', 'get', 'create', 'update', 'delete'];
+
+    methods.forEach(function (method) {
+      it('should have a ' + method + ' method', function () {
+        expect(this.clients[method])
+          .to.exist
+          .to.be.an.instanceOf(Function);
+      })
+    });
+  });
+
+
   describe('#constructor', function () {
     it('should error when no options are provided', function () {
       expect(ClientsManager)
@@ -51,18 +65,11 @@ describe('ClientsManager', function () {
   describe('#getAll', function () {
 
     beforeEach(function () {
-      this.request = nock(BASE_API_URL)
+      this.request = nock(API_URL)
         .get('/clients')
         .reply(200);
     })
 
-    it('should be defined', function () {
-      expect(this.clients.getAll)
-        .to.exist
-        .to.be.an.instanceOf(Function);
-    });
-
-
     it('should accept a callback', function (done) {
       this.clients.getAll(function () {
         done();
@@ -82,7 +89,7 @@ describe('ClientsManager', function () {
     it('should pass any errors to the promise catch handler', function (done) {
       nock.cleanAll();
 
-      var request = nock(BASE_API_URL)
+      var request = nock(API_URL)
         .get('/clients')
         .reply(500);
 
@@ -100,7 +107,7 @@ describe('ClientsManager', function () {
       nock.cleanAll();
 
       var data = [{ test: true }];
-      var request = nock(BASE_API_URL)
+      var request = nock(API_URL)
         .get('/clients')
         .reply(200, data);
 
@@ -137,7 +144,7 @@ describe('ClientsManager', function () {
     it('should include the token in the Authorization header', function (done) {
       nock.cleanAll();
 
-      var request = nock(BASE_API_URL)
+      var request = nock(API_URL)
         .get('/clients')
         .matchHeader('Authorization', 'Bearer ' + this.token)
         .reply(200)
@@ -154,7 +161,7 @@ describe('ClientsManager', function () {
     it('should pass the parameters in the query-string', function (done) {
       nock.cleanAll();
 
-      var request = nock(BASE_API_URL)
+      var request = nock(API_URL)
         .get('/clients')
         .query({
           include_fields: true,
@@ -177,18 +184,11 @@ describe('ClientsManager', function () {
     var data = { name: 'Test client' };
 
     beforeEach(function () {
-      this.request = nock(BASE_API_URL)
+      this.request = nock(API_URL)
         .post('/clients')
         .reply(201, data)
     })
 
-    it('should be defined', function () {
-      expect(this.clients.create)
-        .to.exist
-        .to.be.an.instanceOf(Function);
-    });
-
-
     it('should accept a callback', function (done) {
       this
         .clients
@@ -223,7 +223,7 @@ describe('ClientsManager', function () {
     it('should include the token in the Authorization header', function (done) {
       nock.cleanAll();
 
-      var request = nock(BASE_API_URL)
+      var request = nock(API_URL)
         .post('/clients')
         .matchHeader('Authorization', 'Bearer ' + this.token)
         .reply(201, data)
@@ -243,7 +243,7 @@ describe('ClientsManager', function () {
     it('should include the new client data in the request body', function (done) {
       nock.cleanAll();
 
-      var request = nock(BASE_API_URL)
+      var request = nock(API_URL)
         .post('/clients', data)
         .reply(201, data)
 
@@ -268,19 +268,12 @@ describe('ClientsManager', function () {
         email: 'john@doe.com'
       };
 
-      this.request = nock(BASE_API_URL)
+      this.request = nock(API_URL)
         .get('/clients/' + this.data.id)
         .reply(201, this.data);
     })
 
 
-    it('should be defined', function () {
-      expect(this.clients.get)
-        .to.exist
-        .to.be.an.instanceOf(Function);
-    });
-
-
     it('should accept a callback', function (done) {
       var params = { id: this.data.id };
 
@@ -319,17 +312,19 @@ describe('ClientsManager', function () {
     beforeEach(function () {
       this.data = { id: 5 };
 
-      this.request = nock(BASE_API_URL)
+      this.request = nock(API_URL)
         .patch('/clients/' + this.data.id)
         .reply(200, this.data);
     });
 
+
     it('should accept a callback', function (done) {
       this
         .clients
         .update({ client_id: 5 }, {}, done.bind(null, null));
     });
 
+
     it('should return a promise if no callback is given', function (done) {
       this
         .clients
@@ -338,6 +333,7 @@ describe('ClientsManager', function () {
         .catch(done.bind(null, null));
     });
 
+
     it('should perform a PATCH request to /api/v2/clients/5', function (done) {
       var request = this.request;
 
@@ -351,6 +347,66 @@ describe('ClientsManager', function () {
           done();
         });
     });
+
+
+    it('should include the new data in the body of the request', function (done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .patch('/clients/' + this.data.id, this.data)
+        .reply(200);
+
+      this
+        .clients
+        .update({ client_id: 5 }, this.data)
+        .then(function () {
+          expect(request.isDone())
+            .to.be.true;
+
+          done();
+        });
+    });
+  });
+
+
+  describe('#delete', function () {
+    var id = 5;
+
+    beforeEach(function () {
+      this.request = nock(API_URL)
+        .delete('/clients/' + id)
+        .reply(200);
+    });
+
+
+    it('should accept a callback', function (done) {
+      this
+        .clients
+        .delete({ client_id: id }, done.bind(null, null));
+    });
+
+
+    it('should return a promise when no callback is given', function (done) {
+      this
+        .clients
+        .delete({ client_id: id })
+        .then(done.bind(null, null));
+    });
+
+
+    it('should perform a DELETE request to /clients/' + id, function (done) {
+      var request = this.request;
+
+      this
+        .clients
+        .delete({ client_id: id })
+        .then(function () {
+          expect(request.isDone())
+            .to.be.true;
+
+          done();
+        });
+    });
   });
 });