Merge lp:~benji/juju-gui/go-sandbox into lp:juju-gui/experimental

Proposed by Benji York
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
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.

https://codereview.appspot.com/10235045/

To post a comment you must log in.
Revision history for this message
Gary Poster (gary) wrote :

LGTM with questions

https://codereview.appspot.com/10235045/diff/1/app/store/env/sandbox.js
File app/store/env/sandbox.js (right):

https://codereview.appspot.com/10235045/diff/1/app/store/env/sandbox.js#newcode812
app/store/env/sandbox.js:812: client.receive({Response: {AllWatcherId:
'42'}});
Heh. :-)

https://codereview.appspot.com/10235045/diff/1/app/store/env/sandbox.js#newcode839
app/store/env/sandbox.js:839: var response = {RequestId:
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
app/store/env/sandbox.js:879: // We do not provide the optional Config
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/

Revision history for this message
Jeff Pihach (hatch) wrote :

LGTM Thanks this looks good. I am not sure about the method casing
senario :-)

https://codereview.appspot.com/10235045/diff/5001/app/store/env/sandbox.js
File app/store/env/sandbox.js (right):

https://codereview.appspot.com/10235045/diff/5001/app/store/env/sandbox.js#newcode774
app/store/env/sandbox.js:774: @method handle_Admin_Login
camel_Snake_Case? ;-)

https://codereview.appspot.com/10235045/diff/5001/test/test_sandbox.js
File test/test_sandbox.js (right):

https://codereview.appspot.com/10235045/diff/5001/test/test_sandbox.js#newcode1486
test/test_sandbox.js:1486: this.timeout(2000); // Long timeouts make
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://codereview.appspot.com/10235045/diff/5001/test/test_sandbox.js#newcode1506
test/test_sandbox.js:1506: Y.each(cleanups, function(f) {f();});
cleanups is never used.

https://codereview.appspot.com/10235045/

Revision history for this message
Jujugui Lander (jujugui-lander) wrote :

No commit message specified.

Revision history for this message
Jujugui Lander (jujugui-lander) wrote :

Voting does not meet specified criteria. Required: Approve >= 1, Disapprove == 0. Got: 1 Pending.

Revision history for this message
Diogo Matsubara (matsubara) :
review: Approve
Revision history for this message
Jujugui Lander (jujugui-lander) wrote :

The Jenkins job https://jenkins.qa.ubuntu.com/job/jujugui-merger-trunk/7/console reported an error when processing this lp:~benji/juju-gui/go-sandbox branch.
Not merging it.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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 })();

Subscribers

People subscribed via source and target branches