summaryrefslogtreecommitdiffhomepage
path: root/gui
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-10-12 08:03:35 -0300
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-10-15 10:27:05 -0300
commit573f2a69956e28b3b5cbb665b099edb30714074a (patch)
tree56ff47447fe9b10c1f9f5279db024b660fc7ab4f /gui
parenta3b0dfc70589ea29ba097747f81b6d22f1bcbcf5 (diff)
downloadmullvadvpn-573f2a69956e28b3b5cbb665b099edb30714074a.tar.xz
mullvadvpn-573f2a69956e28b3b5cbb665b099edb30714074a.zip
Test `AccountDataCache`
Diffstat (limited to 'gui')
-rw-r--r--gui/flow-libs/tests.js.flow1
-rw-r--r--gui/packages/desktop/package.json3
-rw-r--r--gui/packages/desktop/src/renderer/app.js2
-rw-r--r--gui/packages/desktop/test/.eslintrc1
-rw-r--r--gui/packages/desktop/test/account-data-cache.spec.js163
-rw-r--r--gui/packages/desktop/test/setup/renderer.js2
-rw-r--r--gui/yarn.lock83
7 files changed, 250 insertions, 5 deletions
diff --git a/gui/flow-libs/tests.js.flow b/gui/flow-libs/tests.js.flow
index 8b6621a6de..e497690359 100644
--- a/gui/flow-libs/tests.js.flow
+++ b/gui/flow-libs/tests.js.flow
@@ -9,4 +9,5 @@ declare function afterEach((done: Function) => void): Promise<any> | void;
declare function beforeEach((done: Function) => void): Promise<any> | void;
declare var expect: $PropertyType<chai, 'expect'>;
+declare var sinon: any;
declare var spy: any;
diff --git a/gui/packages/desktop/package.json b/gui/packages/desktop/package.json
index bf86081b15..5ef54f94b8 100644
--- a/gui/packages/desktop/package.json
+++ b/gui/packages/desktop/package.json
@@ -57,7 +57,8 @@
"enzyme-adapter-react-16": "^1.1.0",
"mock-socket": "^8.0.2",
"npm-run-all": "^4.0.1",
- "rimraf": "^2.5.4"
+ "rimraf": "^2.5.4",
+ "sinon": "^6.3.5"
},
"scripts": {
"postinstall": "electron-builder install-app-deps",
diff --git a/gui/packages/desktop/src/renderer/app.js b/gui/packages/desktop/src/renderer/app.js
index d1bdb03724..1ef54ca4e9 100644
--- a/gui/packages/desktop/src/renderer/app.js
+++ b/gui/packages/desktop/src/renderer/app.js
@@ -662,7 +662,7 @@ type AccountFetchWatcher = {
// An account data cache that helps to throttle RPC requests to get_account_data and retain the
// cached value for 1 minute.
-class AccountDataCache {
+export class AccountDataCache {
_currentAccount: ?AccountToken;
_expiresAt: ?Date;
_fetchAttempt: number;
diff --git a/gui/packages/desktop/test/.eslintrc b/gui/packages/desktop/test/.eslintrc
index b68ec7a83e..5efd09aff1 100644
--- a/gui/packages/desktop/test/.eslintrc
+++ b/gui/packages/desktop/test/.eslintrc
@@ -9,6 +9,7 @@
},
"globals": {
"expect": true,
+ "sinon": true,
"spy": true
}
}
diff --git a/gui/packages/desktop/test/account-data-cache.spec.js b/gui/packages/desktop/test/account-data-cache.spec.js
new file mode 100644
index 0000000000..821de1af9b
--- /dev/null
+++ b/gui/packages/desktop/test/account-data-cache.spec.js
@@ -0,0 +1,163 @@
+// @flow
+
+import { AccountDataCache } from '../src/renderer/app';
+import type { AccountData } from '../src/renderer/lib/daemon-rpc';
+
+describe('AccountData cache', () => {
+ const dummyAccountToken = '9876543210';
+ const dummyAccountData: AccountData = {
+ expiry: new Date('2038-01-01').toISOString(),
+ };
+
+ let clock;
+
+ beforeEach(() => {
+ clock = sinon.useFakeTimers({ shouldAdvanceTime: true });
+ });
+
+ afterEach(() => {
+ clock.restore();
+ });
+
+ it('should notify when fetch succeeds on the first attempt', async () => {
+ const cache = new AccountDataCache((_) => Promise.resolve(dummyAccountData), (_) => {});
+
+ const watcher = new Promise((resolve, reject) => {
+ cache.fetch(dummyAccountToken, {
+ onFinish: () => resolve(),
+ onError: (_) => {
+ reject();
+ return 'stop';
+ },
+ });
+ });
+
+ return expect(watcher).to.eventually.be.fulfilled;
+ });
+
+ it('should notify when fetch fails on the first attempt', async () => {
+ const cache = new AccountDataCache((_) => Promise.reject(new Error('Fetch fail')), (_) => {});
+
+ const watcher = new Promise((resolve, reject) => {
+ cache.fetch(dummyAccountToken, {
+ onFinish: () => resolve(),
+ onError: (_) => {
+ reject();
+ return 'stop';
+ },
+ });
+ });
+
+ return expect(watcher).to.eventually.be.rejected;
+ });
+
+ it('should update when fetch succeeds on the first attempt', async () => {
+ const update = new Promise((resolve, reject) => {
+ const cache = new AccountDataCache((_) => Promise.resolve(dummyAccountData), () => resolve());
+
+ cache.fetch(dummyAccountToken, {
+ onFinish: spy(),
+ onError: (_) => {
+ reject();
+ return 'stop';
+ },
+ });
+ });
+
+ return expect(update).to.eventually.be.fulfilled;
+ });
+
+ it('should update when fetch succeeds on the second attempt', async () => {
+ const update = new Promise((resolve, reject) => {
+ let firstAttempt = true;
+ const fetch = (_) => {
+ if (firstAttempt) {
+ firstAttempt = false;
+ setTimeout(() => clock.tick(9000), 0);
+ return Promise.reject(new Error('First attempt fails'));
+ } else {
+ resolve();
+ return Promise.resolve(dummyAccountData);
+ }
+ };
+
+ const cache = new AccountDataCache(fetch, () => resolve());
+
+ cache.fetch(dummyAccountToken, {
+ onFinish: reject,
+ onError: spy((_) => 'retry'),
+ });
+ });
+
+ return expect(update).to.eventually.be.fulfilled;
+ });
+
+ it('should not retry if told to stop', async () => {
+ const update = new Promise((resolve, reject) => {
+ let firstAttempt = true;
+ const fetch = (_) => {
+ if (firstAttempt) {
+ firstAttempt = false;
+ setTimeout(() => clock.tick(14000), 0);
+ return Promise.reject(new Error('First attempt fails'));
+ } else {
+ reject();
+ return Promise.resolve(dummyAccountData);
+ }
+ };
+
+ const cache = new AccountDataCache(fetch, () => resolve());
+
+ setTimeout(resolve, 12000);
+
+ cache.fetch(dummyAccountToken, {
+ onFinish: spy(),
+ onError: spy((_) => 'stop'),
+ });
+ });
+
+ return expect(update).to.eventually.be.fulfilled;
+ });
+
+ it('should cancel first fetch', async () => {
+ const firstError = spy((_) => 'stop');
+ const secondSuccess = spy();
+
+ const update = new Promise((resolve, reject) => {
+ let firstAttempt = true;
+ const fetch = (_) => {
+ if (firstAttempt) {
+ firstAttempt = false;
+
+ cache.fetch('1231231231', {
+ onFinish: secondSuccess,
+ onError: (_) => {
+ reject();
+ return 'stop';
+ },
+ });
+
+ return new Promise((resolve) => setTimeout(() => resolve(dummyAccountData), 1000));
+ } else {
+ reject();
+ return Promise.resolve(dummyAccountData);
+ }
+ };
+
+ const cache = new AccountDataCache(fetch, () => resolve());
+
+ setTimeout(resolve, 12000);
+
+ cache.fetch(dummyAccountToken, {
+ onFinish: reject,
+ onError: firstError,
+ });
+ });
+
+ return expect(update).to.eventually.be.fulfilled.then(() => {
+ expect(firstError).to.have.been.called.once;
+ expect(secondSuccess).to.have.been.called.once;
+ return;
+ });
+ });
+});
diff --git a/gui/packages/desktop/test/setup/renderer.js b/gui/packages/desktop/test/setup/renderer.js
index a1bd11a4d8..c008567930 100644
--- a/gui/packages/desktop/test/setup/renderer.js
+++ b/gui/packages/desktop/test/setup/renderer.js
@@ -3,6 +3,7 @@ const Adapter = require('enzyme-adapter-react-16');
const chai = require('chai');
const spies = require('chai-spies');
const chaiAsPromised = require('chai-as-promised');
+const sinon = require('sinon');
chai.use(spies);
chai.use(chaiAsPromised);
@@ -12,4 +13,5 @@ Enzyme.configure({
});
global.expect = chai.expect;
+global.sinon = sinon;
global.spy = chai.spy;
diff --git a/gui/yarn.lock b/gui/yarn.lock
index cb1b4365c3..4df509c364 100644
--- a/gui/yarn.lock
+++ b/gui/yarn.lock
@@ -673,6 +673,32 @@
node-fetch "^2.1.1"
url-template "^2.0.8"
+"@sinonjs/commons@^1.0.2":
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.0.2.tgz#3e0ac737781627b8844257fadc3d803997d0526e"
+ integrity sha512-WR3dlgqJP4QNrLC4iXN/5/2WaLQQ0VijOOkmflqFGVJ6wLEpbSjo7c0ZeGIdtY8Crk7xBBp87sM6+Mkerz7alw==
+ dependencies:
+ type-detect "4.0.8"
+
+"@sinonjs/formatio@3.0.0", "@sinonjs/formatio@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.0.0.tgz#9d282d81030a03a03fa0c5ce31fd8786a4da311a"
+ integrity sha512-vdjoYLDptCgvtJs57ULshak3iJe4NW3sJ3g36xVDGff5AE8P30S6A093EIEPjdi2noGhfuNOEkbxt3J3awFW1w==
+ dependencies:
+ "@sinonjs/samsam" "2.1.0"
+
+"@sinonjs/samsam@2.1.0":
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-2.1.0.tgz#b8b8f5b819605bd63601a6ede459156880f38ea3"
+ integrity sha512-5x2kFgJYupaF1ns/RmharQ90lQkd2ELS8A9X0ymkAAdemYHGtI2KiUHG8nX2WU0T1qgnOU5YMqnBM2V7NUanNw==
+ dependencies:
+ array-from "^2.1.1"
+
+"@sinonjs/samsam@^2.1.2":
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-2.1.2.tgz#16947fce5f57258d01f1688fdc32723093c55d3f"
+ integrity sha512-ZwTHAlC9akprWDinwEPD4kOuwaYZlyMwVJIANsKNC3QVp0AHB04m7RnB4eqeWfgmxw8MGTzS9uMaw93Z3QcZbw==
+
"@types/chai@^4.1.4":
version "4.1.4"
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.4.tgz#5ca073b330d90b4066d6ce18f60d57f2084ce8ca"
@@ -1063,6 +1089,11 @@ array-find-index@^1.0.1:
resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=
+array-from@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195"
+ integrity sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=
+
array-includes@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d"
@@ -3125,7 +3156,7 @@ dev-ip@^1.0.1:
resolved "https://registry.yarnpkg.com/dev-ip/-/dev-ip-1.0.1.tgz#a76a3ed1855be7a012bb8ac16cb80f3c00dc28f0"
integrity sha1-p2o+0YVb56ASu4rBbLgPPADcKPA=
-diff@3.5.0, diff@^3.1.0, diff@^3.2.0:
+diff@3.5.0, diff@^3.1.0, diff@^3.2.0, diff@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
@@ -5369,6 +5400,11 @@ jsx-ast-utils@^2.0.1:
dependencies:
array-includes "^3.0.3"
+just-extend@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-3.0.0.tgz#cee004031eaabf6406da03a7b84e4fe9d78ef288"
+ integrity sha512-Fu3T6pKBuxjWT/p4DkqGHFRsysc8OauWr4ZRTY9dIx07Y9O0RkoR5jcv28aeD1vuAwhm3nLkDurwLXoALp4DpQ==
+
kind-of@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-1.1.0.tgz#140a3d2d41a36d2efcfa9377b62c24f8495a5c44"
@@ -5529,6 +5565,11 @@ lodash.flattendeep@^4.4.0:
resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=
+lodash.get@^4.4.2:
+ version "4.4.2"
+ resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
+ integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
+
lodash.isfinite@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz#fb89b65a9a80281833f0b7478b3a5104f898ebb3"
@@ -5569,6 +5610,11 @@ lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5,
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
integrity sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==
+lolex@^2.3.2, lolex@^2.7.5:
+ version "2.7.5"
+ resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.7.5.tgz#113001d56bfc7e02d56e36291cc5c413d1aa0733"
+ integrity sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==
+
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
@@ -6127,6 +6173,17 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4"
integrity sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==
+nise@^1.4.5:
+ version "1.4.5"
+ resolved "https://registry.yarnpkg.com/nise/-/nise-1.4.5.tgz#979a97a19c48d627bb53703726ae8d53ce8d4b3e"
+ integrity sha512-OHRVvdxKgwZELf2DTgsJEIA4MOq8XWvpSUzoOXyxJ2mY0mMENWC66+70AShLR2z05B1dzrzWlUQJmJERlOUpZw==
+ dependencies:
+ "@sinonjs/formatio" "3.0.0"
+ just-extend "^3.0.0"
+ lolex "^2.3.2"
+ path-to-regexp "^1.7.0"
+ text-encoding "^0.6.4"
+
node-fetch@^1.0.1, node-fetch@^1.3.3:
version "1.7.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
@@ -7868,6 +7925,21 @@ single-line-log@^1.1.2:
dependencies:
string-width "^1.0.1"
+sinon@^6.3.5:
+ version "6.3.5"
+ resolved "https://registry.yarnpkg.com/sinon/-/sinon-6.3.5.tgz#0f6d6a5b4ebaad1f6e8e019395542d1d02c144a0"
+ integrity sha512-xgoZ2gKjyVRcF08RrIQc+srnSyY1JDJtxu3Nsz07j1ffjgXoY6uPLf/qja6nDBZgzYYEovVkFryw2+KiZz11xQ==
+ dependencies:
+ "@sinonjs/commons" "^1.0.2"
+ "@sinonjs/formatio" "^3.0.0"
+ "@sinonjs/samsam" "^2.1.2"
+ diff "^3.5.0"
+ lodash.get "^4.4.2"
+ lolex "^2.7.5"
+ nise "^1.4.5"
+ supports-color "^5.5.0"
+ type-detect "^4.0.8"
+
slash@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
@@ -8263,7 +8335,7 @@ supports-color@^2.0.0:
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
-supports-color@^5.3.0:
+supports-color@^5.3.0, supports-color@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
@@ -8340,6 +8412,11 @@ term-size@^1.2.0:
dependencies:
execa "^0.7.0"
+text-encoding@^0.6.4:
+ version "0.6.4"
+ resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19"
+ integrity sha1-45mpgiV6J22uQou5KEXLcb3CbRk=
+
text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@@ -8558,7 +8635,7 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"
-type-detect@^4.0.0:
+type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==