Updating XTBs based on .GRDs from branch master
[chromium-blink-merge.git] / remoting / webapp / crd / js / host_controller_unittest.js
blobf02a46c8e3e6fad4fde3102da812b33080cc4419
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       expectedConfig['host_id'] = FAKE_HOST_ID;
374       assert.deepEqual(
375           startDaemonSpy.args[0].slice(0, 2),
376           [expectedConfig, consent]);
377     });
378   });
381 // Check what happens when stopDaemon calls onError.
382 // TODO(jrw): Should stopDaemon even have an onError callback?
383 QUnit.test('stop with stopDaemon failure', function(assert) {
384   mockHostDaemonFacade.stopDaemonResult = null;
385   return new Promise(function(resolve, reject) {
386     controller.stop(function() {
387       reject('test failed');
388     }, function(/** remoting.Error */ e) {
389       assert.equal(e.getDetail(), 'stopDaemon');
390       // TODO(jrw): Is it really desirable to leave the host registered?
391       assert.equal(unregisterHostByIdSpy.callCount, 0);
392       resolve(null);
393     });
394   });
397 // Check what happens when stopDaemon returns FAILED.
398 QUnit.test('stop with stopDaemon cancelled', function(assert) {
399   mockHostDaemonFacade.stopDaemonResult =
400       remoting.HostController.AsyncResult.FAILED;
401   return new Promise(function(resolve, reject) {
402     controller.stop(function() {
403       reject('test failed');
404     }, function(/** remoting.Error */ e) {
405       // TODO(jrw): Is it really desirable to leave the host registered?
406       assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
407       assert.equal(unregisterHostByIdSpy.callCount, 0);
408       resolve(null);
409     });
410   });
413 // Check what happens when stopDaemon returns CANCELLED.
414 QUnit.test('stop with stopDaemon cancelled', function(assert) {
415   mockHostDaemonFacade.stopDaemonResult =
416       remoting.HostController.AsyncResult.CANCELLED;
417   return new Promise(function(resolve, reject) {
418     controller.stop(function() {
419       reject('test failed');
420     }, function(/** remoting.Error */ e) {
421       assert.equal(e.getTag(), remoting.Error.Tag.CANCELLED);
422       assert.equal(unregisterHostByIdSpy.callCount, 0);
423       resolve(null);
424     });
425   });
428 // Check what happens when stopDaemon succeeds.
429 QUnit.test('stop succeeds', function(assert) {
430   sinon.stub(controller, 'getLocalHostId').callsArgWith(0, FAKE_HOST_ID);
431   return new Promise(function(resolve, reject) {
432     controller.stop(function() {
433       assert.equal(unregisterHostByIdSpy.callCount, 1);
434       assert.equal(unregisterHostByIdSpy.args[0][0], FAKE_HOST_ID);
435       resolve(null);
436     }, reject);
437   });
440 // Check what happens when the host reports an invalid config.
441 QUnit.test('updatePin where config is invalid', function(assert) {
442   mockHostDaemonFacade.daemonConfig = {};
443   return new Promise(function(resolve, reject) {
444     controller.updatePin(FAKE_NEW_HOST_PIN, function() {
445       reject('test failed');
446     }, function(/** remoting.Error */ e) {
447       assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
448       assert.equal(clearPairedClientsSpy.callCount, 0);
449       resolve(null);
450     });
451   });
454 // Check what happens when getDaemonConfig calls onError.
455 QUnit.test('updatePin where getDaemonConfig fails', function(assert) {
456   mockHostDaemonFacade.daemonConfig = null;
457   return new Promise(function(resolve, reject) {
458     controller.updatePin(FAKE_NEW_HOST_PIN, function() {
459       reject('test failed');
460     }, function(/** remoting.Error */ e) {
461       assert.equal(e.getDetail(), 'getDaemonConfig');
462       assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
463       assert.equal(clearPairedClientsSpy.callCount, 0);
464       resolve(null);
465     });
466   });
469 // Check what happens when updateDaemonConfig calls onError.
470 // TODO(jrw): Should updateDaemonConfig even have an onError callback?
471 QUnit.test('updatePin where updateDaemonConfig calls onError', function(
472     assert) {
473   mockHostDaemonFacade.updateDaemonConfigResult = null;
474   return new Promise(function(resolve, reject) {
475     controller.updatePin(FAKE_NEW_HOST_PIN, function() {
476       reject('test failed');
477     }, function(/** remoting.Error */ e) {
478       assert.equal(e.getDetail(), 'updateDaemonConfig');
479       assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
480       assert.equal(clearPairedClientsSpy.callCount, 0);
481       resolve(null);
482     });
483   });
486 // Check what happens when updateDaemonConfig returns CANCELLED.
487 QUnit.test('updatePin where updateDaemonConfig is cancelled', function(
488     assert) {
489   mockHostDaemonFacade.updateDaemonConfigResult =
490       remoting.HostController.AsyncResult.CANCELLED;
491   return new Promise(function(resolve, reject) {
492     controller.updatePin(FAKE_NEW_HOST_PIN, function() {
493       reject('test failed');
494     }, function(/** remoting.Error */ e) {
495       assert.equal(e.getTag(), remoting.Error.Tag.CANCELLED);
496       assert.equal(clearPairedClientsSpy.callCount, 0);
497       resolve(null);
498     });
499   });
502 // Check what happens when updateDaemonConfig returns FAILED.
503 QUnit.test('updatePin where updateDaemonConfig is returns failure', function(
504     assert) {
505   mockHostDaemonFacade.updateDaemonConfigResult =
506       remoting.HostController.AsyncResult.FAILED;
507   return new Promise(function(resolve, reject) {
508     controller.updatePin(FAKE_NEW_HOST_PIN, function() {
509       reject('test failed');
510     }, function(/** remoting.Error */ e) {
511       assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
512       assert.equal(clearPairedClientsSpy.callCount, 0);
513       resolve(null);
514     });
515   });
518 // Check what happens when updatePin succeeds.
519 QUnit.test('updatePin succeeds', function(assert) {
520   mockHostDaemonFacade.pairedClients = [];
521   /** @const */
522   var fakePinHash = fakePinHashFunc(FAKE_HOST_ID, FAKE_NEW_HOST_PIN);
523   return new Promise(function(resolve, reject) {
524     controller.updatePin(FAKE_NEW_HOST_PIN, function() {
525       assert.equal(getPinHashSpy.callCount, 1);
526       assert.equal(getPinHashSpy.args[0][0], FAKE_HOST_ID);
527       assert.equal(getPinHashSpy.args[0][1], FAKE_NEW_HOST_PIN);
528       assert.equal(updateDaemonConfigSpy.callCount, 1);
529       assert.deepEqual(
530           updateDaemonConfigSpy.args[0][0], {
531             host_secret_hash: fakePinHash
532           });
533       assert.equal(clearPairedClientsSpy.callCount, 1);
534       resolve(null);
535     }, reject);
536   });
539 // Check what happens when getLocalHostState fails.
540 QUnit.test('getLocalHostState with error', function(assert) {
541   mockHostDaemonFacade.daemonState = null;
542   return new Promise(function(resolve, reject) {
543     controller.getLocalHostState(function(
544         /** remoting.HostController.State */ state) {
545       assert.equal(state, remoting.HostController.State.UNKNOWN);
546       resolve(null);
547     });
548   });
551 // Check what happens when getLocalHostState reports no plugin.
552 QUnit.test('getLocalHostState with no plugin', function(assert) {
553   sinon.stub(mockHostDaemonFacade, 'getDaemonState').returns(
554       Promise.reject(new remoting.Error(remoting.Error.Tag.MISSING_PLUGIN)));
555   return new Promise(function(resolve, reject) {
556     controller.getLocalHostState(function(
557         /** remoting.HostController.State */ state) {
558       assert.equal(state, remoting.HostController.State.NOT_INSTALLED);
559       resolve(null);
560     });
561   });
564 // Check what happens when getLocalHostState succeeds.
565 QUnit.test('getLocalHostState succeeds', function(assert) {
566   return new Promise(function(resolve, reject) {
567     controller.getLocalHostState(function(
568         /** remoting.HostController.State */ state) {
569       assert.equal(state, remoting.HostController.State.STARTED);
570       resolve(null);
571     });
572   });
575 // Check what happens to getLocalHostId when getDaemonConfig
576 // returns an invalid config.
577 QUnit.test('getLocalHostId with invalid daemon config', function(assert) {
578   mockHostDaemonFacade.daemonConfig = {};
579   return new Promise(function(resolve, reject) {
580     controller.getLocalHostId(function(/** ?string */ id) {
581       assert.strictEqual(id, null);
582       resolve(null);
583     });
584   });
587 // Check what happens to getLocalHostId when getDaemonConfig fails.
588 QUnit.test('getLocalHostId with getDaemonConfig failure', function(assert) {
589   mockHostDaemonFacade.daemonConfig = null;
590   return new Promise(function(resolve, reject) {
591     controller.getLocalHostId(function(/** ?string */ id) {
592       assert.strictEqual(id, null);
593       resolve(null);
594     });
595   });
598 // Check what happens when getLocalHostId succeeds.
599 QUnit.test('getLocalHostId succeeds', function(assert) {
600   return new Promise(function(resolve, reject) {
601     controller.getLocalHostId(function(/** ?string */ id) {
602       assert.equal(id, FAKE_HOST_ID);
603       resolve(null);
604     });
605   });
608 // Tests omitted for hasFeature, getPairedClients, deletePairedClient,
609 // and clearPairedClients because they simply call through to
610 // HostDaemonFacade.
612 })();