From 7c3107f78d229d981afa85a00205febc3658140e Mon Sep 17 00:00:00 2001 From: busticated Date: Wed, 20 Jun 2018 15:34:50 -0700 Subject: [PATCH 1/5] add basic tests for cloud login command --- test/cmd/cloud.spec.js | 110 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 test/cmd/cloud.spec.js diff --git a/test/cmd/cloud.spec.js b/test/cmd/cloud.spec.js new file mode 100644 index 000000000..cab370ead --- /dev/null +++ b/test/cmd/cloud.spec.js @@ -0,0 +1,110 @@ +const proxyquire = require('proxyquire'); +const expect = require('chai').expect; +const sandbox = require('sinon').createSandbox(); + +let settings = { + clientId: 'CLITESTS', + username: 'test@example.com', + override: () => {} +}; + +function utilities() {} + +let api; +function ApiClient() { + return api || {}; +} + +let prompts = {}; + +const CloudCommands = proxyquire('../../src/cmd/cloud', { + '../../settings.js': settings, + '../lib/utilities.js': utilities, + '../lib/ApiClient.js': ApiClient, + '../lib/prompts': prompts +}); + + +describe('Cloud Commands', () => { + afterEach(() => { + sandbox.restore(); + api = {}; + }); + + it('prompts for username and password when they are not provided', withConsoleStubs(() => { + const cloud = new CloudCommands(); + const fakeToken = 'FAKE-ACCESS-TOKEN'; + const fakeCredentials = { username: 'test@example.com', password: 'fake-pw' }; + + cloud.newSpin = sandbox.stub(); + cloud.stopSpin = sandbox.stub(); + cloud.newSpin.returns({ start: sandbox.stub() }); + prompts.getCredentials = sandbox.stub(); + prompts.getCredentials.returns(fakeCredentials); + api = { login: sandbox.stub() }; + api.login.returns(fakeToken); + + return cloud.login() + .then(t => { + expect(prompts.getCredentials).to.have.property('callCount', 1); + expect(cloud.newSpin).to.have.property('callCount', 1); + expect(cloud.stopSpin).to.have.property('callCount', 1); + expect(t).to.equal(fakeToken); + }); + })); + + it('does not retry after 3 attemps', () => { + const message = "It seems we're having trouble with logging in."; + let error = simulateLoginAttempt(3); + + expect(error).to.have.property('message', message); + }); + + it('does not retry after 1 attemp when password is provided', () => { + const message = "It seems we're having trouble with logging in."; + let error = simulateLoginAttempt(1, { password: 'fake-password' }); + + expect(error).to.have.property('message', message); + }); + + function simulateLoginAttempt(tries, { username, password } = {}){ + const cloud = new CloudCommands(); + let error; + + try { + cloud.tries = tries; + cloud.login(username, password); + } catch (e) { + error = e; + } + + return error; + } + + // TODO (mirande): figure out a better approach. this allows us to verify + // log output without supressing mocha's success / error messages but is a + // bit awkward + function withConsoleStubs(fn){ + + return () => { + let result; + + sandbox.stub(process.stdout, 'write'); + sandbox.stub(process.stderr, 'write'); + + try { + result = fn(); + } catch (error) { + sandbox.restore(); + throw error; + } + + if (result && typeof result.finally === 'function'){ + return result.finally(() => sandbox.restore()); + } + sandbox.restore(); + return result; + }; + } +}); + From 3f6f5c0ac54f0055d7deb3291e0198799972b6d7 Mon Sep 17 00:00:00 2001 From: busticated Date: Thu, 21 Jun 2018 11:44:41 -0700 Subject: [PATCH 2/5] login command accepts username & password options --- src/cli/cloud.js | 19 ++++++++- src/cmd/cloud.js | 17 ++++---- test/cmd/cloud.spec.js | 94 +++++++++++++++++++++++++++++------------- 3 files changed, 90 insertions(+), 40 deletions(-) diff --git a/src/cli/cloud.js b/src/cli/cloud.js index 41d6f8f02..fb3b6358b 100644 --- a/src/cli/cloud.js +++ b/src/cli/cloud.js @@ -105,9 +105,26 @@ export default ({ commandProcessor, root }) => { }); commandProcessor.createCommand(cloud, 'login', 'Login to the cloud and store an access token locally', { + examples: { + '$0 $command': 'prompt for credentials and log in', + '$0 $command --username user@example.com --password test': 'log in with credentials provided on the command line' + }, + options: { + u: { + description: 'your username', + alias: 'username', + nargs: 1 + }, + p: { + description: 'your password', + alias: 'password', + nargs: 1 + } + }, handler: (args) => { const CloudCommands = require('../cmd/cloud'); - return new CloudCommands().login(); + const { username, password } = args; + return new CloudCommands().login(username, password); } }); diff --git a/src/cmd/cloud.js b/src/cmd/cloud.js index 19cf50dc8..44dc41d42 100644 --- a/src/cmd/cloud.js +++ b/src/cmd/cloud.js @@ -352,24 +352,19 @@ class CloudCommand { } login(username, password) { - if (this.tries >= (password ? 1 : 3)) { - throw new VError("It seems we're having trouble with logging in."); - } + const shouldRetry = !(username && password && !this.tries); return Promise.resolve().then(() => { - //prompt for creds - if (password) { + if (username && password) { return { username: username, password: password }; } return prompts.getCredentials(username, password); }).then(creds => { - //login to the server const api = new ApiClient(); username = creds.username; this.newSpin('Sending login details...').start(); return api.login(settings.clientId, creds.username, creds.password); }).then(accessToken => { - this.stopSpin(); console.log(arrow, 'Successfully completed login!'); settings.override(null, 'access_token', accessToken); @@ -380,15 +375,17 @@ class CloudCommand { return accessToken; }).catch((err) => { this.stopSpin(); - console.log(alert, "There was an error logging you in! Let's try again."); + console.log(alert, `There was an error logging you in! ${shouldRetry ? "Let's try again." : ''}`); console.error(alert, err); this.tries = (this.tries || 0) + 1; - return this.login(username); + if (shouldRetry && this.tries < 3){ + return this.login(username); + } + throw new VError("It seems we're having trouble with logging in."); }); } - doLogout(keep, password) { const api = new ApiClient(); diff --git a/test/cmd/cloud.spec.js b/test/cmd/cloud.spec.js index cab370ead..c856cb52d 100644 --- a/test/cmd/cloud.spec.js +++ b/test/cmd/cloud.spec.js @@ -26,16 +26,35 @@ const CloudCommands = proxyquire('../../src/cmd/cloud', { describe('Cloud Commands', () => { + let cloud, fakeToken, fakeCredentials; + + beforeEach(() => { + cloud = new CloudCommands(); + fakeToken = 'FAKE-ACCESS-TOKEN'; + fakeCredentials = { username: 'test@example.com', password: 'fake-pw' }; + }); + afterEach(() => { sandbox.restore(); api = {}; }); - it('prompts for username and password when they are not provided', withConsoleStubs(() => { - const cloud = new CloudCommands(); - const fakeToken = 'FAKE-ACCESS-TOKEN'; - const fakeCredentials = { username: 'test@example.com', password: 'fake-pw' }; + it('accepts username and password args', withConsoleStubs(() => { + api = { login: sandbox.stub() }; + api.login.returns(fakeToken); + + return cloud.login('username', 'password') + .then(t => { + expect(api.login).to.have.property('callCount', 1); + expect(api.login.firstCall).to.have.property('args').lengthOf(3); + expect(api.login.firstCall.args[0]).to.equal(settings.clientId); + expect(api.login.firstCall.args[1]).to.equal('username'); + expect(api.login.firstCall.args[2]).to.equal('password'); + expect(t).to.equal(fakeToken); + }); + })); + it('prompts for username and password when they are not provided', withConsoleStubs(() => { cloud.newSpin = sandbox.stub(); cloud.stopSpin = sandbox.stub(); cloud.newSpin.returns({ start: sandbox.stub() }); @@ -53,33 +72,50 @@ describe('Cloud Commands', () => { }); })); - it('does not retry after 3 attemps', () => { - const message = "It seems we're having trouble with logging in."; - let error = simulateLoginAttempt(3); - - expect(error).to.have.property('message', message); - }); - - it('does not retry after 1 attemp when password is provided', () => { - const message = "It seems we're having trouble with logging in."; - let error = simulateLoginAttempt(1, { password: 'fake-password' }); - - expect(error).to.have.property('message', message); - }); - - function simulateLoginAttempt(tries, { username, password } = {}){ - const cloud = new CloudCommands(); - let error; + it('does not retry after 3 attemps', withConsoleStubs(() => { + sandbox.spy(cloud, 'login'); + cloud.newSpin = sandbox.stub(); + cloud.stopSpin = sandbox.stub(); + cloud.newSpin.returns({ start: sandbox.stub() }); + prompts.getCredentials = sandbox.stub(); + prompts.getCredentials.returns(fakeCredentials); + api = { login: sandbox.stub() }; + api.login.throws(); - try { - cloud.tries = tries; - cloud.login(username, password); - } catch (e) { - error = e; - } + return cloud.login() + .then(() => { + throw new Error('expected promise to be rejected'); + }) + .catch(error => { + const stdoutArgs = process.stdout.write.args; + const lastLog = stdoutArgs[stdoutArgs.length - 1]; + + expect(cloud.login).to.have.property('callCount', 3); + expect(lastLog[0]).to.match(/There was an error logging you in! Let's try again.\n$/); + expect(process.stderr.write).to.have.property('callCount', 3); + expect(error).to.have.property('message', 'It seems we\'re having trouble with logging in.'); + }); + })); - return error; - } + it('does not retry when username & password args are provided', withConsoleStubs(() => { + sandbox.spy(cloud, 'login'); + api = { login: sandbox.stub() }; + api.login.throws(); + + return cloud.login('username', 'password') + .then(() => { + throw new Error('expected promise to be rejected'); + }) + .catch(error => { + const stdoutArgs = process.stdout.write.args; + const lastLog = stdoutArgs[stdoutArgs.length - 1]; + + expect(cloud.login).to.have.property('callCount', 1); + expect(lastLog[0]).to.match(/There was an error logging you in! \n$/); + expect(process.stderr.write).to.have.property('callCount', 1); + expect(error).to.have.property('message', 'It seems we\'re having trouble with logging in.'); + }); + })); // TODO (mirande): figure out a better approach. this allows us to verify // log output without supressing mocha's success / error messages but is a From 13dc94a3dad4dd39b41de4f8cd9056c2bd8ea47d Mon Sep 17 00:00:00 2001 From: busticated Date: Thu, 21 Jun 2018 14:07:21 -0700 Subject: [PATCH 3/5] clean up cloud login tests + extract common stub setup routine + add checks for settings.override() calls to verify updates to user data + organize module-level stubs --- test/cmd/cloud.spec.js | 98 +++++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 40 deletions(-) diff --git a/test/cmd/cloud.spec.js b/test/cmd/cloud.spec.js index c856cb52d..62a3a9cc4 100644 --- a/test/cmd/cloud.spec.js +++ b/test/cmd/cloud.spec.js @@ -2,84 +2,89 @@ const proxyquire = require('proxyquire'); const expect = require('chai').expect; const sandbox = require('sinon').createSandbox(); -let settings = { - clientId: 'CLITESTS', - username: 'test@example.com', - override: () => {} +const stubs = { + api: { + login: () => {} + }, + utils: {}, + prompts: { + getCredentials: () => {} + }, + settings: { + clientId: 'CLITESTS', + username: 'test@example.com', + override: () => {} + }, + ApiClient: function ApiClient(){ + return stubs.api; + } }; -function utilities() {} - -let api; -function ApiClient() { - return api || {}; -} - -let prompts = {}; - const CloudCommands = proxyquire('../../src/cmd/cloud', { - '../../settings.js': settings, - '../lib/utilities.js': utilities, - '../lib/ApiClient.js': ApiClient, - '../lib/prompts': prompts + '../../settings.js': stubs.settings, + '../lib/utilities.js': stubs.utils, + '../lib/ApiClient.js': stubs.ApiClient, + '../lib/prompts': stubs.prompts }); describe('Cloud Commands', () => { - let cloud, fakeToken, fakeCredentials; + let fakeToken, fakeCredentials; beforeEach(() => { - cloud = new CloudCommands(); fakeToken = 'FAKE-ACCESS-TOKEN'; fakeCredentials = { username: 'test@example.com', password: 'fake-pw' }; }); afterEach(() => { sandbox.restore(); - api = {}; }); it('accepts username and password args', withConsoleStubs(() => { - api = { login: sandbox.stub() }; + const { cloud, api, settings } = stubForLogin(new CloudCommands(), stubs); + const { username, password } = fakeCredentials; api.login.returns(fakeToken); - return cloud.login('username', 'password') + return cloud.login(username, password) .then(t => { + expect(t).to.equal(fakeToken); expect(api.login).to.have.property('callCount', 1); expect(api.login.firstCall).to.have.property('args').lengthOf(3); - expect(api.login.firstCall.args[0]).to.equal(settings.clientId); - expect(api.login.firstCall.args[1]).to.equal('username'); - expect(api.login.firstCall.args[2]).to.equal('password'); - expect(t).to.equal(fakeToken); + expect(api.login.firstCall.args[0]).to.equal(stubs.settings.clientId); + expect(api.login.firstCall.args[1]).to.equal(username); + expect(api.login.firstCall.args[2]).to.equal(password); + expect(settings.override).to.have.property('callCount', 2); + expect(settings.override.firstCall.args).to.eql([null, 'access_token', fakeToken]); + expect(settings.override.secondCall.args).to.eql([null, 'username', username]); }); })); it('prompts for username and password when they are not provided', withConsoleStubs(() => { - cloud.newSpin = sandbox.stub(); - cloud.stopSpin = sandbox.stub(); - cloud.newSpin.returns({ start: sandbox.stub() }); - prompts.getCredentials = sandbox.stub(); + const { cloud, api, prompts, settings } = stubForLogin(new CloudCommands(), stubs); + const { username, password } = fakeCredentials; prompts.getCredentials.returns(fakeCredentials); - api = { login: sandbox.stub() }; api.login.returns(fakeToken); return cloud.login() .then(t => { + expect(t).to.equal(fakeToken); expect(prompts.getCredentials).to.have.property('callCount', 1); expect(cloud.newSpin).to.have.property('callCount', 1); expect(cloud.stopSpin).to.have.property('callCount', 1); - expect(t).to.equal(fakeToken); + expect(api.login).to.have.property('callCount', 1); + expect(api.login.firstCall).to.have.property('args').lengthOf(3); + expect(api.login.firstCall.args[0]).to.equal(stubs.settings.clientId); + expect(api.login.firstCall.args[1]).to.equal(username); + expect(api.login.firstCall.args[2]).to.equal(password); + expect(settings.override).to.have.property('callCount', 2); + expect(settings.override.firstCall.args).to.eql([null, 'access_token', fakeToken]); + expect(settings.override.secondCall.args).to.eql([null, 'username', username]); }); })); it('does not retry after 3 attemps', withConsoleStubs(() => { - sandbox.spy(cloud, 'login'); - cloud.newSpin = sandbox.stub(); - cloud.stopSpin = sandbox.stub(); - cloud.newSpin.returns({ start: sandbox.stub() }); - prompts.getCredentials = sandbox.stub(); + const { cloud, api, prompts, settings } = stubForLogin(new CloudCommands(), stubs); prompts.getCredentials.returns(fakeCredentials); - api = { login: sandbox.stub() }; api.login.throws(); return cloud.login() @@ -91,6 +96,7 @@ describe('Cloud Commands', () => { const lastLog = stdoutArgs[stdoutArgs.length - 1]; expect(cloud.login).to.have.property('callCount', 3); + expect(settings.override).to.have.property('callCount', 0); expect(lastLog[0]).to.match(/There was an error logging you in! Let's try again.\n$/); expect(process.stderr.write).to.have.property('callCount', 3); expect(error).to.have.property('message', 'It seems we\'re having trouble with logging in.'); @@ -98,8 +104,7 @@ describe('Cloud Commands', () => { })); it('does not retry when username & password args are provided', withConsoleStubs(() => { - sandbox.spy(cloud, 'login'); - api = { login: sandbox.stub() }; + const { cloud, api, settings } = stubForLogin(new CloudCommands(), stubs); api.login.throws(); return cloud.login('username', 'password') @@ -111,12 +116,25 @@ describe('Cloud Commands', () => { const lastLog = stdoutArgs[stdoutArgs.length - 1]; expect(cloud.login).to.have.property('callCount', 1); + expect(settings.override).to.have.property('callCount', 0); expect(lastLog[0]).to.match(/There was an error logging you in! \n$/); expect(process.stderr.write).to.have.property('callCount', 1); expect(error).to.have.property('message', 'It seems we\'re having trouble with logging in.'); }); })); + function stubForLogin(cloud, stubs){ + const { api, prompts, settings } = stubs; + sandbox.spy(cloud, 'login'); + sandbox.stub(cloud, 'newSpin'); + sandbox.stub(cloud, 'stopSpin'); + cloud.newSpin.returns({ start: sandbox.stub() }); + sandbox.stub(api, 'login'); + sandbox.stub(prompts, 'getCredentials'); + sandbox.stub(settings, 'override'); + return { cloud, api, prompts, settings }; + } + // TODO (mirande): figure out a better approach. this allows us to verify // log output without supressing mocha's success / error messages but is a // bit awkward From ed293b10c3c3f169fee8530689e862cda321f177 Mon Sep 17 00:00:00 2001 From: busticated Date: Thu, 21 Jun 2018 16:33:19 -0700 Subject: [PATCH 4/5] cloud login command accepts token option --- src/cli/cloud.js | 13 +++++-- src/cmd/cloud.js | 83 ++++++++++++++++++++++++++---------------- src/lib/ApiClient.js | 34 ++++++++++++++++- test/cmd/cloud.spec.js | 45 +++++++++++++++++++++-- 4 files changed, 136 insertions(+), 39 deletions(-) diff --git a/src/cli/cloud.js b/src/cli/cloud.js index fb3b6358b..711264138 100644 --- a/src/cli/cloud.js +++ b/src/cli/cloud.js @@ -107,7 +107,9 @@ export default ({ commandProcessor, root }) => { commandProcessor.createCommand(cloud, 'login', 'Login to the cloud and store an access token locally', { examples: { '$0 $command': 'prompt for credentials and log in', - '$0 $command --username user@example.com --password test': 'log in with credentials provided on the command line' + '$0 $command --username user@example.com --password test': 'log in with credentials provided on the command line', + '$0 $command --token ': 'log in with an access token provided on the command line', + '$0 $command --token --username user@example.com': 'log in with an access token provided on the command line and set your username' }, options: { u: { @@ -119,12 +121,17 @@ export default ({ commandProcessor, root }) => { description: 'your password', alias: 'password', nargs: 1 + }, + t: { + description: '', + alias: 'token', + nargs: 1 } }, handler: (args) => { const CloudCommands = require('../cmd/cloud'); - const { username, password } = args; - return new CloudCommands().login(username, password); + const { username, password, token } = args; + return new CloudCommands().login(username, password, token); } }); diff --git a/src/cmd/cloud.js b/src/cmd/cloud.js index 44dc41d42..547461f9d 100644 --- a/src/cmd/cloud.js +++ b/src/cmd/cloud.js @@ -351,39 +351,60 @@ class CloudCommand { }); } - login(username, password) { - const shouldRetry = !(username && password && !this.tries); + login(username, password, token) { + const shouldRetry = !((username && password) || token && !this.tries); - return Promise.resolve().then(() => { - if (username && password) { - return { username: username, password: password }; - } - return prompts.getCredentials(username, password); - }).then(creds => { - const api = new ApiClient(); - username = creds.username; - this.newSpin('Sending login details...').start(); - return api.login(settings.clientId, creds.username, creds.password); - }).then(accessToken => { - this.stopSpin(); - console.log(arrow, 'Successfully completed login!'); - settings.override(null, 'access_token', accessToken); - if (username) { - settings.override(null, 'username', username); - } - this.tries = 0; - return accessToken; - }).catch((err) => { - this.stopSpin(); - console.log(alert, `There was an error logging you in! ${shouldRetry ? "Let's try again." : ''}`); - console.error(alert, err); - this.tries = (this.tries || 0) + 1; + return Promise.resolve() + .then(() => { + if (token){ + return { token, username, password }; + } + if (username && password){ + return { username, password }; + } + return prompts.getCredentials(username, password); + }) + .then(credentials => { + const { token, username, password } = credentials; + const api = new ApiClient(); - if (shouldRetry && this.tries < 3){ - return this.login(username); - } - throw new VError("It seems we're having trouble with logging in."); - }); + this.newSpin('Sending login details...').start(); + this._usernameProvided = username; + + if (token){ + return api.getUser(token).then(() => ({ token, username, password })); + } + return api.login(settings.clientId, username, password) + .then(token => ({ token, username, password })); + }) + .then(credentials => { + const { token, username } = credentials; + + this.stopSpin(); + console.log(arrow, 'Successfully completed login!'); + + settings.override(null, 'access_token', token); + + if (username) { + settings.override(null, 'username', username); + } + + this._usernameProvided = null; + this.tries = 0; + + return token; + }) + .catch(error => { + this.stopSpin(); + console.log(alert, `There was an error logging you in! ${shouldRetry ? "Let's try again." : ''}`); + console.error(alert, error); + this.tries = (this.tries || 0) + 1; + + if (shouldRetry && this.tries < 3){ + return this.login(this._usernameProvided); + } + throw new VError("It seems we're having trouble with logging in."); + }); } doLogout(keep, password) { diff --git a/src/lib/ApiClient.js b/src/lib/ApiClient.js index 384ab9024..c57f10322 100644 --- a/src/lib/ApiClient.js +++ b/src/lib/ApiClient.js @@ -75,7 +75,8 @@ class ApiClient { }); } ready() { - let hasToken = !!this._access_token; + let hasToken = this.hasToken(); + if (!hasToken) { console.log("You're not logged in. Please login using", chalk.bold.cyan('particle cloud login'), 'before using this command'); } @@ -89,6 +90,10 @@ class ApiClient { } } + hasToken() { + return !!this._access_token; + } + clearToken() { this._access_token = null; } @@ -148,6 +153,32 @@ class ApiClient { return dfd.promise; } + getUser(token){ + const { request, hasBadToken } = this; + token = token || this._access_token; + + return when.promise((resolve, reject) => { + request({ + uri: '/v1/user', + method: 'GET', + json: true, + headers: { + Authorization: `Bearer ${token}` + } + }, (error, response, body) => { + if (error) { + return reject(error); + } + if (hasBadToken(body)) { + // TODO (mirande): throw a real error and supress the logging + // done within hasBadToken(); + return reject('Invalid token'); + } + return resolve(body); + }); + }); + } + /** * Login and update the access token on this instance. Doesn't update the global settings. * Outputs failure to the console. @@ -1039,3 +1070,4 @@ class ApiClient { } module.exports = ApiClient; + diff --git a/test/cmd/cloud.spec.js b/test/cmd/cloud.spec.js index 62a3a9cc4..78b564ed7 100644 --- a/test/cmd/cloud.spec.js +++ b/test/cmd/cloud.spec.js @@ -4,7 +4,8 @@ const sandbox = require('sinon').createSandbox(); const stubs = { api: { - login: () => {} + login: () => {}, + getUser: () => {} }, utils: {}, prompts: { @@ -29,21 +30,56 @@ const CloudCommands = proxyquire('../../src/cmd/cloud', { describe('Cloud Commands', () => { - let fakeToken, fakeCredentials; + let fakeToken, fakeTokenPromise, fakeCredentials, fakeUser, fakeUserPromise; beforeEach(() => { fakeToken = 'FAKE-ACCESS-TOKEN'; + fakeTokenPromise = Promise.resolve(fakeToken); fakeCredentials = { username: 'test@example.com', password: 'fake-pw' }; + fakeUser = {}; + fakeUserPromise = Promise.resolve(fakeUser); }); afterEach(() => { sandbox.restore(); }); + it('accepts token arg', withConsoleStubs(() => { + const { cloud, api, settings } = stubForLogin(new CloudCommands(), stubs); + api.getUser.returns(fakeUserPromise); + + return cloud.login(null, null, fakeToken) + .then(t => { + expect(t).to.equal(fakeToken); + expect(api.login).to.have.property('callCount', 0); + expect(api.getUser).to.have.property('callCount', 1); + expect(api.getUser.firstCall.args).to.eql([fakeToken]); + expect(settings.override).to.have.property('callCount', 1); + expect(settings.override.firstCall.args).to.eql([null, 'access_token', fakeToken]); + }); + })); + + it('accepts token and username args', withConsoleStubs(() => { + const { cloud, api, settings } = stubForLogin(new CloudCommands(), stubs); + const { username } = fakeCredentials; + api.getUser.returns(fakeUserPromise); + + return cloud.login(username, null, fakeToken) + .then(t => { + expect(t).to.equal(fakeToken); + expect(api.login).to.have.property('callCount', 0); + expect(api.getUser).to.have.property('callCount', 1); + expect(api.getUser.firstCall.args).to.eql([fakeToken]); + expect(settings.override).to.have.property('callCount', 2); + expect(settings.override.firstCall.args).to.eql([null, 'access_token', fakeToken]); + expect(settings.override.secondCall.args).to.eql([null, 'username', username]); + }); + })); + it('accepts username and password args', withConsoleStubs(() => { const { cloud, api, settings } = stubForLogin(new CloudCommands(), stubs); const { username, password } = fakeCredentials; - api.login.returns(fakeToken); + api.login.returns(fakeTokenPromise); return cloud.login(username, password) .then(t => { @@ -63,7 +99,7 @@ describe('Cloud Commands', () => { const { cloud, api, prompts, settings } = stubForLogin(new CloudCommands(), stubs); const { username, password } = fakeCredentials; prompts.getCredentials.returns(fakeCredentials); - api.login.returns(fakeToken); + api.login.returns(fakeTokenPromise); return cloud.login() .then(t => { @@ -130,6 +166,7 @@ describe('Cloud Commands', () => { sandbox.stub(cloud, 'stopSpin'); cloud.newSpin.returns({ start: sandbox.stub() }); sandbox.stub(api, 'login'); + sandbox.stub(api, 'getUser'); sandbox.stub(prompts, 'getCredentials'); sandbox.stub(settings, 'override'); return { cloud, api, prompts, settings }; From 59993a34fe1116ea55c8b4800b786103a75e5e07 Mon Sep 17 00:00:00 2001 From: busticated Date: Fri, 22 Jun 2018 15:34:52 -0700 Subject: [PATCH 5/5] remove unused EarlyReturnError in cloud commands --- src/cmd/cloud.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/cmd/cloud.js b/src/cmd/cloud.js index 547461f9d..a5571c9ec 100644 --- a/src/cmd/cloud.js +++ b/src/cmd/cloud.js @@ -22,14 +22,6 @@ const chalk = require('chalk'); const arrow = chalk.green('>'); const alert = chalk.yellow('!'); -class EarlyReturnError extends VError { - constructor(...args) { - super(...args); - Error.captureStackTrace(this, this.constructor); - this.name = this.constructor.name; - } -} - // Use known platforms and add shortcuts const PLATFORMS = extend(utilities.knownPlatforms(), { 'c': 0, @@ -165,11 +157,6 @@ class CloudCommand { return this._doFlash({ api, deviceId, fileMapping, version }); }); }).catch((err) => { - if (VError.hasCauseWithName(err, EarlyReturnError.name)) { - console.log(err.message); - return; - } - throw new VError(ensureError(err), 'Flash device failed'); }); }