Remove base.debug.assert.
[chromium-blink-merge.git] / remoting / webapp / crd / js / host_controller_unittest.js
blobf1204a98c32c08ce7fc982d0baa263642959d67f
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|Function} */
69 var clearPairedClientsSpy;
71 /** @type {sinon.Spy} */
72 var unregisterHostByIdSpy;
74 /** @type {sinon.Spy} */
75 var onLocalHostStartedSpy;
77 /** @type {remoting.MockHostListApi} */
78 var mockHostListApi;
80 QUnit.module('host_controller', {
81   beforeEach: function(/** QUnit.Assert */ assert) {
82     chromeMocks.identity.mock$setToken(FAKE_IDENTITY_TOKEN);
83     remoting.settings = new remoting.Settings();
84     remoting.identity = new remoting.Identity();
85     mockHostListApi = new remoting.MockHostListApi;
86     mockHostListApi.authCodeFromRegister = FAKE_AUTH_CODE;
87     mockHostListApi.emailFromRegister = '';
88     mockHostListApi.hostIdFromRegister = FAKE_HOST_ID;
89     remoting.HostListApi.setInstance(mockHostListApi);
90     console.assert(remoting.oauth2 === null, '|oauth2| already exists.');
91     remoting.oauth2 = new remoting.OAuth2();
92     console.assert(remoting.hostList === null, '|hostList| already exists.');
93     remoting.hostList = /** @type {remoting.HostList} */
94         (Object.create(remoting.HostList.prototype));
96     // When the HostList's unregisterHostById method is called, make
97     // sure the argument is correct.
98     unregisterHostByIdSpy =
99         sinon.stub(remoting.hostList, 'unregisterHostById', function(
100             /** string */ hostId, /** Function */ onDone) {
101           assert.equal(hostId, FAKE_HOST_ID);
102           if (onDone) {
103             onDone();
104           }
105         });
107     // When the HostList's onLocalHostStarted method is called, make
108     // sure the arguments are correct.
109     onLocalHostStartedSpy =
110         sinon.stub(
111             remoting.hostList, 'onLocalHostStarted', function(
112                 /** string */ hostName,
113                 /** string */ newHostId,
114                 /** string */ publicKey) {
115               assert.equal(hostName, FAKE_HOST_NAME);
116               assert.equal(newHostId, FAKE_HOST_ID);
117               assert.equal(publicKey, FAKE_PUBLIC_KEY);
118             });
120     mockSignalStrategy = new remoting.MockSignalStrategy(
121         FAKE_CLIENT_JID + '/extra_junk',
122         remoting.SignalStrategy.Type.XMPP);
123     signalStrategyCreateStub = sinon.stub(remoting.SignalStrategy, 'create');
124     signalStrategyCreateStub.returns(mockSignalStrategy);
126     hostDaemonFacadeCtorStub = sinon.stub(remoting, 'HostDaemonFacade');
127     mockHostDaemonFacade = new remoting.MockHostDaemonFacade();
128     hostDaemonFacadeCtorStub.returns(mockHostDaemonFacade);
129     generateUuidStub = sinon.stub(base, 'generateUuid');
130     generateUuidStub.returns(FAKE_HOST_ID);
131     getCredentialsFromAuthCodeSpy = sinon.spy(
132         mockHostDaemonFacade, 'getCredentialsFromAuthCode');
133     getPinHashSpy = sinon.spy(mockHostDaemonFacade, 'getPinHash');
134     startDaemonSpy = sinon.spy(mockHostDaemonFacade, 'startDaemon');
135     updateDaemonConfigSpy =
136         sinon.spy(mockHostDaemonFacade, 'updateDaemonConfig');
137     clearPairedClientsSpy =
138         sinon.spy(mockHostDaemonFacade, 'clearPairedClients');
140     // Set up successful responses from mockHostDaemonFacade.
141     // Individual tests override these values to create errors.
142     mockHostDaemonFacade.features =
143         [remoting.HostController.Feature.OAUTH_CLIENT];
144     mockHostDaemonFacade.daemonVersion = FAKE_DAEMON_VERSION;
145     mockHostDaemonFacade.hostName = FAKE_HOST_NAME;
146     mockHostDaemonFacade.privateKey = FAKE_PRIVATE_KEY;
147     mockHostDaemonFacade.publicKey = FAKE_PUBLIC_KEY;
148     mockHostDaemonFacade.hostClientId = FAKE_HOST_CLIENT_ID;
149     mockHostDaemonFacade.userEmail = FAKE_XMPP_LOGIN;
150     mockHostDaemonFacade.refreshToken = FAKE_REFRESH_TOKEN;
151     mockHostDaemonFacade.pinHashFunc = fakePinHashFunc;
152     mockHostDaemonFacade.startDaemonResult =
153         remoting.HostController.AsyncResult.OK;
154     mockHostDaemonFacade.stopDaemonResult =
155         remoting.HostController.AsyncResult.OK;
156     mockHostDaemonFacade.daemonConfig = {
157       host_id: FAKE_HOST_ID,
158       xmpp_login: FAKE_XMPP_LOGIN
159     };
160     mockHostDaemonFacade.updateDaemonConfigResult =
161         remoting.HostController.AsyncResult.OK;
162     mockHostDaemonFacade.daemonState =
163         remoting.HostController.State.STARTED;
165     sinon.stub(remoting.identity, 'getEmail').returns(
166         Promise.resolve(FAKE_USER_EMAIL));
167     sinon.stub(remoting.oauth2, 'getRefreshToken').returns(
168         FAKE_REFRESH_TOKEN);
170     controller = new remoting.HostController();
171   },
173   afterEach: function() {
174     controller = null;
175     getCredentialsFromAuthCodeSpy.restore();
176     generateUuidStub.restore();
177     hostDaemonFacadeCtorStub.restore();
178     signalStrategyCreateStub.restore();
179     remoting.hostList = null;
180     remoting.oauth2 = null;
181     remoting.HostListApi.setInstance(null);
182     remoting.identity = null;
183   }
187  * @param {string} hostId
188  * @param {string} pin
189  * @return {string}
190  */
191 function fakePinHashFunc(hostId, pin) {
192   return '<FAKE_PIN:' + hostId + ':' + pin + '>';
196  * @param {boolean} successful
197  */
198 function stubSignalStrategyConnect(successful) {
199   sinon.stub(mockSignalStrategy, 'connect', function() {
200     Promise.resolve().then(function() {
201       mockSignalStrategy.setStateForTesting(
202           successful ?
203             remoting.SignalStrategy.State.CONNECTED :
204             remoting.SignalStrategy.State.FAILED);
205     });
206   });
209 // Check what happens when the HostDaemonFacade's getHostName method
210 // fails.
211 QUnit.test('start with getHostName failure', function(assert) {
212   mockHostDaemonFacade.hostName = null;
213   return controller.start(FAKE_HOST_PIN, true).then(function() {
214     throw 'test failed';
215   }, function(/** remoting.Error */ e) {
216     assert.equal(e.getDetail(), 'getHostName');
217     assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
218     assert.equal(unregisterHostByIdSpy.callCount, 0);
219     assert.equal(onLocalHostStartedSpy.callCount, 0);
220     assert.equal(startDaemonSpy.callCount, 0);
221   });
224 // Check what happens when the HostDaemonFacade's generateKeyPair
225 // method fails.
226 QUnit.test('start with generateKeyPair failure', function(assert) {
227   mockHostDaemonFacade.publicKey = null;
228   mockHostDaemonFacade.privateKey = null;
229   return controller.start(FAKE_HOST_PIN, true).then(function() {
230     throw 'test failed';
231   }, function(/** remoting.Error */ e) {
232     assert.equal(e.getDetail(), 'generateKeyPair');
233     assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
234     assert.equal(unregisterHostByIdSpy.callCount, 0);
235     assert.equal(onLocalHostStartedSpy.callCount, 0);
236     assert.equal(startDaemonSpy.callCount, 0);
237   });
240 // Check what happens when the HostDaemonFacade's getHostClientId
241 // method fails.
242 QUnit.test('start with getHostClientId failure', function(assert) {
243   mockHostDaemonFacade.hostClientId = null;
244   return controller.start(FAKE_HOST_PIN, true).then(function() {
245     throw 'test failed';
246   }, function(/** remoting.Error */ e) {
247     assert.equal(e.getDetail(), 'getHostClientId');
248     assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
249     assert.equal(unregisterHostByIdSpy.callCount, 0);
250     assert.equal(onLocalHostStartedSpy.callCount, 0);
251     assert.equal(startDaemonSpy.callCount, 0);
252   });
255 // Check what happens when the registry returns an HTTP when we try to
256 // register a host.
257 QUnit.test('start with host registration failure', function(assert) {
258   mockHostListApi.authCodeFromRegister = null;
259   return controller.start(FAKE_HOST_PIN, true).then(function() {
260     throw 'test failed';
261   }, function(/** remoting.Error */ e) {
262     assert.equal(e.getTag(), remoting.Error.Tag.REGISTRATION_FAILED);
263     assert.equal(unregisterHostByIdSpy.callCount, 0);
264     assert.equal(onLocalHostStartedSpy.callCount, 0);
265     assert.equal(startDaemonSpy.callCount, 0);
266   });
269 // Check what happens when the HostDaemonFacade's
270 // getCredentialsFromAuthCode method fails.
271 QUnit.test('start with getCredentialsFromAuthCode failure', function(assert) {
272   mockHostDaemonFacade.useEmail = null;
273   mockHostDaemonFacade.refreshToken = null;
274   return controller.start(FAKE_HOST_PIN, true).then(function(result) {
275     throw 'test failed';
276   }, function(/** remoting.Error */ e) {
277     assert.equal(e.getDetail(), 'getCredentialsFromAuthCode');
278     assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
279     assert.equal(getCredentialsFromAuthCodeSpy.callCount, 1);
280     assert.equal(onLocalHostStartedSpy.callCount, 0);
281     assert.equal(startDaemonSpy.callCount, 0);
282   });
285 // Check what happens when the SignalStrategy fails to connect.
286 QUnit.test('start with signalStrategy failure', function(assert) {
287   stubSignalStrategyConnect(false);
288   return controller.start(FAKE_HOST_PIN, true).then(function() {
289     throw 'test failed';
290   }, function(/** remoting.Error */ e) {
291     assert.equal(e.getDetail(), 'setStateForTesting');
292     assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
293     assert.equal(unregisterHostByIdSpy.callCount, 1);
294   });
297 // Check what happens when the HostDaemonFacade's startDaemon method
298 // fails and calls its onError argument.
299 // TODO(jrw): Should startDaemon even have an onError callback?
300 QUnit.test('start with startDaemon failure', function(assert) {
301   stubSignalStrategyConnect(true);
302   mockHostDaemonFacade.startDaemonResult = null;
303   return controller.start(FAKE_HOST_PIN, true).then(function() {
304     throw 'test failed';
305   }, function(/** remoting.Error */ e) {
306     assert.equal(e.getDetail(), 'startDaemon');
307     assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
308     assert.equal(unregisterHostByIdSpy.callCount, 1);
309     assert.equal(unregisterHostByIdSpy.args[0][0], FAKE_HOST_ID);
310     assert.equal(onLocalHostStartedSpy.callCount, 0);
311   });
314 // Check what happens when the HostDaemonFacade's startDaemon method
315 // calls is onDone method with a CANCELLED error code.
316 QUnit.test('start with startDaemon cancelled', function(assert) {
317   stubSignalStrategyConnect(true);
318   mockHostDaemonFacade.startDaemonResult =
319       remoting.HostController.AsyncResult.CANCELLED;
320   return controller.start(FAKE_HOST_PIN, true).then(function() {
321     throw 'test failed';
322   }, function(/** remoting.Error */ e) {
323     assert.equal(e.getTag(), remoting.Error.Tag.CANCELLED);
324     assert.equal(unregisterHostByIdSpy.callCount, 1);
325     assert.equal(unregisterHostByIdSpy.args[0][0], FAKE_HOST_ID);
326     assert.equal(onLocalHostStartedSpy.callCount, 0);
327   });
330 // Check what happens when the HostDaemonFacade's startDaemon method
331 // calls is onDone method with an async error code.
332 QUnit.test('start with startDaemon returning failure code', function(assert) {
333   stubSignalStrategyConnect(true);
334   mockHostDaemonFacade.startDaemonResult =
335       remoting.HostController.AsyncResult.FAILED;
336   return controller.start(FAKE_HOST_PIN, true).then(function() {
337     throw 'test failed';
338   }, function(/** remoting.Error */ e) {
339     assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
340     assert.equal(unregisterHostByIdSpy.callCount, 1);
341     assert.equal(onLocalHostStartedSpy.callCount, 0);
342   });
345 // Check what happens when the entire host registration process
346 // succeeds.
347 [false, true].forEach(function(/** boolean */ consent) {
348   QUnit.test('start with consent=' + consent, function(assert) {
349     /** @const */
350     var fakePinHash = fakePinHashFunc(FAKE_HOST_ID, FAKE_HOST_PIN);
351     stubSignalStrategyConnect(true);
352     return controller.start(FAKE_HOST_PIN, consent).then(function() {
353       assert.equal(getCredentialsFromAuthCodeSpy.callCount, 1);
354       assert.deepEqual(
355           getCredentialsFromAuthCodeSpy.args[0][0],
356           FAKE_AUTH_CODE);
357       assert.equal(getPinHashSpy.callCount, 1);
358       assert.deepEqual(
359           getPinHashSpy.args[0].slice(0, 2),
360           [FAKE_HOST_ID, FAKE_HOST_PIN]);
361       assert.equal(unregisterHostByIdSpy.callCount, 0);
362       assert.equal(onLocalHostStartedSpy.callCount, 1);
363       assert.equal(startDaemonSpy.callCount, 1);
364       var expectedConfig = {
365         xmpp_login: FAKE_XMPP_LOGIN,
366         oauth_refresh_token: FAKE_REFRESH_TOKEN,
367         host_owner: FAKE_CLIENT_JID.toLowerCase(),
368         host_owner_email: FAKE_USER_EMAIL,
369         host_name: FAKE_HOST_NAME,
370         host_secret_hash: fakePinHash,
371         private_key: FAKE_PRIVATE_KEY
372       };
373       if (remoting.settings.USE_GCD) {
374         expectedConfig['gcd_device_id'] = FAKE_HOST_ID;
375       } else {
376         expectedConfig['host_id'] = FAKE_HOST_ID;
377       }
378       assert.deepEqual(
379           startDaemonSpy.args[0].slice(0, 2),
380           [expectedConfig, consent]);
381     });
382   });
385 // Check what happens when stopDaemon calls onError.
386 // TODO(jrw): Should stopDaemon even have an onError callback?
387 QUnit.test('stop with stopDaemon failure', function(assert) {
388   mockHostDaemonFacade.stopDaemonResult = null;
389   return new Promise(function(resolve, reject) {
390     controller.stop(function() {
391       reject('test failed');
392     }, function(/** remoting.Error */ e) {
393       assert.equal(e.getDetail(), 'stopDaemon');
394       // TODO(jrw): Is it really desirable to leave the host registered?
395       assert.equal(unregisterHostByIdSpy.callCount, 0);
396       resolve(null);
397     });
398   });
401 // Check what happens when stopDaemon returns FAILED.
402 QUnit.test('stop with stopDaemon cancelled', function(assert) {
403   mockHostDaemonFacade.stopDaemonResult =
404       remoting.HostController.AsyncResult.FAILED;
405   return new Promise(function(resolve, reject) {
406     controller.stop(function() {
407       reject('test failed');
408     }, function(/** remoting.Error */ e) {
409       // TODO(jrw): Is it really desirable to leave the host registered?
410       assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
411       assert.equal(unregisterHostByIdSpy.callCount, 0);
412       resolve(null);
413     });
414   });
417 // Check what happens when stopDaemon returns CANCELLED.
418 QUnit.test('stop with stopDaemon cancelled', function(assert) {
419   mockHostDaemonFacade.stopDaemonResult =
420       remoting.HostController.AsyncResult.CANCELLED;
421   return new Promise(function(resolve, reject) {
422     controller.stop(function() {
423       reject('test failed');
424     }, function(/** remoting.Error */ e) {
425       assert.equal(e.getTag(), remoting.Error.Tag.CANCELLED);
426       assert.equal(unregisterHostByIdSpy.callCount, 0);
427       resolve(null);
428     });
429   });
432 // Check what happens when stopDaemon succeeds.
433 QUnit.test('stop succeeds', function(assert) {
434   sinon.stub(controller, 'getLocalHostId').callsArgWith(0, FAKE_HOST_ID);
435   return new Promise(function(resolve, reject) {
436     controller.stop(function() {
437       assert.equal(unregisterHostByIdSpy.callCount, 1);
438       assert.equal(unregisterHostByIdSpy.args[0][0], FAKE_HOST_ID);
439       resolve(null);
440     }, reject);
441   });
444 // Check what happens when the host reports an invalid config.
445 QUnit.test('updatePin where config is invalid', function(assert) {
446   mockHostDaemonFacade.daemonConfig = {};
447   return new Promise(function(resolve, reject) {
448     controller.updatePin(FAKE_NEW_HOST_PIN, function() {
449       reject('test failed');
450     }, function(/** remoting.Error */ e) {
451       assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
452       assert.equal(clearPairedClientsSpy.callCount, 0);
453       resolve(null);
454     });
455   });
458 // Check what happens when getDaemonConfig calls onError.
459 QUnit.test('updatePin where getDaemonConfig fails', function(assert) {
460   mockHostDaemonFacade.daemonConfig = null;
461   return new Promise(function(resolve, reject) {
462     controller.updatePin(FAKE_NEW_HOST_PIN, function() {
463       reject('test failed');
464     }, function(/** remoting.Error */ e) {
465       assert.equal(e.getDetail(), 'getDaemonConfig');
466       assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
467       assert.equal(clearPairedClientsSpy.callCount, 0);
468       resolve(null);
469     });
470   });
473 // Check what happens when updateDaemonConfig calls onError.
474 // TODO(jrw): Should updateDaemonConfig even have an onError callback?
475 QUnit.test('updatePin where updateDaemonConfig calls onError', function(
476     assert) {
477   mockHostDaemonFacade.updateDaemonConfigResult = null;
478   return new Promise(function(resolve, reject) {
479     controller.updatePin(FAKE_NEW_HOST_PIN, function() {
480       reject('test failed');
481     }, function(/** remoting.Error */ e) {
482       assert.equal(e.getDetail(), 'updateDaemonConfig');
483       assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
484       assert.equal(clearPairedClientsSpy.callCount, 0);
485       resolve(null);
486     });
487   });
490 // Check what happens when updateDaemonConfig returns CANCELLED.
491 QUnit.test('updatePin where updateDaemonConfig is cancelled', function(
492     assert) {
493   mockHostDaemonFacade.updateDaemonConfigResult =
494       remoting.HostController.AsyncResult.CANCELLED;
495   return new Promise(function(resolve, reject) {
496     controller.updatePin(FAKE_NEW_HOST_PIN, function() {
497       reject('test failed');
498     }, function(/** remoting.Error */ e) {
499       assert.equal(e.getTag(), remoting.Error.Tag.CANCELLED);
500       assert.equal(clearPairedClientsSpy.callCount, 0);
501       resolve(null);
502     });
503   });
506 // Check what happens when updateDaemonConfig returns FAILED.
507 QUnit.test('updatePin where updateDaemonConfig is returns failure', function(
508     assert) {
509   mockHostDaemonFacade.updateDaemonConfigResult =
510       remoting.HostController.AsyncResult.FAILED;
511   return new Promise(function(resolve, reject) {
512     controller.updatePin(FAKE_NEW_HOST_PIN, function() {
513       reject('test failed');
514     }, function(/** remoting.Error */ e) {
515       assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
516       assert.equal(clearPairedClientsSpy.callCount, 0);
517       resolve(null);
518     });
519   });
522 // Check what happens when updatePin succeeds.
523 QUnit.test('updatePin succeeds', function(assert) {
524   mockHostDaemonFacade.pairedClients = [];
525   /** @const */
526   var fakePinHash = fakePinHashFunc(FAKE_HOST_ID, FAKE_NEW_HOST_PIN);
527   return new Promise(function(resolve, reject) {
528     controller.updatePin(FAKE_NEW_HOST_PIN, function() {
529       assert.equal(getPinHashSpy.callCount, 1);
530       assert.equal(getPinHashSpy.args[0][0], FAKE_HOST_ID);
531       assert.equal(getPinHashSpy.args[0][1], FAKE_NEW_HOST_PIN);
532       assert.equal(updateDaemonConfigSpy.callCount, 1);
533       assert.deepEqual(
534           updateDaemonConfigSpy.args[0][0], {
535             host_secret_hash: fakePinHash
536           });
537       assert.equal(clearPairedClientsSpy.callCount, 1);
538       resolve(null);
539     }, reject);
540   });
543 // Check what happens when getLocalHostState fails.
544 QUnit.test('getLocalHostState with error', function(assert) {
545   mockHostDaemonFacade.daemonState = null;
546   return new Promise(function(resolve, reject) {
547     controller.getLocalHostState(function(
548         /** remoting.HostController.State */ state) {
549       assert.equal(state, remoting.HostController.State.UNKNOWN);
550       resolve(null);
551     });
552   });
555 // Check what happens when getLocalHostState reports no plugin.
556 QUnit.test('getLocalHostState with no plugin', function(assert) {
557   sinon.stub(mockHostDaemonFacade, 'getDaemonState').returns(
558       Promise.reject(new remoting.Error(remoting.Error.Tag.MISSING_PLUGIN)));
559   return new Promise(function(resolve, reject) {
560     controller.getLocalHostState(function(
561         /** remoting.HostController.State */ state) {
562       assert.equal(state, remoting.HostController.State.NOT_INSTALLED);
563       resolve(null);
564     });
565   });
568 // Check what happens when getLocalHostState succeeds.
569 QUnit.test('getLocalHostState succeeds', function(assert) {
570   return new Promise(function(resolve, reject) {
571     controller.getLocalHostState(function(
572         /** remoting.HostController.State */ state) {
573       assert.equal(state, remoting.HostController.State.STARTED);
574       resolve(null);
575     });
576   });
579 // Check what happens to getLocalHostId when getDaemonConfig
580 // returns an invalid config.
581 QUnit.test('getLocalHostId with invalid daemon config', function(assert) {
582   mockHostDaemonFacade.daemonConfig = {};
583   return new Promise(function(resolve, reject) {
584     controller.getLocalHostId(function(/** ?string */ id) {
585       assert.strictEqual(id, null);
586       resolve(null);
587     });
588   });
591 // Check what happens to getLocalHostId when getDaemonConfig fails.
592 QUnit.test('getLocalHostId with getDaemonConfig failure', function(assert) {
593   mockHostDaemonFacade.daemonConfig = null;
594   return new Promise(function(resolve, reject) {
595     controller.getLocalHostId(function(/** ?string */ id) {
596       assert.strictEqual(id, null);
597       resolve(null);
598     });
599   });
602 // Check what happens when getLocalHostId succeeds.
603 QUnit.test('getLocalHostId succeeds', function(assert) {
604   return new Promise(function(resolve, reject) {
605     controller.getLocalHostId(function(/** ?string */ id) {
606       assert.equal(id, FAKE_HOST_ID);
607       resolve(null);
608     });
609   });
612 // Tests omitted for hasFeature, getPairedClients, deletePairedClient,
613 // and clearPairedClients because they simply call through to
614 // HostDaemonFacade.
616 })();