summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
authorErik Larkö <erik@mullvad.net>2017-10-13 11:31:57 +0200
committerErik Larkö <erik@mullvad.net>2017-10-13 11:31:57 +0200
commit992f7946dbe9004d88a331b97ea9e030701599ea (patch)
tree858ef774e0751435f2b039af846cef4d29c37a0c /test
parent8f5c5aa9a433a65d8a7f6f6d8e4211f5a82068f5 (diff)
downloadmullvadvpn-992f7946dbe9004d88a331b97ea9e030701599ea.tar.xz
mullvadvpn-992f7946dbe9004d88a331b97ea9e030701599ea.zip
Move the auth flow into backend.js and write tests
Diffstat (limited to 'test')
-rw-r--r--test/auth.spec.js56
-rw-r--r--test/connect.spec.js30
-rw-r--r--test/helpers/IpcChain.js19
-rw-r--r--test/helpers/ipc-helpers.js26
-rw-r--r--test/mocks/ipc.js16
5 files changed, 127 insertions, 20 deletions
diff --git a/test/auth.spec.js b/test/auth.spec.js
new file mode 100644
index 0000000000..6bb23a7136
--- /dev/null
+++ b/test/auth.spec.js
@@ -0,0 +1,56 @@
+// @flow
+
+import { expect } from 'chai';
+import { setupIpcAndStore, setupBackendAndStore, failFast, checkNextTick } from './helpers/ipc-helpers';
+import { IpcChain } from './helpers/IpcChain';
+import { Backend } from '../app/lib/backend';
+
+describe('authentication', () => {
+
+ it('authenticates before ipc call if unauthenticated', (done) => {
+ const { store, mockIpc } = setupIpcAndStore();
+ const credentials = {
+ sharedSecret: 'foo',
+ connectionString: '',
+ };
+
+
+ const chain = new IpcChain(mockIpc);
+ chain.require('auth')
+ .withInputValidation( secret => {
+ expect(secret).to.equal(credentials.sharedSecret);
+ })
+ .done();
+
+ chain.require('connect')
+ .done();
+
+ chain.onSuccessOrFailure(done);
+
+
+ const backend = new Backend(store, credentials, mockIpc);
+ backend.connect();
+ });
+
+ it('reauthenticates on reconnect', (done) => {
+ const { mockIpc, backend } = setupBackendAndStore();
+
+ let authCount = 0;
+ mockIpc.auth = () => {
+ authCount++;
+ return Promise.resolve();
+ };
+
+
+ mockIpc.killWebSocket();
+ failFast(() => {
+ expect(authCount).to.equal(0);
+ }, done);
+
+
+ backend.connect();
+ checkNextTick(() => {
+ expect(authCount).to.equal(1);
+ }, done);
+ });
+});
diff --git a/test/connect.spec.js b/test/connect.spec.js
index bb6ac41526..07e3091c6e 100644
--- a/test/connect.spec.js
+++ b/test/connect.spec.js
@@ -61,29 +61,35 @@ describe('connect', () => {
});
});
- it('should correctly deduce \'connected\' from backend states', () => {
+ it('should correctly deduce \'connected\' from backend states', (done) => {
const { store, mockIpc } = setupBackendAndStore();
- expect(store.getState().connection.status).not.to.equal('connected');
- mockIpc.sendNewState({ state: 'secured', target_state: 'secured' });
- expect(store.getState().connection.status).to.equal('connected');
+ checkNextTick( () => {
+ expect(store.getState().connection.status).not.to.equal('connected');
+ mockIpc.sendNewState({ state: 'secured', target_state: 'secured' });
+ expect(store.getState().connection.status).to.equal('connected');
+ }, done);
});
- it('should correctly deduce \'connecting\' from backend states', () => {
+ it('should correctly deduce \'connecting\' from backend states', (done) => {
const { store, mockIpc } = setupBackendAndStore();
- expect(store.getState().connection.status).not.to.equal('connecting');
- mockIpc.sendNewState({ state: 'unsecured', target_state: 'secured' });
- expect(store.getState().connection.status).to.equal('connecting');
+ checkNextTick( () => {
+ expect(store.getState().connection.status).not.to.equal('connecting');
+ mockIpc.sendNewState({ state: 'unsecured', target_state: 'secured' });
+ expect(store.getState().connection.status).to.equal('connecting');
+ }, done);
});
- it('should correctly deduce \'disconnected\' from backend states', () => {
+ it('should correctly deduce \'disconnected\' from backend states', (done) => {
const { store, mockIpc } = setupBackendAndStore();
store.dispatch(connectionActions.connected());
- expect(store.getState().connection.status).not.to.equal('disconnected');
- mockIpc.sendNewState({ state: 'unsecured', target_state: 'unsecured' });
- expect(store.getState().connection.status).to.equal('disconnected');
+ checkNextTick( () => {
+ expect(store.getState().connection.status).not.to.equal('disconnected');
+ mockIpc.sendNewState({ state: 'unsecured', target_state: 'unsecured' });
+ expect(store.getState().connection.status).to.equal('disconnected');
+ }, done);
});
});
diff --git a/test/helpers/IpcChain.js b/test/helpers/IpcChain.js
index a7916e1e9f..a62626a6a6 100644
--- a/test/helpers/IpcChain.js
+++ b/test/helpers/IpcChain.js
@@ -8,11 +8,13 @@ export class IpcChain {
_recordedCalls: Array<string>;
_mockIpc: {};
_done: (*) => void;
+ _aborted: boolean;
constructor(mockIpc: {}) {
this._expectedCalls = [];
this._recordedCalls = [];
this._mockIpc = mockIpc;
+ this._aborted = false;
}
require(ipcCall: string): StepBuilder {
@@ -28,10 +30,21 @@ export class IpcChain {
}
_stepPromiseCallback(step, resolve, args) {
+ if (this._aborted) {
+ return;
+ }
+
this._registerCall(step.ipcCall);
if (step.inputValidation) {
- failFast(() => step.inputValidation(...args), this._done);
+ const failedInputValidation = failFast(() => {
+ step.inputValidation(...args);
+ }, this._done);
+
+ if (failedInputValidation) {
+ this._abort();
+ return;
+ }
}
if (this._isLastCall()) {
@@ -41,6 +54,10 @@ export class IpcChain {
resolve(step.returnValue);
}
+ _abort() {
+ this._aborted = true;
+ }
+
_isLastCall(): boolean {
return this._recordedCalls.length === this._expectedCalls.length;
}
diff --git a/test/helpers/ipc-helpers.js b/test/helpers/ipc-helpers.js
index 5e4b2941d6..e2a578973b 100644
--- a/test/helpers/ipc-helpers.js
+++ b/test/helpers/ipc-helpers.js
@@ -9,14 +9,24 @@ import { mockState, mockStore } from '../mocks/redux';
type DoneCallback = (?mixed) => void;
type Check = () => void;
-export function setupBackendAndStore() {
-
+export function setupIpcAndStore() {
const memoryHistory = createMemoryHistory();
const store = configureStore(null, memoryHistory);
const mockIpc = newMockIpc();
- const backend = new Backend(store, mockIpc);
+ return { store, mockIpc };
+}
+
+export function setupBackendAndStore() {
+
+ const { store, mockIpc } = setupIpcAndStore();
+
+ const credentials = {
+ sharedSecret: '',
+ connectionString: '',
+ };
+ const backend = new Backend(store, credentials, mockIpc);
return { store, mockIpc, backend };
}
@@ -24,7 +34,11 @@ export function setupBackendAndStore() {
export function setupBackendAndMockStore() {
const store = mockStore(mockState());
const mockIpc = newMockIpc();
- const backend = new Backend(store, mockIpc);
+ const credentials = {
+ sharedSecret: '',
+ connectionString: '',
+ };
+ const backend = new Backend(store, credentials, mockIpc);
return { store, mockIpc, backend };
}
@@ -54,11 +68,13 @@ export function checkNextTick(fn: Check, done: DoneCallback) {
// In async tests where we want to test a chain of IPC messages
// we can only invoke `done` for the last message. This function
// is for the intermediate messages.
-export function failFast(fn: Check, done: DoneCallback) {
+export function failFast(fn: Check, done: DoneCallback): boolean {
try {
fn();
+ return false;
} catch(e) {
done(e);
+ return true;
}
}
export function failFastNextTick(fn: Check, done: DoneCallback) {
diff --git a/test/mocks/ipc.js b/test/mocks/ipc.js
index 8f0b82ba37..2672a431fd 100644
--- a/test/mocks/ipc.js
+++ b/test/mocks/ipc.js
@@ -1,20 +1,23 @@
// @flow
-import type { IpcFacade, BackendState, IpcCredentials } from '../../app/lib/ipc-facade';
+import type { IpcFacade, BackendState } from '../../app/lib/ipc-facade';
interface MockIpc {
sendNewState: (BackendState) => void;
+ killWebSocket: () => void;
-getAccountData: *;
-connect: *;
-getAccount: *;
+ -auth: *;
}
export function newMockIpc() {
const stateListeners = [];
+ const connectionCloseListeners = [];
const mockIpc: IpcFacade & MockIpc = {
- setCredentials: (_credentials: IpcCredentials) => {},
+ setConnectionString: (_str: string) => {},
getAccountData: (accountToken) => {
return new Promise(r => r({
accountToken: accountToken,
@@ -60,6 +63,15 @@ export function newMockIpc() {
l(state);
}
},
+ auth: (_secret: string) => Promise.resolve(),
+ setCloseConnectionHandler: (listener: () => void) => {
+ connectionCloseListeners.push(listener);
+ },
+ killWebSocket: () => {
+ for(const l of connectionCloseListeners) {
+ l();
+ }
+ }
};
return mockIpc;