Merge lp:~benji/juju-gui/go-sandbox into lp:juju-gui/experimental
- go-sandbox
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Diogo Matsubara |
Approved revision: | 721 |
Merged at revision: | 728 |
Proposed branch: | lp:~benji/juju-gui/go-sandbox |
Merge into: | lp:juju-gui/experimental |
Diff against target: |
675 lines (+449/-32) 8 files modified
app/app.js (+13/-6) app/models/models.js (+1/-1) app/store/env/sandbox.js (+203/-1) lib/deploy_charm_for_testing.py (+0/-1) test/test_app.js (+11/-2) test/test_deploy_charm_for_testing.py (+24/-14) test/test_env_go.js (+2/-0) test/test_sandbox.js (+195/-7) |
To merge this branch: | bzr merge lp:~benji/juju-gui/go-sandbox |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Diogo Matsubara (community) | Approve | ||
Review via email: mp+169036@code.launchpad.net |
Commit message
The beginings of a Go-flavored sandbox.
Logging in, deploying a service, and moving it around all work.
Description of the change
The beginings of a Go-flavored sandbox.
Logging in, deploying a service, and moving it around all work.
Gary Poster (gary) wrote : | # |
Jeff Pihach (hatch) wrote : | # |
LGTM Thanks this looks good. I am not sure about the method casing
senario :-)
https:/
File app/store/
https:/
app/store/
camel_Snake_Case? ;-)
https:/
File test/test_
https:/
test/test_
async failures hard to detect.
this technique has caused false failures on our CI before because of a
slow instance so I would suggest increasing it or removing all together.
https:/
test/test_
cleanups is never used.
Jujugui Lander (jujugui-lander) wrote : | # |
No commit message specified.
Jujugui Lander (jujugui-lander) wrote : | # |
Voting does not meet specified criteria. Required: Approve >= 1, Disapprove == 0. Got: 1 Pending.
Diogo Matsubara (matsubara) : | # |
Jujugui Lander (jujugui-lander) wrote : | # |
The Jenkins job https:/
Not merging it.
Preview Diff
1 | === modified file 'app/app.js' |
2 | --- app/app.js 2013-06-12 17:55:05 +0000 |
3 | +++ app/app.js 2013-06-13 12:44:26 +0000 |
4 | @@ -360,8 +360,7 @@ |
5 | } |
6 | } |
7 | |
8 | - |
9 | - if (window.flags.websocket_capture) { |
10 | + if (window.flags && window.flags.websocket_capture) { |
11 | this.websocketLogging = new Y.juju.WebsocketLogging(); |
12 | } |
13 | |
14 | @@ -448,7 +447,7 @@ |
15 | }; |
16 | var apiBackend = this.get('apiBackend'); |
17 | // The sandbox mode does not support the Go API (yet?). |
18 | - if (this.get('sandbox') && apiBackend === 'python') { |
19 | + if (this.get('sandbox')) { |
20 | var sandboxModule = Y.namespace('juju.environments.sandbox'); |
21 | var State = Y.namespace('juju.environments').FakeBackend; |
22 | var state = new State({charmStore: this.charm_store}); |
23 | @@ -457,8 +456,16 @@ |
24 | credentials[envOptions.user] = envOptions.password; |
25 | state.set('authorizedUsers', credentials); |
26 | } |
27 | - envOptions.conn = new sandboxModule.ClientConnection( |
28 | - {juju: new sandboxModule.PyJujuAPI({state: state})}); |
29 | + if (apiBackend === 'python') { |
30 | + envOptions.conn = new sandboxModule.ClientConnection( |
31 | + {juju: new sandboxModule.PyJujuAPI({state: state})}); |
32 | + } else if (apiBackend === 'go') { |
33 | + envOptions.conn = new sandboxModule.ClientConnection( |
34 | + {juju: new sandboxModule.GoJujuAPI({state: state})}); |
35 | + } else { |
36 | + throw 'unrecognized backend type: ' + apiBackend; |
37 | + } |
38 | + |
39 | } |
40 | this.env = juju.newEnvironment(envOptions, apiBackend); |
41 | } |
42 | @@ -938,7 +945,7 @@ |
43 | // If the Juju environment is not connected, exit without letting the |
44 | // route dispatch proceed. On env connection change, the app will |
45 | // re-dispatch and this route callback will be executed again. |
46 | - if (!this.env.get('connected')) { |
47 | + if (!this.env || !this.env.get('connected')) { |
48 | return; |
49 | } |
50 | var credentials = this.env.getCredentials(); |
51 | |
52 | === modified file 'app/models/models.js' |
53 | --- app/models/models.js 2013-06-12 15:41:45 +0000 |
54 | +++ app/models/models.js 2013-06-13 12:44:26 +0000 |
55 | @@ -344,7 +344,7 @@ |
56 | service.set('aggregated_status', aggregate); |
57 | |
58 | // Set Google Analytics tracking event. |
59 | - if (previous_unit_count !== sum) { |
60 | + if (previous_unit_count !== sum && window._gaq) { |
61 | window._gaq.push(['_trackEvent', 'Service Stats', 'Update', |
62 | service.get('id'), sum]); |
63 | } |
64 | |
65 | === modified file 'app/store/env/sandbox.js' |
66 | --- app/store/env/sandbox.js 2013-05-17 14:51:05 +0000 |
67 | +++ app/store/env/sandbox.js 2013-06-13 12:44:26 +0000 |
68 | @@ -19,7 +19,7 @@ |
69 | 'use strict'; |
70 | |
71 | /** |
72 | -Sandbox APIs mimicking communications with the Go and Juju backends. |
73 | +Sandbox APIs mimicking communications with the Go and Python backends. |
74 | |
75 | @module env |
76 | @submodule env.sandbox |
77 | @@ -682,6 +682,208 @@ |
78 | }); |
79 | |
80 | sandboxModule.PyJujuAPI = PyJujuAPI; |
81 | + |
82 | + /** |
83 | + A sandbox Juju environment using the Go API. |
84 | + |
85 | + @class GoJujuAPI |
86 | + */ |
87 | + function GoJujuAPI(config) { |
88 | + GoJujuAPI.superclass.constructor.apply(this, arguments); |
89 | + } |
90 | + |
91 | + GoJujuAPI.NAME = 'sandbox-py-juju-api'; |
92 | + GoJujuAPI.ATTRS = { |
93 | + state: {}, |
94 | + client: {} |
95 | + }; |
96 | + |
97 | + Y.extend(GoJujuAPI, Y.Base, { |
98 | + |
99 | + /** |
100 | + Initializes. |
101 | + |
102 | + @method initializer |
103 | + @return {undefined} Nothing. |
104 | + */ |
105 | + initializer: function() { |
106 | + this.connected = false; |
107 | + }, |
108 | + |
109 | + /** |
110 | + Opens the connection to the sandbox Juju environment. |
111 | + Called by ClientConnection, which sends itself. |
112 | + |
113 | + @method open |
114 | + @param {Object} client A ClientConnection. |
115 | + @return {undefined} Nothing. |
116 | + */ |
117 | + open: function(client) { |
118 | + if (!this.connected) { |
119 | + this.connected = true; |
120 | + this.set('client', client); |
121 | + } else if (this.get('client') !== client) { |
122 | + throw 'INVALID_STATE_ERR : Connection is open to another client.'; |
123 | + } |
124 | + }, |
125 | + |
126 | + /** |
127 | + Closes the connection to the sandbox Juju environment. |
128 | + Called by ClientConnection. |
129 | + |
130 | + @method close |
131 | + @return {undefined} Nothing. |
132 | + */ |
133 | + close: function() { |
134 | + if (this.connected) { |
135 | + this.connected = false; |
136 | + this.set('client', undefined); |
137 | + } |
138 | + }, |
139 | + |
140 | + /** |
141 | + Do any extra work to destroy the object. |
142 | + |
143 | + @method destructor |
144 | + @return {undefined} Nothing. |
145 | + */ |
146 | + destructor: function() { |
147 | + this.close(); |
148 | + }, |
149 | + |
150 | + /** |
151 | + Receives messages from the client and dispatches them. |
152 | + |
153 | + @method receive |
154 | + @param {Object} data A hash of data sent from the client. |
155 | + @return {undefined} Nothing. |
156 | + */ |
157 | + receive: function(data) { |
158 | + console.log('client message', data); |
159 | + if (this.connected) { |
160 | + this['handle' + data.Type + data.Request](data, |
161 | + this.get('client'), this.get('state')); |
162 | + } else { |
163 | + throw CLOSEDERROR; |
164 | + } |
165 | + }, |
166 | + |
167 | + /** |
168 | + Handle Login messages to the state object. |
169 | + |
170 | + @method handleAdminLogin |
171 | + @param {Object} data The contents of the API arguments. |
172 | + @param {Object} client The active ClientConnection. |
173 | + @param {Object} state An instance of FakeBackend. |
174 | + */ |
175 | + handleAdminLogin: function(data, client, state) { |
176 | + data.Error = !state.login(data.Params.AuthTag, data.Params.Password); |
177 | + client.receive(data); |
178 | + }, |
179 | + |
180 | + /** |
181 | + Handle EnvironmentView messages. |
182 | + |
183 | + @method handleClientServiceGet |
184 | + @param {Object} data The contents of the API arguments. |
185 | + @param {Object} client The active ClientConnection. |
186 | + @param {Object} state An instance of FakeBackend. |
187 | + */ |
188 | + handleClientEnvironmentInfo: function(data, client, state) { |
189 | + client.receive({ |
190 | + ProviderType: state.get('providerType'), |
191 | + DefaultSeries: state.get('defaultSeries'), |
192 | + Name: 'Sandbox' |
193 | + }); |
194 | + }, |
195 | + |
196 | + /** |
197 | + Handle WatchAll messages. |
198 | + |
199 | + @method handleClientWatchAll |
200 | + @param {Object} data The contents of the API arguments. |
201 | + @param {Object} client The active ClientConnection. |
202 | + @param {Object} state An instance of FakeBackend. |
203 | + */ |
204 | + handleClientWatchAll: function(data, client, state) { |
205 | + // TODO wire up delta stream to "Next" responses here. |
206 | + client.receive({Response: {AllWatcherId: '42'}}); |
207 | + }, |
208 | + |
209 | + /** |
210 | + Handle AllWatcher Next messages. |
211 | + |
212 | + @method handleAllWatcherNext |
213 | + @param {Object} data The contents of the API arguments. |
214 | + @param {Object} client The active ClientConnection. |
215 | + @param {Object} state An instance of FakeBackend. |
216 | + */ |
217 | + handleAllWatcherNext: function(data, client, state) { |
218 | + // This is a noop for the moment because it must exist but the current |
219 | + // functionality does not depend on it having any effect. |
220 | + // TODO See if there are any changes and if so, send them. |
221 | + }, |
222 | + |
223 | + /** |
224 | + Handle ServiceDeploy messages |
225 | + |
226 | + @method handleClientServiceDeploy |
227 | + @param {Object} data The contents of the API arguments. |
228 | + @param {Object} client The active ClientConnection. |
229 | + @param {Object} state An instance of FakeBackend. |
230 | + */ |
231 | + handleClientServiceDeploy: function(data, client, state) { |
232 | + var callback = function(result) { |
233 | + var response = {RequestId: data.RequestId}; |
234 | + if (result.error) { |
235 | + response.Error = result.error; |
236 | + } |
237 | + client.receive(response); |
238 | + }; |
239 | + state.deploy(data.Params.CharmUrl, callback, { |
240 | + name: data.Params.ServiceName, |
241 | + config: data.Params.Config, |
242 | + configYAML: data.Params.ConfigYAML, |
243 | + unitCount: data.Params.NumUnits |
244 | + }); |
245 | + }, |
246 | + |
247 | + /** |
248 | + Handle SetAnnotations messages |
249 | + |
250 | + @method handleClientSetAnnotations |
251 | + @param {Object} data The contents of the API arguments. |
252 | + @param {Object} client The active ClientConnection. |
253 | + @param {Object} state An instance of FakeBackend. |
254 | + */ |
255 | + handleClientSetAnnotations: function(data, client, state) { |
256 | + var serviceId = /service-([^ ]*)$/.exec(data.Params.Tag)[1]; |
257 | + var reply = state.updateAnnotations(serviceId, data.Params.Pairs); |
258 | + client.receive({ |
259 | + RequestId: data.RequestId, |
260 | + Error: reply.error}); |
261 | + }, |
262 | + |
263 | + /** |
264 | + Handle ServiceGet messages |
265 | + |
266 | + @method handleClientServiceGet |
267 | + @param {Object} data The contents of the API arguments. |
268 | + @param {Object} client The active ClientConnection. |
269 | + @param {Object} state An instance of FakeBackend. |
270 | + */ |
271 | + handleClientServiceGet: function(data, client, state) { |
272 | + var reply = state.getService(data.Params.ServiceName); |
273 | + // TODO Include the optional Config or Constraints in the response. |
274 | + client.receive({ |
275 | + RequestId: data.RequestId, |
276 | + Error: reply.error, |
277 | + Response: {Service: data.Params.ServiceName}}); |
278 | + } |
279 | + |
280 | + }); |
281 | + |
282 | + sandboxModule.GoJujuAPI = GoJujuAPI; |
283 | }, '0.1.0', { |
284 | requires: [ |
285 | 'base', |
286 | |
287 | === modified file 'lib/deploy_charm_for_testing.py' |
288 | --- lib/deploy_charm_for_testing.py 2013-05-22 11:23:34 +0000 |
289 | +++ lib/deploy_charm_for_testing.py 2013-06-13 12:44:26 +0000 |
290 | @@ -88,7 +88,6 @@ |
291 | while True: |
292 | state = get_state() |
293 | if 'error' in state: |
294 | - print(get_status()) |
295 | raise RuntimeError('error deploying service') |
296 | if state == 'started': |
297 | break |
298 | |
299 | === modified file 'test/test_app.js' |
300 | --- test/test_app.js 2013-06-11 12:23:12 +0000 |
301 | +++ test/test_app.js 2013-06-13 12:44:26 +0000 |
302 | @@ -112,6 +112,15 @@ |
303 | assert.equal(app.env.get('password'), undefined); |
304 | }); |
305 | |
306 | + it('should report backend misconfiguration', function() { |
307 | + var config = { |
308 | + sandbox: true, |
309 | + apiBackend: 'not really a backend'}; |
310 | + assert.throws( |
311 | + function() {var stupidLinter = new Y.juju.App(config);}, |
312 | + 'unrecognized backend type: not really a backend'); |
313 | + }); |
314 | + |
315 | it('should propagate login credentials from the configuration', |
316 | function(done) { |
317 | var the_username = 'nehi'; |
318 | @@ -487,7 +496,7 @@ |
319 | })(); |
320 | |
321 | (function() { |
322 | - describe('Application sandbox', function() { |
323 | + describe('Application sandbox mode', function() { |
324 | var Y, app, container, utils; |
325 | |
326 | before(function(done) { |
327 | @@ -507,7 +516,7 @@ |
328 | } |
329 | }); |
330 | |
331 | - it('app instantiates correctly in sandbox mode.', function() { |
332 | + it('instantiates correctly', function() { |
333 | app = new Y.juju.App( |
334 | { container: container, |
335 | viewContainer: container, |
336 | |
337 | === modified file 'test/test_deploy_charm_for_testing.py' |
338 | --- test/test_deploy_charm_for_testing.py 2013-05-28 14:00:30 +0000 |
339 | +++ test/test_deploy_charm_for_testing.py 2013-06-13 12:44:26 +0000 |
340 | @@ -102,7 +102,7 @@ |
341 | self.__class__.options = options |
342 | |
343 | |
344 | -# Mock argparse |
345 | +# Fake argparse output. |
346 | def _options(**kwargs): |
347 | def _wrapper(): |
348 | class _o(dict): |
349 | @@ -113,14 +113,15 @@ |
350 | return result |
351 | return _wrapper |
352 | |
353 | + |
354 | class TestScript(unittest.TestCase): |
355 | """The main() function is the entry point when run as a script.""" |
356 | |
357 | def test_status_messages_are_displayed(self): |
358 | # While running, the script tells the user what is happening. |
359 | printed = [] |
360 | - main(options=_options(), print=printed.append, juju=noop, wait_for_service=noop, |
361 | - wait_for_machine=noop) |
362 | + main(options=_options(), print=printed.append, juju=noop, |
363 | + wait_for_service=noop, wait_for_machine=noop) |
364 | self.assertSequenceEqual( |
365 | printed, |
366 | ['Bootstrapping...', |
367 | @@ -136,22 +137,26 @@ |
368 | def juju(s): |
369 | juju_commands.append(s) |
370 | |
371 | - main(options=_options(origin='lp:foo'), print=noop, juju=juju, wait_for_service=noop, |
372 | - make_config_file=MakeConfigFile, wait_for_machine=noop, |
373 | - make_environments_yaml=noop) |
374 | + main(options=_options(origin='lp:foo'), print=noop, juju=juju, |
375 | + wait_for_service=noop, make_config_file=MakeConfigFile, |
376 | + wait_for_machine=noop, make_environments_yaml=noop) |
377 | options = MakeConfigFile.options |
378 | deploy_command = juju_commands[1] |
379 | self.assertIn('--config my-config-file.yaml', deploy_command) |
380 | - self.assertDictEqual({'serve-tests': True, 'staging': True, |
381 | - 'juju-gui-source': 'lp:foo', 'secure': False}, options) |
382 | + self.assertDictEqual( |
383 | + {'serve-tests': True, |
384 | + 'staging': True, |
385 | + 'juju-gui-source': 'lp:foo', |
386 | + 'secure': False}, |
387 | + options) |
388 | |
389 | def test_providing_a_branch(self): |
390 | # If the user provides a branch name on the command line, it will be |
391 | # passed to the charm. |
392 | printed = [] |
393 | |
394 | - main(options=_options(origin='lp:foo'), print=printed.append, juju=noop, |
395 | - wait_for_service=noop, make_config_file=MakeConfigFile, |
396 | + main(options=_options(origin='lp:foo'), print=printed.append, |
397 | + juju=noop, wait_for_service=noop, make_config_file=MakeConfigFile, |
398 | wait_for_machine=noop, make_environments_yaml=noop) |
399 | options = MakeConfigFile.options |
400 | self.assertSequenceEqual( |
401 | @@ -164,6 +169,7 @@ |
402 | self.assertIn('juju-gui-source', options) |
403 | self.assertEqual('lp:foo', options['juju-gui-source']) |
404 | |
405 | + |
406 | class TestParseImageData(unittest.TestCase): |
407 | """Test the nova machine image data parsing.""" |
408 | |
409 | @@ -209,13 +215,17 @@ |
410 | |
411 | def test_picks_last(self): |
412 | data = '\n'.join([ |
413 | - "| abc-123 | ubuntu-released/ubuntu-precise-12.04-amd64-1.img | ACTIVE | |", |
414 | - "| def-123 | smoser-proposed/ubuntu-precise-12.04-amd64-2.img | ACTIVE | |", |
415 | - "| fad-123 | ubuntu-released/ubuntu-precise-12.04-amd64-3.img | ACTIVE | |"]) |
416 | + '| abc-123 | ubuntu-released/ubuntu-precise-12.04-amd64-1.img ' + |
417 | + '| ACTIVE | |', |
418 | + '| def-123 | smoser-proposed/ubuntu-precise-12.04-amd64-2.img ' + |
419 | + '| ACTIVE | |', |
420 | + '| fad-123 | ubuntu-released/ubuntu-precise-12.04-amd64-3.img ' + |
421 | + '| ACTIVE | |']) |
422 | img_id, desc = parse_image_data(data) |
423 | self.assertEqual('fad-123', img_id) |
424 | self.assertEqual( |
425 | - 'ubuntu-released/ubuntu-precise-12.04-amd64-3.img', desc) |
426 | + 'ubuntu-released/ubuntu-precise-12.04-amd64-3.img', |
427 | + desc) |
428 | |
429 | |
430 | if __name__ == '__main__': |
431 | |
432 | === modified file 'test/test_env_go.js' |
433 | --- test/test_env_go.js 2013-05-21 16:30:38 +0000 |
434 | +++ test/test_env_go.js 2013-06-13 12:44:26 +0000 |
435 | @@ -107,6 +107,8 @@ |
436 | // In order to avoid rewriting all of these go tests we need to destroy |
437 | // the env created in the beforeEach |
438 | env.destroy(); |
439 | + // We need to clear any credentials stored in sessionStorage. |
440 | + env.setCredentials(null); |
441 | oldHandleLogin = Y.juju.environments.GoEnvironment.handleLogin; |
442 | Y.juju.environments.GoEnvironment.handleLogin = function() {}; |
443 | conn = new utils.SocketStub(); |
444 | |
445 | === modified file 'test/test_sandbox.js' |
446 | --- test/test_sandbox.js 2013-05-17 14:51:05 +0000 |
447 | +++ test/test_sandbox.js 2013-06-13 12:44:26 +0000 |
448 | @@ -182,10 +182,8 @@ |
449 | var requires = [ |
450 | 'juju-env-sandbox', 'juju-tests-utils', 'juju-env-python', |
451 | 'juju-models', 'promise']; |
452 | - var Y, sandboxModule, ClientConnection, PyJujuAPI, environmentsModule, |
453 | - state, juju, client, env, utils, cleanups; |
454 | - |
455 | - this.timeout(2000); // Long timeouts make async failures hard to detect. |
456 | + var Y, sandboxModule, ClientConnection, environmentsModule, state, juju, |
457 | + client, env, utils, cleanups; |
458 | |
459 | before(function(done) { |
460 | Y = YUI(GlobalConfig).use(requires, function(Y) { |
461 | @@ -1430,8 +1428,6 @@ |
462 | } |
463 | |
464 | it('should support export', function(done) { |
465 | - this.timeout(250); |
466 | - |
467 | client.open(); |
468 | promise(state, 'deploy', 'cs:wordpress') |
469 | .then(promise(state, 'deploy', 'cs:mysql')) |
470 | @@ -1447,7 +1443,6 @@ |
471 | }); |
472 | |
473 | it('should support import', function(done) { |
474 | - this.timeout(300); |
475 | var fixture = utils.loadFixture('data/sample-fakebackend.json', false); |
476 | |
477 | client.onmessage = function() { |
478 | @@ -1475,4 +1470,197 @@ |
479 | |
480 | }); |
481 | |
482 | + |
483 | + describe('sandbox.GoJujuAPI', function() { |
484 | + var requires = [ |
485 | + 'juju-env-sandbox', 'juju-tests-utils', 'juju-env-go', |
486 | + 'juju-models', 'promise']; |
487 | + var Y, sandboxModule, ClientConnection, environmentsModule, state, juju, |
488 | + client, env, utils; |
489 | + |
490 | + before(function(done) { |
491 | + Y = YUI(GlobalConfig).use(requires, function(Y) { |
492 | + sandboxModule = Y.namespace('juju.environments.sandbox'); |
493 | + environmentsModule = Y.namespace('juju.environments'); |
494 | + utils = Y.namespace('juju-tests.utils'); |
495 | + done(); |
496 | + }); |
497 | + }); |
498 | + |
499 | + beforeEach(function() { |
500 | + state = utils.makeFakeBackendWithCharmStore(); |
501 | + juju = new sandboxModule.GoJujuAPI({state: state}); |
502 | + client = new sandboxModule.ClientConnection({juju: juju}); |
503 | + env = new environmentsModule.GoEnvironment({conn: client}); |
504 | + }); |
505 | + |
506 | + afterEach(function() { |
507 | + env.destroy(); |
508 | + client.destroy(); |
509 | + juju.destroy(); |
510 | + state.destroy(); |
511 | + }); |
512 | + |
513 | + it('opens successfully.', function() { |
514 | + assert.isFalse(juju.connected); |
515 | + assert.isUndefined(juju.get('client')); |
516 | + client.open(); |
517 | + assert.isTrue(juju.connected); |
518 | + assert.strictEqual(juju.get('client'), client); |
519 | + }); |
520 | + |
521 | + it('ignores "open" when already open to same client.', function() { |
522 | + client.receive = function() { |
523 | + assert.ok(false, 'The receive method should not be called.'); |
524 | + }; |
525 | + // Whitebox test: duplicate "open" state. |
526 | + juju.connected = true; |
527 | + juju.set('client', client); |
528 | + // This is effectively a re-open. |
529 | + client.open(); |
530 | + // The assert.ok above is the verification. |
531 | + }); |
532 | + |
533 | + it('refuses to open if already open to another client.', function() { |
534 | + // This is a simple way to make sure that we don't leave multiple |
535 | + // setInterval calls running. If for some reason we want more |
536 | + // simultaneous clients, that's fine, though that will require |
537 | + // reworking the delta code generally. |
538 | + juju.connected = true; |
539 | + juju.set('client', {receive: function() { |
540 | + assert.ok(false, 'The receive method should not have been called.'); |
541 | + }}); |
542 | + assert.throws( |
543 | + client.open.bind(client), |
544 | + 'INVALID_STATE_ERR : Connection is open to another client.'); |
545 | + }); |
546 | + |
547 | + it('closes successfully.', function() { |
548 | + client.open(); |
549 | + assert.isTrue(juju.connected); |
550 | + assert.notEqual(juju.get('client'), undefined); |
551 | + client.close(); |
552 | + assert.isFalse(juju.connected); |
553 | + assert.isUndefined(juju.get('client')); |
554 | + }); |
555 | + |
556 | + it('ignores "close" when already closed.', function() { |
557 | + // This simply shows that we do not raise an error. |
558 | + juju.close(); |
559 | + }); |
560 | + |
561 | + it('can dispatch on received information.', function(done) { |
562 | + var data = {Type: 'TheType', Request: 'TheRequest'}; |
563 | + juju.handleTheTypeTheRequest = function(received) { |
564 | + assert.notStrictEqual(received, data); |
565 | + assert.deepEqual(received, data); |
566 | + done(); |
567 | + }; |
568 | + client.open(); |
569 | + client.send(Y.JSON.stringify(data)); |
570 | + }); |
571 | + |
572 | + it('refuses to dispatch when closed.', function() { |
573 | + assert.throws( |
574 | + juju.receive.bind(juju, {}), |
575 | + 'INVALID_STATE_ERR : Connection is closed.' |
576 | + ); |
577 | + }); |
578 | + |
579 | + it('can log in.', function(done) { |
580 | + // See FakeBackend's authorizedUsers for these default authentication |
581 | + // values. |
582 | + var data = { |
583 | + Type: 'Admin', |
584 | + Request: 'Login', |
585 | + Params: { |
586 | + AuthTag: 'admin', |
587 | + Password: 'password' |
588 | + }, |
589 | + RequestId: 42 |
590 | + }; |
591 | + client.onmessage = function(received) { |
592 | + // Add in the error indicator so the deepEqual is comparing apples to |
593 | + // apples. |
594 | + data.Error = false; |
595 | + assert.deepEqual(Y.JSON.parse(received.data), data); |
596 | + assert.isTrue(state.get('authenticated')); |
597 | + done(); |
598 | + }; |
599 | + state.logout(); |
600 | + assert.isFalse(state.get('authenticated')); |
601 | + client.open(); |
602 | + client.send(Y.JSON.stringify(data)); |
603 | + }); |
604 | + |
605 | + it('can log in (environment integration).', function(done) { |
606 | + state.logout(); |
607 | + env.after('login', function() { |
608 | + assert.isTrue(env.userIsAuthenticated); |
609 | + done(); |
610 | + }); |
611 | + env.connect(); |
612 | + env.setCredentials({user: 'admin', password: 'password'}); |
613 | + env.login(); |
614 | + }); |
615 | + |
616 | + it('can deploy.', function(done) { |
617 | + // We begin logged in. See utils.makeFakeBackendWithCharmStore. |
618 | + var data = { |
619 | + Type: 'Client', |
620 | + Request: 'ServiceDeploy', |
621 | + Params: { |
622 | + CharmUrl: 'cs:wordpress', |
623 | + ServiceName: 'kumquat', |
624 | + ConfigYAML: 'funny: business', |
625 | + NumUnits: 2 |
626 | + }, |
627 | + RequestId: 42 |
628 | + }; |
629 | + client.onmessage = function(received) { |
630 | + var receivedData = Y.JSON.parse(received.data); |
631 | + assert.equal(receivedData.RequestId, data.RequestId); |
632 | + assert.isUndefined(receivedData.Error); |
633 | + assert.isObject( |
634 | + state.db.charms.getById('cs:precise/wordpress-10')); |
635 | + var service = state.db.services.getById('kumquat'); |
636 | + assert.isObject(service); |
637 | + assert.equal(service.get('charm'), 'cs:precise/wordpress-10'); |
638 | + assert.deepEqual(service.get('config'), {funny: 'business'}); |
639 | + var units = state.db.units.get_units_for_service(service); |
640 | + assert.lengthOf(units, 2); |
641 | + done(); |
642 | + }; |
643 | + client.open(); |
644 | + client.send(Y.JSON.stringify(data)); |
645 | + }); |
646 | + |
647 | + it('can deploy (environment integration).', function() { |
648 | + env.connect(); |
649 | + // We begin logged in. See utils.makeFakeBackendWithCharmStore. |
650 | + var callback = function(result) { |
651 | + assert.isUndefined(result.err); |
652 | + assert.equal(result.charm_url, 'cs:wordpress'); |
653 | + var service = state.db.services.getById('kumquat'); |
654 | + assert.equal(service.get('charm'), 'cs:precise/wordpress-10'); |
655 | + assert.deepEqual(service.get('config'), {llama: 'pajama'}); |
656 | + }; |
657 | + env.deploy( |
658 | + 'cs:wordpress', 'kumquat', {llama: 'pajama'}, null, 1, callback); |
659 | + }); |
660 | + |
661 | + it('can communicate errors after attempting to deploy', function(done) { |
662 | + env.connect(); |
663 | + state.deploy('cs:wordpress', function() {}); |
664 | + var callback = function(result) { |
665 | + assert.equal( |
666 | + result.err, 'A service with this name already exists.'); |
667 | + done(); |
668 | + }; |
669 | + env.deploy('cs:wordpress', undefined, undefined, undefined, 1, |
670 | + callback); |
671 | + }); |
672 | + |
673 | + }); |
674 | + |
675 | })(); |
LGTM with questions
https:/ /codereview. appspot. com/10235045/ diff/1/ app/store/ env/sandbox. js env/sandbox. js (right):
File app/store/
https:/ /codereview. appspot. com/10235045/ diff/1/ app/store/ env/sandbox. js#newcode812 env/sandbox. js:812: client. receive( {Response: {AllWatcherId:
app/store/
'42'}});
Heh. :-)
https:/ /codereview. appspot. com/10235045/ diff/1/ app/store/ env/sandbox. js#newcode839 env/sandbox. js:839: var response = {RequestId:
app/store/
data.RequestId};
I guess we make a new response because Juju Core does not send back the
whole original request the way PyJuju does, right? If so, cool...though
I wonder if we should therefore remove the copying code in line 766
("Y.merge(data)" and the preceding comment).
https:/ /codereview. appspot. com/10235045/ diff/1/ app/store/ env/sandbox. js#newcode879 env/sandbox. js:879: // We do not provide the optional Config
app/store/
or Constraints in the response.
How do you get it then? Or do you mean that this will be in a future
branch? (If so, let's use TODO as you did elsewhere)
https:/ /codereview. appspot. com/10235045/