OLD | NEW |
---|---|
(Empty) | |
1 /** | |
2 * @fileoverview | |
3 * Unit tests for host_controller.js. | |
4 */ | |
5 | |
6 (function() { | |
7 | |
8 'use strict'; | |
9 | |
10 /** @type {remoting.HostController} */ | |
11 var controller = null; | |
12 | |
13 /** @type {sinon.Mock} */ | |
14 var hostListMock = null; | |
15 | |
16 /** @type {sinon.TestStub} */ | |
17 var generateUuidStub; | |
18 | |
19 /** @type {remoting.MockHostDaemonFacade} */ | |
20 var mockHostDaemonFacade; | |
21 | |
22 /** @type {sinon.TestStub} */ | |
23 var hostDaemonFacadeCtorStub; | |
24 | |
25 var FAKE_HOST_PIN = '<FAKE_HOST_PIN>'; | |
26 var FAKE_USER_EMAIL = '<FAKE_USER_EMAIL>'; | |
27 var FAKE_USER_NAME = '<FAKE_USER_NAME>'; | |
28 var FAKE_UUID = '0bad0bad-0bad-0bad-0bad-0bad0bad0bad'; | |
29 var FAKE_DAEMON_VERSION = '1.2.3.4'; | |
30 var FAKE_HOST_NAME = '<FAKE_HOST_NAME>'; | |
31 var FAKE_PUBLIC_KEY = '<FAKE_PUBLIC_KEY>'; | |
32 var FAKE_PRIVATE_KEY = '<FAKE_PRIVATE_KEY>'; | |
33 var FAKE_AUTH_CODE = '<FAKE_AUTH_CODE>'; | |
34 var FAKE_REFRESH_TOKEN = '<FAKE_REFRESH_TOKEN>'; | |
35 var FAKE_PIN_HASH = '<FAKE_PIN_HASH>'; | |
36 | |
37 /** @type {sinon.Spy|Function} */ | |
38 var getCredentialsFromAuthCodeSpy; | |
39 | |
40 /** @type {sinon.Spy|Function} */ | |
41 var getPinHashSpy; | |
42 | |
43 /** @type {sinon.Spy|Function} */ | |
44 var startDaemonSpy; | |
45 | |
46 /** @type {sinon.Spy} */ | |
47 var unregisterHostByIdSpy; | |
48 | |
49 /** @type {sinon.Spy} */ | |
50 var onLocalHostStartedSpy; | |
51 | |
52 QUnit.module('host_controller', { | |
53 beforeEach: function(/** QUnit.Assert */ assert) { | |
54 chromeMocks.activate(['identity', 'runtime']); | |
55 chromeMocks.identity.mock$setToken('my_token'); | |
56 remoting.identity = new remoting.Identity(); | |
57 remoting.MockXhr.activate(); | |
58 base.debug.assert(remoting.oauth2 === null); | |
59 remoting.oauth2 = new remoting.OAuth2(); | |
60 base.debug.assert(remoting.hostList === null); | |
61 remoting.hostList = /** @type {remoting.HostList} */ | |
62 (Object.create(remoting.HostList.prototype)); | |
63 | |
64 // When the HostList's unregisterHostById method is called, make | |
65 // sure the argument is correct. | |
66 unregisterHostByIdSpy = | |
67 sinon.stub(remoting.hostList, 'unregisterHostById', function( | |
68 /** string */ hostId) { | |
69 assert.equal(hostId, FAKE_UUID); | |
70 }); | |
71 | |
72 // When the HostList's onLocalHostStarted method is called, make | |
73 // sure the arguments are correct. | |
74 onLocalHostStartedSpy = | |
75 sinon.stub( | |
76 remoting.hostList, 'onLocalHostStarted', function( | |
77 /** string */ hostName, | |
78 /** string */ newHostId, | |
79 /** string */ publicKey) { | |
80 assert.equal(hostName, FAKE_HOST_NAME); | |
81 assert.equal(newHostId, FAKE_UUID); | |
82 assert.equal(publicKey, FAKE_PUBLIC_KEY); | |
83 }); | |
84 | |
85 hostDaemonFacadeCtorStub = sinon.stub(remoting, 'HostDaemonFacade'); | |
86 mockHostDaemonFacade = new remoting.MockHostDaemonFacade(); | |
87 hostDaemonFacadeCtorStub.returns(mockHostDaemonFacade); | |
88 generateUuidStub = sinon.stub(base, 'generateUuid'); | |
89 generateUuidStub.returns(FAKE_UUID); | |
90 getCredentialsFromAuthCodeSpy = sinon.spy( | |
91 mockHostDaemonFacade, 'getCredentialsFromAuthCode'); | |
92 getPinHashSpy = sinon.spy(mockHostDaemonFacade, 'getPinHash'); | |
93 startDaemonSpy = sinon.spy(mockHostDaemonFacade, 'startDaemon'); | |
94 }, | |
95 afterEach: function() { | |
Jamie
2015/04/07 01:13:12
Blank line before afterEach for readability.
John Williams
2015/04/07 20:10:26
Done.
| |
96 controller = null; | |
97 getCredentialsFromAuthCodeSpy.restore(); | |
98 generateUuidStub.restore(); | |
99 hostDaemonFacadeCtorStub.restore(); | |
100 remoting.hostList = null; | |
101 remoting.oauth2 = null; | |
102 remoting.MockXhr.restore(); | |
103 chromeMocks.restore(); | |
104 remoting.identity = null; | |
105 } | |
106 }); | |
107 | |
108 // Check that hasFeature returns false by default. | |
109 QUnit.test('hasFeature returns false', function(assert) { | |
110 controller = new remoting.HostController(); | |
Jamie
2015/04/07 01:13:12
Can this be moved into beforeEach (for symmetry wi
John Williams
2015/04/07 20:10:25
Made local.
| |
111 return new Promise(function(resolve, reject) { | |
112 controller.hasFeature( | |
113 remoting.HostController.Feature.PAIRING_REGISTRY, | |
114 resolve); | |
115 }).then(function(/** boolean */ result) { | |
116 assert.equal(result, false); | |
117 }); | |
118 }); | |
119 | |
120 // Check what happens when the HostDaemonFacade's getHostName method | |
121 // fails. | |
122 QUnit.test('start with getHostName failure', function(assert) { | |
123 controller = new remoting.HostController(); | |
124 return new Promise(function(resolve, reject) { | |
125 controller.start(FAKE_HOST_PIN, true, function() { | |
126 reject(null); | |
127 }, function(/** remoting.Error */ e) { | |
128 assert.equal(e.getDetail(), 'getHostName'); | |
129 resolve(null); | |
130 }); | |
131 }); | |
132 }); | |
133 | |
134 // Prepare the to execute HostController.start up to the point where | |
Jamie
2015/04/07 01:13:12
Prepare the what? (here and below)
John Williams
2015/04/07 20:10:25
The word "the" wasn't supposed to be there.
| |
135 // the hostname is retreived. | |
136 function initThroughHostName() { | |
Jamie
2015/04/07 01:13:12
Using "through" to mean "until" is idiomatic to US
John Williams
2015/04/07 20:10:26
News to me. I'll keep that in mind.
| |
137 mockHostDaemonFacade.daemonVersion = FAKE_DAEMON_VERSION; | |
138 mockHostDaemonFacade.hostName = FAKE_HOST_NAME; | |
139 }; | |
140 | |
141 // Check what happens when the HostDaemonFacade's generateKeyPair | |
142 // method fails. | |
143 QUnit.test('start with generateKeyPair failure', function(assert) { | |
144 initThroughHostName(); | |
145 controller = new remoting.HostController(); | |
146 return new Promise(function(resolve, reject) { | |
147 controller.start(FAKE_HOST_PIN, true, function() { | |
148 reject(null); | |
149 }, function(/** remoting.Error */ e) { | |
150 assert.equal(e.getDetail(), 'generateKeyPair'); | |
Jamie
2015/04/07 01:13:12
Check the tag as well?
John Williams
2015/04/07 20:10:26
Seems redundant since it's always UNEXPECTED and t
Jamie
2015/04/08 00:16:14
It's only UNEXPECTED if the code is bug-free :) If
John Williams
2015/04/08 20:20:09
Done.
| |
151 resolve(null); | |
152 }); | |
153 }); | |
154 }); | |
155 | |
156 // Prepare the to execute HostController.start up to the point where | |
157 // a key pair is generated. | |
158 function initThroughPublicPrivateKey() { | |
159 initThroughHostName(); | |
160 mockHostDaemonFacade.privateKey = FAKE_PRIVATE_KEY; | |
161 mockHostDaemonFacade.publicKey = FAKE_PUBLIC_KEY; | |
162 } | |
163 | |
164 // Check what happens when the HostDaemonFacade's getHostClientId | |
165 // method fails. | |
166 QUnit.test('start with getHostClientId failure', function(assert) { | |
167 initThroughPublicPrivateKey(); | |
168 mockHostDaemonFacade.features = [ | |
169 remoting.HostController.Feature.OAUTH_CLIENT | |
Jamie
2015/04/07 01:13:12
Is there a reason why you don't want this Feature
John Williams
2015/04/07 20:10:25
Minimalism again. I want the tests to demonstrate
| |
170 ]; | |
171 controller = new remoting.HostController(); | |
172 return new Promise(function(resolve, reject) { | |
173 controller.start(FAKE_HOST_PIN, true, function() { | |
174 reject(null); | |
175 }, function(/** remoting.Error */ e) { | |
176 assert.equal(e.getDetail(), 'getHostClientId'); | |
177 resolve(null); | |
178 }); | |
179 }); | |
180 }); | |
181 | |
182 // Check what happens when the registry returns an HTTP when we try to | |
183 // register a host. | |
184 QUnit.test('start with host registration failure', function(assert) { | |
185 initThroughPublicPrivateKey(); | |
186 controller = new remoting.HostController(); | |
187 remoting.MockXhr.setEmptyResponseFor( | |
188 'POST', 'DIRECTORY_API_BASE_URL/@me/hosts', 500); | |
189 return new Promise(function(resolve, reject) { | |
190 controller.start(FAKE_HOST_PIN, true, function() { | |
191 reject(null); | |
192 }, function(/** remoting.Error */ e) { | |
193 assert.equal(e.getTag(), remoting.Error.Tag.REGISTRATION_FAILED); | |
194 resolve(null); | |
195 }); | |
196 }); | |
197 }); | |
198 | |
199 // Check what happens when the HostDaemonFacade's | |
200 // getCredentialsFromAuthCode method fails. | |
201 QUnit.test('start with getCredentialsFromAuthCode failure', function(assert) { | |
202 initThroughPublicPrivateKey(); | |
203 remoting.MockXhr.setTextResponseFor( | |
204 'POST', 'DIRECTORY_API_BASE_URL/@me/hosts', JSON.stringify({ | |
205 data: { | |
206 authorizationCode: FAKE_AUTH_CODE | |
207 } | |
208 })); | |
209 controller = new remoting.HostController(); | |
210 return new Promise(function(resolve, reject) { | |
211 controller.start(FAKE_HOST_PIN, true, function() { | |
212 reject(null); | |
213 }, function(/** remoting.Error */ e) { | |
214 assert.equal(e.getDetail(), 'getCredentialsFromAuthCode'); | |
215 resolve(null); | |
216 }); | |
217 }); | |
218 }); | |
219 | |
220 // Prepare the to execute HostController.start up to the point where | |
221 // a refresh token is generated. | |
222 function initThroughRefreshToken() { | |
223 initThroughPublicPrivateKey(); | |
224 sinon.stub(remoting.identity, 'getEmail').returns( | |
225 Promise.resolve(FAKE_USER_EMAIL)); | |
226 sinon.stub(remoting.oauth2, 'getRefreshToken').returns( | |
227 FAKE_REFRESH_TOKEN); | |
228 mockHostDaemonFacade.userEmail = FAKE_USER_EMAIL; | |
229 mockHostDaemonFacade.refreshToken = FAKE_REFRESH_TOKEN; | |
230 } | |
231 | |
232 // Check what happens when the HostDaemonFacade's getPinHash method | |
233 // fails, and verify that getPinHash is called when the registry | |
234 // does't return an auth code. | |
235 QUnit.test('start with getRefreshToken+getPinHash failure', function(assert) { | |
236 initThroughRefreshToken(); | |
237 remoting.MockXhr.setTextResponseFor( | |
238 'POST', 'DIRECTORY_API_BASE_URL/@me/hosts', '{}'); | |
239 controller = new remoting.HostController(); | |
240 return new Promise(function(resolve, reject) { | |
241 controller.start(FAKE_HOST_PIN, true, function() { | |
242 reject(null); | |
243 }, function(/** remoting.Error */ e) { | |
244 assert.equal(unregisterHostByIdSpy.callCount, 0); | |
245 assert.equal(e.getDetail(), 'getPinHash'); | |
246 resolve(null); | |
247 }); | |
248 }); | |
249 }); | |
250 | |
251 // Check what happens when the HostController's getClientBaseJid_ | |
252 // method fails. | |
253 QUnit.test('start with getClientBaseJid_ failure', function(assert) { | |
254 initThroughRefreshToken(); | |
255 remoting.MockXhr.setTextResponseFor( | |
256 'POST', 'DIRECTORY_API_BASE_URL/@me/hosts', JSON.stringify({ | |
257 data: { | |
258 authorizationCode: FAKE_AUTH_CODE | |
259 } | |
260 })); | |
261 controller = new remoting.HostController(); | |
262 sinon.stub(controller, 'getClientBaseJid_'). | |
263 callsArgWith(1, remoting.Error.unexpected('getClientBaseJid_')); | |
264 return new Promise(function(resolve, reject) { | |
265 controller.start(FAKE_HOST_PIN, true, function() { | |
266 reject(null); | |
267 }, function(/** remoting.Error */ e) { | |
268 assert.equal(unregisterHostByIdSpy.callCount, 1); | |
269 assert.equal(e.getDetail(), 'getClientBaseJid_'); | |
270 resolve(null); | |
271 }); | |
272 }); | |
273 }); | |
274 | |
275 // Prepare the to execute HostController.start up to the point where a | |
276 // PIN hash is generated. This function also installs a check to | |
277 // ensure that the registry is updated correctly. | |
278 function initThroughPinHash(/** QUnit.Assert */ assert) { | |
279 initThroughRefreshToken(); | |
280 remoting.MockXhr.setResponseFor( | |
281 'POST', 'DIRECTORY_API_BASE_URL/@me/hosts', | |
282 function(/** remoting.MockXhr */ xhr) { | |
283 assert.deepEqual( | |
284 xhr.params.jsonContent, | |
285 { data: { | |
286 hostId: FAKE_UUID, | |
287 hostName: FAKE_HOST_NAME, | |
288 publicKey: FAKE_PUBLIC_KEY | |
289 } }); | |
290 xhr.setTextResponse(200, JSON.stringify({ | |
291 data: { | |
292 authorizationCode: FAKE_AUTH_CODE | |
293 } | |
294 })); | |
295 }); | |
296 mockHostDaemonFacade.pinHash = FAKE_PIN_HASH; | |
297 }; | |
298 | |
299 // Check what happens when the HostDaemonFacade's startDaemon method | |
300 // fails and calls its onError argument. | |
301 QUnit.test('start with startDaemon failure', function(assert) { | |
302 initThroughPinHash(assert); | |
303 controller = new remoting.HostController(); | |
304 sinon.stub(controller, 'getClientBaseJid_').callsArgWith(0, FAKE_USER_EMAIL); | |
305 return new Promise(function(resolve, reject) { | |
306 controller.start(FAKE_HOST_PIN, true, function() { | |
307 reject(null); | |
308 }, function(/** remoting.Error */ e) { | |
309 assert.equal(unregisterHostByIdSpy.callCount, 1); | |
310 assert.equal(e.getDetail(), 'startDaemon'); | |
311 resolve(null); | |
312 }); | |
313 }); | |
314 }); | |
315 | |
316 // Check what happens when the HostDaemonFacade's startDaemon method | |
317 // calls is onDone method with an async error code. | |
318 QUnit.test('start with startDaemon cancelled', function(assert) { | |
319 initThroughPinHash(assert); | |
320 mockHostDaemonFacade.startDaemonResult = | |
321 remoting.HostController.AsyncResult.CANCELLED; | |
322 controller = new remoting.HostController(); | |
323 sinon.stub(controller, 'getClientBaseJid_').callsArgWith(0, FAKE_USER_EMAIL); | |
324 return new Promise(function(resolve, reject) { | |
325 controller.start(FAKE_HOST_PIN, true, function() { | |
326 reject(null); | |
327 }, function(/** remoting.Error */ e) { | |
328 assert.equal(unregisterHostByIdSpy.callCount, 1); | |
329 assert.equal(e.getTag(), remoting.Error.Tag.CANCELLED); | |
330 resolve(null); | |
331 }); | |
332 }); | |
333 }); | |
334 | |
335 // Check what happens when the entire host registration process | |
336 // succeeds. | |
337 [false, true].forEach(function(/** boolean */ consent) { | |
338 QUnit.test('start succeeds with consent=' + consent, function(assert) { | |
339 initThroughPinHash(assert); | |
340 mockHostDaemonFacade.startDaemonResult = | |
341 remoting.HostController.AsyncResult.OK; | |
342 controller = new remoting.HostController(); | |
343 sinon.stub(controller, 'getClientBaseJid_'). | |
344 callsArgWith(0, FAKE_USER_EMAIL); | |
345 return new Promise(function(resolve, reject) { | |
346 controller.start(FAKE_HOST_PIN, consent, 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_UUID, FAKE_HOST_PIN]); | |
355 | |
356 assert.equal(unregisterHostByIdSpy.callCount, 0); | |
357 assert.equal(onLocalHostStartedSpy.callCount, 1); | |
358 assert.equal(startDaemonSpy.callCount, 1); | |
359 assert.deepEqual( | |
360 startDaemonSpy.args[0].slice(0, 2), | |
361 [{ | |
362 xmpp_login: FAKE_USER_EMAIL, | |
363 oauth_refresh_token: FAKE_REFRESH_TOKEN, | |
364 host_id: FAKE_UUID, | |
365 host_name: FAKE_HOST_NAME, | |
366 host_secret_hash: FAKE_PIN_HASH, | |
367 private_key: FAKE_PRIVATE_KEY | |
368 }, consent]); | |
369 resolve(null); | |
370 }, reject); | |
371 }); | |
372 }); | |
373 }); | |
374 | |
375 // TODO(jrw): Add tests for |stop| method. | |
376 // TODO(jrw): Add tests for |updatePin| method. | |
377 // TODO(jrw): Add tests for |getLocalHostState| method. | |
378 // TODO(jrw): Add tests for |getLocalHostId| method. | |
379 // TODO(jrw): Add tests for |getPairedClients| method. | |
380 // TODO(jrw): Add tests for |deletePairedClient| method. | |
381 // TODO(jrw): Add tests for |clearPairedClients| method. | |
382 // TODO(jrw): Make sure getClientBaseJid_ method is tested. | |
383 | |
384 })(); | |
OLD | NEW |