Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / remoting / webapp / crd / js / host_controller_unittest.js
blob9b4aa77eca32a168c64266ddd5a4d31dab08900d
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 /**
6  * @fileoverview
7  * Unit tests for host_controller.js.
8  */
10 (function() {
12 'use strict';
14 /** @type {remoting.HostController} */
15 var controller;
17 /** @type {sinon.Mock} */
18 var hostListMock = null;
20 /** @type {sinon.TestStub} */
21 var generateUuidStub;
23 /** @type {remoting.MockHostDaemonFacade} */
24 var mockHostDaemonFacade;
26 /** @type {sinon.TestStub} */
27 var hostDaemonFacadeCtorStub;
29 /** @type {remoting.MockSignalStrategy} */
30 var mockSignalStrategy;
32 /** @type {sinon.TestStub} */
33 var signalStrategyCreateStub;
35 /** @type {sinon.TestStub|Function} */
36 var signalStrategyConnectStub;
38 var FAKE_HOST_PIN = '<FAKE_HOST_PIN>';
39 var FAKE_PIN_HASH = '<FAKE_PIN_HASH>';
40 var FAKE_NEW_HOST_PIN = '<FAKE_NEW_HOST_PIN>';
41 var FAKE_USER_EMAIL = '<FAKE_USER_EMAIL>';
42 var FAKE_XMPP_LOGIN = '<FAKE_XMPP_LOGIN>';
43 var FAKE_USER_NAME = '<FAKE_USER_NAME>';
44 var FAKE_HOST_ID = '0bad0bad-0bad-0bad-0bad-0bad0bad0bad';
45 var FAKE_DAEMON_VERSION = '1.2.3.4';
46 var FAKE_HOST_NAME = '<FAKE_HOST_NAME>';
47 var FAKE_PUBLIC_KEY = '<FAKE_PUBLIC_KEY>';
48 var FAKE_PRIVATE_KEY = '<FAKE_PRIVATE_KEY>';
49 var FAKE_AUTH_CODE = '<FAKE_AUTH_CODE>';
50 var FAKE_REFRESH_TOKEN = '<FAKE_REFRESH_TOKEN>';
51 var FAKE_HOST_CLIENT_ID = '<FAKE_HOST_CLIENT_ID>';
52 var FAKE_CLIENT_JID = '<FAKE_CLIENT_JID>';
53 var FAKE_CLIENT_BASE_JID = '<FAKE_CLIENT_BASE_JID>';
54 var FAKE_IDENTITY_TOKEN = '<FAKE_IDENTITY_TOKEN>';
56 /** @type {sinon.Spy|Function} */
57 var getCredentialsFromAuthCodeSpy;
59 /** @type {sinon.Spy|Function} */
60 var getPinHashSpy;
62 /** @type {sinon.Spy|Function} */
63 var startDaemonSpy;
65 /** @type {sinon.Spy|Function} */
66 var updateDaemonConfigSpy;
68 /** @type {sinon.Spy} */
69 var unregisterHostByIdSpy;
71 /** @type {sinon.Spy} */
72 var onLocalHostStartedSpy;
74 /** @type {remoting.MockHostListApi} */
75 var mockHostListApi;
77 QUnit.module('host_controller', {
78   beforeEach: function(/** QUnit.Assert */ assert) {
79     chromeMocks.identity.mock$setToken(FAKE_IDENTITY_TOKEN);
80     remoting.settings = new remoting.Settings();
81     remoting.identity = new remoting.Identity();
82     mockHostListApi = new remoting.MockHostListApi;
83     mockHostListApi.authCodeFromRegister = FAKE_AUTH_CODE;
84     mockHostListApi.emailFromRegister = '';
85     remoting.HostListApi.setInstance(mockHostListApi);
86     base.debug.assert(remoting.oauth2 === null);
87     remoting.oauth2 = new remoting.OAuth2();
88     base.debug.assert(remoting.hostList === null);
89     remoting.hostList = /** @type {remoting.HostList} */
90         (Object.create(remoting.HostList.prototype));
92     // When the HostList's unregisterHostById method is called, make
93     // sure the argument is correct.
94     unregisterHostByIdSpy =
95         sinon.stub(remoting.hostList, 'unregisterHostById', function(
96             /** string */ hostId, /** Function */ onDone) {
97           assert.equal(hostId, FAKE_HOST_ID);
98           if (onDone) {
99             onDone();
100           }
101         });
103     // When the HostList's onLocalHostStarted method is called, make
104     // sure the arguments are correct.
105     onLocalHostStartedSpy =
106         sinon.stub(
107             remoting.hostList, 'onLocalHostStarted', function(
108                 /** string */ hostName,
109                 /** string */ newHostId,
110                 /** string */ publicKey) {
111               assert.equal(hostName, FAKE_HOST_NAME);
112               assert.equal(newHostId, FAKE_HOST_ID);
113               assert.equal(publicKey, FAKE_PUBLIC_KEY);
114             });
116     mockSignalStrategy = new remoting.MockSignalStrategy(
117         FAKE_CLIENT_JID + '/extra_junk',
118         remoting.SignalStrategy.Type.XMPP);
119     signalStrategyCreateStub = sinon.stub(remoting.SignalStrategy, 'create');
120     signalStrategyCreateStub.returns(mockSignalStrategy);
122     hostDaemonFacadeCtorStub = sinon.stub(remoting, 'HostDaemonFacade');
123     mockHostDaemonFacade = new remoting.MockHostDaemonFacade();
124     hostDaemonFacadeCtorStub.returns(mockHostDaemonFacade);
125     generateUuidStub = sinon.stub(base, 'generateUuid');
126     generateUuidStub.returns(FAKE_HOST_ID);
127     getCredentialsFromAuthCodeSpy = sinon.spy(
128         mockHostDaemonFacade, 'getCredentialsFromAuthCode');
129     getPinHashSpy = sinon.spy(mockHostDaemonFacade, 'getPinHash');
130     startDaemonSpy = sinon.spy(mockHostDaemonFacade, 'startDaemon');
131     updateDaemonConfigSpy =
132         sinon.spy(mockHostDaemonFacade, 'updateDaemonConfig');
134     // Set up successful responses from mockHostDaemonFacade.
135     // Individual tests override these values to create errors.
136     mockHostDaemonFacade.features =
137         [remoting.HostController.Feature.OAUTH_CLIENT];
138     mockHostDaemonFacade.daemonVersion = FAKE_DAEMON_VERSION;
139     mockHostDaemonFacade.hostName = FAKE_HOST_NAME;
140     mockHostDaemonFacade.privateKey = FAKE_PRIVATE_KEY;
141     mockHostDaemonFacade.publicKey = FAKE_PUBLIC_KEY;
142     mockHostDaemonFacade.hostClientId = FAKE_HOST_CLIENT_ID;
143     mockHostDaemonFacade.userEmail = FAKE_XMPP_LOGIN;
144     mockHostDaemonFacade.refreshToken = FAKE_REFRESH_TOKEN;
145     mockHostDaemonFacade.pinHashFunc = fakePinHashFunc;
146     mockHostDaemonFacade.startDaemonResult =
147         remoting.HostController.AsyncResult.OK;
148     mockHostDaemonFacade.stopDaemonResult =
149         remoting.HostController.AsyncResult.OK;
150     mockHostDaemonFacade.daemonConfig = {
151       host_id: FAKE_HOST_ID,
152       xmpp_login: FAKE_XMPP_LOGIN
153     };
154     mockHostDaemonFacade.updateDaemonConfigResult =
155         remoting.HostController.AsyncResult.OK;
156     mockHostDaemonFacade.daemonState =
157         remoting.HostController.State.STARTED;
159     sinon.stub(remoting.identity, 'getEmail').returns(
160         Promise.resolve(FAKE_USER_EMAIL));
161     sinon.stub(remoting.oauth2, 'getRefreshToken').returns(
162         FAKE_REFRESH_TOKEN);
164     controller = new remoting.HostController();
165   },
167   afterEach: function() {
168     controller = null;
169     getCredentialsFromAuthCodeSpy.restore();
170     generateUuidStub.restore();
171     hostDaemonFacadeCtorStub.restore();
172     signalStrategyCreateStub.restore();
173     remoting.hostList = null;
174     remoting.oauth2 = null;
175     remoting.HostListApi.setInstance(null);
176     remoting.identity = null;
177   }
181  * @param {string} hostId
182  * @param {string} pin
183  * @return {string}
184  */
185 function fakePinHashFunc(hostId, pin) {
186   return '<FAKE_PIN:' + hostId + ':' + pin + '>';
190  * @param {boolean} successful
191  */
192 function stubSignalStrategyConnect(successful) {
193   sinon.stub(mockSignalStrategy, 'connect', function() {
194     Promise.resolve().then(function() {
195       mockSignalStrategy.setStateForTesting(
196           successful ?
197             remoting.SignalStrategy.State.CONNECTED :
198             remoting.SignalStrategy.State.FAILED);
199     });
200   });
203 // Check what happens when the HostDaemonFacade's getHostName method
204 // fails.
205 QUnit.test('start with getHostName failure', function(assert) {
206   mockHostDaemonFacade.hostName = null;
207   return controller.start(FAKE_HOST_PIN, true).then(function() {
208     throw 'test failed';
209   }, function(/** remoting.Error */ e) {
210     assert.equal(e.getDetail(), 'getHostName');
211     assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
212     assert.equal(unregisterHostByIdSpy.callCount, 0);
213     assert.equal(onLocalHostStartedSpy.callCount, 0);
214     assert.equal(startDaemonSpy.callCount, 0);
215   });
218 // Check what happens when the HostDaemonFacade's generateKeyPair
219 // method fails.
220 QUnit.test('start with generateKeyPair failure', function(assert) {
221   mockHostDaemonFacade.publicKey = null;
222   mockHostDaemonFacade.privateKey = null;
223   return controller.start(FAKE_HOST_PIN, true).then(function() {
224     throw 'test failed';
225   }, function(/** remoting.Error */ e) {
226     assert.equal(e.getDetail(), 'generateKeyPair');
227     assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
228     assert.equal(unregisterHostByIdSpy.callCount, 0);
229     assert.equal(onLocalHostStartedSpy.callCount, 0);
230     assert.equal(startDaemonSpy.callCount, 0);
231   });
234 // Check what happens when the HostDaemonFacade's getHostClientId
235 // method fails.
236 QUnit.test('start with getHostClientId failure', function(assert) {
237   mockHostDaemonFacade.hostClientId = null;
238   return controller.start(FAKE_HOST_PIN, true).then(function() {
239     throw 'test failed';
240   }, function(/** remoting.Error */ e) {
241     assert.equal(e.getDetail(), 'getHostClientId');
242     assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
243     assert.equal(unregisterHostByIdSpy.callCount, 0);
244     assert.equal(onLocalHostStartedSpy.callCount, 0);
245     assert.equal(startDaemonSpy.callCount, 0);
246   });
249 // Check what happens when the registry returns an HTTP when we try to
250 // register a host.
251 QUnit.test('start with host registration failure', function(assert) {
252   mockHostListApi.authCodeFromRegister = null;
253   return controller.start(FAKE_HOST_PIN, true).then(function() {
254     throw 'test failed';
255   }, function(/** remoting.Error */ e) {
256     assert.equal(e.getTag(), remoting.Error.Tag.REGISTRATION_FAILED);
257     assert.equal(unregisterHostByIdSpy.callCount, 0);
258     assert.equal(onLocalHostStartedSpy.callCount, 0);
259     assert.equal(startDaemonSpy.callCount, 0);
260   });
263 // Check what happens when the HostDaemonFacade's
264 // getCredentialsFromAuthCode method fails.
265 QUnit.test('start with getCredentialsFromAuthCode failure', function(assert) {
266   mockHostDaemonFacade.useEmail = null;
267   mockHostDaemonFacade.refreshToken = null;
268   return controller.start(FAKE_HOST_PIN, true).then(function() {
269     throw 'test failed';
270   }, function(/** remoting.Error */ e) {
271     assert.equal(e.getDetail(), 'getCredentialsFromAuthCode');
272     assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
273     assert.equal(getCredentialsFromAuthCodeSpy.callCount, 1);
274     assert.equal(onLocalHostStartedSpy.callCount, 0);
275     assert.equal(startDaemonSpy.callCount, 0);
276   });
279 // Check what happens when the SignalStrategy fails to connect.
280 QUnit.test('start with signalStrategy failure', function(assert) {
281   stubSignalStrategyConnect(false);
282   return controller.start(FAKE_HOST_PIN, true).then(function() {
283     throw 'test failed';
284   }, function(/** remoting.Error */ e) {
285     assert.equal(e.getDetail(), 'setStateForTesting');
286     assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
287     assert.equal(unregisterHostByIdSpy.callCount, 1);
288   });
291 // Check what happens when the HostDaemonFacade's startDaemon method
292 // fails and calls its onError argument.
293 // TODO(jrw): Should startDaemon even have an onError callback?
294 QUnit.test('start with startDaemon failure', function(assert) {
295   stubSignalStrategyConnect(true);
296   mockHostDaemonFacade.startDaemonResult = null;
297   return controller.start(FAKE_HOST_PIN, true).then(function() {
298     throw 'test failed';
299   }, function(/** remoting.Error */ e) {
300     assert.equal(e.getDetail(), 'startDaemon');
301     assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
302     assert.equal(unregisterHostByIdSpy.callCount, 1);
303     assert.equal(unregisterHostByIdSpy.args[0][0], FAKE_HOST_ID);
304     assert.equal(onLocalHostStartedSpy.callCount, 0);
305   });
308 // Check what happens when the HostDaemonFacade's startDaemon method
309 // calls is onDone method with a CANCELLED error code.
310 QUnit.test('start with startDaemon cancelled', function(assert) {
311   stubSignalStrategyConnect(true);
312   mockHostDaemonFacade.startDaemonResult =
313       remoting.HostController.AsyncResult.CANCELLED;
314   return controller.start(FAKE_HOST_PIN, true).then(function() {
315     throw 'test failed';
316   }, function(/** remoting.Error */ e) {
317     assert.equal(e.getTag(), remoting.Error.Tag.CANCELLED);
318     assert.equal(unregisterHostByIdSpy.callCount, 1);
319     assert.equal(unregisterHostByIdSpy.args[0][0], FAKE_HOST_ID);
320     assert.equal(onLocalHostStartedSpy.callCount, 0);
321   });
324 // Check what happens when the HostDaemonFacade's startDaemon method
325 // calls is onDone method with an async error code.
326 QUnit.test('start with startDaemon returning failure code', function(assert) {
327   stubSignalStrategyConnect(true);
328   mockHostDaemonFacade.startDaemonResult =
329       remoting.HostController.AsyncResult.FAILED;
330   return controller.start(FAKE_HOST_PIN, true).then(function() {
331     throw 'test failed';
332   }, function(/** remoting.Error */ e) {
333     assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
334     assert.equal(unregisterHostByIdSpy.callCount, 1);
335     assert.equal(onLocalHostStartedSpy.callCount, 0);
336   });
339 // Check what happens when the entire host registration process
340 // succeeds.
341 [false, true].forEach(function(/** boolean */ consent) {
342   QUnit.test('start with consent=' + consent, function(assert) {
343     /** @const */
344     var fakePinHash = fakePinHashFunc(FAKE_HOST_ID, FAKE_HOST_PIN);
345     stubSignalStrategyConnect(true);
346     return controller.start(FAKE_HOST_PIN, consent).then(function() {
347       assert.equal(getCredentialsFromAuthCodeSpy.callCount, 1);
348       assert.deepEqual(
349           getCredentialsFromAuthCodeSpy.args[0][0],
350           FAKE_AUTH_CODE);
351       assert.equal(getPinHashSpy.callCount, 1);
352       assert.deepEqual(
353           getPinHashSpy.args[0].slice(0, 2),
354           [FAKE_HOST_ID, FAKE_HOST_PIN]);
355       assert.equal(unregisterHostByIdSpy.callCount, 0);
356       assert.equal(onLocalHostStartedSpy.callCount, 1);
357       assert.equal(startDaemonSpy.callCount, 1);
358       assert.deepEqual(
359           startDaemonSpy.args[0].slice(0, 2),
360           [{
361             xmpp_login: FAKE_XMPP_LOGIN,
362             oauth_refresh_token: FAKE_REFRESH_TOKEN,
363             host_owner: FAKE_CLIENT_JID.toLowerCase(),
364             host_owner_email: FAKE_USER_EMAIL,
365             host_id: FAKE_HOST_ID,
366             host_name: FAKE_HOST_NAME,
367             host_secret_hash: fakePinHash,
368             private_key: FAKE_PRIVATE_KEY
369           }, consent]);
370     });
371   });
374 // Check what happens when stopDaemon calls onError.
375 // TODO(jrw): Should stopDaemon even have an onError callback?
376 QUnit.test('stop with stopDaemon failure', function(assert) {
377   mockHostDaemonFacade.stopDaemonResult = null;
378   return new Promise(function(resolve, reject) {
379     controller.stop(function() {
380       reject('test failed');
381     }, function(/** remoting.Error */ e) {
382       assert.equal(e.getDetail(), 'stopDaemon');
383       // TODO(jrw): Is it really desirable to leave the host registered?
384       assert.equal(unregisterHostByIdSpy.callCount, 0);
385       resolve(null);
386     });
387   });
390 // Check what happens when stopDaemon returns FAILED.
391 QUnit.test('stop with stopDaemon cancelled', function(assert) {
392   mockHostDaemonFacade.stopDaemonResult =
393       remoting.HostController.AsyncResult.FAILED;
394   return new Promise(function(resolve, reject) {
395     controller.stop(function() {
396       reject('test failed');
397     }, function(/** remoting.Error */ e) {
398       // TODO(jrw): Is it really desirable to leave the host registered?
399       assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
400       assert.equal(unregisterHostByIdSpy.callCount, 0);
401       resolve(null);
402     });
403   });
406 // Check what happens when stopDaemon returns CANCELLED.
407 QUnit.test('stop with stopDaemon cancelled', function(assert) {
408   mockHostDaemonFacade.stopDaemonResult =
409       remoting.HostController.AsyncResult.CANCELLED;
410   return new Promise(function(resolve, reject) {
411     controller.stop(function() {
412       reject('test failed');
413     }, function(/** remoting.Error */ e) {
414       assert.equal(e.getTag(), remoting.Error.Tag.CANCELLED);
415       assert.equal(unregisterHostByIdSpy.callCount, 0);
416       resolve(null);
417     });
418   });
421 // Check what happens when stopDaemon succeeds.
422 QUnit.test('stop succeeds', function(assert) {
423   sinon.stub(controller, 'getLocalHostId').callsArgWith(0, FAKE_HOST_ID);
424   return new Promise(function(resolve, reject) {
425     controller.stop(function() {
426       assert.equal(unregisterHostByIdSpy.callCount, 1);
427       assert.equal(unregisterHostByIdSpy.args[0][0], FAKE_HOST_ID);
428       resolve(null);
429     }, reject);
430   });
433 // Check what happens when the host reports an invalid config.
434 QUnit.test('updatePin where config is invalid', function(assert) {
435   mockHostDaemonFacade.daemonConfig = {};
436   return new Promise(function(resolve, reject) {
437     controller.updatePin(FAKE_NEW_HOST_PIN, function() {
438       reject('test failed');
439     }, function(/** remoting.Error */ e) {
440       assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
441       resolve(null);
442     });
443   });
446 // Check what happens when getDaemonConfig calls onError.
447 QUnit.test('updatePin where getDaemonConfig fails', function(assert) {
448   mockHostDaemonFacade.daemonConfig = null;
449   return new Promise(function(resolve, reject) {
450     controller.updatePin(FAKE_NEW_HOST_PIN, function() {
451       reject('test failed');
452     }, function(/** remoting.Error */ e) {
453       assert.equal(e.getDetail(), 'getDaemonConfig');
454       assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
455       resolve(null);
456     });
457   });
460 // Check what happens when updateDaemonConfig calls onError.
461 // TODO(jrw): Should updateDaemonConfig even have an onError callback?
462 QUnit.test('updatePin where updateDaemonConfig calls onError', function(
463     assert) {
464   mockHostDaemonFacade.updateDaemonConfigResult = null;
465   return new Promise(function(resolve, reject) {
466     controller.updatePin(FAKE_NEW_HOST_PIN, function() {
467       reject('test failed');
468     }, function(/** remoting.Error */ e) {
469       assert.equal(e.getDetail(), 'updateDaemonConfig');
470       assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
471       resolve(null);
472     });
473   });
476 // Check what happens when updateDaemonConfig returns CANCELLED.
477 QUnit.test('updatePin where updateDaemonConfig is cancelled', function(
478     assert) {
479   mockHostDaemonFacade.updateDaemonConfigResult =
480       remoting.HostController.AsyncResult.CANCELLED;
481   return new Promise(function(resolve, reject) {
482     controller.updatePin(FAKE_NEW_HOST_PIN, function() {
483       reject('test failed');
484     }, function(/** remoting.Error */ e) {
485       assert.equal(e.getTag(), remoting.Error.Tag.CANCELLED);
486       resolve(null);
487     });
488   });
491 // Check what happens when updateDaemonConfig returns FAILED.
492 QUnit.test('updatePin where updateDaemonConfig is returns failure', function(
493     assert) {
494   mockHostDaemonFacade.updateDaemonConfigResult =
495       remoting.HostController.AsyncResult.FAILED;
496   return new Promise(function(resolve, reject) {
497     controller.updatePin(FAKE_NEW_HOST_PIN, function() {
498       reject('test failed');
499     }, function(/** remoting.Error */ e) {
500       assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
501       resolve(null);
502     });
503   });
506 // Check what happens when updatePin succeeds.
507 QUnit.test('updatePin succeeds', function(assert) {
508   /** @const */
509   var fakePinHash = fakePinHashFunc(FAKE_HOST_ID, FAKE_NEW_HOST_PIN);
510   return new Promise(function(resolve, reject) {
511     controller.updatePin(FAKE_NEW_HOST_PIN, function() {
512       assert.equal(getPinHashSpy.callCount, 1);
513       assert.equal(getPinHashSpy.args[0][0], FAKE_HOST_ID);
514       assert.equal(getPinHashSpy.args[0][1], FAKE_NEW_HOST_PIN);
515       assert.equal(updateDaemonConfigSpy.callCount, 1);
516       assert.deepEqual(
517           updateDaemonConfigSpy.args[0][0], {
518             host_secret_hash: fakePinHash
519           });
520       resolve(null);
521     }, reject);
522   });
525 // Check what happens when getLocalHostState fails.
526 QUnit.test('getLocalHostState with error', function(assert) {
527   mockHostDaemonFacade.daemonState = null;
528   return new Promise(function(resolve, reject) {
529     controller.getLocalHostState(function(
530         /** remoting.HostController.State */ state) {
531       assert.equal(state, remoting.HostController.State.UNKNOWN);
532       resolve(null);
533     });
534   });
537 // Check what happens when getLocalHostState reports no plugin.
538 QUnit.test('getLocalHostState with no plugin', function(assert) {
539   sinon.stub(mockHostDaemonFacade, 'getDaemonState').returns(
540       Promise.reject(new remoting.Error(remoting.Error.Tag.MISSING_PLUGIN)));
541   return new Promise(function(resolve, reject) {
542     controller.getLocalHostState(function(
543         /** remoting.HostController.State */ state) {
544       assert.equal(state, remoting.HostController.State.NOT_INSTALLED);
545       resolve(null);
546     });
547   });
550 // Check what happens when getLocalHostState succeeds.
551 QUnit.test('getLocalHostState succeeds', function(assert) {
552   return new Promise(function(resolve, reject) {
553     controller.getLocalHostState(function(
554         /** remoting.HostController.State */ state) {
555       assert.equal(state, remoting.HostController.State.STARTED);
556       resolve(null);
557     });
558   });
561 // Check what happens to getLocalHostId when getDaemonConfig
562 // returns an invalid config.
563 QUnit.test('getLocalHostId with invalid daemon config', function(assert) {
564   mockHostDaemonFacade.daemonConfig = {};
565   return new Promise(function(resolve, reject) {
566     controller.getLocalHostId(function(/** ?string */ id) {
567       assert.strictEqual(id, null);
568       resolve(null);
569     });
570   });
573 // Check what happens to getLocalHostId when getDaemonConfig fails.
574 QUnit.test('getLocalHostId with getDaemonConfig failure', function(assert) {
575   mockHostDaemonFacade.daemonConfig = null;
576   return new Promise(function(resolve, reject) {
577     controller.getLocalHostId(function(/** ?string */ id) {
578       assert.strictEqual(id, null);
579       resolve(null);
580     });
581   });
584 // Check what happens when getLocalHostId succeeds.
585 QUnit.test('getLocalHostId succeeds', function(assert) {
586   return new Promise(function(resolve, reject) {
587     controller.getLocalHostId(function(/** ?string */ id) {
588       assert.equal(id, FAKE_HOST_ID);
589       resolve(null);
590     });
591   });
594 // Tests omitted for hasFeature, getPairedClients, deletePairedClient,
595 // and clearPairedClients because they simply call through to
596 // HostDaemonFacade.
598 })();