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
=== modified file 'app/app.js'
--- app/app.js 2013-06-12 17:55:05 +0000
+++ app/app.js 2013-06-13 12:44:26 +0000
@@ -360,8 +360,7 @@
360 }360 }
361 }361 }
362362
363363 if (window.flags && window.flags.websocket_capture) {
364 if (window.flags.websocket_capture) {
365 this.websocketLogging = new Y.juju.WebsocketLogging();364 this.websocketLogging = new Y.juju.WebsocketLogging();
366 }365 }
367366
@@ -448,7 +447,7 @@
448 };447 };
449 var apiBackend = this.get('apiBackend');448 var apiBackend = this.get('apiBackend');
450 // The sandbox mode does not support the Go API (yet?).449 // The sandbox mode does not support the Go API (yet?).
451 if (this.get('sandbox') && apiBackend === 'python') {450 if (this.get('sandbox')) {
452 var sandboxModule = Y.namespace('juju.environments.sandbox');451 var sandboxModule = Y.namespace('juju.environments.sandbox');
453 var State = Y.namespace('juju.environments').FakeBackend;452 var State = Y.namespace('juju.environments').FakeBackend;
454 var state = new State({charmStore: this.charm_store});453 var state = new State({charmStore: this.charm_store});
@@ -457,8 +456,16 @@
457 credentials[envOptions.user] = envOptions.password;456 credentials[envOptions.user] = envOptions.password;
458 state.set('authorizedUsers', credentials);457 state.set('authorizedUsers', credentials);
459 }458 }
460 envOptions.conn = new sandboxModule.ClientConnection(459 if (apiBackend === 'python') {
461 {juju: new sandboxModule.PyJujuAPI({state: state})});460 envOptions.conn = new sandboxModule.ClientConnection(
461 {juju: new sandboxModule.PyJujuAPI({state: state})});
462 } else if (apiBackend === 'go') {
463 envOptions.conn = new sandboxModule.ClientConnection(
464 {juju: new sandboxModule.GoJujuAPI({state: state})});
465 } else {
466 throw 'unrecognized backend type: ' + apiBackend;
467 }
468
462 }469 }
463 this.env = juju.newEnvironment(envOptions, apiBackend);470 this.env = juju.newEnvironment(envOptions, apiBackend);
464 }471 }
@@ -938,7 +945,7 @@
938 // If the Juju environment is not connected, exit without letting the945 // If the Juju environment is not connected, exit without letting the
939 // route dispatch proceed. On env connection change, the app will946 // route dispatch proceed. On env connection change, the app will
940 // re-dispatch and this route callback will be executed again.947 // re-dispatch and this route callback will be executed again.
941 if (!this.env.get('connected')) {948 if (!this.env || !this.env.get('connected')) {
942 return;949 return;
943 }950 }
944 var credentials = this.env.getCredentials();951 var credentials = this.env.getCredentials();
945952
=== modified file 'app/models/models.js'
--- app/models/models.js 2013-06-12 15:41:45 +0000
+++ app/models/models.js 2013-06-13 12:44:26 +0000
@@ -344,7 +344,7 @@
344 service.set('aggregated_status', aggregate);344 service.set('aggregated_status', aggregate);
345345
346 // Set Google Analytics tracking event.346 // Set Google Analytics tracking event.
347 if (previous_unit_count !== sum) {347 if (previous_unit_count !== sum && window._gaq) {
348 window._gaq.push(['_trackEvent', 'Service Stats', 'Update',348 window._gaq.push(['_trackEvent', 'Service Stats', 'Update',
349 service.get('id'), sum]);349 service.get('id'), sum]);
350 }350 }
351351
=== modified file 'app/store/env/sandbox.js'
--- app/store/env/sandbox.js 2013-05-17 14:51:05 +0000
+++ app/store/env/sandbox.js 2013-06-13 12:44:26 +0000
@@ -19,7 +19,7 @@
19'use strict';19'use strict';
2020
21/**21/**
22Sandbox APIs mimicking communications with the Go and Juju backends.22Sandbox APIs mimicking communications with the Go and Python backends.
2323
24@module env24@module env
25@submodule env.sandbox25@submodule env.sandbox
@@ -682,6 +682,208 @@
682 });682 });
683683
684 sandboxModule.PyJujuAPI = PyJujuAPI;684 sandboxModule.PyJujuAPI = PyJujuAPI;
685
686 /**
687 A sandbox Juju environment using the Go API.
688
689 @class GoJujuAPI
690 */
691 function GoJujuAPI(config) {
692 GoJujuAPI.superclass.constructor.apply(this, arguments);
693 }
694
695 GoJujuAPI.NAME = 'sandbox-py-juju-api';
696 GoJujuAPI.ATTRS = {
697 state: {},
698 client: {}
699 };
700
701 Y.extend(GoJujuAPI, Y.Base, {
702
703 /**
704 Initializes.
705
706 @method initializer
707 @return {undefined} Nothing.
708 */
709 initializer: function() {
710 this.connected = false;
711 },
712
713 /**
714 Opens the connection to the sandbox Juju environment.
715 Called by ClientConnection, which sends itself.
716
717 @method open
718 @param {Object} client A ClientConnection.
719 @return {undefined} Nothing.
720 */
721 open: function(client) {
722 if (!this.connected) {
723 this.connected = true;
724 this.set('client', client);
725 } else if (this.get('client') !== client) {
726 throw 'INVALID_STATE_ERR : Connection is open to another client.';
727 }
728 },
729
730 /**
731 Closes the connection to the sandbox Juju environment.
732 Called by ClientConnection.
733
734 @method close
735 @return {undefined} Nothing.
736 */
737 close: function() {
738 if (this.connected) {
739 this.connected = false;
740 this.set('client', undefined);
741 }
742 },
743
744 /**
745 Do any extra work to destroy the object.
746
747 @method destructor
748 @return {undefined} Nothing.
749 */
750 destructor: function() {
751 this.close();
752 },
753
754 /**
755 Receives messages from the client and dispatches them.
756
757 @method receive
758 @param {Object} data A hash of data sent from the client.
759 @return {undefined} Nothing.
760 */
761 receive: function(data) {
762 console.log('client message', data);
763 if (this.connected) {
764 this['handle' + data.Type + data.Request](data,
765 this.get('client'), this.get('state'));
766 } else {
767 throw CLOSEDERROR;
768 }
769 },
770
771 /**
772 Handle Login messages to the state object.
773
774 @method handleAdminLogin
775 @param {Object} data The contents of the API arguments.
776 @param {Object} client The active ClientConnection.
777 @param {Object} state An instance of FakeBackend.
778 */
779 handleAdminLogin: function(data, client, state) {
780 data.Error = !state.login(data.Params.AuthTag, data.Params.Password);
781 client.receive(data);
782 },
783
784 /**
785 Handle EnvironmentView messages.
786
787 @method handleClientServiceGet
788 @param {Object} data The contents of the API arguments.
789 @param {Object} client The active ClientConnection.
790 @param {Object} state An instance of FakeBackend.
791 */
792 handleClientEnvironmentInfo: function(data, client, state) {
793 client.receive({
794 ProviderType: state.get('providerType'),
795 DefaultSeries: state.get('defaultSeries'),
796 Name: 'Sandbox'
797 });
798 },
799
800 /**
801 Handle WatchAll messages.
802
803 @method handleClientWatchAll
804 @param {Object} data The contents of the API arguments.
805 @param {Object} client The active ClientConnection.
806 @param {Object} state An instance of FakeBackend.
807 */
808 handleClientWatchAll: function(data, client, state) {
809 // TODO wire up delta stream to "Next" responses here.
810 client.receive({Response: {AllWatcherId: '42'}});
811 },
812
813 /**
814 Handle AllWatcher Next messages.
815
816 @method handleAllWatcherNext
817 @param {Object} data The contents of the API arguments.
818 @param {Object} client The active ClientConnection.
819 @param {Object} state An instance of FakeBackend.
820 */
821 handleAllWatcherNext: function(data, client, state) {
822 // This is a noop for the moment because it must exist but the current
823 // functionality does not depend on it having any effect.
824 // TODO See if there are any changes and if so, send them.
825 },
826
827 /**
828 Handle ServiceDeploy messages
829
830 @method handleClientServiceDeploy
831 @param {Object} data The contents of the API arguments.
832 @param {Object} client The active ClientConnection.
833 @param {Object} state An instance of FakeBackend.
834 */
835 handleClientServiceDeploy: function(data, client, state) {
836 var callback = function(result) {
837 var response = {RequestId: data.RequestId};
838 if (result.error) {
839 response.Error = result.error;
840 }
841 client.receive(response);
842 };
843 state.deploy(data.Params.CharmUrl, callback, {
844 name: data.Params.ServiceName,
845 config: data.Params.Config,
846 configYAML: data.Params.ConfigYAML,
847 unitCount: data.Params.NumUnits
848 });
849 },
850
851 /**
852 Handle SetAnnotations messages
853
854 @method handleClientSetAnnotations
855 @param {Object} data The contents of the API arguments.
856 @param {Object} client The active ClientConnection.
857 @param {Object} state An instance of FakeBackend.
858 */
859 handleClientSetAnnotations: function(data, client, state) {
860 var serviceId = /service-([^ ]*)$/.exec(data.Params.Tag)[1];
861 var reply = state.updateAnnotations(serviceId, data.Params.Pairs);
862 client.receive({
863 RequestId: data.RequestId,
864 Error: reply.error});
865 },
866
867 /**
868 Handle ServiceGet messages
869
870 @method handleClientServiceGet
871 @param {Object} data The contents of the API arguments.
872 @param {Object} client The active ClientConnection.
873 @param {Object} state An instance of FakeBackend.
874 */
875 handleClientServiceGet: function(data, client, state) {
876 var reply = state.getService(data.Params.ServiceName);
877 // TODO Include the optional Config or Constraints in the response.
878 client.receive({
879 RequestId: data.RequestId,
880 Error: reply.error,
881 Response: {Service: data.Params.ServiceName}});
882 }
883
884 });
885
886 sandboxModule.GoJujuAPI = GoJujuAPI;
685}, '0.1.0', {887}, '0.1.0', {
686 requires: [888 requires: [
687 'base',889 'base',
688890
=== modified file 'lib/deploy_charm_for_testing.py'
--- lib/deploy_charm_for_testing.py 2013-05-22 11:23:34 +0000
+++ lib/deploy_charm_for_testing.py 2013-06-13 12:44:26 +0000
@@ -88,7 +88,6 @@
88 while True:88 while True:
89 state = get_state()89 state = get_state()
90 if 'error' in state:90 if 'error' in state:
91 print(get_status())
92 raise RuntimeError('error deploying service')91 raise RuntimeError('error deploying service')
93 if state == 'started':92 if state == 'started':
94 break93 break
9594
=== modified file 'test/test_app.js'
--- test/test_app.js 2013-06-11 12:23:12 +0000
+++ test/test_app.js 2013-06-13 12:44:26 +0000
@@ -112,6 +112,15 @@
112 assert.equal(app.env.get('password'), undefined);112 assert.equal(app.env.get('password'), undefined);
113 });113 });
114114
115 it('should report backend misconfiguration', function() {
116 var config = {
117 sandbox: true,
118 apiBackend: 'not really a backend'};
119 assert.throws(
120 function() {var stupidLinter = new Y.juju.App(config);},
121 'unrecognized backend type: not really a backend');
122 });
123
115 it('should propagate login credentials from the configuration',124 it('should propagate login credentials from the configuration',
116 function(done) {125 function(done) {
117 var the_username = 'nehi';126 var the_username = 'nehi';
@@ -487,7 +496,7 @@
487})();496})();
488497
489(function() {498(function() {
490 describe('Application sandbox', function() {499 describe('Application sandbox mode', function() {
491 var Y, app, container, utils;500 var Y, app, container, utils;
492501
493 before(function(done) {502 before(function(done) {
@@ -507,7 +516,7 @@
507 }516 }
508 });517 });
509518
510 it('app instantiates correctly in sandbox mode.', function() {519 it('instantiates correctly', function() {
511 app = new Y.juju.App(520 app = new Y.juju.App(
512 { container: container,521 { container: container,
513 viewContainer: container,522 viewContainer: container,
514523
=== modified file 'test/test_deploy_charm_for_testing.py'
--- test/test_deploy_charm_for_testing.py 2013-05-28 14:00:30 +0000
+++ test/test_deploy_charm_for_testing.py 2013-06-13 12:44:26 +0000
@@ -102,7 +102,7 @@
102 self.__class__.options = options102 self.__class__.options = options
103103
104104
105# Mock argparse105# Fake argparse output.
106def _options(**kwargs):106def _options(**kwargs):
107 def _wrapper():107 def _wrapper():
108 class _o(dict):108 class _o(dict):
@@ -113,14 +113,15 @@
113 return result113 return result
114 return _wrapper114 return _wrapper
115115
116
116class TestScript(unittest.TestCase):117class TestScript(unittest.TestCase):
117 """The main() function is the entry point when run as a script."""118 """The main() function is the entry point when run as a script."""
118119
119 def test_status_messages_are_displayed(self):120 def test_status_messages_are_displayed(self):
120 # While running, the script tells the user what is happening.121 # While running, the script tells the user what is happening.
121 printed = []122 printed = []
122 main(options=_options(), print=printed.append, juju=noop, wait_for_service=noop,123 main(options=_options(), print=printed.append, juju=noop,
123 wait_for_machine=noop)124 wait_for_service=noop, wait_for_machine=noop)
124 self.assertSequenceEqual(125 self.assertSequenceEqual(
125 printed,126 printed,
126 ['Bootstrapping...',127 ['Bootstrapping...',
@@ -136,22 +137,26 @@
136 def juju(s):137 def juju(s):
137 juju_commands.append(s)138 juju_commands.append(s)
138139
139 main(options=_options(origin='lp:foo'), print=noop, juju=juju, wait_for_service=noop,140 main(options=_options(origin='lp:foo'), print=noop, juju=juju,
140 make_config_file=MakeConfigFile, wait_for_machine=noop,141 wait_for_service=noop, make_config_file=MakeConfigFile,
141 make_environments_yaml=noop)142 wait_for_machine=noop, make_environments_yaml=noop)
142 options = MakeConfigFile.options143 options = MakeConfigFile.options
143 deploy_command = juju_commands[1]144 deploy_command = juju_commands[1]
144 self.assertIn('--config my-config-file.yaml', deploy_command)145 self.assertIn('--config my-config-file.yaml', deploy_command)
145 self.assertDictEqual({'serve-tests': True, 'staging': True,146 self.assertDictEqual(
146 'juju-gui-source': 'lp:foo', 'secure': False}, options)147 {'serve-tests': True,
148 'staging': True,
149 'juju-gui-source': 'lp:foo',
150 'secure': False},
151 options)
147152
148 def test_providing_a_branch(self):153 def test_providing_a_branch(self):
149 # If the user provides a branch name on the command line, it will be154 # If the user provides a branch name on the command line, it will be
150 # passed to the charm.155 # passed to the charm.
151 printed = []156 printed = []
152157
153 main(options=_options(origin='lp:foo'), print=printed.append, juju=noop,158 main(options=_options(origin='lp:foo'), print=printed.append,
154 wait_for_service=noop, make_config_file=MakeConfigFile,159 juju=noop, wait_for_service=noop, make_config_file=MakeConfigFile,
155 wait_for_machine=noop, make_environments_yaml=noop)160 wait_for_machine=noop, make_environments_yaml=noop)
156 options = MakeConfigFile.options161 options = MakeConfigFile.options
157 self.assertSequenceEqual(162 self.assertSequenceEqual(
@@ -164,6 +169,7 @@
164 self.assertIn('juju-gui-source', options)169 self.assertIn('juju-gui-source', options)
165 self.assertEqual('lp:foo', options['juju-gui-source'])170 self.assertEqual('lp:foo', options['juju-gui-source'])
166171
172
167class TestParseImageData(unittest.TestCase):173class TestParseImageData(unittest.TestCase):
168 """Test the nova machine image data parsing."""174 """Test the nova machine image data parsing."""
169175
@@ -209,13 +215,17 @@
209215
210 def test_picks_last(self):216 def test_picks_last(self):
211 data = '\n'.join([217 data = '\n'.join([
212 "| abc-123 | ubuntu-released/ubuntu-precise-12.04-amd64-1.img | ACTIVE | |",218 '| abc-123 | ubuntu-released/ubuntu-precise-12.04-amd64-1.img ' +
213 "| def-123 | smoser-proposed/ubuntu-precise-12.04-amd64-2.img | ACTIVE | |",219 '| ACTIVE | |',
214 "| fad-123 | ubuntu-released/ubuntu-precise-12.04-amd64-3.img | ACTIVE | |"])220 '| def-123 | smoser-proposed/ubuntu-precise-12.04-amd64-2.img ' +
221 '| ACTIVE | |',
222 '| fad-123 | ubuntu-released/ubuntu-precise-12.04-amd64-3.img ' +
223 '| ACTIVE | |'])
215 img_id, desc = parse_image_data(data)224 img_id, desc = parse_image_data(data)
216 self.assertEqual('fad-123', img_id)225 self.assertEqual('fad-123', img_id)
217 self.assertEqual(226 self.assertEqual(
218 'ubuntu-released/ubuntu-precise-12.04-amd64-3.img', desc)227 'ubuntu-released/ubuntu-precise-12.04-amd64-3.img',
228 desc)
219229
220230
221if __name__ == '__main__':231if __name__ == '__main__':
222232
=== modified file 'test/test_env_go.js'
--- test/test_env_go.js 2013-05-21 16:30:38 +0000
+++ test/test_env_go.js 2013-06-13 12:44:26 +0000
@@ -107,6 +107,8 @@
107 // In order to avoid rewriting all of these go tests we need to destroy107 // In order to avoid rewriting all of these go tests we need to destroy
108 // the env created in the beforeEach108 // the env created in the beforeEach
109 env.destroy();109 env.destroy();
110 // We need to clear any credentials stored in sessionStorage.
111 env.setCredentials(null);
110 oldHandleLogin = Y.juju.environments.GoEnvironment.handleLogin;112 oldHandleLogin = Y.juju.environments.GoEnvironment.handleLogin;
111 Y.juju.environments.GoEnvironment.handleLogin = function() {};113 Y.juju.environments.GoEnvironment.handleLogin = function() {};
112 conn = new utils.SocketStub();114 conn = new utils.SocketStub();
113115
=== modified file 'test/test_sandbox.js'
--- test/test_sandbox.js 2013-05-17 14:51:05 +0000
+++ test/test_sandbox.js 2013-06-13 12:44:26 +0000
@@ -182,10 +182,8 @@
182 var requires = [182 var requires = [
183 'juju-env-sandbox', 'juju-tests-utils', 'juju-env-python',183 'juju-env-sandbox', 'juju-tests-utils', 'juju-env-python',
184 'juju-models', 'promise'];184 'juju-models', 'promise'];
185 var Y, sandboxModule, ClientConnection, PyJujuAPI, environmentsModule,185 var Y, sandboxModule, ClientConnection, environmentsModule, state, juju,
186 state, juju, client, env, utils, cleanups;186 client, env, utils, cleanups;
187
188 this.timeout(2000); // Long timeouts make async failures hard to detect.
189187
190 before(function(done) {188 before(function(done) {
191 Y = YUI(GlobalConfig).use(requires, function(Y) {189 Y = YUI(GlobalConfig).use(requires, function(Y) {
@@ -1430,8 +1428,6 @@
1430 }1428 }
14311429
1432 it('should support export', function(done) {1430 it('should support export', function(done) {
1433 this.timeout(250);
1434
1435 client.open();1431 client.open();
1436 promise(state, 'deploy', 'cs:wordpress')1432 promise(state, 'deploy', 'cs:wordpress')
1437 .then(promise(state, 'deploy', 'cs:mysql'))1433 .then(promise(state, 'deploy', 'cs:mysql'))
@@ -1447,7 +1443,6 @@
1447 });1443 });
14481444
1449 it('should support import', function(done) {1445 it('should support import', function(done) {
1450 this.timeout(300);
1451 var fixture = utils.loadFixture('data/sample-fakebackend.json', false);1446 var fixture = utils.loadFixture('data/sample-fakebackend.json', false);
14521447
1453 client.onmessage = function() {1448 client.onmessage = function() {
@@ -1475,4 +1470,197 @@
14751470
1476 });1471 });
14771472
1473
1474 describe('sandbox.GoJujuAPI', function() {
1475 var requires = [
1476 'juju-env-sandbox', 'juju-tests-utils', 'juju-env-go',
1477 'juju-models', 'promise'];
1478 var Y, sandboxModule, ClientConnection, environmentsModule, state, juju,
1479 client, env, utils;
1480
1481 before(function(done) {
1482 Y = YUI(GlobalConfig).use(requires, function(Y) {
1483 sandboxModule = Y.namespace('juju.environments.sandbox');
1484 environmentsModule = Y.namespace('juju.environments');
1485 utils = Y.namespace('juju-tests.utils');
1486 done();
1487 });
1488 });
1489
1490 beforeEach(function() {
1491 state = utils.makeFakeBackendWithCharmStore();
1492 juju = new sandboxModule.GoJujuAPI({state: state});
1493 client = new sandboxModule.ClientConnection({juju: juju});
1494 env = new environmentsModule.GoEnvironment({conn: client});
1495 });
1496
1497 afterEach(function() {
1498 env.destroy();
1499 client.destroy();
1500 juju.destroy();
1501 state.destroy();
1502 });
1503
1504 it('opens successfully.', function() {
1505 assert.isFalse(juju.connected);
1506 assert.isUndefined(juju.get('client'));
1507 client.open();
1508 assert.isTrue(juju.connected);
1509 assert.strictEqual(juju.get('client'), client);
1510 });
1511
1512 it('ignores "open" when already open to same client.', function() {
1513 client.receive = function() {
1514 assert.ok(false, 'The receive method should not be called.');
1515 };
1516 // Whitebox test: duplicate "open" state.
1517 juju.connected = true;
1518 juju.set('client', client);
1519 // This is effectively a re-open.
1520 client.open();
1521 // The assert.ok above is the verification.
1522 });
1523
1524 it('refuses to open if already open to another client.', function() {
1525 // This is a simple way to make sure that we don't leave multiple
1526 // setInterval calls running. If for some reason we want more
1527 // simultaneous clients, that's fine, though that will require
1528 // reworking the delta code generally.
1529 juju.connected = true;
1530 juju.set('client', {receive: function() {
1531 assert.ok(false, 'The receive method should not have been called.');
1532 }});
1533 assert.throws(
1534 client.open.bind(client),
1535 'INVALID_STATE_ERR : Connection is open to another client.');
1536 });
1537
1538 it('closes successfully.', function() {
1539 client.open();
1540 assert.isTrue(juju.connected);
1541 assert.notEqual(juju.get('client'), undefined);
1542 client.close();
1543 assert.isFalse(juju.connected);
1544 assert.isUndefined(juju.get('client'));
1545 });
1546
1547 it('ignores "close" when already closed.', function() {
1548 // This simply shows that we do not raise an error.
1549 juju.close();
1550 });
1551
1552 it('can dispatch on received information.', function(done) {
1553 var data = {Type: 'TheType', Request: 'TheRequest'};
1554 juju.handleTheTypeTheRequest = function(received) {
1555 assert.notStrictEqual(received, data);
1556 assert.deepEqual(received, data);
1557 done();
1558 };
1559 client.open();
1560 client.send(Y.JSON.stringify(data));
1561 });
1562
1563 it('refuses to dispatch when closed.', function() {
1564 assert.throws(
1565 juju.receive.bind(juju, {}),
1566 'INVALID_STATE_ERR : Connection is closed.'
1567 );
1568 });
1569
1570 it('can log in.', function(done) {
1571 // See FakeBackend's authorizedUsers for these default authentication
1572 // values.
1573 var data = {
1574 Type: 'Admin',
1575 Request: 'Login',
1576 Params: {
1577 AuthTag: 'admin',
1578 Password: 'password'
1579 },
1580 RequestId: 42
1581 };
1582 client.onmessage = function(received) {
1583 // Add in the error indicator so the deepEqual is comparing apples to
1584 // apples.
1585 data.Error = false;
1586 assert.deepEqual(Y.JSON.parse(received.data), data);
1587 assert.isTrue(state.get('authenticated'));
1588 done();
1589 };
1590 state.logout();
1591 assert.isFalse(state.get('authenticated'));
1592 client.open();
1593 client.send(Y.JSON.stringify(data));
1594 });
1595
1596 it('can log in (environment integration).', function(done) {
1597 state.logout();
1598 env.after('login', function() {
1599 assert.isTrue(env.userIsAuthenticated);
1600 done();
1601 });
1602 env.connect();
1603 env.setCredentials({user: 'admin', password: 'password'});
1604 env.login();
1605 });
1606
1607 it('can deploy.', function(done) {
1608 // We begin logged in. See utils.makeFakeBackendWithCharmStore.
1609 var data = {
1610 Type: 'Client',
1611 Request: 'ServiceDeploy',
1612 Params: {
1613 CharmUrl: 'cs:wordpress',
1614 ServiceName: 'kumquat',
1615 ConfigYAML: 'funny: business',
1616 NumUnits: 2
1617 },
1618 RequestId: 42
1619 };
1620 client.onmessage = function(received) {
1621 var receivedData = Y.JSON.parse(received.data);
1622 assert.equal(receivedData.RequestId, data.RequestId);
1623 assert.isUndefined(receivedData.Error);
1624 assert.isObject(
1625 state.db.charms.getById('cs:precise/wordpress-10'));
1626 var service = state.db.services.getById('kumquat');
1627 assert.isObject(service);
1628 assert.equal(service.get('charm'), 'cs:precise/wordpress-10');
1629 assert.deepEqual(service.get('config'), {funny: 'business'});
1630 var units = state.db.units.get_units_for_service(service);
1631 assert.lengthOf(units, 2);
1632 done();
1633 };
1634 client.open();
1635 client.send(Y.JSON.stringify(data));
1636 });
1637
1638 it('can deploy (environment integration).', function() {
1639 env.connect();
1640 // We begin logged in. See utils.makeFakeBackendWithCharmStore.
1641 var callback = function(result) {
1642 assert.isUndefined(result.err);
1643 assert.equal(result.charm_url, 'cs:wordpress');
1644 var service = state.db.services.getById('kumquat');
1645 assert.equal(service.get('charm'), 'cs:precise/wordpress-10');
1646 assert.deepEqual(service.get('config'), {llama: 'pajama'});
1647 };
1648 env.deploy(
1649 'cs:wordpress', 'kumquat', {llama: 'pajama'}, null, 1, callback);
1650 });
1651
1652 it('can communicate errors after attempting to deploy', function(done) {
1653 env.connect();
1654 state.deploy('cs:wordpress', function() {});
1655 var callback = function(result) {
1656 assert.equal(
1657 result.err, 'A service with this name already exists.');
1658 done();
1659 };
1660 env.deploy('cs:wordpress', undefined, undefined, undefined, 1,
1661 callback);
1662 });
1663
1664 });
1665
1478})();1666})();

Subscribers

People subscribed via source and target branches