diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | caldav.js | 147 | ||||
-rw-r--r-- | lib/caldav/errors.js | 40 | ||||
-rw-r--r-- | lib/caldav/index.js | 1 | ||||
-rw-r--r-- | lib/caldav/request/abstract.js | 30 | ||||
-rw-r--r-- | lib/caldav/request/calendar_home.js | 31 | ||||
-rw-r--r-- | lib/caldav/request/errors.js | 49 | ||||
-rw-r--r-- | test/caldav/errors_test.js | 27 | ||||
-rw-r--r-- | test/caldav/http/basic_auth_test.js | 31 | ||||
-rw-r--r-- | test/caldav/index_test.js | 1 | ||||
-rw-r--r-- | test/caldav/request/abstract_test.js | 22 | ||||
-rw-r--r-- | test/caldav/request/calendar_home_test.js | 28 | ||||
-rw-r--r-- | test/helper.js | 4 | ||||
-rw-r--r-- | test/support/fake_xhr.js | 3 |
14 files changed, 257 insertions, 159 deletions
@@ -17,6 +17,7 @@ package: test-agent-config echo '/* caldav.js - https://github.com/mozilla-b2g/caldav */' >> $(WEB_FILE) cat $(LIB_ROOT)/caldav.js >> $(WEB_FILE) cat $(LIB_ROOT)/responder.js >> $(WEB_FILE) + cat $(LIB_ROOT)/errors.js >> $(WEB_FILE) cat $(LIB_ROOT)/querystring.js >> $(WEB_FILE) cat $(LIB_ROOT)/sax.js >> $(WEB_FILE) cat $(LIB_ROOT)/template.js >> $(WEB_FILE) @@ -29,7 +30,6 @@ package: test-agent-config cat $(LIB_ROOT)/sax/base.js >> $(WEB_FILE) cat $(LIB_ROOT)/sax/calendar_data_handler.js >> $(WEB_FILE) cat $(LIB_ROOT)/sax/dav_response.js >> $(WEB_FILE) - cat $(LIB_ROOT)/request/errors.js >> $(WEB_FILE) cat $(LIB_ROOT)/request/abstract.js >> $(WEB_FILE) cat $(LIB_ROOT)/request/asset.js >> $(WEB_FILE) cat $(LIB_ROOT)/request/propfind.js >> $(WEB_FILE) @@ -1321,6 +1321,46 @@ function write (chunk) { [Caldav('responder'), Caldav] : [module, require('./caldav')] )); +(function(module, ns) { + + Errors = {}; + + /** + * Errors typically are for front-end routing purposes so the important + * part really is just the name and (maybe) the symbol... These are really + * intended to be consumed by name... So once a name has been assigned it + * should never be modified. + */ + [ + { symbol: 'Authentication', name: 'authentication' }, + { symbol: 'InvalidEntrypoint', name: 'invalid-entrypoint' }, + { symbol: 'ServerFailure', name: 'server-failure' }, + { symbol: 'Unknown', name: 'unknown' } + ].forEach(function createError(def) { + var obj = Errors[def.symbol] = function(message) { + this.message = message; + this.name = 'caldav-' + def.name; + + try { + throw new Error(); + } catch (e) { + this.stack = e.stack; + } + }; + + // just so instanceof Error works + obj.prototype = Object.create(Error.prototype); + }); + + module.exports = Errors; + +}.apply( + this, + (this.Caldav) ? + [Caldav('errors'), Caldav] : + [module, require('./caldav')] +)); + // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -3124,58 +3164,22 @@ function write (chunk) { )); (function(module, ns) { - function CaldavHttpError(code) { - this.code = code; - var message; - switch(this.code) { - case 401: - message = 'Wrong username or/and password'; - break; - case 404: - message = 'Url not found'; - break; - case 500: - message = 'Server error'; - break; - default: - message = this.code; - } - Error.call(this, message); - } - - CaldavHttpError.prototype = { - __proto__: Error.prototype, - constructor: CaldavHttpError - } - - // Unauthenticated error for - // Google Calendar - function UnauthenticatedError() { - var message = "Wrong username or/and password"; - Error.call(this, message); - } + var SAX = ns.require('sax'); + var XHR = ns.require('xhr'); + var Errors = ns.require('errors'); - UnauthenticatedError.prototype = { - __proto__: Error.prototype, - constructor: UnauthenticatedError - } + function determineHttpStatusError(status) { + var message = 'Cannot handle request due to server response'; + var err = 'Unknown'; - module.exports = { - CaldavHttpError: CaldavHttpError, - UnauthenticatedError: UnauthenticatedError - }; + if (status === 500) + err = 'ServerFailure'; -}.apply( - this, - (this.Caldav) ? - [Caldav('request/errors'), Caldav] : - [module, require('../caldav')] -)); -(function(module, ns) { + if (status === 401) + err = 'Authentication'; - var SAX = ns.require('sax'); - var XHR = ns.require('xhr'); - var Errors = ns.require('request/errors'); + return new Errors[err](message); + } /** * Creates an (Web/Cal)Dav request. @@ -3245,14 +3249,19 @@ function write (chunk) { return callback(err); } + // handle the success case if (xhr.status > 199 && xhr.status < 300) { - // success self.sax.close(); - self._processResult(req, callback); - } else { - // fail - callback(new Errors.CaldavHttpError(xhr.status)); + return self._processResult(req, callback); } + + // probable error cases + callback( + determineHttpStatusError(xhr.status), + xhr + ); + + }); return req; @@ -3587,8 +3596,8 @@ function write (chunk) { )); (function(module, ns) { - var Errors = ns.require('request/errors'); - + var RequestErrors = ns.require('errors'); + /** * Creates a propfind request. * @@ -3659,19 +3668,32 @@ function write (chunk) { return; } - principal = findProperty('current-user-principal', data, true); + // some fairly dumb allowances + principal = + findProperty('current-user-principal', data, true) || + findProperty('principal-URL', data, true); if (!principal) { - principal = findProperty('principal-URL', data, true); + return callback(new Errors.InvalidEntrypoint( + 'both current-user-principal and principal-URL are missing' + )); } + // per http://tools.ietf.org/html/rfc6638 we get unauthenticated if ('unauthenticated' in principal) { - callback(new Errors.UnauthenticatedError()); - } else if (principal.href) { - callback(null, principal.href); - } else { - callback(new Errors.CaldavHttpError(404)); + return callback( + new Errors.Authentication('caldav response is unauthenticated') + ); + } + + // we might have both principal.href & unauthenticated + if (principal.href) { + return callback(null, principal.href); } + + callback( + new Errors.InvalidEntrypoint('no useful location information found') + ); }); }, @@ -4036,6 +4058,7 @@ function write (chunk) { exports.Resources = ns.require('resources'); exports.Http = ns.require('http'); exports.OAuth2 = ns.require('oauth2'); + exports.Errors = ns.require('errors'); }.apply( this, diff --git a/lib/caldav/errors.js b/lib/caldav/errors.js new file mode 100644 index 0000000..1b58a6f --- /dev/null +++ b/lib/caldav/errors.js @@ -0,0 +1,40 @@ +(function(module, ns) { + + Errors = {}; + + /** + * Errors typically are for front-end routing purposes so the important + * part really is just the name and (maybe) the symbol... These are really + * intended to be consumed by name... So once a name has been assigned it + * should never be modified. + */ + [ + { symbol: 'Authentication', name: 'authentication' }, + { symbol: 'InvalidEntrypoint', name: 'invalid-entrypoint' }, + { symbol: 'ServerFailure', name: 'server-failure' }, + { symbol: 'Unknown', name: 'unknown' } + ].forEach(function createError(def) { + var obj = Errors[def.symbol] = function(message) { + this.message = message; + this.name = 'caldav-' + def.name; + + try { + throw new Error(); + } catch (e) { + this.stack = e.stack; + } + }; + + // just so instanceof Error works + obj.prototype = Object.create(Error.prototype); + }); + + module.exports = Errors; + +}.apply( + this, + (this.Caldav) ? + [Caldav('errors'), Caldav] : + [module, require('./caldav')] +)); + diff --git a/lib/caldav/index.js b/lib/caldav/index.js index 636fc18..3e3dd51 100644 --- a/lib/caldav/index.js +++ b/lib/caldav/index.js @@ -12,6 +12,7 @@ exports.Resources = ns.require('resources'); exports.Http = ns.require('http'); exports.OAuth2 = ns.require('oauth2'); + exports.Errors = ns.require('errors'); }.apply( this, diff --git a/lib/caldav/request/abstract.js b/lib/caldav/request/abstract.js index 4db3883..6f33af4 100644 --- a/lib/caldav/request/abstract.js +++ b/lib/caldav/request/abstract.js @@ -2,7 +2,20 @@ var SAX = ns.require('sax'); var XHR = ns.require('xhr'); - var Errors = ns.require('request/errors'); + var Errors = ns.require('errors'); + + function determineHttpStatusError(status) { + var message = 'Cannot handle request due to server response'; + var err = 'Unknown'; + + if (status === 500) + err = 'ServerFailure'; + + if (status === 401) + err = 'Authentication'; + + return new Errors[err](message); + } /** * Creates an (Web/Cal)Dav request. @@ -72,14 +85,19 @@ return callback(err); } + // handle the success case if (xhr.status > 199 && xhr.status < 300) { - // success self.sax.close(); - self._processResult(req, callback); - } else { - // fail - callback(new Errors.CaldavHttpError(xhr.status)); + return self._processResult(req, callback); } + + // probable error cases + callback( + determineHttpStatusError(xhr.status), + xhr + ); + + }); return req; diff --git a/lib/caldav/request/calendar_home.js b/lib/caldav/request/calendar_home.js index d0659bb..e7bed35 100644 --- a/lib/caldav/request/calendar_home.js +++ b/lib/caldav/request/calendar_home.js @@ -1,7 +1,7 @@ (function(module, ns) { - var Errors = ns.require('request/errors'); - + var RequestErrors = ns.require('errors'); + /** * Creates a propfind request. * @@ -72,19 +72,32 @@ return; } - principal = findProperty('current-user-principal', data, true); + // some fairly dumb allowances + principal = + findProperty('current-user-principal', data, true) || + findProperty('principal-URL', data, true); if (!principal) { - principal = findProperty('principal-URL', data, true); + return callback(new Errors.InvalidEntrypoint( + 'both current-user-principal and principal-URL are missing' + )); } + // per http://tools.ietf.org/html/rfc6638 we get unauthenticated if ('unauthenticated' in principal) { - callback(new Errors.UnauthenticatedError()); - } else if (principal.href) { - callback(null, principal.href); - } else { - callback(new Errors.CaldavHttpError(404)); + return callback( + new Errors.Authentication('caldav response is unauthenticated') + ); + } + + // we might have both principal.href & unauthenticated + if (principal.href) { + return callback(null, principal.href); } + + callback( + new Errors.InvalidEntrypoint('no useful location information found') + ); }); }, diff --git a/lib/caldav/request/errors.js b/lib/caldav/request/errors.js deleted file mode 100644 index 700cfba..0000000 --- a/lib/caldav/request/errors.js +++ /dev/null @@ -1,49 +0,0 @@ -(function(module, ns) { - - function CaldavHttpError(code) { - this.code = code; - var message; - switch(this.code) { - case 401: - message = 'Wrong username or/and password'; - break; - case 404: - message = 'Url not found'; - break; - case 500: - message = 'Server error'; - break; - default: - message = this.code; - } - Error.call(this, message); - } - - CaldavHttpError.prototype = { - __proto__: Error.prototype, - constructor: CaldavHttpError - } - - // Unauthenticated error for - // Google Calendar - function UnauthenticatedError() { - var message = "Wrong username or/and password"; - Error.call(this, message); - } - - UnauthenticatedError.prototype = { - __proto__: Error.prototype, - constructor: UnauthenticatedError - } - - module.exports = { - CaldavHttpError: CaldavHttpError, - UnauthenticatedError: UnauthenticatedError - }; - -}.apply( - this, - (this.Caldav) ? - [Caldav('request/errors'), Caldav] : - [module, require('../caldav')] -)); diff --git a/test/caldav/errors_test.js b/test/caldav/errors_test.js new file mode 100644 index 0000000..e02ea84 --- /dev/null +++ b/test/caldav/errors_test.js @@ -0,0 +1,27 @@ +testSupport.lib('errors'); + +suite('errors/authentication', function() { + var Errors; + + suiteSetup(function() { + Errors = Caldav.require('errors'); + }); + + function verifyErrorExists(symbol) { + test(symbol, function() { + var error = new Errors[symbol]('oops'); + assert.equal(error.message, 'oops'); + assert.ok(error.name, 'has name'); + assert.ok(error.stack, 'has stack'); + }); + } + + // why grouped? gjslint hates us otherwise + ([ + 'Authentication', + 'InvalidEntrypoint', + 'ServerFailure', + 'Unknown' + ]).forEach(verifyErrorExists); + +}); diff --git a/test/caldav/http/basic_auth_test.js b/test/caldav/http/basic_auth_test.js index 566de08..a264d1b 100644 --- a/test/caldav/http/basic_auth_test.js +++ b/test/caldav/http/basic_auth_test.js @@ -39,19 +39,24 @@ suite('http/basic_auth', function() { assert.equal(subject.url, url); }); - test('#send', function() { - var xhr = subject.send(); - - assert.deepEqual( - xhr.openArgs, - [ - 'GET', - url, - subject.async, - connection.user, - connection.password - ] - ); + suite('#send', function() { + test('success', function() { + var xhr = subject.send(); + + assert.deepEqual( + xhr.openArgs, + [ + 'GET', + url, + subject.async, + connection.user, + connection.password + ] + ); + }); + + + }); }); diff --git a/test/caldav/index_test.js b/test/caldav/index_test.js index baad822..d8f25b9 100644 --- a/test/caldav/index_test.js +++ b/test/caldav/index_test.js @@ -33,6 +33,7 @@ suite('caldav', function() { assert.ok(root.Resources.Calendar, 'Calendar.Resources.Calendar'); assert.ok(root.OAuth2, 'OAuth2'); assert.ok(root.Http, 'Http'); + assert.ok(root.Errors, 'Errors'); }); }); diff --git a/test/caldav/request/abstract_test.js b/test/caldav/request/abstract_test.js index e2fb3d1..18d6bc3 100644 --- a/test/caldav/request/abstract_test.js +++ b/test/caldav/request/abstract_test.js @@ -77,22 +77,26 @@ suite('caldav/request/abstract.js', function() { assert.deepEqual(con.password, req.password); }); - suite('error', function() { + suite('errors', function() { var calledWith; - setup(function(done) { + setup(function() { subject.send(function() { calledWith = arguments; - done(); }); - - xhr = getXhr(); - xhr.respond('NOT XML <div>', 500); }); - test('on response', function() { - assert.equal(calledWith[0].code, 500); - }); + function verifyStatusFailure(status, name) { + test('status ' + status, function() { + xhr = getXhr(); + xhr.respond('', status); + assert.equal(calledWith[0].name, name); + }); + } + + verifyStatusFailure(403, 'caldav-unknown'); + verifyStatusFailure(401, 'caldav-authentication'); + verifyStatusFailure(500, 'caldav-server-failure'); }); suite('success', function() { diff --git a/test/caldav/request/calendar_home_test.js b/test/caldav/request/calendar_home_test.js index 13483ad..6ed65fb 100644 --- a/test/caldav/request/calendar_home_test.js +++ b/test/caldav/request/calendar_home_test.js @@ -20,7 +20,7 @@ suite('caldav/request/propfind', function() { Connection = Caldav.require('connection'); Home = Caldav.require('request/calendar_home'); MockRequest = Caldav.require('support/mock_request'); - Errors = Caldav.require('request/errors'); + Errors = Caldav.require('errors'); }); suiteSetup(function() { @@ -76,7 +76,7 @@ suite('caldav/request/propfind', function() { response[url] = { 'current-user-principal': { status: '200', - value: { href:'foo.com/' } + value: { href: 'foo.com/' } } }; @@ -104,7 +104,7 @@ suite('caldav/request/propfind', function() { assert.equal(data, 'bar.com/'); }); - + test('unauthenticated', function() { var req = request('_findPrincipal'); @@ -116,12 +116,28 @@ suite('caldav/request/propfind', function() { } } }; - req.respond(null, response); - assert.equal(true, err instanceof Errors.UnauthenticatedError); + assert.instanceOf(err, Errors.Authentication); + }); + + test('without href', function() { + var req = request('_findPrincipal'); + response[url] = { + 'principal-URL': {} + }; + + req.respond(null, response); + assert.instanceOf(err, Errors.InvalidEntrypoint); + }); + + test('without useful response', function() { + var req = request('_findPrincipal'); + response[url] = {}; + req.respond(null, response); + + assert.instanceOf(err, Errors.InvalidEntrypoint); }); - }); suite('#_findCalendarHome', function() { diff --git a/test/helper.js b/test/helper.js index 5cf8d5f..92585f5 100644 --- a/test/helper.js +++ b/test/helper.js @@ -204,7 +204,10 @@ /* since we have global mocks easier to just include these globally */ requireRequest = function(callback) { + testSupport.helper('fake_xhr'); testSupport.lib('responder'); + testSupport.lib('errors'); + testSupport.lib('xhr'); testSupport.lib('oauth2'); testSupport.lib('http/basic_auth'); testSupport.lib('http/oauth2'); @@ -212,7 +215,6 @@ testSupport.lib('sax'); testSupport.lib('sax/base'); testSupport.lib('sax/dav_response'); - testSupport.lib('request/errors'); testSupport.lib('request/abstract'); testSupport.lib('template'); testSupport.lib('request/propfind'); diff --git a/test/support/fake_xhr.js b/test/support/fake_xhr.js index 3f6cea0..59c6598 100644 --- a/test/support/fake_xhr.js +++ b/test/support/fake_xhr.js @@ -1,6 +1,4 @@ (function(module) { - console.log('I HAZ LOADED'); - function FakeXhr() { this.openArgs = null; this.sendArgs = null; @@ -44,7 +42,6 @@ } }; - console.log('EXPORTS ME', FakeXhr); module.exports = FakeXhr; }.apply( |