diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2018-06-20 15:34:40 +0200 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2018-07-03 13:37:54 +0200 |
| commit | 73840e98952dd3f7c005fcec44c971455da179eb (patch) | |
| tree | 15a887410858007a802450ad2870d8bf495dd16d /test | |
| parent | 67e82627564f8e4a2d8da4bcf5f0fd00867876bc (diff) | |
| download | mullvadvpn-73840e98952dd3f7c005fcec44c971455da179eb.tar.xz mullvadvpn-73840e98952dd3f7c005fcec44c971455da179eb.zip | |
Refactor IpcFacade to DaemonRpc and JsonRpcWs to JsonRpcTransport
Diffstat (limited to 'test')
| -rw-r--r-- | test/auth.spec.js | 20 | ||||
| -rw-r--r-- | test/autologin.spec.js | 2 | ||||
| -rw-r--r-- | test/connect.spec.js | 4 | ||||
| -rw-r--r-- | test/helpers/IpcChain.js | 18 | ||||
| -rw-r--r-- | test/helpers/ipc-helpers.js | 14 | ||||
| -rw-r--r-- | test/ipc.spec.js | 134 | ||||
| -rw-r--r-- | test/jsonrpc-transport.spec.js | 144 | ||||
| -rw-r--r-- | test/login.spec.js | 2 | ||||
| -rw-r--r-- | test/logout.spec.js | 2 | ||||
| -rw-r--r-- | test/mocks/ipc.js | 58 |
10 files changed, 199 insertions, 199 deletions
diff --git a/test/auth.spec.js b/test/auth.spec.js index e6b2c25486..4f919dff87 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -8,30 +8,34 @@ describe('authentication', () => { it('authenticates before ipc call if unauthenticated', (done) => { const { store, mockIpc } = setupIpcAndStore(); + const credentials = { + connectionString: 'ws://localhost:1234/', + sharedSecret: '1234', + }; + const chain = new IpcChain(mockIpc); - chain.onSuccessOrFailure(done); chain.expect('authenticate').withInputValidation((secret) => { expect(secret).to.equal(credentials.sharedSecret); }); chain.expect('connect'); + chain.end(done); - const credentials = { - sharedSecret: '', - connectionString: '', - }; - const backend = new Backend(store, credentials, mockIpc); - backend.connect(); + const backend = new Backend(store, mockIpc); + backend.connect(credentials); + + backend.connectTunnel(); }); it('reauthenticates on reconnect', async () => { const { mockIpc, backend } = setupBackendAndStore(); mockIpc.authenticate = spy(mockIpc.authenticate); + await mockIpc.connectTunnel(); mockIpc.killWebSocket(); expect(mockIpc.authenticate).to.not.have.been.called(); - await backend.connect(); + await backend.connectTunnel(); expect(mockIpc.authenticate).to.have.been.called.once; }); }); diff --git a/test/autologin.spec.js b/test/autologin.spec.js index 60c5cc5bde..505ad285c0 100644 --- a/test/autologin.spec.js +++ b/test/autologin.spec.js @@ -16,7 +16,7 @@ describe('autologin', () => { expect(num).to.equal(randomAccountToken); }); - chain.onSuccessOrFailure(done); + chain.end(done); backend.autologin(); }); diff --git a/test/connect.spec.js b/test/connect.spec.js index f1621dbeb6..409a04c0fb 100644 --- a/test/connect.spec.js +++ b/test/connect.spec.js @@ -7,7 +7,7 @@ describe('connect', () => { it("should set the connection state to 'disconnected' on failed attempts", (done) => { const { store, mockIpc, backend } = setupBackendAndStore(); - mockIpc.connect = () => new Promise((_, reject) => reject('Some error')); + mockIpc.connectTunnel = () => new Promise((_, reject) => reject('Some error')); store.dispatch(connectionActions.connected()); @@ -23,7 +23,7 @@ describe('connect', () => { it('should update the state with the server address', () => { const { store, backend } = setupBackendAndStore(); - return backend.connect().then(() => { + return backend.connectTunnel().then(() => { const state = store.getState().connection; expect(state.status).to.equal('connecting'); }); diff --git a/test/helpers/IpcChain.js b/test/helpers/IpcChain.js index 1505621cc0..547851fdcf 100644 --- a/test/helpers/IpcChain.js +++ b/test/helpers/IpcChain.js @@ -1,7 +1,5 @@ // @flow -import { check, failFast } from './ipc-helpers'; - export class IpcChain { _expectedCalls: Array<string>; _recordedCalls: Array<string>; @@ -39,12 +37,11 @@ export class IpcChain { const inputValidation = step.inputValidation; if (inputValidation) { - const failedInputValidation = failFast(() => { + try { inputValidation(...args); - }, this._done); - - if (failedInputValidation) { + } catch (error) { this._abort(); + this._done(error); return; } } @@ -65,16 +62,19 @@ export class IpcChain { } _ensureChainCalledCorrectly() { - check(() => { + try { expect(this._expectedCalls).to.deep.equal(this._recordedCalls); - }, this._done); + this._done(); + } catch (error) { + this._done(error); + } } _registerCall(ipcCall: string) { this._recordedCalls.push(ipcCall); } - onSuccessOrFailure(done: (*) => void) { + end(done: (?Error) => void) { this._done = done; } } diff --git a/test/helpers/ipc-helpers.js b/test/helpers/ipc-helpers.js index 4466c06eb2..5bfe1d0267 100644 --- a/test/helpers/ipc-helpers.js +++ b/test/helpers/ipc-helpers.js @@ -12,7 +12,6 @@ type Check = () => void; export function setupIpcAndStore() { const memoryHistory = createMemoryHistory(); const store = configureStore(null, memoryHistory); - const mockIpc = newMockIpc(); return { store, mockIpc }; @@ -20,12 +19,7 @@ export function setupIpcAndStore() { export function setupBackendAndStore() { const { store, mockIpc } = setupIpcAndStore(); - - const credentials = { - sharedSecret: '', - connectionString: '', - }; - const backend = new Backend(store, credentials, mockIpc); + const backend = new Backend(store, mockIpc); return { store, mockIpc, backend }; } @@ -33,11 +27,7 @@ export function setupBackendAndStore() { export function setupBackendAndMockStore() { const store = mockStore(_initialState()); const mockIpc = newMockIpc(); - const credentials = { - sharedSecret: '', - connectionString: '', - }; - const backend = new Backend(store, credentials, mockIpc); + const backend = new Backend(store, mockIpc); return { store, mockIpc, backend }; } diff --git a/test/ipc.spec.js b/test/ipc.spec.js deleted file mode 100644 index 1c8b19b5a6..0000000000 --- a/test/ipc.spec.js +++ /dev/null @@ -1,134 +0,0 @@ -// @flow - -import Ipc from '../app/lib/jsonrpc-ws-ipc'; -import jsonrpc from 'jsonrpc-lite'; -import type { JsonRpcMessage } from '../app/lib/jsonrpc-ws-ipc'; - -describe('The IPC server', () => { - it('should send as soon as the websocket connects', () => { - const { ws, ipc } = setupIpc(); - ws.close(); - - let sent = false; - const p = ipc.send('hello').then(() => { - expect(sent).to.be.true; - }); - - ws.on('hello', (msg) => { - sent = true; - - ws.replyOk(msg.id); - }); - ws.acceptConnection(); - - return p; - }); - - it('should reject failed jsonrpc requests', () => { - const { ws, ipc } = setupIpc(); - ws.on('WHAT_IS_THIS', (msg) => { - ws.replyFail(msg.id, 'Method not found', -32601); - }); - - return ipc.send('WHAT_IS_THIS').catch((e) => { - expect(e.code).to.equal(-32601); - expect(e.message).to.contain('Method not found'); - }); - }); - - it('should route reply to correct promise', () => { - const { ws, ipc } = setupIpc(); - - ws.on('a message', (msg) => ws.replyOk(msg.id, 'a reply')); - - const decoy = ipc - .send('a decoy', [], 1) - .then(() => { - throw new Error('Should not be called'); - }) - .catch((e) => { - if (e.name !== 'TimeOutError') { - throw e; - } - }); - const message = ipc.send('a message', [], 1).then((reply) => expect(reply).to.equal('a reply')); - - return Promise.all([message, decoy]); - }); - - it('should timeout if no response is returned', () => { - const { ipc } = setupIpc(); - - return ipc.send('a message', [], 1).catch((e) => { - expect(e.name).to.equal('TimeOutError'); - expect(e.message).to.contain('timed out'); - }); - }); - - it('should route notifications', (done) => { - const { ws, ipc } = setupIpc(); - - const eventListener = (event) => { - try { - expect(event).to.equal('an event!'); - done(); - } catch (ex) { - done(ex); - } - }; - - ws.on('event_subscribe', (msg) => ws.replyOk(msg.id, 1)); - ipc - .on('event', eventListener) - .then(() => { - ws.reply(jsonrpc.notification('event', { subscription: 1, result: 'an event!' })); - }) - .catch((e) => done(e)); - }); -}); - -function mockWebsocket() { - const ws: any = { - listeners: {}, - readyState: 1, - }; - - ws.on = (event, listener) => (ws.listeners[event] = listener); - ws.send = (data) => { - const listener = ws.listeners[data.method]; - if (listener) { - listener(data); - } - }; - - ws.factory = () => ws; - - ws.acceptConnection = () => { - ws.readyState = 1; - ws.onopen(); - }; - ws.close = () => { - ws.readyState = 3; - ws.onclose(); - }; - - ws.reply = (msg: JsonRpcMessage) => { - ws.onmessage({ data: JSON.stringify(msg) }); - }; - ws.replyOk = (id: string, msg) => { - ws.reply(jsonrpc.success(id, msg || '')); - }; - ws.replyFail = (id: string, msg: string, code: number) => { - ws.reply(jsonrpc.error(id, new jsonrpc.JsonRpcError(msg, code))); - }; - - return ws; -} - -function setupIpc() { - const ws = mockWebsocket(); - return { - ws: ws, - ipc: new Ipc('1.2.3.4', ws.factory), - }; -} diff --git a/test/jsonrpc-transport.spec.js b/test/jsonrpc-transport.spec.js new file mode 100644 index 0000000000..124bba8f00 --- /dev/null +++ b/test/jsonrpc-transport.spec.js @@ -0,0 +1,144 @@ +// @flow + +import JsonRpcTransport, { + TimeOutError as JsonRpcTransportTimeOutError, +} from '../app/lib/jsonrpc-transport'; +import jsonrpc from 'jsonrpc-lite'; +import { Server, WebSocket as MockWebSocket } from 'mock-socket'; + +describe('JSON RPC transport', () => { + const WEBSOCKET_URL = 'ws://localhost:8080'; + let server: Server, transport: JsonRpcTransport; + + beforeEach(() => { + server = new Server(WEBSOCKET_URL); + transport = new JsonRpcTransport((s) => new MockWebSocket(s)); + }); + + afterEach(() => { + server.close(); + }); + + it('should send as soon as the websocket connects', (done) => { + server.on('message', (msg) => { + const { payload } = jsonrpc.parse(msg); + + if (payload.method === 'hello') { + server.send(JSON.stringify(jsonrpc.success(payload.id, 'ok'))); + } + }); + + transport + .send('hello') + .then(() => { + done(); + }) + .catch((error) => { + done(error); + }); + + transport.connect(WEBSOCKET_URL); + }); + + it('should reject failed jsonrpc requests', (done) => { + server.on('message', (msg) => { + const { payload } = jsonrpc.parse(msg); + + if (payload.method === 'invalid-method') { + server.send( + JSON.stringify( + jsonrpc.error(payload.id, new jsonrpc.JsonRpcError('Method not found', -32601)), + ), + ); + } + }); + + transport.send('invalid-method').catch((error) => { + try { + expect(error.code).to.equal(-32601); + expect(error.message).to.contain('Method not found'); + done(); + } catch (error) { + done(error); + } + }); + + transport.connect(WEBSOCKET_URL); + }); + + it('should route reply to correct promise', () => { + server.on('message', (msg) => { + const { payload } = jsonrpc.parse(msg); + + if (payload.method === 'a message') { + server.send(JSON.stringify(jsonrpc.success(payload.id, 'a reply'))); + } + }); + + const decoy = transport + .send('a decoy', [], 100) + .then(() => { + throw new Error('Should not be called'); + }) + .catch((error) => { + expect(error).to.be.an.instanceof(JsonRpcTransportTimeOutError); + }); + + const message = transport.send('a message', [], 100).then((reply) => { + expect(reply).to.equal('a reply'); + }); + + transport.connect(WEBSOCKET_URL); + + return Promise.all([message, decoy]); + }); + + it('should timeout if no response is returned', (done) => { + transport + .send('timeout-message', {}, 1) + .then(() => { + done(new Error('Should not be called')); + }) + .catch((error) => { + try { + expect(error).to.be.an.instanceof(JsonRpcTransportTimeOutError); + expect(error.message).to.contain('Request timed out'); + done(); + } catch (error) { + done(error); + } + }); + + transport.connect(WEBSOCKET_URL); + }); + + it('should route notifications', (done) => { + server.on('message', (msg) => { + const { payload } = jsonrpc.parse(msg); + + if (payload.method === 'event_subscribe') { + server.send(JSON.stringify(jsonrpc.success(payload.id, 1))); + } + }); + + transport + .subscribe('event', (event) => { + try { + expect(event).to.equal('an event!'); + done(); + } catch (error) { + done(error); + } + }) + .then(() => { + server.send( + JSON.stringify(jsonrpc.notification('event', { subscription: 1, result: 'an event!' })), + ); + }) + .catch((error) => { + done(error); + }); + + transport.connect(WEBSOCKET_URL); + }); +}); diff --git a/test/login.spec.js b/test/login.spec.js index 68db14494b..a2390b5241 100644 --- a/test/login.spec.js +++ b/test/login.spec.js @@ -24,7 +24,7 @@ describe('Logging in', () => { expect(an).to.equal('123'); }); - chain.onSuccessOrFailure(done); + chain.end(done); store.dispatch(accountActions.login(backend, '123')); }); diff --git a/test/logout.spec.js b/test/logout.spec.js index 65bc3aba09..c973c6a81a 100644 --- a/test/logout.spec.js +++ b/test/logout.spec.js @@ -19,7 +19,7 @@ describe('logging out', () => { expect(num).to.be.null; }); chain.expect('disconnect'); - chain.onSuccessOrFailure(done); + chain.end(done); backend.logout(); }); diff --git a/test/mocks/ipc.js b/test/mocks/ipc.js index 590313ec13..b610244462 100644 --- a/test/mocks/ipc.js +++ b/test/mocks/ipc.js @@ -1,39 +1,39 @@ // @flow -import type { IpcFacade, AccountToken, AccountData, BackendState } from '../../app/lib/ipc-facade'; +import type { + DaemonRpcProtocol, + AccountToken, + AccountData, + BackendState, +} from '../../app/lib/daemon-rpc'; interface MockIpc { sendNewState: (BackendState) => void; killWebSocket: () => void; -getAccountData: (AccountToken) => Promise<AccountData>; - -connect: () => Promise<void>; + -connectTunnel: () => Promise<void>; -getAccount: () => Promise<?AccountToken>; -authenticate: (string) => Promise<void>; } export function newMockIpc() { const stateListeners = []; - const connectionCloseListeners = []; + let connectionOpenListener: ?() => void; + let connectionCloseListener: ?(error: ?Error) => void; - const mockIpc: IpcFacade & MockIpc = { + const mockIpc: DaemonRpcProtocol & MockIpc = { setConnectionString: (_str: string) => {}, - getAccountData: (accountToken) => Promise.resolve({ accountToken: accountToken, expiry: '', }), - getRelayLocations: () => Promise.resolve({ countries: [], }), - getAccount: () => Promise.resolve('1111'), - setAccount: () => Promise.resolve(), - updateRelaySettings: () => Promise.resolve(), - getRelaySettings: () => Promise.resolve({ custom_tunnel_endpoint: { @@ -46,17 +46,16 @@ export function newMockIpc() { }, }, }), - setAllowLan: (_allowLan: boolean) => Promise.resolve(), - getAllowLan: () => Promise.resolve(true), - - connect: () => Promise.resolve(), - - disconnect: () => Promise.resolve(), - - shutdown: () => Promise.resolve(), - + connect: () => { + if (connectionOpenListener) { + connectionOpenListener(); + } + }, + disconnect: () => {}, + connectTunnel: () => Promise.resolve(), + disconnectTunnel: () => Promise.resolve(), getLocation: () => Promise.resolve({ ip: '', @@ -66,36 +65,33 @@ export function newMockIpc() { longitude: 0.0, mullvad_exit_ip: false, }), - getState: () => Promise.resolve({ state: 'unsecured', target_state: 'unsecured', }), - - registerStateListener: (listener: (BackendState) => void) => { + subscribeStateListener: (listener: (state: ?BackendState, error: ?Error) => void) => { stateListeners.push(listener); + return Promise.resolve(); }, - sendNewState: (state: BackendState) => { for (const listener of stateListeners) { listener(state); } }, - - setCloseConnectionHandler: (listener: () => void) => { - connectionCloseListeners.push(listener); + addOpenConnectionObserver: (listener: () => void) => { + connectionOpenListener = listener; + }, + addCloseConnectionObserver: (listener: (error: ?Error) => void) => { + connectionCloseListener = listener; }, - authenticate: (_secret: string) => Promise.resolve(), - getAccountHistory: () => Promise.resolve([]), - removeAccountFromHistory: (_accountToken) => Promise.resolve(), killWebSocket: () => { - for (const listener of connectionCloseListeners) { - listener(); + if (connectionCloseListener) { + connectionCloseListener(); } }, }; |
