mirror of
https://github.com/moparisthebest/mail
synced 2024-11-26 02:42:17 -05:00
[WO-85] introduce silent public key verification
This commit is contained in:
parent
6e144a23e1
commit
14919847e3
@ -41,7 +41,17 @@ define([], function() {
|
||||
cryptPrefix: '-----BEGIN PGP MESSAGE-----',
|
||||
cryptSuffix: '-----END PGP MESSAGE-----',
|
||||
signature: 'Sent securely from whiteout mail',
|
||||
webSite: 'http://whiteout.io'
|
||||
webSite: 'http://whiteout.io',
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Contants are maintained here.
|
||||
*/
|
||||
app.constants = {
|
||||
verificationSubject: 'New public key uploaded',
|
||||
verificationUrlPrefix: 'https://keys.whiteout.io/verify/',
|
||||
verificationUuidLength: 36
|
||||
};
|
||||
|
||||
return app;
|
||||
|
@ -3,7 +3,8 @@ define(function(require) {
|
||||
|
||||
var _ = require('underscore'),
|
||||
util = require('cryptoLib/util'),
|
||||
str = require('js/app-config').string;
|
||||
str = require('js/app-config').string,
|
||||
consts = require('js/app-config').constants;
|
||||
|
||||
/**
|
||||
* A high-level Data-Access Api for handling Email synchronization
|
||||
@ -208,7 +209,7 @@ define(function(require) {
|
||||
*/
|
||||
EmailDAO.prototype.listMessages = function(options, callback) {
|
||||
var self = this,
|
||||
encryptedList = [];
|
||||
cleartextList = [];
|
||||
|
||||
// validate options
|
||||
if (!options.folder) {
|
||||
@ -227,63 +228,123 @@ define(function(require) {
|
||||
return;
|
||||
}
|
||||
|
||||
// find encrypted items
|
||||
emails.forEach(function(i) {
|
||||
if (typeof i.body === 'string' && i.body.indexOf(str.cryptPrefix) !== -1 && i.body.indexOf(str.cryptSuffix) !== -1) {
|
||||
// parse ct object from ascii armored message block
|
||||
encryptedList.push(parseMessageBlock(i));
|
||||
}
|
||||
});
|
||||
|
||||
if (encryptedList.length === 0) {
|
||||
callback(null, []);
|
||||
if (emails.length === 0) {
|
||||
callback(null, cleartextList);
|
||||
return;
|
||||
}
|
||||
|
||||
// decrypt items
|
||||
decryptList(encryptedList, callback);
|
||||
var after = _.after(emails.length, function() {
|
||||
callback(null, cleartextList);
|
||||
});
|
||||
|
||||
_.each(emails, function(email) {
|
||||
handleMail(email, after);
|
||||
});
|
||||
});
|
||||
|
||||
function handleMail(email, localCallback) {
|
||||
email.subject = email.subject.split(str.subjectPrefix)[1];
|
||||
|
||||
// encrypted mail
|
||||
if (isPGPMail(email)) {
|
||||
email = parseMessageBlock(email);
|
||||
decrypt(email, localCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
// verification mail
|
||||
if (isVerificationMail(email)) {
|
||||
verify(email, localCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
// cleartext mail
|
||||
cleartextList.push(email);
|
||||
localCallback();
|
||||
}
|
||||
|
||||
function isPGPMail(email) {
|
||||
return typeof email.body === 'string' && email.body.indexOf(str.cryptPrefix) !== -1 && email.body.indexOf(str.cryptSuffix) !== -1;
|
||||
}
|
||||
|
||||
function isVerificationMail(email) {
|
||||
return email.subject === consts.verificationSubject;
|
||||
}
|
||||
|
||||
function parseMessageBlock(email) {
|
||||
var messageBlock;
|
||||
|
||||
// parse email body for encrypted message block
|
||||
try {
|
||||
// get ascii armored message block by prefix and suffix
|
||||
messageBlock = email.body.split(str.cryptPrefix)[1].split(str.cryptSuffix)[0];
|
||||
// add prefix and suffix again
|
||||
email.body = str.cryptPrefix + messageBlock + str.cryptSuffix;
|
||||
email.subject = email.subject.split(str.subjectPrefix)[1];
|
||||
} catch (e) {
|
||||
callback({
|
||||
errMsg: 'Error parsing encrypted message block!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
return email;
|
||||
}
|
||||
|
||||
function decryptList(list, callback) {
|
||||
var after = _.after(list.length, function() {
|
||||
callback(null, list);
|
||||
});
|
||||
|
||||
list.forEach(function(i) {
|
||||
// gather public keys required to verify signatures
|
||||
var sender = i.from[0].address;
|
||||
function decrypt(email, localCallback) {
|
||||
// fetch public key required to verify signatures
|
||||
var sender = email.from[0].address;
|
||||
self._keychain.getReceiverPublicKey(sender, function(err, senderPubkey) {
|
||||
|
||||
// decrypt and verfiy signatures
|
||||
self._crypto.decrypt(i.body, senderPubkey.publicKey, function(err, decrypted) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
i.body = decrypted;
|
||||
// decrypt and verfiy signatures
|
||||
self._crypto.decrypt(email.body, senderPubkey.publicKey, function(err, decrypted) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
after();
|
||||
email.body = decrypted;
|
||||
cleartextList.push(email);
|
||||
localCallback();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function verify(email, localCallback) {
|
||||
var uuid, index;
|
||||
|
||||
if (!email.unread) {
|
||||
// don't bother if the email was already marked as read
|
||||
localCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
index = email.body.indexOf(consts.verificationUrlPrefix);
|
||||
if (index === -1) {
|
||||
localCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
uuid = email.body.substr(index + consts.verificationUrlPrefix.length, consts.verificationUuidLength);
|
||||
self._keychain.verifyPublicKey(uuid, function(err) {
|
||||
if (err) {
|
||||
console.error('Unable to verify public key: ' + err.errMsg);
|
||||
localCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
// public key has been verified, mark the message as read, delete it, and ignore it in the future
|
||||
self.imapMarkMessageRead({
|
||||
folder: options.folder,
|
||||
uid: email.uid
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
// if marking the mail as read failed, don't bother
|
||||
localCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
self.imapDeleteMessage({
|
||||
folder: options.folder,
|
||||
uid: email.uid
|
||||
}, function() {
|
||||
localCallback();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -363,11 +424,6 @@ define(function(require) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof message.body !== 'string' || message.body.indexOf(str.cryptPrefix) === -1 || message.body.indexOf(str.cryptSuffix) === -1) {
|
||||
after();
|
||||
return;
|
||||
}
|
||||
|
||||
// set gotten attributes like body to message object containing list meta data like 'unread' or 'replied'
|
||||
header.id = message.id;
|
||||
header.body = message.body;
|
||||
|
@ -12,6 +12,15 @@ define(function(require) {
|
||||
this._publicKeyDao = publicKeyDao;
|
||||
};
|
||||
|
||||
/**
|
||||
* Verifies the public key of a user o nthe public key store
|
||||
* @param {String} uuid The uuid to verify the key
|
||||
* @param {Function} callback(error) Callback with an optional error object when the verification is done. If the was an error, the error object contains the information for it.
|
||||
*/
|
||||
KeychainDAO.prototype.verifyPublicKey = function(uuid, callback) {
|
||||
this._publicKeyDao.verify(uuid, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an array of public keys by looking in local storage and
|
||||
* fetching missing keys from the cloud service.
|
||||
|
@ -5,13 +5,34 @@ define(function() {
|
||||
this._restDao = restDao;
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify the public key behind the given uuid
|
||||
*/
|
||||
PublicKeyDAO.prototype.verify = function(uuid, callback) {
|
||||
var uri = '/verify/' + uuid;
|
||||
|
||||
this._restDao.get({
|
||||
uri: uri,
|
||||
type: 'text'
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the user's corresponding public key
|
||||
*/
|
||||
PublicKeyDAO.prototype.get = function(keyId, callback) {
|
||||
var uri = '/publickey/key/' + keyId;
|
||||
|
||||
this._restDao.get(uri, function(err, key) {
|
||||
this._restDao.get({
|
||||
uri: uri
|
||||
}, function(err, key) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
@ -34,7 +55,9 @@ define(function() {
|
||||
PublicKeyDAO.prototype.getByUserId = function(userId, callback) {
|
||||
var uri = '/publickey/user/' + userId;
|
||||
|
||||
this._restDao.get(uri, function(err, keys) {
|
||||
this._restDao.get({
|
||||
uri: uri
|
||||
}, function(err, keys) {
|
||||
// not found
|
||||
if (err && err.code === 404) {
|
||||
callback();
|
||||
|
@ -14,14 +14,42 @@ define(function(require) {
|
||||
|
||||
/**
|
||||
* GET (read) request
|
||||
* @param {String} options.uri URI relative to the base uri to perform the GET request with.
|
||||
* @param {String} options.type (optional) The type of data that you're expecting back from the server: json, xml, text. Default: json.
|
||||
*/
|
||||
RestDAO.prototype.get = function(uri, callback) {
|
||||
RestDAO.prototype.get = function(options, callback) {
|
||||
var acceptHeader;
|
||||
|
||||
if (typeof options.uri === 'undefined') {
|
||||
callback({
|
||||
code: 400,
|
||||
errMsg: 'Bad Request! URI is a mandatory parameter.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
options.type = options.type || 'json';
|
||||
|
||||
if (options.type === 'json') {
|
||||
acceptHeader = 'application/json';
|
||||
} else if (options.type === 'xml') {
|
||||
acceptHeader = 'application/xml';
|
||||
} else if (options.type === 'text') {
|
||||
acceptHeader = 'text/plain';
|
||||
} else {
|
||||
callback({
|
||||
code: 400,
|
||||
errMsg: 'Bad Request! Unhandled data type.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: this._baseUri + uri,
|
||||
url: this._baseUri + options.uri,
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
dataType: options.type,
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Accept': acceptHeader
|
||||
},
|
||||
success: function(res) {
|
||||
callback(null, res);
|
||||
|
@ -16,7 +16,7 @@ define(function(require) {
|
||||
asymKeySize: 512
|
||||
};
|
||||
|
||||
var dummyMail;
|
||||
var dummyMail, verificationMail, plaintextMail;
|
||||
|
||||
var publicKey = "-----BEGIN PUBLIC KEY-----\r\n" + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxy+Te5dyeWd7g0P+8LNO7fZDQ\r\n" + "g96xTb1J6pYE/pPTMlqhB6BRItIYjZ1US5q2vk5Zk/5KasBHAc9RbCqvh9v4XFEY\r\n" + "JVmTXC4p8ft1LYuNWIaDk+R3dyYXmRNct/JC4tks2+8fD3aOvpt0WNn3R75/FGBt\r\n" + "h4BgojAXDE+PRQtcVQIDAQAB\r\n" + "-----END PUBLIC KEY-----";
|
||||
|
||||
@ -37,6 +37,28 @@ define(function(require) {
|
||||
}], // list of receivers
|
||||
subject: "[whiteout] Hello", // Subject line
|
||||
body: "Hello world" // plaintext body
|
||||
}, verificationMail = {
|
||||
from: [{
|
||||
name: 'Whiteout Test',
|
||||
address: 'whiteout.test@t-online.de'
|
||||
}], // sender address
|
||||
to: [{
|
||||
address: 'safewithme.testuser@gmail.com'
|
||||
}], // list of receivers
|
||||
subject: "[whiteout] New public key uploaded", // Subject line
|
||||
body: "https://keys.whiteout.io/verify/OMFG_FUCKING_BASTARD_UUID_FROM_HELL!", // plaintext body
|
||||
unread: true
|
||||
}, plaintextMail = {
|
||||
from: [{
|
||||
name: 'Whiteout Test',
|
||||
address: 'whiteout.test@t-online.de'
|
||||
}], // sender address
|
||||
to: [{
|
||||
address: 'safewithme.testuser@gmail.com'
|
||||
}], // list of receivers
|
||||
subject: "OMG SO PLAIN TEXT", // Subject line
|
||||
body: "yo dawg, we be all plaintext and stuff...", // plaintext body
|
||||
unread: true
|
||||
};
|
||||
|
||||
account = {
|
||||
@ -578,7 +600,7 @@ define(function(require) {
|
||||
describe('IMAP: list messages from local storage', function() {
|
||||
it('should work', function(done) {
|
||||
dummyMail.body = app.string.cryptPrefix + btoa('asdf') + app.string.cryptSuffix;
|
||||
devicestorageStub.listItems.yields(null, [dummyMail, dummyMail]);
|
||||
devicestorageStub.listItems.yields(null, [dummyMail]);
|
||||
keychainStub.getReceiverPublicKey.yields(null, {
|
||||
_id: "fcf8b4aa-5d09-4089-8b4f-e3bc5091daf3",
|
||||
userId: "safewithme.testuser@gmail.com",
|
||||
@ -589,13 +611,124 @@ define(function(require) {
|
||||
emailDao.listMessages({
|
||||
folder: 'INBOX',
|
||||
offset: 0,
|
||||
num: 2
|
||||
num: 1
|
||||
}, function(err, emails) {
|
||||
expect(err).to.not.exist;
|
||||
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
||||
expect(keychainStub.getReceiverPublicKey.calledTwice).to.be.true;
|
||||
expect(pgpStub.decrypt.calledTwice).to.be.true;
|
||||
expect(emails.length).to.equal(2);
|
||||
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
||||
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
||||
expect(emails.length).to.equal(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Plain text', function() {
|
||||
it('should display plaintext mails with [whiteout] prefix', function(done) {
|
||||
devicestorageStub.listItems.yields(null, [plaintextMail]);
|
||||
|
||||
emailDao.listMessages({
|
||||
folder: 'INBOX',
|
||||
offset: 0,
|
||||
num: 1
|
||||
}, function(err, emails) {
|
||||
expect(err).to.not.exist;
|
||||
expect(emails.length).to.equal(1);
|
||||
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Verification', function() {
|
||||
it('should verify pending public keys', function(done) {
|
||||
devicestorageStub.listItems.yields(null, [verificationMail]);
|
||||
keychainStub.verifyPublicKey.yields();
|
||||
imapClientStub.updateFlags.yields();
|
||||
imapClientStub.deleteMessage.yields();
|
||||
devicestorageStub.removeList.yields();
|
||||
|
||||
emailDao.listMessages({
|
||||
folder: 'INBOX',
|
||||
offset: 0,
|
||||
num: 1
|
||||
}, function(err, emails) {
|
||||
expect(err).to.not.exist;
|
||||
expect(emails.length).to.equal(0);
|
||||
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
||||
expect(keychainStub.verifyPublicKey.calledOnce).to.be.true;
|
||||
expect(imapClientStub.updateFlags.calledOnce).to.be.true;
|
||||
expect(imapClientStub.deleteMessage.calledOnce).to.be.true;
|
||||
expect(devicestorageStub.removeList.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not verify pending public keys if the mail was read', function(done) {
|
||||
verificationMail.unread = false;
|
||||
devicestorageStub.listItems.yields(null, [verificationMail]);
|
||||
|
||||
emailDao.listMessages({
|
||||
folder: 'INBOX',
|
||||
offset: 0,
|
||||
num: 1
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
||||
verificationMail.unread = true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not verify pending public keys if the mail contains erroneous links', function(done) {
|
||||
var properBody = verificationMail.body;
|
||||
verificationMail.body = 'UGA UGA!';
|
||||
devicestorageStub.listItems.yields(null, [verificationMail]);
|
||||
|
||||
emailDao.listMessages({
|
||||
folder: 'INBOX',
|
||||
offset: 0,
|
||||
num: 1
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
||||
verificationMail.body = properBody;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should not mark verification mails read if verification fails', function(done) {
|
||||
devicestorageStub.listItems.yields(null, [verificationMail]);
|
||||
keychainStub.verifyPublicKey.yields({
|
||||
errMsg: 'snafu.'
|
||||
});
|
||||
|
||||
emailDao.listMessages({
|
||||
folder: 'INBOX',
|
||||
offset: 0,
|
||||
num: 1
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
||||
expect(keychainStub.verifyPublicKey.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should not delete verification mails read if marking read fails', function(done) {
|
||||
devicestorageStub.listItems.yields(null, [verificationMail]);
|
||||
keychainStub.verifyPublicKey.yields();
|
||||
imapClientStub.updateFlags.yields({
|
||||
errMsg: 'snafu.'
|
||||
});
|
||||
|
||||
emailDao.listMessages({
|
||||
folder: 'INBOX',
|
||||
offset: 0,
|
||||
num: 1
|
||||
}, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
||||
expect(keychainStub.verifyPublicKey.calledOnce).to.be.true;
|
||||
expect(imapClientStub.updateFlags.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -20,6 +20,18 @@ define(function(require) {
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
describe('verify public key', function() {
|
||||
it('should verify public key', function(done) {
|
||||
var uuid = 'asdfasdfasdfasdf';
|
||||
pubkeyDaoStub.verify.yields();
|
||||
|
||||
keychainDao.verifyPublicKey(uuid, function() {
|
||||
expect(pubkeyDaoStub.verify.calledWith(uuid)).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('lookup public key', function() {
|
||||
it('should fail', function(done) {
|
||||
keychainDao.lookupPublicKey(undefined, function(err, key) {
|
||||
|
@ -45,6 +45,30 @@ define(function(require) {
|
||||
});
|
||||
});
|
||||
|
||||
describe('verify', function() {
|
||||
it('should fail', function(done) {
|
||||
restDaoStub.get.yields(42);
|
||||
|
||||
pubkeyDao.get('id', function(err) {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
var uuid = 'c621e328-8548-40a1-8309-adf1955e98a9';
|
||||
restDaoStub.get.yields(null);
|
||||
|
||||
pubkeyDao.verify(uuid, function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(restDaoStub.get.calledWith(sinon.match(function(arg){
|
||||
return arg.uri === '/verify/' + uuid && arg.type === 'text';
|
||||
}))).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('get by userId', function() {
|
||||
it('should fail', function(done) {
|
||||
restDaoStub.get.yields(42);
|
||||
|
@ -39,7 +39,100 @@ define(function(require) {
|
||||
});
|
||||
|
||||
describe('get', function() {
|
||||
it('should fail', function(done) {
|
||||
it('should work with json as default type', function(done) {
|
||||
$.ajax.restore();
|
||||
var spy = sinon.stub($, 'ajax').yieldsTo('success', {
|
||||
foo: 'bar'
|
||||
});
|
||||
|
||||
restDao.get({
|
||||
uri: '/asdf',
|
||||
type: 'json'
|
||||
}, function(err, data) {
|
||||
expect(err).to.not.exist;
|
||||
expect(data.foo).to.equal('bar');
|
||||
expect(spy.calledWith(sinon.match(function(request) {
|
||||
return request.headers.Accept === 'application/json' && request.dataType === 'json';
|
||||
}))).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with json', function(done) {
|
||||
$.ajax.restore();
|
||||
var spy = sinon.stub($, 'ajax').yieldsTo('success', {
|
||||
foo: 'bar'
|
||||
});
|
||||
|
||||
restDao.get({
|
||||
uri: '/asdf',
|
||||
type: 'json'
|
||||
}, function(err, data) {
|
||||
expect(err).to.not.exist;
|
||||
expect(data.foo).to.equal('bar');
|
||||
expect(spy.calledWith(sinon.match(function(request) {
|
||||
return request.headers.Accept === 'application/json' && request.dataType === 'json';
|
||||
}))).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with plain text', function(done) {
|
||||
$.ajax.restore();
|
||||
var spy = sinon.stub($, 'ajax').yieldsTo('success', 'foobar!');
|
||||
|
||||
restDao.get({
|
||||
uri: '/asdf',
|
||||
type: 'text'
|
||||
}, function(err, data) {
|
||||
expect(err).to.not.exist;
|
||||
expect(data).to.equal('foobar!');
|
||||
expect(spy.calledWith(sinon.match(function(request) {
|
||||
return request.headers.Accept === 'text/plain' && request.dataType === 'text';
|
||||
}))).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with xml', function(done) {
|
||||
$.ajax.restore();
|
||||
var spy = sinon.stub($, 'ajax').yieldsTo('success', '<foo>bar</foo>');
|
||||
|
||||
restDao.get({
|
||||
uri: '/asdf',
|
||||
type: 'xml'
|
||||
}, function(err, data) {
|
||||
expect(err).to.not.exist;
|
||||
expect(data).to.equal('<foo>bar</foo>'); // that's probably not right, but in the unit test, it is :)
|
||||
expect(spy.calledWith(sinon.match(function(request) {
|
||||
return request.headers.Accept === 'application/xml' && request.dataType === 'xml';
|
||||
}))).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail for missing uri parameter', function(done) {
|
||||
restDao.get({}, function(err, data) {
|
||||
expect(err).to.exist;
|
||||
expect(err.code).to.equal(400);
|
||||
expect(data).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail for unhandled data type', function(done) {
|
||||
restDao.get({
|
||||
uri: '/asdf',
|
||||
type: 'snafu'
|
||||
}, function(err, data) {
|
||||
expect(err).to.exist;
|
||||
expect(err.code).to.equal(400);
|
||||
expect(data).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail for server error', function(done) {
|
||||
$.ajax.restore();
|
||||
sinon.stub($, 'ajax').yieldsTo('error', {
|
||||
status: 500
|
||||
@ -47,21 +140,15 @@ define(function(require) {
|
||||
statusText: 'Internal error'
|
||||
}, {});
|
||||
|
||||
restDao.get('/asdf', function(err, data) {
|
||||
restDao.get({
|
||||
uri: '/asdf'
|
||||
}, function(err, data) {
|
||||
expect(err).to.exist;
|
||||
expect(err.code).to.equal(500);
|
||||
expect(data).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
restDao.get('/asdf', function(err, data) {
|
||||
expect(err).to.not.exist;
|
||||
expect(data.foo).to.equal('bar');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('put', function() {
|
||||
|
Loading…
Reference in New Issue
Block a user