WIP add unit tests

This commit is contained in:
Felix Hammerl 2014-10-07 20:32:23 +02:00
parent c36cd069e0
commit 9bfda73969
40 changed files with 9527 additions and 9675 deletions

2
.gitignore vendored
View File

@ -8,3 +8,5 @@ dist/
release/ release/
test/integration/src/ test/integration/src/
.elasticbeanstalk/ .elasticbeanstalk/
test/unit/index.js
test/unit/index.js.map

View File

@ -18,19 +18,18 @@
"unused": true, "unused": true,
"predef": [ "predef": [
"self",
"console", "console",
"Notification", "process",
"importScripts",
"process",
"Event",
"chrome", "chrome",
"define", "Notification",
"self", "Event",
"sinon",
"mocha",
"chai",
"expect",
"describe", "describe",
"it", "it",
"chai",
"sinon",
"mocha",
"before", "before",
"beforeEach", "beforeEach",
"after", "after",

View File

@ -1,6 +1,8 @@
module.exports = function(grunt) { module.exports = function(grunt) {
'use strict'; 'use strict';
require('time-grunt')(grunt);
var version = grunt.option('release'), var version = grunt.option('release'),
zipName = (version) ? version : 'DEV'; zipName = (version) ? version : 'DEV';
@ -24,7 +26,7 @@ module.exports = function(grunt) {
}, },
jshint: { jshint: {
all: ['Gruntfile.js', 'src/*.js', 'src/js/**/*.js', 'test/unit/*.js', 'test/integration/*.js'], all: ['Gruntfile.js', 'src/*.js', 'src/js/**/*.js', 'test/unit/*-test.js', 'test/integration/*.js'],
options: { options: {
jshintrc: '.jshintrc' jshintrc: '.jshintrc'
} }
@ -109,16 +111,22 @@ module.exports = function(grunt) {
'dist/js/app.min.js': ['src/js/app.js'] 'dist/js/app.min.js': ['src/js/app.js']
}, },
options: { options: {
external: ['node-forge', 'net', 'tls'] // common.js apis not required at build time external: []
} }
}, },
/* TODO: unitTest: {
tls-worker: {}, files: {
mailreader-worker: {}, 'test/unit/index.js': ['test/unit/*-test.js']
pbkdf2-worker: {}, },
unitTest: {}, options: {
unitTest: {}, external: []
integrationTest: {} }
},
/*
TODO:
mailreader-worker: {},
pbkdf2-worker: {},
integrationTest: {}
*/ */
}, },
@ -126,6 +134,7 @@ module.exports = function(grunt) {
all: { all: {
files: { files: {
'dist/js/app.min.js': [ 'dist/js/app.min.js': [
'src/lib/openpgp/openpgp.js',
'src/lib/underscore/underscore-min.js', 'src/lib/underscore/underscore-min.js',
'node_modules/jquery/dist/jquery.min.js', 'node_modules/jquery/dist/jquery.min.js',
'src/lib/angular/angular.min.js', 'src/lib/angular/angular.min.js',
@ -142,6 +151,28 @@ module.exports = function(grunt) {
] ]
} }
}, },
unitTest: {
files: {
'test/unit/index.js': [
'src/lib/underscore/underscore-min.js',
'node_modules/jquery/dist/jquery.min.js',
'src/lib/openpgp/openpgp.js',
'src/lib/angular/angular.min.js',
'node_modules/angular-mocks/angular-mocks.js',
'src/lib/angular/angular-route.min.js',
'src/lib/angular/angular-animate.min.js',
'src/lib/ngtagsinput/ng-tags-input.min.js',
'src/lib/fastclick/fastclick.js',
'node_modules/ng-infinite-scroll/build/ng-infinite-scroll.min.js',
'src/lib/lawnchair/lawnchair-git.js',
'src/lib/lawnchair/lawnchair-adapter-webkit-sqlite-git.js',
'src/lib/lawnchair/lawnchair-adapter-indexed-db-git.js',
'node_modules/dompurify/purify.js',
'test/lib/angular-mocks.js',
'test/unit/index.js'
]
}
},
options: { options: {
banner: '/*! Copyright © <%= grunt.template.today("yyyy") %>, Whiteout Networks GmbH.*/\n' banner: '/*! Copyright © <%= grunt.template.today("yyyy") %>, Whiteout Networks GmbH.*/\n'
} }
@ -152,7 +183,7 @@ module.exports = function(grunt) {
expand: true, expand: true,
flatten: true, flatten: true,
cwd: 'node_modules/', cwd: 'node_modules/',
src: ['requirejs/require.js', 'mocha/mocha.css', 'mocha/mocha.js', 'chai/chai.js', 'sinon/pkg/sinon.js', 'angularjs/src/ngMock/angular-mocks.js', 'browsercrow/src/*.js', 'browsersmtp/src/*.js'], src: ['mocha/mocha.css', 'mocha/mocha.js', 'chai/chai.js', 'sinon/pkg/sinon.js', 'browsercrow/src/*.js', 'browsersmtp/src/*.js'],
dest: 'test/lib/' dest: 'test/lib/'
}, },
font: { font: {

View File

@ -1,70 +1,72 @@
{ {
"name": "whiteout-mail", "name": "whiteout-mail",
"version": "0.0.1", "version": "0.0.1",
"description": "Mail App with integrated OpenPGP encryption.", "description": "Mail App with integrated OpenPGP encryption.",
"author": "Whiteout Networks", "author": "Whiteout Networks",
"homepage": "https://whiteout.io", "homepage": "https://whiteout.io",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/whiteout-io/mail-html5.git" "url": "https://github.com/whiteout-io/mail-html5.git"
}, },
"keywords": [ "keywords": [
"email", "email",
"mail", "mail",
"client", "client",
"app", "app",
"openpgp", "openpgp",
"pgp", "pgp",
"gpg", "gpg",
"imap", "imap",
"smtp" "smtp"
], ],
"engines": { "engines": {
"node": ">=0.10" "node": ">=0.10"
}, },
"scripts": { "scripts": {
"test": "grunt && grunt test", "test": "grunt && grunt test",
"start": "node server.js" "start": "node server.js"
}, },
"dependencies": { "dependencies": {
"axe-logger": "~0.0.2", "axe-logger": "~0.0.2",
"compression": "^1.0.11", "compression": "^1.0.11",
"config": "^1.0.2", "config": "^1.0.2",
"crypto-lib": "~0.2.1", "crypto-lib": "~0.2.1",
"dompurify": "~0.4.2", "dompurify": "~0.4.2",
"express": "^4.8.3", "express": "^4.8.3",
"imap-client": "~0.4.3", "imap-client": "~0.4.3",
"jquery": "~2.1.1", "jquery": "~2.1.1",
"mailreader": "~0.3.5", "mailreader": "~0.3.5",
"morgan": "^1.2.3", "morgan": "^1.2.3",
"ng-infinite-scroll": "~1.1.2", "ng-infinite-scroll": "~1.1.2",
"npmlog": "^0.1.1", "npmlog": "^0.1.1",
"pgpbuilder": "~0.4.0", "pgpbuilder": "~0.4.0",
"pgpmailer": "~0.4.0", "pgpmailer": "~0.4.0",
"socket.io": "^1.0.6", "socket.io": "^1.0.6",
"tcp-socket": "^0.3.9", "tcp-socket": "^0.3.9",
"wo-smtpclient": "^0.3.8" "wo-smtpclient": "^0.3.8"
}, },
"devDependencies": { "devDependencies": {
"angularjs": "https://github.com/whiteout-io/angular.js/tarball/npm-version", "angular-mocks": "^1.2.25",
"browsercrow": "https://github.com/whiteout-io/browsercrow/tarball/master", "angularjs": "https://github.com/whiteout-io/angular.js/tarball/npm-version",
"browsersmtp": "https://github.com/whiteout-io/browsersmtp/tarball/master", "browsercrow": "https://github.com/whiteout-io/browsercrow/tarball/master",
"chai": "~1.7.2", "browsersmtp": "https://github.com/whiteout-io/browsersmtp/tarball/master",
"grunt": "~0.4.1", "chai": "~1.7.2",
"grunt-autoprefixer": "~0.7.2", "grunt": "~0.4.1",
"grunt-browserify": "^3.0.1", "grunt-autoprefixer": "~0.7.2",
"grunt-contrib-clean": "~0.5.0", "grunt-browserify": "^3.0.1",
"grunt-contrib-compress": "~0.5.2", "grunt-contrib-clean": "~0.5.0",
"grunt-contrib-connect": "~0.5.0", "grunt-contrib-compress": "~0.5.2",
"grunt-contrib-copy": "~0.4.1", "grunt-contrib-connect": "~0.5.0",
"grunt-contrib-jshint": "~0.6.4", "grunt-contrib-copy": "~0.4.1",
"grunt-contrib-sass": "~0.7.3", "grunt-contrib-jshint": "~0.6.4",
"grunt-contrib-uglify": "^0.6.0", "grunt-contrib-sass": "~0.7.3",
"grunt-contrib-watch": "~0.5.3", "grunt-contrib-uglify": "^0.6.0",
"grunt-csso": "~0.6.1", "grunt-contrib-watch": "~0.5.3",
"grunt-manifest": "^0.4.0", "grunt-csso": "~0.6.1",
"grunt-mocha": "~0.4.1", "grunt-manifest": "^0.4.0",
"mocha": "~1.13.0", "grunt-mocha": "~0.4.1",
"sinon": "~1.7.3" "mocha": "~1.13.0",
} "sinon": "~1.7.3",
"time-grunt": "^1.0.0"
}
} }

View File

@ -1,101 +1,97 @@
define(function(require) { 'use strict';
'use strict';
var expect = chai.expect, var mocks = angular.mocks,
angular = require('angular'), AccountCtrl = require('../../src/js/controller/account'),
mocks = require('angularMocks'), PGP = require('../../src/js/crypto/pgp'),
AccountCtrl = require('js/controller/account'), dl = require('../../src/js/util/download'),
PGP = require('js/crypto/pgp'), appController = require('../../src/js/app-controller'),
dl = require('js/util/download'), KeychainDAO = require('../../src/js/dao/keychain-dao');
appController = require('js/app-controller'),
KeychainDAO = require('js/dao/keychain-dao');
describe('Account Controller unit test', function() { describe('Account Controller unit test', function() {
var scope, accountCtrl, var scope, accountCtrl,
dummyFingerprint, expectedFingerprint, dummyFingerprint, expectedFingerprint,
dummyKeyId, expectedKeyId, dummyKeyId, expectedKeyId,
emailAddress, keySize, pgpMock, keychainMock; emailAddress, keySize, pgpMock, keychainMock;
beforeEach(function() { beforeEach(function() {
appController._pgp = pgpMock = sinon.createStubInstance(PGP); appController._pgp = pgpMock = sinon.createStubInstance(PGP);
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO); appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
dummyFingerprint = '3A2D39B4E1404190B8B949DE7D7E99036E712926'; dummyFingerprint = '3A2D39B4E1404190B8B949DE7D7E99036E712926';
expectedFingerprint = '3A2D 39B4 E140 4190 B8B9 49DE 7D7E 9903 6E71 2926'; expectedFingerprint = '3A2D 39B4 E140 4190 B8B9 49DE 7D7E 9903 6E71 2926';
dummyKeyId = '9FEB47936E712926'; dummyKeyId = '9FEB47936E712926';
expectedKeyId = '6E712926'; expectedKeyId = '6E712926';
pgpMock.getFingerprint.returns(dummyFingerprint); pgpMock.getFingerprint.returns(dummyFingerprint);
pgpMock.getKeyId.returns(dummyKeyId); pgpMock.getKeyId.returns(dummyKeyId);
emailAddress = 'fred@foo.com'; emailAddress = 'fred@foo.com';
keySize = 1234; keySize = 1234;
appController._emailDao = { appController._emailDao = {
_account: { _account: {
emailAddress: emailAddress, emailAddress: emailAddress,
asymKeySize: keySize asymKeySize: keySize
} }
}; };
pgpMock.getKeyParams.returns({ pgpMock.getKeyParams.returns({
_id: dummyKeyId, _id: dummyKeyId,
fingerprint: dummyFingerprint, fingerprint: dummyFingerprint,
userId: emailAddress, userId: emailAddress,
bitSize: keySize bitSize: keySize
});
angular.module('accounttest', []);
mocks.module('accounttest');
mocks.inject(function($rootScope, $controller) {
scope = $rootScope.$new();
scope.state = {};
accountCtrl = $controller(AccountCtrl, {
$scope: scope
});
});
}); });
afterEach(function() {}); angular.module('accounttest', []);
mocks.module('accounttest');
describe('scope variables', function() { mocks.inject(function($rootScope, $controller) {
it('should be set correctly', function() { scope = $rootScope.$new();
expect(scope.eMail).to.equal(emailAddress); scope.state = {};
expect(scope.keyId).to.equal(expectedKeyId); accountCtrl = $controller(AccountCtrl, {
expect(scope.fingerprint).to.equal(expectedFingerprint); $scope: scope
expect(scope.keysize).to.equal(keySize);
});
});
describe('export to key file', function() {
it('should work', function() {
var createDownloadMock = sinon.stub(dl, 'createDownload');
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
publicKey: {
_id: dummyKeyId,
publicKey: 'a'
},
privateKey: {
encryptedKey: 'b'
}
});
createDownloadMock.withArgs(sinon.match(function(arg) {
return arg.content === 'a\r\nb' && arg.filename === 'whiteout_mail_' + emailAddress + '_' + expectedKeyId + '.asc' && arg.contentType === 'text/plain';
})).returns();
scope.exportKeyFile();
expect(scope.state.lightbox).to.equal(undefined);
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
expect(dl.createDownload.calledOnce).to.be.true;
dl.createDownload.restore();
});
it('should not work when key export failed', function(done) {
keychainMock.getUserKeyPair.yields(new Error('Boom!'));
scope.onError = function(err) {
expect(err.message).to.equal('Boom!');
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
done();
};
scope.exportKeyFile();
}); });
}); });
}); });
afterEach(function() {});
describe('scope variables', function() {
it('should be set correctly', function() {
expect(scope.eMail).to.equal(emailAddress);
expect(scope.keyId).to.equal(expectedKeyId);
expect(scope.fingerprint).to.equal(expectedFingerprint);
expect(scope.keysize).to.equal(keySize);
});
});
describe('export to key file', function() {
it('should work', function() {
var createDownloadMock = sinon.stub(dl, 'createDownload');
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
publicKey: {
_id: dummyKeyId,
publicKey: 'a'
},
privateKey: {
encryptedKey: 'b'
}
});
createDownloadMock.withArgs(sinon.match(function(arg) {
return arg.content === 'a\r\nb' && arg.filename === 'whiteout_mail_' + emailAddress + '_' + expectedKeyId + '.asc' && arg.contentType === 'text/plain';
})).returns();
scope.exportKeyFile();
expect(scope.state.lightbox).to.equal(undefined);
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
expect(dl.createDownload.calledOnce).to.be.true;
dl.createDownload.restore();
});
it('should not work when key export failed', function(done) {
keychainMock.getUserKeyPair.yields(new Error('Boom!'));
scope.onError = function(err) {
expect(err.message).to.equal('Boom!');
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
done();
};
scope.exportKeyFile();
});
});
}); });

View File

@ -1,216 +1,212 @@
define(function(require) { 'use strict';
'use strict';
var expect = chai.expect, var mocks = angular.mocks,
angular = require('angular'), AddAccountCtrl = require('../../src/js/controller/add-account'),
mocks = require('angularMocks'), Auth = require('../../src/js/bo/auth'),
AddAccountCtrl = require('js/controller/add-account'), AdminDao = require('../../src/js/dao/admin-dao'),
Auth = require('js/bo/auth'), appController = require('../../src/js/app-controller');
AdminDao = require('js/dao/admin-dao'),
appController = require('js/app-controller');
describe('Add Account Controller unit test', function() { describe('Add Account Controller unit test', function() {
var scope, location, ctrl, authStub, origAuth, adminStub; var scope, location, ctrl, authStub, origAuth, adminStub;
beforeEach(function() { beforeEach(function() {
// remember original module to restore later, then replace it // remember original module to restore later, then replace it
origAuth = appController._auth; origAuth = appController._auth;
appController._auth = authStub = sinon.createStubInstance(Auth); appController._auth = authStub = sinon.createStubInstance(Auth);
appController._adminDao = adminStub = sinon.createStubInstance(AdminDao); appController._adminDao = adminStub = sinon.createStubInstance(AdminDao);
angular.module('addaccounttest', []); angular.module('addaccounttest', []);
mocks.module('addaccounttest'); mocks.module('addaccounttest');
mocks.inject(function($controller, $rootScope, $location) { mocks.inject(function($controller, $rootScope, $location) {
location = $location; location = $location;
scope = $rootScope.$new(); scope = $rootScope.$new();
scope.state = {}; scope.state = {};
scope.form = {}; scope.form = {};
scope.formValidate = {}; scope.formValidate = {};
sinon.stub(location, 'path').returns(location); sinon.stub(location, 'path').returns(location);
sinon.stub(location, 'search').returns(location); sinon.stub(location, 'search').returns(location);
sinon.stub(scope, '$apply', function() {}); sinon.stub(scope, '$apply', function() {});
ctrl = $controller(AddAccountCtrl, { ctrl = $controller(AddAccountCtrl, {
$location: location, $location: location,
$scope: scope, $scope: scope,
$routeParams: {} $routeParams: {}
});
}); });
}); });
afterEach(function() {
// restore the app controller module
appController._auth = origAuth;
location.path.restore();
location.search.restore();
if (scope.$apply.restore) {
scope.$apply.restore();
}
});
describe('createWhiteoutAccount', function() {
it('should return early for invalid form', function() {
scope.form.$invalid = true;
scope.createWhiteoutAccount();
expect(adminStub.createUser.called).to.be.false;
});
it('should fail to error creating user', function(done) {
scope.form.$invalid = false;
scope.betaCode = 'asfd';
scope.phone = '12345';
adminStub.createUser.yieldsAsync(new Error('asdf'));
scope.$apply = function() {
expect(scope.busy).to.be.false;
expect(scope.errMsg).to.equal('asdf');
expect(adminStub.createUser.calledOnce).to.be.true;
done();
};
scope.createWhiteoutAccount();
expect(scope.busy).to.be.true;
});
it('should work', function(done) {
scope.form.$invalid = false;
scope.betaCode = 'asfd';
scope.phone = '12345';
adminStub.createUser.yieldsAsync();
scope.$apply = function() {
expect(scope.busy).to.be.false;
expect(scope.errMsg).to.be.undefined;
expect(scope.step).to.equal(3);
expect(adminStub.createUser.calledOnce).to.be.true;
done();
};
scope.createWhiteoutAccount();
expect(scope.busy).to.be.true;
});
});
describe('validateUser', function() {
it('should return early for invalid form', function() {
scope.formValidate.$invalid = true;
scope.validateUser();
expect(adminStub.validateUser.called).to.be.false;
});
it('should fail to error creating user', function(done) {
scope.formValidate.$invalid = false;
scope.token = 'asfd';
adminStub.validateUser.yieldsAsync(new Error('asdf'));
scope.$apply = function() {
expect(scope.busyValidate).to.be.false;
expect(scope.errMsgValidate).to.equal('asdf');
expect(adminStub.validateUser.calledOnce).to.be.true;
done();
};
scope.validateUser();
expect(scope.busyValidate).to.be.true;
});
it('should work', function(done) {
scope.formValidate.$invalid = false;
scope.token = 'asfd';
adminStub.validateUser.yieldsAsync();
scope.login = function() {
expect(scope.busyValidate).to.be.true;
expect(scope.errMsgValidate).to.be.undefined;
expect(adminStub.validateUser.calledOnce).to.be.true;
done();
};
scope.validateUser();
expect(scope.busyValidate).to.be.true;
});
});
describe('login', function() {
it('should work', function() {
scope.form.$invalid = false;
authStub.setCredentials.returns();
scope.login();
expect(authStub.setCredentials.calledOnce).to.be.true;
expect(location.path.calledWith('/login')).to.be.true;
});
});
describe('connectToGoogle', function() {
it('should forward to login', function() {
authStub._oauth = {
isSupported: function() {
return true;
}
};
authStub.getOAuthToken.yields();
scope.connectToGoogle();
expect(location.path.calledWith('/login-set-credentials')).to.be.true;
expect(location.search.calledWith({
provider: 'gmail'
})).to.be.true;
expect(authStub.getOAuthToken.calledOnce).to.be.true;
});
it('should not use oauth for gmail', function() {
authStub._oauth = {
isSupported: function() {
return false;
}
};
scope.connectToGoogle();
expect(location.path.calledWith('/login-set-credentials')).to.be.true;
expect(location.search.calledWith({
provider: 'gmail'
})).to.be.true;
expect(authStub.getOAuthToken.called).to.be.false;
});
it('should not forward to login when oauth fails', function(done) {
authStub._oauth = {
isSupported: function() {
return true;
}
};
authStub.getOAuthToken.yields(new Error());
scope.onError = function(err) {
expect(err).to.exist;
expect(location.path.called).to.be.false;
expect(location.search.called).to.be.false;
done();
};
scope.connectToGoogle();
});
});
describe('connectTo', function() {
it('should forward to login', function() {
var provider = 'wmail';
scope.connectTo(provider);
expect(location.path.calledWith('/login-set-credentials')).to.be.true;
expect(location.search.calledWith({
provider: provider
})).to.be.true;
});
});
}); });
afterEach(function() {
// restore the app controller module
appController._auth = origAuth;
location.path.restore();
location.search.restore();
if (scope.$apply.restore) {
scope.$apply.restore();
}
});
describe('createWhiteoutAccount', function() {
it('should return early for invalid form', function() {
scope.form.$invalid = true;
scope.createWhiteoutAccount();
expect(adminStub.createUser.called).to.be.false;
});
it('should fail to error creating user', function(done) {
scope.form.$invalid = false;
scope.betaCode = 'asfd';
scope.phone = '12345';
adminStub.createUser.yieldsAsync(new Error('asdf'));
scope.$apply = function() {
expect(scope.busy).to.be.false;
expect(scope.errMsg).to.equal('asdf');
expect(adminStub.createUser.calledOnce).to.be.true;
done();
};
scope.createWhiteoutAccount();
expect(scope.busy).to.be.true;
});
it('should work', function(done) {
scope.form.$invalid = false;
scope.betaCode = 'asfd';
scope.phone = '12345';
adminStub.createUser.yieldsAsync();
scope.$apply = function() {
expect(scope.busy).to.be.false;
expect(scope.errMsg).to.be.undefined;
expect(scope.step).to.equal(3);
expect(adminStub.createUser.calledOnce).to.be.true;
done();
};
scope.createWhiteoutAccount();
expect(scope.busy).to.be.true;
});
});
describe('validateUser', function() {
it('should return early for invalid form', function() {
scope.formValidate.$invalid = true;
scope.validateUser();
expect(adminStub.validateUser.called).to.be.false;
});
it('should fail to error creating user', function(done) {
scope.formValidate.$invalid = false;
scope.token = 'asfd';
adminStub.validateUser.yieldsAsync(new Error('asdf'));
scope.$apply = function() {
expect(scope.busyValidate).to.be.false;
expect(scope.errMsgValidate).to.equal('asdf');
expect(adminStub.validateUser.calledOnce).to.be.true;
done();
};
scope.validateUser();
expect(scope.busyValidate).to.be.true;
});
it('should work', function(done) {
scope.formValidate.$invalid = false;
scope.token = 'asfd';
adminStub.validateUser.yieldsAsync();
scope.login = function() {
expect(scope.busyValidate).to.be.true;
expect(scope.errMsgValidate).to.be.undefined;
expect(adminStub.validateUser.calledOnce).to.be.true;
done();
};
scope.validateUser();
expect(scope.busyValidate).to.be.true;
});
});
describe('login', function() {
it('should work', function() {
scope.form.$invalid = false;
authStub.setCredentials.returns();
scope.login();
expect(authStub.setCredentials.calledOnce).to.be.true;
expect(location.path.calledWith('/login')).to.be.true;
});
});
describe('connectToGoogle', function() {
it('should forward to login', function() {
authStub._oauth = {
isSupported: function() {
return true;
}
};
authStub.getOAuthToken.yields();
scope.connectToGoogle();
expect(location.path.calledWith('/login-set-credentials')).to.be.true;
expect(location.search.calledWith({
provider: 'gmail'
})).to.be.true;
expect(authStub.getOAuthToken.calledOnce).to.be.true;
});
it('should not use oauth for gmail', function() {
authStub._oauth = {
isSupported: function() {
return false;
}
};
scope.connectToGoogle();
expect(location.path.calledWith('/login-set-credentials')).to.be.true;
expect(location.search.calledWith({
provider: 'gmail'
})).to.be.true;
expect(authStub.getOAuthToken.called).to.be.false;
});
it('should not forward to login when oauth fails', function(done) {
authStub._oauth = {
isSupported: function() {
return true;
}
};
authStub.getOAuthToken.yields(new Error());
scope.onError = function(err) {
expect(err).to.exist;
expect(location.path.called).to.be.false;
expect(location.search.called).to.be.false;
done();
};
scope.connectToGoogle();
});
});
describe('connectTo', function() {
it('should forward to login', function() {
var provider = 'wmail';
scope.connectTo(provider);
expect(location.path.calledWith('/login-set-credentials')).to.be.true;
expect(location.search.calledWith({
provider: provider
})).to.be.true;
});
});
}); });

View File

@ -1,146 +1,142 @@
define(function(require) { 'use strict';
'use strict';
var RestDAO = require('js/dao/rest-dao'), var RestDAO = require('../../src/js/dao/rest-dao'),
AdminDAO = require('js/dao/admin-dao'), AdminDAO = require('../../src/js/dao/admin-dao');
expect = chai.expect;
describe('Admin DAO unit tests', function() { describe('Admin DAO unit tests', function() {
var adminDao, restDaoStub, var adminDao, restDaoStub,
emailAddress = 'test@example.com', emailAddress = 'test@example.com',
password = 'secret'; password = 'secret';
beforeEach(function() { beforeEach(function() {
restDaoStub = sinon.createStubInstance(RestDAO); restDaoStub = sinon.createStubInstance(RestDAO);
adminDao = new AdminDAO(restDaoStub); adminDao = new AdminDAO(restDaoStub);
}); });
afterEach(function() {}); afterEach(function() {});
describe('createUser', function() { describe('createUser', function() {
it('should fail due to incomplete args', function(done) { it('should fail due to incomplete args', function(done) {
var opt = { var opt = {
emailAddress: emailAddress emailAddress: emailAddress
}; };
adminDao.createUser(opt, function(err) { adminDao.createUser(opt, function(err) {
expect(err).to.exist; expect(err).to.exist;
done(); done();
});
});
it('should fail if user already exists', function(done) {
var opt = {
emailAddress: emailAddress,
password: password,
phone: '12345'
};
restDaoStub.post.withArgs(opt, '/user').yields({
code: 409
});
adminDao.createUser(opt, function(err) {
expect(err.message).to.contain('already taken');
expect(restDaoStub.post.calledOnce).to.be.true;
done();
});
});
it('should fail due to unknown error', function(done) {
var opt = {
emailAddress: emailAddress,
password: password,
phone: '12345'
};
restDaoStub.post.withArgs(opt, '/user').yields(new Error());
adminDao.createUser(opt, function(err) {
expect(err).to.exist;
expect(restDaoStub.post.calledOnce).to.be.true;
done();
});
});
it('should work', function(done) {
var opt = {
emailAddress: emailAddress,
password: password,
phone: '12345'
};
restDaoStub.post.withArgs(opt, '/user').yields();
adminDao.createUser(opt, function(err) {
expect(err).to.not.exist;
expect(restDaoStub.post.calledOnce).to.be.true;
done();
});
}); });
}); });
describe('validateUser', function() { it('should fail if user already exists', function(done) {
it('should fail due to incomplete args', function(done) { var opt = {
var opt = { emailAddress: emailAddress,
emailAddress: emailAddress password: password,
}; phone: '12345'
};
adminDao.validateUser(opt, function(err) { restDaoStub.post.withArgs(opt, '/user').yields({
expect(err).to.exist; code: 409
done();
});
}); });
it('should fail due to error in rest api', function(done) { adminDao.createUser(opt, function(err) {
var opt = { expect(err.message).to.contain('already taken');
emailAddress: emailAddress, expect(restDaoStub.post.calledOnce).to.be.true;
token: 'H45Z6D' done();
};
restDaoStub.post.withArgs(opt, '/user/validate').yields(new Error());
adminDao.validateUser(opt, function(err) {
expect(err).to.exist;
expect(restDaoStub.post.calledOnce).to.be.true;
done();
});
});
it('should work with no error object', function(done) {
var opt = {
emailAddress: emailAddress,
token: 'H45Z6D'
};
restDaoStub.post.withArgs(opt, '/user/validate').yields();
adminDao.validateUser(opt, function(err) {
expect(err).to.not.exist;
expect(restDaoStub.post.calledOnce).to.be.true;
done();
});
});
it('should work with 202', function(done) {
var opt = {
emailAddress: emailAddress,
token: 'H45Z6D'
};
restDaoStub.post.withArgs(opt, '/user/validate').yields({
code: 202
});
adminDao.validateUser(opt, function(err) {
expect(err).to.not.exist;
expect(restDaoStub.post.calledOnce).to.be.true;
done();
});
}); });
}); });
it('should fail due to unknown error', function(done) {
var opt = {
emailAddress: emailAddress,
password: password,
phone: '12345'
};
restDaoStub.post.withArgs(opt, '/user').yields(new Error());
adminDao.createUser(opt, function(err) {
expect(err).to.exist;
expect(restDaoStub.post.calledOnce).to.be.true;
done();
});
});
it('should work', function(done) {
var opt = {
emailAddress: emailAddress,
password: password,
phone: '12345'
};
restDaoStub.post.withArgs(opt, '/user').yields();
adminDao.createUser(opt, function(err) {
expect(err).to.not.exist;
expect(restDaoStub.post.calledOnce).to.be.true;
done();
});
});
});
describe('validateUser', function() {
it('should fail due to incomplete args', function(done) {
var opt = {
emailAddress: emailAddress
};
adminDao.validateUser(opt, function(err) {
expect(err).to.exist;
done();
});
});
it('should fail due to error in rest api', function(done) {
var opt = {
emailAddress: emailAddress,
token: 'H45Z6D'
};
restDaoStub.post.withArgs(opt, '/user/validate').yields(new Error());
adminDao.validateUser(opt, function(err) {
expect(err).to.exist;
expect(restDaoStub.post.calledOnce).to.be.true;
done();
});
});
it('should work with no error object', function(done) {
var opt = {
emailAddress: emailAddress,
token: 'H45Z6D'
};
restDaoStub.post.withArgs(opt, '/user/validate').yields();
adminDao.validateUser(opt, function(err) {
expect(err).to.not.exist;
expect(restDaoStub.post.calledOnce).to.be.true;
done();
});
});
it('should work with 202', function(done) {
var opt = {
emailAddress: emailAddress,
token: 'H45Z6D'
};
restDaoStub.post.withArgs(opt, '/user/validate').yields({
code: 202
});
adminDao.validateUser(opt, function(err) {
expect(err).to.not.exist;
expect(restDaoStub.post.calledOnce).to.be.true;
done();
});
});
}); });
}); });

View File

@ -1,212 +1,209 @@
define(function(require) { 'use strict';
'use strict';
var controller = require('js/app-controller'), var controller = require('../../src/js/app-controller'),
EmailDAO = require('js/dao/email-dao'), EmailDAO = require('../../src/js/dao/email-dao'),
OutboxBO = require('js/bo/outbox'), OutboxBO = require('../../src/js/bo/outbox'),
DeviceStorageDAO = require('js/dao/devicestorage-dao'), DeviceStorageDAO = require('../../src/js/dao/devicestorage-dao'),
UpdateHandler = require('js/util/update/update-handler'), UpdateHandler = require('../../src/js/util/update/update-handler'),
Auth = require('js/bo/auth'), Auth = require('../../src/js/bo/auth');
expect = chai.expect;
describe('App Controller unit tests', function() { describe('App Controller unit tests', function() {
var emailDaoStub, outboxStub, updateHandlerStub, appConfigStoreStub, devicestorageStub, isOnlineStub, authStub; var emailDaoStub, outboxStub, updateHandlerStub, appConfigStoreStub, devicestorageStub, isOnlineStub, authStub;
beforeEach(function() {
controller._emailDao = emailDaoStub = sinon.createStubInstance(EmailDAO);
controller._outboxBo = outboxStub = sinon.createStubInstance(OutboxBO);
controller._appConfigStore = appConfigStoreStub = sinon.createStubInstance(DeviceStorageDAO);
controller._userStorage = devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
controller._updateHandler = updateHandlerStub = sinon.createStubInstance(UpdateHandler);
controller._auth = authStub = sinon.createStubInstance(Auth);
isOnlineStub = sinon.stub(controller, 'isOnline');
});
afterEach(function() {
isOnlineStub.restore();
});
describe('buildModules', function() {
it('should work', function() {
controller.buildModules({
onError: function() {}
});
expect(controller._appConfigStore).to.exist;
expect(controller._auth).to.exist;
expect(controller._userStorage).to.exist;
expect(controller._invitationDao).to.exist;
expect(controller._keychain).to.exist;
expect(controller._pgp).to.exist;
expect(controller._pgpbuilder).to.exist;
expect(controller._emailDao).to.exist;
expect(controller._outboxBo).to.exist;
expect(controller._updateHandler).to.exist;
});
});
describe('start', function() {
it('should not explode', function(done) {
controller.start({
onError: function() {}
}, function(err) {
expect(err).to.not.exist;
done();
});
});
});
describe('onDisconnect', function() {
it('should work', function() {
controller.onDisconnect();
expect(emailDaoStub.onDisconnect.calledOnce).to.be.true;
});
});
describe('logout', function() {
it('should work', function(done) {
authStub.logout.yields();
emailDaoStub.onDisconnect.yields(new Error());
controller.onError = function(err) {
expect(err).to.exist;
expect(authStub.logout.calledOnce).to.be.true;
expect(emailDaoStub.onDisconnect.calledOnce).to.be.true;
done();
};
controller.logout();
});
});
describe('onConnect', function() {
beforeEach(function() {
controller._emailDao._account = {};
});
it('should not connect if offline', function(done) {
isOnlineStub.returns(false);
controller.onConnect(function(err) {
expect(err).to.not.exist;
done();
});
});
it('should not connect if account is not initialized', function(done) {
controller._emailDao._account = null;
controller.onConnect(function(err) {
expect(err).to.not.exist;
done();
});
});
it('should fail due to error in auth.getCredentials', function(done) {
isOnlineStub.returns(true);
authStub.getCredentials.yields(new Error());
controller.onConnect(function(err) {
expect(err).to.exist;
expect(authStub.getCredentials.calledOnce).to.be.true;
done();
});
});
it('should work', function(done) {
isOnlineStub.returns(true);
authStub.getCredentials.yields(null, {
emailAddress: 'asdf@example.com',
oauthToken: 'token',
sslCert: 'cert',
imap: {},
smtp: {}
});
emailDaoStub.onConnect.yields();
controller.onConnect(function(err) {
expect(err).to.not.exist;
expect(authStub.getCredentials.calledOnce).to.be.true;
expect(emailDaoStub.onConnect.calledOnce).to.be.true;
done();
});
});
});
describe('init', function() {
var onConnectStub, emailAddress;
beforeEach(function() { beforeEach(function() {
controller._emailDao = emailDaoStub = sinon.createStubInstance(EmailDAO); emailAddress = 'alice@bob.com';
controller._outboxBo = outboxStub = sinon.createStubInstance(OutboxBO);
controller._appConfigStore = appConfigStoreStub = sinon.createStubInstance(DeviceStorageDAO);
controller._userStorage = devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
controller._updateHandler = updateHandlerStub = sinon.createStubInstance(UpdateHandler);
controller._auth = authStub = sinon.createStubInstance(Auth);
isOnlineStub = sinon.stub(controller, 'isOnline'); // onConnect
onConnectStub = sinon.stub(controller, 'onConnect');
}); });
afterEach(function() { afterEach(function() {
isOnlineStub.restore(); onConnectStub.restore();
}); });
describe('buildModules', function() { it('should fail due to error in storage initialization', function(done) {
it('should work', function() { devicestorageStub.init.withArgs(undefined).yields({});
controller.buildModules({
onError: function() {} controller.init({}, function(err, keypair) {
}); expect(err).to.exist;
expect(controller._appConfigStore).to.exist; expect(keypair).to.not.exist;
expect(controller._auth).to.exist; expect(devicestorageStub.init.calledOnce).to.be.true;
expect(controller._userStorage).to.exist; expect(updateHandlerStub.update.calledOnce).to.be.false;
expect(controller._invitationDao).to.exist; done();
expect(controller._keychain).to.exist;
expect(controller._pgp).to.exist;
expect(controller._pgpbuilder).to.exist;
expect(controller._emailDao).to.exist;
expect(controller._outboxBo).to.exist;
expect(controller._updateHandler).to.exist;
}); });
}); });
describe('start', function() { it('should fail due to error in update handler', function(done) {
it('should not explode', function(done) { devicestorageStub.init.yields();
controller.start({ updateHandlerStub.update.yields({});
onError: function() {}
}, function(err) { controller.init({
expect(err).to.not.exist; emailAddress: emailAddress
done(); }, function(err, keypair) {
}); expect(err).to.exist;
expect(keypair).to.not.exist;
expect(updateHandlerStub.update.calledOnce).to.be.true;
expect(devicestorageStub.init.calledOnce).to.be.true;
done();
}); });
}); });
describe('onDisconnect', function() { it('should fail due to error in emailDao.init', function(done) {
it('should work', function() { devicestorageStub.init.yields();
controller.onDisconnect(); updateHandlerStub.update.yields();
emailDaoStub.init.yields({});
expect(emailDaoStub.onDisconnect.calledOnce).to.be.true; controller.init({
emailAddress: emailAddress
}, function(err, keypair) {
expect(err).to.exist;
expect(keypair).to.not.exist;
expect(updateHandlerStub.update.calledOnce).to.be.true;
expect(emailDaoStub.init.calledOnce).to.be.true;
expect(devicestorageStub.init.calledOnce).to.be.true;
done();
}); });
}); });
describe('logout', function() { it('should work and return a keypair', function(done) {
it('should work', function(done) { devicestorageStub.init.withArgs(emailAddress).yields();
authStub.logout.yields(); emailDaoStub.init.yields(null, {});
emailDaoStub.onDisconnect.yields(new Error()); updateHandlerStub.update.yields();
controller.onError = function(err) { controller.init({
expect(err).to.exist; emailAddress: emailAddress
expect(authStub.logout.calledOnce).to.be.true; }, function(err, keypair) {
expect(emailDaoStub.onDisconnect.calledOnce).to.be.true; expect(err).to.not.exist;
done(); expect(keypair).to.exist;
}; expect(updateHandlerStub.update.calledOnce).to.be.true;
expect(emailDaoStub.init.calledOnce).to.be.true;
controller.logout(); expect(devicestorageStub.init.calledOnce).to.be.true;
}); done();
});
describe('onConnect', function() {
beforeEach(function() {
controller._emailDao._account = {};
});
it('should not connect if offline', function(done) {
isOnlineStub.returns(false);
controller.onConnect(function(err) {
expect(err).to.not.exist;
done();
});
});
it('should not connect if account is not initialized', function(done) {
controller._emailDao._account = null;
controller.onConnect(function(err) {
expect(err).to.not.exist;
done();
});
});
it('should fail due to error in auth.getCredentials', function(done) {
isOnlineStub.returns(true);
authStub.getCredentials.yields(new Error());
controller.onConnect(function(err) {
expect(err).to.exist;
expect(authStub.getCredentials.calledOnce).to.be.true;
done();
});
});
it('should work', function(done) {
isOnlineStub.returns(true);
authStub.getCredentials.yields(null, {
emailAddress: 'asdf@example.com',
oauthToken: 'token',
sslCert: 'cert',
imap: {},
smtp: {}
});
emailDaoStub.onConnect.yields();
controller.onConnect(function(err) {
expect(err).to.not.exist;
expect(authStub.getCredentials.calledOnce).to.be.true;
expect(emailDaoStub.onConnect.calledOnce).to.be.true;
done();
});
});
});
describe('init', function() {
var onConnectStub, emailAddress;
beforeEach(function() {
emailAddress = 'alice@bob.com';
// onConnect
onConnectStub = sinon.stub(controller, 'onConnect');
});
afterEach(function() {
onConnectStub.restore();
});
it('should fail due to error in storage initialization', function(done) {
devicestorageStub.init.withArgs(undefined).yields({});
controller.init({}, function(err, keypair) {
expect(err).to.exist;
expect(keypair).to.not.exist;
expect(devicestorageStub.init.calledOnce).to.be.true;
expect(updateHandlerStub.update.calledOnce).to.be.false;
done();
});
});
it('should fail due to error in update handler', function(done) {
devicestorageStub.init.yields();
updateHandlerStub.update.yields({});
controller.init({
emailAddress: emailAddress
}, function(err, keypair) {
expect(err).to.exist;
expect(keypair).to.not.exist;
expect(updateHandlerStub.update.calledOnce).to.be.true;
expect(devicestorageStub.init.calledOnce).to.be.true;
done();
});
});
it('should fail due to error in emailDao.init', function(done) {
devicestorageStub.init.yields();
updateHandlerStub.update.yields();
emailDaoStub.init.yields({});
controller.init({
emailAddress: emailAddress
}, function(err, keypair) {
expect(err).to.exist;
expect(keypair).to.not.exist;
expect(updateHandlerStub.update.calledOnce).to.be.true;
expect(emailDaoStub.init.calledOnce).to.be.true;
expect(devicestorageStub.init.calledOnce).to.be.true;
done();
});
});
it('should work and return a keypair', function(done) {
devicestorageStub.init.withArgs(emailAddress).yields();
emailDaoStub.init.yields(null, {});
updateHandlerStub.update.yields();
controller.init({
emailAddress: emailAddress
}, function(err, keypair) {
expect(err).to.not.exist;
expect(keypair).to.exist;
expect(updateHandlerStub.update.calledOnce).to.be.true;
expect(emailDaoStub.init.calledOnce).to.be.true;
expect(devicestorageStub.init.calledOnce).to.be.true;
done();
});
}); });
}); });
}); });

View File

@ -1,377 +1,374 @@
define(function(require) { 'use strict';
'use strict';
var Auth = require('js/bo/auth'), var Auth = require('../../src/js/bo/auth'),
OAuth = require('js/util/oauth'), OAuth = require('../../src/js/util/oauth'),
PGP = require('js/crypto/pgp'), PGP = require('../../src/js/crypto/pgp'),
DeviceStorageDAO = require('js/dao/devicestorage-dao'), DeviceStorageDAO = require('../../src/js/dao/devicestorage-dao');
expect = chai.expect;
describe('Auth unit tests', function() { describe('Auth unit tests', function() {
// Constancts // Constancts
var EMAIL_ADDR_DB_KEY = 'emailaddress'; var EMAIL_ADDR_DB_KEY = 'emailaddress';
var USERNAME_DB_KEY = 'username'; var USERNAME_DB_KEY = 'username';
var REALNAME_DB_KEY = 'realname'; var REALNAME_DB_KEY = 'realname';
var PASSWD_DB_KEY = 'password'; var PASSWD_DB_KEY = 'password';
var PROVIDER_DB_KEY = 'provider'; var PROVIDER_DB_KEY = 'provider';
var IMAP_DB_KEY = 'imap'; var IMAP_DB_KEY = 'imap';
var SMTP_DB_KEY = 'smtp'; var SMTP_DB_KEY = 'smtp';
// SUT // SUT
var auth; var auth;
// Dependencies // Dependencies
var storageStub, oauthStub, pgpStub; var storageStub, oauthStub, pgpStub;
// test data // test data
var emailAddress = 'bla@blubb.com'; var emailAddress = 'bla@blubb.com';
var password = 'passwordpasswordpassword'; var password = 'passwordpasswordpassword';
var encryptedPassword = 'pgppasswordpgppassword'; var encryptedPassword = 'pgppasswordpgppassword';
var oauthToken = 'tokentokentokentoken'; var oauthToken = 'tokentokentokentoken';
var provider = 'gmail'; var provider = 'gmail';
var realname = 'Bla Blubb'; var realname = 'Bla Blubb';
var username = 'bla'; var username = 'bla';
var imap = { var imap = {
host: 'mail.blablubb.com', host: 'mail.blablubb.com',
port: 123, port: 123,
secure: true, secure: true,
ca: 'PEMPEMPEMPEMPEMPEMPEMPEMPEMPEM' ca: 'PEMPEMPEMPEMPEMPEMPEMPEMPEMPEM'
}; };
var smtp = { var smtp = {
host: 'mail.blablubb.com', host: 'mail.blablubb.com',
port: 456, port: 456,
secure: true, secure: true,
ca: 'PEMPEMPEMPEMPEMPEMPEMPEMPEMPEM' ca: 'PEMPEMPEMPEMPEMPEMPEMPEMPEMPEM'
}; };
beforeEach(function() { beforeEach(function() {
storageStub = sinon.createStubInstance(DeviceStorageDAO); storageStub = sinon.createStubInstance(DeviceStorageDAO);
oauthStub = sinon.createStubInstance(OAuth); oauthStub = sinon.createStubInstance(OAuth);
pgpStub = sinon.createStubInstance(PGP); pgpStub = sinon.createStubInstance(PGP);
auth = new Auth(storageStub, oauthStub, pgpStub); auth = new Auth(storageStub, oauthStub, pgpStub);
});
describe('#getCredentials', function() {
it('should load credentials and retrieve credentials from cfg', function(done) {
storageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY, 0, null).yieldsAsync(null, [emailAddress]);
storageStub.listItems.withArgs(PASSWD_DB_KEY, 0, null).yieldsAsync(null, [encryptedPassword]);
storageStub.listItems.withArgs(PROVIDER_DB_KEY, 0, null).yieldsAsync(null, [provider]);
storageStub.listItems.withArgs(USERNAME_DB_KEY, 0, null).yieldsAsync(null, [username]);
storageStub.listItems.withArgs(REALNAME_DB_KEY, 0, null).yieldsAsync(null, [realname]);
storageStub.listItems.withArgs(IMAP_DB_KEY, 0, null).yieldsAsync(null, [imap]);
storageStub.listItems.withArgs(SMTP_DB_KEY, 0, null).yieldsAsync(null, [smtp]);
pgpStub.decrypt.withArgs(encryptedPassword, undefined).yields(null, password);
auth.getCredentials(function(err, cred) {
expect(err).to.not.exist;
expect(auth.provider).to.equal(provider);
expect(auth.emailAddress).to.equal(emailAddress);
expect(auth.password).to.equal(password);
expect(cred.imap.host).to.equal(imap.host);
expect(cred.imap.port).to.equal(imap.port);
expect(cred.imap.secure).to.equal(imap.secure);
expect(cred.imap.ca).to.equal(imap.ca);
expect(cred.imap.auth.user).to.equal(username);
expect(cred.imap.auth.pass).to.equal(password);
expect(cred.smtp.host).to.equal(smtp.host);
expect(cred.smtp.port).to.equal(smtp.port);
expect(cred.smtp.secure).to.equal(smtp.secure);
expect(cred.smtp.ca).to.equal(smtp.ca);
expect(cred.smtp.auth.user).to.equal(username);
expect(cred.smtp.auth.pass).to.equal(password);
expect(storageStub.listItems.callCount).to.equal(7);
expect(pgpStub.decrypt.calledOnce).to.be.true;
done();
});
});
});
describe('#setCredentials', function() {
it('should set the credentials', function() {
auth.setCredentials({
provider: 'albhsvadlbvsdalbsadflb',
emailAddress: emailAddress,
username: username,
realname: realname,
password: password,
imap: imap,
smtp: smtp
});
expect(auth.provider).to.equal('albhsvadlbvsdalbsadflb');
expect(auth.emailAddress).to.equal(emailAddress);
expect(auth.username).to.equal(username);
expect(auth.realname).to.equal(realname);
expect(auth.password).to.equal(password);
expect(auth.smtp).to.equal(smtp);
expect(auth.imap).to.equal(imap);
expect(auth.credentialsDirty).to.be.true;
}); });
describe('#getCredentials', function() { });
it('should load credentials and retrieve credentials from cfg', function(done) {
storageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY, 0, null).yieldsAsync(null, [emailAddress]);
storageStub.listItems.withArgs(PASSWD_DB_KEY, 0, null).yieldsAsync(null, [encryptedPassword]);
storageStub.listItems.withArgs(PROVIDER_DB_KEY, 0, null).yieldsAsync(null, [provider]);
storageStub.listItems.withArgs(USERNAME_DB_KEY, 0, null).yieldsAsync(null, [username]);
storageStub.listItems.withArgs(REALNAME_DB_KEY, 0, null).yieldsAsync(null, [realname]);
storageStub.listItems.withArgs(IMAP_DB_KEY, 0, null).yieldsAsync(null, [imap]);
storageStub.listItems.withArgs(SMTP_DB_KEY, 0, null).yieldsAsync(null, [smtp]);
pgpStub.decrypt.withArgs(encryptedPassword, undefined).yields(null, password);
auth.getCredentials(function(err, cred) { describe('#storeCredentials', function() {
expect(err).to.not.exist; it('should persist ALL the things!', function(done) {
auth.credentialsDirty = true;
auth.emailAddress = emailAddress;
auth.username = username;
auth.realname = realname;
auth.password = password;
auth.smtp = smtp;
auth.imap = imap;
auth.provider = provider;
expect(auth.provider).to.equal(provider); storageStub.storeList.withArgs([encryptedPassword], PASSWD_DB_KEY).yieldsAsync();
expect(auth.emailAddress).to.equal(emailAddress); storageStub.storeList.withArgs([emailAddress], EMAIL_ADDR_DB_KEY).yieldsAsync();
expect(auth.password).to.equal(password); storageStub.storeList.withArgs([provider], PROVIDER_DB_KEY).yieldsAsync();
storageStub.storeList.withArgs([username], USERNAME_DB_KEY).yieldsAsync();
storageStub.storeList.withArgs([realname], REALNAME_DB_KEY).yieldsAsync();
storageStub.storeList.withArgs([imap], IMAP_DB_KEY).yieldsAsync();
storageStub.storeList.withArgs([smtp], SMTP_DB_KEY).yieldsAsync();
pgpStub.encrypt.withArgs(password).yields(null, encryptedPassword);
expect(cred.imap.host).to.equal(imap.host); auth.storeCredentials(function(err) {
expect(cred.imap.port).to.equal(imap.port); expect(err).to.not.exist;
expect(cred.imap.secure).to.equal(imap.secure);
expect(cred.imap.ca).to.equal(imap.ca);
expect(cred.imap.auth.user).to.equal(username);
expect(cred.imap.auth.pass).to.equal(password);
expect(cred.smtp.host).to.equal(smtp.host); expect(storageStub.storeList.callCount).to.equal(7);
expect(cred.smtp.port).to.equal(smtp.port); expect(pgpStub.encrypt.calledOnce).to.be.true;
expect(cred.smtp.secure).to.equal(smtp.secure);
expect(cred.smtp.ca).to.equal(smtp.ca);
expect(cred.smtp.auth.user).to.equal(username);
expect(cred.smtp.auth.pass).to.equal(password);
expect(storageStub.listItems.callCount).to.equal(7); done();
expect(pgpStub.decrypt.calledOnce).to.be.true; });
});
});
done(); describe('#getOAuthToken', function() {
}); it('should refresh token with known email address', function(done) {
auth.emailAddress = emailAddress;
auth.oauthToken = 'oldToken';
oauthStub.refreshToken.withArgs({
emailAddress: emailAddress,
oldToken: 'oldToken'
}).yieldsAsync(null, oauthToken);
auth.getOAuthToken(function(err) {
expect(err).to.not.exist;
expect(auth.emailAddress).to.equal(emailAddress);
expect(auth.oauthToken).to.equal(oauthToken);
expect(oauthStub.refreshToken.calledOnce).to.be.true;
done();
}); });
}); });
describe('#setCredentials', function() { it('should fetch token with known email address', function(done) {
it('should set the credentials', function() { auth.emailAddress = emailAddress;
auth.setCredentials({ oauthStub.getOAuthToken.withArgs(emailAddress).yieldsAsync(null, oauthToken);
provider: 'albhsvadlbvsdalbsadflb',
emailAddress: emailAddress,
username: username,
realname: realname,
password: password,
imap: imap,
smtp: smtp
});
expect(auth.provider).to.equal('albhsvadlbvsdalbsadflb'); auth.getOAuthToken(function(err) {
expect(err).to.not.exist;
expect(auth.emailAddress).to.equal(emailAddress); expect(auth.emailAddress).to.equal(emailAddress);
expect(auth.oauthToken).to.equal(oauthToken);
expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
done();
});
});
it('should fetch token with unknown email address', function(done) {
oauthStub.getOAuthToken.withArgs(undefined).yieldsAsync(null, oauthToken);
oauthStub.queryEmailAddress.withArgs(oauthToken).yieldsAsync(null, emailAddress);
auth.getOAuthToken(function(err) {
expect(err).to.not.exist;
expect(auth.emailAddress).to.equal(emailAddress);
expect(auth.oauthToken).to.equal(oauthToken);
expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
expect(oauthStub.queryEmailAddress.calledOnce).to.be.true;
done();
});
});
it('should fail when email address fetch fails', function(done) {
oauthStub.getOAuthToken.yieldsAsync(null, oauthToken);
oauthStub.queryEmailAddress.yieldsAsync(new Error());
auth.getOAuthToken(function(err) {
expect(err).to.exist;
expect(auth.emailAddress).to.not.exist;
expect(auth.oauthToken).to.not.exist;
expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
expect(oauthStub.queryEmailAddress.calledOnce).to.be.true;
done();
});
});
it('should fail when oauth fetch fails', function(done) {
oauthStub.getOAuthToken.yieldsAsync(new Error());
auth.getOAuthToken(function(err) {
expect(err).to.exist;
expect(auth.emailAddress).to.not.exist;
expect(auth.oauthToken).to.not.exist;
expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
expect(oauthStub.queryEmailAddress.called).to.be.false;
done();
});
});
});
describe('#_loadCredentials', function() {
it('should work', function(done) {
storageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY, 0, null).yieldsAsync(null, [emailAddress]);
storageStub.listItems.withArgs(PASSWD_DB_KEY, 0, null).yieldsAsync(null, [encryptedPassword]);
storageStub.listItems.withArgs(PROVIDER_DB_KEY, 0, null).yieldsAsync(null, [provider]);
storageStub.listItems.withArgs(USERNAME_DB_KEY, 0, null).yieldsAsync(null, [username]);
storageStub.listItems.withArgs(REALNAME_DB_KEY, 0, null).yieldsAsync(null, [realname]);
storageStub.listItems.withArgs(IMAP_DB_KEY, 0, null).yieldsAsync(null, [imap]);
storageStub.listItems.withArgs(SMTP_DB_KEY, 0, null).yieldsAsync(null, [smtp]);
auth._loadCredentials(function(err) {
expect(err).to.not.exist;
expect(auth.emailAddress).to.equal(emailAddress);
expect(auth.password).to.equal(encryptedPassword);
expect(auth.provider).to.equal(provider);
expect(auth.imap).to.equal(imap);
expect(auth.smtp).to.equal(smtp);
expect(auth.username).to.equal(username); expect(auth.username).to.equal(username);
expect(auth.realname).to.equal(realname); expect(auth.realname).to.equal(realname);
expect(auth.password).to.equal(password);
expect(auth.smtp).to.equal(smtp);
expect(auth.imap).to.equal(imap);
expect(auth.credentialsDirty).to.be.true;
});
}); expect(auth.passwordNeedsDecryption).to.be.true;
describe('#storeCredentials', function() { expect(storageStub.listItems.callCount).to.equal(7);
it('should persist ALL the things!', function(done) {
auth.credentialsDirty = true;
auth.emailAddress = emailAddress;
auth.username = username;
auth.realname = realname;
auth.password = password;
auth.smtp = smtp;
auth.imap = imap;
auth.provider = provider;
storageStub.storeList.withArgs([encryptedPassword], PASSWD_DB_KEY).yieldsAsync(); done();
storageStub.storeList.withArgs([emailAddress], EMAIL_ADDR_DB_KEY).yieldsAsync();
storageStub.storeList.withArgs([provider], PROVIDER_DB_KEY).yieldsAsync();
storageStub.storeList.withArgs([username], USERNAME_DB_KEY).yieldsAsync();
storageStub.storeList.withArgs([realname], REALNAME_DB_KEY).yieldsAsync();
storageStub.storeList.withArgs([imap], IMAP_DB_KEY).yieldsAsync();
storageStub.storeList.withArgs([smtp], SMTP_DB_KEY).yieldsAsync();
pgpStub.encrypt.withArgs(password).yields(null, encryptedPassword);
auth.storeCredentials(function(err) {
expect(err).to.not.exist;
expect(storageStub.storeList.callCount).to.equal(7);
expect(pgpStub.encrypt.calledOnce).to.be.true;
done();
});
}); });
}); });
describe('#getOAuthToken', function() { it('should fail', function(done) {
it('should refresh token with known email address', function(done) { storageStub.listItems.yieldsAsync(new Error());
auth.emailAddress = emailAddress;
auth.oauthToken = 'oldToken';
oauthStub.refreshToken.withArgs({ auth._loadCredentials(function(err) {
emailAddress: emailAddress, expect(err).to.exist;
oldToken: 'oldToken' expect(auth.emailAddress).to.not.exist;
}).yieldsAsync(null, oauthToken); expect(auth.password).to.not.exist;
expect(auth.provider).to.not.exist;
expect(auth.imap).to.not.exist;
expect(auth.smtp).to.not.exist;
expect(auth.username).to.not.exist;
expect(auth.realname).to.not.exist;
auth.getOAuthToken(function(err) { expect(storageStub.listItems.calledOnce).to.be.true;
expect(err).to.not.exist;
expect(auth.emailAddress).to.equal(emailAddress);
expect(auth.oauthToken).to.equal(oauthToken);
expect(oauthStub.refreshToken.calledOnce).to.be.true; done();
done();
});
});
it('should fetch token with known email address', function(done) {
auth.emailAddress = emailAddress;
oauthStub.getOAuthToken.withArgs(emailAddress).yieldsAsync(null, oauthToken);
auth.getOAuthToken(function(err) {
expect(err).to.not.exist;
expect(auth.emailAddress).to.equal(emailAddress);
expect(auth.oauthToken).to.equal(oauthToken);
expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
done();
});
});
it('should fetch token with unknown email address', function(done) {
oauthStub.getOAuthToken.withArgs(undefined).yieldsAsync(null, oauthToken);
oauthStub.queryEmailAddress.withArgs(oauthToken).yieldsAsync(null, emailAddress);
auth.getOAuthToken(function(err) {
expect(err).to.not.exist;
expect(auth.emailAddress).to.equal(emailAddress);
expect(auth.oauthToken).to.equal(oauthToken);
expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
expect(oauthStub.queryEmailAddress.calledOnce).to.be.true;
done();
});
});
it('should fail when email address fetch fails', function(done) {
oauthStub.getOAuthToken.yieldsAsync(null, oauthToken);
oauthStub.queryEmailAddress.yieldsAsync(new Error());
auth.getOAuthToken(function(err) {
expect(err).to.exist;
expect(auth.emailAddress).to.not.exist;
expect(auth.oauthToken).to.not.exist;
expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
expect(oauthStub.queryEmailAddress.calledOnce).to.be.true;
done();
});
});
it('should fail when oauth fetch fails', function(done) {
oauthStub.getOAuthToken.yieldsAsync(new Error());
auth.getOAuthToken(function(err) {
expect(err).to.exist;
expect(auth.emailAddress).to.not.exist;
expect(auth.oauthToken).to.not.exist;
expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
expect(oauthStub.queryEmailAddress.called).to.be.false;
done();
});
}); });
}); });
});
describe('#_loadCredentials', function() { describe('#handleCertificateUpdate', function() {
it('should work', function(done) { var storeCredentialsStub;
storageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY, 0, null).yieldsAsync(null, [emailAddress]); var dummyCert = 'cert';
storageStub.listItems.withArgs(PASSWD_DB_KEY, 0, null).yieldsAsync(null, [encryptedPassword]);
storageStub.listItems.withArgs(PROVIDER_DB_KEY, 0, null).yieldsAsync(null, [provider]);
storageStub.listItems.withArgs(USERNAME_DB_KEY, 0, null).yieldsAsync(null, [username]);
storageStub.listItems.withArgs(REALNAME_DB_KEY, 0, null).yieldsAsync(null, [realname]);
storageStub.listItems.withArgs(IMAP_DB_KEY, 0, null).yieldsAsync(null, [imap]);
storageStub.listItems.withArgs(SMTP_DB_KEY, 0, null).yieldsAsync(null, [smtp]);
auth._loadCredentials(function(err) { function onConnectDummy() {}
expect(err).to.not.exist;
expect(auth.emailAddress).to.equal(emailAddress);
expect(auth.password).to.equal(encryptedPassword);
expect(auth.provider).to.equal(provider);
expect(auth.imap).to.equal(imap);
expect(auth.smtp).to.equal(smtp);
expect(auth.username).to.equal(username);
expect(auth.realname).to.equal(realname);
expect(auth.passwordNeedsDecryption).to.be.true; beforeEach(function() {
storeCredentialsStub = sinon.stub(auth, 'storeCredentials');
expect(storageStub.listItems.callCount).to.equal(7);
done();
});
});
it('should fail', function(done) {
storageStub.listItems.yieldsAsync(new Error());
auth._loadCredentials(function(err) {
expect(err).to.exist;
expect(auth.emailAddress).to.not.exist;
expect(auth.password).to.not.exist;
expect(auth.provider).to.not.exist;
expect(auth.imap).to.not.exist;
expect(auth.smtp).to.not.exist;
expect(auth.username).to.not.exist;
expect(auth.realname).to.not.exist;
expect(storageStub.listItems.calledOnce).to.be.true;
done();
});
});
}); });
describe('#handleCertificateUpdate', function() { it('should work for Trust on first use', function(done) {
var storeCredentialsStub; auth.imap = {};
var dummyCert = 'cert'; storeCredentialsStub.yields();
function onConnectDummy() {} function callback(err) {
expect(err).to.not.exist;
expect(storeCredentialsStub.callCount).to.equal(1);
done();
}
auth.handleCertificateUpdate('imap', onConnectDummy, callback, dummyCert);
});
beforeEach(function() { it('should work for stored cert', function() {
storeCredentialsStub = sinon.stub(auth, 'storeCredentials'); auth.imap = {
}); ca: dummyCert
};
storeCredentialsStub.yields();
it('should work for Trust on first use', function(done) { auth.handleCertificateUpdate('imap', onConnectDummy, onConnectDummy, dummyCert);
auth.imap = {}; expect(storeCredentialsStub.callCount).to.equal(0);
storeCredentialsStub.yields(); });
function callback(err) { it('should work for pinned cert', function(done) {
expect(err).to.not.exist; auth.imap = {
expect(storeCredentialsStub.callCount).to.equal(1); ca: 'other',
done(); pinned: true
} };
auth.handleCertificateUpdate('imap', onConnectDummy, callback, dummyCert); storeCredentialsStub.yields();
});
it('should work for stored cert', function() { function callback(err) {
auth.imap = { expect(err).to.exist;
ca: dummyCert expect(err.message).to.exist;
};
storeCredentialsStub.yields();
auth.handleCertificateUpdate('imap', onConnectDummy, onConnectDummy, dummyCert);
expect(storeCredentialsStub.callCount).to.equal(0); expect(storeCredentialsStub.callCount).to.equal(0);
}); done();
}
auth.handleCertificateUpdate('imap', onConnectDummy, callback, dummyCert);
});
it('should work for pinned cert', function(done) { it('should work for updated cert', function(done) {
auth.imap = { auth.imap = {
ca: 'other', ca: 'other'
pinned: true };
}; storeCredentialsStub.yields();
storeCredentialsStub.yields();
function callback(err) { function callback(err) {
if (err && err.callback) {
expect(err).to.exist; expect(err).to.exist;
expect(err.message).to.exist; expect(err.message).to.exist;
expect(storeCredentialsStub.callCount).to.equal(0); expect(storeCredentialsStub.callCount).to.equal(0);
err.callback(true);
} else {
expect(storeCredentialsStub.callCount).to.equal(1);
done(); done();
} }
auth.handleCertificateUpdate('imap', onConnectDummy, callback, dummyCert); }
});
it('should work for updated cert', function(done) { function onConnect(callback) {
auth.imap = { callback();
ca: 'other' }
};
storeCredentialsStub.yields();
function callback(err) { auth.handleCertificateUpdate('imap', onConnect, callback, dummyCert);
if (err && err.callback) { });
expect(err).to.exist; });
expect(err.message).to.exist;
expect(storeCredentialsStub.callCount).to.equal(0);
err.callback(true);
} else {
expect(storeCredentialsStub.callCount).to.equal(1);
done();
}
}
function onConnect(callback) { describe('#logout', function() {
callback(); it('should fail to to error in calling db clear', function(done) {
} storageStub.clear.yields(new Error());
auth.handleCertificateUpdate('imap', onConnect, callback, dummyCert); auth.logout(function(err) {
expect(err).to.exist;
done();
}); });
}); });
describe('#logout', function() { it('should work', function(done) {
it('should fail to to error in calling db clear', function(done) { storageStub.clear.yields();
storageStub.clear.yields(new Error());
auth.logout(function(err) { auth.logout(function(err) {
expect(err).to.exist; expect(err).to.not.exist;
done(); expect(auth.password).to.be.undefined;
}); expect(auth.initialized).to.be.undefined;
}); expect(auth.credentialsDirty).to.be.undefined;
expect(auth.passwordNeedsDecryption).to.be.undefined;
it('should work', function(done) { done();
storageStub.clear.yields();
auth.logout(function(err) {
expect(err).to.not.exist;
expect(auth.password).to.be.undefined;
expect(auth.initialized).to.be.undefined;
expect(auth.credentialsDirty).to.be.undefined;
expect(auth.passwordNeedsDecryption).to.be.undefined;
done();
});
}); });
}); });
}); });

View File

@ -1,67 +1,64 @@
define(function(require) { 'use strict';
'use strict';
var btnHandler = require('js/util/backbutton-handler'), var btnHandler = require('../../src/js/util/backbutton-handler');
expect = chai.expect;
describe('Backbutton Handler', function() { describe('Backbutton Handler', function() {
chai.Assertion.includeStack = true; chai.Assertion.includeStack = true;
var scope, event; var scope, event;
beforeEach(function() { beforeEach(function() {
scope = { scope = {
state: {}, state: {},
$apply: function() {} $apply: function() {}
}; };
event = new CustomEvent('backbutton'); event = new CustomEvent('backbutton');
// this is a precondition for the test. throw an exception // this is a precondition for the test. throw an exception
// if this would produce side effects // if this would produce side effects
expect(navigator.app).to.not.exist; expect(navigator.app).to.not.exist;
navigator.app = {}; navigator.app = {};
btnHandler.attachHandler(scope); btnHandler.attachHandler(scope);
btnHandler.start(); btnHandler.start();
}); });
afterEach(function() { afterEach(function() {
btnHandler.stop(); btnHandler.stop();
delete navigator.app; delete navigator.app;
}); });
it('should close lightbox', function() { it('should close lightbox', function() {
scope.state.lightbox = 'asd'; scope.state.lightbox = 'asd';
document.dispatchEvent(event); document.dispatchEvent(event);
expect(scope.state.lightbox).to.be.undefined; expect(scope.state.lightbox).to.be.undefined;
}); });
it('should close reader', function() { it('should close reader', function() {
scope.state.read = { scope.state.read = {
open: true, open: true,
toggle: function(state) { toggle: function(state) {
scope.state.read.open = state; scope.state.read.open = state;
} }
}; };
document.dispatchEvent(event); document.dispatchEvent(event);
expect(scope.state.read.open).to.be.false; expect(scope.state.read.open).to.be.false;
}); });
it('should close navigation', function() { it('should close navigation', function() {
scope.state.nav = { scope.state.nav = {
open: true, open: true,
toggle: function(state) { toggle: function(state) {
scope.state.nav.open = state; scope.state.nav.open = state;
} }
}; };
document.dispatchEvent(event); document.dispatchEvent(event);
expect(scope.state.nav.open).to.be.false; expect(scope.state.nav.open).to.be.false;
}); });
it('should close app', function(done) { it('should close app', function(done) {
navigator.app.exitApp = done; navigator.app.exitApp = done;
document.dispatchEvent(event); document.dispatchEvent(event);
});
}); });
}); });

View File

@ -1,413 +1,410 @@
define(function(require) { 'use strict';
'use strict';
var ConnectionDoctor = require('js/util/connection-doctor'), var TCPSocket = require('tcp-socket'),
TCPSocket = require('tcp-socket'), ImapClient = require('imap-client'),
ImapClient = require('imap-client'), SmtpClient = require('wo-smtpclient'),
SmtpClient = require('smtpclient'), ConnectionDoctor = require('../../src/js/util/connection-doctor'),
cfg = require('js/app-config').config, cfg = require('../../src/js/app-config').config;
expect = chai.expect;
describe('Connection Doctor', function() { describe('Connection Doctor', function() {
var doctor; var doctor;
var socketStub, imapStub, smtpStub, credentials; var socketStub, imapStub, smtpStub, credentials;
beforeEach(function() { beforeEach(function() {
// //
// Stubs // Stubs
// //
// there is no socket shim for for this use case, use dummy object // there is no socket shim for for this use case, use dummy object
socketStub = { socketStub = {
close: function() { close: function() {
this.onclose(); this.onclose();
}
};
imapStub = sinon.createStubInstance(ImapClient);
smtpStub = sinon.createStubInstance(SmtpClient);
//
// Fixture
//
credentials = {
imap: {
host: 'asd',
port: 1234,
secure: true,
ca: 'cert'
},
smtp: {
host: 'qwe',
port: 5678,
secure: false,
ca: 'cert'
},
username: 'username',
password: 'password'
};
sinon.stub(TCPSocket, 'open').returns(socketStub); // convenience constructors suck
//
// Setup SUT
//
doctor = new ConnectionDoctor();
doctor.configure(credentials);
doctor._imap = imapStub;
doctor._smtp = smtpStub;
});
afterEach(function() {
TCPSocket.open.restore();
});
describe('#_checkOnline', function() {
it('should check if browser is online', function(done) {
doctor._checkOnline(function(error) {
if (navigator.onLine) {
expect(error).to.not.exist;
} else {
expect(error).to.exist;
expect(error.code).to.equal(ConnectionDoctor.OFFLINE);
} }
}; done();
});
});
});
imapStub = sinon.createStubInstance(ImapClient); describe('#_checkReachable', function() {
smtpStub = sinon.createStubInstance(SmtpClient); it('should be able to reach the host w/o cert', function(done) {
credentials.imap.ca = undefined;
// doctor._checkReachable(credentials.imap, function(error) {
// Fixture expect(error).to.not.exist;
// expect(TCPSocket.open.calledOnce).to.be.true;
credentials = { expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
imap: { binaryType: 'arraybuffer',
host: 'asd', useSecureTransport: credentials.imap.secure,
port: 1234, ca: credentials.imap.ca
secure: true, })).to.be.true;
ca: 'cert'
},
smtp: {
host: 'qwe',
port: 5678,
secure: false,
ca: 'cert'
},
username: 'username',
password: 'password'
};
sinon.stub(TCPSocket, 'open').returns(socketStub); // convenience constructors suck done();
});
// socketStub.oncert();
// Setup SUT socketStub.onopen();
//
doctor = new ConnectionDoctor();
doctor.configure(credentials);
doctor._imap = imapStub;
doctor._smtp = smtpStub;
}); });
afterEach(function() { it('should catch Mozilla TCPSocket exception', function(done) {
TCPSocket.open.restore(); // Mozilla forbids extensions to the TCPSocket object
Object.defineProperty(socketStub, 'oncert', {
set: function() {
throw 'Mozilla specific behavior';
}
});
doctor._checkReachable(credentials.imap, function(error) {
expect(error).to.not.exist;
expect(TCPSocket.open.calledOnce).to.be.true;
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
binaryType: 'arraybuffer',
useSecureTransport: credentials.imap.secure,
ca: credentials.imap.ca
})).to.be.true;
done();
});
socketStub.onopen();
}); });
describe('#_checkOnline', function() { it('should fail w/ wrong cert', function(done) {
it('should check if browser is online', function(done) { doctor._checkReachable(credentials.imap, function(error) {
doctor._checkOnline(function(error) { expect(error).to.exist;
if (navigator.onLine) { expect(error.code).to.equal(ConnectionDoctor.TLS_WRONG_CERT);
expect(error).to.not.exist; expect(TCPSocket.open.calledOnce).to.be.true;
} else { expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
expect(error).to.exist; binaryType: 'arraybuffer',
expect(error.code).to.equal(ConnectionDoctor.OFFLINE); useSecureTransport: credentials.imap.secure,
} ca: credentials.imap.ca
done(); })).to.be.true;
});
done();
});
socketStub.oncert();
socketStub.onerror();
socketStub.onclose();
});
it('should fail w/ host unreachable', function(done) {
doctor._checkReachable(credentials.imap, function(error) {
expect(error).to.exist;
expect(error.code).to.equal(ConnectionDoctor.HOST_UNREACHABLE);
expect(TCPSocket.open.calledOnce).to.be.true;
done();
});
socketStub.onerror({
data: new Error()
});
socketStub.onclose();
});
it('should fail w/ timeout', function(done) {
var origTimeout = cfg.connDocTimeout; // remember timeout from the config to reset it on done
cfg.connDocTimeout = 20; // set to 20ms for the test
doctor._checkReachable(credentials.imap, function(error) {
expect(error).to.exist;
expect(error.code).to.equal(ConnectionDoctor.HOST_TIMEOUT);
expect(TCPSocket.open.calledOnce).to.be.true;
cfg.connDocTimeout = origTimeout;
done();
});
});
});
describe('#_checkImap', function() {
it('should perform IMAP login, list folders, logout', function(done) {
imapStub.login.yieldsAsync();
imapStub.listWellKnownFolders.yieldsAsync(null, {
Inbox: [{}]
});
imapStub.logout.yieldsAsync();
doctor._checkImap(function(error) {
expect(error).to.not.exist;
expect(imapStub.login.calledOnce).to.be.true;
expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
expect(imapStub.logout.calledOnce).to.be.true;
done();
}); });
}); });
describe('#_checkReachable', function() { it('should fail w/ generic error on logout', function(done) {
it('should be able to reach the host w/o cert', function(done) { imapStub.login.yieldsAsync();
credentials.imap.ca = undefined; imapStub.listWellKnownFolders.yieldsAsync(null, {
Inbox: [{}]
doctor._checkReachable(credentials.imap, function(error) {
expect(error).to.not.exist;
expect(TCPSocket.open.calledOnce).to.be.true;
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
binaryType: 'arraybuffer',
useSecureTransport: credentials.imap.secure,
ca: credentials.imap.ca
})).to.be.true;
done();
});
socketStub.oncert();
socketStub.onopen();
}); });
it('should catch Mozilla TCPSocket exception', function(done) { doctor._checkImap(function(error) {
// Mozilla forbids extensions to the TCPSocket object expect(error).to.exist;
Object.defineProperty(socketStub, 'oncert', { expect(error.code).to.equal(ConnectionDoctor.GENERIC_ERROR);
set: function() { expect(error.underlyingError).to.exist;
throw 'Mozilla specific behavior'; expect(imapStub.login.calledOnce).to.be.true;
} expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
}); expect(imapStub.logout.calledOnce).to.be.true;
doctor._checkReachable(credentials.imap, function(error) { done();
expect(error).to.not.exist;
expect(TCPSocket.open.calledOnce).to.be.true;
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
binaryType: 'arraybuffer',
useSecureTransport: credentials.imap.secure,
ca: credentials.imap.ca
})).to.be.true;
done();
});
socketStub.onopen();
}); });
it('should fail w/ wrong cert', function(done) { setTimeout(function() {
doctor._checkReachable(credentials.imap, function(error) { // this error is thrown while we're waiting for the logout
expect(error).to.exist; imapStub.onError(new Error());
expect(error.code).to.equal(ConnectionDoctor.TLS_WRONG_CERT); }, 50);
expect(TCPSocket.open.calledOnce).to.be.true; });
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
binaryType: 'arraybuffer',
useSecureTransport: credentials.imap.secure,
ca: credentials.imap.ca
})).to.be.true;
done(); it('should fail w/ generic error on inbox missing', function(done) {
}); imapStub.login.yieldsAsync();
imapStub.listWellKnownFolders.yieldsAsync(null, {
socketStub.oncert(); Inbox: []
socketStub.onerror();
socketStub.onclose();
}); });
it('should fail w/ host unreachable', function(done) { doctor._checkImap(function(error) {
doctor._checkReachable(credentials.imap, function(error) { expect(error).to.exist;
expect(error).to.exist; expect(error.code).to.equal(ConnectionDoctor.NO_INBOX);
expect(error.code).to.equal(ConnectionDoctor.HOST_UNREACHABLE); expect(imapStub.login.calledOnce).to.be.true;
expect(TCPSocket.open.calledOnce).to.be.true; expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
expect(imapStub.logout.called).to.be.false;
done(); done();
});
socketStub.onerror({
data: new Error()
});
socketStub.onclose();
});
it('should fail w/ timeout', function(done) {
var origTimeout = cfg.connDocTimeout; // remember timeout from the config to reset it on done
cfg.connDocTimeout = 20; // set to 20ms for the test
doctor._checkReachable(credentials.imap, function(error) {
expect(error).to.exist;
expect(error.code).to.equal(ConnectionDoctor.HOST_TIMEOUT);
expect(TCPSocket.open.calledOnce).to.be.true;
cfg.connDocTimeout = origTimeout;
done();
});
}); });
}); });
describe('#_checkImap', function() { it('should fail w/ generic error on listing folders fails', function(done) {
it('should perform IMAP login, list folders, logout', function(done) { imapStub.login.yieldsAsync();
imapStub.login.yieldsAsync(); imapStub.listWellKnownFolders.yieldsAsync(new Error());
imapStub.listWellKnownFolders.yieldsAsync(null, {
Inbox: [{}]
});
imapStub.logout.yieldsAsync();
doctor._checkImap(function(error) { doctor._checkImap(function(error) {
expect(error).to.not.exist; expect(error).to.exist;
expect(imapStub.login.calledOnce).to.be.true; expect(error.code).to.equal(ConnectionDoctor.GENERIC_ERROR);
expect(imapStub.listWellKnownFolders.calledOnce).to.be.true; expect(error.underlyingError).to.exist;
expect(imapStub.logout.calledOnce).to.be.true; expect(imapStub.login.calledOnce).to.be.true;
expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
expect(imapStub.logout.called).to.be.false;
done(); done();
});
});
it('should fail w/ generic error on logout', function(done) {
imapStub.login.yieldsAsync();
imapStub.listWellKnownFolders.yieldsAsync(null, {
Inbox: [{}]
});
doctor._checkImap(function(error) {
expect(error).to.exist;
expect(error.code).to.equal(ConnectionDoctor.GENERIC_ERROR);
expect(error.underlyingError).to.exist;
expect(imapStub.login.calledOnce).to.be.true;
expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
expect(imapStub.logout.calledOnce).to.be.true;
done();
});
setTimeout(function() {
// this error is thrown while we're waiting for the logout
imapStub.onError(new Error());
}, 50);
});
it('should fail w/ generic error on inbox missing', function(done) {
imapStub.login.yieldsAsync();
imapStub.listWellKnownFolders.yieldsAsync(null, {
Inbox: []
});
doctor._checkImap(function(error) {
expect(error).to.exist;
expect(error.code).to.equal(ConnectionDoctor.NO_INBOX);
expect(imapStub.login.calledOnce).to.be.true;
expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
expect(imapStub.logout.called).to.be.false;
done();
});
});
it('should fail w/ generic error on listing folders fails', function(done) {
imapStub.login.yieldsAsync();
imapStub.listWellKnownFolders.yieldsAsync(new Error());
doctor._checkImap(function(error) {
expect(error).to.exist;
expect(error.code).to.equal(ConnectionDoctor.GENERIC_ERROR);
expect(error.underlyingError).to.exist;
expect(imapStub.login.calledOnce).to.be.true;
expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
expect(imapStub.logout.called).to.be.false;
done();
});
});
it('should fail w/ auth rejected', function(done) {
doctor._checkImap(function(error) {
expect(error).to.exist;
expect(error.code).to.equal(ConnectionDoctor.AUTH_REJECTED);
expect(error.underlyingError).to.exist;
expect(imapStub.login.calledOnce).to.be.true;
expect(imapStub.listWellKnownFolders.called).to.be.false;
expect(imapStub.logout.called).to.be.false;
done();
});
setTimeout(function() {
// this error is thrown while we're waiting for the login
imapStub.onError(new Error());
}, 50);
}); });
}); });
describe('#_checkSmtp', function() { it('should fail w/ auth rejected', function(done) {
it('should perform SMTP login, logout', function(done) { doctor._checkImap(function(error) {
doctor._checkSmtp(function(error) { expect(error).to.exist;
expect(error).to.not.exist; expect(error.code).to.equal(ConnectionDoctor.AUTH_REJECTED);
expect(smtpStub.connect.calledOnce).to.be.true; expect(error.underlyingError).to.exist;
expect(smtpStub.quit.calledOnce).to.be.true; expect(imapStub.login.calledOnce).to.be.true;
expect(imapStub.listWellKnownFolders.called).to.be.false;
expect(imapStub.logout.called).to.be.false;
done(); done();
});
smtpStub.onidle();
smtpStub.onclose();
}); });
it('should fail w/ auth rejected', function(done) { setTimeout(function() {
doctor._checkSmtp(function(error) { // this error is thrown while we're waiting for the login
expect(error).to.exist; imapStub.onError(new Error());
expect(error.code).to.equal(ConnectionDoctor.AUTH_REJECTED); }, 50);
expect(error.underlyingError).to.exist; });
expect(smtpStub.connect.calledOnce).to.be.true; });
expect(smtpStub.quit.called).to.be.false;
done(); describe('#_checkSmtp', function() {
}); it('should perform SMTP login, logout', function(done) {
doctor._checkSmtp(function(error) {
expect(error).to.not.exist;
expect(smtpStub.connect.calledOnce).to.be.true;
expect(smtpStub.quit.calledOnce).to.be.true;
smtpStub.onerror(new Error()); done();
});
smtpStub.onidle();
smtpStub.onclose();
});
it('should fail w/ auth rejected', function(done) {
doctor._checkSmtp(function(error) {
expect(error).to.exist;
expect(error.code).to.equal(ConnectionDoctor.AUTH_REJECTED);
expect(error.underlyingError).to.exist;
expect(smtpStub.connect.calledOnce).to.be.true;
expect(smtpStub.quit.called).to.be.false;
done();
});
smtpStub.onerror(new Error());
});
});
describe('#check', function() {
beforeEach(function() {
sinon.stub(doctor, '_checkOnline');
sinon.stub(doctor, '_checkReachable');
sinon.stub(doctor, '_checkImap');
sinon.stub(doctor, '_checkSmtp');
});
it('should perform all tests', function(done) {
doctor._checkOnline.yieldsAsync();
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync();
doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync();
doctor._checkImap.yieldsAsync();
doctor._checkSmtp.yieldsAsync();
doctor.check(function(err) {
expect(err).to.not.exist;
expect(doctor._checkOnline.calledOnce).to.be.true;
expect(doctor._checkReachable.calledTwice).to.be.true;
expect(doctor._checkImap.calledOnce).to.be.true;
expect(doctor._checkSmtp.calledOnce).to.be.true;
done();
}); });
}); });
describe('#check', function() { it('should fail for smtp', function(done) {
beforeEach(function() { doctor._checkOnline.yieldsAsync();
sinon.stub(doctor, '_checkOnline'); doctor._checkReachable.withArgs(credentials.imap).yieldsAsync();
sinon.stub(doctor, '_checkReachable'); doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync();
sinon.stub(doctor, '_checkImap'); doctor._checkImap.yieldsAsync();
sinon.stub(doctor, '_checkSmtp'); doctor._checkSmtp.yieldsAsync(new Error());
doctor.check(function(err) {
expect(err).to.exist;
expect(doctor._checkOnline.calledOnce).to.be.true;
expect(doctor._checkReachable.calledTwice).to.be.true;
expect(doctor._checkImap.calledOnce).to.be.true;
expect(doctor._checkSmtp.calledOnce).to.be.true;
done();
}); });
});
it('should perform all tests', function(done) { it('should fail for imap', function(done) {
doctor._checkOnline.yieldsAsync(); doctor._checkOnline.yieldsAsync();
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync(); doctor._checkReachable.withArgs(credentials.imap).yieldsAsync();
doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync(); doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync();
doctor._checkImap.yieldsAsync(); doctor._checkImap.yieldsAsync(new Error());
doctor._checkSmtp.yieldsAsync();
doctor.check(function(err) { doctor.check(function(err) {
expect(err).to.not.exist; expect(err).to.exist;
expect(doctor._checkOnline.calledOnce).to.be.true; expect(doctor._checkOnline.calledOnce).to.be.true;
expect(doctor._checkReachable.calledTwice).to.be.true; expect(doctor._checkReachable.calledTwice).to.be.true;
expect(doctor._checkImap.calledOnce).to.be.true; expect(doctor._checkImap.calledOnce).to.be.true;
expect(doctor._checkSmtp.calledOnce).to.be.true; expect(doctor._checkSmtp.called).to.be.false;
done(); done();
});
}); });
});
it('should fail for smtp', function(done) { it('should fail for smtp reachability', function(done) {
doctor._checkOnline.yieldsAsync(); doctor._checkOnline.yieldsAsync();
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync(); doctor._checkReachable.withArgs(credentials.imap).yieldsAsync();
doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync(); doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync(new Error());
doctor._checkImap.yieldsAsync();
doctor._checkSmtp.yieldsAsync(new Error());
doctor.check(function(err) { doctor.check(function(err) {
expect(err).to.exist; expect(err).to.exist;
expect(doctor._checkOnline.calledOnce).to.be.true; expect(doctor._checkOnline.calledOnce).to.be.true;
expect(doctor._checkReachable.calledTwice).to.be.true; expect(doctor._checkReachable.calledTwice).to.be.true;
expect(doctor._checkImap.calledOnce).to.be.true; expect(doctor._checkImap.called).to.be.false;
expect(doctor._checkSmtp.calledOnce).to.be.true; expect(doctor._checkSmtp.called).to.be.false;
done(); done();
});
}); });
});
it('should fail for imap', function(done) { it('should fail for imap reachability', function(done) {
doctor._checkOnline.yieldsAsync(); doctor._checkOnline.yieldsAsync();
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync(); doctor._checkReachable.withArgs(credentials.imap).yieldsAsync(new Error());
doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync();
doctor._checkImap.yieldsAsync(new Error());
doctor.check(function(err) { doctor.check(function(err) {
expect(err).to.exist; expect(err).to.exist;
expect(doctor._checkOnline.calledOnce).to.be.true; expect(doctor._checkOnline.calledOnce).to.be.true;
expect(doctor._checkReachable.calledTwice).to.be.true; expect(doctor._checkReachable.calledOnce).to.be.true;
expect(doctor._checkImap.calledOnce).to.be.true; expect(doctor._checkImap.called).to.be.false;
expect(doctor._checkSmtp.called).to.be.false; expect(doctor._checkSmtp.called).to.be.false;
done(); done();
});
}); });
});
it('should fail for smtp reachability', function(done) { it('should fail for offline', function(done) {
doctor._checkOnline.yieldsAsync(); doctor._checkOnline.yieldsAsync(new Error());
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync();
doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync(new Error());
doctor.check(function(err) { doctor.check(function(err) {
expect(err).to.exist; expect(err).to.exist;
expect(doctor._checkOnline.calledOnce).to.be.true; expect(doctor._checkOnline.calledOnce).to.be.true;
expect(doctor._checkReachable.calledTwice).to.be.true; expect(doctor._checkReachable.called).to.be.false;
expect(doctor._checkImap.called).to.be.false; expect(doctor._checkImap.called).to.be.false;
expect(doctor._checkSmtp.called).to.be.false; expect(doctor._checkSmtp.called).to.be.false;
done(); done();
});
}); });
});
it('should fail for imap reachability', function(done) { it('should fail w/o config', function(done) {
doctor._checkOnline.yieldsAsync(); doctor.credentials = doctor._imap = doctor._smtp = undefined;
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync(new Error());
doctor.check(function(err) { doctor.check(function(err) {
expect(err).to.exist; expect(err).to.exist;
expect(doctor._checkOnline.calledOnce).to.be.true; expect(doctor._checkOnline.called).to.be.false;
expect(doctor._checkReachable.calledOnce).to.be.true; expect(doctor._checkReachable.called).to.be.false;
expect(doctor._checkImap.called).to.be.false; expect(doctor._checkImap.called).to.be.false;
expect(doctor._checkSmtp.called).to.be.false; expect(doctor._checkSmtp.called).to.be.false;
done(); done();
});
});
it('should fail for offline', function(done) {
doctor._checkOnline.yieldsAsync(new Error());
doctor.check(function(err) {
expect(err).to.exist;
expect(doctor._checkOnline.calledOnce).to.be.true;
expect(doctor._checkReachable.called).to.be.false;
expect(doctor._checkImap.called).to.be.false;
expect(doctor._checkSmtp.called).to.be.false;
done();
});
});
it('should fail w/o config', function(done) {
doctor.credentials = doctor._imap = doctor._smtp = undefined;
doctor.check(function(err) {
expect(err).to.exist;
expect(doctor._checkOnline.called).to.be.false;
expect(doctor._checkReachable.called).to.be.false;
expect(doctor._checkImap.called).to.be.false;
expect(doctor._checkSmtp.called).to.be.false;
done();
});
}); });
}); });
}); });

View File

@ -1,190 +1,186 @@
define(function(require) { 'use strict';
'use strict';
var expect = chai.expect, var mocks = angular.mocks,
angular = require('angular'), ContactsCtrl = require('../../src/js/controller/contacts'),
mocks = require('angularMocks'), appController = require('../../src/js/app-controller'),
ContactsCtrl = require('js/controller/contacts'), KeychainDAO = require('../../src/js/dao/keychain-dao'),
appController = require('js/app-controller'), PGP = require('../../src/js/crypto/pgp');
KeychainDAO = require('js/dao/keychain-dao'),
PGP = require('js/crypto/pgp');
describe('Contacts Controller unit test', function() { describe('Contacts Controller unit test', function() {
var scope, contactsCtrl, var scope, contactsCtrl,
origKeychain, keychainMock, origKeychain, keychainMock,
origPgp, pgpMock; origPgp, pgpMock;
beforeEach(function() { beforeEach(function() {
origPgp = appController._pgp; origPgp = appController._pgp;
appController._pgp = pgpMock = sinon.createStubInstance(PGP); appController._pgp = pgpMock = sinon.createStubInstance(PGP);
origKeychain = appController._keychain; origKeychain = appController._keychain;
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO); appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
angular.module('contactstest', []); angular.module('contactstest', []);
mocks.module('contactstest'); mocks.module('contactstest');
mocks.inject(function($rootScope, $controller) { mocks.inject(function($rootScope, $controller) {
scope = $rootScope.$new(); scope = $rootScope.$new();
scope.state = {}; scope.state = {};
contactsCtrl = $controller(ContactsCtrl, { contactsCtrl = $controller(ContactsCtrl, {
$scope: scope $scope: scope
});
});
});
afterEach(function() {
// restore the module
appController._pgp = origPgp;
appController._keychain = origKeychain;
});
describe('scope variables', function() {
it('should be set correctly', function() {
expect(scope.fingerprint).to.equal('XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX');
expect(scope.state.contacts.toggle).to.exist;
});
});
describe('listKeys', function() {
it('should fail due to error in keychain.listLocalPublicKeys', function(done) {
keychainMock.listLocalPublicKeys.yields(42);
scope.onError = function(err) {
expect(err).to.equal(42);
done();
};
scope.listKeys();
});
it('should work', function(done) {
keychainMock.listLocalPublicKeys.yields(null, [{
_id: '12345'
}]);
pgpMock.getKeyParams.returns({
fingerprint: 'asdf'
});
scope.$apply = function() {
expect(scope.keys.length).to.equal(1);
expect(scope.keys[0]._id).to.equal('12345');
expect(scope.keys[0].fingerprint).to.equal('asdf');
done();
};
expect(scope.keys).to.not.exist;
scope.listKeys();
});
});
describe('getFingerprint', function() {
it('should work', function() {
var key = {
fingerprint: 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY'
};
scope.getFingerprint(key);
expect(scope.fingerprint).to.equal('YYYY YYYY YYYY YYYY YYYY ... YYYY YYYY YYYY YYYY YYYY');
});
});
describe('importKey', function() {
it('should work', function(done) {
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
pgpMock.getKeyParams.returns({
_id: '12345',
userId: 'max@example.com',
userIds: []
});
keychainMock.saveLocalPublicKey.withArgs({
_id: '12345',
userId: 'max@example.com',
userIds: [],
publicKey: '-----BEGIN PGP PUBLIC KEY BLOCK-----',
imported: true
}).yields();
scope.listKeys = function() {
done();
};
scope.importKey(keyArmored);
});
it('should fail due to invalid armored key', function(done) {
var keyArmored = '-----BEGIN PGP PRIVATE KEY BLOCK-----';
scope.onError = function(err) {
expect(err).to.exist;
done();
};
scope.importKey(keyArmored);
});
it('should fail due to error in pgp.getKeyParams', function(done) {
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
pgpMock.getKeyParams.throws(new Error('WAT'));
scope.onError = function(err) {
expect(err).to.exist;
done();
};
scope.importKey(keyArmored);
});
it('should fail due to error in keychain.saveLocalPublicKey', function(done) {
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
pgpMock.getKeyParams.returns({
_id: '12345',
userId: 'max@example.com'
});
keychainMock.saveLocalPublicKey.yields(42);
scope.onError = function(err) {
expect(err).to.equal(42);
done();
};
scope.importKey(keyArmored);
});
});
describe('removeKey', function() {
it('should work', function(done) {
var key = {
_id: '12345'
};
keychainMock.removeLocalPublicKey.withArgs('12345').yields();
scope.listKeys = function() {
done();
};
scope.removeKey(key);
});
it('should fail due to error in keychain.removeLocalPublicKey', function(done) {
var key = {
_id: '12345'
};
keychainMock.removeLocalPublicKey.withArgs('12345').yields(42);
scope.onError = function(err) {
expect(err).to.equal(42);
done();
};
scope.removeKey(key);
}); });
}); });
}); });
afterEach(function() {
// restore the module
appController._pgp = origPgp;
appController._keychain = origKeychain;
});
describe('scope variables', function() {
it('should be set correctly', function() {
expect(scope.fingerprint).to.equal('XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX');
expect(scope.state.contacts.toggle).to.exist;
});
});
describe('listKeys', function() {
it('should fail due to error in keychain.listLocalPublicKeys', function(done) {
keychainMock.listLocalPublicKeys.yields(42);
scope.onError = function(err) {
expect(err).to.equal(42);
done();
};
scope.listKeys();
});
it('should work', function(done) {
keychainMock.listLocalPublicKeys.yields(null, [{
_id: '12345'
}]);
pgpMock.getKeyParams.returns({
fingerprint: 'asdf'
});
scope.$apply = function() {
expect(scope.keys.length).to.equal(1);
expect(scope.keys[0]._id).to.equal('12345');
expect(scope.keys[0].fingerprint).to.equal('asdf');
done();
};
expect(scope.keys).to.not.exist;
scope.listKeys();
});
});
describe('getFingerprint', function() {
it('should work', function() {
var key = {
fingerprint: 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY'
};
scope.getFingerprint(key);
expect(scope.fingerprint).to.equal('YYYY YYYY YYYY YYYY YYYY ... YYYY YYYY YYYY YYYY YYYY');
});
});
describe('importKey', function() {
it('should work', function(done) {
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
pgpMock.getKeyParams.returns({
_id: '12345',
userId: 'max@example.com',
userIds: []
});
keychainMock.saveLocalPublicKey.withArgs({
_id: '12345',
userId: 'max@example.com',
userIds: [],
publicKey: '-----BEGIN PGP PUBLIC KEY BLOCK-----',
imported: true
}).yields();
scope.listKeys = function() {
done();
};
scope.importKey(keyArmored);
});
it('should fail due to invalid armored key', function(done) {
var keyArmored = '-----BEGIN PGP PRIVATE KEY BLOCK-----';
scope.onError = function(err) {
expect(err).to.exist;
done();
};
scope.importKey(keyArmored);
});
it('should fail due to error in pgp.getKeyParams', function(done) {
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
pgpMock.getKeyParams.throws(new Error('WAT'));
scope.onError = function(err) {
expect(err).to.exist;
done();
};
scope.importKey(keyArmored);
});
it('should fail due to error in keychain.saveLocalPublicKey', function(done) {
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
pgpMock.getKeyParams.returns({
_id: '12345',
userId: 'max@example.com'
});
keychainMock.saveLocalPublicKey.yields(42);
scope.onError = function(err) {
expect(err).to.equal(42);
done();
};
scope.importKey(keyArmored);
});
});
describe('removeKey', function() {
it('should work', function(done) {
var key = {
_id: '12345'
};
keychainMock.removeLocalPublicKey.withArgs('12345').yields();
scope.listKeys = function() {
done();
};
scope.removeKey(key);
});
it('should fail due to error in keychain.removeLocalPublicKey', function(done) {
var key = {
_id: '12345'
};
keychainMock.removeLocalPublicKey.withArgs('12345').yields(42);
scope.onError = function(err) {
expect(err).to.equal(42);
done();
};
scope.removeKey(key);
});
});
}); });

View File

@ -1,58 +1,55 @@
define(function(require) { 'use strict';
'use strict';
var Crypto = require('js/crypto/crypto'), var Crypto = require('../../src/js/crypto/crypto'),
util = require('js/crypto/util'), config = require('../../src/js/app-config').config,
config = require('js/app-config').config, util = require('crypto-lib').util;
expect = chai.expect;
describe('Crypto unit tests', function() { describe('Crypto unit tests', function() {
this.timeout(20000); this.timeout(20000);
var crypto, var crypto,
password = 'password', password = 'password',
keySize = config.symKeySize, keySize = config.symKeySize,
ivSize = config.symIvSize; ivSize = config.symIvSize;
beforeEach(function() { beforeEach(function() {
crypto = new Crypto(); crypto = new Crypto();
}); });
afterEach(function() {}); afterEach(function() {});
describe('AES encrypt/decrypt', function() { describe('AES encrypt/decrypt', function() {
it('should work', function(done) { it('should work', function(done) {
var plaintext = 'Hello, World!'; var plaintext = 'Hello, World!';
var key = util.random(keySize); var key = util.random(keySize);
var iv = util.random(ivSize); var iv = util.random(ivSize);
crypto.encrypt(plaintext, key, iv, function(err, ciphertext) { crypto.encrypt(plaintext, key, iv, function(err, ciphertext) {
expect(err).to.not.exist;
expect(ciphertext).to.exist;
crypto.decrypt(ciphertext, key, iv, function(err, decrypted) {
expect(err).to.not.exist; expect(err).to.not.exist;
expect(ciphertext).to.exist; expect(decrypted).to.equal(plaintext);
crypto.decrypt(ciphertext, key, iv, function(err, decrypted) {
expect(err).to.not.exist;
expect(decrypted).to.equal(plaintext);
done();
});
});
});
});
describe("PBKDF2 (Async/Worker)", function() {
it('should work', function(done) {
var salt = util.random(keySize);
crypto.deriveKey(password, salt, keySize, function(err, key) {
expect(err).to.not.exist;
expect(util.base642Str(key).length * 8).to.equal(keySize);
done(); done();
}); });
}); });
});
});
describe("PBKDF2 (Async/Worker)", function() {
it('should work', function(done) {
var salt = util.random(keySize);
crypto.deriveKey(password, salt, keySize, function(err, key) {
expect(err).to.not.exist;
expect(util.base642Str(key).length * 8).to.equal(keySize);
done();
});
}); });
}); });
}); });

View File

@ -1,105 +1,101 @@
define(function(require) { 'use strict';
'use strict';
var LawnchairDAO = require('js/dao/lawnchair-dao'), var LawnchairDAO = require('../../src/js/dao/lawnchair-dao'),
DeviceStorageDAO = require('js/dao/devicestorage-dao'), DeviceStorageDAO = require('../../src/js/dao/devicestorage-dao');
expect = chai.expect;
var testUser = 'test@example.com'; var testUser = 'test@example.com';
describe('Device Storage DAO unit tests', function() { describe('Device Storage DAO unit tests', function() {
var storageDao, lawnchairDaoStub; var storageDao, lawnchairDaoStub;
beforeEach(function() { beforeEach(function() {
lawnchairDaoStub = sinon.createStubInstance(LawnchairDAO); lawnchairDaoStub = sinon.createStubInstance(LawnchairDAO);
storageDao = new DeviceStorageDAO(lawnchairDaoStub); storageDao = new DeviceStorageDAO(lawnchairDaoStub);
});
afterEach(function() {});
describe('init', function() {
it('should work', function(done) {
lawnchairDaoStub.init.yields();
storageDao.init(testUser, function(err) {
expect(err).to.not.exist;
expect(lawnchairDaoStub.init.calledOnce).to.be.true;
done();
});
}); });
});
afterEach(function() {}); describe('store list', function() {
it('should fail', function(done) {
var list = [{}];
describe('init', function() { storageDao.storeList(list, '', function(err) {
it('should work', function(done) { expect(err).to.exist;
lawnchairDaoStub.init.yields(); done();
storageDao.init(testUser, function(err) {
expect(err).to.not.exist;
expect(lawnchairDaoStub.init.calledOnce).to.be.true;
done();
});
}); });
}); });
describe('store list', function() { it('should work with empty list', function(done) {
it('should fail', function(done) { var list = [];
var list = [{}];
storageDao.storeList(list, '', function(err) { storageDao.storeList(list, 'email', function(err) {
expect(err).to.exist; expect(err).to.not.exist;
done(); done();
});
});
it('should work with empty list', function(done) {
var list = [];
storageDao.storeList(list, 'email', function(err) {
expect(err).to.not.exist;
done();
});
});
it('should work', function(done) {
lawnchairDaoStub.batch.yields();
var list = [{
foo: 'bar'
}];
storageDao.storeList(list, 'email', function(err) {
expect(err).to.not.exist;
expect(lawnchairDaoStub.batch.calledOnce).to.be.true;
done();
});
}); });
}); });
describe('remove list', function() { it('should work', function(done) {
it('should work', function(done) { lawnchairDaoStub.batch.yields();
lawnchairDaoStub.removeList.yields();
storageDao.removeList('email', function(err) { var list = [{
expect(err).to.not.exist; foo: 'bar'
expect(lawnchairDaoStub.removeList.calledOnce).to.be.true; }];
done();
}); storageDao.storeList(list, 'email', function(err) {
expect(err).to.not.exist;
expect(lawnchairDaoStub.batch.calledOnce).to.be.true;
done();
}); });
}); });
});
describe('list items', function() { describe('remove list', function() {
it('should work', function(done) { it('should work', function(done) {
lawnchairDaoStub.list.yields(); lawnchairDaoStub.removeList.yields();
storageDao.listItems('email', 0, null, function(err) { storageDao.removeList('email', function(err) {
expect(err).to.not.exist; expect(err).to.not.exist;
expect(lawnchairDaoStub.list.calledOnce).to.be.true; expect(lawnchairDaoStub.removeList.calledOnce).to.be.true;
done(); done();
});
}); });
}); });
});
describe('clear', function() { describe('list items', function() {
it('should work', function(done) { it('should work', function(done) {
lawnchairDaoStub.clear.yields(); lawnchairDaoStub.list.yields();
storageDao.clear(function(err) { storageDao.listItems('email', 0, null, function(err) {
expect(err).to.not.exist; expect(err).to.not.exist;
expect(lawnchairDaoStub.clear.calledOnce).to.be.true; expect(lawnchairDaoStub.list.calledOnce).to.be.true;
done(); done();
});
}); });
}); });
});
describe('clear', function() {
it('should work', function(done) {
lawnchairDaoStub.clear.yields();
storageDao.clear(function(err) {
expect(err).to.not.exist;
expect(lawnchairDaoStub.clear.calledOnce).to.be.true;
done();
});
});
}); });
}); });

View File

@ -1,50 +1,46 @@
define(function(require) { 'use strict';
'use strict';
var expect = chai.expect, var mocks = angular.mocks,
angular = require('angular'), DialogCtrl = require('../../src/js/controller/dialog');
mocks = require('angularMocks'),
DialogCtrl = require('js/controller/dialog');
describe('Dialog Controller unit test', function() { describe('Dialog Controller unit test', function() {
var scope, dialogCtrl; var scope, dialogCtrl;
beforeEach(function() { beforeEach(function() {
angular.module('dialogtest', []); angular.module('dialogtest', []);
mocks.module('dialogtest'); mocks.module('dialogtest');
mocks.inject(function($rootScope, $controller) { mocks.inject(function($rootScope, $controller) {
scope = $rootScope.$new(); scope = $rootScope.$new();
scope.state = { scope.state = {
dialog: {} dialog: {}
}; };
dialogCtrl = $controller(DialogCtrl, { dialogCtrl = $controller(DialogCtrl, {
$scope: scope $scope: scope
});
});
});
afterEach(function() {});
describe('confirm', function() {
it('should work', function(done) {
scope.state.dialog.callback = function(confirmed) {
expect(confirmed).to.be.true;
expect(scope.state.dialog.open).to.be.false;
done();
};
scope.confirm(true);
});
});
describe('cancel', function() {
it('should work', function(done) {
scope.state.dialog.callback = function(confirmed) {
expect(confirmed).to.be.false;
expect(scope.state.dialog.open).to.be.false;
done();
};
scope.confirm(false);
}); });
}); });
}); });
afterEach(function() {});
describe('confirm', function() {
it('should work', function(done) {
scope.state.dialog.callback = function(confirmed) {
expect(confirmed).to.be.true;
expect(scope.state.dialog.open).to.be.false;
done();
};
scope.confirm(true);
});
});
describe('cancel', function() {
it('should work', function(done) {
scope.state.dialog.callback = function(confirmed) {
expect(confirmed).to.be.false;
expect(scope.state.dialog.open).to.be.false;
done();
};
scope.confirm(false);
});
});
}); });

File diff suppressed because it is too large Load Diff

View File

@ -9,11 +9,70 @@
<body> <body>
<div id="mocha"></div> <div id="mocha"></div>
<script>
//
// Polyfills
//
(function() {
'use strict';
// Mozilla bind polyfill because phantomjs is stupid
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
FNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof FNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
};
FNOP.prototype = this.prototype;
fBound.prototype = new FNOP();
return fBound;
};
}
// a warm round of applause for phantomjs for missing events
(function() {
function CustomEvent(event, params) {
params = params || {
bubbles: false,
cancelable: false,
detail: undefined
};
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return evt;
}
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent;
})();
})();
</script>
<script src="../lib/chai.js"></script> <script src="../lib/chai.js"></script>
<script src="../lib/sinon.js"></script>
<script src="../lib/mocha.js"></script> <script src="../lib/mocha.js"></script>
<script src="../lib/sinon.js"></script>
<script data-main="main.js" src="../../src/lib/require.js"></script> <script>
window.expect = chai.expect;
mocha.setup('bdd');
</script>
<script src="index.js"></script>
<script>
mocha.checkLeaks();
mocha.globals([]);
mocha.run();
</script>
</body> </body>
</html> </html>

View File

@ -1,108 +1,105 @@
define(function(require) { 'use strict';
'use strict';
var RestDAO = require('js/dao/rest-dao'), var RestDAO = require('../../src/js/dao/rest-dao'),
InvitationDAO = require('js/dao/invitation-dao'), InvitationDAO = require('../../src/js/dao/invitation-dao');
expect = chai.expect;
describe('Invitation DAO unit tests', function() { describe('Invitation DAO unit tests', function() {
var restDaoStub, invitationDao, var restDaoStub, invitationDao,
alice = 'zuhause@aol.com', alice = 'zuhause@aol.com',
bob = 'manfred.mustermann@musterdomain.com', bob = 'manfred.mustermann@musterdomain.com',
expectedUri = '/invitation/recipient/' + alice + '/sender/' + bob; expectedUri = '/invitation/recipient/' + alice + '/sender/' + bob;
beforeEach(function() { beforeEach(function() {
restDaoStub = sinon.createStubInstance(RestDAO); restDaoStub = sinon.createStubInstance(RestDAO);
invitationDao = new InvitationDAO(restDaoStub); invitationDao = new InvitationDAO(restDaoStub);
});
describe('initialization', function() {
it('should wire up correctly', function() {
expect(invitationDao._restDao).to.equal(restDaoStub);
expect(invitationDao.invite).to.exist;
expect(InvitationDAO.INVITE_MISSING).to.equal(1);
expect(InvitationDAO.INVITE_PENDING).to.equal(2);
expect(InvitationDAO.INVITE_SUCCESS).to.equal(4);
}); });
});
describe('initialization', function() { describe('invite', function() {
it('should wire up correctly', function() { it('should invite the recipient', function(done) {
expect(invitationDao._restDao).to.equal(restDaoStub); restDaoStub.put.yieldsAsync(null, undefined, 201);
expect(invitationDao.invite).to.exist;
expect(InvitationDAO.INVITE_MISSING).to.equal(1); invitationDao.invite({
expect(InvitationDAO.INVITE_PENDING).to.equal(2); recipient: alice,
expect(InvitationDAO.INVITE_SUCCESS).to.equal(4); sender: bob
}, function(err, status) {
expect(err).to.not.exist;
expect(status).to.equal(InvitationDAO.INVITE_SUCCESS);
expect(restDaoStub.put.calledWith({}, expectedUri)).to.be.true;
done();
}); });
}); });
describe('invite', function() { it('should point out already invited recipient', function(done) {
it('should invite the recipient', function(done) { restDaoStub.put.yieldsAsync(null, undefined, 304);
restDaoStub.put.yieldsAsync(null, undefined, 201);
invitationDao.invite({ invitationDao.invite({
recipient: alice, recipient: alice,
sender: bob sender: bob
}, function(err, status) { }, function(err, status) {
expect(err).to.not.exist; expect(err).to.not.exist;
expect(status).to.equal(InvitationDAO.INVITE_SUCCESS); expect(status).to.equal(InvitationDAO.INVITE_PENDING);
expect(restDaoStub.put.calledWith({}, expectedUri)).to.be.true; done();
done(); });
}); });
it('should not work for http error', function(done) {
restDaoStub.put.yieldsAsync({
errMsg: 'jawollja.'
}); });
it('should point out already invited recipient', function(done) { invitationDao.invite({
restDaoStub.put.yieldsAsync(null, undefined, 304); recipient: alice,
sender: bob
invitationDao.invite({ }, function(err, status) {
recipient: alice, expect(err).to.exist;
sender: bob expect(status).to.not.exist;
}, function(err, status) { done();
expect(err).to.not.exist;
expect(status).to.equal(InvitationDAO.INVITE_PENDING);
done();
});
}); });
});
it('should not work for http error', function(done) { it('should not work for unexpected response', function(done) {
restDaoStub.put.yieldsAsync({ restDaoStub.put.yieldsAsync(null, undefined, 1337);
errMsg: 'jawollja.'
});
invitationDao.invite({ invitationDao.invite({
recipient: alice, recipient: alice,
sender: bob sender: bob
}, function(err, status) { }, function(err, status) {
expect(err).to.exist; expect(err).to.exist;
expect(status).to.not.exist; expect(status).to.not.exist;
done(); done();
});
}); });
});
it('should not work for unexpected response', function(done) { it('should report erroneous usage', function() {
restDaoStub.put.yieldsAsync(null, undefined, 1337); invitationDao.invite({
sender: bob
}, expectError);
invitationDao.invite({ invitationDao.invite({
recipient: alice, recipient: alice,
sender: bob }, expectError);
}, function(err, status) {
expect(err).to.exist;
expect(status).to.not.exist;
done();
});
});
it('should report erroneous usage', function() { invitationDao.invite({
invitationDao.invite({ recipient: 123,
sender: bob sender: 123
}, expectError); }, expectError);
invitationDao.invite({ invitationDao.invite('asd', expectError);
recipient: alice,
}, expectError);
invitationDao.invite({ function expectError(err, status) {
recipient: 123, expect(err).to.exist;
sender: 123 expect(status).to.not.exist;
}, expectError); }
invitationDao.invite('asd', expectError);
function expectError(err, status) {
expect(err).to.exist;
expect(status).to.not.exist;
}
});
}); });
}); });
}); });

File diff suppressed because it is too large Load Diff

View File

@ -1,152 +1,148 @@
define(function(require) { 'use strict';
'use strict';
var LawnchairDAO = require('js/dao/lawnchair-dao'), var LawnchairDAO = require('../../src/js/dao/lawnchair-dao');
expect = chai.expect;
var dbName = 'lawnchair@test.com'; var dbName = 'lawnchair@test.com';
var key = 'type_1'; var key = 'type_1';
var data = { var data = {
name: 'testName1', name: 'testName1',
type: 'testType1' type: 'testType1'
}; };
var key2 = 'type_2'; var key2 = 'type_2';
var data2 = { var data2 = {
name: 'testName2', name: 'testName2',
type: 'testType2' type: 'testType2'
}; };
describe('Lawnchair DAO unit tests', function() { describe('Lawnchair DAO unit tests', function() {
var lawnchairDao; var lawnchairDao;
beforeEach(function(done) { beforeEach(function(done) {
lawnchairDao = new LawnchairDAO(); lawnchairDao = new LawnchairDAO();
lawnchairDao.init(dbName, function(err) { lawnchairDao.init(dbName, function(err) {
expect(err).to.not.exist; expect(err).to.not.exist;
expect(lawnchairDao._db).to.exist; expect(lawnchairDao._db).to.exist;
done();
});
});
afterEach(function(done) {
lawnchairDao.clear(function(err) {
expect(err).to.not.exist;
done();
});
});
describe('read', function() {
it('should fail', function(done) {
lawnchairDao.read(undefined, function(err) {
expect(err).to.exist;
done();
});
});
});
describe('list', function() {
it('should fail', function(done) {
lawnchairDao.list(undefined, 0, null, function(err) {
expect(err).to.exist;
done();
});
});
});
describe('remove list', function() {
it('should fail', function(done) {
lawnchairDao.removeList(undefined, function(err) {
expect(err).to.exist;
done();
});
});
});
describe('persist/read/remove', function() {
it('should fail', function(done) {
lawnchairDao.persist(undefined, data, function(err) {
expect(err).to.exist;
done();
});
});
it('should fail', function(done) {
lawnchairDao.persist('1234', undefined, function(err) {
expect(err).to.exist;
done(); done();
}); });
}); });
afterEach(function(done) { it('should work', function(done) {
lawnchairDao.clear(function(err) { lawnchairDao.persist(key, data, function(err) {
expect(err).to.not.exist; expect(err).to.not.exist;
lawnchairDao.read(key, onRead);
});
function onRead(err, fetched) {
expect(err).to.not.exist;
expect(fetched).to.deep.equal(data);
lawnchairDao.remove(key, onRemove);
}
function onRemove(err) {
expect(err).to.not.exist;
lawnchairDao.read(key, onReadAgain);
}
function onReadAgain(err, fetched) {
expect(err).to.not.exist;
expect(fetched).to.not.exist;
done();
}
});
});
describe('batch/list/removeList', function() {
it('should fails', function(done) {
lawnchairDao.batch({}, function(err) {
expect(err).to.exist;
done(); done();
}); });
}); });
describe('read', function() { it('should work', function(done) {
it('should fail', function(done) { var list = [{
lawnchairDao.read(undefined, function(err) { key: key,
expect(err).to.exist; object: data
done(); }, {
}); key: key2,
object: data2
}];
lawnchairDao.batch(list, function(err) {
expect(err).to.not.exist;
lawnchairDao.list('type', 0, null, onList);
}); });
function onList(err, fetched) {
expect(err).to.not.exist;
expect(fetched.length).to.equal(2);
expect(fetched[0]).to.deep.equal(list[0].object);
lawnchairDao.removeList('type', onRemoveList);
}
function onRemoveList(err) {
expect(err).to.not.exist;
lawnchairDao.list('type', 0, null, onListAgain);
}
function onListAgain(err, fetched) {
expect(err).to.not.exist;
expect(fetched).to.exist;
expect(fetched.length).to.equal(0);
done();
}
}); });
describe('list', function() {
it('should fail', function(done) {
lawnchairDao.list(undefined, 0, null, function(err) {
expect(err).to.exist;
done();
});
});
});
describe('remove list', function() {
it('should fail', function(done) {
lawnchairDao.removeList(undefined, function(err) {
expect(err).to.exist;
done();
});
});
});
describe('persist/read/remove', function() {
it('should fail', function(done) {
lawnchairDao.persist(undefined, data, function(err) {
expect(err).to.exist;
done();
});
});
it('should fail', function(done) {
lawnchairDao.persist('1234', undefined, function(err) {
expect(err).to.exist;
done();
});
});
it('should work', function(done) {
lawnchairDao.persist(key, data, function(err) {
expect(err).to.not.exist;
lawnchairDao.read(key, onRead);
});
function onRead(err, fetched) {
expect(err).to.not.exist;
expect(fetched).to.deep.equal(data);
lawnchairDao.remove(key, onRemove);
}
function onRemove(err) {
expect(err).to.not.exist;
lawnchairDao.read(key, onReadAgain);
}
function onReadAgain(err, fetched) {
expect(err).to.not.exist;
expect(fetched).to.not.exist;
done();
}
});
});
describe('batch/list/removeList', function() {
it('should fails', function(done) {
lawnchairDao.batch({}, function(err) {
expect(err).to.exist;
done();
});
});
it('should work', function(done) {
var list = [{
key: key,
object: data
}, {
key: key2,
object: data2
}];
lawnchairDao.batch(list, function(err) {
expect(err).to.not.exist;
lawnchairDao.list('type', 0, null, onList);
});
function onList(err, fetched) {
expect(err).to.not.exist;
expect(fetched.length).to.equal(2);
expect(fetched[0]).to.deep.equal(list[0].object);
lawnchairDao.removeList('type', onRemoveList);
}
function onRemoveList(err) {
expect(err).to.not.exist;
lawnchairDao.list('type', 0, null, onListAgain);
}
function onListAgain(err, fetched) {
expect(err).to.not.exist;
expect(fetched).to.exist;
expect(fetched.length).to.equal(0);
done();
}
});
});
}); });
}); });

View File

@ -1,236 +1,232 @@
define(function(require) { 'use strict';
'use strict';
var expect = chai.expect, var mocks = angular.mocks,
angular = require('angular'), LoginCtrl = require('../../src/js/controller/login'),
mocks = require('angularMocks'), EmailDAO = require('../../src/js/dao/email-dao'),
LoginCtrl = require('js/controller/login'), Auth = require('../../src/js/bo/auth'),
EmailDAO = require('js/dao/email-dao'), appController = require('../../src/js/app-controller'),
Auth = require('js/bo/auth'), KeychainDAO = require('../../src/js/dao/keychain-dao');
appController = require('js/app-controller'),
KeychainDAO = require('js/dao/keychain-dao');
describe('Login Controller unit test', function() { describe('Login Controller unit test', function() {
var scope, location, ctrl, var scope, location, ctrl,
origEmailDao, emailDaoMock, origEmailDao, emailDaoMock,
origKeychain, keychainMock, origKeychain, keychainMock,
origAuth, authStub, origAuth, authStub,
emailAddress = 'fred@foo.com', emailAddress = 'fred@foo.com',
startAppStub, startAppStub,
checkForUpdateStub, checkForUpdateStub,
initStub; initStub;
describe('initialization', function() { describe('initialization', function() {
var hasChrome, hasIdentity; var hasChrome, hasIdentity;
beforeEach(function() { beforeEach(function() {
hasChrome = !!window.chrome; hasChrome = !!window.chrome;
hasIdentity = !!window.chrome.identity; hasIdentity = !!window.chrome.identity;
window.chrome = window.chrome || {}; window.chrome = window.chrome || {};
window.chrome.identity = window.chrome.identity || {}; window.chrome.identity = window.chrome.identity || {};
// remember original module to restore later, then replace it // remember original module to restore later, then replace it
origEmailDao = appController._emailDao; origEmailDao = appController._emailDao;
origKeychain = appController._keychain; origKeychain = appController._keychain;
origAuth = appController._auth; origAuth = appController._auth;
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO); appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO);
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO); appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
appController._auth = authStub = sinon.createStubInstance(Auth); appController._auth = authStub = sinon.createStubInstance(Auth);
startAppStub = sinon.stub(appController, 'start'); startAppStub = sinon.stub(appController, 'start');
checkForUpdateStub = sinon.stub(appController, 'checkForUpdate'); checkForUpdateStub = sinon.stub(appController, 'checkForUpdate');
initStub = sinon.stub(appController, 'init'); initStub = sinon.stub(appController, 'init');
});
afterEach(function() {
// restore the browser
if (!hasIdentity) {
delete window.chrome.identity;
}
if (!hasChrome) {
delete window.chrome;
}
// restore the app controller module
appController._emailDao = origEmailDao;
appController._keychain = origKeychain;
appController._auth = origAuth;
appController.start.restore && appController.start.restore();
appController.checkForUpdate.restore && appController.checkForUpdate.restore();
appController.init.restore && appController.init.restore();
location.path.restore && location.path.restore();
startAppStub.restore();
checkForUpdateStub.restore();
initStub.restore();
});
it('should forward directly to desktop for empty passphrase', function(done) {
var testKeys = {
privateKey: 'a',
publicKey: 'b'
};
startAppStub.yields();
authStub.getEmailAddress.yields(null, {
emailAddress: emailAddress,
realname: 'asd'
}); });
initStub.yields(null, testKeys);
afterEach(function() { emailDaoMock.unlock.withArgs({
// restore the browser keypair: testKeys,
if (!hasIdentity) { passphrase: undefined
delete window.chrome.identity; }).yields();
}
if (!hasChrome) { angular.module('logintest', []);
delete window.chrome; mocks.module('logintest');
} mocks.inject(function($controller, $rootScope, $location) {
location = $location;
// restore the app controller module sinon.stub(location, 'path', function(path) {
appController._emailDao = origEmailDao; expect(path).to.equal('/desktop');
appController._keychain = origKeychain; expect(startAppStub.calledOnce).to.be.true;
appController._auth = origAuth; expect(checkForUpdateStub.calledOnce).to.be.true;
appController.start.restore && appController.start.restore(); expect(authStub.getEmailAddress.calledOnce).to.be.true;
appController.checkForUpdate.restore && appController.checkForUpdate.restore(); done();
appController.init.restore && appController.init.restore();
location.path.restore && location.path.restore();
startAppStub.restore();
checkForUpdateStub.restore();
initStub.restore();
});
it('should forward directly to desktop for empty passphrase', function(done) {
var testKeys = {
privateKey: 'a',
publicKey: 'b'
};
startAppStub.yields();
authStub.getEmailAddress.yields(null, {
emailAddress: emailAddress,
realname: 'asd'
}); });
initStub.yields(null, testKeys); scope = $rootScope.$new();
scope.state = {};
emailDaoMock.unlock.withArgs({ ctrl = $controller(LoginCtrl, {
keypair: testKeys, $location: location,
passphrase: undefined $scope: scope
}).yields();
angular.module('logintest', []);
mocks.module('logintest');
mocks.inject(function($controller, $rootScope, $location) {
location = $location;
sinon.stub(location, 'path', function(path) {
expect(path).to.equal('/desktop');
expect(startAppStub.calledOnce).to.be.true;
expect(checkForUpdateStub.calledOnce).to.be.true;
expect(authStub.getEmailAddress.calledOnce).to.be.true;
done();
});
scope = $rootScope.$new();
scope.state = {};
ctrl = $controller(LoginCtrl, {
$location: location,
$scope: scope
});
}); });
}); });
});
it('should forward to existing user login', function(done) { it('should forward to existing user login', function(done) {
var testKeys = { var testKeys = {
privateKey: 'a', privateKey: 'a',
publicKey: 'b' publicKey: 'b'
}; };
startAppStub.yields(); startAppStub.yields();
authStub.getEmailAddress.yields(null, { authStub.getEmailAddress.yields(null, {
emailAddress: emailAddress, emailAddress: emailAddress,
realname: 'asd' realname: 'asd'
});
initStub.yields(null, testKeys);
emailDaoMock.unlock.withArgs({
keypair: testKeys,
passphrase: undefined
}).yields({});
angular.module('logintest', []);
mocks.module('logintest');
mocks.inject(function($controller, $rootScope, $location) {
location = $location;
sinon.stub(location, 'path', function(path) {
expect(path).to.equal('/login-existing');
expect(startAppStub.calledOnce).to.be.true;
expect(checkForUpdateStub.calledOnce).to.be.true;
expect(authStub.getEmailAddress.calledOnce).to.be.true;
done();
}); });
initStub.yields(null, testKeys); scope = $rootScope.$new();
scope.state = {};
emailDaoMock.unlock.withArgs({ ctrl = $controller(LoginCtrl, {
keypair: testKeys, $location: location,
passphrase: undefined $scope: scope
}).yields({});
angular.module('logintest', []);
mocks.module('logintest');
mocks.inject(function($controller, $rootScope, $location) {
location = $location;
sinon.stub(location, 'path', function(path) {
expect(path).to.equal('/login-existing');
expect(startAppStub.calledOnce).to.be.true;
expect(checkForUpdateStub.calledOnce).to.be.true;
expect(authStub.getEmailAddress.calledOnce).to.be.true;
done();
});
scope = $rootScope.$new();
scope.state = {};
ctrl = $controller(LoginCtrl, {
$location: location,
$scope: scope
});
}); });
}); });
});
it('should forward to privatekey download login', function(done) { it('should forward to privatekey download login', function(done) {
startAppStub.yields(); startAppStub.yields();
authStub.getEmailAddress.yields(null, { authStub.getEmailAddress.yields(null, {
emailAddress: emailAddress, emailAddress: emailAddress,
realname: 'asd' realname: 'asd'
}); });
initStub.yields(null, { initStub.yields(null, {
publicKey: 'b' publicKey: 'b'
}); });
keychainMock.requestPrivateKeyDownload.yields(null, {}); keychainMock.requestPrivateKeyDownload.yields(null, {});
angular.module('logintest', []); angular.module('logintest', []);
mocks.module('logintest'); mocks.module('logintest');
mocks.inject(function($controller, $rootScope, $location) { mocks.inject(function($controller, $rootScope, $location) {
location = $location; location = $location;
sinon.stub(location, 'path', function(path) { sinon.stub(location, 'path', function(path) {
expect(path).to.equal('/login-privatekey-download'); expect(path).to.equal('/login-privatekey-download');
expect(startAppStub.calledOnce).to.be.true; expect(startAppStub.calledOnce).to.be.true;
expect(checkForUpdateStub.calledOnce).to.be.true; expect(checkForUpdateStub.calledOnce).to.be.true;
expect(authStub.getEmailAddress.calledOnce).to.be.true; expect(authStub.getEmailAddress.calledOnce).to.be.true;
expect(keychainMock.requestPrivateKeyDownload.calledOnce).to.be.true; expect(keychainMock.requestPrivateKeyDownload.calledOnce).to.be.true;
done(); done();
}); });
scope = $rootScope.$new(); scope = $rootScope.$new();
scope.state = {}; scope.state = {};
ctrl = $controller(LoginCtrl, { ctrl = $controller(LoginCtrl, {
$location: location, $location: location,
$scope: scope $scope: scope
});
}); });
}); });
});
it('should forward to new device login', function(done) { it('should forward to new device login', function(done) {
startAppStub.yields(); startAppStub.yields();
authStub.getEmailAddress.yields(null, { authStub.getEmailAddress.yields(null, {
emailAddress: emailAddress, emailAddress: emailAddress,
realname: 'asd' realname: 'asd'
}); });
initStub.yields(null, { initStub.yields(null, {
publicKey: 'b' publicKey: 'b'
}); });
keychainMock.requestPrivateKeyDownload.yields(); keychainMock.requestPrivateKeyDownload.yields();
angular.module('logintest', []); angular.module('logintest', []);
mocks.module('logintest'); mocks.module('logintest');
mocks.inject(function($controller, $rootScope, $location) { mocks.inject(function($controller, $rootScope, $location) {
location = $location; location = $location;
sinon.stub(location, 'path', function(path) { sinon.stub(location, 'path', function(path) {
expect(path).to.equal('/login-new-device'); expect(path).to.equal('/login-new-device');
expect(startAppStub.calledOnce).to.be.true; expect(startAppStub.calledOnce).to.be.true;
expect(checkForUpdateStub.calledOnce).to.be.true; expect(checkForUpdateStub.calledOnce).to.be.true;
expect(authStub.getEmailAddress.calledOnce).to.be.true; expect(authStub.getEmailAddress.calledOnce).to.be.true;
expect(keychainMock.requestPrivateKeyDownload.calledOnce).to.be.true; expect(keychainMock.requestPrivateKeyDownload.calledOnce).to.be.true;
done(); done();
}); });
scope = $rootScope.$new(); scope = $rootScope.$new();
scope.state = {}; scope.state = {};
ctrl = $controller(LoginCtrl, { ctrl = $controller(LoginCtrl, {
$location: location, $location: location,
$scope: scope $scope: scope
});
}); });
}); });
});
it('should forward to initial login', function(done) { it('should forward to initial login', function(done) {
startAppStub.yields(); startAppStub.yields();
authStub.getEmailAddress.yields(null, { authStub.getEmailAddress.yields(null, {
emailAddress: emailAddress, emailAddress: emailAddress,
realname: 'asd' realname: 'asd'
});
initStub.yields();
angular.module('logintest', []);
mocks.module('logintest');
mocks.inject(function($controller, $rootScope, $location) {
location = $location;
sinon.stub(location, 'path', function(path) {
expect(path).to.equal('/login-initial');
expect(startAppStub.calledOnce).to.be.true;
expect(checkForUpdateStub.calledOnce).to.be.true;
expect(authStub.getEmailAddress.calledOnce).to.be.true;
done();
}); });
initStub.yields(); scope = $rootScope.$new();
scope.state = {};
angular.module('logintest', []); ctrl = $controller(LoginCtrl, {
mocks.module('logintest'); $location: location,
mocks.inject(function($controller, $rootScope, $location) { $scope: scope
location = $location;
sinon.stub(location, 'path', function(path) {
expect(path).to.equal('/login-initial');
expect(startAppStub.calledOnce).to.be.true;
expect(checkForUpdateStub.calledOnce).to.be.true;
expect(authStub.getEmailAddress.calledOnce).to.be.true;
done();
});
scope = $rootScope.$new();
scope.state = {};
ctrl = $controller(LoginCtrl, {
$location: location,
$scope: scope
});
}); });
}); });
}); });

View File

@ -1,116 +1,112 @@
define(function(require) { 'use strict';
'use strict';
var expect = chai.expect, var Auth = require('../../src/js/bo/auth'),
angular = require('angular'), mocks = angular.mocks,
Auth = require('js/bo/auth'), LoginExistingCtrl = require('../../src/js/controller/login-existing'),
mocks = require('angularMocks'), EmailDAO = require('../../src/js/dao/email-dao'),
LoginExistingCtrl = require('js/controller/login-existing'), KeychainDAO = require('../../src/js/dao/keychain-dao'),
EmailDAO = require('js/dao/email-dao'), appController = require('../../src/js/app-controller');
KeychainDAO = require('js/dao/keychain-dao'),
appController = require('js/app-controller');
describe('Login (existing user) Controller unit test', function() { describe('Login (existing user) Controller unit test', function() {
var scope, location, ctrl, origEmailDao, emailDaoMock, var scope, location, ctrl, origEmailDao, emailDaoMock,
origAuth, authMock, origAuth, authMock,
emailAddress = 'fred@foo.com', emailAddress = 'fred@foo.com',
passphrase = 'asd', passphrase = 'asd',
keychainMock; keychainMock;
beforeEach(function() { beforeEach(function() {
// remember original module to restore later // remember original module to restore later
origEmailDao = appController._emailDao; origEmailDao = appController._emailDao;
origAuth = appController._auth; origAuth = appController._auth;
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO); appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO);
appController._auth = authMock = sinon.createStubInstance(Auth); appController._auth = authMock = sinon.createStubInstance(Auth);
keychainMock = sinon.createStubInstance(KeychainDAO); keychainMock = sinon.createStubInstance(KeychainDAO);
emailDaoMock._keychain = keychainMock; emailDaoMock._keychain = keychainMock;
emailDaoMock._account = { emailDaoMock._account = {
emailAddress: emailAddress, emailAddress: emailAddress,
}; };
angular.module('loginexistingtest', []); angular.module('loginexistingtest', []);
mocks.module('loginexistingtest'); mocks.module('loginexistingtest');
mocks.inject(function($rootScope, $controller, $location) { mocks.inject(function($rootScope, $controller, $location) {
location = $location; location = $location;
scope = $rootScope.$new(); scope = $rootScope.$new();
scope.state = {}; scope.state = {};
ctrl = $controller(LoginExistingCtrl, { ctrl = $controller(LoginExistingCtrl, {
$scope: scope, $scope: scope,
$routeParams: {} $routeParams: {}
});
}); });
}); });
});
afterEach(function() { afterEach(function() {
// restore the module // restore the module
appController._emailDao = origEmailDao; appController._emailDao = origEmailDao;
appController._auth = origAuth; appController._auth = origAuth;
});
describe('initial state', function() {
it('should be well defined', function() {
expect(scope.buttonEnabled).to.be.true;
expect(scope.incorrect).to.be.false;
expect(scope.change).to.exist;
expect(scope.confirmPassphrase).to.exist;
}); });
});
describe('initial state', function() { describe('functionality', function() {
it('should be well defined', function() { describe('change', function() {
expect(scope.buttonEnabled).to.be.true; it('should set incorrect to false', function() {
scope.incorrect = true;
scope.change();
expect(scope.incorrect).to.be.false; expect(scope.incorrect).to.be.false;
expect(scope.change).to.exist;
expect(scope.confirmPassphrase).to.exist;
}); });
}); });
describe('functionality', function() { describe('confirm passphrase', function() {
describe('change', function() { it('should unlock crypto and start', function() {
it('should set incorrect to false', function() { var keypair = {},
scope.incorrect = true; pathSpy = sinon.spy(location, 'path');
scope.passphrase = passphrase;
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, keypair);
emailDaoMock.unlock.withArgs({
keypair: keypair,
passphrase: passphrase
}).yields();
authMock.storeCredentials.yields();
scope.change();
expect(scope.incorrect).to.be.false; scope.confirmPassphrase();
});
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
expect(emailDaoMock.unlock.calledOnce).to.be.true;
expect(pathSpy.calledOnce).to.be.true;
expect(pathSpy.calledWith('/desktop')).to.be.true;
}); });
describe('confirm passphrase', function() { it('should not do anything without passphrase', function() {
it('should unlock crypto and start', function() { var pathSpy = sinon.spy(location, 'path');
var keypair = {}, scope.passphrase = '';
pathSpy = sinon.spy(location, 'path');
scope.passphrase = passphrase;
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, keypair);
emailDaoMock.unlock.withArgs({
keypair: keypair,
passphrase: passphrase
}).yields();
authMock.storeCredentials.yields();
scope.confirmPassphrase();
expect(pathSpy.callCount).to.equal(0);
});
scope.confirmPassphrase(); it('should not work when keypair unavailable', function(done) {
scope.passphrase = passphrase;
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(new Error('asd'));
scope.onError = function(err) {
expect(err.message).to.equal('asd');
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true; expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
expect(emailDaoMock.unlock.calledOnce).to.be.true; done();
expect(pathSpy.calledOnce).to.be.true; };
expect(pathSpy.calledWith('/desktop')).to.be.true;
});
it('should not do anything without passphrase', function() { scope.confirmPassphrase();
var pathSpy = sinon.spy(location, 'path');
scope.passphrase = '';
scope.confirmPassphrase();
expect(pathSpy.callCount).to.equal(0);
});
it('should not work when keypair unavailable', function(done) {
scope.passphrase = passphrase;
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(new Error('asd'));
scope.onError = function(err) {
expect(err.message).to.equal('asd');
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
done();
};
scope.confirmPassphrase();
});
}); });
}); });
}); });

View File

@ -1,202 +1,197 @@
define(function(require) { 'use strict';
'use strict';
var expect = chai.expect, var Auth = require('../../src/js/bo/auth'),
angular = require('angular'), mocks = angular.mocks,
Auth = require('js/bo/auth'), LoginInitialCtrl = require('../../src/js/controller/login-initial'),
mocks = require('angularMocks'), PGP = require('../../src/js/crypto/pgp'),
LoginInitialCtrl = require('js/controller/login-initial'), EmailDAO = require('../../src/js/dao/email-dao'),
PGP = require('js/crypto/pgp'), appController = require('../../src/js/app-controller');
EmailDAO = require('js/dao/email-dao'),
appController = require('js/app-controller');
describe('Login (initial user) Controller unit test', function() { describe('Login (initial user) Controller unit test', function() {
var scope, ctrl, location, origEmailDao, emailDaoMock, var scope, ctrl, location, origEmailDao, emailDaoMock,
origAuth, authMock, origAuth, authMock,
emailAddress = 'fred@foo.com', emailAddress = 'fred@foo.com',
keyId, expectedKeyId, keyId, expectedKeyId,
cryptoMock; cryptoMock;
beforeEach(function() {
// remember original module to restore later
origEmailDao = appController._emailDao;
origAuth = appController._auth;
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO);
appController._auth = authMock = sinon.createStubInstance(Auth);
keyId = '9FEB47936E712926';
expectedKeyId = '6E712926';
cryptoMock = sinon.createStubInstance(PGP);
emailDaoMock._crypto = cryptoMock;
emailDaoMock._account = {
emailAddress: emailAddress,
};
angular.module('logininitialtest', []);
mocks.module('logininitialtest');
mocks.inject(function($rootScope, $controller, $location) {
scope = $rootScope.$new();
location = $location;
scope.state = {
ui: {}
};
ctrl = $controller(LoginInitialCtrl, {
$scope: scope,
$routeParams: {}
});
});
});
afterEach(function() {
// restore the module
appController._emailDao = origEmailDao;
appController._auth = origAuth;
});
describe('initial state', function() {
it('should be well defined', function() {
expect(scope.state.ui).to.equal(1);
});
});
describe('signUpToNewsletter', function() {
var xhrMock, requests;
beforeEach(function() { beforeEach(function() {
// remember original module to restore later xhrMock = sinon.useFakeXMLHttpRequest();
origEmailDao = appController._emailDao; requests = [];
origAuth = appController._auth;
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO); xhrMock.onCreate = function(xhr) {
appController._auth = authMock = sinon.createStubInstance(Auth); requests.push(xhr);
keyId = '9FEB47936E712926';
expectedKeyId = '6E712926';
cryptoMock = sinon.createStubInstance(PGP);
emailDaoMock._crypto = cryptoMock;
emailDaoMock._account = {
emailAddress: emailAddress,
}; };
angular.module('logininitialtest', []);
mocks.module('logininitialtest');
mocks.inject(function($rootScope, $controller, $location) {
scope = $rootScope.$new();
location = $location;
scope.state = {
ui: {}
};
ctrl = $controller(LoginInitialCtrl, {
$scope: scope,
$routeParams: {}
});
});
}); });
afterEach(function() { afterEach(function() {
// restore the module xhrMock.restore();
appController._emailDao = origEmailDao;
appController._auth = origAuth;
}); });
describe('initial state', function() { it('should not signup', function() {
it('should be well defined', function() { scope.state.newsletter = false;
scope.signUpToNewsletter();
expect(requests.length).to.equal(0);
});
it('should fail', function(done) {
scope.state.newsletter = true;
scope.signUpToNewsletter(function(err, xhr) {
expect(err).to.exist;
expect(xhr).to.not.exist;
done();
});
expect(requests.length).to.equal(1);
requests[0].onerror('err');
});
it('should work without callback', function() {
scope.state.newsletter = true;
scope.signUpToNewsletter();
expect(requests.length).to.equal(1);
requests[0].respond(200, {
"Content-Type": "text/plain"
}, 'foobar!');
});
});
describe('go to import key', function() {
var signUpToNewsletterStub;
beforeEach(function() {
signUpToNewsletterStub = sinon.stub(scope, 'signUpToNewsletter');
});
afterEach(function() {
signUpToNewsletterStub.restore();
});
it('should not continue if terms are not accepted', function(done) {
scope.state.agree = undefined;
scope.onError = function(err) {
expect(err.message).to.contain('Terms');
expect(signUpToNewsletterStub.called).to.be.false;
done();
};
scope.importKey();
});
it('should work', function() {
scope.state.agree = true;
scope.importKey();
expect(signUpToNewsletterStub.calledOnce).to.be.true;
expect(location.$$path).to.equal('/login-new-device');
});
});
describe('generate key', function() {
var signUpToNewsletterStub;
beforeEach(function() {
signUpToNewsletterStub = sinon.stub(scope, 'signUpToNewsletter');
});
afterEach(function() {
signUpToNewsletterStub.restore();
});
it('should not continue if terms are not accepted', function(done) {
scope.state.agree = undefined;
scope.onError = function(err) {
expect(err.message).to.contain('Terms');
expect(scope.state.ui).to.equal(1); expect(scope.state.ui).to.equal(1);
}); expect(signUpToNewsletterStub.called).to.be.false;
done();
};
scope.generateKey();
}); });
describe('signUpToNewsletter', function() { it('should fail due to error in emailDao.unlock', function(done) {
var xhrMock, requests; scope.state.agree = true;
beforeEach(function() { emailDaoMock.unlock.withArgs({
xhrMock = sinon.useFakeXMLHttpRequest(); passphrase: undefined
requests = []; }).yields(new Error());
authMock.storeCredentials.yields();
xhrMock.onCreate = function(xhr) { scope.onError = function(err) {
requests.push(xhr); expect(err).to.exist;
}; expect(scope.state.ui).to.equal(1);
}); expect(signUpToNewsletterStub.called).to.be.true;
done();
};
afterEach(function() { scope.generateKey();
xhrMock.restore(); expect(scope.state.ui).to.equal(2);
});
it('should not signup', function() {
scope.state.newsletter = false;
scope.signUpToNewsletter();
expect(requests.length).to.equal(0);
});
it('should fail', function(done) {
scope.state.newsletter = true;
scope.signUpToNewsletter(function(err, xhr) {
expect(err).to.exist;
expect(xhr).to.not.exist;
done();
});
expect(requests.length).to.equal(1);
requests[0].onerror('err');
});
it('should work without callback', function() {
scope.state.newsletter = true;
scope.signUpToNewsletter();
expect(requests.length).to.equal(1);
requests[0].respond(200, {
"Content-Type": "text/plain"
}, 'foobar!');
});
}); });
describe('go to import key', function() { it('should unlock crypto', function(done) {
var signUpToNewsletterStub; scope.state.agree = true;
beforeEach(function() {
signUpToNewsletterStub = sinon.stub(scope, 'signUpToNewsletter');
});
afterEach(function() {
signUpToNewsletterStub.restore();
});
it('should not continue if terms are not accepted', function(done) { emailDaoMock.unlock.withArgs({
scope.state.agree = undefined; passphrase: undefined
}).yields();
authMock.storeCredentials.yields();
scope.onError = function(err) { scope.$apply = function() {
expect(err.message).to.contain('Terms');
expect(signUpToNewsletterStub.called).to.be.false;
done();
};
scope.importKey();
});
it('should work', function() {
scope.state.agree = true;
scope.importKey();
expect(signUpToNewsletterStub.calledOnce).to.be.true;
expect(location.$$path).to.equal('/login-new-device');
});
});
describe('generate key', function() {
var signUpToNewsletterStub;
beforeEach(function() {
signUpToNewsletterStub = sinon.stub(scope, 'signUpToNewsletter');
});
afterEach(function() {
signUpToNewsletterStub.restore();
});
it('should not continue if terms are not accepted', function(done) {
scope.state.agree = undefined;
scope.onError = function(err) {
expect(err.message).to.contain('Terms');
expect(scope.state.ui).to.equal(1);
expect(signUpToNewsletterStub.called).to.be.false;
done();
};
scope.generateKey();
});
it('should fail due to error in emailDao.unlock', function(done) {
scope.state.agree = true;
emailDaoMock.unlock.withArgs({
passphrase: undefined
}).yields(new Error());
authMock.storeCredentials.yields();
scope.onError = function(err) {
expect(err).to.exist;
expect(scope.state.ui).to.equal(1);
expect(signUpToNewsletterStub.called).to.be.true;
done();
};
scope.generateKey();
expect(scope.state.ui).to.equal(2); expect(scope.state.ui).to.equal(2);
}); expect(location.$$path).to.equal('/desktop');
expect(emailDaoMock.unlock.calledOnce).to.be.true;
done();
};
it('should unlock crypto', function(done) { scope.generateKey();
scope.state.agree = true;
emailDaoMock.unlock.withArgs({
passphrase: undefined
}).yields();
authMock.storeCredentials.yields();
scope.$apply = function() {
expect(scope.state.ui).to.equal(2);
expect(location.$$path).to.equal('/desktop');
expect(emailDaoMock.unlock.calledOnce).to.be.true;
done();
};
scope.generateKey();
});
}); });
}); });
}); });

View File

@ -1,190 +1,186 @@
define(function(require) { 'use strict';
'use strict';
var expect = chai.expect, var mocks = angular.mocks,
angular = require('angular'), PGP = require('../../src/js/crypto/pgp'),
mocks = require('angularMocks'), LoginNewDeviceCtrl = require('../../src/js/controller/login-new-device'),
PGP = require('js/crypto/pgp'), KeychainDAO = require('../../src/js/dao/keychain-dao'),
LoginNewDeviceCtrl = require('js/controller/login-new-device'), EmailDAO = require('../../src/js/dao/email-dao'),
KeychainDAO = require('js/dao/keychain-dao'), appController = require('../../src/js/app-controller');
EmailDAO = require('js/dao/email-dao'),
appController = require('js/app-controller');
describe('Login (new device) Controller unit test', function() { describe('Login (new device) Controller unit test', function() {
var scope, ctrl, origEmailDao, emailDaoMock, pgpMock, var scope, ctrl, origEmailDao, emailDaoMock, pgpMock,
emailAddress = 'fred@foo.com', emailAddress = 'fred@foo.com',
passphrase = 'asd', passphrase = 'asd',
keyId, keyId,
keychainMock; keychainMock;
beforeEach(function() { beforeEach(function() {
// remember original module to restore later // remember original module to restore later
origEmailDao = appController._emailDao; origEmailDao = appController._emailDao;
emailDaoMock = sinon.createStubInstance(EmailDAO); emailDaoMock = sinon.createStubInstance(EmailDAO);
appController._emailDao = emailDaoMock; appController._emailDao = emailDaoMock;
keyId = '9FEB47936E712926'; keyId = '9FEB47936E712926';
emailDaoMock._keychain = keychainMock = sinon.createStubInstance(KeychainDAO); emailDaoMock._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
appController._pgp = pgpMock = sinon.createStubInstance(PGP); appController._pgp = pgpMock = sinon.createStubInstance(PGP);
pgpMock.extractPublicKey.returns('publicKeyArmored'); pgpMock.extractPublicKey.returns('publicKeyArmored');
emailDaoMock._account = { emailDaoMock._account = {
emailAddress: emailAddress, emailAddress: emailAddress,
};
angular.module('loginnewdevicetest', []);
mocks.module('loginnewdevicetest');
mocks.inject(function($rootScope, $controller) {
scope = $rootScope.$new();
scope.state = {
ui: {}
}; };
ctrl = $controller(LoginNewDeviceCtrl, {
angular.module('loginnewdevicetest', []); $scope: scope,
mocks.module('loginnewdevicetest'); $routeParams: {}
mocks.inject(function($rootScope, $controller) {
scope = $rootScope.$new();
scope.state = {
ui: {}
};
ctrl = $controller(LoginNewDeviceCtrl, {
$scope: scope,
$routeParams: {}
});
});
});
afterEach(function() {
// restore the module
appController._emailDao = origEmailDao;
});
describe('initial state', function() {
it('should be well defined', function() {
expect(scope.incorrect).to.be.false;
expect(scope.confirmPassphrase).to.exist;
});
});
describe('confirm passphrase', function() {
it('should unlock crypto with a public key on the server', function() {
scope.passphrase = passphrase;
scope.key = {
privateKeyArmored: 'b'
};
pgpMock.getKeyParams.returns({
_id: 'id',
userIds: []
});
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
_id: keyId,
publicKey: 'a'
});
emailDaoMock.unlock.withArgs(sinon.match.any, passphrase).yields();
keychainMock.putUserKeyPair.yields();
scope.confirmPassphrase();
expect(emailDaoMock.unlock.calledOnce).to.be.true;
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
});
it('should unlock crypto with no key on the server', function() {
scope.passphrase = passphrase;
scope.key = {
privateKeyArmored: 'b',
publicKeyArmored: 'a'
};
pgpMock.getKeyParams.returns({
_id: 'id',
userIds: []
});
keychainMock.getUserKeyPair.withArgs(emailAddress).yields();
emailDaoMock.unlock.withArgs(sinon.match.any, passphrase).yields();
keychainMock.putUserKeyPair.yields();
scope.confirmPassphrase();
expect(emailDaoMock.unlock.calledOnce).to.be.true;
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
});
it('should not work when keypair upload fails', function(done) {
scope.passphrase = passphrase;
scope.key = {
privateKeyArmored: 'b'
};
pgpMock.getKeyParams.returns({
_id: 'id',
userIds: []
});
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
_id: keyId,
publicKey: 'a'
});
emailDaoMock.unlock.yields();
keychainMock.putUserKeyPair.yields({
errMsg: 'yo mamma.'
});
scope.onError = function(err) {
expect(err.errMsg).to.equal('yo mamma.');
done();
};
scope.confirmPassphrase();
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
expect(emailDaoMock.unlock.calledOnce).to.be.true;
expect(keychainMock.putUserKeyPair.calledOnce).to.be.true;
});
it('should not work when unlock fails', function(done) {
scope.passphrase = passphrase;
scope.key = {
privateKeyArmored: 'b'
};
pgpMock.getKeyParams.returns({
_id: 'id',
userIds: []
});
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
_id: keyId,
publicKey: 'a'
});
emailDaoMock.unlock.yields({
errMsg: 'yo mamma.'
});
scope.onError = function(err) {
expect(err.errMsg).to.equal('yo mamma.');
done();
};
scope.confirmPassphrase();
expect(scope.incorrect).to.be.true;
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
expect(emailDaoMock.unlock.calledOnce).to.be.true;
});
it('should not work when keypair retrieval', function(done) {
scope.passphrase = passphrase;
keychainMock.getUserKeyPair.withArgs(emailAddress).yields({
errMsg: 'yo mamma.'
});
scope.onError = function(err) {
expect(err.errMsg).to.equal('yo mamma.');
done();
};
scope.confirmPassphrase();
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
}); });
}); });
}); });
afterEach(function() {
// restore the module
appController._emailDao = origEmailDao;
});
describe('initial state', function() {
it('should be well defined', function() {
expect(scope.incorrect).to.be.false;
expect(scope.confirmPassphrase).to.exist;
});
});
describe('confirm passphrase', function() {
it('should unlock crypto with a public key on the server', function() {
scope.passphrase = passphrase;
scope.key = {
privateKeyArmored: 'b'
};
pgpMock.getKeyParams.returns({
_id: 'id',
userIds: []
});
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
_id: keyId,
publicKey: 'a'
});
emailDaoMock.unlock.withArgs(sinon.match.any, passphrase).yields();
keychainMock.putUserKeyPair.yields();
scope.confirmPassphrase();
expect(emailDaoMock.unlock.calledOnce).to.be.true;
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
});
it('should unlock crypto with no key on the server', function() {
scope.passphrase = passphrase;
scope.key = {
privateKeyArmored: 'b',
publicKeyArmored: 'a'
};
pgpMock.getKeyParams.returns({
_id: 'id',
userIds: []
});
keychainMock.getUserKeyPair.withArgs(emailAddress).yields();
emailDaoMock.unlock.withArgs(sinon.match.any, passphrase).yields();
keychainMock.putUserKeyPair.yields();
scope.confirmPassphrase();
expect(emailDaoMock.unlock.calledOnce).to.be.true;
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
});
it('should not work when keypair upload fails', function(done) {
scope.passphrase = passphrase;
scope.key = {
privateKeyArmored: 'b'
};
pgpMock.getKeyParams.returns({
_id: 'id',
userIds: []
});
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
_id: keyId,
publicKey: 'a'
});
emailDaoMock.unlock.yields();
keychainMock.putUserKeyPair.yields({
errMsg: 'yo mamma.'
});
scope.onError = function(err) {
expect(err.errMsg).to.equal('yo mamma.');
done();
};
scope.confirmPassphrase();
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
expect(emailDaoMock.unlock.calledOnce).to.be.true;
expect(keychainMock.putUserKeyPair.calledOnce).to.be.true;
});
it('should not work when unlock fails', function(done) {
scope.passphrase = passphrase;
scope.key = {
privateKeyArmored: 'b'
};
pgpMock.getKeyParams.returns({
_id: 'id',
userIds: []
});
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
_id: keyId,
publicKey: 'a'
});
emailDaoMock.unlock.yields({
errMsg: 'yo mamma.'
});
scope.onError = function(err) {
expect(err.errMsg).to.equal('yo mamma.');
done();
};
scope.confirmPassphrase();
expect(scope.incorrect).to.be.true;
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
expect(emailDaoMock.unlock.calledOnce).to.be.true;
});
it('should not work when keypair retrieval', function(done) {
scope.passphrase = passphrase;
keychainMock.getUserKeyPair.withArgs(emailAddress).yields({
errMsg: 'yo mamma.'
});
scope.onError = function(err) {
expect(err.errMsg).to.equal('yo mamma.');
done();
};
scope.confirmPassphrase();
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
});
});
}); });

View File

@ -1,39 +1,250 @@
define(function(require) { 'use strict';
'use strict';
var expect = chai.expect, var mocks = angular.mocks,
angular = require('angular'), Auth = require('../../src/js/bo/auth'),
mocks = require('angularMocks'), LoginPrivateKeyDownloadCtrl = require('../../src/js/controller/login-privatekey-download'),
Auth = require('js/bo/auth'), EmailDAO = require('../../src/js/dao/email-dao'),
LoginPrivateKeyDownloadCtrl = require('js/controller/login-privatekey-download'), appController = require('../../src/js/app-controller'),
EmailDAO = require('js/dao/email-dao'), KeychainDAO = require('../../src/js/dao/keychain-dao');
appController = require('js/app-controller'),
KeychainDAO = require('js/dao/keychain-dao');
describe('Login Private Key Download Controller unit test', function() { describe('Login Private Key Download Controller unit test', function() {
var scope, location, ctrl, var scope, location, ctrl,
origEmailDao, emailDaoMock, origEmailDao, emailDaoMock,
origAuth, authMock, origAuth, authMock,
origKeychain, keychainMock, origKeychain, keychainMock,
emailAddress = 'fred@foo.com'; emailAddress = 'fred@foo.com';
beforeEach(function(done) { beforeEach(function(done) {
// remember original module to restore later, then replace it // remember original module to restore later, then replace it
origEmailDao = appController._emailDao; origEmailDao = appController._emailDao;
origKeychain = appController._keychain; origKeychain = appController._keychain;
origAuth = appController._auth; origAuth = appController._auth;
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO); appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO);
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO); appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
appController._auth = authMock = sinon.createStubInstance(Auth); appController._auth = authMock = sinon.createStubInstance(Auth);
emailDaoMock._account = { emailDaoMock._account = {
emailAddress: emailAddress emailAddress: emailAddress
};
angular.module('login-privatekey-download-test', []);
mocks.module('login-privatekey-download-test');
mocks.inject(function($controller, $rootScope) {
scope = $rootScope.$new();
scope.state = {};
ctrl = $controller(LoginPrivateKeyDownloadCtrl, {
$location: location,
$scope: scope,
$routeParams: {}
});
done();
});
});
afterEach(function() {
// restore the app controller module
appController._emailDao = origEmailDao;
appController._keychain = origKeychain;
appController._auth = origAuth;
});
describe('initialization', function() {
it('should work', function() {
expect(scope.step).to.equal(1);
});
});
describe('verifyRecoveryToken', function() {
var testKeypair = {
publicKey: {
_id: 'id'
}
};
it('should fail for empty recovery token', function(done) {
scope.onError = function(err) {
expect(err).to.exist;
done();
}; };
angular.module('login-privatekey-download-test', []); scope.recoveryToken = undefined;
mocks.module('login-privatekey-download-test'); scope.verifyRecoveryToken();
mocks.inject(function($controller, $rootScope) { });
it('should fail in keychain.getUserKeyPair', function(done) {
keychainMock.getUserKeyPair.yields(42);
scope.onError = function(err) {
expect(err).to.exist;
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
done();
};
scope.recoveryToken = 'token';
scope.verifyRecoveryToken();
});
it('should fail in keychain.downloadPrivateKey', function(done) {
keychainMock.getUserKeyPair.yields(null, testKeypair);
keychainMock.downloadPrivateKey.yields(42);
scope.onError = function(err) {
expect(err).to.exist;
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
expect(keychainMock.downloadPrivateKey.calledOnce).to.be.true;
done();
};
scope.recoveryToken = 'token';
scope.verifyRecoveryToken();
});
it('should work', function(done) {
keychainMock.getUserKeyPair.yields(null, testKeypair);
keychainMock.downloadPrivateKey.yields(null, 'encryptedPrivateKey');
scope.recoveryToken = 'token';
scope.verifyRecoveryToken(function() {
expect(scope.encryptedPrivateKey).to.equal('encryptedPrivateKey');
done();
});
});
});
describe('handlePaste', function() {
it('should work', function() {
scope.handlePaste({
clipboardData: {
getData: function(val) {
expect(val).to.equal('text/plain');
return '1qaz-2wsx-3edc-4rfv-5tgb-6yhn';
}
}
});
expect(scope.code0).to.equal('1qaz');
expect(scope.code1).to.equal('2wsx');
expect(scope.code2).to.equal('3edc');
expect(scope.code3).to.equal('4rfv');
expect(scope.code4).to.equal('5tgb');
expect(scope.code5).to.equal('6yhn');
});
});
describe('decryptAndStorePrivateKeyLocally', function() {
beforeEach(function() {
scope.code0 = '0';
scope.code1 = '1';
scope.code2 = '2';
scope.code3 = '3';
scope.code4 = '4';
scope.code5 = '5';
scope.encryptedPrivateKey = {
encryptedPrivateKey: 'encryptedPrivateKey'
};
scope.cachedKeypair = {
publicKey: {
_id: 'keyId'
}
};
});
it('should fail on empty code', function(done) {
scope.code0 = '';
scope.code1 = '';
scope.code2 = '';
scope.code3 = '';
scope.code4 = '';
scope.code5 = '';
scope.onError = function(err) {
expect(err).to.exist;
done();
};
scope.decryptAndStorePrivateKeyLocally();
});
it('should fail on decryptAndStorePrivateKeyLocally', function(done) {
keychainMock.decryptAndStorePrivateKeyLocally.yields(42);
scope.onError = function(err) {
expect(err).to.exist;
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
done();
};
scope.decryptAndStorePrivateKeyLocally();
});
it('should goto /login-existing on emailDao.unlock fail', function(done) {
keychainMock.decryptAndStorePrivateKeyLocally.yields(null, {
encryptedKey: 'keyArmored'
});
emailDaoMock.unlock.yields(42);
scope.goTo = function(location) {
expect(location).to.equal('/login-existing');
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
expect(emailDaoMock.unlock.calledOnce).to.be.true;
done();
};
scope.decryptAndStorePrivateKeyLocally();
});
it('should goto /desktop on emailDao.unlock success', function(done) {
keychainMock.decryptAndStorePrivateKeyLocally.yields(null, {
encryptedKey: 'keyArmored'
});
emailDaoMock.unlock.yields();
authMock.storeCredentials.yields();
scope.goTo = function(location) {
expect(location).to.equal('/desktop');
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
expect(emailDaoMock.unlock.calledOnce).to.be.true;
done();
};
scope.decryptAndStorePrivateKeyLocally();
});
});
describe('goForward', function() {
it('should work in step 1', function() {
var verifyRecoveryTokenStub = sinon.stub(scope, 'verifyRecoveryToken');
verifyRecoveryTokenStub.yields();
scope.step = 1;
scope.goForward();
expect(verifyRecoveryTokenStub.calledOnce).to.be.true;
expect(scope.step).to.equal(2);
verifyRecoveryTokenStub.restore();
});
it('should work in step 2', function() {
var decryptAndStorePrivateKeyLocallyStub = sinon.stub(scope, 'decryptAndStorePrivateKeyLocally');
decryptAndStorePrivateKeyLocallyStub.returns();
scope.step = 2;
scope.goForward();
expect(decryptAndStorePrivateKeyLocallyStub.calledOnce).to.be.true;
decryptAndStorePrivateKeyLocallyStub.restore();
});
});
describe('goTo', function() {
it('should work', function(done) {
mocks.inject(function($controller, $rootScope, $location) {
location = $location;
sinon.stub(location, 'path', function(path) {
expect(path).to.equal('/desktop');
done();
});
scope = $rootScope.$new(); scope = $rootScope.$new();
scope.state = {}; scope.state = {};
ctrl = $controller(LoginPrivateKeyDownloadCtrl, { ctrl = $controller(LoginPrivateKeyDownloadCtrl, {
@ -41,224 +252,9 @@ define(function(require) {
$scope: scope, $scope: scope,
$routeParams: {} $routeParams: {}
}); });
done();
});
});
afterEach(function() {
// restore the app controller module
appController._emailDao = origEmailDao;
appController._keychain = origKeychain;
appController._auth = origAuth;
});
describe('initialization', function() {
it('should work', function() {
expect(scope.step).to.equal(1);
});
});
describe('verifyRecoveryToken', function() {
var testKeypair = {
publicKey: {
_id: 'id'
}
};
it('should fail for empty recovery token', function(done) {
scope.onError = function(err) {
expect(err).to.exist;
done();
};
scope.recoveryToken = undefined;
scope.verifyRecoveryToken();
}); });
it('should fail in keychain.getUserKeyPair', function(done) { scope.goTo('/desktop');
keychainMock.getUserKeyPair.yields(42);
scope.onError = function(err) {
expect(err).to.exist;
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
done();
};
scope.recoveryToken = 'token';
scope.verifyRecoveryToken();
});
it('should fail in keychain.downloadPrivateKey', function(done) {
keychainMock.getUserKeyPair.yields(null, testKeypair);
keychainMock.downloadPrivateKey.yields(42);
scope.onError = function(err) {
expect(err).to.exist;
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
expect(keychainMock.downloadPrivateKey.calledOnce).to.be.true;
done();
};
scope.recoveryToken = 'token';
scope.verifyRecoveryToken();
});
it('should work', function(done) {
keychainMock.getUserKeyPair.yields(null, testKeypair);
keychainMock.downloadPrivateKey.yields(null, 'encryptedPrivateKey');
scope.recoveryToken = 'token';
scope.verifyRecoveryToken(function() {
expect(scope.encryptedPrivateKey).to.equal('encryptedPrivateKey');
done();
});
});
});
describe('handlePaste', function() {
it('should work', function() {
scope.handlePaste({
clipboardData: {
getData: function(val) {
expect(val).to.equal('text/plain');
return '1qaz-2wsx-3edc-4rfv-5tgb-6yhn';
}
}
});
expect(scope.code0).to.equal('1qaz');
expect(scope.code1).to.equal('2wsx');
expect(scope.code2).to.equal('3edc');
expect(scope.code3).to.equal('4rfv');
expect(scope.code4).to.equal('5tgb');
expect(scope.code5).to.equal('6yhn');
});
});
describe('decryptAndStorePrivateKeyLocally', function() {
beforeEach(function() {
scope.code0 = '0';
scope.code1 = '1';
scope.code2 = '2';
scope.code3 = '3';
scope.code4 = '4';
scope.code5 = '5';
scope.encryptedPrivateKey = {
encryptedPrivateKey: 'encryptedPrivateKey'
};
scope.cachedKeypair = {
publicKey: {
_id: 'keyId'
}
};
});
it('should fail on empty code', function(done) {
scope.code0 = '';
scope.code1 = '';
scope.code2 = '';
scope.code3 = '';
scope.code4 = '';
scope.code5 = '';
scope.onError = function(err) {
expect(err).to.exist;
done();
};
scope.decryptAndStorePrivateKeyLocally();
});
it('should fail on decryptAndStorePrivateKeyLocally', function(done) {
keychainMock.decryptAndStorePrivateKeyLocally.yields(42);
scope.onError = function(err) {
expect(err).to.exist;
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
done();
};
scope.decryptAndStorePrivateKeyLocally();
});
it('should goto /login-existing on emailDao.unlock fail', function(done) {
keychainMock.decryptAndStorePrivateKeyLocally.yields(null, {
encryptedKey: 'keyArmored'
});
emailDaoMock.unlock.yields(42);
scope.goTo = function(location) {
expect(location).to.equal('/login-existing');
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
expect(emailDaoMock.unlock.calledOnce).to.be.true;
done();
};
scope.decryptAndStorePrivateKeyLocally();
});
it('should goto /desktop on emailDao.unlock success', function(done) {
keychainMock.decryptAndStorePrivateKeyLocally.yields(null, {
encryptedKey: 'keyArmored'
});
emailDaoMock.unlock.yields();
authMock.storeCredentials.yields();
scope.goTo = function(location) {
expect(location).to.equal('/desktop');
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
expect(emailDaoMock.unlock.calledOnce).to.be.true;
done();
};
scope.decryptAndStorePrivateKeyLocally();
});
});
describe('goForward', function() {
it('should work in step 1', function() {
var verifyRecoveryTokenStub = sinon.stub(scope, 'verifyRecoveryToken');
verifyRecoveryTokenStub.yields();
scope.step = 1;
scope.goForward();
expect(verifyRecoveryTokenStub.calledOnce).to.be.true;
expect(scope.step).to.equal(2);
verifyRecoveryTokenStub.restore();
});
it('should work in step 2', function() {
var decryptAndStorePrivateKeyLocallyStub = sinon.stub(scope, 'decryptAndStorePrivateKeyLocally');
decryptAndStorePrivateKeyLocallyStub.returns();
scope.step = 2;
scope.goForward();
expect(decryptAndStorePrivateKeyLocallyStub.calledOnce).to.be.true;
decryptAndStorePrivateKeyLocallyStub.restore();
});
});
describe('goTo', function() {
it('should work', function(done) {
mocks.inject(function($controller, $rootScope, $location) {
location = $location;
sinon.stub(location, 'path', function(path) {
expect(path).to.equal('/desktop');
done();
});
scope = $rootScope.$new();
scope.state = {};
ctrl = $controller(LoginPrivateKeyDownloadCtrl, {
$location: location,
$scope: scope,
$routeParams: {}
});
});
scope.goTo('/desktop');
});
}); });
}); });
}); });

View File

@ -1,101 +1,97 @@
define(function(require) { 'use strict';
'use strict';
var expect = chai.expect, var mocks = angular.mocks,
angular = require('angular'), Auth = require('../../src/js/bo/auth'),
mocks = require('angularMocks'), ConnectionDoctor = require('../../src/js/util/connection-doctor'),
Auth = require('js/bo/auth'), SetCredentialsCtrl = require('../../src/js/controller/login-set-credentials'),
ConnectionDoctor = require('js/util/connection-doctor'), appController = require('../../src/js/app-controller');
SetCredentialsCtrl = require('js/controller/login-set-credentials'),
appController = require('js/app-controller');
describe('Login (Set Credentials) Controller unit test', function() { describe('Login (Set Credentials) Controller unit test', function() {
// Angular parameters // Angular parameters
var scope, location, provider; var scope, location, provider;
// Stubs // Stubs
var auth, origAuth, doctor, origDoctor; var auth, origAuth, doctor, origDoctor;
// SUT // SUT
var setCredentialsCtrl; var setCredentialsCtrl;
beforeEach(function() { beforeEach(function() {
// remeber pre-test state to restore later // remeber pre-test state to restore later
origAuth = appController._auth; origAuth = appController._auth;
origDoctor = appController._doctor; origDoctor = appController._doctor;
auth = appController._auth = sinon.createStubInstance(Auth); auth = appController._auth = sinon.createStubInstance(Auth);
doctor = appController._doctor = sinon.createStubInstance(ConnectionDoctor); doctor = appController._doctor = sinon.createStubInstance(ConnectionDoctor);
// setup the controller // setup the controller
angular.module('setcredentialstest', []); angular.module('setcredentialstest', []);
mocks.module('setcredentialstest'); mocks.module('setcredentialstest');
mocks.inject(function($rootScope, $controller, $location) { mocks.inject(function($rootScope, $controller, $location) {
scope = $rootScope.$new(); scope = $rootScope.$new();
location = $location; location = $location;
location.search({ location.search({
provider: provider provider: provider
});
scope.state = {};
setCredentialsCtrl = $controller(SetCredentialsCtrl, {
$scope: scope,
$routeParams: {}
});
}); });
});
afterEach(function() { scope.state = {};
// restore pre-test state setCredentialsCtrl = $controller(SetCredentialsCtrl, {
appController._auth = origAuth; $scope: scope,
appController._doctor = origDoctor; $routeParams: {}
});
describe('set credentials', function() {
it('should work', function() {
scope.emailAddress = 'emailemailemailemail';
scope.password = 'passwdpasswdpasswdpasswd';
scope.smtpHost = 'hosthosthost';
scope.smtpPort = 1337;
scope.smtpEncryption = '1'; // STARTTLS
scope.imapHost = 'hosthosthost';
scope.imapPort = 1337;
scope.imapEncryption = '2'; // TLS
scope.realname = 'peter pan';
var expectedCredentials = {
provider: provider,
emailAddress: scope.emailAddress,
username: scope.username || scope.emailAddress,
realname: scope.realname,
password: scope.password,
xoauth2: undefined,
imap: {
host: scope.imapHost.toLowerCase(),
port: scope.imapPort,
secure: true,
ignoreTLS: false,
ca: undefined,
pinned: false
},
smtp: {
host: scope.smtpHost.toLowerCase(),
port: scope.smtpPort,
secure: false,
ignoreTLS: false,
ca: undefined,
pinned: false
}
};
doctor.check.yields(); // synchronous yields!
scope.test();
expect(doctor.check.calledOnce).to.be.true;
expect(doctor.configure.calledOnce).to.be.true;
expect(doctor.configure.calledWith(expectedCredentials)).to.be.true;
expect(auth.setCredentials.calledOnce).to.be.true;
}); });
}); });
}); });
afterEach(function() {
// restore pre-test state
appController._auth = origAuth;
appController._doctor = origDoctor;
});
describe('set credentials', function() {
it('should work', function() {
scope.emailAddress = 'emailemailemailemail';
scope.password = 'passwdpasswdpasswdpasswd';
scope.smtpHost = 'hosthosthost';
scope.smtpPort = 1337;
scope.smtpEncryption = '1'; // STARTTLS
scope.imapHost = 'hosthosthost';
scope.imapPort = 1337;
scope.imapEncryption = '2'; // TLS
scope.realname = 'peter pan';
var expectedCredentials = {
provider: provider,
emailAddress: scope.emailAddress,
username: scope.username || scope.emailAddress,
realname: scope.realname,
password: scope.password,
xoauth2: undefined,
imap: {
host: scope.imapHost.toLowerCase(),
port: scope.imapPort,
secure: true,
ignoreTLS: false,
ca: undefined,
pinned: false
},
smtp: {
host: scope.smtpHost.toLowerCase(),
port: scope.smtpPort,
secure: false,
ignoreTLS: false,
ca: undefined,
pinned: false
}
};
doctor.check.yields(); // synchronous yields!
scope.test();
expect(doctor.check.calledOnce).to.be.true;
expect(doctor.configure.calledOnce).to.be.true;
expect(doctor.configure.calledWith(expectedCredentials)).to.be.true;
expect(auth.setCredentials.calledOnce).to.be.true;
});
});
}); });

View File

@ -1,451 +1,447 @@
define(function(require) { 'use strict';
'use strict';
var expect = chai.expect, var mocks = angular.mocks,
angular = require('angular'), MailListCtrl = require('../../src/js/controller/mail-list'),
mocks = require('angularMocks'), EmailDAO = require('../../src/js/dao/email-dao'),
MailListCtrl = require('js/controller/mail-list'), DeviceStorageDAO = require('../../src/js/dao/devicestorage-dao'),
EmailDAO = require('js/dao/email-dao'), KeychainDAO = require('../../src/js/dao/keychain-dao'),
DeviceStorageDAO = require('js/dao/devicestorage-dao'), appController = require('../../src/js/app-controller'),
KeychainDAO = require('js/dao/keychain-dao'), notification = require('../../src/js/util/notification');
appController = require('js/app-controller'),
notification = require('js/util/notification');
chai.Assertion.includeStack = true; chai.Assertion.includeStack = true;
describe('Mail List controller unit test', function() { describe('Mail List controller unit test', function() {
var scope, ctrl, origEmailDao, emailDaoMock, keychainMock, deviceStorageMock, var scope, ctrl, origEmailDao, emailDaoMock, keychainMock, deviceStorageMock,
emailAddress, emails, emailAddress, emails,
hasChrome, hasSocket, hasRuntime, hasIdentity; hasChrome, hasSocket, hasRuntime, hasIdentity;
beforeEach(function() { beforeEach(function() {
hasChrome = !!window.chrome; hasChrome = !!window.chrome;
hasSocket = !!window.chrome.socket; hasSocket = !!window.chrome.socket;
hasIdentity = !!window.chrome.identity; hasIdentity = !!window.chrome.identity;
if (!hasChrome) { if (!hasChrome) {
window.chrome = {}; window.chrome = {};
} }
if (!hasSocket) { if (!hasSocket) {
window.chrome.socket = {}; window.chrome.socket = {};
} }
if (!hasRuntime) { if (!hasRuntime) {
window.chrome.runtime = { window.chrome.runtime = {
getURL: function() {} getURL: function() {}
}; };
} }
if (!hasIdentity) { if (!hasIdentity) {
window.chrome.identity = {}; window.chrome.identity = {};
} }
emails = [{ emails = [{
unread: true unread: true
}, { }, {
unread: true unread: true
}, { }, {
unread: true unread: true
}]; }];
appController._outboxBo = { appController._outboxBo = {
pendingEmails: emails pendingEmails: emails
};
origEmailDao = appController._emailDao;
emailDaoMock = sinon.createStubInstance(EmailDAO);
appController._emailDao = emailDaoMock;
emailAddress = 'fred@foo.com';
emailDaoMock._account = {
emailAddress: emailAddress,
};
keychainMock = sinon.createStubInstance(KeychainDAO);
appController._keychain = keychainMock;
deviceStorageMock = sinon.createStubInstance(DeviceStorageDAO);
emailDaoMock._devicestorage = deviceStorageMock;
angular.module('maillisttest', []);
mocks.module('maillisttest');
mocks.inject(function($rootScope, $controller) {
scope = $rootScope.$new();
scope.state = {
read: {
toggle: function() {}
}
}; };
origEmailDao = appController._emailDao; scope.loadVisibleBodies = function() {};
emailDaoMock = sinon.createStubInstance(EmailDAO); ctrl = $controller(MailListCtrl, {
appController._emailDao = emailDaoMock; $scope: scope,
emailAddress = 'fred@foo.com'; $routeParams: {}
emailDaoMock._account = {
emailAddress: emailAddress,
};
keychainMock = sinon.createStubInstance(KeychainDAO);
appController._keychain = keychainMock;
deviceStorageMock = sinon.createStubInstance(DeviceStorageDAO);
emailDaoMock._devicestorage = deviceStorageMock;
angular.module('maillisttest', []);
mocks.module('maillisttest');
mocks.inject(function($rootScope, $controller) {
scope = $rootScope.$new();
scope.state = {
read: {
toggle: function() {}
}
};
scope.loadVisibleBodies = function() {};
ctrl = $controller(MailListCtrl, {
$scope: scope,
$routeParams: {}
});
});
});
afterEach(function() {
if (!hasSocket) {
delete window.chrome.socket;
}
if (!hasRuntime) {
delete window.chrome.runtime;
}
if (!hasChrome) {
delete window.chrome;
}
if (!hasIdentity) {
delete window.chrome.identity;
}
// restore the module
appController._emailDao = origEmailDao;
});
describe('displayMore', function() {
beforeEach(function() {
scope.state.nav = {
currentFolder: {
messages: ['a', 'b']
}
};
});
it('should not do anything when display length equals messages length', function() {
scope.displayMessages = ['a', 'b'];
scope.displayMore();
expect(scope.displayMessages.length).to.equal(scope.state.nav.currentFolder.messages.length);
});
it('should append next message interval', function() {
scope.displayMessages = ['a'];
scope.displayMore();
expect(scope.displayMessages.length).to.equal(scope.state.nav.currentFolder.messages.length);
});
});
describe('displaySearchResults', function() {
var clock;
beforeEach(function() {
scope.state.nav = {
currentFolder: {
messages: ['a', 'b']
}
};
scope.watchMessages();
scope.watchOnline();
clock = sinon.useFakeTimers();
});
afterEach(function() {
clock.restore();
});
it('should show initial message on empty', function() {
scope.displaySearchResults();
expect(scope.searching).to.be.false;
expect(scope.lastUpdateLbl).to.equal('Online');
expect(scope.displayMessages.length).to.equal(2);
});
it('should show initial message on empty', function() {
var searchStub = sinon.stub(scope, 'search');
searchStub.returns(['a']);
scope.displaySearchResults('query');
expect(scope.searching).to.be.true;
expect(scope.lastUpdateLbl).to.equal('Searching ...');
clock.tick(500);
expect(scope.displayMessages).to.deep.equal(['a']);
expect(scope.searching).to.be.false;
expect(scope.lastUpdateLbl).to.equal('Matches in this folder');
});
});
describe('search', function() {
var message1 = {
to: [{
name: 'name1',
address: 'address1'
}],
subject: 'subject1',
body: 'body1',
html: 'html1'
},
message2 = {
to: [{
name: 'name2',
address: 'address2'
}],
subject: 'subject2',
body: 'body2',
html: 'html2'
},
message3 = {
to: [{
name: 'name3',
address: 'address3'
}],
subject: 'subject3',
body: 'body1',
html: 'html1',
encrypted: true
},
message4 = {
to: [{
name: 'name4',
address: 'address4'
}],
subject: 'subject4',
body: 'body1',
html: 'html1',
encrypted: true,
decrypted: true
},
testMessages = [message1, message2, message3, message4];
it('return same messages array on empty query string', function() {
var result = scope.search(testMessages, '');
expect(result).to.equal(testMessages);
});
it('return message1 on matching subject', function() {
var result = scope.search(testMessages, 'subject1');
expect(result.length).to.equal(1);
expect(result[0]).to.equal(message1);
});
it('return message1 on matching name', function() {
var result = scope.search(testMessages, 'name1');
expect(result.length).to.equal(1);
expect(result[0]).to.equal(message1);
});
it('return message1 on matching address', function() {
var result = scope.search(testMessages, 'address1');
expect(result.length).to.equal(1);
expect(result[0]).to.equal(message1);
});
it('return plaintext and decrypted messages on matching body', function() {
var result = scope.search(testMessages, 'body1');
expect(result.length).to.equal(2);
expect(result[0]).to.equal(message1);
expect(result[1]).to.equal(message4);
});
it('return plaintext and decrypted messages on matching html', function() {
var result = scope.search(testMessages, 'html1');
expect(result.length).to.equal(2);
expect(result[0]).to.equal(message1);
expect(result[1]).to.equal(message4);
});
});
describe('scope variables', function() {
it('should be set correctly', function() {
expect(scope.select).to.exist;
expect(scope.remove).to.exist;
expect(scope.state.mailList).to.exist;
});
});
describe('push notification', function() {
beforeEach(function() {
scope._stopWatchTask();
});
afterEach(function() {
notification.create.restore();
});
it('should succeed for single mail', function(done) {
var mail = {
uid: 123,
from: [{
address: 'asd'
}],
subject: 'this is the subject!',
unread: true
};
sinon.stub(notification, 'create', function(opts) {
expect(opts.title).to.equal(mail.from[0].address);
expect(opts.message).to.equal(mail.subject);
opts.onClick();
expect(scope.state.mailList.selected).to.equal(mail);
done();
});
scope.state.nav = {
currentFolder: {
type: 'asd',
messages: [mail]
}
};
emailDaoMock.onIncomingMessage([mail]);
});
it('should succeed for multiple mails', function(done) {
var mails = [{
uid: 1,
from: [{
address: 'asd'
}],
subject: 'this is the subject!',
unread: true
}, {
uid: 2,
from: [{
address: 'qwe'
}],
subject: 'this is the other subject!',
unread: true
}, {
uid: 3,
from: [{
address: 'qwe'
}],
subject: 'this is the other subject!',
unread: false
}];
sinon.stub(notification, 'create', function(opts) {
expect(opts.title).to.equal('2 new messages');
expect(opts.message).to.equal(mails[0].subject + '\n' + mails[1].subject);
opts.onClick();
expect(scope.state.mailList.selected).to.equal(mails[0]);
done();
});
scope.state.nav = {
currentFolder: {
type: 'asd',
messages: mails
}
};
emailDaoMock.onIncomingMessage(mails);
});
});
describe('getBody', function() {
it('should get the mail content', function() {
scope.state.nav = {
currentFolder: {
type: 'asd',
}
};
scope.getBody();
expect(emailDaoMock.getBody.calledOnce).to.be.true;
});
});
describe('select', function() {
it('should decrypt, focus mark an unread mail as read', function() {
scope.pendingNotifications = ['asd'];
sinon.stub(notification, 'close');
var mail = {
from: [{
address: 'asd'
}],
unread: true,
};
scope.state = {
nav: {
currentFolder: {
type: 'Inbox'
}
},
mailList: {},
read: {
toggle: function() {}
}
};
keychainMock.refreshKeyForUserId.withArgs(mail.from[0].address).yields();
scope.select(mail);
expect(emailDaoMock.decryptBody.calledOnce).to.be.true;
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
expect(scope.state.mailList.selected).to.equal(mail);
expect(notification.close.calledWith('asd')).to.be.true;
expect(notification.close.calledOnce).to.be.true;
notification.close.restore();
});
it('should decrypt and focus a read mail', function() {
var mail = {
from: [{
address: 'asd'
}],
unread: false
};
scope.state = {
mailList: {},
read: {
toggle: function() {}
},
nav: {
currentFolder: {
type: 'asd'
}
}
};
keychainMock.refreshKeyForUserId.withArgs(mail.from[0].address).yields();
scope.select(mail);
expect(emailDaoMock.decryptBody.calledOnce).to.be.true;
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
expect(scope.state.mailList.selected).to.equal(mail);
});
});
describe('remove', function() {
it('should not delete without a selected mail', function() {
scope.remove();
});
it('should delete the selected mail', function() {
var uid, mail, currentFolder;
scope._stopWatchTask();
scope.account = {};
uid = 123;
mail = {
uid: uid,
from: [{
address: 'asd'
}],
subject: '[whiteout] asdasd',
unread: true
};
currentFolder = {
type: 'Inbox',
path: 'INBOX',
messages: [mail]
};
scope.account.folders = [currentFolder];
scope.state.nav = {
currentFolder: currentFolder
};
emailDaoMock.deleteMessage.yields();
scope.remove(mail);
expect(emailDaoMock.deleteMessage.calledOnce).to.be.true;
expect(scope.state.mailList.selected).to.exist;
}); });
}); });
}); });
afterEach(function() {
if (!hasSocket) {
delete window.chrome.socket;
}
if (!hasRuntime) {
delete window.chrome.runtime;
}
if (!hasChrome) {
delete window.chrome;
}
if (!hasIdentity) {
delete window.chrome.identity;
}
// restore the module
appController._emailDao = origEmailDao;
});
describe('displayMore', function() {
beforeEach(function() {
scope.state.nav = {
currentFolder: {
messages: ['a', 'b']
}
};
});
it('should not do anything when display length equals messages length', function() {
scope.displayMessages = ['a', 'b'];
scope.displayMore();
expect(scope.displayMessages.length).to.equal(scope.state.nav.currentFolder.messages.length);
});
it('should append next message interval', function() {
scope.displayMessages = ['a'];
scope.displayMore();
expect(scope.displayMessages.length).to.equal(scope.state.nav.currentFolder.messages.length);
});
});
describe('displaySearchResults', function() {
var clock;
beforeEach(function() {
scope.state.nav = {
currentFolder: {
messages: ['a', 'b']
}
};
scope.watchMessages();
scope.watchOnline();
clock = sinon.useFakeTimers();
});
afterEach(function() {
clock.restore();
});
it('should show initial message on empty', function() {
scope.displaySearchResults();
expect(scope.searching).to.be.false;
expect(scope.lastUpdateLbl).to.equal('Online');
expect(scope.displayMessages.length).to.equal(2);
});
it('should show initial message on empty', function() {
var searchStub = sinon.stub(scope, 'search');
searchStub.returns(['a']);
scope.displaySearchResults('query');
expect(scope.searching).to.be.true;
expect(scope.lastUpdateLbl).to.equal('Searching ...');
clock.tick(500);
expect(scope.displayMessages).to.deep.equal(['a']);
expect(scope.searching).to.be.false;
expect(scope.lastUpdateLbl).to.equal('Matches in this folder');
});
});
describe('search', function() {
var message1 = {
to: [{
name: 'name1',
address: 'address1'
}],
subject: 'subject1',
body: 'body1',
html: 'html1'
},
message2 = {
to: [{
name: 'name2',
address: 'address2'
}],
subject: 'subject2',
body: 'body2',
html: 'html2'
},
message3 = {
to: [{
name: 'name3',
address: 'address3'
}],
subject: 'subject3',
body: 'body1',
html: 'html1',
encrypted: true
},
message4 = {
to: [{
name: 'name4',
address: 'address4'
}],
subject: 'subject4',
body: 'body1',
html: 'html1',
encrypted: true,
decrypted: true
},
testMessages = [message1, message2, message3, message4];
it('return same messages array on empty query string', function() {
var result = scope.search(testMessages, '');
expect(result).to.equal(testMessages);
});
it('return message1 on matching subject', function() {
var result = scope.search(testMessages, 'subject1');
expect(result.length).to.equal(1);
expect(result[0]).to.equal(message1);
});
it('return message1 on matching name', function() {
var result = scope.search(testMessages, 'name1');
expect(result.length).to.equal(1);
expect(result[0]).to.equal(message1);
});
it('return message1 on matching address', function() {
var result = scope.search(testMessages, 'address1');
expect(result.length).to.equal(1);
expect(result[0]).to.equal(message1);
});
it('return plaintext and decrypted messages on matching body', function() {
var result = scope.search(testMessages, 'body1');
expect(result.length).to.equal(2);
expect(result[0]).to.equal(message1);
expect(result[1]).to.equal(message4);
});
it('return plaintext and decrypted messages on matching html', function() {
var result = scope.search(testMessages, 'html1');
expect(result.length).to.equal(2);
expect(result[0]).to.equal(message1);
expect(result[1]).to.equal(message4);
});
});
describe('scope variables', function() {
it('should be set correctly', function() {
expect(scope.select).to.exist;
expect(scope.remove).to.exist;
expect(scope.state.mailList).to.exist;
});
});
describe('push notification', function() {
beforeEach(function() {
scope._stopWatchTask();
});
afterEach(function() {
notification.create.restore();
});
it('should succeed for single mail', function(done) {
var mail = {
uid: 123,
from: [{
address: 'asd'
}],
subject: 'this is the subject!',
unread: true
};
sinon.stub(notification, 'create', function(opts) {
expect(opts.title).to.equal(mail.from[0].address);
expect(opts.message).to.equal(mail.subject);
opts.onClick();
expect(scope.state.mailList.selected).to.equal(mail);
done();
});
scope.state.nav = {
currentFolder: {
type: 'asd',
messages: [mail]
}
};
emailDaoMock.onIncomingMessage([mail]);
});
it('should succeed for multiple mails', function(done) {
var mails = [{
uid: 1,
from: [{
address: 'asd'
}],
subject: 'this is the subject!',
unread: true
}, {
uid: 2,
from: [{
address: 'qwe'
}],
subject: 'this is the other subject!',
unread: true
}, {
uid: 3,
from: [{
address: 'qwe'
}],
subject: 'this is the other subject!',
unread: false
}];
sinon.stub(notification, 'create', function(opts) {
expect(opts.title).to.equal('2 new messages');
expect(opts.message).to.equal(mails[0].subject + '\n' + mails[1].subject);
opts.onClick();
expect(scope.state.mailList.selected).to.equal(mails[0]);
done();
});
scope.state.nav = {
currentFolder: {
type: 'asd',
messages: mails
}
};
emailDaoMock.onIncomingMessage(mails);
});
});
describe('getBody', function() {
it('should get the mail content', function() {
scope.state.nav = {
currentFolder: {
type: 'asd',
}
};
scope.getBody();
expect(emailDaoMock.getBody.calledOnce).to.be.true;
});
});
describe('select', function() {
it('should decrypt, focus mark an unread mail as read', function() {
scope.pendingNotifications = ['asd'];
sinon.stub(notification, 'close');
var mail = {
from: [{
address: 'asd'
}],
unread: true,
};
scope.state = {
nav: {
currentFolder: {
type: 'Inbox'
}
},
mailList: {},
read: {
toggle: function() {}
}
};
keychainMock.refreshKeyForUserId.withArgs(mail.from[0].address).yields();
scope.select(mail);
expect(emailDaoMock.decryptBody.calledOnce).to.be.true;
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
expect(scope.state.mailList.selected).to.equal(mail);
expect(notification.close.calledWith('asd')).to.be.true;
expect(notification.close.calledOnce).to.be.true;
notification.close.restore();
});
it('should decrypt and focus a read mail', function() {
var mail = {
from: [{
address: 'asd'
}],
unread: false
};
scope.state = {
mailList: {},
read: {
toggle: function() {}
},
nav: {
currentFolder: {
type: 'asd'
}
}
};
keychainMock.refreshKeyForUserId.withArgs(mail.from[0].address).yields();
scope.select(mail);
expect(emailDaoMock.decryptBody.calledOnce).to.be.true;
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
expect(scope.state.mailList.selected).to.equal(mail);
});
});
describe('remove', function() {
it('should not delete without a selected mail', function() {
scope.remove();
});
it('should delete the selected mail', function() {
var uid, mail, currentFolder;
scope._stopWatchTask();
scope.account = {};
uid = 123;
mail = {
uid: uid,
from: [{
address: 'asd'
}],
subject: '[whiteout] asdasd',
unread: true
};
currentFolder = {
type: 'Inbox',
path: 'INBOX',
messages: [mail]
};
scope.account.folders = [currentFolder];
scope.state.nav = {
currentFolder: currentFolder
};
emailDaoMock.deleteMessage.yields();
scope.remove(mail);
expect(emailDaoMock.deleteMessage.calledOnce).to.be.true;
expect(scope.state.mailList.selected).to.exist;
});
});
}); });

View File

@ -1,113 +0,0 @@
'use strict';
// Mozilla bind polyfill because phantomjs is stupid
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
FNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof FNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
};
FNOP.prototype = this.prototype;
fBound.prototype = new FNOP();
return fBound;
};
}
// a warm round of applause for phantomjs for missing events
(function() {
function CustomEvent(event, params) {
params = params || {
bubbles: false,
cancelable: false,
detail: undefined
};
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return evt;
}
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent;
})();
require(['../../src/require-config'], function() {
require.config({
baseUrl: '../../src/lib',
paths: {
angularMocks: '../../test/lib/angular-mocks'
},
shim: {
angularMocks: {
exports: 'angular.mock',
deps: ['angular']
}
}
});
// Start the main app logic.
require(['js/app-config', 'axe'], function(app, axe) {
app.config.workerPath = '../../src/js';
// turn off logging in the test
axe.removeAppender(axe.defaultAppender);
startTests();
});
});
function startTests() {
mocha.setup('bdd');
require(
[
'test/unit/oauth-test',
'test/unit/auth-test',
'test/unit/email-dao-test',
'test/unit/app-controller-test',
'test/unit/pgp-test',
'test/unit/crypto-test',
'test/unit/backbutton-handler-test',
'test/unit/rest-dao-test',
'test/unit/admin-dao-test',
'test/unit/publickey-dao-test',
'test/unit/privatekey-dao-test',
'test/unit/lawnchair-dao-test',
'test/unit/keychain-dao-test',
'test/unit/devicestorage-dao-test',
'test/unit/dialog-ctrl-test',
'test/unit/add-account-ctrl-test',
'test/unit/account-ctrl-test',
'test/unit/set-passphrase-ctrl-test',
'test/unit/contacts-ctrl-test',
'test/unit/login-existing-ctrl-test',
'test/unit/login-initial-ctrl-test',
'test/unit/login-new-device-ctrl-test',
'test/unit/login-privatekey-download-ctrl-test',
'test/unit/login-set-credentials-ctrl-test',
'test/unit/privatekey-upload-ctrl-test',
'test/unit/login-ctrl-test',
'test/unit/read-ctrl-test',
'test/unit/navigation-ctrl-test',
'test/unit/mail-list-ctrl-test',
'test/unit/write-ctrl-test',
'test/unit/outbox-bo-test',
'test/unit/invitation-dao-test',
'test/unit/update-handler-test',
'test/unit/connection-doctor-test'
], function() {
//Tests loaded, run tests
mocha.run();
}
);
}

View File

@ -1,101 +1,97 @@
define(function(require) { 'use strict';
'use strict';
var expect = chai.expect, var mocks = angular.mocks,
angular = require('angular'), NavigationCtrl = require('../../src/js/controller/navigation'),
mocks = require('angularMocks'), EmailDAO = require('../../src/js/dao/email-dao'),
NavigationCtrl = require('js/controller/navigation'), OutboxBO = require('../../src/js/bo/outbox'),
EmailDAO = require('js/dao/email-dao'), appController = require('../../src/js/app-controller');
OutboxBO = require('js/bo/outbox'),
appController = require('js/app-controller');
describe('Navigation Controller unit test', function() { describe('Navigation Controller unit test', function() {
var scope, ctrl, origEmailDao, emailDaoMock, outboxBoMock, outboxFolder, onConnectStub; var scope, ctrl, origEmailDao, emailDaoMock, outboxBoMock, outboxFolder, onConnectStub;
beforeEach(function(done) { beforeEach(function(done) {
// remember original module to restore later // remember original module to restore later
origEmailDao = appController._emailDao; origEmailDao = appController._emailDao;
emailDaoMock = sinon.createStubInstance(EmailDAO); emailDaoMock = sinon.createStubInstance(EmailDAO);
emailDaoMock._account = { emailDaoMock._account = {
folders: [{ folders: [{
type: 'Inbox', type: 'Inbox',
count: 2, count: 2,
path: 'INBOX' path: 'INBOX'
}, { }, {
type: 'Outbox', type: 'Outbox',
count: 0, count: 0,
path: 'OUTBOX' path: 'OUTBOX'
}] }]
}; };
outboxFolder = emailDaoMock._account.folders[1]; outboxFolder = emailDaoMock._account.folders[1];
appController._emailDao = emailDaoMock; appController._emailDao = emailDaoMock;
outboxBoMock = sinon.createStubInstance(OutboxBO); outboxBoMock = sinon.createStubInstance(OutboxBO);
appController._outboxBo = outboxBoMock; appController._outboxBo = outboxBoMock;
outboxBoMock.startChecking.returns(); outboxBoMock.startChecking.returns();
onConnectStub = sinon.stub(appController, 'onConnect'); onConnectStub = sinon.stub(appController, 'onConnect');
onConnectStub.yields(); onConnectStub.yields();
angular.module('navigationtest', []); angular.module('navigationtest', []);
mocks.module('navigationtest'); mocks.module('navigationtest');
mocks.inject(function($rootScope, $controller) { mocks.inject(function($rootScope, $controller) {
scope = $rootScope.$new(); scope = $rootScope.$new();
scope.state = {}; scope.state = {};
ctrl = $controller(NavigationCtrl, { ctrl = $controller(NavigationCtrl, {
$scope: scope, $scope: scope,
$routeParams: {} $routeParams: {}
});
done();
}); });
done();
}); });
});
afterEach(function() { afterEach(function() {
// restore the module // restore the module
appController._emailDao = origEmailDao; appController._emailDao = origEmailDao;
onConnectStub.restore(); onConnectStub.restore();
});
describe('initial state', function() {
it('should be well defined', function() {
expect(scope.state).to.exist;
expect(scope.state.lightbox).to.be.undefined;
expect(scope.account.folders).to.not.be.empty;
expect(scope.openFolder).to.exist;
}); });
});
describe('initial state', function() { describe('open/close nav view', function() {
it('should be well defined', function() { it('should open/close', function() {
expect(scope.state).to.exist; expect(scope.state.nav.open).to.be.false;
expect(scope.state.lightbox).to.be.undefined; scope.state.nav.toggle(true);
expect(scope.account.folders).to.not.be.empty; expect(scope.state.nav.open).to.be.true;
expect(scope.openFolder).to.exist; scope.state.nav.toggle(false);
}); expect(scope.state.nav.open).to.be.false;
}); });
});
describe('open/close nav view', function() { describe('open folder', function() {
it('should open/close', function() { it('should work', function() {
expect(scope.state.nav.open).to.be.false; scope.state.nav.open = true;
scope.state.nav.toggle(true);
expect(scope.state.nav.open).to.be.true; scope.openFolder('asd');
scope.state.nav.toggle(false); expect(scope.state.nav.currentFolder).to.equal('asd');
expect(scope.state.nav.open).to.be.false; expect(scope.state.nav.open).to.be.false;
});
}); });
});
describe('open folder', function() { describe('empty outbox', function() {
it('should work', function() { it('should work', function() {
scope.state.nav.open = true; var callback;
scope.openFolder('asd'); expect(outboxBoMock.startChecking.callCount).to.equal(1);
expect(scope.state.nav.currentFolder).to.equal('asd');
expect(scope.state.nav.open).to.be.false;
});
});
describe('empty outbox', function() { outboxBoMock.startChecking.calledWith(sinon.match(function(cb) {
it('should work', function() { callback = cb;
var callback; }));
expect(outboxBoMock.startChecking.callCount).to.equal(1); callback(null, 5);
expect(outboxFolder.count).to.equal(5);
outboxBoMock.startChecking.calledWith(sinon.match(function(cb) {
callback = cb;
}));
callback(null, 5);
expect(outboxFolder.count).to.equal(5);
});
}); });
}); });
}); });

View File

@ -1,201 +1,198 @@
define(function(require) { 'use strict';
'use strict';
var OAuth = require('js/util/oauth'), var OAuth = require('../../src/js/util/oauth'),
RestDAO = require('js/dao/rest-dao'), RestDAO = require('../../src/js/dao/rest-dao');
expect = chai.expect;
describe('OAuth unit tests', function() { describe('OAuth unit tests', function() {
var oauth, googleApiStub, identityStub, getPlatformInfoStub, removeCachedStub, var oauth, googleApiStub, identityStub, getPlatformInfoStub, removeCachedStub,
testEmail = 'test@example.com'; testEmail = 'test@example.com';
beforeEach(function() {
googleApiStub = sinon.createStubInstance(RestDAO);
oauth = new OAuth(googleApiStub);
window.chrome = window.chrome || {};
window.chrome.identity = window.chrome.identity || {};
if (typeof window.chrome.identity.getAuthToken !== 'function') {
window.chrome.identity.getAuthToken = function() {};
}
identityStub = sinon.stub(window.chrome.identity, 'getAuthToken');
if (typeof window.chrome.identity.removeCachedAuthToken !== 'function') {
window.chrome.identity.removeCachedAuthToken = function() {};
}
removeCachedStub = sinon.stub(window.chrome.identity, 'removeCachedAuthToken');
window.chrome.runtime = window.chrome.runtime || {};
if (typeof window.chrome.runtime.getPlatformInfo !== 'function') {
window.chrome.runtime.getPlatformInfo = function() {};
}
getPlatformInfoStub = sinon.stub(window.chrome.runtime, 'getPlatformInfo');
});
afterEach(function() {
identityStub.restore();
getPlatformInfoStub.restore();
removeCachedStub.restore();
});
describe('isSupported', function() {
it('should work', function() {
expect(oauth.isSupported()).to.be.true;
});
});
describe('refreshToken', function() {
var getOAuthTokenStub;
beforeEach(function() { beforeEach(function() {
googleApiStub = sinon.createStubInstance(RestDAO); getOAuthTokenStub = sinon.stub(oauth, 'getOAuthToken');
oauth = new OAuth(googleApiStub);
window.chrome = window.chrome || {};
window.chrome.identity = window.chrome.identity || {};
if (typeof window.chrome.identity.getAuthToken !== 'function') {
window.chrome.identity.getAuthToken = function() {};
}
identityStub = sinon.stub(window.chrome.identity, 'getAuthToken');
if (typeof window.chrome.identity.removeCachedAuthToken !== 'function') {
window.chrome.identity.removeCachedAuthToken = function() {};
}
removeCachedStub = sinon.stub(window.chrome.identity, 'removeCachedAuthToken');
window.chrome.runtime = window.chrome.runtime || {};
if (typeof window.chrome.runtime.getPlatformInfo !== 'function') {
window.chrome.runtime.getPlatformInfo = function() {};
}
getPlatformInfoStub = sinon.stub(window.chrome.runtime, 'getPlatformInfo');
}); });
afterEach(function() { afterEach(function() {
identityStub.restore(); getOAuthTokenStub.restore();
getPlatformInfoStub.restore();
removeCachedStub.restore();
}); });
describe('isSupported', function() { it('should work', function() {
it('should work', function() { removeCachedStub.withArgs({
expect(oauth.isSupported()).to.be.true; token: 'oldToken'
}).yields();
getOAuthTokenStub.withArgs(testEmail).yields();
oauth.refreshToken({
oldToken: 'oldToken',
emailAddress: testEmail
}, function(err) {
expect(err).to.not.exist;
expect(removeCachedStub.calledOnce).to.be.true;
expect(getOAuthTokenStub.calledOnce).to.be.true;
}); });
}); });
describe('refreshToken', function() { it('should work without email', function() {
var getOAuthTokenStub; removeCachedStub.withArgs({
token: 'oldToken'
}).yields();
beforeEach(function() { getOAuthTokenStub.withArgs(undefined).yields();
getOAuthTokenStub = sinon.stub(oauth, 'getOAuthToken');
});
afterEach(function() {
getOAuthTokenStub.restore();
});
it('should work', function() { oauth.refreshToken({
removeCachedStub.withArgs({ oldToken: 'oldToken',
token: 'oldToken' }, function(err) {
}).yields(); expect(err).to.not.exist;
expect(removeCachedStub.calledOnce).to.be.true;
getOAuthTokenStub.withArgs(testEmail).yields(); expect(getOAuthTokenStub.calledOnce).to.be.true;
expect(getOAuthTokenStub.calledWith(undefined)).to.be.true;
oauth.refreshToken({
oldToken: 'oldToken',
emailAddress: testEmail
}, function(err) {
expect(err).to.not.exist;
expect(removeCachedStub.calledOnce).to.be.true;
expect(getOAuthTokenStub.calledOnce).to.be.true;
});
});
it('should work without email', function() {
removeCachedStub.withArgs({
token: 'oldToken'
}).yields();
getOAuthTokenStub.withArgs(undefined).yields();
oauth.refreshToken({
oldToken: 'oldToken',
}, function(err) {
expect(err).to.not.exist;
expect(removeCachedStub.calledOnce).to.be.true;
expect(getOAuthTokenStub.calledOnce).to.be.true;
expect(getOAuthTokenStub.calledWith(undefined)).to.be.true;
});
});
it('should fail without all options', function() {
oauth.refreshToken({
emailAddress: testEmail
}, function(err) {
expect(err).to.exist;
expect(removeCachedStub.called).to.be.false;
expect(getOAuthTokenStub.called).to.be.false;
});
}); });
}); });
describe('getOAuthToken', function() { it('should fail without all options', function() {
it('should work for empty emailAddress', function(done) { oauth.refreshToken({
getPlatformInfoStub.yields({ emailAddress: testEmail
os: 'android' }, function(err) {
}); expect(err).to.exist;
identityStub.withArgs({ expect(removeCachedStub.called).to.be.false;
interactive: true expect(getOAuthTokenStub.called).to.be.false;
}).yields('token');
oauth.getOAuthToken(undefined, function(err, token) {
expect(err).to.not.exist;
expect(token).to.equal('token');
done();
});
});
it('should work on android app', function(done) {
getPlatformInfoStub.yields({
os: 'android'
});
identityStub.withArgs({
interactive: true,
accountHint: testEmail
}).yields('token');
oauth.getOAuthToken(testEmail, function(err, token) {
expect(err).to.not.exist;
expect(token).to.equal('token');
done();
});
});
it('should work on desktop chrome', function(done) {
getPlatformInfoStub.yields({
os: 'mac'
});
identityStub.withArgs({
interactive: true
}).yields('token');
oauth.getOAuthToken(testEmail, function(err, token) {
expect(err).to.not.exist;
expect(token).to.equal('token');
done();
});
});
it('should fail', function(done) {
getPlatformInfoStub.yields({
os: 'android'
});
identityStub.yields();
oauth.getOAuthToken(testEmail, function(err, token) {
expect(err).to.exist;
expect(token).to.not.exist;
done();
});
}); });
}); });
describe('queryEmailAddress', function() {
it('should work', function(done) {
googleApiStub.get.withArgs({
uri: '/oauth2/v3/userinfo?access_token=token'
}).yields(null, {
email: 'asdf@example.com'
});
oauth.queryEmailAddress('token', function(err, emailAddress) {
expect(err).to.not.exist;
expect(emailAddress).to.equal('asdf@example.com');
done();
});
});
it('should fail due to invalid token', function(done) {
oauth.queryEmailAddress('', function(err, emailAddress) {
expect(err).to.exist;
expect(emailAddress).to.not.exist;
done();
});
});
it('should fail due to error in rest api', function(done) {
googleApiStub.get.withArgs({
uri: '/oauth2/v3/userinfo?access_token=token'
}).yields(new Error());
oauth.queryEmailAddress('token', function(err, emailAddress) {
expect(err).to.exist;
expect(emailAddress).to.not.exist;
done();
});
});
});
}); });
describe('getOAuthToken', function() {
it('should work for empty emailAddress', function(done) {
getPlatformInfoStub.yields({
os: 'android'
});
identityStub.withArgs({
interactive: true
}).yields('token');
oauth.getOAuthToken(undefined, function(err, token) {
expect(err).to.not.exist;
expect(token).to.equal('token');
done();
});
});
it('should work on android app', function(done) {
getPlatformInfoStub.yields({
os: 'android'
});
identityStub.withArgs({
interactive: true,
accountHint: testEmail
}).yields('token');
oauth.getOAuthToken(testEmail, function(err, token) {
expect(err).to.not.exist;
expect(token).to.equal('token');
done();
});
});
it('should work on desktop chrome', function(done) {
getPlatformInfoStub.yields({
os: 'mac'
});
identityStub.withArgs({
interactive: true
}).yields('token');
oauth.getOAuthToken(testEmail, function(err, token) {
expect(err).to.not.exist;
expect(token).to.equal('token');
done();
});
});
it('should fail', function(done) {
getPlatformInfoStub.yields({
os: 'android'
});
identityStub.yields();
oauth.getOAuthToken(testEmail, function(err, token) {
expect(err).to.exist;
expect(token).to.not.exist;
done();
});
});
});
describe('queryEmailAddress', function() {
it('should work', function(done) {
googleApiStub.get.withArgs({
uri: '/oauth2/v3/userinfo?access_token=token'
}).yields(null, {
email: 'asdf@example.com'
});
oauth.queryEmailAddress('token', function(err, emailAddress) {
expect(err).to.not.exist;
expect(emailAddress).to.equal('asdf@example.com');
done();
});
});
it('should fail due to invalid token', function(done) {
oauth.queryEmailAddress('', function(err, emailAddress) {
expect(err).to.exist;
expect(emailAddress).to.not.exist;
done();
});
});
it('should fail due to error in rest api', function(done) {
googleApiStub.get.withArgs({
uri: '/oauth2/v3/userinfo?access_token=token'
}).yields(new Error());
oauth.queryEmailAddress('token', function(err, emailAddress) {
expect(err).to.exist;
expect(emailAddress).to.not.exist;
done();
});
});
});
}); });

View File

@ -1,279 +1,276 @@
define(function(require) { 'use strict';
'use strict';
var expect = chai.expect, var OutboxBO = require('../../src/js/bo/outbox'),
OutboxBO = require('js/bo/outbox'), KeychainDAO = require('../../src/js/dao/keychain-dao'),
KeychainDAO = require('js/dao/keychain-dao'), EmailDAO = require('../../src/js/dao/email-dao'),
EmailDAO = require('js/dao/email-dao'), DeviceStorageDAO = require('../../src/js/dao/devicestorage-dao');
DeviceStorageDAO = require('js/dao/devicestorage-dao');
chai.Assertion.includeStack = true; chai.Assertion.includeStack = true;
describe('Outbox Business Object unit test', function() { describe('Outbox Business Object unit test', function() {
var outbox, emailDaoStub, devicestorageStub, keychainStub, var outbox, emailDaoStub, devicestorageStub, keychainStub,
dummyUser = 'spiderpig@springfield.com'; dummyUser = 'spiderpig@springfield.com';
beforeEach(function() { beforeEach(function() {
emailDaoStub = sinon.createStubInstance(EmailDAO); emailDaoStub = sinon.createStubInstance(EmailDAO);
emailDaoStub._account = { emailDaoStub._account = {
emailAddress: dummyUser, emailAddress: dummyUser,
folders: [{ folders: [{
type: 'Outbox' type: 'Outbox'
}], }],
online: true online: true
};
devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
keychainStub = sinon.createStubInstance(KeychainDAO);
outbox = new OutboxBO(emailDaoStub, keychainStub, devicestorageStub);
});
afterEach(function() {});
describe('start/stop checking', function() {
it('should work', function() {
function onOutboxUpdate(err) {
expect(err).to.not.exist;
}
outbox.startChecking(onOutboxUpdate);
expect(outbox._intervalId).to.exist;
outbox.stopChecking();
expect(outbox._intervalId).to.not.exist;
});
});
describe('put', function() {
it('should not encrypt and store a mail', function(done) {
var mail, senderKey, receiverKey;
senderKey = {
publicKey: 'SENDER PUBLIC KEY'
}; };
devicestorageStub = sinon.createStubInstance(DeviceStorageDAO); receiverKey = {
keychainStub = sinon.createStubInstance(KeychainDAO); publicKey: 'RECEIVER PUBLIC KEY'
outbox = new OutboxBO(emailDaoStub, keychainStub, devicestorageStub); };
}); mail = {
from: [{
afterEach(function() {});
describe('start/stop checking', function() {
it('should work', function() {
function onOutboxUpdate(err) {
expect(err).to.not.exist;
}
outbox.startChecking(onOutboxUpdate);
expect(outbox._intervalId).to.exist;
outbox.stopChecking();
expect(outbox._intervalId).to.not.exist;
});
});
describe('put', function() {
it('should not encrypt and store a mail', function(done) {
var mail, senderKey, receiverKey;
senderKey = {
publicKey: 'SENDER PUBLIC KEY'
};
receiverKey = {
publicKey: 'RECEIVER PUBLIC KEY'
};
mail = {
from: [{
name: 'member',
address: 'member@whiteout.io'
}],
to: [{
name: 'member',
address: 'member'
}, {
name: 'notamember',
address: 'notamember'
}],
cc: [],
bcc: []
};
keychainStub.getReceiverPublicKey.withArgs(mail.from[0].address).yieldsAsync(null, senderKey);
keychainStub.getReceiverPublicKey.withArgs(mail.to[0].address).yieldsAsync(null, receiverKey);
keychainStub.getReceiverPublicKey.withArgs(mail.to[1].address).yieldsAsync();
devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
outbox.put(mail, function(error) {
expect(error).to.not.exist;
expect(mail.publicKeysArmored.length).to.equal(2);
expect(emailDaoStub.encrypt.called).to.be.false;
expect(devicestorageStub.storeList.calledOnce).to.be.true;
done();
});
});
it('should not encrypt a mail with bcc and store a mail', function(done) {
var mail;
mail = {
from: [{
name: 'member',
address: 'member@whiteout.io'
}],
to: [{
name: 'member',
address: 'member@whiteout.io'
}],
cc: [],
bcc: [{
name: 'member',
address: 'member@whiteout.io'
}]
};
devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
outbox.put(mail, function(error) {
expect(error).to.not.exist;
expect(mail.publicKeysArmored.length).to.equal(0);
expect(keychainStub.getReceiverPublicKey.called).to.be.false;
expect(emailDaoStub.encrypt.called).to.be.false;
expect(devicestorageStub.storeList.calledOnce).to.be.true;
done();
});
});
it('should encrypt and store a mail', function(done) {
var mail, senderKey, receiverKey;
senderKey = {
publicKey: 'SENDER PUBLIC KEY'
};
receiverKey = {
publicKey: 'RECEIVER PUBLIC KEY'
};
mail = {
from: [{
name: 'member',
address: 'member@whiteout.io'
}],
to: [{
name: 'member',
address: 'member'
}, {
name: 'notamember',
address: 'notamember'
}],
cc: [],
bcc: []
};
keychainStub.getReceiverPublicKey.withArgs(mail.from[0].address).yieldsAsync(null, senderKey);
keychainStub.getReceiverPublicKey.withArgs(mail.to[0].address).yieldsAsync(null, receiverKey);
keychainStub.getReceiverPublicKey.withArgs(mail.to[1].address).yieldsAsync(null, receiverKey);
emailDaoStub.encrypt.withArgs({
mail: mail,
publicKeysArmored: [senderKey.publicKey, receiverKey.publicKey, receiverKey.publicKey]
}).yieldsAsync();
devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
outbox.put(mail, function(error) {
expect(error).to.not.exist;
expect(mail.publicKeysArmored.length).to.equal(3);
expect(emailDaoStub.encrypt.calledOnce).to.be.true;
expect(devicestorageStub.storeList.calledOnce).to.be.true;
done();
});
});
});
describe('process outbox', function() {
it('should send to registered users and update pending mails', function(done) {
var from, member, invited, notinvited, newlyjoined, dummyMails, newlyjoinedKey;
from = [{
name: 'member', name: 'member',
address: 'member@whiteout.io' address: 'member@whiteout.io'
}]; }],
member = { to: [{
id: '12', name: 'member',
from: from, address: 'member'
to: [{ }, {
name: 'member', name: 'notamember',
address: 'member' address: 'notamember'
}], }],
encrypted: true, cc: [],
publicKeysArmored: ['ARMORED KEY OF MEMBER'], bcc: []
unregisteredUsers: [] };
};
invited = {
id: '34',
from: from,
to: [{
name: 'invited',
address: 'invited'
}],
publicKeysArmored: [],
unregisteredUsers: [{
name: 'invited',
address: 'invited'
}]
};
notinvited = {
id: '56',
from: from,
to: [{
name: 'notinvited',
address: 'notinvited'
}],
publicKeysArmored: [],
unregisteredUsers: [{
name: 'notinvited',
address: 'notinvited'
}]
};
newlyjoined = {
id: '78',
from: from,
to: [{
name: 'newlyjoined',
address: 'newlyjoined'
}],
encrypted: true,
publicKeysArmored: [],
unregisteredUsers: [{
name: 'newlyjoined',
address: 'newlyjoined'
}]
};
newlyjoinedKey = {
publicKey: 'THIS IS THE NEWLY JOINED PUBLIC KEY!'
};
dummyMails = [member, invited, notinvited, newlyjoined]; keychainStub.getReceiverPublicKey.withArgs(mail.from[0].address).yieldsAsync(null, senderKey);
keychainStub.getReceiverPublicKey.withArgs(mail.to[0].address).yieldsAsync(null, receiverKey);
keychainStub.getReceiverPublicKey.withArgs(mail.to[1].address).yieldsAsync();
devicestorageStub.listItems.yieldsAsync(null, dummyMails); devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
emailDaoStub.sendPlaintext.yieldsAsync(); outbox.put(mail, function(error) {
expect(error).to.not.exist;
emailDaoStub.sendEncrypted.withArgs({ expect(mail.publicKeysArmored.length).to.equal(2);
email: newlyjoined expect(emailDaoStub.encrypt.called).to.be.false;
}).yieldsAsync(); expect(devicestorageStub.storeList.calledOnce).to.be.true;
emailDaoStub.sendEncrypted.withArgs({ done();
email: member
}).yieldsAsync();
devicestorageStub.removeList.yieldsAsync();
function onOutboxUpdate(err, count) {
expect(err).to.not.exist;
expect(count).to.equal(0);
expect(outbox._outboxBusy).to.be.false;
expect(emailDaoStub.sendEncrypted.callCount).to.equal(2);
expect(emailDaoStub.sendPlaintext.callCount).to.equal(2);
expect(devicestorageStub.listItems.callCount).to.equal(1);
expect(devicestorageStub.removeList.callCount).to.equal(4);
expect(keychainStub.getReceiverPublicKey.callCount).to.equal(0);
done();
}
outbox._processOutbox(onOutboxUpdate);
}); });
});
it('should not process outbox in offline mode', function(done) { it('should not encrypt a mail with bcc and store a mail', function(done) {
emailDaoStub._account.online = false; var mail;
devicestorageStub.listItems.yieldsAsync(null, [{}]);
outbox._processOutbox(function(err, count) { mail = {
expect(err).to.not.exist; from: [{
expect(count).to.equal(1); name: 'member',
expect(devicestorageStub.listItems.callCount).to.equal(1); address: 'member@whiteout.io'
expect(outbox._outboxBusy).to.be.false; }],
done(); to: [{
}); name: 'member',
address: 'member@whiteout.io'
}],
cc: [],
bcc: [{
name: 'member',
address: 'member@whiteout.io'
}]
};
devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
outbox.put(mail, function(error) {
expect(error).to.not.exist;
expect(mail.publicKeysArmored.length).to.equal(0);
expect(keychainStub.getReceiverPublicKey.called).to.be.false;
expect(emailDaoStub.encrypt.called).to.be.false;
expect(devicestorageStub.storeList.calledOnce).to.be.true;
done();
});
});
it('should encrypt and store a mail', function(done) {
var mail, senderKey, receiverKey;
senderKey = {
publicKey: 'SENDER PUBLIC KEY'
};
receiverKey = {
publicKey: 'RECEIVER PUBLIC KEY'
};
mail = {
from: [{
name: 'member',
address: 'member@whiteout.io'
}],
to: [{
name: 'member',
address: 'member'
}, {
name: 'notamember',
address: 'notamember'
}],
cc: [],
bcc: []
};
keychainStub.getReceiverPublicKey.withArgs(mail.from[0].address).yieldsAsync(null, senderKey);
keychainStub.getReceiverPublicKey.withArgs(mail.to[0].address).yieldsAsync(null, receiverKey);
keychainStub.getReceiverPublicKey.withArgs(mail.to[1].address).yieldsAsync(null, receiverKey);
emailDaoStub.encrypt.withArgs({
mail: mail,
publicKeysArmored: [senderKey.publicKey, receiverKey.publicKey, receiverKey.publicKey]
}).yieldsAsync();
devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
outbox.put(mail, function(error) {
expect(error).to.not.exist;
expect(mail.publicKeysArmored.length).to.equal(3);
expect(emailDaoStub.encrypt.calledOnce).to.be.true;
expect(devicestorageStub.storeList.calledOnce).to.be.true;
done();
});
});
});
describe('process outbox', function() {
it('should send to registered users and update pending mails', function(done) {
var from, member, invited, notinvited, newlyjoined, dummyMails, newlyjoinedKey;
from = [{
name: 'member',
address: 'member@whiteout.io'
}];
member = {
id: '12',
from: from,
to: [{
name: 'member',
address: 'member'
}],
encrypted: true,
publicKeysArmored: ['ARMORED KEY OF MEMBER'],
unregisteredUsers: []
};
invited = {
id: '34',
from: from,
to: [{
name: 'invited',
address: 'invited'
}],
publicKeysArmored: [],
unregisteredUsers: [{
name: 'invited',
address: 'invited'
}]
};
notinvited = {
id: '56',
from: from,
to: [{
name: 'notinvited',
address: 'notinvited'
}],
publicKeysArmored: [],
unregisteredUsers: [{
name: 'notinvited',
address: 'notinvited'
}]
};
newlyjoined = {
id: '78',
from: from,
to: [{
name: 'newlyjoined',
address: 'newlyjoined'
}],
encrypted: true,
publicKeysArmored: [],
unregisteredUsers: [{
name: 'newlyjoined',
address: 'newlyjoined'
}]
};
newlyjoinedKey = {
publicKey: 'THIS IS THE NEWLY JOINED PUBLIC KEY!'
};
dummyMails = [member, invited, notinvited, newlyjoined];
devicestorageStub.listItems.yieldsAsync(null, dummyMails);
emailDaoStub.sendPlaintext.yieldsAsync();
emailDaoStub.sendEncrypted.withArgs({
email: newlyjoined
}).yieldsAsync();
emailDaoStub.sendEncrypted.withArgs({
email: member
}).yieldsAsync();
devicestorageStub.removeList.yieldsAsync();
function onOutboxUpdate(err, count) {
expect(err).to.not.exist;
expect(count).to.equal(0);
expect(outbox._outboxBusy).to.be.false;
expect(emailDaoStub.sendEncrypted.callCount).to.equal(2);
expect(emailDaoStub.sendPlaintext.callCount).to.equal(2);
expect(devicestorageStub.listItems.callCount).to.equal(1);
expect(devicestorageStub.removeList.callCount).to.equal(4);
expect(keychainStub.getReceiverPublicKey.callCount).to.equal(0);
done();
}
outbox._processOutbox(onOutboxUpdate);
});
it('should not process outbox in offline mode', function(done) {
emailDaoStub._account.online = false;
devicestorageStub.listItems.yieldsAsync(null, [{}]);
outbox._processOutbox(function(err, count) {
expect(err).to.not.exist;
expect(count).to.equal(1);
expect(devicestorageStub.listItems.callCount).to.equal(1);
expect(outbox._outboxBusy).to.be.false;
done();
}); });
}); });
}); });

View File

@ -1,464 +1,460 @@
define(function(require) { 'use strict';
'use strict';
var PGP = require('js/crypto/pgp'), var PGP = require('../../src/js/crypto/pgp');
openpgp = require('openpgp'),
expect = chai.expect;
describe('PGP Crypto Api unit tests', function() { describe('PGP Crypto Api unit tests', function() {
this.timeout(20000); this.timeout(20000);
var pgp, var pgp,
user = 'whiteout.test@t-online.de', user = 'whiteout.test@t-online.de',
passphrase = 'asdf', passphrase = 'asdf',
keySize = 512, keySize = 512,
keyId = 'F6F60E9B42CDFF4C', keyId = 'F6F60E9B42CDFF4C',
pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n' + pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n' +
'Version: OpenPGP.js v0.7.2\r\n' + 'Version: OpenPGP.js v0.7.2\r\n' +
'Comment: Whiteout Mail - https://whiteout.io\r\n' + 'Comment: Whiteout Mail - https://whiteout.io\r\n' +
'\r\n' + '\r\n' +
'xk0EUlhMvAEB/2MZtCUOAYvyLFjDp3OBMGn3Ev8FwjzyPbIF0JUw+L7y2XR5\r\n' + 'xk0EUlhMvAEB/2MZtCUOAYvyLFjDp3OBMGn3Ev8FwjzyPbIF0JUw+L7y2XR5\r\n' +
'RVGvbK88unV3cU/1tOYdNsXI6pSp/Ztjyv7vbBUAEQEAAc0pV2hpdGVvdXQg\r\n' + 'RVGvbK88unV3cU/1tOYdNsXI6pSp/Ztjyv7vbBUAEQEAAc0pV2hpdGVvdXQg\r\n' +
'VXNlciA8d2hpdGVvdXQudGVzdEB0LW9ubGluZS5kZT7CXAQQAQgAEAUCUlhM\r\n' + 'VXNlciA8d2hpdGVvdXQudGVzdEB0LW9ubGluZS5kZT7CXAQQAQgAEAUCUlhM\r\n' +
'vQkQ9vYOm0LN/0wAAAW4Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXq\r\n' + 'vQkQ9vYOm0LN/0wAAAW4Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXq\r\n' +
'IiN602mWrkd8jcEzLsW5IUNzVPLhrFIuKyBDTpLnC07Loce1\r\n' + 'IiN602mWrkd8jcEzLsW5IUNzVPLhrFIuKyBDTpLnC07Loce1\r\n' +
'=6XMW\r\n' + '=6XMW\r\n' +
'-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n', '-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n',
privkey = '-----BEGIN PGP PRIVATE KEY BLOCK-----\r\n' + privkey = '-----BEGIN PGP PRIVATE KEY BLOCK-----\r\n' +
'Version: OpenPGP.js v0.7.2\r\n' + 'Version: OpenPGP.js v0.7.2\r\n' +
'Comment: Whiteout Mail - https://whiteout.io\r\n' + 'Comment: Whiteout Mail - https://whiteout.io\r\n' +
'\r\n' + '\r\n' +
'xcBeBFJYTLwBAf9jGbQlDgGL8ixYw6dzgTBp9xL/BcI88j2yBdCVMPi+8tl0\r\n' + 'xcBeBFJYTLwBAf9jGbQlDgGL8ixYw6dzgTBp9xL/BcI88j2yBdCVMPi+8tl0\r\n' +
'eUVRr2yvPLp1d3FP9bTmHTbFyOqUqf2bY8r+72wVABEBAAH+AwMIhNB4ivtv\r\n' + 'eUVRr2yvPLp1d3FP9bTmHTbFyOqUqf2bY8r+72wVABEBAAH+AwMIhNB4ivtv\r\n' +
'Y2xg6VeMcjjHxZayESHACV+nQx5Tx6ev6xzIF1Qh72fNPDppLhFSFOuTTMsU\r\n' + 'Y2xg6VeMcjjHxZayESHACV+nQx5Tx6ev6xzIF1Qh72fNPDppLhFSFOuTTMsU\r\n' +
'kTN4c+BVYt29spH+cA1jcDAxQ2ULrNAXo+hheOqhpedTs8aCbcLFkJAS16hk\r\n' + 'kTN4c+BVYt29spH+cA1jcDAxQ2ULrNAXo+hheOqhpedTs8aCbcLFkJAS16hk\r\n' +
'YSk4OnJgp/z24rVju1SHRSFbgundPzmNgXeX9e8IkviGhhQ11Wc5YwVkx03t\r\n' + 'YSk4OnJgp/z24rVju1SHRSFbgundPzmNgXeX9e8IkviGhhQ11Wc5YwVkx03t\r\n' +
'Z3MdDMF0jyhopbPIoBdyJB0dhvBh98w3JmwpYh9wjUA9MBHD1tvHpRmSZ3BM\r\n' + 'Z3MdDMF0jyhopbPIoBdyJB0dhvBh98w3JmwpYh9wjUA9MBHD1tvHpRmSZ3BM\r\n' +
'UCmATn2ZLWBRWiYqFbgDnL1GM80pV2hpdGVvdXQgVXNlciA8d2hpdGVvdXQu\r\n' + 'UCmATn2ZLWBRWiYqFbgDnL1GM80pV2hpdGVvdXQgVXNlciA8d2hpdGVvdXQu\r\n' +
'dGVzdEB0LW9ubGluZS5kZT7CXAQQAQgAEAUCUlhMvQkQ9vYOm0LN/0wAAAW4\r\n' + 'dGVzdEB0LW9ubGluZS5kZT7CXAQQAQgAEAUCUlhMvQkQ9vYOm0LN/0wAAAW4\r\n' +
'Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXqIiN602mWrkd8jcEzLsW5\r\n' + 'Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXqIiN602mWrkd8jcEzLsW5\r\n' +
'IUNzVPLhrFIuKyBDTpLnC07Loce1\r\n' + 'IUNzVPLhrFIuKyBDTpLnC07Loce1\r\n' +
'=ULta\r\n' + '=ULta\r\n' +
'-----END PGP PRIVATE KEY BLOCK-----\r\n'; '-----END PGP PRIVATE KEY BLOCK-----\r\n';
beforeEach(function() { beforeEach(function() {
pgp = new PGP(); pgp = new PGP();
});
afterEach(function() {});
describe('Generate key pair', function() {
it('should fail', function(done) {
pgp.generateKeys({
emailAddress: 'whiteout.test@t-onlinede',
keySize: keySize,
passphrase: passphrase
}, function(err, keys) {
expect(err).to.exist;
expect(keys).to.not.exist;
done();
});
}); });
it('should fail', function(done) {
afterEach(function() {}); pgp.generateKeys({
emailAddress: 'whiteout.testt-online.de',
describe('Generate key pair', function() { keySize: keySize,
it('should fail', function(done) { passphrase: passphrase
pgp.generateKeys({ }, function(err, keys) {
emailAddress: 'whiteout.test@t-onlinede', expect(err).to.exist;
keySize: keySize, expect(keys).to.not.exist;
passphrase: passphrase done();
}, function(err, keys) {
expect(err).to.exist;
expect(keys).to.not.exist;
done();
});
}); });
it('should fail', function(done) { });
pgp.generateKeys({ it('should work with passphrase', function(done) {
emailAddress: 'whiteout.testt-online.de', pgp.generateKeys({
keySize: keySize, emailAddress: user,
passphrase: passphrase keySize: keySize,
}, function(err, keys) { passphrase: passphrase
expect(err).to.exist; }, function(err, keys) {
expect(keys).to.not.exist; expect(err).to.not.exist;
done(); expect(keys.keyId).to.exist;
}); expect(keys.privateKeyArmored).to.exist;
}); expect(keys.publicKeyArmored).to.exist;
it('should work with passphrase', function(done) {
pgp.generateKeys({ // test encrypt/decrypt
emailAddress: user, pgp.importKeys({
keySize: keySize, passphrase: passphrase,
passphrase: passphrase privateKeyArmored: keys.privateKeyArmored,
}, function(err, keys) { publicKeyArmored: keys.publicKeyArmored
}, function(err) {
expect(err).to.not.exist; expect(err).to.not.exist;
expect(keys.keyId).to.exist;
expect(keys.privateKeyArmored).to.exist;
expect(keys.publicKeyArmored).to.exist;
// test encrypt/decrypt pgp.encrypt('secret', [keys.publicKeyArmored], function(err, ct) {
pgp.importKeys({
passphrase: passphrase,
privateKeyArmored: keys.privateKeyArmored,
publicKeyArmored: keys.publicKeyArmored
}, function(err) {
expect(err).to.not.exist; expect(err).to.not.exist;
expect(ct).to.exist;
pgp.encrypt('secret', [keys.publicKeyArmored], function(err, ct) { pgp.decrypt(ct, keys.publicKeyArmored, function(err, pt, signValid) {
expect(err).to.not.exist; expect(err).to.not.exist;
expect(ct).to.exist; expect(pt).to.equal('secret');
expect(signValid).to.be.true;
pgp.decrypt(ct, keys.publicKeyArmored, function(err, pt, signValid) { done();
expect(err).to.not.exist;
expect(pt).to.equal('secret');
expect(signValid).to.be.true;
done();
});
});
});
});
});
it('should work without passphrase', function(done) {
pgp.generateKeys({
emailAddress: user,
keySize: keySize,
passphrase: ''
}, function(err, keys) {
expect(err).to.not.exist;
expect(keys.keyId).to.exist;
expect(keys.privateKeyArmored).to.exist;
expect(keys.publicKeyArmored).to.exist;
// test encrypt/decrypt
pgp.importKeys({
passphrase: undefined,
privateKeyArmored: keys.privateKeyArmored,
publicKeyArmored: keys.publicKeyArmored
}, function(err) {
expect(err).to.not.exist;
pgp.encrypt('secret', [keys.publicKeyArmored], function(err, ct) {
expect(err).to.not.exist;
expect(ct).to.exist;
pgp.decrypt(ct, keys.publicKeyArmored, function(err, pt, signValid) {
expect(err).to.not.exist;
expect(pt).to.equal('secret');
expect(signValid).to.be.true;
done();
});
}); });
}); });
}); });
}); });
}); });
it('should work without passphrase', function(done) {
pgp.generateKeys({
emailAddress: user,
keySize: keySize,
passphrase: ''
}, function(err, keys) {
expect(err).to.not.exist;
expect(keys.keyId).to.exist;
expect(keys.privateKeyArmored).to.exist;
expect(keys.publicKeyArmored).to.exist;
describe('Import/Export key pair', function() { // test encrypt/decrypt
it('should fail', function(done) {
pgp.importKeys({ pgp.importKeys({
passphrase: 'asd', passphrase: undefined,
privateKeyArmored: privkey, privateKeyArmored: keys.privateKeyArmored,
publicKeyArmored: pubkey publicKeyArmored: keys.publicKeyArmored
}, function(err) {
expect(err).to.exist;
expect(err.message).to.equal('Incorrect passphrase!');
pgp.exportKeys(function(err, keys) {
expect(err).to.exist;
expect(keys).to.not.exist;
done();
});
});
});
it('should work', function(done) {
pgp.importKeys({
passphrase: passphrase,
privateKeyArmored: privkey,
publicKeyArmored: pubkey
}, function(err) { }, function(err) {
expect(err).to.not.exist; expect(err).to.not.exist;
pgp.exportKeys(function(err, keys) { pgp.encrypt('secret', [keys.publicKeyArmored], function(err, ct) {
expect(err).to.not.exist;
expect(keys.keyId).to.equal(keyId);
expect(keys.privateKeyArmored.replace(/\r/g, '')).to.equal(privkey.replace(/\r/g, ''));
expect(keys.publicKeyArmored.replace(/\r/g, '')).to.equal(pubkey.replace(/\r/g, ''));
done();
});
});
});
});
describe('Change passphrase of private key', function() {
it('should work with new passphrase', function(done) {
pgp.changePassphrase({
privateKeyArmored: privkey,
oldPassphrase: passphrase,
newPassphrase: 'yxcv'
}, function(err, reEncryptedKey) {
expect(err).to.not.exist;
expect(reEncryptedKey).to.exist;
pgp.importKeys({
passphrase: 'yxcv',
privateKeyArmored: reEncryptedKey,
publicKeyArmored: pubkey
}, function(err) {
expect(err).to.not.exist;
done();
});
});
});
it('should work with empty passphrase', function(done) {
pgp.changePassphrase({
privateKeyArmored: privkey,
oldPassphrase: passphrase,
newPassphrase: undefined
}, function(err, reEncryptedKey) {
expect(err).to.not.exist;
expect(reEncryptedKey).to.exist;
pgp.importKeys({
passphrase: undefined,
privateKeyArmored: reEncryptedKey,
publicKeyArmored: pubkey
}, function(err) {
expect(err).to.not.exist;
done();
});
});
});
it('should fail when passphrases are equal', function(done) {
pgp.changePassphrase({
privateKeyArmored: privkey,
oldPassphrase: passphrase,
newPassphrase: passphrase
}, function(err, reEncryptedKey) {
expect(err).to.exist;
expect(reEncryptedKey).to.not.exist;
done();
});
});
it('should fail when old passphrase is incorrect', function(done) {
pgp.changePassphrase({
privateKeyArmored: privkey,
oldPassphrase: 'asd',
newPassphrase: 'yxcv'
}, function(err, reEncryptedKey) {
expect(err).to.exist;
expect(reEncryptedKey).to.not.exist;
done();
});
});
});
describe('Encrypt/Sign/Decrypt/Verify', function() {
var message = 'asdfs\n\nThursday, Nov 21, 2013 7:38 PM asdf@example.com wrote:\n' +
'> asdf\n' +
'> \n' +
'> Thursday, Nov 21, 2013 7:32 PM asdf@example.com wrote:\n' +
'> > secret 3';
var wrongPubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: OpenPGP.js v.1.20131116\r\nComment: Whiteout Mail - http://whiteout.io\r\n\r\nxsBNBFKODs4BB/9iOF4THsjQMY+WEpT7ShgKxj4bHzRRaQkqczS4nZvP0U3g\r\nqeqCnbpagyeKXA+bhWFQW4GmXtgAoeD5PXs6AZYrw3tWNxLKu2Oe6Tp9K/XI\r\nxTMQ2wl4qZKDXHvuPsJ7cmgaWqpPyXtxA4zHHS3WrkI/6VzHAcI/y6x4szSB\r\nKgSuhI3hjh3s7TybUC1U6AfoQGx/S7e3WwlCOrK8GTClirN/2mCPRC5wuIft\r\nnkoMfA6jK8d2OPrJ63shy5cgwHOjQg/xuk46dNS7tkvGmbaa+X0PgqSKB+Hf\r\nYPPNS/ylg911DH9qa8BqYU2QpNh9jUKXSF+HbaOM+plWkCSAL7czV+R3ABEB\r\nAAHNLVdoaXRlb3V0IFVzZXIgPHNhZmV3aXRobWUudGVzdHVzZXJAZ21haWwu\r\nY29tPsLAXAQQAQgAEAUCUo4O2gkQ1/uT/N+/wjwAAN2cB/9gFRmAfvEQ2qz+\r\nWubmT2EsSSnjPMxzG4uyykFoa+TaZCWo2Xa2tQghmU103kEkQb1OEjRjpgwJ\r\nYX9Kghnl8DByM686L5AXnRyHP78qRJCLXSXl0AGicboUDp5sovaa4rswQceH\r\nvcdWgZ/mgHTRoiQeJddy9k+H6MPFiyFaVcFwegVsmpc+dCcC8yT+qh8ZIbyG\r\nRJU60PmKKN7LUusP+8DbSv39zCGJCBlVVKyA4MzdF5uM+sqTdXbKzOrT5DGd\r\nCZaox4s+w16Sq1rHzZKFWfQPfKLDB9pyA0ufCVRA3AF6BUi7G3ZqhZiHNhMP\r\nNvE45V/hS1PbZcfPVoUjE2qc1Ix1\r\n=7Wpe\r\n-----END PGP PUBLIC KEY BLOCK-----';
beforeEach(function(done) {
pgp.importKeys({
passphrase: passphrase,
privateKeyArmored: privkey,
publicKeyArmored: pubkey
}, function(err) {
expect(err).to.not.exist;
done();
});
});
describe('Get KeyId', function() {
it('should work without param', function() {
var keyId = pgp.getKeyId();
expect(keyId).to.equal('F6F60E9B42CDFF4C');
});
it('should work with param', function() {
var keyId = pgp.getKeyId(pubkey);
expect(keyId).to.equal('F6F60E9B42CDFF4C');
});
});
describe('Get Fingerprint', function() {
it('should work without param', function() {
var fingerprint = pgp.getFingerprint();
expect(fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
});
it('should work with param', function() {
var fingerprint = pgp.getFingerprint(pubkey);
expect(fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
});
});
describe('getKeyParams', function() {
it('should work with param', function() {
var params = pgp.getKeyParams(pubkey);
expect(params.fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
expect(params._id).to.equal("F6F60E9B42CDFF4C");
expect(params.bitSize).to.equal(keySize);
expect(params.userId).to.equal("whiteout.test@t-online.de");
expect(params.userIds[0].name).to.equal("Whiteout User");
expect(params.userIds[0].emailAddress).to.equal("whiteout.test@t-online.de");
expect(params.algorithm).to.equal("rsa_encrypt_sign");
});
it('should work without param', function() {
var params = pgp.getKeyParams();
expect(params.fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
expect(params._id).to.equal("F6F60E9B42CDFF4C");
expect(params.bitSize).to.equal(keySize);
expect(params.userId).to.equal("whiteout.test@t-online.de");
expect(params.userIds[0].name).to.equal("Whiteout User");
expect(params.userIds[0].emailAddress).to.equal("whiteout.test@t-online.de");
expect(params.algorithm).to.equal("rsa_encrypt_sign");
});
});
describe('extractPublicKey', function() {
it('should work', function() {
var pk = pgp.extractPublicKey(privkey);
expect(pk).to.exist;
expect(pk).to.contain('-----BEGIN PGP PUBLIC KEY BLOCK-----');
});
});
describe('Encrypt and sign', function() {
it('should fail', function(done) {
var input = null;
pgp.encrypt(input, [pubkey], function(err, ct) {
expect(err).to.exist;
expect(ct).to.not.exist;
done();
});
});
it('should work', function(done) {
pgp.encrypt(message, [pubkey], function(err, ct) {
expect(err).to.not.exist; expect(err).to.not.exist;
expect(ct).to.exist; expect(ct).to.exist;
done();
});
});
it('should encrypt to myself if public keys are empty', function(done) {
pgp.encrypt(message, undefined, function(err, ct) {
expect(err).to.not.exist;
expect(ct).to.exist;
done();
});
});
});
describe('Decrypt and verify', function() { pgp.decrypt(ct, keys.publicKeyArmored, function(err, pt, signValid) {
var ciphertext; expect(err).to.not.exist;
expect(pt).to.equal('secret');
beforeEach(function(done) { expect(signValid).to.be.true;
pgp.encrypt(message, [pubkey], function(err, ct) { done();
expect(err).to.not.exist; });
expect(ct).to.exist;
ciphertext = ct;
done();
});
});
it('should fail', function(done) {
var input = 'asdfa\rsdf';
pgp.decrypt(input, pubkey, function(err, pt) {
expect(err).to.exist;
expect(pt).to.not.exist;
done();
});
});
it('should work', function(done) {
pgp.decrypt(ciphertext, pubkey, function(err, pt, signValid) {
expect(err).to.not.exist;
expect(pt).to.equal(message);
expect(signValid).to.be.true;
done();
});
});
it('should work without signature', function(done) {
var ct = openpgp.encryptMessage([pgp._publicKey], message);
pgp.decrypt(ct, undefined, function(err, pt, signValid) {
expect(err).to.not.exist;
expect(pt).to.equal(message);
expect(signValid).to.be.undefined;
done();
});
});
it('should fail to verify if public keys are empty', function(done) {
// setup another public key so that signature verification fails
pgp._publicKey = openpgp.key.readArmored(wrongPubkey).keys[0];
pgp.decrypt(ciphertext, undefined, function(err, pt, signValid) {
expect(err).to.not.exist;
expect(pt).to.equal(message);
expect(signValid).to.be.null;
done();
});
});
it('should decrypt but signValid should be null for wrong public key', function(done) {
pgp.decrypt(ciphertext, wrongPubkey, function(err, pt, signValid) {
expect(err).to.not.exist;
expect(pt).to.equal(message);
expect(signValid).to.be.null;
done();
});
});
});
describe('Verify clearsigned message', function() {
var clearsigned;
beforeEach(function() {
clearsigned = openpgp.signClearMessage(pgp._privateKey, 'this is a clearsigned message');
});
it('should work', function(done) {
pgp.verifyClearSignedMessage(clearsigned, pubkey, function(err, signaturesValid) {
expect(err).to.not.exist;
expect(signaturesValid).to.be.true;
done();
});
});
it('should fail', function(done) {
pgp.verifyClearSignedMessage(clearsigned.replace('clearsigned', 'invalid'), pubkey, function(err, signaturesValid) {
expect(err).to.not.exist;
expect(signaturesValid).to.be.false;
done();
});
});
it('should be null for wrong public key', function(done) {
pgp.verifyClearSignedMessage(clearsigned, wrongPubkey, function(err, signaturesValid) {
expect(err).to.not.exist;
expect(signaturesValid).to.be.null;
done();
});
});
});
describe('Verify detached signature', function() {
var signedMessage, signature;
beforeEach(function() {
signedMessage = 'this is a signed message';
var clearsigned = openpgp.signClearMessage(pgp._privateKey, signedMessage);
var signatureHeader = '-----BEGIN PGP SIGNATURE-----';
signature = signatureHeader + clearsigned.split(signatureHeader).pop();
});
it('should work', function(done) {
pgp.verifySignedMessage(signedMessage, signature, pubkey, function(err, signaturesValid) {
expect(err).to.not.exist;
expect(signaturesValid).to.be.true;
done();
});
});
it('should fail', function(done) {
pgp.verifySignedMessage(signedMessage.replace('signed', 'invalid'), signature, pubkey, function(err, signaturesValid) {
expect(err).to.not.exist;
expect(signaturesValid).to.be.false;
done();
});
});
it('should be null for wrong public key', function(done) {
pgp.verifySignedMessage(signedMessage, signature, wrongPubkey, function(err, signaturesValid) {
expect(err).to.not.exist;
expect(signaturesValid).to.be.null;
done();
}); });
}); });
}); });
}); });
}); });
describe('Import/Export key pair', function() {
it('should fail', function(done) {
pgp.importKeys({
passphrase: 'asd',
privateKeyArmored: privkey,
publicKeyArmored: pubkey
}, function(err) {
expect(err).to.exist;
expect(err.message).to.equal('Incorrect passphrase!');
pgp.exportKeys(function(err, keys) {
expect(err).to.exist;
expect(keys).to.not.exist;
done();
});
});
});
it('should work', function(done) {
pgp.importKeys({
passphrase: passphrase,
privateKeyArmored: privkey,
publicKeyArmored: pubkey
}, function(err) {
expect(err).to.not.exist;
pgp.exportKeys(function(err, keys) {
expect(err).to.not.exist;
expect(keys.keyId).to.equal(keyId);
expect(keys.privateKeyArmored.replace(/\r/g, '')).to.equal(privkey.replace(/\r/g, ''));
expect(keys.publicKeyArmored.replace(/\r/g, '')).to.equal(pubkey.replace(/\r/g, ''));
done();
});
});
});
});
describe('Change passphrase of private key', function() {
it('should work with new passphrase', function(done) {
pgp.changePassphrase({
privateKeyArmored: privkey,
oldPassphrase: passphrase,
newPassphrase: 'yxcv'
}, function(err, reEncryptedKey) {
expect(err).to.not.exist;
expect(reEncryptedKey).to.exist;
pgp.importKeys({
passphrase: 'yxcv',
privateKeyArmored: reEncryptedKey,
publicKeyArmored: pubkey
}, function(err) {
expect(err).to.not.exist;
done();
});
});
});
it('should work with empty passphrase', function(done) {
pgp.changePassphrase({
privateKeyArmored: privkey,
oldPassphrase: passphrase,
newPassphrase: undefined
}, function(err, reEncryptedKey) {
expect(err).to.not.exist;
expect(reEncryptedKey).to.exist;
pgp.importKeys({
passphrase: undefined,
privateKeyArmored: reEncryptedKey,
publicKeyArmored: pubkey
}, function(err) {
expect(err).to.not.exist;
done();
});
});
});
it('should fail when passphrases are equal', function(done) {
pgp.changePassphrase({
privateKeyArmored: privkey,
oldPassphrase: passphrase,
newPassphrase: passphrase
}, function(err, reEncryptedKey) {
expect(err).to.exist;
expect(reEncryptedKey).to.not.exist;
done();
});
});
it('should fail when old passphrase is incorrect', function(done) {
pgp.changePassphrase({
privateKeyArmored: privkey,
oldPassphrase: 'asd',
newPassphrase: 'yxcv'
}, function(err, reEncryptedKey) {
expect(err).to.exist;
expect(reEncryptedKey).to.not.exist;
done();
});
});
});
describe('Encrypt/Sign/Decrypt/Verify', function() {
var message = 'asdfs\n\nThursday, Nov 21, 2013 7:38 PM asdf@example.com wrote:\n' +
'> asdf\n' +
'> \n' +
'> Thursday, Nov 21, 2013 7:32 PM asdf@example.com wrote:\n' +
'> > secret 3';
var wrongPubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: OpenPGP.js v.1.20131116\r\nComment: Whiteout Mail - http://whiteout.io\r\n\r\nxsBNBFKODs4BB/9iOF4THsjQMY+WEpT7ShgKxj4bHzRRaQkqczS4nZvP0U3g\r\nqeqCnbpagyeKXA+bhWFQW4GmXtgAoeD5PXs6AZYrw3tWNxLKu2Oe6Tp9K/XI\r\nxTMQ2wl4qZKDXHvuPsJ7cmgaWqpPyXtxA4zHHS3WrkI/6VzHAcI/y6x4szSB\r\nKgSuhI3hjh3s7TybUC1U6AfoQGx/S7e3WwlCOrK8GTClirN/2mCPRC5wuIft\r\nnkoMfA6jK8d2OPrJ63shy5cgwHOjQg/xuk46dNS7tkvGmbaa+X0PgqSKB+Hf\r\nYPPNS/ylg911DH9qa8BqYU2QpNh9jUKXSF+HbaOM+plWkCSAL7czV+R3ABEB\r\nAAHNLVdoaXRlb3V0IFVzZXIgPHNhZmV3aXRobWUudGVzdHVzZXJAZ21haWwu\r\nY29tPsLAXAQQAQgAEAUCUo4O2gkQ1/uT/N+/wjwAAN2cB/9gFRmAfvEQ2qz+\r\nWubmT2EsSSnjPMxzG4uyykFoa+TaZCWo2Xa2tQghmU103kEkQb1OEjRjpgwJ\r\nYX9Kghnl8DByM686L5AXnRyHP78qRJCLXSXl0AGicboUDp5sovaa4rswQceH\r\nvcdWgZ/mgHTRoiQeJddy9k+H6MPFiyFaVcFwegVsmpc+dCcC8yT+qh8ZIbyG\r\nRJU60PmKKN7LUusP+8DbSv39zCGJCBlVVKyA4MzdF5uM+sqTdXbKzOrT5DGd\r\nCZaox4s+w16Sq1rHzZKFWfQPfKLDB9pyA0ufCVRA3AF6BUi7G3ZqhZiHNhMP\r\nNvE45V/hS1PbZcfPVoUjE2qc1Ix1\r\n=7Wpe\r\n-----END PGP PUBLIC KEY BLOCK-----';
beforeEach(function(done) {
pgp.importKeys({
passphrase: passphrase,
privateKeyArmored: privkey,
publicKeyArmored: pubkey
}, function(err) {
expect(err).to.not.exist;
done();
});
});
describe('Get KeyId', function() {
it('should work without param', function() {
var keyId = pgp.getKeyId();
expect(keyId).to.equal('F6F60E9B42CDFF4C');
});
it('should work with param', function() {
var keyId = pgp.getKeyId(pubkey);
expect(keyId).to.equal('F6F60E9B42CDFF4C');
});
});
describe('Get Fingerprint', function() {
it('should work without param', function() {
var fingerprint = pgp.getFingerprint();
expect(fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
});
it('should work with param', function() {
var fingerprint = pgp.getFingerprint(pubkey);
expect(fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
});
});
describe('getKeyParams', function() {
it('should work with param', function() {
var params = pgp.getKeyParams(pubkey);
expect(params.fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
expect(params._id).to.equal("F6F60E9B42CDFF4C");
expect(params.bitSize).to.equal(keySize);
expect(params.userId).to.equal("whiteout.test@t-online.de");
expect(params.userIds[0].name).to.equal("Whiteout User");
expect(params.userIds[0].emailAddress).to.equal("whiteout.test@t-online.de");
expect(params.algorithm).to.equal("rsa_encrypt_sign");
});
it('should work without param', function() {
var params = pgp.getKeyParams();
expect(params.fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
expect(params._id).to.equal("F6F60E9B42CDFF4C");
expect(params.bitSize).to.equal(keySize);
expect(params.userId).to.equal("whiteout.test@t-online.de");
expect(params.userIds[0].name).to.equal("Whiteout User");
expect(params.userIds[0].emailAddress).to.equal("whiteout.test@t-online.de");
expect(params.algorithm).to.equal("rsa_encrypt_sign");
});
});
describe('extractPublicKey', function() {
it('should work', function() {
var pk = pgp.extractPublicKey(privkey);
expect(pk).to.exist;
expect(pk).to.contain('-----BEGIN PGP PUBLIC KEY BLOCK-----');
});
});
describe('Encrypt and sign', function() {
it('should fail', function(done) {
var input = null;
pgp.encrypt(input, [pubkey], function(err, ct) {
expect(err).to.exist;
expect(ct).to.not.exist;
done();
});
});
it('should work', function(done) {
pgp.encrypt(message, [pubkey], function(err, ct) {
expect(err).to.not.exist;
expect(ct).to.exist;
done();
});
});
it('should encrypt to myself if public keys are empty', function(done) {
pgp.encrypt(message, undefined, function(err, ct) {
expect(err).to.not.exist;
expect(ct).to.exist;
done();
});
});
});
describe('Decrypt and verify', function() {
var ciphertext;
beforeEach(function(done) {
pgp.encrypt(message, [pubkey], function(err, ct) {
expect(err).to.not.exist;
expect(ct).to.exist;
ciphertext = ct;
done();
});
});
it('should fail', function(done) {
var input = 'asdfa\rsdf';
pgp.decrypt(input, pubkey, function(err, pt) {
expect(err).to.exist;
expect(pt).to.not.exist;
done();
});
});
it('should work', function(done) {
pgp.decrypt(ciphertext, pubkey, function(err, pt, signValid) {
expect(err).to.not.exist;
expect(pt).to.equal(message);
expect(signValid).to.be.true;
done();
});
});
it('should work without signature', function(done) {
var ct = openpgp.encryptMessage([pgp._publicKey], message);
pgp.decrypt(ct, undefined, function(err, pt, signValid) {
expect(err).to.not.exist;
expect(pt).to.equal(message);
expect(signValid).to.be.undefined;
done();
});
});
it('should fail to verify if public keys are empty', function(done) {
// setup another public key so that signature verification fails
pgp._publicKey = openpgp.key.readArmored(wrongPubkey).keys[0];
pgp.decrypt(ciphertext, undefined, function(err, pt, signValid) {
expect(err).to.not.exist;
expect(pt).to.equal(message);
expect(signValid).to.be.null;
done();
});
});
it('should decrypt but signValid should be null for wrong public key', function(done) {
pgp.decrypt(ciphertext, wrongPubkey, function(err, pt, signValid) {
expect(err).to.not.exist;
expect(pt).to.equal(message);
expect(signValid).to.be.null;
done();
});
});
});
describe('Verify clearsigned message', function() {
var clearsigned;
beforeEach(function() {
clearsigned = openpgp.signClearMessage(pgp._privateKey, 'this is a clearsigned message');
});
it('should work', function(done) {
pgp.verifyClearSignedMessage(clearsigned, pubkey, function(err, signaturesValid) {
expect(err).to.not.exist;
expect(signaturesValid).to.be.true;
done();
});
});
it('should fail', function(done) {
pgp.verifyClearSignedMessage(clearsigned.replace('clearsigned', 'invalid'), pubkey, function(err, signaturesValid) {
expect(err).to.not.exist;
expect(signaturesValid).to.be.false;
done();
});
});
it('should be null for wrong public key', function(done) {
pgp.verifyClearSignedMessage(clearsigned, wrongPubkey, function(err, signaturesValid) {
expect(err).to.not.exist;
expect(signaturesValid).to.be.null;
done();
});
});
});
describe('Verify detached signature', function() {
var signedMessage, signature;
beforeEach(function() {
signedMessage = 'this is a signed message';
var clearsigned = openpgp.signClearMessage(pgp._privateKey, signedMessage);
var signatureHeader = '-----BEGIN PGP SIGNATURE-----';
signature = signatureHeader + clearsigned.split(signatureHeader).pop();
});
it('should work', function(done) {
pgp.verifySignedMessage(signedMessage, signature, pubkey, function(err, signaturesValid) {
expect(err).to.not.exist;
expect(signaturesValid).to.be.true;
done();
});
});
it('should fail', function(done) {
pgp.verifySignedMessage(signedMessage.replace('signed', 'invalid'), signature, pubkey, function(err, signaturesValid) {
expect(err).to.not.exist;
expect(signaturesValid).to.be.false;
done();
});
});
it('should be null for wrong public key', function(done) {
pgp.verifySignedMessage(signedMessage, signature, wrongPubkey, function(err, signaturesValid) {
expect(err).to.not.exist;
expect(signaturesValid).to.be.null;
done();
});
});
});
});
}); });

View File

@ -1,226 +1,222 @@
define(function(require) { 'use strict';
'use strict';
var RestDAO = require('js/dao/rest-dao'), var RestDAO = require('../../src/js/dao/rest-dao'),
PrivateKeyDAO = require('js/dao/privatekey-dao'), PrivateKeyDAO = require('../../src/js/dao/privatekey-dao');
expect = chai.expect;
describe('Private Key DAO unit tests', function() { describe('Private Key DAO unit tests', function() {
var privkeyDao, restDaoStub, var privkeyDao, restDaoStub,
emailAddress = 'test@example.com', emailAddress = 'test@example.com',
deviceName = 'iPhone Work'; deviceName = 'iPhone Work';
beforeEach(function() { beforeEach(function() {
restDaoStub = sinon.createStubInstance(RestDAO); restDaoStub = sinon.createStubInstance(RestDAO);
privkeyDao = new PrivateKeyDAO(restDaoStub); privkeyDao = new PrivateKeyDAO(restDaoStub);
}); });
afterEach(function() {}); afterEach(function() {});
describe('requestDeviceRegistration', function() { describe('requestDeviceRegistration', function() {
it('should fail due to invalid args', function(done) { it('should fail due to invalid args', function(done) {
privkeyDao.requestDeviceRegistration({}, function(err, sessionKey) { privkeyDao.requestDeviceRegistration({}, function(err, sessionKey) {
expect(err).to.exist; expect(err).to.exist;
expect(sessionKey).to.not.exist; expect(sessionKey).to.not.exist;
done(); done();
});
});
it('should work', function(done) {
restDaoStub.post.yields(null, {
encryptedRegSessionKey: 'asdf'
});
privkeyDao.requestDeviceRegistration({
userId: emailAddress,
deviceName: deviceName
}, function(err, sessionKey) {
expect(err).to.not.exist;
expect(sessionKey).to.exist;
done();
});
}); });
}); });
describe('uploadDeviceSecret', function() { it('should work', function(done) {
it('should fail due to invalid args', function(done) { restDaoStub.post.yields(null, {
privkeyDao.uploadDeviceSecret({}, function(err) { encryptedRegSessionKey: 'asdf'
expect(err).to.exist;
done();
});
}); });
it('should work', function(done) { privkeyDao.requestDeviceRegistration({
restDaoStub.put.yields(); userId: emailAddress,
deviceName: deviceName
}, function(err, sessionKey) {
expect(err).to.not.exist;
expect(sessionKey).to.exist;
done();
});
});
});
privkeyDao.uploadDeviceSecret({ describe('uploadDeviceSecret', function() {
userId: emailAddress, it('should fail due to invalid args', function(done) {
deviceName: deviceName, privkeyDao.uploadDeviceSecret({}, function(err) {
encryptedDeviceSecret: 'asdf', expect(err).to.exist;
iv: 'iv' done();
}, function(err) {
expect(err).to.not.exist;
done();
});
}); });
}); });
describe('requestAuthSessionKey', function() { it('should work', function(done) {
it('should fail due to invalid args', function(done) { restDaoStub.put.yields();
privkeyDao.requestAuthSessionKey({}, function(err) {
expect(err).to.exist; privkeyDao.uploadDeviceSecret({
done(); userId: emailAddress,
}); deviceName: deviceName,
encryptedDeviceSecret: 'asdf',
iv: 'iv'
}, function(err) {
expect(err).to.not.exist;
done();
}); });
});
});
it('should work', function(done) { describe('requestAuthSessionKey', function() {
restDaoStub.post.withArgs(undefined, '/auth/user/' + emailAddress).yields(); it('should fail due to invalid args', function(done) {
privkeyDao.requestAuthSessionKey({}, function(err) {
privkeyDao.requestAuthSessionKey({ expect(err).to.exist;
userId: emailAddress done();
}, function(err) {
expect(err).to.not.exist;
done();
});
}); });
}); });
describe('verifyAuthentication', function() { it('should work', function(done) {
it('should fail due to invalid args', function(done) { restDaoStub.post.withArgs(undefined, '/auth/user/' + emailAddress).yields();
privkeyDao.verifyAuthentication({}, function(err) {
expect(err).to.exist; privkeyDao.requestAuthSessionKey({
done(); userId: emailAddress
}); }, function(err) {
expect(err).to.not.exist;
done();
}); });
});
});
it('should work', function(done) { describe('verifyAuthentication', function() {
var sessionId = '1'; it('should fail due to invalid args', function(done) {
privkeyDao.verifyAuthentication({}, function(err) {
var options = { expect(err).to.exist;
userId: emailAddress, done();
sessionId: sessionId,
encryptedChallenge: 'asdf',
encryptedDeviceSecret: 'qwer',
iv: ' iv'
};
restDaoStub.put.withArgs(options, '/auth/user/' + emailAddress + '/session/' + sessionId).yields();
privkeyDao.verifyAuthentication(options, function(err) {
expect(err).to.not.exist;
done();
});
}); });
}); });
describe('upload', function() { it('should work', function(done) {
it('should fail due to invalid args', function(done) { var sessionId = '1';
privkeyDao.upload({}, function(err) {
expect(err).to.exist; var options = {
done(); userId: emailAddress,
}); sessionId: sessionId,
encryptedChallenge: 'asdf',
encryptedDeviceSecret: 'qwer',
iv: ' iv'
};
restDaoStub.put.withArgs(options, '/auth/user/' + emailAddress + '/session/' + sessionId).yields();
privkeyDao.verifyAuthentication(options, function(err) {
expect(err).to.not.exist;
done();
}); });
});
});
it('should work', function(done) { describe('upload', function() {
var options = { it('should fail due to invalid args', function(done) {
_id: '12345', privkeyDao.upload({}, function(err) {
userId: emailAddress, expect(err).to.exist;
encryptedPrivateKey: 'asdf', done();
sessionId: '1',
salt: 'salt',
iv: 'iv'
};
restDaoStub.post.withArgs(options, '/privatekey/user/' + emailAddress + '/session/' + options.sessionId).yields();
privkeyDao.upload(options, function(err) {
expect(err).to.not.exist;
done();
});
}); });
}); });
describe('requestDownload', function() { it('should work', function(done) {
it('should fail due to invalid args', function(done) { var options = {
privkeyDao.requestDownload({}, function(err) { _id: '12345',
expect(err).to.exist; userId: emailAddress,
done(); encryptedPrivateKey: 'asdf',
}); sessionId: '1',
salt: 'salt',
iv: 'iv'
};
restDaoStub.post.withArgs(options, '/privatekey/user/' + emailAddress + '/session/' + options.sessionId).yields();
privkeyDao.upload(options, function(err) {
expect(err).to.not.exist;
done();
}); });
});
});
it('should work', function(done) { describe('requestDownload', function() {
var keyId = '12345'; it('should fail due to invalid args', function(done) {
privkeyDao.requestDownload({}, function(err) {
restDaoStub.get.withArgs({ expect(err).to.exist;
uri: '/privatekey/user/' + emailAddress + '/key/' + keyId done();
}).yields();
privkeyDao.requestDownload({
userId: emailAddress,
keyId: keyId
}, function(err, found) {
expect(err).to.not.exist;
expect(found).to.be.true;
done();
});
}); });
}); });
describe('hasPrivateKey', function() { it('should work', function(done) {
it('should fail due to invalid args', function(done) { var keyId = '12345';
privkeyDao.hasPrivateKey({}, function(err) {
expect(err).to.exist; restDaoStub.get.withArgs({
done(); uri: '/privatekey/user/' + emailAddress + '/key/' + keyId
}); }).yields();
privkeyDao.requestDownload({
userId: emailAddress,
keyId: keyId
}, function(err, found) {
expect(err).to.not.exist;
expect(found).to.be.true;
done();
}); });
});
});
it('should work', function(done) { describe('hasPrivateKey', function() {
var keyId = '12345'; it('should fail due to invalid args', function(done) {
privkeyDao.hasPrivateKey({}, function(err) {
restDaoStub.get.withArgs({ expect(err).to.exist;
uri: '/privatekey/user/' + emailAddress + '/key/' + keyId + '?ignoreRecovery=true' done();
}).yields();
privkeyDao.hasPrivateKey({
userId: emailAddress,
keyId: keyId
}, function(err, found) {
expect(err).to.not.exist;
expect(found).to.be.true;
done();
});
}); });
}); });
describe('download', function() { it('should work', function(done) {
it('should fail due to invalid args', function(done) { var keyId = '12345';
privkeyDao.download({}, function(err) {
expect(err).to.exist; restDaoStub.get.withArgs({
done(); uri: '/privatekey/user/' + emailAddress + '/key/' + keyId + '?ignoreRecovery=true'
}); }).yields();
privkeyDao.hasPrivateKey({
userId: emailAddress,
keyId: keyId
}, function(err, found) {
expect(err).to.not.exist;
expect(found).to.be.true;
done();
}); });
});
});
it('should work', function(done) { describe('download', function() {
var key = { it('should fail due to invalid args', function(done) {
_id: '12345' privkeyDao.download({}, function(err) {
}; expect(err).to.exist;
done();
restDaoStub.get.withArgs({
uri: '/privatekey/user/' + emailAddress + '/key/' + key._id + '/recovery/token'
}).yields();
privkeyDao.download({
userId: emailAddress,
keyId: key._id,
recoveryToken: 'token'
}, function(err) {
expect(err).to.not.exist;
done();
});
}); });
}); });
it('should work', function(done) {
var key = {
_id: '12345'
};
restDaoStub.get.withArgs({
uri: '/privatekey/user/' + emailAddress + '/key/' + key._id + '/recovery/token'
}).yields();
privkeyDao.download({
userId: emailAddress,
keyId: key._id,
recoveryToken: 'token'
}, function(err) {
expect(err).to.not.exist;
done();
});
});
}); });
}); });

View File

@ -1,286 +1,281 @@
define(function(require) { 'use strict';
'use strict';
var expect = chai.expect, var mocks = angular.mocks,
angular = require('angular'), PrivateKeyUploadCtrl = require('../../src/js/controller/privatekey-upload'),
mocks = require('angularMocks'), appController = require('../../src/js/app-controller'),
PrivateKeyUploadCtrl = require('js/controller/privatekey-upload'), KeychainDAO = require('../../src/js/dao/keychain-dao'),
appController = require('js/app-controller'), PGP = require('../../src/js/crypto/pgp');
KeychainDAO = require('js/dao/keychain-dao'),
PGP = require('js/crypto/pgp');
describe('Private Key Upload Controller unit test', function() { describe('Private Key Upload Controller unit test', function() {
var scope, location, ctrl, var scope, location, ctrl,
origEmailDao, emailDaoMock, origEmailDao, emailDaoMock,
origKeychain, keychainMock, origKeychain, keychainMock,
pgpStub, pgpStub,
emailAddress = 'fred@foo.com'; emailAddress = 'fred@foo.com';
beforeEach(function(done) { beforeEach(function(done) {
// remember original module to restore later, then replace it // remember original module to restore later, then replace it
origEmailDao = appController._emailDao; origEmailDao = appController._emailDao;
appController._emailDao = emailDaoMock = { appController._emailDao = emailDaoMock = {
_account: { _account: {
emailAddress: emailAddress emailAddress: emailAddress
} }
};
origKeychain = appController._keychain;
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
keychainMock._pgp = pgpStub = sinon.createStubInstance(PGP);
angular.module('login-privatekey-download-test', []);
mocks.module('login-privatekey-download-test');
mocks.inject(function($controller, $rootScope) {
scope = $rootScope.$new();
scope.state = {};
ctrl = $controller(PrivateKeyUploadCtrl, {
$location: location,
$scope: scope
});
done();
});
});
afterEach(function() {
// restore the app controller module
appController._keychain = origKeychain;
appController._emailDao = origEmailDao;
});
describe('checkServerForKey', function() {
var keyParams = {
userId: emailAddress,
_id: 'keyId',
};
it('should fail', function(done) {
pgpStub.getKeyParams.returns(keyParams);
keychainMock.hasPrivateKey.yields(42);
scope.onError = function(err) {
expect(err).to.exist;
expect(keychainMock.hasPrivateKey.calledOnce).to.be.true;
done();
}; };
origKeychain = appController._keychain;
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
keychainMock._pgp = pgpStub = sinon.createStubInstance(PGP);
angular.module('login-privatekey-download-test', []); scope.checkServerForKey();
mocks.module('login-privatekey-download-test'); });
mocks.inject(function($controller, $rootScope) {
scope = $rootScope.$new(); it('should return true', function(done) {
scope.state = {}; pgpStub.getKeyParams.returns(keyParams);
ctrl = $controller(PrivateKeyUploadCtrl, { keychainMock.hasPrivateKey.withArgs({
$location: location, userId: keyParams.userId,
$scope: scope keyId: keyParams._id
}); }).yields(null, true);
scope.checkServerForKey(function(privateKeySynced) {
expect(privateKeySynced).to.be.true;
done(); done();
}); });
}); });
afterEach(function() { it('should return undefined', function(done) {
// restore the app controller module pgpStub.getKeyParams.returns(keyParams);
appController._keychain = origKeychain; keychainMock.hasPrivateKey.withArgs({
appController._emailDao = origEmailDao; userId: keyParams.userId,
keyId: keyParams._id
}).yields(null, false);
scope.checkServerForKey(function(privateKeySynced) {
expect(privateKeySynced).to.be.undefined;
done();
});
});
});
describe('handlePaste', function() {
it('should work', function() {
scope.handlePaste({
clipboardData: {
getData: function(val) {
expect(val).to.equal('text/plain');
return '1qaz-2wsx-3edc-4rfv-5tgb-6yhn';
}
}
});
expect(scope.code0).to.equal('1qaz');
expect(scope.code1).to.equal('2wsx');
expect(scope.code2).to.equal('3edc');
expect(scope.code3).to.equal('4rfv');
expect(scope.code4).to.equal('5tgb');
expect(scope.code5).to.equal('6yhn');
});
});
describe('displayUploadUi', function() {
it('should work', function() {
// add some artifacts from a previous key input
scope.code0 = scope.code1 = scope.code2 = scope.code3 = scope.code4 = scope.code5 = 'asdasd';
scope.displayUploadUi();
expect(scope.step).to.equal(1);
expect(scope.code.length).to.equal(24);
// artifacts should be cleared
expect(scope.code0).to.be.empty;
expect(scope.code1).to.be.empty;
expect(scope.code2).to.be.empty;
expect(scope.code3).to.be.empty;
expect(scope.code4).to.be.empty;
expect(scope.code5).to.be.empty;
});
});
describe('verifyCode', function() {
it('should fail for wrong code', function() {
scope.code0 = 'b';
scope.code1 = 'b';
scope.code2 = 'b';
scope.code3 = 'b';
scope.code4 = 'b';
scope.code5 = 'b';
scope.code = 'aaaaaa';
scope.onError = function() {};
expect(scope.verifyCode()).to.be.false;
}); });
describe('checkServerForKey', function() { it('should work', function() {
var keyParams = { scope.code0 = 'a';
userId: emailAddress, scope.code1 = 'a';
_id: 'keyId', scope.code2 = 'a';
scope.code3 = 'a';
scope.code4 = 'a';
scope.code5 = 'a';
scope.code = 'aaaaaa';
scope.onError = function() {};
expect(scope.verifyCode()).to.be.false;
});
});
describe('setDeviceName', function() {
it('should work', function(done) {
keychainMock.setDeviceName.yields();
scope.setDeviceName(done);
});
});
describe('encryptAndUploadKey', function() {
it('should fail due to keychain.registerDevice', function(done) {
keychainMock.registerDevice.yields(42);
scope.onError = function(err) {
expect(err).to.exist;
expect(keychainMock.registerDevice.calledOnce).to.be.true;
done();
}; };
it('should fail', function(done) { scope.encryptAndUploadKey();
pgpStub.getKeyParams.returns(keyParams);
keychainMock.hasPrivateKey.yields(42);
scope.onError = function(err) {
expect(err).to.exist;
expect(keychainMock.hasPrivateKey.calledOnce).to.be.true;
done();
};
scope.checkServerForKey();
});
it('should return true', function(done) {
pgpStub.getKeyParams.returns(keyParams);
keychainMock.hasPrivateKey.withArgs({
userId: keyParams.userId,
keyId: keyParams._id
}).yields(null, true);
scope.checkServerForKey(function(privateKeySynced) {
expect(privateKeySynced).to.be.true;
done();
});
});
it('should return undefined', function(done) {
pgpStub.getKeyParams.returns(keyParams);
keychainMock.hasPrivateKey.withArgs({
userId: keyParams.userId,
keyId: keyParams._id
}).yields(null, false);
scope.checkServerForKey(function(privateKeySynced) {
expect(privateKeySynced).to.be.undefined;
done();
});
});
}); });
describe('handlePaste', function() { it('should work', function(done) {
it('should work', function() { keychainMock.registerDevice.yields();
scope.handlePaste({ keychainMock.uploadPrivateKey.yields();
clipboardData: {
getData: function(val) {
expect(val).to.equal('text/plain');
return '1qaz-2wsx-3edc-4rfv-5tgb-6yhn';
}
}
});
expect(scope.code0).to.equal('1qaz'); scope.encryptAndUploadKey(function(err) {
expect(scope.code1).to.equal('2wsx'); expect(err).to.not.exist;
expect(scope.code2).to.equal('3edc'); expect(keychainMock.registerDevice.calledOnce).to.be.true;
expect(scope.code3).to.equal('4rfv'); expect(keychainMock.uploadPrivateKey.calledOnce).to.be.true;
expect(scope.code4).to.equal('5tgb'); done();
expect(scope.code5).to.equal('6yhn');
}); });
}); });
});
describe('displayUploadUi', function() { describe('goBack', function() {
it('should work', function() { it('should work', function() {
// add some artifacts from a previous key input scope.step = 2;
scope.code0 = scope.code1 = scope.code2 = scope.code3 = scope.code4 = scope.code5 = 'asdasd'; scope.goBack();
expect(scope.step).to.equal(1);
scope.displayUploadUi();
expect(scope.step).to.equal(1);
expect(scope.code.length).to.equal(24);
// artifacts should be cleared
expect(scope.code0).to.be.empty;
expect(scope.code1).to.be.empty;
expect(scope.code2).to.be.empty;
expect(scope.code3).to.be.empty;
expect(scope.code4).to.be.empty;
expect(scope.code5).to.be.empty;
});
}); });
describe('verifyCode', function() { it('should not work for < 2', function() {
it('should fail for wrong code', function() { scope.step = 1;
scope.code0 = 'b'; scope.goBack();
scope.code1 = 'b'; expect(scope.step).to.equal(1);
scope.code2 = 'b'; });
scope.code3 = 'b'; });
scope.code4 = 'b';
scope.code5 = 'b';
scope.code = 'aaaaaa';
scope.onError = function() {}; describe('goForward', function() {
expect(scope.verifyCode()).to.be.false; var verifyCodeStub, setDeviceNameStub, encryptAndUploadKeyStub;
}); beforeEach(function() {
verifyCodeStub = sinon.stub(scope, 'verifyCode');
it('should work', function() { setDeviceNameStub = sinon.stub(scope, 'setDeviceName');
scope.code0 = 'a'; encryptAndUploadKeyStub = sinon.stub(scope, 'encryptAndUploadKey');
scope.code1 = 'a'; });
scope.code2 = 'a'; afterEach(function() {
scope.code3 = 'a'; verifyCodeStub.restore();
scope.code4 = 'a'; setDeviceNameStub.restore();
scope.code5 = 'a'; encryptAndUploadKeyStub.restore();
scope.code = 'aaaaaa';
scope.onError = function() {};
expect(scope.verifyCode()).to.be.false;
});
}); });
describe('setDeviceName', function() { it('should work for < 2', function() {
it('should work', function(done) { scope.step = 1;
keychainMock.setDeviceName.yields(); scope.goForward();
scope.setDeviceName(done); expect(scope.step).to.equal(2);
});
}); });
describe('encryptAndUploadKey', function() { it('should work for 2', function() {
it('should fail due to keychain.registerDevice', function(done) { verifyCodeStub.returns(true);
keychainMock.registerDevice.yields(42); scope.step = 2;
scope.goForward();
scope.onError = function(err) { expect(scope.step).to.equal(3);
expect(err).to.exist;
expect(keychainMock.registerDevice.calledOnce).to.be.true;
done();
};
scope.encryptAndUploadKey();
});
it('should work', function(done) {
keychainMock.registerDevice.yields();
keychainMock.uploadPrivateKey.yields();
scope.encryptAndUploadKey(function(err) {
expect(err).to.not.exist;
expect(keychainMock.registerDevice.calledOnce).to.be.true;
expect(keychainMock.uploadPrivateKey.calledOnce).to.be.true;
done();
});
});
}); });
describe('goBack', function() { it('should not work for 2 when code invalid', function() {
it('should work', function() { verifyCodeStub.returns(false);
scope.step = 2; scope.step = 2;
scope.goBack(); scope.goForward();
expect(scope.step).to.equal(1); expect(scope.step).to.equal(2);
});
it('should not work for < 2', function() {
scope.step = 1;
scope.goBack();
expect(scope.step).to.equal(1);
});
}); });
describe('goForward', function() { it('should fail for 3 due to error in setDeviceName', function(done) {
var verifyCodeStub, setDeviceNameStub, encryptAndUploadKeyStub; scope.step = 3;
beforeEach(function() { setDeviceNameStub.yields(42);
verifyCodeStub = sinon.stub(scope, 'verifyCode');
setDeviceNameStub = sinon.stub(scope, 'setDeviceName');
encryptAndUploadKeyStub = sinon.stub(scope, 'encryptAndUploadKey');
});
afterEach(function() {
verifyCodeStub.restore();
setDeviceNameStub.restore();
encryptAndUploadKeyStub.restore();
});
it('should work for < 2', function() { scope.onError = function(err) {
scope.step = 1; expect(err).to.exist;
scope.goForward();
expect(scope.step).to.equal(2);
});
it('should work for 2', function() {
verifyCodeStub.returns(true);
scope.step = 2;
scope.goForward();
expect(scope.step).to.equal(3); expect(scope.step).to.equal(3);
}); done();
};
it('should not work for 2 when code invalid', function() { scope.goForward();
verifyCodeStub.returns(false);
scope.step = 2;
scope.goForward();
expect(scope.step).to.equal(2);
});
it('should fail for 3 due to error in setDeviceName', function(done) {
scope.step = 3;
setDeviceNameStub.yields(42);
scope.onError = function(err) {
expect(err).to.exist;
expect(scope.step).to.equal(3);
done();
};
scope.goForward();
});
it('should fail for 3 due to error in encryptAndUploadKey', function(done) {
scope.step = 3;
setDeviceNameStub.yields();
encryptAndUploadKeyStub.yields(42);
scope.onError = function(err) {
expect(err).to.exist;
expect(scope.step).to.equal(4);
done();
};
scope.goForward();
});
it('should work for 3', function(done) {
scope.step = 3;
setDeviceNameStub.yields();
encryptAndUploadKeyStub.yields();
scope.onError = function(err) {
expect(err.title).to.equal('Success');
expect(scope.step).to.equal(4);
done();
};
scope.goForward();
});
}); });
it('should fail for 3 due to error in encryptAndUploadKey', function(done) {
scope.step = 3;
setDeviceNameStub.yields();
encryptAndUploadKeyStub.yields(42);
scope.onError = function(err) {
expect(err).to.exist;
expect(scope.step).to.equal(4);
done();
};
scope.goForward();
});
it('should work for 3', function(done) {
scope.step = 3;
setDeviceNameStub.yields();
encryptAndUploadKeyStub.yields();
scope.onError = function(err) {
expect(err.title).to.equal('Success');
expect(scope.step).to.equal(4);
done();
};
scope.goForward();
});
}); });
}); });

View File

@ -1,165 +1,161 @@
define(function(require) { 'use strict';
'use strict';
var RestDAO = require('js/dao/rest-dao'), var RestDAO = require('../../src/js/dao/rest-dao'),
PublicKeyDAO = require('js/dao/publickey-dao'), PublicKeyDAO = require('../../src/js/dao/publickey-dao');
expect = chai.expect;
describe('Public Key DAO unit tests', function() { describe('Public Key DAO unit tests', function() {
var pubkeyDao, restDaoStub; var pubkeyDao, restDaoStub;
beforeEach(function() { beforeEach(function() {
restDaoStub = sinon.createStubInstance(RestDAO); restDaoStub = sinon.createStubInstance(RestDAO);
pubkeyDao = new PublicKeyDAO(restDaoStub); pubkeyDao = new PublicKeyDAO(restDaoStub);
}); });
afterEach(function() {}); afterEach(function() {});
describe('get', function() { describe('get', function() {
it('should fail', function(done) { it('should fail', function(done) {
restDaoStub.get.yields(42); restDaoStub.get.yields(42);
pubkeyDao.get('id', function(err, key) { pubkeyDao.get('id', function(err, key) {
expect(err).to.exist; expect(err).to.exist;
expect(key).to.not.exist; expect(key).to.not.exist;
expect(restDaoStub.get.calledOnce).to.be.true; expect(restDaoStub.get.calledOnce).to.be.true;
done(); done();
});
});
it('should work', function(done) {
restDaoStub.get.yields(null, {
_id: '12345',
publicKey: 'asdf'
});
pubkeyDao.get('id', function(err, key) {
expect(err).to.not.exist;
expect(key).to.exist;
expect(key._id).to.exist;
expect(key.publicKey).to.exist;
expect(restDaoStub.get.calledOnce).to.be.true;
done();
});
}); });
}); });
describe('verify', function() { it('should work', function(done) {
it('should fail', function(done) { restDaoStub.get.yields(null, {
restDaoStub.get.yields(42); _id: '12345',
publicKey: 'asdf'
pubkeyDao.verify('id', function(err) {
expect(err).to.exist;
done();
});
}); });
it('should not error for 400', function(done) { pubkeyDao.get('id', function(err, key) {
restDaoStub.get.yields({ expect(err).to.not.exist;
code: 400 expect(key).to.exist;
}); expect(key._id).to.exist;
expect(key.publicKey).to.exist;
pubkeyDao.verify('id', function(err) { expect(restDaoStub.get.calledOnce).to.be.true;
expect(err).to.not.exist; done();
done();
});
}); });
});
});
it('should work', function(done) { describe('verify', function() {
var uuid = 'c621e328-8548-40a1-8309-adf1955e98a9'; it('should fail', function(done) {
restDaoStub.get.yields(null); restDaoStub.get.yields(42);
pubkeyDao.verify(uuid, function(err) { pubkeyDao.verify('id', function(err) {
expect(err).to.not.exist; expect(err).to.exist;
expect(restDaoStub.get.calledWith(sinon.match(function(arg){ done();
return arg.uri === '/verify/' + uuid && arg.type === 'text';
}))).to.be.true;
done();
});
}); });
}); });
describe('get by userId', function() { it('should not error for 400', function(done) {
it('should fail', function(done) { restDaoStub.get.yields({
restDaoStub.get.yields(42); code: 400
pubkeyDao.getByUserId('userId', function(err, key) {
expect(err).to.exist;
expect(key).to.not.exist;
expect(restDaoStub.get.calledOnce).to.be.true;
done();
});
}); });
it('should react to 404', function(done) { pubkeyDao.verify('id', function(err) {
restDaoStub.get.yields({ expect(err).to.not.exist;
code: 404 done();
});
pubkeyDao.getByUserId('userId', function(err, key) {
expect(err).to.not.exist;
expect(key).to.not.exist;
expect(restDaoStub.get.calledOnce).to.be.true;
done();
});
});
it('should return empty array', function(done) {
restDaoStub.get.yields(null, []);
pubkeyDao.getByUserId('userId', function(err, key) {
expect(err).to.not.exist;
expect(key).to.not.exist;
expect(restDaoStub.get.calledOnce).to.be.true;
done();
});
});
it('should work', function(done) {
restDaoStub.get.yields(null, [{
_id: '12345',
publicKey: 'asdf'
}]);
pubkeyDao.getByUserId('userId', function(err, key) {
expect(err).to.not.exist;
expect(key).to.exist;
expect(key._id).to.exist;
expect(key.publicKey).to.exist;
expect(restDaoStub.get.calledOnce).to.be.true;
done();
});
}); });
}); });
describe('put', function() { it('should work', function(done) {
it('should fail', function(done) { var uuid = 'c621e328-8548-40a1-8309-adf1955e98a9';
restDaoStub.put.yields(); restDaoStub.get.yields(null);
pubkeyDao.put({ pubkeyDao.verify(uuid, function(err) {
_id: '12345', expect(err).to.not.exist;
publicKey: 'asdf' expect(restDaoStub.get.calledWith(sinon.match(function(arg) {
}, function(err) { return arg.uri === '/verify/' + uuid && arg.type === 'text';
expect(err).to.not.exist; }))).to.be.true;
expect(restDaoStub.put.calledOnce).to.be.true; done();
done(); });
}); });
});
describe('get by userId', function() {
it('should fail', function(done) {
restDaoStub.get.yields(42);
pubkeyDao.getByUserId('userId', function(err, key) {
expect(err).to.exist;
expect(key).to.not.exist;
expect(restDaoStub.get.calledOnce).to.be.true;
done();
}); });
}); });
describe('remove', function() { it('should react to 404', function(done) {
it('should fail', function(done) { restDaoStub.get.yields({
restDaoStub.remove.yields(); code: 404
});
pubkeyDao.remove('12345', function(err) { pubkeyDao.getByUserId('userId', function(err, key) {
expect(err).to.not.exist; expect(err).to.not.exist;
expect(restDaoStub.remove.calledOnce).to.be.true; expect(key).to.not.exist;
done(); expect(restDaoStub.get.calledOnce).to.be.true;
}); done();
}); });
}); });
it('should return empty array', function(done) {
restDaoStub.get.yields(null, []);
pubkeyDao.getByUserId('userId', function(err, key) {
expect(err).to.not.exist;
expect(key).to.not.exist;
expect(restDaoStub.get.calledOnce).to.be.true;
done();
});
});
it('should work', function(done) {
restDaoStub.get.yields(null, [{
_id: '12345',
publicKey: 'asdf'
}]);
pubkeyDao.getByUserId('userId', function(err, key) {
expect(err).to.not.exist;
expect(key).to.exist;
expect(key._id).to.exist;
expect(key.publicKey).to.exist;
expect(restDaoStub.get.calledOnce).to.be.true;
done();
});
});
});
describe('put', function() {
it('should fail', function(done) {
restDaoStub.put.yields();
pubkeyDao.put({
_id: '12345',
publicKey: 'asdf'
}, function(err) {
expect(err).to.not.exist;
expect(restDaoStub.put.calledOnce).to.be.true;
done();
});
});
});
describe('remove', function() {
it('should fail', function(done) {
restDaoStub.remove.yields();
pubkeyDao.remove('12345', function(err) {
expect(err).to.not.exist;
expect(restDaoStub.remove.calledOnce).to.be.true;
done();
});
});
}); });
}); });

View File

@ -1,197 +1,193 @@
define(function(require) { 'use strict';
'use strict';
var expect = chai.expect, var mocks = angular.mocks,
angular = require('angular'), KeychainDAO = require('../../src/js/dao/keychain-dao'),
mocks = require('angularMocks'), InvitationDAO = require('../../src/js/dao/invitation-dao'),
KeychainDAO = require('js/dao/keychain-dao'), PGP = require('../../src/js/crypto/pgp'),
InvitationDAO = require('js/dao/invitation-dao'), ReadCtrl = require('../../src/js/controller/read'),
PGP = require('js/crypto/pgp'), OutboxBO = require('../../src/js/bo/outbox'),
ReadCtrl = require('js/controller/read'), appController = require('../../src/js/app-controller');
OutboxBO = require('js/bo/outbox'),
appController = require('js/app-controller');
describe('Read Controller unit test', function() { describe('Read Controller unit test', function() {
var scope, ctrl, var scope, ctrl,
origKeychain, keychainMock, origKeychain, keychainMock,
origInvitation, invitationMock, origInvitation, invitationMock,
origCrypto, cryptoMock, origCrypto, cryptoMock,
origOutbox, outboxMock, origOutbox, outboxMock,
origEmailDao; origEmailDao;
beforeEach(function() { beforeEach(function() {
origKeychain = appController._keychain; origKeychain = appController._keychain;
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO); appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
origInvitation = appController._invitationDao; origInvitation = appController._invitationDao;
appController._invitationDao = invitationMock = sinon.createStubInstance(InvitationDAO); appController._invitationDao = invitationMock = sinon.createStubInstance(InvitationDAO);
origCrypto = appController._pgp; origCrypto = appController._pgp;
appController._pgp = cryptoMock = sinon.createStubInstance(PGP); appController._pgp = cryptoMock = sinon.createStubInstance(PGP);
origOutbox = appController._outboxBo; origOutbox = appController._outboxBo;
appController._outboxBo = outboxMock = sinon.createStubInstance(OutboxBO); appController._outboxBo = outboxMock = sinon.createStubInstance(OutboxBO);
origEmailDao = appController._emailDao; origEmailDao = appController._emailDao;
appController._emailDao = { appController._emailDao = {
_account: 'sender@example.com' _account: 'sender@example.com'
};
angular.module('readtest', []);
mocks.module('readtest');
mocks.inject(function($rootScope, $controller) {
scope = $rootScope.$new();
scope.state = {};
ctrl = $controller(ReadCtrl, {
$scope: scope
});
});
});
afterEach(function() {
appController._keychain = origKeychain;
appController._invitationDao = origInvitation;
appController._pgp = origCrypto;
appController._outboxBo = origOutbox;
appController._emailDao = origEmailDao;
});
describe('scope variables', function() {
it('should be set correctly', function() {
expect(scope.state.read).to.exist;
expect(scope.state.read.open).to.be.false;
expect(scope.state.read.toggle).to.exist;
});
});
describe('open/close read view', function() {
it('should open/close', function() {
expect(scope.state.read.open).to.be.false;
scope.state.read.toggle(true);
expect(scope.state.read.open).to.be.true;
scope.state.read.toggle(false);
expect(scope.state.read.open).to.be.false;
});
});
describe('getKeyId', function() {
var address = 'asfd@asdf.com';
it('should show searching on error', function() {
expect(scope.keyId).to.equal('No key found.');
keychainMock.getReceiverPublicKey.yields(42);
scope.onError = function(err) {
expect(err).to.equal(42);
expect(scope.keyId).to.equal('Searching...');
}; };
angular.module('readtest', []); scope.getKeyId(address);
mocks.module('readtest');
mocks.inject(function($rootScope, $controller) {
scope = $rootScope.$new();
scope.state = {};
ctrl = $controller(ReadCtrl, {
$scope: scope
});
});
}); });
afterEach(function() { it('should allow invitation on empty key', function() {
appController._keychain = origKeychain; keychainMock.getReceiverPublicKey.yields();
appController._invitationDao = origInvitation;
appController._pgp = origCrypto; scope.onError = function(err) {
appController._outboxBo = origOutbox; expect(err).not.exist;
appController._emailDao = origEmailDao; expect(scope.keyId).to.equal('User has no key. Click to invite.');
};
scope.getKeyId(address);
}); });
describe('scope variables', function() { it('should show searching on error', function() {
it('should be set correctly', function() { keychainMock.getReceiverPublicKey.yields(null, {
expect(scope.state.read).to.exist; publicKey: 'PUBLIC KEY'
expect(scope.state.read.open).to.be.false;
expect(scope.state.read.toggle).to.exist;
}); });
cryptoMock.getFingerprint.returns('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
scope.onError = function(err) {
expect(err).to.not.exist;
expect(scope.keyId).to.equal('PGP key: XXXXXXXX');
};
scope.getKeyId(address);
}); });
describe('open/close read view', function() {
it('should open/close', function() {
expect(scope.state.read.open).to.be.false;
scope.state.read.toggle(true);
expect(scope.state.read.open).to.be.true;
scope.state.read.toggle(false);
expect(scope.state.read.open).to.be.false;
});
});
describe('getKeyId', function() {
var address = 'asfd@asdf.com';
it('should show searching on error', function() {
expect(scope.keyId).to.equal('No key found.');
keychainMock.getReceiverPublicKey.yields(42);
scope.onError = function(err) {
expect(err).to.equal(42);
expect(scope.keyId).to.equal('Searching...');
};
scope.getKeyId(address);
});
it('should allow invitation on empty key', function() {
keychainMock.getReceiverPublicKey.yields();
scope.onError = function(err) {
expect(err).not.exist;
expect(scope.keyId).to.equal('User has no key. Click to invite.');
};
scope.getKeyId(address);
});
it('should show searching on error', function() {
keychainMock.getReceiverPublicKey.yields(null, {
publicKey: 'PUBLIC KEY'
});
cryptoMock.getFingerprint.returns('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
scope.onError = function(err) {
expect(err).to.not.exist;
expect(scope.keyId).to.equal('PGP key: XXXXXXXX');
};
scope.getKeyId(address);
});
});
describe('invite', function() {
it('not allow invitation for secure users', function() {
expect(scope.keyId).to.equal('No key found.');
scope.invite({
secure: true,
address: 'asdf@asdf.de'
});
expect(scope.keyId).to.equal('No key found.');
});
it('should show error on invitation dao invite error', function() {
invitationMock.invite.yields(42);
scope.onError = function(err) {
expect(err).to.equal(42);
};
scope.invite({
address: 'asdf@asdf.de'
});
});
it('should show error on outbox put error', function() {
invitationMock.invite.yields();
outboxMock.put.yields(42);
scope.onError = function(err) {
expect(err).to.equal(42);
};
scope.invite({
address: 'asdf@asdf.de'
});
});
it('should work', function() {
invitationMock.invite.yields();
outboxMock.put.yields();
scope.onError = function(err) {
expect(err).to.not.exist;
};
scope.invite({
address: 'asdf@asdf.de'
});
});
});
describe('parseConversation', function() {
it.skip('should work', function() {
var body = 'foo\n' +
'\n' +
'> bar\n' +
'>\n' +
'> foofoo\n' +
'>> foofoobar\n' +
'\ncomment\n' +
'>> barbar';
var nodes = scope.parseConversation({
body: body
});
expect(nodes).to.exist;
var expectedJson = '{"children":["foo\\n",{"children":["bar\\n\\nfoofoo",{"children":["foofoobar"]}]},"\\ncomment",{"children":[{"children":["barbar"]}]}]}';
var json = JSON.stringify(nodes);
expect(json).to.equal(expectedJson);
var expectedHtml = '<div class="view-read-body"><div class="line"><span>foo</span><br></div><div class="line empty-line"><span></span><br></div><div class="prev-message"><div class="line"><span>bar</span><br></div><div class="line empty-line"><span></span><br></div><div class="line"><span>foofoo</span><br></div></div><div class="prev-message"><div class="prev-message"><div class="line"><span>foofoobar</span><br></div></div></div><div class="line empty-line"><span></span><br></div><div class="line"><span>comment</span><br></div><div class="prev-message"><div class="prev-message"><div class="line"><span>barbar</span><br></div></div></div></div>';
var html = scope.renderNodes(nodes);
expect(html).to.equal(expectedHtml);
});
});
}); });
describe('invite', function() {
it('not allow invitation for secure users', function() {
expect(scope.keyId).to.equal('No key found.');
scope.invite({
secure: true,
address: 'asdf@asdf.de'
});
expect(scope.keyId).to.equal('No key found.');
});
it('should show error on invitation dao invite error', function() {
invitationMock.invite.yields(42);
scope.onError = function(err) {
expect(err).to.equal(42);
};
scope.invite({
address: 'asdf@asdf.de'
});
});
it('should show error on outbox put error', function() {
invitationMock.invite.yields();
outboxMock.put.yields(42);
scope.onError = function(err) {
expect(err).to.equal(42);
};
scope.invite({
address: 'asdf@asdf.de'
});
});
it('should work', function() {
invitationMock.invite.yields();
outboxMock.put.yields();
scope.onError = function(err) {
expect(err).to.not.exist;
};
scope.invite({
address: 'asdf@asdf.de'
});
});
});
describe('parseConversation', function() {
it.skip('should work', function() {
var body = 'foo\n' +
'\n' +
'> bar\n' +
'>\n' +
'> foofoo\n' +
'>> foofoobar\n' +
'\ncomment\n' +
'>> barbar';
var nodes = scope.parseConversation({
body: body
});
expect(nodes).to.exist;
var expectedJson = '{"children":["foo\\n",{"children":["bar\\n\\nfoofoo",{"children":["foofoobar"]}]},"\\ncomment",{"children":[{"children":["barbar"]}]}]}';
var json = JSON.stringify(nodes);
expect(json).to.equal(expectedJson);
var expectedHtml = '<div class="view-read-body"><div class="line"><span>foo</span><br></div><div class="line empty-line"><span></span><br></div><div class="prev-message"><div class="line"><span>bar</span><br></div><div class="line empty-line"><span></span><br></div><div class="line"><span>foofoo</span><br></div></div><div class="prev-message"><div class="prev-message"><div class="line"><span>foofoobar</span><br></div></div></div><div class="line empty-line"><span></span><br></div><div class="line"><span>comment</span><br></div><div class="prev-message"><div class="prev-message"><div class="line"><span>barbar</span><br></div></div></div></div>';
var html = scope.renderNodes(nodes);
expect(html).to.equal(expectedHtml);
});
});
}); });

View File

@ -1,225 +1,221 @@
define(function(require) { 'use strict';
'use strict';
var RestDAO = require('js/dao/rest-dao'), var RestDAO = require('../../src/js/dao/rest-dao');
expect = chai.expect;
describe('Rest DAO unit tests', function() { describe('Rest DAO unit tests', function() {
var restDao, xhrMock, requests; var restDao, xhrMock, requests;
beforeEach(function() { beforeEach(function() {
restDao = new RestDAO();
xhrMock = sinon.useFakeXMLHttpRequest();
requests = [];
xhrMock.onCreate = function(xhr) {
requests.push(xhr);
};
});
afterEach(function() {
xhrMock.restore();
});
describe('contructor', function() {
it('should set default base uri', function() {
restDao = new RestDAO(); restDao = new RestDAO();
xhrMock = sinon.useFakeXMLHttpRequest(); expect(restDao).to.exist;
requests = []; expect(restDao._baseUri).to.exist;
xhrMock.onCreate = function(xhr) {
requests.push(xhr);
};
}); });
afterEach(function() { it('should accept default base uri', function() {
xhrMock.restore(); var baseUri = 'http://custom.com';
});
describe('contructor', function() { restDao = new RestDAO(baseUri);
it('should set default base uri', function() { expect(restDao).to.exist;
restDao = new RestDAO(); expect(restDao._baseUri).to.equal(baseUri);
expect(restDao).to.exist; });
expect(restDao._baseUri).to.exist; });
describe('get', function() {
it('should work with json as default type', function() {
restDao.get({
uri: '/asdf'
}, function(err, data, status) {
expect(err).to.not.exist;
expect(data.foo).to.equal('bar');
var req = requests[0];
expect(req.requestHeaders.Accept).to.equal('application/json');
expect(status).to.equal(200);
}); });
it('should accept default base uri', function() { expect(requests.length).to.equal(1);
var baseUri = 'http://custom.com'; requests[0].respond(200, {
"Content-Type": "application/json"
}, '{"foo": "bar"}');
});
restDao = new RestDAO(baseUri); it('should work with jsonz', function() {
expect(restDao).to.exist; restDao.get({
expect(restDao._baseUri).to.equal(baseUri); uri: '/asdf',
type: 'json'
}, function(err, data, status) {
expect(err).to.not.exist;
expect(data.foo).to.equal('bar');
var req = requests[0];
expect(req.requestHeaders.Accept).to.equal('application/json');
expect(status).to.equal(200);
});
expect(requests.length).to.equal(1);
requests[0].respond(200, {
"Content-Type": "application/json"
}, '{"foo": "bar"}');
});
it('should work with plain text', function() {
restDao.get({
uri: '/asdf',
type: 'text'
}, function(err, data, status) {
expect(err).to.not.exist;
expect(data).to.equal('foobar!');
var req = requests[0];
expect(req.requestHeaders.Accept).to.equal('text/plain');
expect(status).to.equal(200);
});
expect(requests.length).to.equal(1);
requests[0].respond(200, {
"Content-Type": "text/plain"
}, 'foobar!');
});
it('should work with xml', function() {
restDao.get({
uri: '/asdf',
type: 'xml'
}, function(err, data, status) {
expect(err).to.not.exist;
expect(data).to.equal('<foo>bar</foo>');
var req = requests[0];
expect(req.requestHeaders.Accept).to.equal('application/xml');
expect(status).to.equal(200);
});
expect(requests.length).to.equal(1);
requests[0].respond(200, {
"Content-Type": "application/xml"
}, '<foo>bar</foo>');
});
it('should fail for missing uri parameter', function() {
restDao.get({}, function(err, data) {
expect(err).to.exist;
expect(err.code).to.equal(400);
expect(data).to.not.exist;
}); });
}); });
describe('get', function() { it('should fail for unhandled data type', function() {
it('should work with json as default type', function() { restDao.get({
restDao.get({ uri: '/asdf',
uri: '/asdf' type: 'snafu'
}, function(err, data, status) { }, function(err, data) {
expect(err).to.not.exist; expect(err).to.exist;
expect(data.foo).to.equal('bar'); expect(err.code).to.equal(400);
var req = requests[0]; expect(data).to.not.exist;
expect(req.requestHeaders.Accept).to.equal('application/json');
expect(status).to.equal(200);
});
expect(requests.length).to.equal(1);
requests[0].respond(200, {
"Content-Type": "application/json"
}, '{"foo": "bar"}');
});
it('should work with jsonz', function() {
restDao.get({
uri: '/asdf',
type: 'json'
}, function(err, data, status) {
expect(err).to.not.exist;
expect(data.foo).to.equal('bar');
var req = requests[0];
expect(req.requestHeaders.Accept).to.equal('application/json');
expect(status).to.equal(200);
});
expect(requests.length).to.equal(1);
requests[0].respond(200, {
"Content-Type": "application/json"
}, '{"foo": "bar"}');
});
it('should work with plain text', function() {
restDao.get({
uri: '/asdf',
type: 'text'
}, function(err, data, status) {
expect(err).to.not.exist;
expect(data).to.equal('foobar!');
var req = requests[0];
expect(req.requestHeaders.Accept).to.equal('text/plain');
expect(status).to.equal(200);
});
expect(requests.length).to.equal(1);
requests[0].respond(200, {
"Content-Type": "text/plain"
}, 'foobar!');
});
it('should work with xml', function() {
restDao.get({
uri: '/asdf',
type: 'xml'
}, function(err, data, status) {
expect(err).to.not.exist;
expect(data).to.equal('<foo>bar</foo>');
var req = requests[0];
expect(req.requestHeaders.Accept).to.equal('application/xml');
expect(status).to.equal(200);
});
expect(requests.length).to.equal(1);
requests[0].respond(200, {
"Content-Type": "application/xml"
}, '<foo>bar</foo>');
});
it('should fail for missing uri parameter', function() {
restDao.get({}, function(err, data) {
expect(err).to.exist;
expect(err.code).to.equal(400);
expect(data).to.not.exist;
});
});
it('should fail for unhandled data type', function() {
restDao.get({
uri: '/asdf',
type: 'snafu'
}, function(err, data) {
expect(err).to.exist;
expect(err.code).to.equal(400);
expect(data).to.not.exist;
});
});
it('should fail for server error', function() {
restDao.get({
uri: '/asdf'
}, function(err, data) {
expect(err).to.exist;
expect(err.code).to.equal(500);
expect(data).to.not.exist;
});
expect(requests.length).to.equal(1);
requests[0].respond(500, {
"Content-Type": "text/plain"
}, 'Internal error');
}); });
}); });
describe('post', function() { it('should fail for server error', function() {
it('should fail', function() { restDao.get({
restDao.post('/asdf', {}, function(err) { uri: '/asdf'
expect(err).to.exist; }, function(err, data) {
expect(err.code).to.equal(500); expect(err).to.exist;
}); expect(err.code).to.equal(500);
expect(data).to.not.exist;
expect(requests.length).to.equal(1);
requests[0].respond(500, {
"Content-Type": "text/plain"
}, 'Internal error');
}); });
it('should work', function() { expect(requests.length).to.equal(1);
restDao.post('/asdf', {}, function(err, res, status) { requests[0].respond(500, {
expect(err).to.not.exist; "Content-Type": "text/plain"
expect(res).to.equal(''); }, 'Internal error');
expect(status).to.equal(201); });
}); });
expect(requests.length).to.equal(1); describe('post', function() {
requests[0].respond(201); it('should fail', function() {
restDao.post('/asdf', {}, function(err) {
expect(err).to.exist;
expect(err.code).to.equal(500);
}); });
expect(requests.length).to.equal(1);
requests[0].respond(500, {
"Content-Type": "text/plain"
}, 'Internal error');
}); });
describe('put', function() { it('should work', function() {
it('should fail', function() { restDao.post('/asdf', {}, function(err, res, status) {
restDao.put('/asdf', {}, function(err) { expect(err).to.not.exist;
expect(err).to.exist; expect(res).to.equal('');
expect(err.code).to.equal(500); expect(status).to.equal(201);
});
expect(requests.length).to.equal(1);
requests[0].respond(500, {
"Content-Type": "text/plain"
}, 'Internal error');
}); });
it('should work', function() { expect(requests.length).to.equal(1);
restDao.put('/asdf', {}, function(err, res, status) { requests[0].respond(201);
expect(err).to.not.exist; });
expect(res).to.equal(''); });
expect(status).to.equal(201);
});
expect(requests.length).to.equal(1); describe('put', function() {
requests[0].respond(201); it('should fail', function() {
restDao.put('/asdf', {}, function(err) {
expect(err).to.exist;
expect(err.code).to.equal(500);
}); });
expect(requests.length).to.equal(1);
requests[0].respond(500, {
"Content-Type": "text/plain"
}, 'Internal error');
}); });
describe('remove', function() { it('should work', function() {
it('should fail', function() { restDao.put('/asdf', {}, function(err, res, status) {
restDao.remove('/asdf', function(err) { expect(err).to.not.exist;
expect(err).to.exist; expect(res).to.equal('');
expect(err.code).to.equal(500); expect(status).to.equal(201);
});
expect(requests.length).to.equal(1);
requests[0].respond(500, {
"Content-Type": "text/plain"
}, 'Internal error');
}); });
it('should work', function() { expect(requests.length).to.equal(1);
restDao.remove('/asdf', function(err, res, status) { requests[0].respond(201);
expect(err).to.not.exist; });
expect(res).to.equal(''); });
expect(status).to.equal(200);
});
expect(requests.length).to.equal(1); describe('remove', function() {
requests[0].respond(200); it('should fail', function() {
restDao.remove('/asdf', function(err) {
expect(err).to.exist;
expect(err.code).to.equal(500);
}); });
expect(requests.length).to.equal(1);
requests[0].respond(500, {
"Content-Type": "text/plain"
}, 'Internal error');
}); });
it('should work', function() {
restDao.remove('/asdf', function(err, res, status) {
expect(err).to.not.exist;
expect(res).to.equal('');
expect(status).to.equal(200);
});
expect(requests.length).to.equal(1);
requests[0].respond(200);
});
}); });
}); });

View File

@ -1,126 +1,122 @@
define(function(require) { 'use strict';
'use strict';
var expect = chai.expect, var mocks = angular.mocks,
angular = require('angular'), SetPassphraseCtrl = require('../../src/js/controller/set-passphrase'),
mocks = require('angularMocks'), PGP = require('../../src/js/crypto/pgp'),
SetPassphraseCtrl = require('js/controller/set-passphrase'), appController = require('../../src/js/app-controller'),
PGP = require('js/crypto/pgp'), KeychainDAO = require('../../src/js/dao/keychain-dao');
appController = require('js/app-controller'),
KeychainDAO = require('js/dao/keychain-dao');
describe('Set Passphrase Controller unit test', function() { describe('Set Passphrase Controller unit test', function() {
var scope, setPassphraseCtrl, var scope, setPassphraseCtrl,
dummyFingerprint, expectedFingerprint, dummyFingerprint, expectedFingerprint,
dummyKeyId, expectedKeyId, dummyKeyId, expectedKeyId,
emailAddress, keySize, cryptoMock, keychainMock; emailAddress, keySize, cryptoMock, keychainMock;
beforeEach(function() { beforeEach(function() {
appController._pgp = cryptoMock = sinon.createStubInstance(PGP); appController._pgp = cryptoMock = sinon.createStubInstance(PGP);
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO); appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
dummyFingerprint = '3A2D39B4E1404190B8B949DE7D7E99036E712926'; dummyFingerprint = '3A2D39B4E1404190B8B949DE7D7E99036E712926';
expectedFingerprint = '3A2D 39B4 E140 4190 B8B9 49DE 7D7E 9903 6E71 2926'; expectedFingerprint = '3A2D 39B4 E140 4190 B8B9 49DE 7D7E 9903 6E71 2926';
dummyKeyId = '9FEB47936E712926'; dummyKeyId = '9FEB47936E712926';
expectedKeyId = '6E712926'; expectedKeyId = '6E712926';
cryptoMock.getFingerprint.returns(dummyFingerprint); cryptoMock.getFingerprint.returns(dummyFingerprint);
cryptoMock.getKeyId.returns(dummyKeyId); cryptoMock.getKeyId.returns(dummyKeyId);
emailAddress = 'fred@foo.com'; emailAddress = 'fred@foo.com';
keySize = 1234; keySize = 1234;
cryptoMock.getKeyParams.returns({ cryptoMock.getKeyParams.returns({
_id: dummyKeyId,
fingerprint: dummyFingerprint,
userId: emailAddress,
userIds: [],
bitSize: keySize
});
angular.module('setpassphrasetest', []);
mocks.module('setpassphrasetest');
mocks.inject(function($rootScope, $controller) {
scope = $rootScope.$new();
scope.state = {};
setPassphraseCtrl = $controller(SetPassphraseCtrl, {
$scope: scope
});
});
});
afterEach(function() {});
describe('setPassphrase', function() {
it('should work', function(done) {
scope.oldPassphrase = 'old';
scope.newPassphrase = 'new';
keychainMock.lookupPrivateKey.withArgs(dummyKeyId).yields(null, {
encryptedKey: 'encrypted'
});
cryptoMock.changePassphrase.withArgs({
privateKeyArmored: 'encrypted',
oldPassphrase: 'old',
newPassphrase: 'new'
}).yields(null, 'newArmoredKey');
keychainMock.saveLocalPrivateKey.withArgs({
_id: dummyKeyId, _id: dummyKeyId,
fingerprint: dummyFingerprint,
userId: emailAddress, userId: emailAddress,
userIds: [], userIds: [],
bitSize: keySize encryptedKey: 'newArmoredKey'
}); }).yields();
angular.module('setpassphrasetest', []); scope.onError = function(err) {
mocks.module('setpassphrasetest'); expect(err.title).to.equal('Success');
mocks.inject(function($rootScope, $controller) { done();
scope = $rootScope.$new(); };
scope.state = {};
setPassphraseCtrl = $controller(SetPassphraseCtrl, { scope.setPassphrase();
$scope: scope
});
});
}); });
afterEach(function() {});
describe('setPassphrase', function() {
it('should work', function(done) {
scope.oldPassphrase = 'old';
scope.newPassphrase = 'new';
keychainMock.lookupPrivateKey.withArgs(dummyKeyId).yields(null, {
encryptedKey: 'encrypted'
});
cryptoMock.changePassphrase.withArgs({
privateKeyArmored: 'encrypted',
oldPassphrase: 'old',
newPassphrase: 'new'
}).yields(null, 'newArmoredKey');
keychainMock.saveLocalPrivateKey.withArgs({
_id: dummyKeyId,
userId: emailAddress,
userIds: [],
encryptedKey: 'newArmoredKey'
}).yields();
scope.onError = function(err) {
expect(err.title).to.equal('Success');
done();
};
scope.setPassphrase();
});
});
describe('check passphrase quality', function() {
it('should be too short', function() {
scope.newPassphrase = '&§DG36';
scope.checkPassphraseQuality();
expect(scope.passphraseMsg).to.equal('Very weak');
expect(scope.passphraseRating).to.equal(0);
});
it('should be very weak', function() {
scope.newPassphrase = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
scope.checkPassphraseQuality();
expect(scope.passphraseMsg).to.equal('Very weak');
expect(scope.passphraseRating).to.equal(0);
});
it('should be weak', function() {
scope.newPassphrase = 'asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf';
scope.checkPassphraseQuality();
expect(scope.passphraseMsg).to.equal('Weak');
expect(scope.passphraseRating).to.equal(1);
});
it('should be good', function() {
scope.newPassphrase = 'asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf5';
scope.checkPassphraseQuality();
expect(scope.passphraseMsg).to.equal('Good');
expect(scope.passphraseRating).to.equal(2);
});
it('should be strong', function() {
scope.newPassphrase = '&§DG36abcd';
scope.checkPassphraseQuality();
expect(scope.passphraseMsg).to.equal('Strong');
expect(scope.passphraseRating).to.equal(3);
});
});
}); });
describe('check passphrase quality', function() {
it('should be too short', function() {
scope.newPassphrase = '&§DG36';
scope.checkPassphraseQuality();
expect(scope.passphraseMsg).to.equal('Very weak');
expect(scope.passphraseRating).to.equal(0);
});
it('should be very weak', function() {
scope.newPassphrase = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
scope.checkPassphraseQuality();
expect(scope.passphraseMsg).to.equal('Very weak');
expect(scope.passphraseRating).to.equal(0);
});
it('should be weak', function() {
scope.newPassphrase = 'asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf';
scope.checkPassphraseQuality();
expect(scope.passphraseMsg).to.equal('Weak');
expect(scope.passphraseRating).to.equal(1);
});
it('should be good', function() {
scope.newPassphrase = 'asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf5';
scope.checkPassphraseQuality();
expect(scope.passphraseMsg).to.equal('Good');
expect(scope.passphraseRating).to.equal(2);
});
it('should be strong', function() {
scope.newPassphrase = '&§DG36abcd';
scope.checkPassphraseQuality();
expect(scope.passphraseMsg).to.equal('Strong');
expect(scope.passphraseRating).to.equal(3);
});
});
}); });

View File

@ -1,472 +1,469 @@
define(function(require) { 'use strict';
'use strict';
var DeviceStorageDAO = require('js/dao/devicestorage-dao'), var DeviceStorageDAO = require('../../src/js/dao/devicestorage-dao'),
Auth = require('js/bo/auth'), Auth = require('../../src/js/bo/auth'),
cfg = require('js/app-config').config, cfg = require('../../src/js/app-config').config,
UpdateHandler = require('js/util/update/update-handler'), UpdateHandler = require('../../src/js/util/update/update-handler'),
config = require('js/app-config').config, config = require('../../src/js/app-config').config;
expect = chai.expect;
describe('UpdateHandler', function() { describe('UpdateHandler', function() {
var updateHandler, appConfigStorageStub, authStub, userStorageStub, origDbVersion; var updateHandler, appConfigStorageStub, authStub, userStorageStub, origDbVersion;
chai.Assertion.includeStack = true; chai.Assertion.includeStack = true;
beforeEach(function() { beforeEach(function() {
origDbVersion = cfg.dbVersion; origDbVersion = cfg.dbVersion;
appConfigStorageStub = sinon.createStubInstance(DeviceStorageDAO); appConfigStorageStub = sinon.createStubInstance(DeviceStorageDAO);
userStorageStub = sinon.createStubInstance(DeviceStorageDAO); userStorageStub = sinon.createStubInstance(DeviceStorageDAO);
authStub = sinon.createStubInstance(Auth); authStub = sinon.createStubInstance(Auth);
updateHandler = new UpdateHandler(appConfigStorageStub, userStorageStub, authStub); updateHandler = new UpdateHandler(appConfigStorageStub, userStorageStub, authStub);
});
afterEach(function() {
cfg.dbVersion = origDbVersion;
});
describe('#constructor', function() {
it('should create instance', function() {
expect(updateHandler).to.exist;
expect(updateHandler._appConfigStorage).to.equal(appConfigStorageStub);
expect(updateHandler._userStorage).to.equal(userStorageStub);
// the update handler must contain as many db update sripts as there are database versions
expect(updateHandler._updateScripts.length).to.equal(cfg.dbVersion);
}); });
});
afterEach(function() { describe('#update', function() {
cfg.dbVersion = origDbVersion; var versionDbType = 'dbVersion';
});
describe('#constructor', function() { it('should not update when up to date', function(done) {
it('should create instance', function() { cfg.dbVersion = 10; // app requires database version 10
expect(updateHandler).to.exist; appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, ['10']); // database version is 10
expect(updateHandler._appConfigStorage).to.equal(appConfigStorageStub);
expect(updateHandler._userStorage).to.equal(userStorageStub);
// the update handler must contain as many db update sripts as there are database versions updateHandler.update(function(error) {
expect(updateHandler._updateScripts.length).to.equal(cfg.dbVersion); expect(error).to.not.exist;
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
done();
}); });
}); });
describe('#update', function() { describe('dummy updates for v2 to v4', function() {
var versionDbType = 'dbVersion'; var updateCounter;
it('should not update when up to date', function(done) { beforeEach(function() {
cfg.dbVersion = 10; // app requires database version 10 updateCounter = 0;
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, ['10']); // database version is 10 appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, ['2']); // database version is 0
});
afterEach(function() {
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
});
it('should work', function(done) {
cfg.dbVersion = 4; // app requires database version 4
// a simple dummy update to executed that only increments the update counter
function dummyUpdate(options, callback) {
updateCounter++;
callback();
}
// inject the dummy updates instead of live ones
updateHandler._updateScripts = [dummyUpdate, dummyUpdate, dummyUpdate, dummyUpdate];
// execute test
updateHandler.update(function(error) { updateHandler.update(function(error) {
expect(error).to.not.exist; expect(error).to.not.exist;
expect(appConfigStorageStub.listItems.calledOnce).to.be.true; expect(updateCounter).to.equal(2);
done(); done();
}); });
}); });
describe('dummy updates for v2 to v4', function() { it('should fail while updating to v3', function(done) {
var updateCounter; cfg.dbVersion = 4; // app requires database version 4
beforeEach(function() { function dummyUpdate(options, callback) {
updateCounter = 0; updateCounter++;
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, ['2']); // database version is 0 callback();
}); }
afterEach(function() { function failingUpdate(options, callback) {
expect(appConfigStorageStub.listItems.calledOnce).to.be.true; callback({});
}); }
// inject the dummy updates instead of live ones
updateHandler._updateScripts = [dummyUpdate, dummyUpdate, failingUpdate, dummyUpdate];
it('should work', function(done) { // execute test
cfg.dbVersion = 4; // app requires database version 4 updateHandler.update(function(error) {
expect(error).to.exist;
expect(updateCounter).to.equal(0);
// a simple dummy update to executed that only increments the update counter done();
function dummyUpdate(options, callback) {
updateCounter++;
callback();
}
// inject the dummy updates instead of live ones
updateHandler._updateScripts = [dummyUpdate, dummyUpdate, dummyUpdate, dummyUpdate];
// execute test
updateHandler.update(function(error) {
expect(error).to.not.exist;
expect(updateCounter).to.equal(2);
done();
});
});
it('should fail while updating to v3', function(done) {
cfg.dbVersion = 4; // app requires database version 4
function dummyUpdate(options, callback) {
updateCounter++;
callback();
}
function failingUpdate(options, callback) {
callback({});
}
// inject the dummy updates instead of live ones
updateHandler._updateScripts = [dummyUpdate, dummyUpdate, failingUpdate, dummyUpdate];
// execute test
updateHandler.update(function(error) {
expect(error).to.exist;
expect(updateCounter).to.equal(0);
done();
});
});
});
describe('v0 -> v1', function() {
var emailDbType = 'email_';
beforeEach(function() {
cfg.dbVersion = 1; // app requires database version 1
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(); // database version is 0
});
afterEach(function() {
// database version is only queried for version checking prior to the update script
// so no need to check this in case-specific tests
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
});
it('should work', function(done) {
userStorageStub.removeList.withArgs(emailDbType).yieldsAsync();
appConfigStorageStub.storeList.withArgs([1], versionDbType).yieldsAsync();
updateHandler.update(function(error) {
expect(error).to.not.exist;
expect(userStorageStub.removeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
done();
});
});
it('should fail when persisting database version fails', function(done) {
userStorageStub.removeList.yieldsAsync();
appConfigStorageStub.storeList.yieldsAsync(new Error());
updateHandler.update(function(error) {
expect(error).to.exist;
expect(userStorageStub.removeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
done();
});
});
it('should fail when wiping emails from database fails', function(done) {
userStorageStub.removeList.yieldsAsync(new Error());
updateHandler.update(function(error) {
expect(error).to.exist;
expect(userStorageStub.removeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.called).to.be.false;
done();
});
}); });
}); });
describe('v1 -> v2', function() { });
var emailDbType = 'email_';
beforeEach(function() { describe('v0 -> v1', function() {
cfg.dbVersion = 2; // app requires database version 2 var emailDbType = 'email_';
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, [1]); // database version is 0
});
afterEach(function() { beforeEach(function() {
// database version is only queried for version checking prior to the update script cfg.dbVersion = 1; // app requires database version 1
// so no need to check this in case-specific tests appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(); // database version is 0
expect(appConfigStorageStub.listItems.calledOnce).to.be.true; });
});
it('should work', function(done) { afterEach(function() {
userStorageStub.removeList.withArgs(emailDbType).yieldsAsync(); // database version is only queried for version checking prior to the update script
appConfigStorageStub.storeList.withArgs([2], versionDbType).yieldsAsync(); // so no need to check this in case-specific tests
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
});
updateHandler.update(function(error) { it('should work', function(done) {
expect(error).to.not.exist; userStorageStub.removeList.withArgs(emailDbType).yieldsAsync();
expect(userStorageStub.removeList.calledOnce).to.be.true; appConfigStorageStub.storeList.withArgs([1], versionDbType).yieldsAsync();
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
done(); updateHandler.update(function(error) {
}); expect(error).to.not.exist;
}); expect(userStorageStub.removeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
it('should fail when persisting database version fails', function(done) { done();
userStorageStub.removeList.yieldsAsync();
appConfigStorageStub.storeList.yieldsAsync(new Error());
updateHandler.update(function(error) {
expect(error).to.exist;
expect(userStorageStub.removeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
done();
});
});
it('should fail when wiping emails from database fails', function(done) {
userStorageStub.removeList.yieldsAsync(new Error());
updateHandler.update(function(error) {
expect(error).to.exist;
expect(userStorageStub.removeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.called).to.be.false;
done();
});
}); });
}); });
describe('v2 -> v3', function() { it('should fail when persisting database version fails', function(done) {
var emailDbType = 'email_'; userStorageStub.removeList.yieldsAsync();
appConfigStorageStub.storeList.yieldsAsync(new Error());
beforeEach(function() { updateHandler.update(function(error) {
cfg.dbVersion = 3; // app requires database version 2 expect(error).to.exist;
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, [2]); // database version is 0 expect(userStorageStub.removeList.calledOnce).to.be.true;
}); expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
afterEach(function() { done();
// database version is only queried for version checking prior to the update script
// so no need to check this in case-specific tests
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
});
it('should work', function(done) {
userStorageStub.removeList.withArgs(emailDbType).yieldsAsync();
appConfigStorageStub.storeList.withArgs([3], versionDbType).yieldsAsync();
updateHandler.update(function(error) {
expect(error).to.not.exist;
expect(userStorageStub.removeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
done();
});
});
it('should fail when persisting database version fails', function(done) {
userStorageStub.removeList.yieldsAsync();
appConfigStorageStub.storeList.yieldsAsync(new Error());
updateHandler.update(function(error) {
expect(error).to.exist;
expect(userStorageStub.removeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
done();
});
});
it('should fail when wiping emails from database fails', function(done) {
userStorageStub.removeList.yieldsAsync(new Error());
updateHandler.update(function(error) {
expect(error).to.exist;
expect(userStorageStub.removeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.called).to.be.false;
done();
});
});
});
describe('v3 -> v4', function() {
var EMAIL_ADDR_DB_KEY = 'emailaddress';
var USERNAME_DB_KEY = 'username';
var PROVIDER_DB_KEY = 'provider';
var IMAP_DB_KEY = 'imap';
var SMTP_DB_KEY = 'smtp';
var REALNAME_DB_KEY = 'realname';
var emailaddress = 'bla@blubb.io';
var imap = config.gmail.imap,
smtp = config.gmail.smtp;
beforeEach(function() {
cfg.dbVersion = 4; // app requires database version 4
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, [3]); // database version is 3
});
it('should add gmail as mail service provider with email address and no provider present in db', function(done) {
appConfigStorageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY).yieldsAsync(null, [emailaddress]);
appConfigStorageStub.listItems.withArgs(PROVIDER_DB_KEY).yieldsAsync(null, []);
appConfigStorageStub.storeList.withArgs([4], versionDbType).yieldsAsync();
appConfigStorageStub.storeList.withArgs(['gmail'], PROVIDER_DB_KEY).yieldsAsync();
appConfigStorageStub.storeList.withArgs([emailaddress], USERNAME_DB_KEY).yieldsAsync();
appConfigStorageStub.storeList.withArgs([imap], IMAP_DB_KEY).yieldsAsync();
appConfigStorageStub.storeList.withArgs([smtp], SMTP_DB_KEY).yieldsAsync();
appConfigStorageStub.storeList.withArgs([''], REALNAME_DB_KEY).yieldsAsync();
authStub._loadCredentials.yieldsAsync();
updateHandler.update(function(error) {
expect(error).to.not.exist;
expect(appConfigStorageStub.storeList.callCount).to.equal(6);
expect(appConfigStorageStub.listItems.calledThrice).to.be.true;
expect(authStub._loadCredentials.calledOnce).to.be.true;
done();
});
});
it('should not add a provider when no email adress is in db', function(done) {
appConfigStorageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY).yieldsAsync(null, []);
appConfigStorageStub.listItems.withArgs(PROVIDER_DB_KEY).yieldsAsync(null, []);
appConfigStorageStub.storeList.withArgs([4], versionDbType).yieldsAsync();
updateHandler.update(function(error) {
expect(error).to.not.exist;
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
expect(appConfigStorageStub.listItems.calledThrice).to.be.true;
done();
});
});
it('should fail when appConfigStore write fails', function(done) {
appConfigStorageStub.listItems.yieldsAsync(null, []);
appConfigStorageStub.storeList.yieldsAsync(new Error());
updateHandler.update(function(error) {
expect(error).to.exist;
expect(appConfigStorageStub.listItems.calledThrice).to.be.true;
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
done();
});
});
it('should fail when appConfigStore read fails', function(done) {
appConfigStorageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY).yieldsAsync(new Error());
appConfigStorageStub.storeList.yieldsAsync(new Error());
updateHandler.update(function(error) {
expect(error).to.exist;
expect(appConfigStorageStub.listItems.calledTwice).to.be.true;
expect(appConfigStorageStub.storeList.called).to.be.false;
done();
});
}); });
}); });
describe('v4 -> v5', function() { it('should fail when wiping emails from database fails', function(done) {
var FOLDER_TYPE_INBOX = 'Inbox'; userStorageStub.removeList.yieldsAsync(new Error());
var FOLDER_TYPE_SENT = 'Sent';
var FOLDER_TYPE_DRAFTS = 'Drafts';
var FOLDER_TYPE_TRASH = 'Trash';
var FOLDER_DB_TYPE = 'folders'; updateHandler.update(function(error) {
var VERSION_DB_TYPE = 'dbVersion'; expect(error).to.exist;
expect(userStorageStub.removeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.called).to.be.false;
var POST_UPDATE_DB_VERSION = 5; done();
beforeEach(function() {
cfg.dbVersion = 5; // app requires database version 4
appConfigStorageStub.listItems.withArgs(VERSION_DB_TYPE).yieldsAsync(null, [4]); // database version is 4
}); });
});
});
afterEach(function() { describe('v1 -> v2', function() {
// database version is only queried for version checking prior to the update script var emailDbType = 'email_';
// so no need to check this in case-specific tests
expect(appConfigStorageStub.listItems.calledOnce).to.be.true; beforeEach(function() {
cfg.dbVersion = 2; // app requires database version 2
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, [1]); // database version is 0
});
afterEach(function() {
// database version is only queried for version checking prior to the update script
// so no need to check this in case-specific tests
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
});
it('should work', function(done) {
userStorageStub.removeList.withArgs(emailDbType).yieldsAsync();
appConfigStorageStub.storeList.withArgs([2], versionDbType).yieldsAsync();
updateHandler.update(function(error) {
expect(error).to.not.exist;
expect(userStorageStub.removeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
done();
}); });
});
it('should work', function(done) { it('should fail when persisting database version fails', function(done) {
userStorageStub.listItems.withArgs(FOLDER_DB_TYPE).yieldsAsync(null, [ userStorageStub.removeList.yieldsAsync();
[{ appConfigStorageStub.storeList.yieldsAsync(new Error());
name: 'inbox1',
type: FOLDER_TYPE_INBOX
}, {
name: 'inbox2',
type: FOLDER_TYPE_INBOX
}, {
name: 'sent1',
type: FOLDER_TYPE_SENT
}, {
name: 'sent2',
type: FOLDER_TYPE_SENT
}, {
name: 'drafts1',
type: FOLDER_TYPE_DRAFTS
}, {
name: 'drafts2',
type: FOLDER_TYPE_DRAFTS
}, {
name: 'trash1',
type: FOLDER_TYPE_TRASH
}, {
name: 'trash2',
type: FOLDER_TYPE_TRASH
}]
]);
userStorageStub.storeList.withArgs([ updateHandler.update(function(error) {
[{ expect(error).to.exist;
name: 'inbox1', expect(userStorageStub.removeList.calledOnce).to.be.true;
type: FOLDER_TYPE_INBOX expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
}, {
name: 'sent1',
type: FOLDER_TYPE_SENT
}, {
name: 'drafts1',
type: FOLDER_TYPE_DRAFTS
}, {
name: 'trash1',
type: FOLDER_TYPE_TRASH
}]
], FOLDER_DB_TYPE).yieldsAsync();
appConfigStorageStub.storeList.withArgs([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE).yieldsAsync(); done();
updateHandler.update(function(error) {
expect(error).to.not.exist;
expect(userStorageStub.listItems.calledOnce).to.be.true;
expect(userStorageStub.storeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
done();
});
}); });
});
it('should fail when persisting database version fails', function(done) { it('should fail when wiping emails from database fails', function(done) {
userStorageStub.listItems.yieldsAsync(null, []); userStorageStub.removeList.yieldsAsync(new Error());
userStorageStub.storeList.yieldsAsync();
appConfigStorageStub.storeList.yieldsAsync(new Error());
updateHandler.update(function(error) { updateHandler.update(function(error) {
expect(error).to.exist; expect(error).to.exist;
expect(userStorageStub.listItems.calledOnce).to.be.true; expect(userStorageStub.removeList.calledOnce).to.be.true;
expect(userStorageStub.storeList.calledOnce).to.be.true; expect(appConfigStorageStub.storeList.called).to.be.false;
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
done(); done();
});
}); });
});
});
it('should fail when persisting folders fails', function(done) { describe('v2 -> v3', function() {
userStorageStub.listItems.yieldsAsync(null, []); var emailDbType = 'email_';
userStorageStub.storeList.yieldsAsync(new Error());
updateHandler.update(function(error) { beforeEach(function() {
expect(error).to.exist; cfg.dbVersion = 3; // app requires database version 2
expect(userStorageStub.listItems.calledOnce).to.be.true; appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, [2]); // database version is 0
expect(userStorageStub.storeList.calledOnce).to.be.true; });
expect(appConfigStorageStub.storeList.called).to.be.false;
done(); afterEach(function() {
}); // database version is only queried for version checking prior to the update script
// so no need to check this in case-specific tests
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
});
it('should work', function(done) {
userStorageStub.removeList.withArgs(emailDbType).yieldsAsync();
appConfigStorageStub.storeList.withArgs([3], versionDbType).yieldsAsync();
updateHandler.update(function(error) {
expect(error).to.not.exist;
expect(userStorageStub.removeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
done();
}); });
});
it('should fail when listing folders fails', function(done) { it('should fail when persisting database version fails', function(done) {
userStorageStub.listItems.yieldsAsync(new Error()); userStorageStub.removeList.yieldsAsync();
appConfigStorageStub.storeList.yieldsAsync(new Error());
updateHandler.update(function(error) { updateHandler.update(function(error) {
expect(error).to.exist; expect(error).to.exist;
expect(userStorageStub.listItems.calledOnce).to.be.true; expect(userStorageStub.removeList.calledOnce).to.be.true;
expect(userStorageStub.storeList.called).to.be.false; expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.called).to.be.false;
done(); done();
}); });
});
it('should fail when wiping emails from database fails', function(done) {
userStorageStub.removeList.yieldsAsync(new Error());
updateHandler.update(function(error) {
expect(error).to.exist;
expect(userStorageStub.removeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.called).to.be.false;
done();
});
});
});
describe('v3 -> v4', function() {
var EMAIL_ADDR_DB_KEY = 'emailaddress';
var USERNAME_DB_KEY = 'username';
var PROVIDER_DB_KEY = 'provider';
var IMAP_DB_KEY = 'imap';
var SMTP_DB_KEY = 'smtp';
var REALNAME_DB_KEY = 'realname';
var emailaddress = 'bla@blubb.io';
var imap = config.gmail.imap,
smtp = config.gmail.smtp;
beforeEach(function() {
cfg.dbVersion = 4; // app requires database version 4
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, [3]); // database version is 3
});
it('should add gmail as mail service provider with email address and no provider present in db', function(done) {
appConfigStorageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY).yieldsAsync(null, [emailaddress]);
appConfigStorageStub.listItems.withArgs(PROVIDER_DB_KEY).yieldsAsync(null, []);
appConfigStorageStub.storeList.withArgs([4], versionDbType).yieldsAsync();
appConfigStorageStub.storeList.withArgs(['gmail'], PROVIDER_DB_KEY).yieldsAsync();
appConfigStorageStub.storeList.withArgs([emailaddress], USERNAME_DB_KEY).yieldsAsync();
appConfigStorageStub.storeList.withArgs([imap], IMAP_DB_KEY).yieldsAsync();
appConfigStorageStub.storeList.withArgs([smtp], SMTP_DB_KEY).yieldsAsync();
appConfigStorageStub.storeList.withArgs([''], REALNAME_DB_KEY).yieldsAsync();
authStub._loadCredentials.yieldsAsync();
updateHandler.update(function(error) {
expect(error).to.not.exist;
expect(appConfigStorageStub.storeList.callCount).to.equal(6);
expect(appConfigStorageStub.listItems.calledThrice).to.be.true;
expect(authStub._loadCredentials.calledOnce).to.be.true;
done();
});
});
it('should not add a provider when no email adress is in db', function(done) {
appConfigStorageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY).yieldsAsync(null, []);
appConfigStorageStub.listItems.withArgs(PROVIDER_DB_KEY).yieldsAsync(null, []);
appConfigStorageStub.storeList.withArgs([4], versionDbType).yieldsAsync();
updateHandler.update(function(error) {
expect(error).to.not.exist;
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
expect(appConfigStorageStub.listItems.calledThrice).to.be.true;
done();
});
});
it('should fail when appConfigStore write fails', function(done) {
appConfigStorageStub.listItems.yieldsAsync(null, []);
appConfigStorageStub.storeList.yieldsAsync(new Error());
updateHandler.update(function(error) {
expect(error).to.exist;
expect(appConfigStorageStub.listItems.calledThrice).to.be.true;
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
done();
});
});
it('should fail when appConfigStore read fails', function(done) {
appConfigStorageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY).yieldsAsync(new Error());
appConfigStorageStub.storeList.yieldsAsync(new Error());
updateHandler.update(function(error) {
expect(error).to.exist;
expect(appConfigStorageStub.listItems.calledTwice).to.be.true;
expect(appConfigStorageStub.storeList.called).to.be.false;
done();
});
});
});
describe('v4 -> v5', function() {
var FOLDER_TYPE_INBOX = 'Inbox';
var FOLDER_TYPE_SENT = 'Sent';
var FOLDER_TYPE_DRAFTS = 'Drafts';
var FOLDER_TYPE_TRASH = 'Trash';
var FOLDER_DB_TYPE = 'folders';
var VERSION_DB_TYPE = 'dbVersion';
var POST_UPDATE_DB_VERSION = 5;
beforeEach(function() {
cfg.dbVersion = 5; // app requires database version 4
appConfigStorageStub.listItems.withArgs(VERSION_DB_TYPE).yieldsAsync(null, [4]); // database version is 4
});
afterEach(function() {
// database version is only queried for version checking prior to the update script
// so no need to check this in case-specific tests
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
});
it('should work', function(done) {
userStorageStub.listItems.withArgs(FOLDER_DB_TYPE).yieldsAsync(null, [
[{
name: 'inbox1',
type: FOLDER_TYPE_INBOX
}, {
name: 'inbox2',
type: FOLDER_TYPE_INBOX
}, {
name: 'sent1',
type: FOLDER_TYPE_SENT
}, {
name: 'sent2',
type: FOLDER_TYPE_SENT
}, {
name: 'drafts1',
type: FOLDER_TYPE_DRAFTS
}, {
name: 'drafts2',
type: FOLDER_TYPE_DRAFTS
}, {
name: 'trash1',
type: FOLDER_TYPE_TRASH
}, {
name: 'trash2',
type: FOLDER_TYPE_TRASH
}]
]);
userStorageStub.storeList.withArgs([
[{
name: 'inbox1',
type: FOLDER_TYPE_INBOX
}, {
name: 'sent1',
type: FOLDER_TYPE_SENT
}, {
name: 'drafts1',
type: FOLDER_TYPE_DRAFTS
}, {
name: 'trash1',
type: FOLDER_TYPE_TRASH
}]
], FOLDER_DB_TYPE).yieldsAsync();
appConfigStorageStub.storeList.withArgs([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE).yieldsAsync();
updateHandler.update(function(error) {
expect(error).to.not.exist;
expect(userStorageStub.listItems.calledOnce).to.be.true;
expect(userStorageStub.storeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
done();
});
});
it('should fail when persisting database version fails', function(done) {
userStorageStub.listItems.yieldsAsync(null, []);
userStorageStub.storeList.yieldsAsync();
appConfigStorageStub.storeList.yieldsAsync(new Error());
updateHandler.update(function(error) {
expect(error).to.exist;
expect(userStorageStub.listItems.calledOnce).to.be.true;
expect(userStorageStub.storeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
done();
});
});
it('should fail when persisting folders fails', function(done) {
userStorageStub.listItems.yieldsAsync(null, []);
userStorageStub.storeList.yieldsAsync(new Error());
updateHandler.update(function(error) {
expect(error).to.exist;
expect(userStorageStub.listItems.calledOnce).to.be.true;
expect(userStorageStub.storeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.called).to.be.false;
done();
});
});
it('should fail when listing folders fails', function(done) {
userStorageStub.listItems.yieldsAsync(new Error());
updateHandler.update(function(error) {
expect(error).to.exist;
expect(userStorageStub.listItems.calledOnce).to.be.true;
expect(userStorageStub.storeList.called).to.be.false;
expect(appConfigStorageStub.storeList.called).to.be.false;
done();
}); });
}); });
}); });

View File

@ -1,401 +1,397 @@
define(function(require) { 'use strict';
'use strict';
var expect = chai.expect, var mocks = angular.mocks,
angular = require('angular'), WriteCtrl = require('../../src/js/controller/write'),
mocks = require('angularMocks'), EmailDAO = require('../../src/js/dao/email-dao'),
WriteCtrl = require('js/controller/write'), OutboxBO = require('../../src/js/bo/outbox'),
EmailDAO = require('js/dao/email-dao'), KeychainDAO = require('../../src/js/dao/keychain-dao'),
OutboxBO = require('js/bo/outbox'), appController = require('../../src/js/app-controller');
KeychainDAO = require('js/dao/keychain-dao'),
appController = require('js/app-controller');
describe('Write controller unit test', function() { describe('Write controller unit test', function() {
var ctrl, scope, var ctrl, scope,
origEmailDao, origOutbox, origKeychain, origEmailDao, origOutbox, origKeychain,
emailDaoMock, keychainMock, outboxMock, emailAddress, realname; emailDaoMock, keychainMock, outboxMock, emailAddress, realname;
beforeEach(function() { beforeEach(function() {
// the app controller is a singleton, we need to remember the // the app controller is a singleton, we need to remember the
// outbox and email dao to restore it after the tests // outbox and email dao to restore it after the tests
origEmailDao = appController._emailDao; origEmailDao = appController._emailDao;
origOutbox = appController._outboxBo; origOutbox = appController._outboxBo;
origKeychain = appController._keychain; origKeychain = appController._keychain;
outboxMock = sinon.createStubInstance(OutboxBO); outboxMock = sinon.createStubInstance(OutboxBO);
appController._outboxBo = outboxMock; appController._outboxBo = outboxMock;
emailDaoMock = sinon.createStubInstance(EmailDAO); emailDaoMock = sinon.createStubInstance(EmailDAO);
appController._emailDao = emailDaoMock; appController._emailDao = emailDaoMock;
emailAddress = 'fred@foo.com'; emailAddress = 'fred@foo.com';
realname = 'Fred Foo'; realname = 'Fred Foo';
emailDaoMock._account = { emailDaoMock._account = {
emailAddress: emailAddress, emailAddress: emailAddress,
realname: realname realname: realname
}; };
keychainMock = sinon.createStubInstance(KeychainDAO); keychainMock = sinon.createStubInstance(KeychainDAO);
appController._keychain = keychainMock; appController._keychain = keychainMock;
angular.module('writetest', []); angular.module('writetest', []);
mocks.module('writetest'); mocks.module('writetest');
mocks.inject(function($rootScope, $controller) { mocks.inject(function($rootScope, $controller) {
scope = $rootScope.$new(); scope = $rootScope.$new();
scope.state = {}; scope.state = {};
ctrl = $controller(WriteCtrl, { ctrl = $controller(WriteCtrl, {
$scope: scope $scope: scope
});
}); });
}); });
});
afterEach(function() { afterEach(function() {
// restore the app controller // restore the app controller
appController._emailDao = origEmailDao; appController._emailDao = origEmailDao;
appController._outboxBo = origOutbox; appController._outboxBo = origOutbox;
appController._keychain = origKeychain; appController._keychain = origKeychain;
});
describe('scope variables', function() {
it('should be set correctly', function() {
expect(scope.state.writer).to.exist;
expect(scope.state.lightbox).to.be.undefined;
expect(scope.state.writer.write).to.exist;
expect(scope.state.writer.close).to.exist;
expect(scope.verify).to.exist;
expect(scope.checkSendStatus).to.exist;
expect(scope.sendToOutbox).to.exist;
expect(scope.tagStyle).to.exist;
expect(scope.lookupAddressBook).to.exist;
});
});
describe('close', function() {
it('should close the writer', function() {
scope.state.lightbox = 'write';
scope.state.writer.close();
expect(scope.state.lightbox).to.be.undefined;
});
});
describe('write', function() {
it('should prepare write view', function() {
var verifyMock = sinon.stub(scope, 'verify');
scope.state.writer.write();
expect(scope.writerTitle).to.equal('New email');
expect(scope.to).to.deep.equal([]);
expect(scope.subject).to.equal('');
expect(scope.body).to.equal('');
expect(verifyMock.calledOnce).to.be.true;
scope.verify.restore();
}); });
describe('scope variables', function() { it('should prefill write view for response', function() {
it('should be set correctly', function() { var verifyMock = sinon.stub(scope, 'verify'),
expect(scope.state.writer).to.exist; address = 'pity@dafool',
expect(scope.state.lightbox).to.be.undefined; subject = 'Ermahgerd!',
expect(scope.state.writer.write).to.exist; body = 'so much body!',
expect(scope.state.writer.close).to.exist; re = {
expect(scope.verify).to.exist; id: 'abc',
expect(scope.checkSendStatus).to.exist; from: [{
expect(scope.sendToOutbox).to.exist; address: address
expect(scope.tagStyle).to.exist; }],
expect(scope.lookupAddressBook).to.exist; subject: subject,
}); sentDate: new Date(),
body: body,
references: ['ghi', 'def']
};
scope.sendBtnSecure = true;
scope.state.writer.write(re);
expect(scope.writerTitle).to.equal('Reply');
expect(scope.to).to.deep.equal([{
address: address,
}]);
expect(scope.subject).to.equal('Re: ' + subject);
expect(scope.body).to.contain(body);
expect(scope.references).to.deep.equal(['ghi', 'def', 'abc']);
expect(verifyMock.called).to.be.true;
scope.verify.restore();
}); });
describe('close', function() { it('should prefill write view for forward', function() {
it('should close the writer', function() { var verifyMock = sinon.stub(scope, 'verify'),
scope.state.lightbox = 'write'; address = 'pity@dafool',
subject = 'Ermahgerd!',
scope.state.writer.close(); body = 'so much body!',
re = {
expect(scope.state.lightbox).to.be.undefined; from: [{
}); address: address
}); }],
to: [{
describe('write', function() { address: address
it('should prepare write view', function() { }],
var verifyMock = sinon.stub(scope, 'verify'); subject: subject,
sentDate: new Date(),
scope.state.writer.write(); body: body,
attachments: [{}]
expect(scope.writerTitle).to.equal('New email');
expect(scope.to).to.deep.equal([]);
expect(scope.subject).to.equal('');
expect(scope.body).to.equal('');
expect(verifyMock.calledOnce).to.be.true;
scope.verify.restore();
});
it('should prefill write view for response', function() {
var verifyMock = sinon.stub(scope, 'verify'),
address = 'pity@dafool',
subject = 'Ermahgerd!',
body = 'so much body!',
re = {
id: 'abc',
from: [{
address: address
}],
subject: subject,
sentDate: new Date(),
body: body,
references: ['ghi', 'def']
};
scope.sendBtnSecure = true;
scope.state.writer.write(re);
expect(scope.writerTitle).to.equal('Reply');
expect(scope.to).to.deep.equal([{
address: address,
}]);
expect(scope.subject).to.equal('Re: ' + subject);
expect(scope.body).to.contain(body);
expect(scope.references).to.deep.equal(['ghi', 'def', 'abc']);
expect(verifyMock.called).to.be.true;
scope.verify.restore();
});
it('should prefill write view for forward', function() {
var verifyMock = sinon.stub(scope, 'verify'),
address = 'pity@dafool',
subject = 'Ermahgerd!',
body = 'so much body!',
re = {
from: [{
address: address
}],
to: [{
address: address
}],
subject: subject,
sentDate: new Date(),
body: body,
attachments: [{}]
};
scope.sendBtnSecure = false;
scope.state.writer.write(re, null, true);
expect(scope.writerTitle).to.equal('Forward');
expect(scope.to).to.deep.equal([]);
expect(scope.subject).to.equal('Fwd: ' + subject);
expect(scope.body).to.contain(body);
expect(verifyMock.called).to.be.true;
expect(scope.attachments).to.not.equal(re.attachments); // not the same reference
expect(scope.attachments).to.deep.equal(re.attachments); // but the same content
scope.verify.restore();
});
});
describe('verify', function() {
var checkSendStatusMock;
beforeEach(function() {
checkSendStatusMock = sinon.stub(scope, 'checkSendStatus');
});
afterEach(function() {
scope.checkSendStatus.restore();
});
it('should do nothing if recipient is not provided', function() {
scope.verify(undefined);
});
it('should not work for invalid email addresses', function() {
var recipient = {
address: ''
}; };
scope.verify(recipient); scope.sendBtnSecure = false;
expect(recipient.key).to.be.undefined; scope.state.writer.write(re, null, true);
expect(recipient.secure).to.be.undefined;
expect(scope.checkSendStatus.callCount).to.equal(2);
expect(keychainMock.getReceiverPublicKey.called).to.be.false;
});
it('should not work for error in keychain', function(done) { expect(scope.writerTitle).to.equal('Forward');
var recipient = { expect(scope.to).to.deep.equal([]);
address: 'asds@example.com' expect(scope.subject).to.equal('Fwd: ' + subject);
}; expect(scope.body).to.contain(body);
expect(verifyMock.called).to.be.true;
expect(scope.attachments).to.not.equal(re.attachments); // not the same reference
expect(scope.attachments).to.deep.equal(re.attachments); // but the same content
keychainMock.refreshKeyForUserId.withArgs(recipient.address).yields({ scope.verify.restore();
errMsg: '404 not found yadda yadda'
});
scope.onError = function() {
expect(recipient.key).to.be.undefined;
expect(recipient.secure).to.be.false;
expect(scope.checkSendStatus.callCount).to.equal(1);
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
done();
};
scope.verify(recipient);
});
it('should work for main userId', function(done) {
var recipient = {
address: 'asdf@example.com'
};
keychainMock.refreshKeyForUserId.withArgs(recipient.address).yields(null, {
userId: 'asdf@example.com'
});
scope.$digest = function() {
expect(recipient.key).to.deep.equal({
userId: 'asdf@example.com'
});
expect(recipient.secure).to.be.true;
expect(scope.checkSendStatus.callCount).to.equal(2);
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
done();
};
scope.verify(recipient);
});
it('should work for secondary userId', function(done) {
var recipient = {
address: 'asdf@example.com'
};
var key = {
userId: 'qwer@example.com',
userIds: [{
emailAddress: 'asdf@example.com'
}]
};
keychainMock.refreshKeyForUserId.withArgs(recipient.address).yields(null, key);
scope.$digest = function() {
expect(recipient.key).to.deep.equal(key);
expect(recipient.secure).to.be.true;
expect(scope.checkSendStatus.callCount).to.equal(2);
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
done();
};
scope.verify(recipient);
});
});
describe('checkSendStatus', function() {
beforeEach(function() {
scope.state.writer.write();
});
afterEach(function() {});
it('should not be able to send with no recipients', function() {
scope.checkSendStatus();
expect(scope.okToSend).to.be.false;
expect(scope.sendBtnText).to.be.undefined;
expect(scope.sendBtnSecure).to.be.undefined;
});
it('should be able to send plaintext', function() {
scope.to = [{
address: 'asdf@asdf.de'
}];
scope.checkSendStatus();
expect(scope.okToSend).to.be.true;
expect(scope.sendBtnText).to.equal('Send');
expect(scope.sendBtnSecure).to.be.false;
});
it('should send plaintext if one receiver is not secure', function() {
scope.to = [{
address: 'asdf@asdf.de',
secure: true
}, {
address: 'asdf@asdfg.de'
}];
scope.checkSendStatus();
expect(scope.okToSend).to.be.true;
expect(scope.sendBtnText).to.equal('Send');
expect(scope.sendBtnSecure).to.be.false;
});
it('should be able to send securely to multiple recipients', function() {
scope.to = [{
address: 'asdf@asdf.de',
secure: true
}, {
address: 'asdf@asdfg.de',
secure: true
}];
scope.checkSendStatus();
expect(scope.okToSend).to.be.true;
expect(scope.sendBtnText).to.equal('Send securely');
expect(scope.sendBtnSecure).to.be.true;
});
});
describe('send to outbox', function() {
it('should work', function() {
scope.to = [{
address: 'pity@dafool'
}];
scope.cc = [];
scope.bcc = [];
scope.subject = 'Ermahgerd!';
scope.body = 'wow. much body! very text!';
scope.attachments = [];
scope.state.nav = {
currentFolder: 'currentFolder'
};
scope.replyTo = {};
outboxMock.put.withArgs(sinon.match(function(mail) {
expect(mail.from).to.deep.equal([{
address: emailAddress,
name: realname
}]);
expect(mail.to).to.deep.equal(scope.to);
expect(mail.cc).to.deep.equal(scope.cc);
expect(mail.bcc).to.deep.equal(scope.bcc);
expect(mail.body).to.contain(scope.body);
expect(mail.subject).to.equal(scope.subject);
expect(mail.attachments).to.be.empty;
expect(mail.sentDate).to.exist;
return true;
})).yields();
emailDaoMock.setFlags.yields();
scope.onError = function(err) {
expect(err).to.not.exist;
};
scope.sendToOutbox();
expect(outboxMock.put.calledOnce).to.be.true;
expect(emailDaoMock.setFlags.calledOnce).to.be.true;
expect(scope.state.lightbox).to.be.undefined;
expect(scope.replyTo.answered).to.be.true;
});
});
describe('lookupAddressBook', function() {
it('should work', function(done) {
keychainMock.listLocalPublicKeys.yields(null, [{
userId: 'test@asdf.com',
publicKey: 'KEY'
}]);
var result = scope.lookupAddressBook('test');
result.then(function(response) {
expect(response).to.deep.equal([{
address: 'test@asdf.com'
}]);
done();
});
scope.$digest();
});
it('should work with cache', function(done) {
scope.addressBookCache = [{
address: 'test@asdf.com'
}, {
address: 'tes@asdf.com'
}];
var result = scope.lookupAddressBook('test');
result.then(function(response) {
expect(response).to.deep.equal([{
address: 'test@asdf.com'
}]);
done();
});
scope.$digest();
});
}); });
}); });
describe('verify', function() {
var checkSendStatusMock;
beforeEach(function() {
checkSendStatusMock = sinon.stub(scope, 'checkSendStatus');
});
afterEach(function() {
scope.checkSendStatus.restore();
});
it('should do nothing if recipient is not provided', function() {
scope.verify(undefined);
});
it('should not work for invalid email addresses', function() {
var recipient = {
address: ''
};
scope.verify(recipient);
expect(recipient.key).to.be.undefined;
expect(recipient.secure).to.be.undefined;
expect(scope.checkSendStatus.callCount).to.equal(2);
expect(keychainMock.getReceiverPublicKey.called).to.be.false;
});
it('should not work for error in keychain', function(done) {
var recipient = {
address: 'asds@example.com'
};
keychainMock.refreshKeyForUserId.withArgs(recipient.address).yields({
errMsg: '404 not found yadda yadda'
});
scope.onError = function() {
expect(recipient.key).to.be.undefined;
expect(recipient.secure).to.be.false;
expect(scope.checkSendStatus.callCount).to.equal(1);
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
done();
};
scope.verify(recipient);
});
it('should work for main userId', function(done) {
var recipient = {
address: 'asdf@example.com'
};
keychainMock.refreshKeyForUserId.withArgs(recipient.address).yields(null, {
userId: 'asdf@example.com'
});
scope.$digest = function() {
expect(recipient.key).to.deep.equal({
userId: 'asdf@example.com'
});
expect(recipient.secure).to.be.true;
expect(scope.checkSendStatus.callCount).to.equal(2);
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
done();
};
scope.verify(recipient);
});
it('should work for secondary userId', function(done) {
var recipient = {
address: 'asdf@example.com'
};
var key = {
userId: 'qwer@example.com',
userIds: [{
emailAddress: 'asdf@example.com'
}]
};
keychainMock.refreshKeyForUserId.withArgs(recipient.address).yields(null, key);
scope.$digest = function() {
expect(recipient.key).to.deep.equal(key);
expect(recipient.secure).to.be.true;
expect(scope.checkSendStatus.callCount).to.equal(2);
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
done();
};
scope.verify(recipient);
});
});
describe('checkSendStatus', function() {
beforeEach(function() {
scope.state.writer.write();
});
afterEach(function() {});
it('should not be able to send with no recipients', function() {
scope.checkSendStatus();
expect(scope.okToSend).to.be.false;
expect(scope.sendBtnText).to.be.undefined;
expect(scope.sendBtnSecure).to.be.undefined;
});
it('should be able to send plaintext', function() {
scope.to = [{
address: 'asdf@asdf.de'
}];
scope.checkSendStatus();
expect(scope.okToSend).to.be.true;
expect(scope.sendBtnText).to.equal('Send');
expect(scope.sendBtnSecure).to.be.false;
});
it('should send plaintext if one receiver is not secure', function() {
scope.to = [{
address: 'asdf@asdf.de',
secure: true
}, {
address: 'asdf@asdfg.de'
}];
scope.checkSendStatus();
expect(scope.okToSend).to.be.true;
expect(scope.sendBtnText).to.equal('Send');
expect(scope.sendBtnSecure).to.be.false;
});
it('should be able to send securely to multiple recipients', function() {
scope.to = [{
address: 'asdf@asdf.de',
secure: true
}, {
address: 'asdf@asdfg.de',
secure: true
}];
scope.checkSendStatus();
expect(scope.okToSend).to.be.true;
expect(scope.sendBtnText).to.equal('Send securely');
expect(scope.sendBtnSecure).to.be.true;
});
});
describe('send to outbox', function() {
it('should work', function() {
scope.to = [{
address: 'pity@dafool'
}];
scope.cc = [];
scope.bcc = [];
scope.subject = 'Ermahgerd!';
scope.body = 'wow. much body! very text!';
scope.attachments = [];
scope.state.nav = {
currentFolder: 'currentFolder'
};
scope.replyTo = {};
outboxMock.put.withArgs(sinon.match(function(mail) {
expect(mail.from).to.deep.equal([{
address: emailAddress,
name: realname
}]);
expect(mail.to).to.deep.equal(scope.to);
expect(mail.cc).to.deep.equal(scope.cc);
expect(mail.bcc).to.deep.equal(scope.bcc);
expect(mail.body).to.contain(scope.body);
expect(mail.subject).to.equal(scope.subject);
expect(mail.attachments).to.be.empty;
expect(mail.sentDate).to.exist;
return true;
})).yields();
emailDaoMock.setFlags.yields();
scope.onError = function(err) {
expect(err).to.not.exist;
};
scope.sendToOutbox();
expect(outboxMock.put.calledOnce).to.be.true;
expect(emailDaoMock.setFlags.calledOnce).to.be.true;
expect(scope.state.lightbox).to.be.undefined;
expect(scope.replyTo.answered).to.be.true;
});
});
describe('lookupAddressBook', function() {
it('should work', function(done) {
keychainMock.listLocalPublicKeys.yields(null, [{
userId: 'test@asdf.com',
publicKey: 'KEY'
}]);
var result = scope.lookupAddressBook('test');
result.then(function(response) {
expect(response).to.deep.equal([{
address: 'test@asdf.com'
}]);
done();
});
scope.$digest();
});
it('should work with cache', function(done) {
scope.addressBookCache = [{
address: 'test@asdf.com'
}, {
address: 'tes@asdf.com'
}];
var result = scope.lookupAddressBook('test');
result.then(function(response) {
expect(response).to.deep.equal([{
address: 'test@asdf.com'
}]);
done();
});
scope.$digest();
});
});
}); });