fix tests

This commit is contained in:
Felix Hammerl 2014-02-06 11:55:24 +01:00
parent 20f36285b6
commit 6cd57fa0f6
8 changed files with 278 additions and 155 deletions

View File

@ -12,7 +12,7 @@
"dependencies": {
"crypto-lib": "https://github.com/whiteout-io/crypto-lib/tarball/master",
"imap-client": "https://github.com/whiteout-io/imap-client/tarball/dev/attachments",
"pgpmailer": "https://github.com/whiteout-io/pgpmailer/tarball/master",
"pgpmailer": "https://github.com/whiteout-io/pgpmailer/tarball/dev/attachments",
"requirejs": "2.1.10"
},
"devDependencies": {

View File

@ -96,7 +96,7 @@ define(function(require) {
self._outboxBusy = true;
// get last item from outbox
self._emailDao.list(function(err, pending) {
self._emailDao.listForOutbox(function(err, pending) {
if (err) {
self._outboxBusy = false;
callback(err);

View File

@ -254,7 +254,7 @@ define(function(require) {
}
// persist the email locally for later smtp transmission
emailDao.store(email, function(err) {
emailDao.storeForOutbox(email, function(err) {
if (err) {
$scope.onError(err);
return;

View File

@ -86,6 +86,9 @@ define(function(require) {
}
};
// connect the pgpmailer
self._pgpmailerLogin();
// connect to newly created imap client
self._imapLogin(function(err) {
if (err) {
@ -964,38 +967,6 @@ define(function(require) {
// Internal API
//
// Encryption API
EmailDAO.prototype._encrypt = function(options, callback) {
var self = this,
pt = options.email.body;
options.keys = options.keys || [];
// get own public key so send message can be read
self._crypto.exportKeys(function(err, ownKeys) {
if (err) {
callback(err);
return;
}
// add own public key to receiver list
options.keys.push(ownKeys.publicKeyArmored);
// encrypt the email
self._crypto.encrypt(pt, options.keys, function(err, ct) {
if (err) {
callback(err);
return;
}
// replace plaintext body with pgp message
options.email.body = ct;
callback(null, options.email);
});
});
};
// Local Storage API
EmailDAO.prototype._localListMessages = function(options, callback) {
@ -1023,6 +994,16 @@ define(function(require) {
};
// PGP Mailer API
/**
* Login the smtp client
*/
EmailDAO.prototype._pgpmailerLogin = function() {
this._pgpMailer.login();
};
// IMAP API
/**
@ -1199,29 +1180,48 @@ define(function(require) {
}
};
// to be removed and solved with IMAP!
EmailDAO.prototype.store = function(email, callback) {
/**
* Persists an email object for the outbox, encrypted with the user's public key
* @param {Object} email The email object
* @param {Function} callback(error) Invoked when the email was encrypted and persisted, contains information in case of an error
*/
EmailDAO.prototype.storeForOutbox = function(email, callback) {
var self = this,
dbType = 'email_OUTBOX';
dbType = 'email_OUTBOX',
plaintext = email.body;
// give the email a random identifier (used for storage)
email.id = util.UUID();
// encrypt
self._encrypt({
email: email
}, function(err, email) {
// get own public key so send message can be read
self._crypto.exportKeys(function(err, ownKeys) {
if (err) {
callback(err);
return;
}
// store to local storage
self._devicestorage.storeList([email], dbType, callback);
// encrypt the email with the user's public key
self._crypto.encrypt(plaintext, [ownKeys.publicKeyArmored], function(err, ciphertext) {
if (err) {
callback(err);
return;
}
// replace plaintext body with pgp message
email.body = ciphertext;
// store to local storage
self._devicestorage.storeList([email], dbType, callback);
});
});
};
// to be removed and solved with IMAP!
EmailDAO.prototype.list = function(callback) {
/**
* Reads and decrypts persisted email objects for the outbox
* @param {Function} callback(error, emails) Invoked when the email was encrypted and persisted, contains information in case of an error
*/
EmailDAO.prototype.listForOutbox = function(callback) {
var self = this,
dbType = 'email_OUTBOX';

View File

@ -50,13 +50,20 @@ define(function(require) {
mocks.module('addaccounttest');
mocks.inject(function($controller, $rootScope, $location) {
location = $location;
scope = $rootScope.$new();
scope.state = {};
sinon.stub(location, 'path', function(path) {
expect(path).to.equal('/login');
expect(fetchOAuthTokenStub.calledOnce).to.be.true;
location.path.restore();
scope.$apply.restore();
done();
});
scope = $rootScope.$new();
scope.state = {};
sinon.stub(scope, '$apply', function() {});
ctrl = $controller(AddAccountCtrl, {
$location: location,
$scope: scope

View File

@ -4,14 +4,15 @@ define(function(require) {
var EmailDAO = require('js/dao/email-dao'),
KeychainDAO = require('js/dao/keychain-dao'),
ImapClient = require('imap-client'),
SmtpClient = require('smtp-client'),
PgpMailer = require('pgpmailer'),
PGP = require('js/crypto/pgp'),
DeviceStorageDAO = require('js/dao/devicestorage-dao'),
str = require('js/app-config').string,
expect = chai.expect;
describe('Email DAO unit tests', function() {
var dao, keychainStub, imapClientStub, smtpClientStub, pgpStub, devicestorageStub;
var dao, keychainStub, imapClientStub, pgpMailerStub, pgpStub, devicestorageStub;
var emailAddress, passphrase, asymKeySize, mockkeyId, dummyEncryptedMail,
dummyDecryptedMail, dummyLegacyDecryptedMail, mockKeyPair, account, publicKey, verificationMail, verificationUuid,
@ -124,7 +125,7 @@ define(function(require) {
keychainStub = sinon.createStubInstance(KeychainDAO);
imapClientStub = sinon.createStubInstance(ImapClient);
smtpClientStub = sinon.createStubInstance(SmtpClient);
pgpMailerStub = sinon.createStubInstance(PgpMailer);
pgpStub = sinon.createStubInstance(PGP);
devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
@ -144,7 +145,7 @@ define(function(require) {
dao.onConnect({
imapClient: imapClientStub,
smtpClient: smtpClientStub
pgpMailer: pgpMailerStub
}, function(err) {
expect(err).to.not.exist;
expect(dao._account.online).to.be.true;
@ -289,11 +290,12 @@ define(function(require) {
});
describe('onConnect', function() {
var imapLoginStub, imapListFoldersStub;
var imapLoginStub, imapListFoldersStub, pgpmailerLoginStub;
beforeEach(function(done) {
// imap login
imapLoginStub = sinon.stub(dao, '_imapLogin');
pgpmailerLoginStub = sinon.stub(dao, '_pgpmailerLogin');
imapListFoldersStub = sinon.stub(dao, '_imapListFolders');
dao.onDisconnect(null, function(err) {
@ -307,17 +309,20 @@ define(function(require) {
afterEach(function() {
imapLoginStub.restore();
pgpmailerLoginStub.restore();
imapListFoldersStub.restore();
});
it('should fail due to error in imap login', function(done) {
imapLoginStub.yields({});
pgpmailerLoginStub.returns();
dao.onConnect({
imapClient: imapClientStub,
smtpClient: smtpClientStub
pgpMailer: pgpMailerStub
}, function(err) {
expect(err).to.exist;
expect(pgpmailerLoginStub.calledOnce).to.be.true;
expect(imapLoginStub.calledOnce).to.be.true;
expect(dao._account.online).to.be.false;
done();
@ -327,10 +332,11 @@ define(function(require) {
it('should work when folder already initiated', function(done) {
dao._account.folders = [];
imapLoginStub.yields();
pgpmailerLoginStub.returns();
dao.onConnect({
imapClient: imapClientStub,
smtpClient: smtpClientStub
pgpMailer: pgpMailerStub
}, function(err) {
expect(err).to.not.exist;
expect(dao._account.online).to.be.true;
@ -343,11 +349,12 @@ define(function(require) {
it('should work when folder not yet initiated', function(done) {
var folders = [];
imapLoginStub.yields();
pgpmailerLoginStub.returns();
imapListFoldersStub.yields(null, folders);
dao.onConnect({
imapClient: imapClientStub,
smtpClient: smtpClientStub
pgpMailer: pgpMailerStub
}, function(err) {
expect(err).to.not.exist;
expect(dao._account.online).to.be.true;
@ -498,6 +505,20 @@ define(function(require) {
});
});
describe('_pgpmailerLogin', function() {
it('should work', function() {
// called once in onConnect
expect(pgpMailerStub.login.calledOnce).to.be.true;
pgpMailerStub.login.returns();
dao._pgpmailerLogin();
expect(pgpMailerStub.login.calledTwice).to.be.true;
});
});
describe('_imapLogin', function() {
it('should fail when disconnected', function(done) {
dao.onDisconnect(null, function(err) {
@ -2459,13 +2480,27 @@ define(function(require) {
describe('sendPlaintext', function() {
it('should work', function(done) {
smtpClientStub.send.withArgs(dummyEncryptedMail).yields();
pgpMailerStub.send.withArgs({
mail: dummyEncryptedMail
}).yields();
dao.sendPlaintext({
email: dummyEncryptedMail
}, function(err) {
expect(err).to.not.exist;
expect(smtpClientStub.send.calledOnce).to.be.true;
expect(pgpMailerStub.send.calledOnce).to.be.true;
done();
});
});
it('should not work when pgpmailer fails', function(done) {
pgpMailerStub.send.yields({});
dao.sendPlaintext({
email: dummyEncryptedMail
}, function(err) {
expect(err).to.exist;
expect(pgpMailerStub.send.calledOnce).to.be.true;
done();
});
});
@ -2473,101 +2508,76 @@ define(function(require) {
describe('sendEncrypted', function() {
it('should work', function(done) {
var encryptStub = sinon.stub(dao, '_encrypt').yields(null, {});
smtpClientStub.send.yields();
dao.sendEncrypted({
email: dummyDecryptedMail
}, function(err) {
expect(err).to.not.exist;
expect(encryptStub.calledOnce).to.be.true;
expect(smtpClientStub.send.calledOnce).to.be.true;
done();
});
});
it('should not work when encryption fails', function(done) {
var encryptStub = sinon.stub(dao, '_encrypt').yields({});
dao.sendEncrypted({
email: dummyDecryptedMail
}, function(err) {
expect(err).to.exist;
expect(encryptStub.calledOnce).to.be.true;
expect(smtpClientStub.send.called).to.be.false;
done();
});
});
it('should not work without recipients', function(done) {
var encryptStub = sinon.stub(dao, '_encrypt');
delete dummyDecryptedMail.to;
dao.sendEncrypted({
email: dummyDecryptedMail
}, function(err) {
expect(err).to.exist;
expect(encryptStub.called).to.be.false;
expect(smtpClientStub.send.called).to.be.false;
done();
});
});
it('should not work with without sender', function(done) {
var encryptStub = sinon.stub(dao, '_encrypt');
delete dummyDecryptedMail.from;
dao.sendEncrypted({
email: dummyDecryptedMail
}, function(err) {
expect(err).to.exist;
expect(encryptStub.called).to.be.false;
expect(smtpClientStub.send.called).to.be.false;
done();
});
});
});
describe('_encrypt', function() {
it('should work without attachments', function(done) {
var ct = 'OMGSOENCRYPTED';
pgpStub.exportKeys.yields(null, {
privateKeyArmored: mockKeyPair.privateKey.encryptedKey,
publicKeyArmored: mockKeyPair.publicKey.publicKey
});
pgpStub.encrypt.yields(null, ct);
dao._encrypt({
pgpMailerStub.send.withArgs({
encrypt: true,
cleartextMessage: str.message,
mail: dummyDecryptedMail,
publicKeysArmored: dummyDecryptedMail.receiverKeys
}).yields();
dao.sendEncrypted({
email: dummyDecryptedMail
}, function(err) {
expect(err).to.not.exist;
expect(pgpStub.exportKeys.calledOnce).to.be.true;
expect(pgpStub.encrypt.calledOnce).to.be.true;
expect(dummyDecryptedMail.body).to.contain(ct);
expect(pgpMailerStub.send.calledOnce).to.be.true;
done();
});
});
it('should not work when pgpmailer fails', function(done) {
pgpStub.exportKeys.yields(null, {
privateKeyArmored: mockKeyPair.privateKey.encryptedKey,
publicKeyArmored: mockKeyPair.publicKey.publicKey
});
pgpMailerStub.send.yields({});
dao.sendEncrypted({
email: dummyDecryptedMail
}, function(err) {
expect(err).to.exist;
expect(pgpStub.exportKeys.calledOnce).to.be.true;
expect(pgpMailerStub.send.calledOnce).to.be.true;
done();
});
});
it('should not work when sender key export fails', function(done) {
pgpStub.exportKeys.yields({});
dao.sendEncrypted({
email: dummyDecryptedMail
}, function(err) {
expect(err).to.exist;
expect(pgpStub.exportKeys.calledOnce).to.be.true;
expect(pgpMailerStub.send.calledOnce).to.be.false;
done();
});
});
});
describe('store', function() {
describe('storeForOutbox', function() {
it('should work', function(done) {
pgpStub.exportKeys.yields(null, {
publicKeyArmored: 'omgsocrypto'
});
pgpStub.encrypt.yields(null, 'asdfasfd');
devicestorageStub.storeList.yields();
var key = 'omgsocrypto';
dao.store(dummyDecryptedMail, function(err) {
pgpStub.exportKeys.yields(null, {
publicKeyArmored: key
});
pgpStub.encrypt.withArgs(dummyDecryptedMail.body, [key]).yields(null, 'asdfasfd');
devicestorageStub.storeList.withArgs([dummyDecryptedMail], 'email_OUTBOX').yields();
dao.storeForOutbox(dummyDecryptedMail, function(err) {
expect(err).to.not.exist;
expect(pgpStub.exportKeys.calledOnce).to.be.true;
expect(pgpStub.encrypt.calledOnce).to.be.true;
expect(devicestorageStub.storeList.calledOnce).to.be.true;
@ -2575,17 +2585,72 @@ define(function(require) {
done();
});
});
it('should work when store fails', function(done) {
var key = 'omgsocrypto';
pgpStub.exportKeys.yields(null, {
publicKeyArmored: key
});
pgpStub.encrypt.yields(null, 'asdfasfd');
devicestorageStub.storeList.yields({});
dao.storeForOutbox(dummyDecryptedMail, function(err) {
expect(err).to.exist;
expect(pgpStub.exportKeys.calledOnce).to.be.true;
expect(pgpStub.encrypt.calledOnce).to.be.true;
expect(devicestorageStub.storeList.calledOnce).to.be.true;
done();
});
});
it('should work when encryption fails', function(done) {
var key = 'omgsocrypto';
pgpStub.exportKeys.yields(null, {
publicKeyArmored: key
});
pgpStub.encrypt.yields({});
dao.storeForOutbox(dummyDecryptedMail, function(err) {
expect(err).to.exist;
expect(pgpStub.exportKeys.calledOnce).to.be.true;
expect(pgpStub.encrypt.calledOnce).to.be.true;
expect(devicestorageStub.storeList.called).to.be.false;
done();
});
});
it('should work when key export fails', function(done) {
pgpStub.exportKeys.yields({});
dao.storeForOutbox(dummyDecryptedMail, function(err) {
expect(err).to.exist;
expect(pgpStub.exportKeys.calledOnce).to.be.true;
expect(pgpStub.encrypt.called).to.be.false;
expect(devicestorageStub.storeList.called).to.be.false;
done();
});
});
});
describe('list', function() {
describe('listForOutbox', function() {
it('should work', function(done) {
devicestorageStub.listItems.yields(null, [dummyEncryptedMail]);
pgpStub.exportKeys.yields(null, {
publicKeyArmored: 'omgsocrypto'
});
pgpStub.decrypt.yields(null, dummyDecryptedMail.body);
var key = 'omgsocrypto';
dao.list(function(err, mails) {
devicestorageStub.listItems.withArgs('email_OUTBOX', 0, null).yields(null, [dummyEncryptedMail]);
pgpStub.exportKeys.yields(null, {
publicKeyArmored: key
});
pgpStub.decrypt.withArgs(dummyEncryptedMail.body, key).yields(null, dummyDecryptedMail.body);
dao.listForOutbox(function(err, mails) {
expect(err).to.not.exist;
expect(devicestorageStub.listItems.calledOnce).to.be.true;
@ -2593,12 +2658,63 @@ define(function(require) {
expect(pgpStub.decrypt.calledOnce).to.be.true;
expect(mails.length).to.equal(1);
expect(mails[0].body).to.equal(dummyDecryptedMail.body);
expect(mails[0].subject).to.equal(dummyDecryptedMail.subject);
done();
});
});
it('should not work when decryption fails', function(done) {
var key = 'omgsocrypto',
errMsg = 'THIS IS AN ERROR!';
devicestorageStub.listItems.yields(null, [dummyEncryptedMail]);
pgpStub.exportKeys.yields(null, {
publicKeyArmored: key
});
pgpStub.decrypt.yields({errMsg: errMsg});
dao.listForOutbox(function(err, mails) {
expect(err).to.not.exist;
expect(mails[0].body).to.equal(errMsg);
expect(devicestorageStub.listItems.calledOnce).to.be.true;
expect(pgpStub.exportKeys.calledOnce).to.be.true;
expect(pgpStub.decrypt.calledOnce).to.be.true;
done();
});
});
it('should not work when key export fails', function(done) {
devicestorageStub.listItems.yields(null, [dummyEncryptedMail]);
pgpStub.exportKeys.yields({});
dao.listForOutbox(function(err, mails) {
expect(err).to.exist;
expect(mails).to.not.exist;
expect(devicestorageStub.listItems.calledOnce).to.be.true;
expect(pgpStub.exportKeys.calledOnce).to.be.true;
expect(pgpStub.decrypt.called).to.be.false;
done();
});
});
it('should not work when list from storage fails', function(done) {
devicestorageStub.listItems.yields({});
dao.listForOutbox(function(err, mails) {
expect(err).to.exist;
expect(mails).to.not.exist;
expect(devicestorageStub.listItems.calledOnce).to.be.true;
expect(pgpStub.exportKeys.called).to.be.false;
expect(pgpStub.decrypt.called).to.be.false;
done();
});
});
});
});
});

View File

@ -85,7 +85,7 @@ define(function(require) {
};
dummyMails = [member, invited, notinvited];
emailDaoStub.list.yieldsAsync(null, dummyMails);
emailDaoStub.listForOutbox.yieldsAsync(null, dummyMails);
emailDaoStub.sendEncrypted.withArgs(sinon.match(function(opts) {
return typeof opts.email !== 'undefined' && opts.email.to.address === member.to.address;
})).yieldsAsync();
@ -111,7 +111,7 @@ define(function(require) {
expect(outbox._outboxBusy).to.be.false;
expect(unsentCount).to.equal(2);
expect(emailDaoStub.list.callCount).to.equal(1);
expect(emailDaoStub.listForOutbox.callCount).to.equal(1);
expect(emailDaoStub.sendEncrypted.callCount).to.equal(1);
expect(emailDaoStub.sendPlaintext.callCount).to.equal(1);
expect(devicestorageStub.removeList.callCount).to.equal(1);
@ -136,7 +136,7 @@ define(function(require) {
it('should not process outbox in offline mode', function(done) {
emailDaoStub._account.online = false;
emailDaoStub.list.yieldsAsync(null, [{
emailDaoStub.listForOutbox.yieldsAsync(null, [{
id: '123',
to: [{
name: 'member',
@ -147,7 +147,7 @@ define(function(require) {
outbox._processOutbox(function(err, count) {
expect(err).to.not.exist;
expect(count).to.equal(1);
expect(emailDaoStub.list.callCount).to.equal(1);
expect(emailDaoStub.listForOutbox.callCount).to.equal(1);
expect(outbox._outboxBusy).to.be.false;
done();
});

View File

@ -277,13 +277,13 @@ define(function(require) {
scope.onError = function(err) {
expect(err).to.not.exist;
expect(scope.state.writer.open).to.be.false;
expect(emailDaoMock.store.calledOnce).to.be.true;
expect(emailDaoMock.storeForOutbox.calledOnce).to.be.true;
expect(emailDaoMock.sync.calledOnce).to.be.true;
done();
};
emailDaoMock.store.yields();
emailDaoMock.storeForOutbox.yields();
emailDaoMock.sync.yields({
code: 42
});
@ -310,13 +310,13 @@ define(function(require) {
scope.onError = function(err) {
expect(err).to.not.exist;
expect(scope.state.writer.open).to.be.false;
expect(emailDaoMock.store.calledOnce).to.be.true;
expect(emailDaoMock.storeForOutbox.calledOnce).to.be.true;
expect(emailDaoMock.sync.calledOnce).to.be.true;
done();
};
emailDaoMock.store.yields();
emailDaoMock.storeForOutbox.yields();
emailDaoMock.sync.yields();
scope.state.writer.write(re);
@ -341,13 +341,13 @@ define(function(require) {
scope.onError = function(err) {
expect(err).to.exist;
expect(scope.state.writer.open).to.be.false;
expect(emailDaoMock.store.calledOnce).to.be.true;
expect(emailDaoMock.storeForOutbox.calledOnce).to.be.true;
expect(emailDaoMock.sync.calledOnce).to.be.true;
done();
};
emailDaoMock.store.yields();
emailDaoMock.storeForOutbox.yields();
emailDaoMock.sync.yields({});
scope.state.writer.write(re);
@ -367,7 +367,7 @@ define(function(require) {
scope.subject = 'yaddablabla';
scope.toKey = 'Public Key';
emailDaoMock.store.withArgs(sinon.match(function(mail) {
emailDaoMock.storeForOutbox.withArgs(sinon.match(function(mail) {
return mail.from[0].address === emailAddress && mail.to.length === 1 && mail.receiverKeys.length === 1;
})).yields({
errMsg: 'snafu'
@ -376,7 +376,7 @@ define(function(require) {
scope.onError = function(err) {
expect(err).to.exist;
expect(scope.state.writer.open).to.be.true;
expect(emailDaoMock.store.calledOnce).to.be.true;
expect(emailDaoMock.storeForOutbox.calledOnce).to.be.true;
done();
};
scope.sendToOutbox();