From 55ee77e3780f0091ac08e7502bf1651bf018715b Mon Sep 17 00:00:00 2001 From: johnnyman727 Date: Wed, 11 Nov 2015 09:31:32 -0800 Subject: [PATCH] Fixing key test issues --- bin/tessel-2.js | 2 +- lib/controller.js | 442 +++++++++++++++++++------------------- lib/tessel/provision.js | 42 ++-- test/unit/bin-tessel-2.js | 7 +- test/unit/controller.js | 146 +++++++------ test/unit/key-path.js | 20 ++ test/unit/tessel.js | 151 +++++++------ 7 files changed, 440 insertions(+), 370 deletions(-) create mode 100644 test/unit/key-path.js diff --git a/bin/tessel-2.js b/bin/tessel-2.js index 1fd6e776..1dd7450b 100755 --- a/bin/tessel-2.js +++ b/bin/tessel-2.js @@ -21,7 +21,7 @@ function makeCommand(commandName) { metavar: 'PRIVATEKEY', abbr: 'i', default: TESSEL_AUTH_KEY, - help: 'RSA key for authorization with your Tessel' + help: 'SSH key for authorization with your Tessel' }) .option('name', { metavar: 'NAME', diff --git a/lib/controller.js b/lib/controller.js index 06307c0e..e73c1143 100644 --- a/lib/controller.js +++ b/lib/controller.js @@ -18,254 +18,252 @@ var responses = { }; Tessel.list = function(opts) { + return provision.setDefaultKey(opts.key) + .then(function() { - return new Promise(function(resolve, reject) { - - // check whether --key is set - provision.setDefaultKey(opts); + return new Promise(function(resolve, reject) { - // Grab all attached Tessels - logs.info('Searching for nearby Tessels...'); + // Grab all attached Tessels + logs.info('Searching for nearby Tessels...'); - // Keep a list of all the Tessels we discovered - var foundTessels = []; + // Keep a list of all the Tessels we discovered + var foundTessels = []; - // Options for Tessel discovery - var seekerOpts = { - timeout: opts.timeout * 1000, - usb: opts.usb, - lan: opts.lan, - authorized: undefined - }; + // Options for Tessel discovery + var seekerOpts = { + timeout: opts.timeout * 1000, + usb: opts.usb, + lan: opts.lan, + authorized: undefined + }; - // Start looking for Tessels - var seeker = new discover.TesselSeeker().start(seekerOpts); - var noTessels = opts.authorized ? - responses.noAuth : - responses.auth; + // Start looking for Tessels + var seeker = new discover.TesselSeeker().start(seekerOpts); + var noTessels = opts.authorized ? + responses.noAuth : + responses.auth; - // When a Tessel is found - seeker.on('tessel', function displayResults(tessel) { + // When a Tessel is found + seeker.on('tessel', function displayResults(tessel) { - var note = ''; + var note = ''; - // Add it to our array - foundTessels.push(tessel); + // Add it to our array + foundTessels.push(tessel); - // Add a note if the user isn't authorized to use it yet - if (tessel.connection.connectionType === 'LAN' && !tessel.connection.authorized) { - note = '(USB connect and run `t2 provision` to authorize)'; - } + // Add a note if the user isn't authorized to use it yet + if (tessel.connection.connectionType === 'LAN' && !tessel.connection.authorized) { + note = '(USB connect and run `t2 provision` to authorize)'; + } - // Print out details... - logs.basic(sprintf('\t%s\t%s\t%s', tessel.name, tessel.connection.connectionType, note)); - }); + // Print out details... + logs.basic(sprintf('\t%s\t%s\t%s', tessel.name, tessel.connection.connectionType, note)); + }); - // Called after CTRL+C or timeout - seeker.once('end', function stopSearch() { - // If there were no Tessels found - if (foundTessels.length === 0) { - // Report the sadness - return reject(noTessels); - } else if (foundTessels.length === 1) { - // Close all opened connections and resolve - controller.closeTesselConnections(foundTessels) - .then(resolve); - } - // If we have only one Tessel or two Tessels with the same name (just USB and LAN) - else if (foundTessels.length === 1 || - (foundTessels.length === 2 && foundTessels[0].name === foundTessels[1].name)) { - // Close all opened connections and resolve - controller.closeTesselConnections(foundTessels) - .then(resolve); - } - // Otherwise - else { - logs.info('Multiple Tessels found.'); - // Figure out which Tessel will be selected - return controller.runHeuristics(opts, foundTessels) - .then(function logSelected(tessel) { - // Report that selected Tessel to the user - logs.info('Will default to %s.', tessel.name); - }) - .catch(function(err) { - if (!(err instanceof controller.HeuristicAmbiguityError)) { - return controller.closeTesselConnections(foundTessels) - .then(reject.bind(this, err)); - } - }) - .then(function() { - // Helpful instructions on how to switch - logs.info('Set default Tessel with environment variable (e.g. "export TESSEL=bulbasaur") or use the --name flag.'); + // Called after CTRL+C or timeout + seeker.once('end', function stopSearch() { + // If there were no Tessels found + if (foundTessels.length === 0) { + // Report the sadness + return reject(noTessels); + } else if (foundTessels.length === 1) { // Close all opened connections and resolve controller.closeTesselConnections(foundTessels) .then(resolve); - }); - } - }); + } + // If we have only one Tessel or two Tessels with the same name (just USB and LAN) + else if (foundTessels.length === 1 || + (foundTessels.length === 2 && foundTessels[0].name === foundTessels[1].name)) { + // Close all opened connections and resolve + controller.closeTesselConnections(foundTessels) + .then(resolve); + } + // Otherwise + else { + logs.info('Multiple Tessels found.'); + // Figure out which Tessel will be selected + return controller.runHeuristics(opts, foundTessels) + .then(function logSelected(tessel) { + // Report that selected Tessel to the user + logs.info('Will default to %s.', tessel.name); + }) + .catch(function(err) { + if (!(err instanceof controller.HeuristicAmbiguityError)) { + return controller.closeTesselConnections(foundTessels) + .then(reject.bind(this, err)); + } + }) + .then(function() { + // Helpful instructions on how to switch + logs.info('Set default Tessel with environment variable (e.g. "export TESSEL=bulbasaur") or use the --name flag.'); + // Close all opened connections and resolve + controller.closeTesselConnections(foundTessels) + .then(resolve); + }); + } + }); - // Stop the search if CTRL+C is hit - process.once('SIGINT', function() { - // If the seeker exists (it should) - if (seeker !== undefined) { - // Stop looking for more Tessels - seeker.stop(); - } + // Stop the search if CTRL+C is hit + process.once('SIGINT', function() { + // If the seeker exists (it should) + if (seeker !== undefined) { + // Stop looking for more Tessels + seeker.stop(); + } + }); + }); }); - }); }; Tessel.get = function(opts) { - return new Promise(function(resolve, reject) { - // check whether --key is set - provision.setDefaultKey(opts); - - logs.info('Looking for your Tessel...'); - // Collection variable as more Tessels are found - var tessels = []; - - // Store the amount of time to look for Tessel in seconds - var seekerOpts = { - timeout: (opts.timeout || 2) * 1000, - usb: opts.usb, - lan: opts.lan, - authorized: true - }; - - if (opts.authorized !== undefined) { - seekerOpts.authorized = opts.authorized; - } + // check whether --key is set + return provision.setDefaultKey(opts.key) + .then(function() { + return new Promise(function(resolve, reject) { + logs.info('Looking for your Tessel...'); + // Collection variable as more Tessels are found + var tessels = []; + + // Store the amount of time to look for Tessel in seconds + var seekerOpts = { + timeout: (opts.timeout || 2) * 1000, + usb: opts.usb, + lan: opts.lan, + authorized: true + }; + + if (opts.authorized !== undefined) { + seekerOpts.authorized = opts.authorized; + } + + // Create a seeker object and start detecting any Tessels + var seeker = new discover.TesselSeeker().start(seekerOpts); + var noTessels = opts.authorized ? + responses.noAuth : + responses.auth; + + function searchComplete() { + // If we found no Tessels + if (tessels.length === 0) { + // Report it + return reject(noTessels); + } + // The name match for a given Tessel happens upon discovery, not at + // the completion of discovery. So if we got to this point, no Tessel + // was found with that name + else if (opts.name !== undefined) { + return reject('No Tessel found by the name ' + opts.name); + } + // If there was only one Tessel + else if (tessels.length === 1) { + // Return it immediately + logAndFinish(tessels[0]); + } + // Otherwise + else { + // Combine the same Tessels into one object + return controller.reconcileTessels(tessels) + .then(function(reconciledTessels) { + tessels = reconciledTessels; + // Run the heuristics to pick which Tessel to use + return controller.runHeuristics(opts, tessels) + .then(function finalSection(tessel) { + return logAndFinish(tessel); + }) + .catch(function(err) { + if (err instanceof controller.HeuristicAmbiguityError) { + var map = {}; + + // Open up an interactive menu for the user to choose + return controller.menu({ + prefix: colors.grey('INFO '), + prompt: { + name: 'selected', + type: 'list', + message: 'Which Tessel do want to use?', + choices: tessels.map(function(tessel, i) { + var isLAN = !!tessel.lanConnection; + var isAuthorized = isLAN && tessel.lanConnection.authorized; + var authorization = isAuthorized ? '' : '(not authorized)'; + var display = sprintf( + '\t%s\t%s\t%s', + tessel.name, + tessel.connection.connectionType, + authorization + ); + + // Map displayed name to tessel index + map[display] = i; + + return display; + }) + }, + translate: function(answer) { + return tessels[map[answer.selected]]; + } + }).then(function(tessel) { + if (!tessel) { + return controller.closeTesselConnections(tessels) + .then(function() { + return reject('No Tessel selected, mission aborted!'); + }); + } else { + // Log we found it and return it to the caller + return logAndFinish(tessel); + } + }); - // Create a seeker object and start detecting any Tessels - var seeker = new discover.TesselSeeker().start(seekerOpts); - var noTessels = opts.authorized ? - responses.noAuth : - responses.auth; - - function searchComplete() { - // If we found no Tessels - if (tessels.length === 0) { - // Report it - reject(noTessels); - return; - } - // The name match for a given Tessel happens upon discovery, not at - // the completion of discovery. So if we got to this point, no Tessel - // was found with that name - else if (opts.name !== undefined) { - return reject('No Tessel found by the name ' + opts.name); - } - // If there was only one Tessel - else if (tessels.length === 1) { - // Return it immediately - logAndFinish(tessels[0]); - } - // Otherwise - else { - // Combine the same Tessels into one object - return controller.reconcileTessels(tessels) - .then(function(reconciledTessels) { - tessels = reconciledTessels; - // Run the heuristics to pick which Tessel to use - return controller.runHeuristics(opts, tessels) - .then(function finalSection(tessel) { - return logAndFinish(tessel); - }) - .catch(function(err) { - if (err instanceof controller.HeuristicAmbiguityError) { - var map = {}; - - // Open up an interactive menu for the user to choose - return controller.menu({ - prefix: colors.grey('INFO '), - prompt: { - name: 'selected', - type: 'list', - message: 'Which Tessel do want to use?', - choices: tessels.map(function(tessel, i) { - var isLAN = !!tessel.lanConnection; - var isAuthorized = isLAN && tessel.lanConnection.authorized; - var authorization = isAuthorized ? '' : '(not authorized)'; - var display = sprintf( - '\t%s\t%s\t%s', - tessel.name, - tessel.connection.connectionType, - authorization - ); - - // Map displayed name to tessel index - map[display] = i; - - return display; - }) - }, - translate: function(answer) { - return tessels[map[answer.selected]]; - } - }).then(function(tessel) { - if (!tessel) { - return controller.closeTesselConnections(tessels) - .then(function() { - reject('No Tessel selected, mission aborted!'); - }); } else { - // Log we found it and return it to the caller - return logAndFinish(tessel); + controller.closeTesselConnections(tessels) + .then(reject.bind(this, err)); } }); - - } else { - controller.closeTesselConnections(tessels) - .then(reject.bind(this, err)); - } }); - }); - } - } - - // When we find Tessels - seeker.on('tessel', function(tessel) { - // Check if this name matches the provided option (if any) - // This speeds up development by immediately ending the search - if (opts.name && opts.name === tessel.name) { - // Remove this listener because we don't need to search for the Tessel - seeker.removeListener('end', searchComplete); - // Stop searching - seeker.stop(); - // Send this Tessel back to the caller - logAndFinish(tessel); - } - // Otherwise - else { - // Store this Tessel with the others - tessels.push(tessel); - } - }); - - seeker.once('end', searchComplete); - - // Accesses `tessels` in closure - function logAndFinish(tessel) { - // The Tessels that we won't be using should have their connections closed - var connectionsToClose = tessels; + } + } - if (tessel) { - logs.info(sprintf('Connected to %s over %s', tessel.name, tessel.connection.connectionType)); - connectionsToClose.splice(tessels.indexOf(tessel), 1); + // When we find Tessels + seeker.on('tessel', function(tessel) { + // Check if this name matches the provided option (if any) + // This speeds up development by immediately ending the search + if (opts.name && opts.name === tessel.name) { + // Remove this listener because we don't need to search for the Tessel + seeker.removeListener('end', searchComplete); + // Stop searching + seeker.stop(); + // Send this Tessel back to the caller + logAndFinish(tessel); + } + // Otherwise + else { + // Store this Tessel with the others + tessels.push(tessel); + } + }); - controller.closeTesselConnections(connectionsToClose) - .then(function() { - return resolve(tessel); - }); - } else { - logs.info('Please specify a Tessel by name [--name ]'); - controller.closeTesselConnections(connectionsToClose) - .then(function() { - reject('Multiple possible Tessel connections found.'); - }); - } - } - }); + seeker.once('end', searchComplete); + + // Accesses `tessels` in closure + function logAndFinish(tessel) { + // The Tessels that we won't be using should have their connections closed + var connectionsToClose = tessels; + if (tessel) { + logs.info(sprintf('Connected to %s over %s', tessel.name, tessel.connection.connectionType)); + connectionsToClose.splice(tessels.indexOf(tessel), 1); + controller.closeTesselConnections(connectionsToClose) + .then(function() { + return resolve(tessel); + }); + } else { + logs.info('Please specify a Tessel by name [--name ]'); + controller.closeTesselConnections(connectionsToClose) + .then(function() { + return reject('Multiple possible Tessel connections found.'); + }); + } + } + }); + }); }; /* diff --git a/lib/tessel/provision.js b/lib/tessel/provision.js index efcf0bbe..f5a7c39d 100644 --- a/lib/tessel/provision.js +++ b/lib/tessel/provision.js @@ -14,14 +14,14 @@ var authPath = path.join(osenv.home(), '.tessel'); var idrsa = 'id_rsa'; var authKey = path.join(authPath, idrsa); var remoteAuthFile = '/etc/dropbear/authorized_keys'; -var debug = require('debug')('provision'); Object.defineProperty(Tessel, 'TESSEL_AUTH_KEY', { get: function() { - return filepath; + return authKey; }, set: function(value) { - filepath = value; + authKey = value; + authPath = path.dirname(authKey); } }); @@ -153,25 +153,25 @@ actions.checkAuthFileExists = function(tessel, authFile) { return tessel.simpleExec(commands.ensureFileExists(authFile)); }; -actions.setDefaultKey = function(opts) { +actions.setDefaultKey = function(keyPath) { + return new Promise(function(resolve, reject) { + if (!keyPath) { + return reject('No key provided to set as default.'); + } - if (opts.key && typeof opts.key === 'string') { - debug('Using --key ' + opts.key + ' after checking existence'); - filepath = opts.key; - } else { - logs.err('No valid path for private key'); - process.exit(); - } - try { - if (fs.statSync(filepath).isFile() && - fs.statSync(filepath + '.pub').isFile()) { - return; + if (typeof keyPath !== 'string') { + return reject('SSH key path must be a string type.'); } - } catch (e) { - debug('Keyfiles not found', e); - logs.err('Could not find ' + filepath + ' and its public key (.pub)'); - process.exit(); - } + + if (!fs.statSync(keyPath).isFile() || + !fs.statSync(keyPath + '.pub').isFile()) { + return reject(keyPath + ' does not contain valid public and private SSH keys.'); + } + + Tessel.TESSEL_AUTH_PATH = keyPath; + + return resolve(); + }); }; function checkIfKeyInFile(tessel, authFile, pubKey) { @@ -219,8 +219,8 @@ function AlreadyAuthenticatedError() { util.inherits(AlreadyAuthenticatedError, Error); actions.AlreadyAuthenticatedError = AlreadyAuthenticatedError; -module.exports.TESSEL_AUTH_KEY = filepath; module.exports = actions; +module.exports.TESSEL_AUTH_KEY = authKey; if (global.IS_TEST_ENV) { module.exports = actions; diff --git a/test/unit/bin-tessel-2.js b/test/unit/bin-tessel-2.js index 0cab62e2..cbd67f26 100644 --- a/test/unit/bin-tessel-2.js +++ b/test/unit/bin-tessel-2.js @@ -2,6 +2,7 @@ var sinon = require('sinon'); var cli = require('../../bin/tessel-2'); var controller = require('../../lib/controller'); var logs = require('../../lib/logs'); +var TESSEL_AUTH_KEY = require('../../lib/tessel/provision.js').TESSEL_AUTH_KEY; // If the defaults are intentionally changed in bin-tessel-2, @@ -33,7 +34,8 @@ var defaults = { help: 'Use USB connection', name: 'usb', string: '--usb', - } + }, + key: TESSEL_AUTH_KEY }; exports['Tessel (cli: makeCommand)'] = { @@ -141,7 +143,8 @@ exports['Tessel (cli: update)'] = { 0: 'update', version: 42, _: ['update'], - timeout: 5 + timeout: 5, + key: TESSEL_AUTH_KEY }); cli(['update', '--list', ' ']); diff --git a/test/unit/controller.js b/test/unit/controller.js index 8fec3dd7..f56ff4f0 100644 --- a/test/unit/controller.js +++ b/test/unit/controller.js @@ -6,7 +6,7 @@ var Seeker = require('../../lib/discover.js'); var util = require('util'); var EventEmitter = require('events').EventEmitter; var logs = require('../../lib/logs'); - +var TESSEL_AUTH_KEY = require('../../lib/tessel/provision.js').TESSEL_AUTH_KEY; function newTessel(options) { var tessel = new Tessel({ @@ -367,10 +367,6 @@ exports['Tessel.list'] = { this.seeker = this.sandbox.stub(Seeker, 'TesselSeeker', function Seeker() { this.start = function(opts) { self.activeSeeker = this; - this.msg = { - noAuth: 'No Authorized Tessels Found.', - auth: 'No Tessels Found.' - }; if (opts.timeout && typeof opts.timeout === 'number') { setTimeout(this.stop, opts.timeout); } @@ -390,6 +386,11 @@ exports['Tessel.list'] = { this.closeTesselConnections = this.sandbox.spy(controller, 'closeTesselConnections'); this.runHeuristics = this.sandbox.spy(controller, 'runHeuristics'); + this.standardOpts = { + timeout: 0.01, + key: TESSEL_AUTH_KEY + }; + done(); }, @@ -407,9 +408,7 @@ exports['Tessel.list'] = { type: 'USB' }); - Tessel.list({ - timeout: 0.01 - }) + Tessel.list(this.standardOpts) .then(function() { test.equal(this.runHeuristics.callCount, 0); test.equal(this.closeTesselConnections.callCount, 1); @@ -418,7 +417,11 @@ exports['Tessel.list'] = { test.done(); }.bind(this)); - this.activeSeeker.emit('tessel', a); + // We must emit the Tessel sometime after list is called + // but before the seeker stops searching + setTimeout(function() { + this.activeSeeker.emit('tessel', a); + }.bind(this), this.standardOpts.timeout - 1); }, oneLANTessel: function(test) { @@ -430,9 +433,7 @@ exports['Tessel.list'] = { type: 'LAN' }); - Tessel.list({ - timeout: 0.01 - }) + Tessel.list(this.standardOpts) .then(function() { test.equal(this.runHeuristics.callCount, 0); test.equal(this.closeTesselConnections.callCount, 1); @@ -441,7 +442,11 @@ exports['Tessel.list'] = { test.done(); }.bind(this)); - this.activeSeeker.emit('tessel', a); + // We must emit the Tessel sometime after list is called + // but before the seeker stops searching + setTimeout(function() { + this.activeSeeker.emit('tessel', a); + }.bind(this), this.standardOpts.timeout - 1); }, oneTesselTwoConnections: function(test) { @@ -460,9 +465,7 @@ exports['Tessel.list'] = { name: 'samesies' }); - Tessel.list({ - timeout: 0.01 - }) + Tessel.list(this.standardOpts) .then(function() { test.equal(this.runHeuristics.callCount, 0); test.equal(this.closeTesselConnections.callCount, 1); @@ -472,8 +475,12 @@ exports['Tessel.list'] = { test.done(); }.bind(this)); - this.activeSeeker.emit('tessel', usb); - this.activeSeeker.emit('tessel', lan); + // We must emit the Tessel sometime after list is called + // but before the seeker stops searching + setTimeout(function() { + this.activeSeeker.emit('tessel', usb); + this.activeSeeker.emit('tessel', lan); + }.bind(this), this.standardOpts.timeout - 1); }, multipleDifferentTessels: function(test) { @@ -492,9 +499,7 @@ exports['Tessel.list'] = { name: 'bar' }); - Tessel.list({ - timeout: 0.01 - }) + Tessel.list(this.standardOpts) .then(function() { test.equal(this.runHeuristics.callCount, 1); test.equal(this.closeTesselConnections.callCount, 1); @@ -504,8 +509,12 @@ exports['Tessel.list'] = { test.done(); }.bind(this)); - this.activeSeeker.emit('tessel', usb); - this.activeSeeker.emit('tessel', lan); + // We must emit the Tessel sometime after list is called + // but before the seeker stops searching + setTimeout(function() { + this.activeSeeker.emit('tessel', usb); + this.activeSeeker.emit('tessel', lan); + }.bind(this), this.standardOpts.timeout - 1); }, }; @@ -529,20 +538,23 @@ exports['Tessel.get'] = { return this; }; this.stop = function() { - this.emit('end'); + self.activeSeeker.emit('end'); return this; }.bind(this); }); util.inherits(this.seeker, EventEmitter); - this.logsWarn = this.sandbox.stub(logs, 'warn', function() {}); this.logsInfo = this.sandbox.stub(logs, 'info', function() {}); this.logsBasic = this.sandbox.stub(logs, 'basic', function() {}); - this.closeTesselConnections = this.sandbox.stub(controller, 'closeTesselConnections'); this.reconcileTessels = this.sandbox.spy(controller, 'reconcileTessels'); this.runHeuristics = this.sandbox.spy(controller, 'runHeuristics'); + this.standardOpts = { + timeout: 0.01, + key: TESSEL_AUTH_KEY + }; + done(); }, @@ -563,10 +575,12 @@ exports['Tessel.get'] = { name: 'the_name' }); - Tessel.get({ - timeout: 0.01, - name: 'the_name' - }) + var customOpts = {}; + customOpts.timeout = this.standardOpts.timeout; + customOpts.key = this.standardOpts.key; + customOpts.name = 'the_name'; + + Tessel.get(customOpts) .then(function() { test.equal(this.reconcileTessels.callCount, 0); test.equal(this.runHeuristics.callCount, 0); @@ -577,7 +591,11 @@ exports['Tessel.get'] = { test.done(); }.bind(this)); - this.activeSeeker.emit('tessel', a); + // We must emit the Tessel sometime after list is called + // but before the seeker stops searching + setTimeout(function() { + this.activeSeeker.emit('tessel', a); + }.bind(this), this.standardOpts.timeout - 1); }, oneUnNamedTessel: function(test) { @@ -592,9 +610,7 @@ exports['Tessel.get'] = { name: 'the_name' }); - Tessel.get({ - timeout: 0.01 - }) + Tessel.get(this.standardOpts) .then(function() { test.equal(this.reconcileTessels.callCount, 0); test.equal(this.runHeuristics.callCount, 0); @@ -605,7 +621,11 @@ exports['Tessel.get'] = { test.done(); }.bind(this)); - this.activeSeeker.emit('tessel', a); + // We must emit the Tessel sometime after list is called + // but before the seeker stops searching + setTimeout(function() { + this.activeSeeker.emit('tessel', a); + }.bind(this), this.standardOpts.timeout - 1); }, oneUnamedTesselTwoConnections: function(test) { @@ -626,9 +646,7 @@ exports['Tessel.get'] = { name: 'samesies' }); - Tessel.get({ - timeout: 0.01 - }) + Tessel.get(this.standardOpts) .then(function() { test.equal(this.reconcileTessels.callCount, 1); test.equal(this.runHeuristics.callCount, 1); @@ -639,17 +657,19 @@ exports['Tessel.get'] = { test.done(); }.bind(this)); - this.activeSeeker.emit('tessel', usb); - this.activeSeeker.emit('tessel', lan); + // We must emit the Tessel sometime after list is called + // but before the seeker stops searching + setTimeout(function() { + this.activeSeeker.emit('tessel', usb); + this.activeSeeker.emit('tessel', lan); + }.bind(this), this.standardOpts.timeout - 1); + }, standardCommandNoTessels: function(test) { test.expect(2); - var opts = { - timeout: 0.01 - }; - controller.standardTesselCommand(opts, function() { + controller.standardTesselCommand(this.standardOpts, function() { // Doesn't matter what this function does b/c Tessel.get will fail return Promise.resolve(); }) @@ -666,10 +686,7 @@ exports['Tessel.get'] = { controller.closeTesselConnections.returns(Promise.resolve()); var optionalValue = 'testValue'; - var opts = { - timeout: 0.01 - }; - controller.standardTesselCommand(opts, function(tessel) { + controller.standardTesselCommand(this.standardOpts, function(tessel) { // Make sure we have been given the tessel that was emitted test.deepEqual(tessel, usb); // Finish the command @@ -693,7 +710,11 @@ exports['Tessel.get'] = { name: 'USBTessel' }); - this.activeSeeker.emit('tessel', usb); + // We must emit the Tessel sometime after list is called + // but before the seeker stops searching + setTimeout(function() { + this.activeSeeker.emit('tessel', usb); + }.bind(this), this.standardOpts.timeout - 1); }, standardCommandFailed: function(test) { @@ -701,13 +722,9 @@ exports['Tessel.get'] = { controller.closeTesselConnections.returns(Promise.resolve()); - var opts = { - timeout: 0.01 - }; - var errMessage = 'This command failed'; - controller.standardTesselCommand(opts, function(tessel) { + controller.standardTesselCommand(this.standardOpts, function(tessel) { // Make sure we have been given the tessel that was emitted test.deepEqual(tessel, usb); // Finish the command @@ -731,7 +748,11 @@ exports['Tessel.get'] = { name: 'USBTessel' }); - this.activeSeeker.emit('tessel', usb); + // We must emit the Tessel sometime after list is called + // but before the seeker stops searching + setTimeout(function() { + this.activeSeeker.emit('tessel', usb); + }.bind(this), this.standardOpts.timeout - 1); }, standardCommandSigInt: function(test) { @@ -739,11 +760,7 @@ exports['Tessel.get'] = { controller.closeTesselConnections.returns(Promise.resolve()); - var opts = { - timeout: 0.01 - }; - - controller.standardTesselCommand(opts, function() { + controller.standardTesselCommand(this.standardOpts, function() { // This command doesn't do anything. It won't resolve so if we do get // to the next clause, it's due to the sigint }) @@ -763,8 +780,11 @@ exports['Tessel.get'] = { name: 'USBTessel' }); - this.activeSeeker.emit('tessel', usb); - - process.emit('SIGINT'); + // We must emit the Tessel sometime after list is called + // but before the seeker stops searching + setTimeout(function() { + this.activeSeeker.emit('tessel', usb); + process.emit('SIGINT'); + }.bind(this), this.standardOpts.timeout - 1); }, }; diff --git a/test/unit/key-path.js b/test/unit/key-path.js new file mode 100644 index 00000000..0bf69495 --- /dev/null +++ b/test/unit/key-path.js @@ -0,0 +1,20 @@ +var sinon = require('sinon'); +var logs = require('../../lib/logs'); + +exports['provision.setDefaultKey'] = { + setUp: function(done) { + this.sandbox = sinon.sandbox.create(); + this.logsWarn = this.sandbox.stub(logs, 'warn', function() {}); + this.logsInfo = this.sandbox.stub(logs, 'info', function() {}); + this.logsBasic = this.sandbox.stub(logs, 'basic', function() {}); + + done(); + }, + + tearDown: function(done) { + this.sandbox.restore(); + done(); + }, + + // Create tests here... +}; diff --git a/test/unit/tessel.js b/test/unit/tessel.js index 8b73aaaf..e4f59945 100644 --- a/test/unit/tessel.js +++ b/test/unit/tessel.js @@ -9,6 +9,7 @@ var logs = require('../../lib/logs'); var controller = require('../../lib/controller'); var lan = require('../../lib/lan_connection'); var TesselSimulator = require('../common/tessel-simulator'); +var TESSEL_AUTH_KEY = require('../../lib/tessel/provision.js').TESSEL_AUTH_KEY; exports['Tessel (get)'] = { @@ -38,6 +39,11 @@ exports['Tessel (get)'] = { return Promise.resolve(); }); + this.standardOpts = { + timeout: 0.01, + key: TESSEL_AUTH_KEY + }; + done(); }, @@ -48,9 +54,7 @@ exports['Tessel (get)'] = { infoOutput: function(test) { test.expect(1); - Tessel.get({ - timeout: 0.01 - }) + Tessel.get(this.standardOpts) .catch(function() { test.equal(this.logsInfo.firstCall.args[0], 'Looking for your Tessel...'); test.done(); @@ -59,9 +63,7 @@ exports['Tessel (get)'] = { noTessels: function(test) { // Try to get Tessels but return none - Tessel.get({ - timeout: 0.01 - }) + Tessel.get(this.standardOpts) // If Tessels were returned, this test should fail because we're // not emitting any Tessels to the seeker .then(function(tessels) { @@ -76,11 +78,14 @@ exports['Tessel (get)'] = { noTesselWithName: function(test) { var testConnectionType = 'USB'; var testName = 'Does_Exist'; + + var customOpts = { + timeout: this.standardOpts.timeout, + key: this.standardOpts.key, + name: 'Does_Not_Exist' + }; // Try to get Tessels but return none - Tessel.get({ - name: 'Does_Not_Exist', - timeout: 0.01 - }) + Tessel.get(customOpts) // If .then(function(tessels) { test.equal(tessels, false, 'Somehow Tessels were returned'); @@ -95,16 +100,16 @@ exports['Tessel (get)'] = { }); tessel.name = testName; - this.activeSeeker.emit('tessel', tessel); + setImmediate(function() { + this.activeSeeker.emit('tessel', tessel); + }.bind(this)); }, oneUSB: function(test) { var testConnectionType = 'USB'; var testName = 'testTessel'; // Try to get Tessels but return none - Tessel.get({ - timeout: 0.01 - }) + Tessel.get(this.standardOpts) // If .then(function(tessel) { test.equal(tessel.name, testName); @@ -119,15 +124,16 @@ exports['Tessel (get)'] = { connectionType: testConnectionType }); tessel.name = testName; - this.activeSeeker.emit('tessel', tessel); + + setImmediate(function() { + this.activeSeeker.emit('tessel', tessel); + }.bind(this)); }, multipleUSBNoName: function(test) { test.expect(2); // Try to get Tessels but return none - Tessel.get({ - timeout: 0.01 - }) + Tessel.get(this.standardOpts) .catch(function(reason) { test.equal(reason, 'No Tessel selected, mission aborted!'); test.equal(this.menu.calledOnce, 1); @@ -150,17 +156,22 @@ exports['Tessel (get)'] = { a.name = 'a'; b.name = 'b'; - this.activeSeeker.emit('tessel', a); - this.activeSeeker.emit('tessel', b); + setImmediate(function() { + this.activeSeeker.emit('tessel', a); + this.activeSeeker.emit('tessel', b); + }.bind(this)); }, multipleUSBHasName: function(test) { test.expect(1); - Tessel.get({ - timeout: 0.01, - name: 'a' - }) + var customOpts = { + timeout: this.standardOpts.timeout, + key: this.standardOpts.key, + name: 'a' + }; + + Tessel.get(customOpts) .then(function(tessel) { test.equal(tessel.name, 'a'); test.done(); @@ -185,17 +196,17 @@ exports['Tessel (get)'] = { a.name = 'a'; b.name = 'b'; - this.activeSeeker.emit('tessel', a); - this.activeSeeker.emit('tessel', b); + setImmediate(function() { + this.activeSeeker.emit('tessel', a); + this.activeSeeker.emit('tessel', b); + }.bind(this)); }, usbAndNonAuthorizedLANSameTessel: function(test) { test.expect(2); // Try to get Tessels but return none - Tessel.get({ - timeout: 0.05, - }) + Tessel.get(this.standardOpts) .then(function(tessel) { test.equal(tessel.name, 'a'); test.equal(tessel.connection.connectionType, 'USB'); @@ -225,16 +236,16 @@ exports['Tessel (get)'] = { usb.name = 'a'; lan.name = 'a'; - this.activeSeeker.emit('tessel', usb); - this.activeSeeker.emit('tessel', lan); + setImmediate(function() { + this.activeSeeker.emit('tessel', usb); + this.activeSeeker.emit('tessel', lan); + }.bind(this)); }, usbAndNonAuthorizedLANSameTesselLANFirst: function(test) { test.expect(2); // Try to get Tessels but return none - Tessel.get({ - timeout: 0.05, - }) + Tessel.get(this.standardOpts) .then(function(tessel) { test.equal(tessel.name, 'a'); test.equal(tessel.connection.connectionType, 'USB'); @@ -261,20 +272,21 @@ exports['Tessel (get)'] = { usb.name = 'a'; lan.name = 'a'; - // "Detect" the lan first. This order is intentional - // 1 - this.activeSeeker.emit('tessel', lan); - // 2 - this.activeSeeker.emit('tessel', usb); + setImmediate(function() { + // "Detect" the lan first. This order is intentional + // 1 + this.activeSeeker.emit('tessel', lan); + // 2 + this.activeSeeker.emit('tessel', usb); + }.bind(this)); + }, usbAndAuthorizedLANSameTessel: function(test) { test.expect(2); // Try to get Tessels but return none - Tessel.get({ - timeout: 0.05, - }) + Tessel.get(this.standardOpts) .then(function(tessel) { test.equal(tessel.name, 'a'); test.equal(tessel.connection.connectionType, 'LAN'); @@ -303,16 +315,16 @@ exports['Tessel (get)'] = { lan.connection.authorized = true; - this.activeSeeker.emit('tessel', usb); - this.activeSeeker.emit('tessel', lan); + setImmediate(function() { + this.activeSeeker.emit('tessel', usb); + this.activeSeeker.emit('tessel', lan); + }.bind(this)); }, multipleLANNoName: function(test) { test.expect(2); // Try to get Tessels but return none - Tessel.get({ - timeout: 0.01 - }) + Tessel.get(this.standardOpts) .catch(function(reason) { test.equal(reason, 'No Tessel selected, mission aborted!'); test.equal(this.menu.calledOnce, 1); @@ -339,17 +351,22 @@ exports['Tessel (get)'] = { a.name = 'a'; b.name = 'b'; - this.activeSeeker.emit('tessel', a); - this.activeSeeker.emit('tessel', b); + setImmediate(function() { + this.activeSeeker.emit('tessel', a); + this.activeSeeker.emit('tessel', b); + }.bind(this)); }, multipleLANHasName: function(test) { test.expect(1); - Tessel.get({ - timeout: 0.01, - name: 'a' - }) + var customOpts = { + timeout: this.standardOpts.timeout, + key: this.standardOpts.key, + name: 'a' + }; + + Tessel.get(customOpts) .then(function(tessel) { test.equal(tessel.name, 'a'); a.close(); @@ -378,8 +395,10 @@ exports['Tessel (get)'] = { a.name = 'a'; b.name = 'b'; - this.activeSeeker.emit('tessel', a); - this.activeSeeker.emit('tessel', b); + setImmediate(function() { + this.activeSeeker.emit('tessel', a); + this.activeSeeker.emit('tessel', b); + }.bind(this)); }, }; @@ -414,6 +433,11 @@ exports['Tessel (get); filter: unauthorized'] = { return Promise.resolve(); }); + this.standardOpts = { + timeout: 0.01, + key: TESSEL_AUTH_KEY + }; + done(); }, @@ -425,10 +449,13 @@ exports['Tessel (get); filter: unauthorized'] = { unauthorizedLANDoesNotSurface: function(test) { test.expect(1); - Tessel.get({ - timeout: 1, - authorized: true, - }) + var customOpts = { + timeout: this.standardOpts.timeout, + key: this.standardOpts.key, + authorized: true + }; + + Tessel.get(customOpts) .then(function() { test.fail(); }.bind(this)) @@ -442,7 +469,9 @@ exports['Tessel (get); filter: unauthorized'] = { authorized: false }); - this.activeSeeker.lanScan.emit('connection', lan.connection); - this.activeSeeker.emit('end'); + setImmediate(function() { + this.activeSeeker.lanScan.emit('connection', lan.connection); + this.activeSeeker.emit('end'); + }.bind(this)); }, };