Merge lp:~teknico/juju-gui/split-sandbox-test-file into lp:juju-gui/experimental
- split-sandbox-test-file
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 778 |
Proposed branch: | lp:~teknico/juju-gui/split-sandbox-test-file |
Merge into: | lp:juju-gui/experimental |
Diff against target: |
4136 lines (+2077/-2030) 4 files modified
test/index.html (+2/-0) test/test_sandbox.js (+0/-2030) test/test_sandbox_go.js (+728/-0) test/test_sandbox_python.js (+1347/-0) |
To merge this branch: | bzr merge lp:~teknico/juju-gui/split-sandbox-test-file |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju GUI Hackers | Pending | ||
Review via email: mp+172042@code.launchpad.net |
Commit message
Description of the change
Split test_sandbox.js into pieces.
The test_sandbox.js file is too big, 2200+ lines and 75 KByte.
This splits the Go and Python sandbox tests out of it into
test_sandbox_go.js and test_sandbox_
tests are left there.
Huge diff unavoidably, but it only moves code around, no other changes,
I promise. :-)
Nicola Larosa (teknico) wrote : | # |
Jeff Pihach (hatch) wrote : | # |
LGTM Thanks for splitting these up
Francesco Banconi (frankban) wrote : | # |
LGTM, thank you.
- 778. By Nicola Larosa
-
Merge from trunk.
Nicola Larosa (teknico) wrote : | # |
*** Submitted:
Split test_sandbox.js into pieces.
The test_sandbox.js file is too big, 2200+ lines and 75 KByte.
This splits the Go and Python sandbox tests out of it into
test_sandbox_go.js and test_sandbox_
ClientConnection
tests are left there.
Huge diff unavoidably, but it only moves code around, no other changes,
I promise. :-)
R=jeff.pihach, frankban
CC=
https:/
Preview Diff
1 | === modified file 'test/index.html' |
2 | --- test/index.html 2013-06-26 16:28:56 +0000 |
3 | +++ test/index.html 2013-06-28 15:03:23 +0000 |
4 | @@ -103,6 +103,8 @@ |
5 | <script src="test_resizing_textarea.js"></script> |
6 | <script src="test_routing.js"></script> |
7 | <script src="test_sandbox.js"></script> |
8 | + <script src="test_sandbox_go.js"></script> |
9 | + <script src="test_sandbox_python.js"></script> |
10 | |
11 | <!-- FIXME: tests flicker, add container. --> |
12 | <script src="test_service_config_view.js"></script> |
13 | |
14 | === modified file 'test/test_sandbox.js' |
15 | --- test/test_sandbox.js 2013-06-27 11:30:59 +0000 |
16 | +++ test/test_sandbox.js 2013-06-28 15:03:23 +0000 |
17 | @@ -178,2034 +178,4 @@ |
18 | |
19 | }); |
20 | |
21 | - describe('sandbox.PyJujuAPI', function() { |
22 | - var requires = [ |
23 | - 'juju-env-sandbox', 'juju-tests-utils', 'juju-env-python', |
24 | - 'juju-models', 'promise']; |
25 | - var Y, sandboxModule, ClientConnection, environmentsModule, state, juju, |
26 | - client, env, utils, cleanups; |
27 | - |
28 | - before(function(done) { |
29 | - Y = YUI(GlobalConfig).use(requires, function(Y) { |
30 | - sandboxModule = Y.namespace('juju.environments.sandbox'); |
31 | - environmentsModule = Y.namespace('juju.environments'); |
32 | - utils = Y.namespace('juju-tests.utils'); |
33 | - // A global variable required for testing. |
34 | - window.flags = {}; |
35 | - done(); |
36 | - }); |
37 | - }); |
38 | - |
39 | - beforeEach(function() { |
40 | - state = utils.makeFakeBackendWithCharmStore(); |
41 | - juju = new sandboxModule.PyJujuAPI({state: state}); |
42 | - client = new sandboxModule.ClientConnection({juju: juju}); |
43 | - env = new environmentsModule.PythonEnvironment({conn: client}); |
44 | - cleanups = []; |
45 | - }); |
46 | - |
47 | - afterEach(function() { |
48 | - Y.each(cleanups, function(f) {f();}); |
49 | - env.destroy(); |
50 | - client.destroy(); |
51 | - juju.destroy(); |
52 | - state.destroy(); |
53 | - }); |
54 | - |
55 | - after(function() { |
56 | - delete window.flags; |
57 | - }); |
58 | - |
59 | - /** |
60 | - Generates the services required for some tests. After the services have |
61 | - been generated it will call the supplied callback. |
62 | - |
63 | - This interacts directly with the fakebackend bypassing the environment. |
64 | - The test "can add additional units" tests this code directly so as long |
65 | - as it passes you can consider this method valid. |
66 | - |
67 | - @method generateServices |
68 | - @param {Function} callback The callback to call after the services have |
69 | - been generated. |
70 | - */ |
71 | - function generateServices(callback) { |
72 | - state.deploy('cs:wordpress', function(service) { |
73 | - var data = { |
74 | - op: 'add_unit', |
75 | - service_name: 'wordpress', |
76 | - num_units: 2 |
77 | - }; |
78 | - state.nextChanges(); |
79 | - client.onmessage = function() { |
80 | - client.onmessage = function(received) { |
81 | - // After done generating the services |
82 | - callback(received); |
83 | - }; |
84 | - client.send(Y.JSON.stringify(data)); |
85 | - }; |
86 | - client.open(); |
87 | - }); |
88 | - } |
89 | - |
90 | - /** |
91 | - Generates the two services required for relation removal tests. After the |
92 | - services have been generated, a relation between them will be added and |
93 | - then removed. |
94 | - |
95 | - This interacts directly with the fakebackend bypassing the environment. |
96 | - |
97 | - @method generateAndRelateServices |
98 | - @param {Array} charms The URLs of two charms to be deployed. |
99 | - @param {Array} relation Two endpoint strings to be related. |
100 | - @param {Array} removeRelation Two enpoint strings identifying |
101 | - a relation to be removed. |
102 | - @param {Object} mock Object with the expected return values of |
103 | - the relation removal operation. |
104 | - @param {Function} done To be called to signal the test end. |
105 | - @return {undefined} Side effects only. |
106 | - */ |
107 | - function generateAndRelateServices(charms, relation, |
108 | - removeRelation, mock, done) { |
109 | - state.deploy(charms[0], function() { |
110 | - state.deploy(charms[1], function() { |
111 | - if (relation) { |
112 | - state.addRelation(relation[0], relation[1]); |
113 | - } |
114 | - var data = { |
115 | - op: 'remove_relation', |
116 | - endpoint_a: removeRelation[0], |
117 | - endpoint_b: removeRelation[1] |
118 | - }; |
119 | - client.onmessage = function(received) { |
120 | - var recData = Y.JSON.parse(received.data); |
121 | - // Skip the defaultSeriesChange message. |
122 | - if (recData.default_series === undefined) { |
123 | - assert.equal(recData.result, mock.result); |
124 | - assert.equal(recData.err, mock.err); |
125 | - if (!recData.err) { |
126 | - assert.equal(recData.endpoint_a, mock.endpoint_a); |
127 | - assert.equal(recData.endpoint_b, mock.endpoint_b); |
128 | - } |
129 | - done(); |
130 | - } |
131 | - }; |
132 | - client.open(); |
133 | - client.send(Y.JSON.stringify(data)); |
134 | - }); |
135 | - }); |
136 | - } |
137 | - |
138 | - /** |
139 | - Same as generateServices but uses the environment integration methods. |
140 | - Should be considered valid if "can add additional units (integration)" |
141 | - test passes. |
142 | - |
143 | - @method generateIntegrationServices |
144 | - @param {Function} callback The callback to call after the services have |
145 | - been generated. |
146 | - */ |
147 | - function generateIntegrationServices(callback) { |
148 | - env.after('defaultSeriesChange', function() { |
149 | - var localCb = function(result) { |
150 | - env.add_unit('kumquat', 2, function(data) { |
151 | - // After finished generating integrated services |
152 | - callback(data); |
153 | - }); |
154 | - }; |
155 | - env.deploy( |
156 | - 'cs:wordpress', 'kumquat', {llama: 'pajama'}, null, 1, localCb); |
157 | - }); |
158 | - env.connect(); |
159 | - } |
160 | - |
161 | - /** |
162 | - Generates the services and then exposes them for the un/expose tests. |
163 | - After they have been exposed it calls the supplied callback. |
164 | - |
165 | - This interacts directly with the fakebackend bypassing the environment and |
166 | - should be considered valid if "can expose a service" test passes. |
167 | - |
168 | - @method generateAndExposeService |
169 | - @param {Function} callback The callback to call after the services have |
170 | - been generated. |
171 | - */ |
172 | - function generateAndExposeService(callback) { |
173 | - state.deploy('cs:wordpress', function(data) { |
174 | - var command = { |
175 | - op: 'expose', |
176 | - service_name: data.service.get('name') |
177 | - }; |
178 | - state.nextChanges(); |
179 | - client.onmessage = function() { |
180 | - client.onmessage = function(rec) { |
181 | - callback(rec); |
182 | - }; |
183 | - client.send(Y.JSON.stringify(command)); |
184 | - }; |
185 | - client.open(); |
186 | - }, { unitCount: 1 }); |
187 | - } |
188 | - |
189 | - /** |
190 | - Same as generateAndExposeService but uses the environment integration |
191 | - methods. Should be considered valid if "can expose a service |
192 | - (integration)" test passes. |
193 | - |
194 | - @method generateAndExposeIntegrationService |
195 | - @param {Function} callback The callback to call after the services have |
196 | - been generated. |
197 | - */ |
198 | - function generateAndExposeIntegrationService(callback) { |
199 | - env.after('defaultSeriesChange', function() { |
200 | - var localCb = function(result) { |
201 | - env.expose(result.service_name, function(rec) { |
202 | - callback(rec); |
203 | - }); |
204 | - }; |
205 | - env.deploy( |
206 | - 'cs:wordpress', 'kumquat', {llama: 'pajama'}, null, 1, localCb); |
207 | - }); |
208 | - env.connect(); |
209 | - } |
210 | - |
211 | - it('opens successfully.', function(done) { |
212 | - var isAsync = false; |
213 | - client.onmessage = function(message) { |
214 | - assert.isTrue(isAsync); |
215 | - assert.deepEqual( |
216 | - Y.JSON.parse(message.data), |
217 | - { |
218 | - ready: true, |
219 | - provider_type: 'demonstration', |
220 | - default_series: 'precise' |
221 | - }); |
222 | - done(); |
223 | - }; |
224 | - assert.isFalse(juju.connected); |
225 | - assert.isUndefined(juju.get('client')); |
226 | - client.open(); |
227 | - assert.isTrue(juju.connected); |
228 | - assert.strictEqual(juju.get('client'), client); |
229 | - isAsync = true; |
230 | - }); |
231 | - |
232 | - it('ignores "open" when already open to same client.', function() { |
233 | - client.receive = function() { |
234 | - assert.ok(false, 'The receive method should not be called.'); |
235 | - }; |
236 | - // Whitebox test: duplicate "open" state. |
237 | - juju.connected = true; |
238 | - juju.set('client', client); |
239 | - // This is effectively a re-open. |
240 | - client.open(); |
241 | - // The assert.ok above is the verification. |
242 | - }); |
243 | - |
244 | - it('refuses to open if already open to another client.', function() { |
245 | - // This is a simple way to make sure that we don't leave multiple |
246 | - // setInterval calls running. If for some reason we want more |
247 | - // simultaneous clients, that's fine, though that will require |
248 | - // reworking the delta code generally. |
249 | - juju.connected = true; |
250 | - juju.set('client', {receive: function() { |
251 | - assert.ok(false, 'The receive method should not have been called.'); |
252 | - }}); |
253 | - assert.throws( |
254 | - client.open.bind(client), |
255 | - 'INVALID_STATE_ERR : Connection is open to another client.'); |
256 | - }); |
257 | - |
258 | - it('closes successfully.', function(done) { |
259 | - client.onmessage = function() { |
260 | - client.close(); |
261 | - assert.isFalse(juju.connected); |
262 | - assert.isUndefined(juju.get('client')); |
263 | - done(); |
264 | - }; |
265 | - client.open(); |
266 | - }); |
267 | - |
268 | - it('ignores "close" when already closed.', function() { |
269 | - // This simply shows that we do not raise an error. |
270 | - juju.close(); |
271 | - }); |
272 | - |
273 | - it('can dispatch on received information.', function(done) { |
274 | - var data = {op: 'testingTesting123', foo: 'bar'}; |
275 | - juju.performOp_testingTesting123 = function(received) { |
276 | - assert.notStrictEqual(received, data); |
277 | - assert.deepEqual(received, data); |
278 | - done(); |
279 | - }; |
280 | - client.open(); |
281 | - client.send(Y.JSON.stringify(data)); |
282 | - }); |
283 | - |
284 | - it('refuses to dispatch when closed.', function() { |
285 | - assert.throws( |
286 | - juju.receive.bind(juju, {}), |
287 | - 'INVALID_STATE_ERR : Connection is closed.' |
288 | - ); |
289 | - }); |
290 | - |
291 | - it('can log in.', function(done) { |
292 | - state.logout(); |
293 | - // See FakeBackend's authorizedUsers for these default authentication |
294 | - // values. |
295 | - var data = { |
296 | - op: 'login', |
297 | - user: 'admin', |
298 | - password: 'password', |
299 | - request_id: 42 |
300 | - }; |
301 | - client.onmessage = function(received) { |
302 | - // First message is the provider type and default series. We ignore |
303 | - // it, and prepare for the next one, which will be the reply to our |
304 | - // login. |
305 | - client.onmessage = function(received) { |
306 | - data.result = true; |
307 | - assert.deepEqual(Y.JSON.parse(received.data), data); |
308 | - assert.isTrue(state.get('authenticated')); |
309 | - done(); |
310 | - }; |
311 | - client.send(Y.JSON.stringify(data)); |
312 | - }; |
313 | - client.open(); |
314 | - }); |
315 | - |
316 | - it('can log in (environment integration).', function(done) { |
317 | - state.logout(); |
318 | - env.after('defaultSeriesChange', function() { |
319 | - // See FakeBackend's authorizedUsers for these default values. |
320 | - env.setCredentials({user: 'admin', password: 'password'}); |
321 | - env.after('login', function() { |
322 | - assert.isTrue(env.userIsAuthenticated); |
323 | - done(); |
324 | - }); |
325 | - env.login(); |
326 | - }); |
327 | - env.connect(); |
328 | - }); |
329 | - |
330 | - it('can deploy.', function(done) { |
331 | - // We begin logged in. See utils.makeFakeBackendWithCharmStore. |
332 | - var data = { |
333 | - op: 'deploy', |
334 | - charm_url: 'cs:wordpress', |
335 | - service_name: 'kumquat', |
336 | - config_raw: 'funny: business', |
337 | - num_units: 2, |
338 | - request_id: 42 |
339 | - }; |
340 | - client.onmessage = function(received) { |
341 | - // First message is the provider type and default series. We ignore |
342 | - // it, and prepare for the next one, which will be the reply to our |
343 | - // deployment. |
344 | - client.onmessage = function(received) { |
345 | - var parsed = Y.JSON.parse(received.data); |
346 | - assert.isUndefined(parsed.err); |
347 | - assert.deepEqual(parsed, data); |
348 | - assert.isObject( |
349 | - state.db.charms.getById('cs:precise/wordpress-10')); |
350 | - var service = state.db.services.getById('kumquat'); |
351 | - assert.isObject(service); |
352 | - assert.equal(service.get('charm'), 'cs:precise/wordpress-10'); |
353 | - assert.deepEqual(service.get('config'), {funny: 'business'}); |
354 | - var units = state.db.units.get_units_for_service(service); |
355 | - assert.lengthOf(units, 2); |
356 | - done(); |
357 | - }; |
358 | - client.send(Y.JSON.stringify(data)); |
359 | - }; |
360 | - client.open(); |
361 | - }); |
362 | - |
363 | - it('can deploy (environment integration).', function(done) { |
364 | - // We begin logged in. See utils.makeFakeBackendWithCharmStore. |
365 | - env.after('defaultSeriesChange', function() { |
366 | - var callback = function(result) { |
367 | - assert.isUndefined(result.err); |
368 | - assert.equal(result.charm_url, 'cs:wordpress'); |
369 | - var service = state.db.services.getById('kumquat'); |
370 | - assert.equal(service.get('charm'), 'cs:precise/wordpress-10'); |
371 | - assert.deepEqual(service.get('config'), {llama: 'pajama'}); |
372 | - done(); |
373 | - }; |
374 | - env.deploy( |
375 | - 'cs:wordpress', 'kumquat', {llama: 'pajama'}, null, 1, callback); |
376 | - }); |
377 | - env.connect(); |
378 | - }); |
379 | - |
380 | - it('can communicate errors after attempting to deploy', function(done) { |
381 | - // Create a service with the name "wordpress". |
382 | - // The charm store is synchronous in tests, so we don't need a real |
383 | - // callback. |
384 | - state.deploy('cs:wordpress', function() {}); |
385 | - env.after('defaultSeriesChange', function() { |
386 | - var callback = function(result) { |
387 | - assert.equal( |
388 | - result.err, 'A service with this name already exists.'); |
389 | - done(); |
390 | - }; |
391 | - env.deploy( |
392 | - 'cs:wordpress', undefined, undefined, undefined, 1, callback); |
393 | - }); |
394 | - env.connect(); |
395 | - }); |
396 | - |
397 | - it('can send a delta stream of changes.', function(done) { |
398 | - // Create a service with the name "wordpress". |
399 | - // The charm store is synchronous in tests, so we don't need a real |
400 | - // callback. |
401 | - state.deploy('cs:wordpress', function() {}); |
402 | - client.onmessage = function(received) { |
403 | - // First message is the provider type and default series. We ignore |
404 | - // it, and prepare for the next one, which will handle the delta |
405 | - // stream. |
406 | - client.onmessage = function(received) { |
407 | - var parsed = Y.JSON.parse(received.data); |
408 | - assert.equal(parsed.op, 'delta'); |
409 | - var deltas = parsed.result; |
410 | - assert.lengthOf(deltas, 3); |
411 | - assert.equal(deltas[0][0], 'service'); |
412 | - assert.equal(deltas[0][1], 'change'); |
413 | - assert.equal(deltas[0][2].charm, 'cs:precise/wordpress-10'); |
414 | - assert.equal(deltas[1][0], 'machine'); |
415 | - assert.equal(deltas[1][1], 'change'); |
416 | - assert.equal(deltas[2][0], 'unit'); |
417 | - assert.equal(deltas[2][1], 'change'); |
418 | - done(); |
419 | - }; |
420 | - juju.sendDelta(); |
421 | - }; |
422 | - client.open(); |
423 | - }); |
424 | - |
425 | - it('does not send a delta if there are no changes.', function(done) { |
426 | - client.onmessage = function(received) { |
427 | - // First message is the provider type and default series. We ignore |
428 | - // it, and prepare for the next one, which will handle the delta |
429 | - // stream. |
430 | - client.receiveNow = function(response) { |
431 | - assert.ok(false, 'This method should not have been called.'); |
432 | - }; |
433 | - juju.sendDelta(); |
434 | - done(); |
435 | - }; |
436 | - client.open(); |
437 | - }); |
438 | - |
439 | - it('can send a delta stream (integration).', function(done) { |
440 | - // Create a service with the name "wordpress". |
441 | - // The charm store is synchronous in tests, so we don't need a real |
442 | - // callback. |
443 | - state.deploy('cs:wordpress', function() {}, {unitCount: 2}); |
444 | - var db = new Y.juju.models.Database(); |
445 | - db.on('update', function() { |
446 | - // We want to verify that the GUI database is equivalent to the state |
447 | - // database. |
448 | - assert.equal(db.services.size(), 1); |
449 | - assert.equal(db.units.size(), 2); |
450 | - assert.equal(db.machines.size(), 2); |
451 | - var stateService = state.db.services.item(0); |
452 | - var guiService = db.services.item(0); |
453 | - Y.each( |
454 | - ['charm', 'config', 'constraints', 'exposed', |
455 | - 'id', 'name', 'subordinate'], |
456 | - function(attrName) { |
457 | - assert.deepEqual( |
458 | - guiService.get(attrName), stateService.get(attrName)); |
459 | - } |
460 | - ); |
461 | - state.db.units.each(function(stateUnit) { |
462 | - var guiUnit = db.units.getById(stateUnit.id); |
463 | - Y.each( |
464 | - ['agent_state', 'machine', 'number', 'service'], |
465 | - function(attrName) { |
466 | - assert.deepEqual(guiUnit[attrName], stateUnit[attrName]); |
467 | - } |
468 | - ); |
469 | - }); |
470 | - state.db.machines.each(function(stateMachine) { |
471 | - var guiMachine = db.machines.getById(stateMachine.id); |
472 | - Y.each( |
473 | - ['agent_state', 'public_address', 'machine_id'], |
474 | - function(attrName) { |
475 | - assert.deepEqual(guiMachine[attrName], stateMachine[attrName]); |
476 | - } |
477 | - ); |
478 | - }); |
479 | - done(); |
480 | - }); |
481 | - env.on('delta', db.onDelta, db); |
482 | - env.after('defaultSeriesChange', function() {juju.sendDelta();}); |
483 | - env.connect(); |
484 | - }); |
485 | - |
486 | - it('sends delta streams periodically after opening.', function(done) { |
487 | - client.onmessage = function(received) { |
488 | - // First message is the provider type and default series. We ignore |
489 | - // it, and prepare for the next one, which will handle the delta |
490 | - // stream. |
491 | - var isAsync = false; |
492 | - client.onmessage = function(received) { |
493 | - assert.isTrue(isAsync); |
494 | - var parsed = Y.JSON.parse(received.data); |
495 | - assert.equal(parsed.op, 'delta'); |
496 | - var deltas = parsed.result; |
497 | - assert.lengthOf(deltas, 3); |
498 | - assert.equal(deltas[0][2].charm, 'cs:precise/wordpress-10'); |
499 | - done(); |
500 | - }; |
501 | - // Create a service with the name "wordpress". |
502 | - // The charm store is synchronous in tests, so we don't need a real |
503 | - // callback. |
504 | - state.deploy('cs:wordpress', function() {}); |
505 | - isAsync = true; |
506 | - }; |
507 | - juju.set('deltaInterval', 4); |
508 | - client.open(); |
509 | - }); |
510 | - |
511 | - it('stops sending delta streams after closing.', function(done) { |
512 | - var sysSetInterval = window.setInterval; |
513 | - var sysClearInterval = window.clearInterval; |
514 | - cleanups.push(function() { |
515 | - window.setInterval = sysSetInterval; |
516 | - window.clearInterval = sysClearInterval; |
517 | - }); |
518 | - window.setInterval = function(f, interval) { |
519 | - assert.isFunction(f); |
520 | - assert.equal(interval, 4); |
521 | - return 42; |
522 | - }; |
523 | - window.clearInterval = function(token) { |
524 | - assert.equal(token, 42); |
525 | - done(); |
526 | - }; |
527 | - client.onmessage = function(received) { |
528 | - // First message is the provider type and default series. We can |
529 | - // close now. |
530 | - client.close(); |
531 | - }; |
532 | - juju.set('deltaInterval', 4); |
533 | - client.open(); |
534 | - }); |
535 | - |
536 | - it('can add additional units', function(done) { |
537 | - function testForAddedUnits(received) { |
538 | - var service = state.db.services.getById('wordpress'), |
539 | - units = state.db.units.get_units_for_service(service), |
540 | - data = Y.JSON.parse(received.data), |
541 | - mock = { |
542 | - num_units: 2, |
543 | - service_name: 'wordpress', |
544 | - op: 'add_unit', |
545 | - result: ['wordpress/1', 'wordpress/2'] |
546 | - }; |
547 | - // Do we have enough total units? |
548 | - assert.lengthOf(units, 3); |
549 | - // Does the response object contain the proper data |
550 | - assert.deepEqual(data, mock); |
551 | - // Error is undefined |
552 | - assert.isUndefined(data.err); |
553 | - done(); |
554 | - } |
555 | - // Generate the default services and add units |
556 | - generateServices(testForAddedUnits); |
557 | - }); |
558 | - |
559 | - it('throws an error when adding units to an invalid service', |
560 | - function(done) { |
561 | - state.deploy('cs:wordpress', function(service) { |
562 | - var data = { |
563 | - op: 'add_unit', |
564 | - service_name: 'noservice', |
565 | - num_units: 2 |
566 | - }; |
567 | - //Clear out the delta stream |
568 | - state.nextChanges(); |
569 | - client.onmessage = function() { |
570 | - client.onmessage = function(received) { |
571 | - var data = Y.JSON.parse(received.data); |
572 | - |
573 | - // If there is no error data.err will be undefined |
574 | - assert.equal(true, !!data.err); |
575 | - done(); |
576 | - }; |
577 | - client.send(Y.JSON.stringify(data)); |
578 | - }; |
579 | - client.open(); |
580 | - }); |
581 | - } |
582 | - ); |
583 | - |
584 | - it('can add additional units (integration)', function(done) { |
585 | - function testForAddedUnits(data) { |
586 | - var service = state.db.services.getById('kumquat'), |
587 | - units = state.db.units.get_units_for_service(service); |
588 | - assert.lengthOf(units, 3); |
589 | - done(); |
590 | - } |
591 | - generateIntegrationServices(testForAddedUnits); |
592 | - }); |
593 | - |
594 | - it('can remove units', function(done) { |
595 | - function removeUnits() { |
596 | - var data = { |
597 | - op: 'remove_units', |
598 | - unit_names: ['wordpress/0', 'wordpress/1'] |
599 | - }; |
600 | - client.onmessage = function(rec) { |
601 | - var data = Y.JSON.parse(rec.data), |
602 | - mock = { |
603 | - op: 'remove_units', |
604 | - result: true, |
605 | - unit_names: ['wordpress/0', 'wordpress/1'] |
606 | - }; |
607 | - // No errors |
608 | - assert.equal(data.result, true); |
609 | - // Returned data object contains all information |
610 | - assert.deepEqual(data, mock); |
611 | - done(); |
612 | - }; |
613 | - client.send(Y.JSON.stringify(data)); |
614 | - } |
615 | - // Generate the services base data and then execute the test |
616 | - generateServices(removeUnits); |
617 | - }); |
618 | - |
619 | - it('can remove units (integration)', function(done) { |
620 | - function removeUnits() { |
621 | - var unitNames = ['kumquat/1', 'kumquat/2']; |
622 | - env.remove_units(unitNames, function(data) { |
623 | - assert.equal(data.result, true); |
624 | - assert.deepEqual(data.unit_names, unitNames); |
625 | - done(); |
626 | - }); |
627 | - } |
628 | - // Generate the services via the integration method then execute the test |
629 | - generateIntegrationServices(removeUnits); |
630 | - }); |
631 | - |
632 | - it('allows attempting to remove units from an invalid service', |
633 | - function(done) { |
634 | - function removeUnit() { |
635 | - var data = { |
636 | - op: 'remove_units', |
637 | - unit_names: ['bar/2'] |
638 | - }; |
639 | - client.onmessage = function(rec) { |
640 | - var data = Y.JSON.parse(rec.data); |
641 | - assert.equal(data.result, true); |
642 | - done(); |
643 | - }; |
644 | - client.send(Y.JSON.stringify(data)); |
645 | - } |
646 | - // Generate the services base data then execute the test. |
647 | - generateServices(removeUnit); |
648 | - } |
649 | - ); |
650 | - |
651 | - it('throws an error if unit is a subordinate', function(done) { |
652 | - function removeUnits() { |
653 | - var data = { |
654 | - op: 'remove_units', |
655 | - unit_names: ['wordpress/1'] |
656 | - }; |
657 | - client.onmessage = function(rec) { |
658 | - var data = Y.JSON.parse(rec.data); |
659 | - assert.equal(Y.Lang.isArray(data.err), true); |
660 | - assert.equal(data.err.length, 1); |
661 | - done(); |
662 | - }; |
663 | - state.db.services.getById('wordpress').set('is_subordinate', true); |
664 | - client.send(Y.JSON.stringify(data)); |
665 | - } |
666 | - // Generate the services base data then execute the test. |
667 | - generateServices(removeUnits); |
668 | - }); |
669 | - |
670 | - it('can get a service', function(done) { |
671 | - generateServices(function(data) { |
672 | - // Post deploy of wordpress so we should be able to |
673 | - // pull its data. |
674 | - var op = { |
675 | - op: 'get_service', |
676 | - service_name: 'wordpress', |
677 | - request_id: 99 |
678 | - }; |
679 | - client.onmessage = function(received) { |
680 | - var parsed = Y.JSON.parse(received.data); |
681 | - var service = parsed.result; |
682 | - assert.equal(service.name, 'wordpress'); |
683 | - // Error should be undefined. |
684 | - done(received.error); |
685 | - }; |
686 | - client.send(Y.JSON.stringify(op)); |
687 | - }); |
688 | - }); |
689 | - |
690 | - it('can destroy a service', function(done) { |
691 | - generateServices(function(data) { |
692 | - // Post deploy of wordpress so we should be able to |
693 | - // destroy it. |
694 | - var op = { |
695 | - op: 'destroy_service', |
696 | - service_name: 'wordpress', |
697 | - request_id: 99 |
698 | - }; |
699 | - client.onmessage = function(received) { |
700 | - var parsed = Y.JSON.parse(received.data); |
701 | - assert.equal(parsed.result, 'wordpress'); |
702 | - // Error should be undefined. |
703 | - done(received.error); |
704 | - }; |
705 | - client.send(Y.JSON.stringify(op)); |
706 | - }); |
707 | - }); |
708 | - |
709 | - it('can destroy a service (integration)', function(done) { |
710 | - function destroyService(rec) { |
711 | - function localCb(rec2) { |
712 | - assert.equal(rec2.result, 'kumquat'); |
713 | - var service = state.db.services.getById('kumquat'); |
714 | - assert.isNull(service); |
715 | - done(); |
716 | - } |
717 | - var result = env.destroy_service(rec.service_name, localCb); |
718 | - } |
719 | - generateAndExposeIntegrationService(destroyService); |
720 | - }); |
721 | - |
722 | - it('can get a charm', function(done) { |
723 | - generateServices(function(data) { |
724 | - // Post deploy of wordpress we should be able to |
725 | - // pull its data. |
726 | - var op = { |
727 | - op: 'get_charm', |
728 | - charm_url: 'cs:wordpress', |
729 | - request_id: 99 |
730 | - }; |
731 | - client.onmessage = function(received) { |
732 | - var parsed = Y.JSON.parse(received.data); |
733 | - var charm = parsed.result; |
734 | - assert.equal(charm.name, 'wordpress'); |
735 | - // Error should be undefined. |
736 | - done(received.error); |
737 | - }; |
738 | - client.send(Y.JSON.stringify(op)); |
739 | - }); |
740 | - }); |
741 | - |
742 | - it('can set service config', function(done) { |
743 | - generateServices(function(data) { |
744 | - // Post deploy of wordpress we should be able to |
745 | - // pull its data. |
746 | - var op = { |
747 | - op: 'set_config', |
748 | - service_name: 'wordpress', |
749 | - config: {'blog-title': 'Inimical'}, |
750 | - request_id: 99 |
751 | - }; |
752 | - client.onmessage = function(received) { |
753 | - var parsed = Y.JSON.parse(received.data); |
754 | - assert.deepEqual(parsed.result, {'blog-title': 'Inimical'}); |
755 | - var service = state.db.services.getById('wordpress'); |
756 | - assert.equal(service.get('config')['blog-title'], 'Inimical'); |
757 | - // Error should be undefined. |
758 | - done(parsed.error); |
759 | - }; |
760 | - client.send(Y.JSON.stringify(op)); |
761 | - }); |
762 | - }); |
763 | - |
764 | - it('can set service constraints', function(done) { |
765 | - generateServices(function(data) { |
766 | - // Post deploy of wordpress we should be able to |
767 | - // pull its data. |
768 | - var op = { |
769 | - op: 'set_constraints', |
770 | - service_name: 'wordpress', |
771 | - constraints: ['cpu=2', 'mem=128'], |
772 | - request_id: 99 |
773 | - }; |
774 | - client.onmessage = function(received) { |
775 | - var service = state.db.services.getById('wordpress'); |
776 | - var constraints = service.get('constraints'); |
777 | - assert.equal(constraints.cpu, '2'); |
778 | - assert.equal(constraints.mem, '128'); |
779 | - // Error should be undefined. |
780 | - done(received.error); |
781 | - }; |
782 | - client.send(Y.JSON.stringify(op)); |
783 | - }); |
784 | - }); |
785 | - |
786 | - it('can expose a service', function(done) { |
787 | - function checkExposedService(rec) { |
788 | - var data = Y.JSON.parse(rec.data), |
789 | - mock = { |
790 | - op: 'expose', |
791 | - result: true, |
792 | - service_name: 'wordpress' |
793 | - }; |
794 | - var service = state.db.services.getById(mock.service_name); |
795 | - assert.equal(service.get('exposed'), true); |
796 | - assert.equal(data.result, true); |
797 | - assert.deepEqual(data, mock); |
798 | - done(); |
799 | - } |
800 | - generateAndExposeService(checkExposedService); |
801 | - }); |
802 | - |
803 | - it('can expose a service (integration)', function(done) { |
804 | - function checkExposedService(rec) { |
805 | - var service = state.db.services.getById('kumquat'); |
806 | - assert.equal(service.get('exposed'), true); |
807 | - assert.equal(rec.result, true); |
808 | - done(); |
809 | - } |
810 | - generateAndExposeIntegrationService(checkExposedService); |
811 | - }); |
812 | - |
813 | - it('fails silently when exposing an exposed service', function(done) { |
814 | - function checkExposedService(rec) { |
815 | - var data = Y.JSON.parse(rec.data), |
816 | - service = state.db.services.getById(data.service_name), |
817 | - command = { |
818 | - op: 'expose', |
819 | - service_name: data.service_name |
820 | - }; |
821 | - state.nextChanges(); |
822 | - client.onmessage = function(rec) { |
823 | - assert.equal(data.err, undefined); |
824 | - assert.equal(service.get('exposed'), true); |
825 | - assert.equal(data.result, true); |
826 | - done(); |
827 | - }; |
828 | - client.send(Y.JSON.stringify(command)); |
829 | - } |
830 | - generateAndExposeService(checkExposedService); |
831 | - }); |
832 | - |
833 | - it('fails with error when exposing an invalid service name', |
834 | - function(done) { |
835 | - state.deploy('cs:wordpress', function(data) { |
836 | - var command = { |
837 | - op: 'expose', |
838 | - service_name: 'foobar' |
839 | - }; |
840 | - state.nextChanges(); |
841 | - client.onmessage = function() { |
842 | - client.onmessage = function(rec) { |
843 | - var data = Y.JSON.parse(rec.data); |
844 | - assert.equal(data.result, false); |
845 | - assert.equal(data.err, |
846 | - '"foobar" is an invalid service name.'); |
847 | - done(); |
848 | - }; |
849 | - client.send(Y.JSON.stringify(command)); |
850 | - }; |
851 | - client.open(); |
852 | - }, { unitCount: 1 }); |
853 | - } |
854 | - ); |
855 | - |
856 | - it('can unexpose a service', function(done) { |
857 | - function unexposeService(rec) { |
858 | - var data = Y.JSON.parse(rec.data), |
859 | - command = { |
860 | - op: 'unexpose', |
861 | - service_name: data.service_name |
862 | - }; |
863 | - state.nextChanges(); |
864 | - client.onmessage = function(rec) { |
865 | - var data = Y.JSON.parse(rec.data), |
866 | - service = state.db.services.getById(data.service_name), |
867 | - mock = { |
868 | - op: 'unexpose', |
869 | - result: true, |
870 | - service_name: 'wordpress' |
871 | - }; |
872 | - assert.equal(service.get('exposed'), false); |
873 | - assert.deepEqual(data, mock); |
874 | - done(); |
875 | - }; |
876 | - client.send(Y.JSON.stringify(command)); |
877 | - } |
878 | - generateAndExposeService(unexposeService); |
879 | - }); |
880 | - |
881 | - it('can unexpose a service (integration)', function(done) { |
882 | - function unexposeService(rec) { |
883 | - function localCb(rec) { |
884 | - var service = state.db.services.getById('kumquat'); |
885 | - assert.equal(service.get('exposed'), false); |
886 | - assert.equal(rec.result, true); |
887 | - done(); |
888 | - } |
889 | - env.unexpose(rec.service_name, localCb); |
890 | - } |
891 | - generateAndExposeIntegrationService(unexposeService); |
892 | - }); |
893 | - |
894 | - it('fails silently when unexposing a not exposed service', |
895 | - function(done) { |
896 | - state.deploy('cs:wordpress', function(data) { |
897 | - var command = { |
898 | - op: 'unexpose', |
899 | - service_name: data.service.get('name') |
900 | - }; |
901 | - state.nextChanges(); |
902 | - client.onmessage = function() { |
903 | - client.onmessage = function(rec) { |
904 | - var data = Y.JSON.parse(rec.data), |
905 | - service = state.db.services.getById(data.service_name); |
906 | - assert.equal(service.get('exposed'), false); |
907 | - assert.equal(data.result, true); |
908 | - assert.equal(data.err, undefined); |
909 | - done(); |
910 | - }; |
911 | - client.send(Y.JSON.stringify(command)); |
912 | - }; |
913 | - client.open(); |
914 | - }, { unitCount: 1 }); |
915 | - } |
916 | - ); |
917 | - |
918 | - it('fails with error when unexposing an invalid service name', |
919 | - function(done) { |
920 | - function unexposeService(rec) { |
921 | - var data = Y.JSON.parse(rec.data), |
922 | - command = { |
923 | - op: 'unexpose', |
924 | - service_name: 'foobar' |
925 | - }; |
926 | - state.nextChanges(); |
927 | - client.onmessage = function(rec) { |
928 | - var data = Y.JSON.parse(rec.data); |
929 | - assert.equal(data.result, false); |
930 | - assert.equal(data.err, '"foobar" is an invalid service name.'); |
931 | - done(); |
932 | - }; |
933 | - client.send(Y.JSON.stringify(command)); |
934 | - } |
935 | - generateAndExposeService(unexposeService); |
936 | - } |
937 | - ); |
938 | - |
939 | - it('can add a relation', function(done) { |
940 | - function localCb() { |
941 | - state.deploy('cs:mysql', function(service) { |
942 | - var data = { |
943 | - op: 'add_relation', |
944 | - endpoint_a: 'wordpress:db', |
945 | - endpoint_b: 'mysql:db' |
946 | - }; |
947 | - client.onmessage = function(rec) { |
948 | - var data = Y.JSON.parse(rec.data), |
949 | - mock = { |
950 | - endpoint_a: 'wordpress:db', |
951 | - endpoint_b: 'mysql:db', |
952 | - op: 'add_relation', |
953 | - result: { |
954 | - id: 'relation-0', |
955 | - 'interface': 'mysql', |
956 | - scope: 'global', |
957 | - endpoints: [ |
958 | - {wordpress: {name: 'db'}}, |
959 | - {mysql: {name: 'db'}} |
960 | - ] |
961 | - } |
962 | - }; |
963 | - |
964 | - assert.equal(data.err, undefined); |
965 | - assert.equal(typeof data.result, 'object'); |
966 | - assert.deepEqual(data, mock); |
967 | - done(); |
968 | - }; |
969 | - client.send(Y.JSON.stringify(data)); |
970 | - }); |
971 | - } |
972 | - generateServices(localCb); |
973 | - }); |
974 | - |
975 | - it('can add a relation (integration)', function(done) { |
976 | - function addRelation() { |
977 | - function localCb(rec) { |
978 | - var mock = { |
979 | - endpoint_a: 'kumquat:db', |
980 | - endpoint_b: 'mysql:db', |
981 | - op: 'add_relation', |
982 | - request_id: rec.request_id, |
983 | - result: { |
984 | - id: 'relation-0', |
985 | - 'interface': 'mysql', |
986 | - scope: 'global', |
987 | - request_id: rec.request_id, |
988 | - endpoints: [ |
989 | - {kumquat: {name: 'db'}}, |
990 | - {mysql: {name: 'db'}} |
991 | - ] |
992 | - } |
993 | - }; |
994 | - |
995 | - assert.equal(rec.err, undefined); |
996 | - assert.equal(typeof rec.result, 'object'); |
997 | - assert.deepEqual(rec.details[0], mock); |
998 | - done(); |
999 | - } |
1000 | - var endpointA = [ |
1001 | - 'kumquat', |
1002 | - { name: 'db', |
1003 | - role: 'client' } |
1004 | - ]; |
1005 | - var endpointB = [ |
1006 | - 'mysql', |
1007 | - { name: 'db', |
1008 | - role: 'server' } |
1009 | - ]; |
1010 | - env.add_relation(endpointA, endpointB, localCb); |
1011 | - } |
1012 | - generateIntegrationServices(function() { |
1013 | - env.deploy('cs:mysql', undefined, undefined, undefined, 1, addRelation); |
1014 | - }); |
1015 | - }); |
1016 | - |
1017 | - it('is able to add a relation with a subordinate service', function(done) { |
1018 | - function localCb() { |
1019 | - state.deploy('cs:puppet', function(service) { |
1020 | - var data = { |
1021 | - op: 'add_relation', |
1022 | - endpoint_a: 'wordpress:juju-info', |
1023 | - endpoint_b: 'puppet:juju-info' |
1024 | - }; |
1025 | - |
1026 | - client.onmessage = function(rec) { |
1027 | - var data = Y.JSON.parse(rec.data), |
1028 | - mock = { |
1029 | - endpoint_a: 'wordpress:juju-info', |
1030 | - endpoint_b: 'puppet:juju-info', |
1031 | - op: 'add_relation', |
1032 | - result: { |
1033 | - id: 'relation-0', |
1034 | - 'interface': 'juju-info', |
1035 | - scope: 'container', |
1036 | - endpoints: [ |
1037 | - {puppet: {name: 'juju-info'}}, |
1038 | - {wordpress: {name: 'juju-info'}} |
1039 | - ] |
1040 | - } |
1041 | - }; |
1042 | - assert.equal(data.err, undefined); |
1043 | - assert.equal(typeof data.result, 'object'); |
1044 | - assert.deepEqual(data, mock); |
1045 | - done(); |
1046 | - }; |
1047 | - client.send(Y.JSON.stringify(data)); |
1048 | - }); |
1049 | - } |
1050 | - generateServices(localCb); |
1051 | - }); |
1052 | - |
1053 | - it('throws an error if only one endpoint is supplied', function(done) { |
1054 | - function localCb() { |
1055 | - var data = { |
1056 | - op: 'add_relation', |
1057 | - endpoint_a: 'wordpress:db' |
1058 | - }; |
1059 | - state.nextChanges(); |
1060 | - client.onmessage = function(rec) { |
1061 | - var data = Y.JSON.parse(rec.data); |
1062 | - assert(data.err, 'Two endpoints required to set up relation.'); |
1063 | - done(); |
1064 | - }; |
1065 | - client.send(Y.JSON.stringify(data)); |
1066 | - } |
1067 | - generateServices(localCb); |
1068 | - }); |
1069 | - |
1070 | - it('throws an error if endpoints are not relatable', function(done) { |
1071 | - function localCb() { |
1072 | - var data = { |
1073 | - op: 'add_relation', |
1074 | - endpoint_a: 'wordpress:db', |
1075 | - endpoint_b: 'mysql:foo' |
1076 | - }; |
1077 | - state.nextChanges(); |
1078 | - client.onmessage = function(rec) { |
1079 | - var data = Y.JSON.parse(rec.data); |
1080 | - assert(data.err, 'No matching interfaces.'); |
1081 | - done(); |
1082 | - }; |
1083 | - client.send(Y.JSON.stringify(data)); |
1084 | - } |
1085 | - generateServices(localCb); |
1086 | - }); |
1087 | - |
1088 | - it('can remove a relation', function(done) { |
1089 | - generateAndRelateServices( |
1090 | - ['cs:wordpress', 'cs:mysql'], |
1091 | - ['wordpress:db', 'mysql:db'], |
1092 | - ['wordpress:db', 'mysql:db'], |
1093 | - {result: true, endpoint_a: 'wordpress:db', endpoint_b: 'mysql:db'}, |
1094 | - done); |
1095 | - }); |
1096 | - |
1097 | - it('can remove a relation (integration)', function(done) { |
1098 | - var endpoints = [ |
1099 | - ['kumquat', |
1100 | - { name: 'db', |
1101 | - role: 'client' }], |
1102 | - ['mysql', |
1103 | - { name: 'db', |
1104 | - role: 'server' }] |
1105 | - ]; |
1106 | - env.after('defaultSeriesChange', function() { |
1107 | - function localCb(result) { |
1108 | - var mock = { |
1109 | - endpoint_a: 'kumquat:db', |
1110 | - endpoint_b: 'mysql:db', |
1111 | - op: 'remove_relation', |
1112 | - request_id: 4, |
1113 | - result: true |
1114 | - }; |
1115 | - assert.deepEqual(result.details[0], mock); |
1116 | - done(); |
1117 | - } |
1118 | - env.deploy( |
1119 | - 'cs:wordpress', 'kumquat', {llama: 'pajama'}, null, 1, function() { |
1120 | - env.deploy('cs:mysql', null, null, null, 1, function() { |
1121 | - env.add_relation(endpoints[0], endpoints[1], function() { |
1122 | - env.remove_relation(endpoints[0], endpoints[1], localCb); |
1123 | - }); |
1124 | - }); |
1125 | - } |
1126 | - ); |
1127 | - }); |
1128 | - env.connect(); |
1129 | - }); |
1130 | - |
1131 | - it('throws an error if the charms do not exist', function(done) { |
1132 | - generateAndRelateServices( |
1133 | - ['cs:wordpress', 'cs:mysql'], |
1134 | - ['wordpress:db', 'mysql:db'], |
1135 | - ['no_such', 'charms'], |
1136 | - {err: 'Charm not loaded.', |
1137 | - endpoint_a: 'wordpress:db', endpoint_b: 'mysql:db'}, |
1138 | - done); |
1139 | - }); |
1140 | - |
1141 | - it('throws an error if the relationship does not exist', function(done) { |
1142 | - generateAndRelateServices( |
1143 | - ['cs:wordpress', 'cs:mysql'], |
1144 | - null, |
1145 | - ['wordpress:db', 'mysql:db'], |
1146 | - {err: 'Relationship does not exist', |
1147 | - endpoint_a: 'wordpress:db', endpoint_b: 'mysql:db'}, |
1148 | - done); |
1149 | - }); |
1150 | - |
1151 | - describe('Sandbox Annotations', function() { |
1152 | - |
1153 | - it('should handle service annotation updates', function(done) { |
1154 | - generateServices(function(data) { |
1155 | - // Post deploy of wordpress we should be able to |
1156 | - // pull its data. |
1157 | - var op = { |
1158 | - op: 'update_annotations', |
1159 | - entity: 'wordpress', |
1160 | - data: {'foo': 'bar'}, |
1161 | - request_id: 99 |
1162 | - }; |
1163 | - client.onmessage = function(received) { |
1164 | - var service = state.db.services.getById('wordpress'); |
1165 | - var annotations = service.get('annotations'); |
1166 | - assert.equal(annotations.foo, 'bar'); |
1167 | - // Validate that annotations appear in the delta stream. |
1168 | - client.onmessage = function(delta) { |
1169 | - delta = Y.JSON.parse(delta.data); |
1170 | - assert.equal(delta.op, 'delta'); |
1171 | - var serviceChange = Y.Array.find(delta.result, function(change) { |
1172 | - return change[0] === 'service'; |
1173 | - }); |
1174 | - assert.equal(serviceChange[0], 'service'); |
1175 | - assert.equal(serviceChange[1], 'change'); |
1176 | - assert.deepEqual(serviceChange[2].annotations, {'foo': 'bar'}); |
1177 | - // Error should be undefined. |
1178 | - done(received.error); |
1179 | - }; |
1180 | - juju.sendDelta(); |
1181 | - }; |
1182 | - client.open(); |
1183 | - client.send(Y.JSON.stringify(op)); |
1184 | - }); |
1185 | - }); |
1186 | - |
1187 | - it('should handle environment annotation updates', function(done) { |
1188 | - generateServices(function(data) { |
1189 | - // We only deploy a service here to reuse the env connect/setup |
1190 | - // code. |
1191 | - // Post deploy of wordpress we should be able to |
1192 | - // pull env data. |
1193 | - client.onmessage = function(received) { |
1194 | - var env = state.db.environment; |
1195 | - var annotations = env.get('annotations'); |
1196 | - assert.equal(annotations.foo, 'bar'); |
1197 | - // Validate that annotations appear in the delta stream. |
1198 | - client.onmessage = function(delta) { |
1199 | - delta = Y.JSON.parse(delta.data); |
1200 | - assert.equal(delta.op, 'delta'); |
1201 | - var envChange = Y.Array.find(delta.result, function(change) { |
1202 | - return change[0] === 'annotations'; |
1203 | - }); |
1204 | - assert.equal(envChange[1], 'change'); |
1205 | - assert.deepEqual(envChange[2], {'foo': 'bar'}); |
1206 | - done(); |
1207 | - }; |
1208 | - juju.sendDelta(); |
1209 | - }; |
1210 | - client.open(); |
1211 | - client.send(Y.JSON.stringify({ |
1212 | - op: 'update_annotations', |
1213 | - entity: 'env', |
1214 | - data: {'foo': 'bar'}, |
1215 | - request_id: 99 |
1216 | - })); |
1217 | - }); |
1218 | - }); |
1219 | - |
1220 | - it('should handle unit annotation updates', function(done) { |
1221 | - generateServices(function(data) { |
1222 | - // Post deploy of wordpress we should be able to |
1223 | - // pull its data. |
1224 | - var op = { |
1225 | - op: 'update_annotations', |
1226 | - entity: 'wordpress/0', |
1227 | - data: {'foo': 'bar'}, |
1228 | - request_id: 99 |
1229 | - }; |
1230 | - client.onmessage = function(received) { |
1231 | - var unit = state.db.units.getById('wordpress/0'); |
1232 | - var annotations = unit.annotations; |
1233 | - assert.equal(annotations.foo, 'bar'); |
1234 | - // Error should be undefined. |
1235 | - done(received.error); |
1236 | - }; |
1237 | - client.open(); |
1238 | - client.send(Y.JSON.stringify(op)); |
1239 | - }); |
1240 | - }); |
1241 | - |
1242 | - }); |
1243 | - |
1244 | - it('should allow unit resolved to be called', function(done) { |
1245 | - generateServices(function(data) { |
1246 | - // Post deploy of wordpress we should be able to |
1247 | - // pull its data. |
1248 | - var op = { |
1249 | - op: 'resolved', |
1250 | - unit_name: 'wordpress/0', |
1251 | - request_id: 99 |
1252 | - }; |
1253 | - client.onmessage = function(received) { |
1254 | - var parsed = Y.JSON.parse(received.data); |
1255 | - assert.equal(parsed.result, true); |
1256 | - done(parsed.error); |
1257 | - }; |
1258 | - client.open(); |
1259 | - client.send(Y.JSON.stringify(op)); |
1260 | - }); |
1261 | - }); |
1262 | - |
1263 | - /** |
1264 | - * Utility method to turn _some_ callback |
1265 | - * styled async methods into Promises. |
1266 | - * It does this by supplying a simple |
1267 | - * adaptor that can handle {error:...} |
1268 | - * and {result: ... } returns. |
1269 | - * |
1270 | - * This callback is appended to any calling arguments |
1271 | - * |
1272 | - * @method promise |
1273 | - * @param {Object} context Calling context. |
1274 | - * @param {String} methodName name of method on context to invoke. |
1275 | - * @param {Arguments} arguments Additional arguments passed |
1276 | - * to resolved method. |
1277 | - * @return {Promise} a Y.Promise object. |
1278 | - */ |
1279 | - function promise(context, methodName) { |
1280 | - var slice = Array.prototype.slice; |
1281 | - var args = slice.call(arguments, 2); |
1282 | - var method = context[methodName]; |
1283 | - |
1284 | - return Y.Promise(function(resolve, reject) { |
1285 | - var resultHandler = function(result) { |
1286 | - if (result.err || result.error) { |
1287 | - reject(result.err || result.error); |
1288 | - } else { |
1289 | - resolve(result); |
1290 | - } |
1291 | - }; |
1292 | - |
1293 | - args.push(resultHandler); |
1294 | - var result = method.apply(context, args); |
1295 | - if (result !== undefined) { |
1296 | - // The method returned right away. |
1297 | - return resultHandler(result); |
1298 | - } |
1299 | - }); |
1300 | - } |
1301 | - |
1302 | - it('should support export', function(done) { |
1303 | - client.open(); |
1304 | - promise(state, 'deploy', 'cs:wordpress') |
1305 | - .then(promise(state, 'deploy', 'cs:mysql')) |
1306 | - .then(promise(state, 'addRelation', 'wordpress:db', 'mysql:db')) |
1307 | - .then(function() { |
1308 | - client.onmessage = function(result) { |
1309 | - var data = Y.JSON.parse(result.data).result; |
1310 | - assert.equal(data.services[0].name, 'wordpress'); |
1311 | - done(); |
1312 | - }; |
1313 | - client.send(Y.JSON.stringify({op: 'exportEnvironment'})); |
1314 | - }); |
1315 | - }); |
1316 | - |
1317 | - it('should support import', function(done) { |
1318 | - var fixture = utils.loadFixture('data/sample-fakebackend.json', false); |
1319 | - |
1320 | - client.onmessage = function() { |
1321 | - client.onmessage = function(result) { |
1322 | - var data = Y.JSON.parse(result.data).result; |
1323 | - assert.isTrue(data); |
1324 | - |
1325 | - // Verify that we can now find an expected entry |
1326 | - // in the database. |
1327 | - assert.isNotNull(state.db.services.getById('wordpress')); |
1328 | - |
1329 | - var changes = state.nextChanges(); |
1330 | - // Validate the delta includes imported services. |
1331 | - assert.include(changes.services, 'wordpress'); |
1332 | - assert.include(changes.services, 'mysql'); |
1333 | - // validate relation was added/updated. |
1334 | - assert.include(changes.relations, 'relation-0'); |
1335 | - done(); |
1336 | - }; |
1337 | - client.send(Y.JSON.stringify({op: 'importEnvironment', |
1338 | - envData: fixture})); |
1339 | - }; |
1340 | - client.open(); |
1341 | - }); |
1342 | - |
1343 | - }); |
1344 | - |
1345 | - |
1346 | - describe('sandbox.GoJujuAPI', function() { |
1347 | - var requires = [ |
1348 | - 'juju-env-sandbox', 'juju-tests-utils', 'juju-env-go', |
1349 | - 'juju-models', 'promise']; |
1350 | - var Y, sandboxModule, ClientConnection, environmentsModule, state, juju, |
1351 | - client, env, utils; |
1352 | - |
1353 | - before(function(done) { |
1354 | - Y = YUI(GlobalConfig).use(requires, function(Y) { |
1355 | - sandboxModule = Y.namespace('juju.environments.sandbox'); |
1356 | - environmentsModule = Y.namespace('juju.environments'); |
1357 | - utils = Y.namespace('juju-tests.utils'); |
1358 | - // A global variable required for testing. |
1359 | - window.flags = {}; |
1360 | - done(); |
1361 | - }); |
1362 | - }); |
1363 | - |
1364 | - beforeEach(function() { |
1365 | - state = utils.makeFakeBackendWithCharmStore(); |
1366 | - juju = new sandboxModule.GoJujuAPI({state: state}); |
1367 | - client = new sandboxModule.ClientConnection({juju: juju}); |
1368 | - env = new environmentsModule.GoEnvironment({conn: client}); |
1369 | - }); |
1370 | - |
1371 | - afterEach(function() { |
1372 | - env.destroy(); |
1373 | - client.destroy(); |
1374 | - juju.destroy(); |
1375 | - state.destroy(); |
1376 | - }); |
1377 | - |
1378 | - after(function() { |
1379 | - delete window.flags; |
1380 | - }); |
1381 | - |
1382 | - it('opens successfully.', function() { |
1383 | - assert.isFalse(juju.connected); |
1384 | - assert.isUndefined(juju.get('client')); |
1385 | - client.open(); |
1386 | - assert.isTrue(juju.connected); |
1387 | - assert.strictEqual(juju.get('client'), client); |
1388 | - }); |
1389 | - |
1390 | - it('ignores "open" when already open to same client.', function() { |
1391 | - client.receive = function() { |
1392 | - assert.ok(false, 'The receive method should not be called.'); |
1393 | - }; |
1394 | - // Whitebox test: duplicate "open" state. |
1395 | - juju.connected = true; |
1396 | - juju.set('client', client); |
1397 | - // This is effectively a re-open. |
1398 | - client.open(); |
1399 | - // The assert.ok above is the verification. |
1400 | - }); |
1401 | - |
1402 | - it('refuses to open if already open to another client.', function() { |
1403 | - // This is a simple way to make sure that we don't leave multiple |
1404 | - // setInterval calls running. If for some reason we want more |
1405 | - // simultaneous clients, that's fine, though that will require |
1406 | - // reworking the delta code generally. |
1407 | - juju.connected = true; |
1408 | - juju.set('client', {receive: function() { |
1409 | - assert.ok(false, 'The receive method should not have been called.'); |
1410 | - }}); |
1411 | - assert.throws( |
1412 | - client.open.bind(client), |
1413 | - 'INVALID_STATE_ERR : Connection is open to another client.'); |
1414 | - }); |
1415 | - |
1416 | - it('closes successfully.', function() { |
1417 | - client.open(); |
1418 | - assert.isTrue(juju.connected); |
1419 | - assert.notEqual(juju.get('client'), undefined); |
1420 | - client.close(); |
1421 | - assert.isFalse(juju.connected); |
1422 | - assert.isUndefined(juju.get('client')); |
1423 | - }); |
1424 | - |
1425 | - it('ignores "close" when already closed.', function() { |
1426 | - // This simply shows that we do not raise an error. |
1427 | - juju.close(); |
1428 | - }); |
1429 | - |
1430 | - it('can dispatch on received information.', function(done) { |
1431 | - var data = {Type: 'TheType', Request: 'TheRequest'}; |
1432 | - juju.handleTheTypeTheRequest = function(received) { |
1433 | - assert.notStrictEqual(received, data); |
1434 | - assert.deepEqual(received, data); |
1435 | - done(); |
1436 | - }; |
1437 | - client.open(); |
1438 | - client.send(Y.JSON.stringify(data)); |
1439 | - }); |
1440 | - |
1441 | - it('refuses to dispatch when closed.', function() { |
1442 | - assert.throws( |
1443 | - juju.receive.bind(juju, {}), |
1444 | - 'INVALID_STATE_ERR : Connection is closed.' |
1445 | - ); |
1446 | - }); |
1447 | - |
1448 | - it('can log in.', function(done) { |
1449 | - // See FakeBackend's authorizedUsers for these default authentication |
1450 | - // values. |
1451 | - var data = { |
1452 | - Type: 'Admin', |
1453 | - Request: 'Login', |
1454 | - Params: { |
1455 | - AuthTag: 'admin', |
1456 | - Password: 'password' |
1457 | - }, |
1458 | - RequestId: 42 |
1459 | - }; |
1460 | - client.onmessage = function(received) { |
1461 | - // Add in the error indicator so the deepEqual is comparing apples to |
1462 | - // apples. |
1463 | - data.Error = false; |
1464 | - assert.deepEqual(Y.JSON.parse(received.data), data); |
1465 | - assert.isTrue(state.get('authenticated')); |
1466 | - done(); |
1467 | - }; |
1468 | - state.logout(); |
1469 | - assert.isFalse(state.get('authenticated')); |
1470 | - client.open(); |
1471 | - client.send(Y.JSON.stringify(data)); |
1472 | - }); |
1473 | - |
1474 | - it('can log in (environment integration).', function(done) { |
1475 | - state.logout(); |
1476 | - env.after('login', function() { |
1477 | - assert.isTrue(env.userIsAuthenticated); |
1478 | - done(); |
1479 | - }); |
1480 | - env.connect(); |
1481 | - env.setCredentials({user: 'admin', password: 'password'}); |
1482 | - env.login(); |
1483 | - }); |
1484 | - |
1485 | - it('can deploy.', function(done) { |
1486 | - // We begin logged in. See utils.makeFakeBackendWithCharmStore. |
1487 | - var data = { |
1488 | - Type: 'Client', |
1489 | - Request: 'ServiceDeploy', |
1490 | - Params: { |
1491 | - CharmUrl: 'cs:wordpress', |
1492 | - ServiceName: 'kumquat', |
1493 | - ConfigYAML: 'funny: business', |
1494 | - NumUnits: 2 |
1495 | - }, |
1496 | - RequestId: 42 |
1497 | - }; |
1498 | - client.onmessage = function(received) { |
1499 | - var receivedData = Y.JSON.parse(received.data); |
1500 | - assert.equal(receivedData.RequestId, data.RequestId); |
1501 | - assert.isUndefined(receivedData.Error); |
1502 | - assert.isObject( |
1503 | - state.db.charms.getById('cs:precise/wordpress-10')); |
1504 | - var service = state.db.services.getById('kumquat'); |
1505 | - assert.isObject(service); |
1506 | - assert.equal(service.get('charm'), 'cs:precise/wordpress-10'); |
1507 | - assert.deepEqual(service.get('config'), {funny: 'business'}); |
1508 | - var units = state.db.units.get_units_for_service(service); |
1509 | - assert.lengthOf(units, 2); |
1510 | - done(); |
1511 | - }; |
1512 | - client.open(); |
1513 | - client.send(Y.JSON.stringify(data)); |
1514 | - }); |
1515 | - |
1516 | - it('can deploy (environment integration).', function() { |
1517 | - env.connect(); |
1518 | - // We begin logged in. See utils.makeFakeBackendWithCharmStore. |
1519 | - var callback = function(result) { |
1520 | - assert.isUndefined(result.err); |
1521 | - assert.equal(result.charm_url, 'cs:wordpress'); |
1522 | - var service = state.db.services.getById('kumquat'); |
1523 | - assert.equal(service.get('charm'), 'cs:precise/wordpress-10'); |
1524 | - assert.deepEqual(service.get('config'), {llama: 'pajama'}); |
1525 | - }; |
1526 | - env.deploy( |
1527 | - 'cs:wordpress', 'kumquat', {llama: 'pajama'}, null, 1, callback); |
1528 | - }); |
1529 | - |
1530 | - it('can communicate errors after attempting to deploy', function(done) { |
1531 | - env.connect(); |
1532 | - state.deploy('cs:wordpress', function() {}); |
1533 | - var callback = function(result) { |
1534 | - assert.equal( |
1535 | - result.err, 'A service with this name already exists.'); |
1536 | - done(); |
1537 | - }; |
1538 | - env.deploy('cs:wordpress', undefined, undefined, undefined, 1, |
1539 | - callback); |
1540 | - }); |
1541 | - |
1542 | - it('can set a charm.', function(done) { |
1543 | - state.deploy('cs:wordpress', function() {}); |
1544 | - var data = { |
1545 | - Type: 'Client', |
1546 | - Request: 'ServiceSetCharm', |
1547 | - Params: { |
1548 | - ServiceName: 'wordpress', |
1549 | - CharmUrl: 'cs:precise/mediawiki-6', |
1550 | - Force: false |
1551 | - }, |
1552 | - RequestId: 42 |
1553 | - }; |
1554 | - client.onmessage = function(received) { |
1555 | - var receivedData = Y.JSON.parse(received.data); |
1556 | - assert.isUndefined(receivedData.err); |
1557 | - var service = state.db.services.getById('wordpress'); |
1558 | - assert.equal(service.get('charm'), 'cs:precise/mediawiki-6'); |
1559 | - done(); |
1560 | - }; |
1561 | - client.open(); |
1562 | - client.send(Y.JSON.stringify(data)); |
1563 | - }); |
1564 | - |
1565 | - it('can set a charm (environment integration).', function(done) { |
1566 | - env.connect(); |
1567 | - state.deploy('cs:wordpress', function() {}); |
1568 | - var callback = function(result) { |
1569 | - assert.isUndefined(result.err); |
1570 | - var service = state.db.services.getById('wordpress'); |
1571 | - assert.equal(service.get('charm'), 'cs:precise/mediawiki-6'); |
1572 | - done(); |
1573 | - }; |
1574 | - env.setCharm('wordpress', 'cs:precise/mediawiki-6', false, callback); |
1575 | - }); |
1576 | - |
1577 | - /** |
1578 | - Generates the services required for some tests. After the services have |
1579 | - been generated it will call the supplied callback. |
1580 | - |
1581 | - This interacts directly with the fakebackend bypassing the environment. |
1582 | - The test "can add additional units" tests this code directly so as long |
1583 | - as it passes you can consider this method valid. |
1584 | - |
1585 | - @method generateServices |
1586 | - @param {Function} callback The callback to call after the services have |
1587 | - been generated. |
1588 | - */ |
1589 | - function generateServices(callback) { |
1590 | - state.deploy('cs:wordpress', function(service) { |
1591 | - var data = { |
1592 | - Type: 'Client', |
1593 | - Request: 'AddServiceUnits', |
1594 | - Params: { |
1595 | - ServiceName: 'wordpress', |
1596 | - NumUnits: 2 |
1597 | - } |
1598 | - }; |
1599 | - state.nextChanges(); |
1600 | - client.onmessage = function(received) { |
1601 | - // After done generating the services |
1602 | - callback(received); |
1603 | - }; |
1604 | - client.open(); |
1605 | - client.send(Y.JSON.stringify(data)); |
1606 | - }); |
1607 | - } |
1608 | - |
1609 | - /** |
1610 | - Same as generateServices but uses the environment integration methods. |
1611 | - Should be considered valid if "can add additional units (integration)" |
1612 | - test passes. |
1613 | - |
1614 | - @method generateIntegrationServices |
1615 | - @param {Function} callback The callback to call after the services have |
1616 | - been generated. |
1617 | - */ |
1618 | - function generateIntegrationServices(callback) { |
1619 | - var localCb = function(result) { |
1620 | - env.add_unit('kumquat', 2, function(data) { |
1621 | - // After finished generating integrated services. |
1622 | - callback(data); |
1623 | - }); |
1624 | - }; |
1625 | - env.connect(); |
1626 | - env.deploy( |
1627 | - 'cs:wordpress', 'kumquat', {llama: 'pajama'}, null, 1, localCb); |
1628 | - } |
1629 | - |
1630 | - /** |
1631 | - Generates the services and then exposes them for the un/expose tests. |
1632 | - After they have been exposed it calls the supplied callback. |
1633 | - |
1634 | - This interacts directly with the fakebackend bypassing the environment and |
1635 | - should be considered valid if "can expose a service" test passes. |
1636 | - |
1637 | - @method generateAndExposeService |
1638 | - @param {Function} callback The callback to call after the services have |
1639 | - been generated. |
1640 | - */ |
1641 | - function generateAndExposeService(callback) { |
1642 | - state.deploy('cs:wordpress', function(data) { |
1643 | - var command = { |
1644 | - Type: 'Client', |
1645 | - Request: 'ServiceExpose', |
1646 | - Params: {ServiceName: data.service.get('name')} |
1647 | - }; |
1648 | - state.nextChanges(); |
1649 | - client.onmessage = function(rec) { |
1650 | - callback(rec); |
1651 | - }; |
1652 | - client.open(); |
1653 | - client.send(Y.JSON.stringify(command)); |
1654 | - }, { unitCount: 1 }); |
1655 | - } |
1656 | - |
1657 | - /** |
1658 | - Same as generateAndExposeService but uses the environment integration |
1659 | - methods. Should be considered valid if "can expose a service |
1660 | - (integration)" test passes. |
1661 | - |
1662 | - @method generateAndExposeIntegrationService |
1663 | - @param {Function} callback The callback to call after the services have |
1664 | - been generated. |
1665 | - */ |
1666 | - function generateAndExposeIntegrationService(callback) { |
1667 | - var localCb = function(result) { |
1668 | - env.expose(result.service_name, function(rec) { |
1669 | - callback(rec); |
1670 | - }); |
1671 | - }; |
1672 | - env.connect(); |
1673 | - env.deploy( |
1674 | - 'cs:wordpress', 'kumquat', {llama: 'pajama'}, null, 1, localCb); |
1675 | - } |
1676 | - |
1677 | - it('can add additional units', function(done) { |
1678 | - function testForAddedUnits(received) { |
1679 | - var service = state.db.services.getById('wordpress'), |
1680 | - units = state.db.units.get_units_for_service(service), |
1681 | - data = Y.JSON.parse(received.data), |
1682 | - mock = { |
1683 | - Response: { |
1684 | - Units: ['wordpress/1', 'wordpress/2'] |
1685 | - } |
1686 | - }; |
1687 | - // Do we have enough total units? |
1688 | - assert.lengthOf(units, 3); |
1689 | - // Does the response object contain the proper data |
1690 | - assert.deepEqual(data, mock); |
1691 | - // Error is undefined |
1692 | - assert.isUndefined(data.Error); |
1693 | - done(); |
1694 | - } |
1695 | - // Generate the default services and add units |
1696 | - generateServices(testForAddedUnits); |
1697 | - }); |
1698 | - |
1699 | - it('throws an error when adding units to an invalid service', |
1700 | - function(done) { |
1701 | - state.deploy('cs:wordpress', function(service) { |
1702 | - var data = { |
1703 | - Type: 'Client', |
1704 | - Request: 'AddServiceUnits', |
1705 | - Params: { |
1706 | - ServiceName: 'noservice', |
1707 | - NumUnits: 2 |
1708 | - } |
1709 | - }; |
1710 | - state.nextChanges(); |
1711 | - client.onmessage = function() { |
1712 | - client.onmessage = function(received) { |
1713 | - var data = Y.JSON.parse(received.data); |
1714 | - |
1715 | - // If there is no error data.err will be undefined |
1716 | - assert.equal(true, !!data.Error); |
1717 | - done(); |
1718 | - }; |
1719 | - client.send(Y.JSON.stringify(data)); |
1720 | - }; |
1721 | - client.open(); |
1722 | - client.onmessage(); |
1723 | - }); |
1724 | - } |
1725 | - ); |
1726 | - |
1727 | - it('can add additional units (integration)', function(done) { |
1728 | - function testForAddedUnits(data) { |
1729 | - var service = state.db.services.getById('kumquat'), |
1730 | - units = state.db.units.get_units_for_service(service); |
1731 | - assert.lengthOf(units, 3); |
1732 | - done(); |
1733 | - } |
1734 | - generateIntegrationServices(testForAddedUnits); |
1735 | - }); |
1736 | - |
1737 | - it('can expose a service', function(done) { |
1738 | - function checkExposedService(rec) { |
1739 | - var serviceName = 'wordpress'; |
1740 | - var data = Y.JSON.parse(rec.data), |
1741 | - mock = {Response: {}}; |
1742 | - var service = state.db.services.getById(serviceName); |
1743 | - assert.equal(service.get('exposed'), true); |
1744 | - assert.deepEqual(data, mock); |
1745 | - done(); |
1746 | - } |
1747 | - generateAndExposeService(checkExposedService); |
1748 | - }); |
1749 | - |
1750 | - it('can expose a service (integration)', function(done) { |
1751 | - function checkExposedService(rec) { |
1752 | - var service = state.db.services.getById('kumquat'); |
1753 | - assert.equal(service.get('exposed'), true); |
1754 | - // The Go API does not set a result value. That is OK as |
1755 | - // it is never used. |
1756 | - assert.isUndefined(rec.result); |
1757 | - done(); |
1758 | - } |
1759 | - generateAndExposeIntegrationService(checkExposedService); |
1760 | - }); |
1761 | - |
1762 | - it('fails silently when exposing an exposed service', function(done) { |
1763 | - function checkExposedService(rec) { |
1764 | - var service_name = 'wordpress', |
1765 | - data = Y.JSON.parse(rec.data), |
1766 | - service = state.db.services.getById(service_name), |
1767 | - command = { |
1768 | - Type: 'Client', |
1769 | - Request: 'ServiceExpose', |
1770 | - Params: {ServiceName: service_name} |
1771 | - }; |
1772 | - state.nextChanges(); |
1773 | - client.onmessage = function(rec) { |
1774 | - assert.equal(data.err, undefined); |
1775 | - assert.equal(service.get('exposed'), true); |
1776 | - done(); |
1777 | - }; |
1778 | - client.send(Y.JSON.stringify(command)); |
1779 | - } |
1780 | - generateAndExposeService(checkExposedService); |
1781 | - }); |
1782 | - |
1783 | - it('fails with error when exposing an invalid service name', |
1784 | - function(done) { |
1785 | - state.deploy('cs:wordpress', function(data) { |
1786 | - var command = { |
1787 | - Type: 'Client', |
1788 | - Request: 'ServiceExpose', |
1789 | - Params: {ServiceName: 'foobar'} |
1790 | - }; |
1791 | - state.nextChanges(); |
1792 | - client.onmessage = function(rec) { |
1793 | - var data = Y.JSON.parse(rec.data); |
1794 | - assert.equal(data.Error, |
1795 | - '"foobar" is an invalid service name.'); |
1796 | - done(); |
1797 | - }; |
1798 | - client.open(); |
1799 | - client.send(Y.JSON.stringify(command)); |
1800 | - }, { unitCount: 1 }); |
1801 | - } |
1802 | - ); |
1803 | - |
1804 | - it('can unexpose a service', function(done) { |
1805 | - function unexposeService(rec) { |
1806 | - var service_name = 'wordpress', |
1807 | - data = Y.JSON.parse(rec.data), |
1808 | - command = { |
1809 | - Type: 'Client', |
1810 | - Request: 'ServiceUnexpose', |
1811 | - Params: {ServiceName: service_name} |
1812 | - }; |
1813 | - state.nextChanges(); |
1814 | - client.onmessage = function(rec) { |
1815 | - var data = Y.JSON.parse(rec.data), |
1816 | - service = state.db.services.getById('wordpress'), |
1817 | - mock = {Response: {}}; |
1818 | - assert.equal(service.get('exposed'), false); |
1819 | - assert.deepEqual(data, mock); |
1820 | - done(); |
1821 | - }; |
1822 | - client.send(Y.JSON.stringify(command)); |
1823 | - } |
1824 | - generateAndExposeService(unexposeService); |
1825 | - }); |
1826 | - |
1827 | - it('can unexpose a service (integration)', function(done) { |
1828 | - var service_name = 'kumquat'; |
1829 | - function unexposeService(rec) { |
1830 | - function localCb(rec) { |
1831 | - var service = state.db.services.getById(service_name); |
1832 | - assert.equal(service.get('exposed'), false); |
1833 | - // No result from Go unexpose. |
1834 | - assert.isUndefined(rec.result); |
1835 | - done(); |
1836 | - } |
1837 | - env.unexpose(service_name, localCb); |
1838 | - } |
1839 | - generateAndExposeIntegrationService(unexposeService); |
1840 | - }); |
1841 | - |
1842 | - it('fails silently when unexposing a not exposed service', |
1843 | - function(done) { |
1844 | - var service_name = 'wordpress'; |
1845 | - state.deploy('cs:wordpress', function(data) { |
1846 | - var command = { |
1847 | - Type: 'Client', |
1848 | - Request: 'ServiceUnexpose', |
1849 | - Params: {ServiceName: service_name} |
1850 | - }; |
1851 | - state.nextChanges(); |
1852 | - client.onmessage = function(rec) { |
1853 | - var data = Y.JSON.parse(rec.data), |
1854 | - service = state.db.services.getById(service_name); |
1855 | - assert.equal(service.get('exposed'), false); |
1856 | - assert.equal(data.err, undefined); |
1857 | - done(); |
1858 | - }; |
1859 | - client.open(); |
1860 | - client.send(Y.JSON.stringify(command)); |
1861 | - }, { unitCount: 1 }); |
1862 | - } |
1863 | - ); |
1864 | - |
1865 | - it('fails with error when unexposing an invalid service name', |
1866 | - function(done) { |
1867 | - function unexposeService(rec) { |
1868 | - var data = Y.JSON.parse(rec.data), |
1869 | - command = { |
1870 | - Type: 'Client', |
1871 | - Request: 'ServiceUnexpose', |
1872 | - Params: {ServiceName: 'foobar'} |
1873 | - }; |
1874 | - state.nextChanges(); |
1875 | - client.onmessage = function(rec) { |
1876 | - var data = Y.JSON.parse(rec.data); |
1877 | - assert.equal(data.Error, '"foobar" is an invalid service name.'); |
1878 | - done(); |
1879 | - }; |
1880 | - client.send(Y.JSON.stringify(command)); |
1881 | - } |
1882 | - generateAndExposeService(unexposeService); |
1883 | - } |
1884 | - ); |
1885 | - |
1886 | - it('can add a relation', function(done) { |
1887 | - // We begin logged in. See utils.makeFakeBackendWithCharmStore. |
1888 | - state.deploy('cs:wordpress', function() { |
1889 | - state.deploy('cs:mysql', function() { |
1890 | - var data = { |
1891 | - RequestId: 42, |
1892 | - Type: 'Client', |
1893 | - Request: 'AddRelation', |
1894 | - Params: { |
1895 | - Endpoints: ['wordpress:db', 'mysql:db'] |
1896 | - } |
1897 | - }; |
1898 | - client.onmessage = function(received) { |
1899 | - var recData = Y.JSON.parse(received.data); |
1900 | - assert.equal(recData.RequestId, data.RequestId); |
1901 | - assert.equal(recData.Error, undefined); |
1902 | - var recEndpoints = recData.Response.Endpoints; |
1903 | - assert.equal(recEndpoints.wordpress.Name, 'db'); |
1904 | - assert.equal(recEndpoints.wordpress.Scope, 'global'); |
1905 | - assert.equal(recEndpoints.mysql.Name, 'db'); |
1906 | - assert.equal(recEndpoints.mysql.Scope, 'global'); |
1907 | - done(); |
1908 | - }; |
1909 | - client.open(); |
1910 | - client.send(Y.JSON.stringify(data)); |
1911 | - }); |
1912 | - }); |
1913 | - }); |
1914 | - |
1915 | - it('can add a relation (integration)', function(done) { |
1916 | - env.connect(); |
1917 | - env.deploy('cs:wordpress', null, null, null, 1, function() { |
1918 | - env.deploy('cs:mysql', null, null, null, 1, function() { |
1919 | - var endpointA = ['wordpress', {name: 'db', role: 'client'}], |
1920 | - endpointB = ['mysql', {name: 'db', role: 'server'}]; |
1921 | - env.add_relation(endpointA, endpointB, function(recData) { |
1922 | - assert.equal(recData.err, undefined); |
1923 | - assert.equal(recData.endpoint_a, 'wordpress:db'); |
1924 | - assert.equal(recData.endpoint_b, 'mysql:db'); |
1925 | - assert.isObject(recData.result); |
1926 | - done(); |
1927 | - }); |
1928 | - }); |
1929 | - }); |
1930 | - }); |
1931 | - |
1932 | - it('is able to add a relation with a subordinate service', function(done) { |
1933 | - state.deploy('cs:wordpress', function() { |
1934 | - state.deploy('cs:puppet', function(service) { |
1935 | - var data = { |
1936 | - RequestId: 42, |
1937 | - Type: 'Client', |
1938 | - Request: 'AddRelation', |
1939 | - Params: { |
1940 | - Endpoints: ['wordpress:juju-info', 'puppet:juju-info'] |
1941 | - } |
1942 | - }; |
1943 | - client.onmessage = function(received) { |
1944 | - var recData = Y.JSON.parse(received.data); |
1945 | - assert.equal(recData.RequestId, data.RequestId); |
1946 | - assert.equal(recData.Error, undefined); |
1947 | - var recEndpoints = recData.Response.Endpoints; |
1948 | - assert.equal(recEndpoints.wordpress.Name, 'juju-info'); |
1949 | - assert.equal(recEndpoints.wordpress.Scope, 'container'); |
1950 | - assert.equal(recEndpoints.puppet.Name, 'juju-info'); |
1951 | - assert.equal(recEndpoints.puppet.Scope, 'container'); |
1952 | - done(); |
1953 | - }; |
1954 | - client.open(); |
1955 | - client.send(Y.JSON.stringify(data)); |
1956 | - }); |
1957 | - }); |
1958 | - }); |
1959 | - |
1960 | - it('throws an error if only one endpoint is supplied', function(done) { |
1961 | - // We begin logged in. See utils.makeFakeBackendWithCharmStore. |
1962 | - state.deploy('cs:wordpress', function() { |
1963 | - var data = { |
1964 | - RequestId: 42, |
1965 | - Type: 'Client', |
1966 | - Request: 'AddRelation', |
1967 | - Params: { |
1968 | - Endpoints: ['wordpress:db'] |
1969 | - } |
1970 | - }; |
1971 | - client.onmessage = function(received) { |
1972 | - var recData = Y.JSON.parse(received.data); |
1973 | - assert.equal(recData.RequestId, data.RequestId); |
1974 | - assert.equal(recData.Error, |
1975 | - 'Two string endpoint names required to establish a relation'); |
1976 | - done(); |
1977 | - }; |
1978 | - client.open(); |
1979 | - client.send(Y.JSON.stringify(data)); |
1980 | - }); |
1981 | - }); |
1982 | - |
1983 | - it('throws an error if endpoints are not relatable', function(done) { |
1984 | - // We begin logged in. See utils.makeFakeBackendWithCharmStore. |
1985 | - state.deploy('cs:wordpress', function() { |
1986 | - var data = { |
1987 | - RequestId: 42, |
1988 | - Type: 'Client', |
1989 | - Request: 'AddRelation', |
1990 | - Params: { |
1991 | - Endpoints: ['wordpress:db', 'mysql:foo'] |
1992 | - } |
1993 | - }; |
1994 | - client.onmessage = function(received) { |
1995 | - var recData = Y.JSON.parse(received.data); |
1996 | - assert.equal(recData.RequestId, data.RequestId); |
1997 | - assert.equal(recData.Error, 'Charm not loaded.'); |
1998 | - done(); |
1999 | - }; |
2000 | - client.open(); |
2001 | - client.send(Y.JSON.stringify(data)); |
2002 | - }); |
2003 | - }); |
2004 | - |
2005 | - it('can remove a relation', function(done) { |
2006 | - // We begin logged in. See utils.makeFakeBackendWithCharmStore. |
2007 | - var relation = ['wordpress:db', 'mysql:db']; |
2008 | - state.deploy('cs:wordpress', function() { |
2009 | - state.deploy('cs:mysql', function() { |
2010 | - state.addRelation(relation[0], relation[1]); |
2011 | - var data = { |
2012 | - RequestId: 42, |
2013 | - Type: 'Client', |
2014 | - Request: 'DestroyRelation', |
2015 | - Params: { |
2016 | - Endpoints: relation |
2017 | - } |
2018 | - }; |
2019 | - client.onmessage = function(received) { |
2020 | - var recData = Y.JSON.parse(received.data); |
2021 | - assert.equal(recData.RequestId, data.RequestId); |
2022 | - assert.equal(recData.Error, undefined); |
2023 | - done(); |
2024 | - }; |
2025 | - client.open(); |
2026 | - client.send(Y.JSON.stringify(data)); |
2027 | - }); |
2028 | - }); |
2029 | - }); |
2030 | - |
2031 | - it('can remove a relation(integration)', function(done) { |
2032 | - env.connect(); |
2033 | - env.deploy('cs:wordpress', null, null, null, 1, function() { |
2034 | - env.deploy('cs:mysql', null, null, null, 1, function() { |
2035 | - var endpointA = ['wordpress', {name: 'db', role: 'client'}], |
2036 | - endpointB = ['mysql', {name: 'db', role: 'server'}]; |
2037 | - env.add_relation(endpointA, endpointB, function() { |
2038 | - env.remove_relation(endpointA, endpointB, function(recData) { |
2039 | - assert.equal(recData.err, undefined); |
2040 | - assert.equal(recData.endpoint_a, 'wordpress:db'); |
2041 | - assert.equal(recData.endpoint_b, 'mysql:db'); |
2042 | - done(); |
2043 | - }); |
2044 | - }); |
2045 | - }); |
2046 | - }); |
2047 | - }); |
2048 | - |
2049 | - }); |
2050 | - |
2051 | })(); |
2052 | |
2053 | === added file 'test/test_sandbox_go.js' |
2054 | --- test/test_sandbox_go.js 1970-01-01 00:00:00 +0000 |
2055 | +++ test/test_sandbox_go.js 2013-06-28 15:03:23 +0000 |
2056 | @@ -0,0 +1,728 @@ |
2057 | +/* |
2058 | +This file is part of the Juju GUI, which lets users view and manage Juju |
2059 | +environments within a graphical interface (https://launchpad.net/juju-gui). |
2060 | +Copyright (C) 2012-2013 Canonical Ltd. |
2061 | + |
2062 | +This program is free software: you can redistribute it and/or modify it under |
2063 | +the terms of the GNU Affero General Public License version 3, as published by |
2064 | +the Free Software Foundation. |
2065 | + |
2066 | +This program is distributed in the hope that it will be useful, but WITHOUT |
2067 | +ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
2068 | +SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero |
2069 | +General Public License for more details. |
2070 | + |
2071 | +You should have received a copy of the GNU Affero General Public License along |
2072 | +with this program. If not, see <http://www.gnu.org/licenses/>. |
2073 | +*/ |
2074 | + |
2075 | +'use strict'; |
2076 | + |
2077 | +(function() { |
2078 | + |
2079 | + describe('sandbox.GoJujuAPI', function() { |
2080 | + var requires = [ |
2081 | + 'juju-env-sandbox', 'juju-tests-utils', 'juju-env-go', |
2082 | + 'juju-models', 'promise']; |
2083 | + var Y, sandboxModule, ClientConnection, environmentsModule, state, juju, |
2084 | + client, env, utils; |
2085 | + |
2086 | + before(function(done) { |
2087 | + Y = YUI(GlobalConfig).use(requires, function(Y) { |
2088 | + sandboxModule = Y.namespace('juju.environments.sandbox'); |
2089 | + environmentsModule = Y.namespace('juju.environments'); |
2090 | + utils = Y.namespace('juju-tests.utils'); |
2091 | + // A global variable required for testing. |
2092 | + window.flags = {}; |
2093 | + done(); |
2094 | + }); |
2095 | + }); |
2096 | + |
2097 | + beforeEach(function() { |
2098 | + state = utils.makeFakeBackendWithCharmStore(); |
2099 | + juju = new sandboxModule.GoJujuAPI({state: state}); |
2100 | + client = new sandboxModule.ClientConnection({juju: juju}); |
2101 | + env = new environmentsModule.GoEnvironment({conn: client}); |
2102 | + }); |
2103 | + |
2104 | + afterEach(function() { |
2105 | + env.destroy(); |
2106 | + client.destroy(); |
2107 | + juju.destroy(); |
2108 | + state.destroy(); |
2109 | + }); |
2110 | + |
2111 | + after(function() { |
2112 | + delete window.flags; |
2113 | + }); |
2114 | + |
2115 | + it('opens successfully.', function() { |
2116 | + assert.isFalse(juju.connected); |
2117 | + assert.isUndefined(juju.get('client')); |
2118 | + client.open(); |
2119 | + assert.isTrue(juju.connected); |
2120 | + assert.strictEqual(juju.get('client'), client); |
2121 | + }); |
2122 | + |
2123 | + it('ignores "open" when already open to same client.', function() { |
2124 | + client.receive = function() { |
2125 | + assert.ok(false, 'The receive method should not be called.'); |
2126 | + }; |
2127 | + // Whitebox test: duplicate "open" state. |
2128 | + juju.connected = true; |
2129 | + juju.set('client', client); |
2130 | + // This is effectively a re-open. |
2131 | + client.open(); |
2132 | + // The assert.ok above is the verification. |
2133 | + }); |
2134 | + |
2135 | + it('refuses to open if already open to another client.', function() { |
2136 | + // This is a simple way to make sure that we don't leave multiple |
2137 | + // setInterval calls running. If for some reason we want more |
2138 | + // simultaneous clients, that's fine, though that will require |
2139 | + // reworking the delta code generally. |
2140 | + juju.connected = true; |
2141 | + juju.set('client', {receive: function() { |
2142 | + assert.ok(false, 'The receive method should not have been called.'); |
2143 | + }}); |
2144 | + assert.throws( |
2145 | + client.open.bind(client), |
2146 | + 'INVALID_STATE_ERR : Connection is open to another client.'); |
2147 | + }); |
2148 | + |
2149 | + it('closes successfully.', function() { |
2150 | + client.open(); |
2151 | + assert.isTrue(juju.connected); |
2152 | + assert.notEqual(juju.get('client'), undefined); |
2153 | + client.close(); |
2154 | + assert.isFalse(juju.connected); |
2155 | + assert.isUndefined(juju.get('client')); |
2156 | + }); |
2157 | + |
2158 | + it('ignores "close" when already closed.', function() { |
2159 | + // This simply shows that we do not raise an error. |
2160 | + juju.close(); |
2161 | + }); |
2162 | + |
2163 | + it('can dispatch on received information.', function(done) { |
2164 | + var data = {Type: 'TheType', Request: 'TheRequest'}; |
2165 | + juju.handleTheTypeTheRequest = function(received) { |
2166 | + assert.notStrictEqual(received, data); |
2167 | + assert.deepEqual(received, data); |
2168 | + done(); |
2169 | + }; |
2170 | + client.open(); |
2171 | + client.send(Y.JSON.stringify(data)); |
2172 | + }); |
2173 | + |
2174 | + it('refuses to dispatch when closed.', function() { |
2175 | + assert.throws( |
2176 | + juju.receive.bind(juju, {}), |
2177 | + 'INVALID_STATE_ERR : Connection is closed.' |
2178 | + ); |
2179 | + }); |
2180 | + |
2181 | + it('can log in.', function(done) { |
2182 | + // See FakeBackend's authorizedUsers for these default authentication |
2183 | + // values. |
2184 | + var data = { |
2185 | + Type: 'Admin', |
2186 | + Request: 'Login', |
2187 | + Params: { |
2188 | + AuthTag: 'admin', |
2189 | + Password: 'password' |
2190 | + }, |
2191 | + RequestId: 42 |
2192 | + }; |
2193 | + client.onmessage = function(received) { |
2194 | + // Add in the error indicator so the deepEqual is comparing apples to |
2195 | + // apples. |
2196 | + data.Error = false; |
2197 | + assert.deepEqual(Y.JSON.parse(received.data), data); |
2198 | + assert.isTrue(state.get('authenticated')); |
2199 | + done(); |
2200 | + }; |
2201 | + state.logout(); |
2202 | + assert.isFalse(state.get('authenticated')); |
2203 | + client.open(); |
2204 | + client.send(Y.JSON.stringify(data)); |
2205 | + }); |
2206 | + |
2207 | + it('can log in (environment integration).', function(done) { |
2208 | + state.logout(); |
2209 | + env.after('login', function() { |
2210 | + assert.isTrue(env.userIsAuthenticated); |
2211 | + done(); |
2212 | + }); |
2213 | + env.connect(); |
2214 | + env.setCredentials({user: 'admin', password: 'password'}); |
2215 | + env.login(); |
2216 | + }); |
2217 | + |
2218 | + it('can deploy.', function(done) { |
2219 | + // We begin logged in. See utils.makeFakeBackendWithCharmStore. |
2220 | + var data = { |
2221 | + Type: 'Client', |
2222 | + Request: 'ServiceDeploy', |
2223 | + Params: { |
2224 | + CharmUrl: 'cs:wordpress', |
2225 | + ServiceName: 'kumquat', |
2226 | + ConfigYAML: 'funny: business', |
2227 | + NumUnits: 2 |
2228 | + }, |
2229 | + RequestId: 42 |
2230 | + }; |
2231 | + client.onmessage = function(received) { |
2232 | + var receivedData = Y.JSON.parse(received.data); |
2233 | + assert.equal(receivedData.RequestId, data.RequestId); |
2234 | + assert.isUndefined(receivedData.Error); |
2235 | + assert.isObject( |
2236 | + state.db.charms.getById('cs:precise/wordpress-10')); |
2237 | + var service = state.db.services.getById('kumquat'); |
2238 | + assert.isObject(service); |
2239 | + assert.equal(service.get('charm'), 'cs:precise/wordpress-10'); |
2240 | + assert.deepEqual(service.get('config'), {funny: 'business'}); |
2241 | + var units = state.db.units.get_units_for_service(service); |
2242 | + assert.lengthOf(units, 2); |
2243 | + done(); |
2244 | + }; |
2245 | + client.open(); |
2246 | + client.send(Y.JSON.stringify(data)); |
2247 | + }); |
2248 | + |
2249 | + it('can deploy (environment integration).', function() { |
2250 | + env.connect(); |
2251 | + // We begin logged in. See utils.makeFakeBackendWithCharmStore. |
2252 | + var callback = function(result) { |
2253 | + assert.isUndefined(result.err); |
2254 | + assert.equal(result.charm_url, 'cs:wordpress'); |
2255 | + var service = state.db.services.getById('kumquat'); |
2256 | + assert.equal(service.get('charm'), 'cs:precise/wordpress-10'); |
2257 | + assert.deepEqual(service.get('config'), {llama: 'pajama'}); |
2258 | + }; |
2259 | + env.deploy( |
2260 | + 'cs:wordpress', 'kumquat', {llama: 'pajama'}, null, 1, callback); |
2261 | + }); |
2262 | + |
2263 | + it('can communicate errors after attempting to deploy', function(done) { |
2264 | + env.connect(); |
2265 | + state.deploy('cs:wordpress', function() {}); |
2266 | + var callback = function(result) { |
2267 | + assert.equal( |
2268 | + result.err, 'A service with this name already exists.'); |
2269 | + done(); |
2270 | + }; |
2271 | + env.deploy('cs:wordpress', undefined, undefined, undefined, 1, |
2272 | + callback); |
2273 | + }); |
2274 | + |
2275 | + it('can set a charm.', function(done) { |
2276 | + state.deploy('cs:wordpress', function() {}); |
2277 | + var data = { |
2278 | + Type: 'Client', |
2279 | + Request: 'ServiceSetCharm', |
2280 | + Params: { |
2281 | + ServiceName: 'wordpress', |
2282 | + CharmUrl: 'cs:precise/mediawiki-6', |
2283 | + Force: false |
2284 | + }, |
2285 | + RequestId: 42 |
2286 | + }; |
2287 | + client.onmessage = function(received) { |
2288 | + var receivedData = Y.JSON.parse(received.data); |
2289 | + assert.isUndefined(receivedData.err); |
2290 | + var service = state.db.services.getById('wordpress'); |
2291 | + assert.equal(service.get('charm'), 'cs:precise/mediawiki-6'); |
2292 | + done(); |
2293 | + }; |
2294 | + client.open(); |
2295 | + client.send(Y.JSON.stringify(data)); |
2296 | + }); |
2297 | + |
2298 | + it('can set a charm (environment integration).', function(done) { |
2299 | + env.connect(); |
2300 | + state.deploy('cs:wordpress', function() {}); |
2301 | + var callback = function(result) { |
2302 | + assert.isUndefined(result.err); |
2303 | + var service = state.db.services.getById('wordpress'); |
2304 | + assert.equal(service.get('charm'), 'cs:precise/mediawiki-6'); |
2305 | + done(); |
2306 | + }; |
2307 | + env.setCharm('wordpress', 'cs:precise/mediawiki-6', false, callback); |
2308 | + }); |
2309 | + |
2310 | + /** |
2311 | + Generates the services required for some tests. After the services have |
2312 | + been generated it will call the supplied callback. |
2313 | + |
2314 | + This interacts directly with the fakebackend bypassing the environment. |
2315 | + The test "can add additional units" tests this code directly so as long |
2316 | + as it passes you can consider this method valid. |
2317 | + |
2318 | + @method generateServices |
2319 | + @param {Function} callback The callback to call after the services have |
2320 | + been generated. |
2321 | + */ |
2322 | + function generateServices(callback) { |
2323 | + state.deploy('cs:wordpress', function(service) { |
2324 | + var data = { |
2325 | + Type: 'Client', |
2326 | + Request: 'AddServiceUnits', |
2327 | + Params: { |
2328 | + ServiceName: 'wordpress', |
2329 | + NumUnits: 2 |
2330 | + } |
2331 | + }; |
2332 | + state.nextChanges(); |
2333 | + client.onmessage = function(received) { |
2334 | + // After done generating the services |
2335 | + callback(received); |
2336 | + }; |
2337 | + client.open(); |
2338 | + client.send(Y.JSON.stringify(data)); |
2339 | + }); |
2340 | + } |
2341 | + |
2342 | + /** |
2343 | + Same as generateServices but uses the environment integration methods. |
2344 | + Should be considered valid if "can add additional units (integration)" |
2345 | + test passes. |
2346 | + |
2347 | + @method generateIntegrationServices |
2348 | + @param {Function} callback The callback to call after the services have |
2349 | + been generated. |
2350 | + */ |
2351 | + function generateIntegrationServices(callback) { |
2352 | + var localCb = function(result) { |
2353 | + env.add_unit('kumquat', 2, function(data) { |
2354 | + // After finished generating integrated services. |
2355 | + callback(data); |
2356 | + }); |
2357 | + }; |
2358 | + env.connect(); |
2359 | + env.deploy( |
2360 | + 'cs:wordpress', 'kumquat', {llama: 'pajama'}, null, 1, localCb); |
2361 | + } |
2362 | + |
2363 | + /** |
2364 | + Generates the services and then exposes them for the un/expose tests. |
2365 | + After they have been exposed it calls the supplied callback. |
2366 | + |
2367 | + This interacts directly with the fakebackend bypassing the environment and |
2368 | + should be considered valid if "can expose a service" test passes. |
2369 | + |
2370 | + @method generateAndExposeService |
2371 | + @param {Function} callback The callback to call after the services have |
2372 | + been generated. |
2373 | + */ |
2374 | + function generateAndExposeService(callback) { |
2375 | + state.deploy('cs:wordpress', function(data) { |
2376 | + var command = { |
2377 | + Type: 'Client', |
2378 | + Request: 'ServiceExpose', |
2379 | + Params: {ServiceName: data.service.get('name')} |
2380 | + }; |
2381 | + state.nextChanges(); |
2382 | + client.onmessage = function(rec) { |
2383 | + callback(rec); |
2384 | + }; |
2385 | + client.open(); |
2386 | + client.send(Y.JSON.stringify(command)); |
2387 | + }, { unitCount: 1 }); |
2388 | + } |
2389 | + |
2390 | + /** |
2391 | + Same as generateAndExposeService but uses the environment integration |
2392 | + methods. Should be considered valid if "can expose a service |
2393 | + (integration)" test passes. |
2394 | + |
2395 | + @method generateAndExposeIntegrationService |
2396 | + @param {Function} callback The callback to call after the services have |
2397 | + been generated. |
2398 | + */ |
2399 | + function generateAndExposeIntegrationService(callback) { |
2400 | + var localCb = function(result) { |
2401 | + env.expose(result.service_name, function(rec) { |
2402 | + callback(rec); |
2403 | + }); |
2404 | + }; |
2405 | + env.connect(); |
2406 | + env.deploy( |
2407 | + 'cs:wordpress', 'kumquat', {llama: 'pajama'}, null, 1, localCb); |
2408 | + } |
2409 | + |
2410 | + it('can add additional units', function(done) { |
2411 | + function testForAddedUnits(received) { |
2412 | + var service = state.db.services.getById('wordpress'), |
2413 | + units = state.db.units.get_units_for_service(service), |
2414 | + data = Y.JSON.parse(received.data), |
2415 | + mock = { |
2416 | + Response: { |
2417 | + Units: ['wordpress/1', 'wordpress/2'] |
2418 | + } |
2419 | + }; |
2420 | + // Do we have enough total units? |
2421 | + assert.lengthOf(units, 3); |
2422 | + // Does the response object contain the proper data |
2423 | + assert.deepEqual(data, mock); |
2424 | + // Error is undefined |
2425 | + assert.isUndefined(data.Error); |
2426 | + done(); |
2427 | + } |
2428 | + // Generate the default services and add units |
2429 | + generateServices(testForAddedUnits); |
2430 | + }); |
2431 | + |
2432 | + it('throws an error when adding units to an invalid service', |
2433 | + function(done) { |
2434 | + state.deploy('cs:wordpress', function(service) { |
2435 | + var data = { |
2436 | + Type: 'Client', |
2437 | + Request: 'AddServiceUnits', |
2438 | + Params: { |
2439 | + ServiceName: 'noservice', |
2440 | + NumUnits: 2 |
2441 | + } |
2442 | + }; |
2443 | + state.nextChanges(); |
2444 | + client.onmessage = function() { |
2445 | + client.onmessage = function(received) { |
2446 | + var data = Y.JSON.parse(received.data); |
2447 | + |
2448 | + // If there is no error data.err will be undefined |
2449 | + assert.equal(true, !!data.Error); |
2450 | + done(); |
2451 | + }; |
2452 | + client.send(Y.JSON.stringify(data)); |
2453 | + }; |
2454 | + client.open(); |
2455 | + client.onmessage(); |
2456 | + }); |
2457 | + } |
2458 | + ); |
2459 | + |
2460 | + it('can add additional units (integration)', function(done) { |
2461 | + function testForAddedUnits(data) { |
2462 | + var service = state.db.services.getById('kumquat'), |
2463 | + units = state.db.units.get_units_for_service(service); |
2464 | + assert.lengthOf(units, 3); |
2465 | + done(); |
2466 | + } |
2467 | + generateIntegrationServices(testForAddedUnits); |
2468 | + }); |
2469 | + |
2470 | + it('can expose a service', function(done) { |
2471 | + function checkExposedService(rec) { |
2472 | + var serviceName = 'wordpress'; |
2473 | + var data = Y.JSON.parse(rec.data), |
2474 | + mock = {Response: {}}; |
2475 | + var service = state.db.services.getById(serviceName); |
2476 | + assert.equal(service.get('exposed'), true); |
2477 | + assert.deepEqual(data, mock); |
2478 | + done(); |
2479 | + } |
2480 | + generateAndExposeService(checkExposedService); |
2481 | + }); |
2482 | + |
2483 | + it('can expose a service (integration)', function(done) { |
2484 | + function checkExposedService(rec) { |
2485 | + var service = state.db.services.getById('kumquat'); |
2486 | + assert.equal(service.get('exposed'), true); |
2487 | + // The Go API does not set a result value. That is OK as |
2488 | + // it is never used. |
2489 | + assert.isUndefined(rec.result); |
2490 | + done(); |
2491 | + } |
2492 | + generateAndExposeIntegrationService(checkExposedService); |
2493 | + }); |
2494 | + |
2495 | + it('fails silently when exposing an exposed service', function(done) { |
2496 | + function checkExposedService(rec) { |
2497 | + var service_name = 'wordpress', |
2498 | + data = Y.JSON.parse(rec.data), |
2499 | + service = state.db.services.getById(service_name), |
2500 | + command = { |
2501 | + Type: 'Client', |
2502 | + Request: 'ServiceExpose', |
2503 | + Params: {ServiceName: service_name} |
2504 | + }; |
2505 | + state.nextChanges(); |
2506 | + client.onmessage = function(rec) { |
2507 | + assert.equal(data.err, undefined); |
2508 | + assert.equal(service.get('exposed'), true); |
2509 | + done(); |
2510 | + }; |
2511 | + client.send(Y.JSON.stringify(command)); |
2512 | + } |
2513 | + generateAndExposeService(checkExposedService); |
2514 | + }); |
2515 | + |
2516 | + it('fails with error when exposing an invalid service name', |
2517 | + function(done) { |
2518 | + state.deploy('cs:wordpress', function(data) { |
2519 | + var command = { |
2520 | + Type: 'Client', |
2521 | + Request: 'ServiceExpose', |
2522 | + Params: {ServiceName: 'foobar'} |
2523 | + }; |
2524 | + state.nextChanges(); |
2525 | + client.onmessage = function(rec) { |
2526 | + var data = Y.JSON.parse(rec.data); |
2527 | + assert.equal(data.Error, |
2528 | + '"foobar" is an invalid service name.'); |
2529 | + done(); |
2530 | + }; |
2531 | + client.open(); |
2532 | + client.send(Y.JSON.stringify(command)); |
2533 | + }, { unitCount: 1 }); |
2534 | + } |
2535 | + ); |
2536 | + |
2537 | + it('can unexpose a service', function(done) { |
2538 | + function unexposeService(rec) { |
2539 | + var service_name = 'wordpress', |
2540 | + data = Y.JSON.parse(rec.data), |
2541 | + command = { |
2542 | + Type: 'Client', |
2543 | + Request: 'ServiceUnexpose', |
2544 | + Params: {ServiceName: service_name} |
2545 | + }; |
2546 | + state.nextChanges(); |
2547 | + client.onmessage = function(rec) { |
2548 | + var data = Y.JSON.parse(rec.data), |
2549 | + service = state.db.services.getById('wordpress'), |
2550 | + mock = {Response: {}}; |
2551 | + assert.equal(service.get('exposed'), false); |
2552 | + assert.deepEqual(data, mock); |
2553 | + done(); |
2554 | + }; |
2555 | + client.send(Y.JSON.stringify(command)); |
2556 | + } |
2557 | + generateAndExposeService(unexposeService); |
2558 | + }); |
2559 | + |
2560 | + it('can unexpose a service (integration)', function(done) { |
2561 | + var service_name = 'kumquat'; |
2562 | + function unexposeService(rec) { |
2563 | + function localCb(rec) { |
2564 | + var service = state.db.services.getById(service_name); |
2565 | + assert.equal(service.get('exposed'), false); |
2566 | + // No result from Go unexpose. |
2567 | + assert.isUndefined(rec.result); |
2568 | + done(); |
2569 | + } |
2570 | + env.unexpose(service_name, localCb); |
2571 | + } |
2572 | + generateAndExposeIntegrationService(unexposeService); |
2573 | + }); |
2574 | + |
2575 | + it('fails silently when unexposing a not exposed service', |
2576 | + function(done) { |
2577 | + var service_name = 'wordpress'; |
2578 | + state.deploy('cs:wordpress', function(data) { |
2579 | + var command = { |
2580 | + Type: 'Client', |
2581 | + Request: 'ServiceUnexpose', |
2582 | + Params: {ServiceName: service_name} |
2583 | + }; |
2584 | + state.nextChanges(); |
2585 | + client.onmessage = function(rec) { |
2586 | + var data = Y.JSON.parse(rec.data), |
2587 | + service = state.db.services.getById(service_name); |
2588 | + assert.equal(service.get('exposed'), false); |
2589 | + assert.equal(data.err, undefined); |
2590 | + done(); |
2591 | + }; |
2592 | + client.open(); |
2593 | + client.send(Y.JSON.stringify(command)); |
2594 | + }, { unitCount: 1 }); |
2595 | + } |
2596 | + ); |
2597 | + |
2598 | + it('fails with error when unexposing an invalid service name', |
2599 | + function(done) { |
2600 | + function unexposeService(rec) { |
2601 | + var data = Y.JSON.parse(rec.data), |
2602 | + command = { |
2603 | + Type: 'Client', |
2604 | + Request: 'ServiceUnexpose', |
2605 | + Params: {ServiceName: 'foobar'} |
2606 | + }; |
2607 | + state.nextChanges(); |
2608 | + client.onmessage = function(rec) { |
2609 | + var data = Y.JSON.parse(rec.data); |
2610 | + assert.equal(data.Error, '"foobar" is an invalid service name.'); |
2611 | + done(); |
2612 | + }; |
2613 | + client.send(Y.JSON.stringify(command)); |
2614 | + } |
2615 | + generateAndExposeService(unexposeService); |
2616 | + } |
2617 | + ); |
2618 | + |
2619 | + it('can add a relation', function(done) { |
2620 | + // We begin logged in. See utils.makeFakeBackendWithCharmStore. |
2621 | + state.deploy('cs:wordpress', function() { |
2622 | + state.deploy('cs:mysql', function() { |
2623 | + var data = { |
2624 | + RequestId: 42, |
2625 | + Type: 'Client', |
2626 | + Request: 'AddRelation', |
2627 | + Params: { |
2628 | + Endpoints: ['wordpress:db', 'mysql:db'] |
2629 | + } |
2630 | + }; |
2631 | + client.onmessage = function(received) { |
2632 | + var recData = Y.JSON.parse(received.data); |
2633 | + assert.equal(recData.RequestId, data.RequestId); |
2634 | + assert.equal(recData.Error, undefined); |
2635 | + var recEndpoints = recData.Response.Endpoints; |
2636 | + assert.equal(recEndpoints.wordpress.Name, 'db'); |
2637 | + assert.equal(recEndpoints.wordpress.Scope, 'global'); |
2638 | + assert.equal(recEndpoints.mysql.Name, 'db'); |
2639 | + assert.equal(recEndpoints.mysql.Scope, 'global'); |
2640 | + done(); |
2641 | + }; |
2642 | + client.open(); |
2643 | + client.send(Y.JSON.stringify(data)); |
2644 | + }); |
2645 | + }); |
2646 | + }); |
2647 | + |
2648 | + it('can add a relation (integration)', function(done) { |
2649 | + env.connect(); |
2650 | + env.deploy('cs:wordpress', null, null, null, 1, function() { |
2651 | + env.deploy('cs:mysql', null, null, null, 1, function() { |
2652 | + var endpointA = ['wordpress', {name: 'db', role: 'client'}], |
2653 | + endpointB = ['mysql', {name: 'db', role: 'server'}]; |
2654 | + env.add_relation(endpointA, endpointB, function(recData) { |
2655 | + assert.equal(recData.err, undefined); |
2656 | + assert.equal(recData.endpoint_a, 'wordpress:db'); |
2657 | + assert.equal(recData.endpoint_b, 'mysql:db'); |
2658 | + assert.isObject(recData.result); |
2659 | + done(); |
2660 | + }); |
2661 | + }); |
2662 | + }); |
2663 | + }); |
2664 | + |
2665 | + it('is able to add a relation with a subordinate service', function(done) { |
2666 | + state.deploy('cs:wordpress', function() { |
2667 | + state.deploy('cs:puppet', function(service) { |
2668 | + var data = { |
2669 | + RequestId: 42, |
2670 | + Type: 'Client', |
2671 | + Request: 'AddRelation', |
2672 | + Params: { |
2673 | + Endpoints: ['wordpress:juju-info', 'puppet:juju-info'] |
2674 | + } |
2675 | + }; |
2676 | + client.onmessage = function(received) { |
2677 | + var recData = Y.JSON.parse(received.data); |
2678 | + assert.equal(recData.RequestId, data.RequestId); |
2679 | + assert.equal(recData.Error, undefined); |
2680 | + var recEndpoints = recData.Response.Endpoints; |
2681 | + assert.equal(recEndpoints.wordpress.Name, 'juju-info'); |
2682 | + assert.equal(recEndpoints.wordpress.Scope, 'container'); |
2683 | + assert.equal(recEndpoints.puppet.Name, 'juju-info'); |
2684 | + assert.equal(recEndpoints.puppet.Scope, 'container'); |
2685 | + done(); |
2686 | + }; |
2687 | + client.open(); |
2688 | + client.send(Y.JSON.stringify(data)); |
2689 | + }); |
2690 | + }); |
2691 | + }); |
2692 | + |
2693 | + it('throws an error if only one endpoint is supplied', function(done) { |
2694 | + // We begin logged in. See utils.makeFakeBackendWithCharmStore. |
2695 | + state.deploy('cs:wordpress', function() { |
2696 | + var data = { |
2697 | + RequestId: 42, |
2698 | + Type: 'Client', |
2699 | + Request: 'AddRelation', |
2700 | + Params: { |
2701 | + Endpoints: ['wordpress:db'] |
2702 | + } |
2703 | + }; |
2704 | + client.onmessage = function(received) { |
2705 | + var recData = Y.JSON.parse(received.data); |
2706 | + assert.equal(recData.RequestId, data.RequestId); |
2707 | + assert.equal(recData.Error, |
2708 | + 'Two string endpoint names required to establish a relation'); |
2709 | + done(); |
2710 | + }; |
2711 | + client.open(); |
2712 | + client.send(Y.JSON.stringify(data)); |
2713 | + }); |
2714 | + }); |
2715 | + |
2716 | + it('throws an error if endpoints are not relatable', function(done) { |
2717 | + // We begin logged in. See utils.makeFakeBackendWithCharmStore. |
2718 | + state.deploy('cs:wordpress', function() { |
2719 | + var data = { |
2720 | + RequestId: 42, |
2721 | + Type: 'Client', |
2722 | + Request: 'AddRelation', |
2723 | + Params: { |
2724 | + Endpoints: ['wordpress:db', 'mysql:foo'] |
2725 | + } |
2726 | + }; |
2727 | + client.onmessage = function(received) { |
2728 | + var recData = Y.JSON.parse(received.data); |
2729 | + assert.equal(recData.RequestId, data.RequestId); |
2730 | + assert.equal(recData.Error, 'Charm not loaded.'); |
2731 | + done(); |
2732 | + }; |
2733 | + client.open(); |
2734 | + client.send(Y.JSON.stringify(data)); |
2735 | + }); |
2736 | + }); |
2737 | + |
2738 | + it('can remove a relation', function(done) { |
2739 | + // We begin logged in. See utils.makeFakeBackendWithCharmStore. |
2740 | + var relation = ['wordpress:db', 'mysql:db']; |
2741 | + state.deploy('cs:wordpress', function() { |
2742 | + state.deploy('cs:mysql', function() { |
2743 | + state.addRelation(relation[0], relation[1]); |
2744 | + var data = { |
2745 | + RequestId: 42, |
2746 | + Type: 'Client', |
2747 | + Request: 'DestroyRelation', |
2748 | + Params: { |
2749 | + Endpoints: relation |
2750 | + } |
2751 | + }; |
2752 | + client.onmessage = function(received) { |
2753 | + var recData = Y.JSON.parse(received.data); |
2754 | + assert.equal(recData.RequestId, data.RequestId); |
2755 | + assert.equal(recData.Error, undefined); |
2756 | + done(); |
2757 | + }; |
2758 | + client.open(); |
2759 | + client.send(Y.JSON.stringify(data)); |
2760 | + }); |
2761 | + }); |
2762 | + }); |
2763 | + |
2764 | + it('can remove a relation(integration)', function(done) { |
2765 | + env.connect(); |
2766 | + env.deploy('cs:wordpress', null, null, null, 1, function() { |
2767 | + env.deploy('cs:mysql', null, null, null, 1, function() { |
2768 | + var endpointA = ['wordpress', {name: 'db', role: 'client'}], |
2769 | + endpointB = ['mysql', {name: 'db', role: 'server'}]; |
2770 | + env.add_relation(endpointA, endpointB, function() { |
2771 | + env.remove_relation(endpointA, endpointB, function(recData) { |
2772 | + assert.equal(recData.err, undefined); |
2773 | + assert.equal(recData.endpoint_a, 'wordpress:db'); |
2774 | + assert.equal(recData.endpoint_b, 'mysql:db'); |
2775 | + done(); |
2776 | + }); |
2777 | + }); |
2778 | + }); |
2779 | + }); |
2780 | + }); |
2781 | + |
2782 | + }); |
2783 | + |
2784 | +})(); |
2785 | |
2786 | === added file 'test/test_sandbox_python.js' |
2787 | --- test/test_sandbox_python.js 1970-01-01 00:00:00 +0000 |
2788 | +++ test/test_sandbox_python.js 2013-06-28 15:03:23 +0000 |
2789 | @@ -0,0 +1,1347 @@ |
2790 | +/* |
2791 | +This file is part of the Juju GUI, which lets users view and manage Juju |
2792 | +environments within a graphical interface (https://launchpad.net/juju-gui). |
2793 | +Copyright (C) 2012-2013 Canonical Ltd. |
2794 | + |
2795 | +This program is free software: you can redistribute it and/or modify it under |
2796 | +the terms of the GNU Affero General Public License version 3, as published by |
2797 | +the Free Software Foundation. |
2798 | + |
2799 | +This program is distributed in the hope that it will be useful, but WITHOUT |
2800 | +ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
2801 | +SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero |
2802 | +General Public License for more details. |
2803 | + |
2804 | +You should have received a copy of the GNU Affero General Public License along |
2805 | +with this program. If not, see <http://www.gnu.org/licenses/>. |
2806 | +*/ |
2807 | + |
2808 | +'use strict'; |
2809 | + |
2810 | +(function() { |
2811 | + |
2812 | + describe('sandbox.PyJujuAPI', function() { |
2813 | + var requires = [ |
2814 | + 'juju-env-sandbox', 'juju-tests-utils', 'juju-env-python', |
2815 | + 'juju-models', 'promise']; |
2816 | + var Y, sandboxModule, ClientConnection, environmentsModule, state, juju, |
2817 | + client, env, utils, cleanups; |
2818 | + |
2819 | + before(function(done) { |
2820 | + Y = YUI(GlobalConfig).use(requires, function(Y) { |
2821 | + sandboxModule = Y.namespace('juju.environments.sandbox'); |
2822 | + environmentsModule = Y.namespace('juju.environments'); |
2823 | + utils = Y.namespace('juju-tests.utils'); |
2824 | + // A global variable required for testing. |
2825 | + window.flags = {}; |
2826 | + done(); |
2827 | + }); |
2828 | + }); |
2829 | + |
2830 | + beforeEach(function() { |
2831 | + state = utils.makeFakeBackendWithCharmStore(); |
2832 | + juju = new sandboxModule.PyJujuAPI({state: state}); |
2833 | + client = new sandboxModule.ClientConnection({juju: juju}); |
2834 | + env = new environmentsModule.PythonEnvironment({conn: client}); |
2835 | + cleanups = []; |
2836 | + }); |
2837 | + |
2838 | + afterEach(function() { |
2839 | + Y.each(cleanups, function(f) {f();}); |
2840 | + env.destroy(); |
2841 | + client.destroy(); |
2842 | + juju.destroy(); |
2843 | + state.destroy(); |
2844 | + }); |
2845 | + |
2846 | + after(function() { |
2847 | + delete window.flags; |
2848 | + }); |
2849 | + |
2850 | + /** |
2851 | + Generates the services required for some tests. After the services have |
2852 | + been generated it will call the supplied callback. |
2853 | + |
2854 | + This interacts directly with the fakebackend bypassing the environment. |
2855 | + The test "can add additional units" tests this code directly so as long |
2856 | + as it passes you can consider this method valid. |
2857 | + |
2858 | + @method generateServices |
2859 | + @param {Function} callback The callback to call after the services have |
2860 | + been generated. |
2861 | + */ |
2862 | + function generateServices(callback) { |
2863 | + state.deploy('cs:wordpress', function(service) { |
2864 | + var data = { |
2865 | + op: 'add_unit', |
2866 | + service_name: 'wordpress', |
2867 | + num_units: 2 |
2868 | + }; |
2869 | + state.nextChanges(); |
2870 | + client.onmessage = function() { |
2871 | + client.onmessage = function(received) { |
2872 | + // After done generating the services |
2873 | + callback(received); |
2874 | + }; |
2875 | + client.send(Y.JSON.stringify(data)); |
2876 | + }; |
2877 | + client.open(); |
2878 | + }); |
2879 | + } |
2880 | + |
2881 | + /** |
2882 | + Generates the two services required for relation removal tests. After the |
2883 | + services have been generated, a relation between them will be added and |
2884 | + then removed. |
2885 | + |
2886 | + This interacts directly with the fakebackend bypassing the environment. |
2887 | + |
2888 | + @method generateAndRelateServices |
2889 | + @param {Array} charms The URLs of two charms to be deployed. |
2890 | + @param {Array} relation Two endpoint strings to be related. |
2891 | + @param {Array} removeRelation Two enpoint strings identifying |
2892 | + a relation to be removed. |
2893 | + @param {Object} mock Object with the expected return values of |
2894 | + the relation removal operation. |
2895 | + @param {Function} done To be called to signal the test end. |
2896 | + @return {undefined} Side effects only. |
2897 | + */ |
2898 | + function generateAndRelateServices(charms, relation, |
2899 | + removeRelation, mock, done) { |
2900 | + state.deploy(charms[0], function() { |
2901 | + state.deploy(charms[1], function() { |
2902 | + if (relation) { |
2903 | + state.addRelation(relation[0], relation[1]); |
2904 | + } |
2905 | + var data = { |
2906 | + op: 'remove_relation', |
2907 | + endpoint_a: removeRelation[0], |
2908 | + endpoint_b: removeRelation[1] |
2909 | + }; |
2910 | + client.onmessage = function(received) { |
2911 | + var recData = Y.JSON.parse(received.data); |
2912 | + // Skip the defaultSeriesChange message. |
2913 | + if (recData.default_series === undefined) { |
2914 | + assert.equal(recData.result, mock.result); |
2915 | + assert.equal(recData.err, mock.err); |
2916 | + if (!recData.err) { |
2917 | + assert.equal(recData.endpoint_a, mock.endpoint_a); |
2918 | + assert.equal(recData.endpoint_b, mock.endpoint_b); |
2919 | + } |
2920 | + done(); |
2921 | + } |
2922 | + }; |
2923 | + client.open(); |
2924 | + client.send(Y.JSON.stringify(data)); |
2925 | + }); |
2926 | + }); |
2927 | + } |
2928 | + |
2929 | + /** |
2930 | + Same as generateServices but uses the environment integration methods. |
2931 | + Should be considered valid if "can add additional units (integration)" |
2932 | + test passes. |
2933 | + |
2934 | + @method generateIntegrationServices |
2935 | + @param {Function} callback The callback to call after the services have |
2936 | + been generated. |
2937 | + */ |
2938 | + function generateIntegrationServices(callback) { |
2939 | + env.after('defaultSeriesChange', function() { |
2940 | + var localCb = function(result) { |
2941 | + env.add_unit('kumquat', 2, function(data) { |
2942 | + // After finished generating integrated services |
2943 | + callback(data); |
2944 | + }); |
2945 | + }; |
2946 | + env.deploy( |
2947 | + 'cs:wordpress', 'kumquat', {llama: 'pajama'}, null, 1, localCb); |
2948 | + }); |
2949 | + env.connect(); |
2950 | + } |
2951 | + |
2952 | + /** |
2953 | + Generates the services and then exposes them for the un/expose tests. |
2954 | + After they have been exposed it calls the supplied callback. |
2955 | + |
2956 | + This interacts directly with the fakebackend bypassing the environment and |
2957 | + should be considered valid if "can expose a service" test passes. |
2958 | + |
2959 | + @method generateAndExposeService |
2960 | + @param {Function} callback The callback to call after the services have |
2961 | + been generated. |
2962 | + */ |
2963 | + function generateAndExposeService(callback) { |
2964 | + state.deploy('cs:wordpress', function(data) { |
2965 | + var command = { |
2966 | + op: 'expose', |
2967 | + service_name: data.service.get('name') |
2968 | + }; |
2969 | + state.nextChanges(); |
2970 | + client.onmessage = function() { |
2971 | + client.onmessage = function(rec) { |
2972 | + callback(rec); |
2973 | + }; |
2974 | + client.send(Y.JSON.stringify(command)); |
2975 | + }; |
2976 | + client.open(); |
2977 | + }, { unitCount: 1 }); |
2978 | + } |
2979 | + |
2980 | + /** |
2981 | + Same as generateAndExposeService but uses the environment integration |
2982 | + methods. Should be considered valid if "can expose a service |
2983 | + (integration)" test passes. |
2984 | + |
2985 | + @method generateAndExposeIntegrationService |
2986 | + @param {Function} callback The callback to call after the services have |
2987 | + been generated. |
2988 | + */ |
2989 | + function generateAndExposeIntegrationService(callback) { |
2990 | + env.after('defaultSeriesChange', function() { |
2991 | + var localCb = function(result) { |
2992 | + env.expose(result.service_name, function(rec) { |
2993 | + callback(rec); |
2994 | + }); |
2995 | + }; |
2996 | + env.deploy( |
2997 | + 'cs:wordpress', 'kumquat', {llama: 'pajama'}, null, 1, localCb); |
2998 | + }); |
2999 | + env.connect(); |
3000 | + } |
3001 | + |
3002 | + it('opens successfully.', function(done) { |
3003 | + var isAsync = false; |
3004 | + client.onmessage = function(message) { |
3005 | + assert.isTrue(isAsync); |
3006 | + assert.deepEqual( |
3007 | + Y.JSON.parse(message.data), |
3008 | + { |
3009 | + ready: true, |
3010 | + provider_type: 'demonstration', |
3011 | + default_series: 'precise' |
3012 | + }); |
3013 | + done(); |
3014 | + }; |
3015 | + assert.isFalse(juju.connected); |
3016 | + assert.isUndefined(juju.get('client')); |
3017 | + client.open(); |
3018 | + assert.isTrue(juju.connected); |
3019 | + assert.strictEqual(juju.get('client'), client); |
3020 | + isAsync = true; |
3021 | + }); |
3022 | + |
3023 | + it('ignores "open" when already open to same client.', function() { |
3024 | + client.receive = function() { |
3025 | + assert.ok(false, 'The receive method should not be called.'); |
3026 | + }; |
3027 | + // Whitebox test: duplicate "open" state. |
3028 | + juju.connected = true; |
3029 | + juju.set('client', client); |
3030 | + // This is effectively a re-open. |
3031 | + client.open(); |
3032 | + // The assert.ok above is the verification. |
3033 | + }); |
3034 | + |
3035 | + it('refuses to open if already open to another client.', function() { |
3036 | + // This is a simple way to make sure that we don't leave multiple |
3037 | + // setInterval calls running. If for some reason we want more |
3038 | + // simultaneous clients, that's fine, though that will require |
3039 | + // reworking the delta code generally. |
3040 | + juju.connected = true; |
3041 | + juju.set('client', {receive: function() { |
3042 | + assert.ok(false, 'The receive method should not have been called.'); |
3043 | + }}); |
3044 | + assert.throws( |
3045 | + client.open.bind(client), |
3046 | + 'INVALID_STATE_ERR : Connection is open to another client.'); |
3047 | + }); |
3048 | + |
3049 | + it('closes successfully.', function(done) { |
3050 | + client.onmessage = function() { |
3051 | + client.close(); |
3052 | + assert.isFalse(juju.connected); |
3053 | + assert.isUndefined(juju.get('client')); |
3054 | + done(); |
3055 | + }; |
3056 | + client.open(); |
3057 | + }); |
3058 | + |
3059 | + it('ignores "close" when already closed.', function() { |
3060 | + // This simply shows that we do not raise an error. |
3061 | + juju.close(); |
3062 | + }); |
3063 | + |
3064 | + it('can dispatch on received information.', function(done) { |
3065 | + var data = {op: 'testingTesting123', foo: 'bar'}; |
3066 | + juju.performOp_testingTesting123 = function(received) { |
3067 | + assert.notStrictEqual(received, data); |
3068 | + assert.deepEqual(received, data); |
3069 | + done(); |
3070 | + }; |
3071 | + client.open(); |
3072 | + client.send(Y.JSON.stringify(data)); |
3073 | + }); |
3074 | + |
3075 | + it('refuses to dispatch when closed.', function() { |
3076 | + assert.throws( |
3077 | + juju.receive.bind(juju, {}), |
3078 | + 'INVALID_STATE_ERR : Connection is closed.' |
3079 | + ); |
3080 | + }); |
3081 | + |
3082 | + it('can log in.', function(done) { |
3083 | + state.logout(); |
3084 | + // See FakeBackend's authorizedUsers for these default authentication |
3085 | + // values. |
3086 | + var data = { |
3087 | + op: 'login', |
3088 | + user: 'admin', |
3089 | + password: 'password', |
3090 | + request_id: 42 |
3091 | + }; |
3092 | + client.onmessage = function(received) { |
3093 | + // First message is the provider type and default series. We ignore |
3094 | + // it, and prepare for the next one, which will be the reply to our |
3095 | + // login. |
3096 | + client.onmessage = function(received) { |
3097 | + data.result = true; |
3098 | + assert.deepEqual(Y.JSON.parse(received.data), data); |
3099 | + assert.isTrue(state.get('authenticated')); |
3100 | + done(); |
3101 | + }; |
3102 | + client.send(Y.JSON.stringify(data)); |
3103 | + }; |
3104 | + client.open(); |
3105 | + }); |
3106 | + |
3107 | + it('can log in (environment integration).', function(done) { |
3108 | + state.logout(); |
3109 | + env.after('defaultSeriesChange', function() { |
3110 | + // See FakeBackend's authorizedUsers for these default values. |
3111 | + env.setCredentials({user: 'admin', password: 'password'}); |
3112 | + env.after('login', function() { |
3113 | + assert.isTrue(env.userIsAuthenticated); |
3114 | + done(); |
3115 | + }); |
3116 | + env.login(); |
3117 | + }); |
3118 | + env.connect(); |
3119 | + }); |
3120 | + |
3121 | + it('can deploy.', function(done) { |
3122 | + // We begin logged in. See utils.makeFakeBackendWithCharmStore. |
3123 | + var data = { |
3124 | + op: 'deploy', |
3125 | + charm_url: 'cs:wordpress', |
3126 | + service_name: 'kumquat', |
3127 | + config_raw: 'funny: business', |
3128 | + num_units: 2, |
3129 | + request_id: 42 |
3130 | + }; |
3131 | + client.onmessage = function(received) { |
3132 | + // First message is the provider type and default series. We ignore |
3133 | + // it, and prepare for the next one, which will be the reply to our |
3134 | + // deployment. |
3135 | + client.onmessage = function(received) { |
3136 | + var parsed = Y.JSON.parse(received.data); |
3137 | + assert.isUndefined(parsed.err); |
3138 | + assert.deepEqual(parsed, data); |
3139 | + assert.isObject( |
3140 | + state.db.charms.getById('cs:precise/wordpress-10')); |
3141 | + var service = state.db.services.getById('kumquat'); |
3142 | + assert.isObject(service); |
3143 | + assert.equal(service.get('charm'), 'cs:precise/wordpress-10'); |
3144 | + assert.deepEqual(service.get('config'), {funny: 'business'}); |
3145 | + var units = state.db.units.get_units_for_service(service); |
3146 | + assert.lengthOf(units, 2); |
3147 | + done(); |
3148 | + }; |
3149 | + client.send(Y.JSON.stringify(data)); |
3150 | + }; |
3151 | + client.open(); |
3152 | + }); |
3153 | + |
3154 | + it('can deploy (environment integration).', function(done) { |
3155 | + // We begin logged in. See utils.makeFakeBackendWithCharmStore. |
3156 | + env.after('defaultSeriesChange', function() { |
3157 | + var callback = function(result) { |
3158 | + assert.isUndefined(result.err); |
3159 | + assert.equal(result.charm_url, 'cs:wordpress'); |
3160 | + var service = state.db.services.getById('kumquat'); |
3161 | + assert.equal(service.get('charm'), 'cs:precise/wordpress-10'); |
3162 | + assert.deepEqual(service.get('config'), {llama: 'pajama'}); |
3163 | + done(); |
3164 | + }; |
3165 | + env.deploy( |
3166 | + 'cs:wordpress', 'kumquat', {llama: 'pajama'}, null, 1, callback); |
3167 | + }); |
3168 | + env.connect(); |
3169 | + }); |
3170 | + |
3171 | + it('can communicate errors after attempting to deploy', function(done) { |
3172 | + // Create a service with the name "wordpress". |
3173 | + // The charm store is synchronous in tests, so we don't need a real |
3174 | + // callback. |
3175 | + state.deploy('cs:wordpress', function() {}); |
3176 | + env.after('defaultSeriesChange', function() { |
3177 | + var callback = function(result) { |
3178 | + assert.equal( |
3179 | + result.err, 'A service with this name already exists.'); |
3180 | + done(); |
3181 | + }; |
3182 | + env.deploy( |
3183 | + 'cs:wordpress', undefined, undefined, undefined, 1, callback); |
3184 | + }); |
3185 | + env.connect(); |
3186 | + }); |
3187 | + |
3188 | + it('can send a delta stream of changes.', function(done) { |
3189 | + // Create a service with the name "wordpress". |
3190 | + // The charm store is synchronous in tests, so we don't need a real |
3191 | + // callback. |
3192 | + state.deploy('cs:wordpress', function() {}); |
3193 | + client.onmessage = function(received) { |
3194 | + // First message is the provider type and default series. We ignore |
3195 | + // it, and prepare for the next one, which will handle the delta |
3196 | + // stream. |
3197 | + client.onmessage = function(received) { |
3198 | + var parsed = Y.JSON.parse(received.data); |
3199 | + assert.equal(parsed.op, 'delta'); |
3200 | + var deltas = parsed.result; |
3201 | + assert.lengthOf(deltas, 3); |
3202 | + assert.equal(deltas[0][0], 'service'); |
3203 | + assert.equal(deltas[0][1], 'change'); |
3204 | + assert.equal(deltas[0][2].charm, 'cs:precise/wordpress-10'); |
3205 | + assert.equal(deltas[1][0], 'machine'); |
3206 | + assert.equal(deltas[1][1], 'change'); |
3207 | + assert.equal(deltas[2][0], 'unit'); |
3208 | + assert.equal(deltas[2][1], 'change'); |
3209 | + done(); |
3210 | + }; |
3211 | + juju.sendDelta(); |
3212 | + }; |
3213 | + client.open(); |
3214 | + }); |
3215 | + |
3216 | + it('does not send a delta if there are no changes.', function(done) { |
3217 | + client.onmessage = function(received) { |
3218 | + // First message is the provider type and default series. We ignore |
3219 | + // it, and prepare for the next one, which will handle the delta |
3220 | + // stream. |
3221 | + client.receiveNow = function(response) { |
3222 | + assert.ok(false, 'This method should not have been called.'); |
3223 | + }; |
3224 | + juju.sendDelta(); |
3225 | + done(); |
3226 | + }; |
3227 | + client.open(); |
3228 | + }); |
3229 | + |
3230 | + it('can send a delta stream (integration).', function(done) { |
3231 | + // Create a service with the name "wordpress". |
3232 | + // The charm store is synchronous in tests, so we don't need a real |
3233 | + // callback. |
3234 | + state.deploy('cs:wordpress', function() {}, {unitCount: 2}); |
3235 | + var db = new Y.juju.models.Database(); |
3236 | + db.on('update', function() { |
3237 | + // We want to verify that the GUI database is equivalent to the state |
3238 | + // database. |
3239 | + assert.equal(db.services.size(), 1); |
3240 | + assert.equal(db.units.size(), 2); |
3241 | + assert.equal(db.machines.size(), 2); |
3242 | + var stateService = state.db.services.item(0); |
3243 | + var guiService = db.services.item(0); |
3244 | + Y.each( |
3245 | + ['charm', 'config', 'constraints', 'exposed', |
3246 | + 'id', 'name', 'subordinate'], |
3247 | + function(attrName) { |
3248 | + assert.deepEqual( |
3249 | + guiService.get(attrName), stateService.get(attrName)); |
3250 | + } |
3251 | + ); |
3252 | + state.db.units.each(function(stateUnit) { |
3253 | + var guiUnit = db.units.getById(stateUnit.id); |
3254 | + Y.each( |
3255 | + ['agent_state', 'machine', 'number', 'service'], |
3256 | + function(attrName) { |
3257 | + assert.deepEqual(guiUnit[attrName], stateUnit[attrName]); |
3258 | + } |
3259 | + ); |
3260 | + }); |
3261 | + state.db.machines.each(function(stateMachine) { |
3262 | + var guiMachine = db.machines.getById(stateMachine.id); |
3263 | + Y.each( |
3264 | + ['agent_state', 'public_address', 'machine_id'], |
3265 | + function(attrName) { |
3266 | + assert.deepEqual(guiMachine[attrName], stateMachine[attrName]); |
3267 | + } |
3268 | + ); |
3269 | + }); |
3270 | + done(); |
3271 | + }); |
3272 | + env.on('delta', db.onDelta, db); |
3273 | + env.after('defaultSeriesChange', function() {juju.sendDelta();}); |
3274 | + env.connect(); |
3275 | + }); |
3276 | + |
3277 | + it('sends delta streams periodically after opening.', function(done) { |
3278 | + client.onmessage = function(received) { |
3279 | + // First message is the provider type and default series. We ignore |
3280 | + // it, and prepare for the next one, which will handle the delta |
3281 | + // stream. |
3282 | + var isAsync = false; |
3283 | + client.onmessage = function(received) { |
3284 | + assert.isTrue(isAsync); |
3285 | + var parsed = Y.JSON.parse(received.data); |
3286 | + assert.equal(parsed.op, 'delta'); |
3287 | + var deltas = parsed.result; |
3288 | + assert.lengthOf(deltas, 3); |
3289 | + assert.equal(deltas[0][2].charm, 'cs:precise/wordpress-10'); |
3290 | + done(); |
3291 | + }; |
3292 | + // Create a service with the name "wordpress". |
3293 | + // The charm store is synchronous in tests, so we don't need a real |
3294 | + // callback. |
3295 | + state.deploy('cs:wordpress', function() {}); |
3296 | + isAsync = true; |
3297 | + }; |
3298 | + juju.set('deltaInterval', 4); |
3299 | + client.open(); |
3300 | + }); |
3301 | + |
3302 | + it('stops sending delta streams after closing.', function(done) { |
3303 | + var sysSetInterval = window.setInterval; |
3304 | + var sysClearInterval = window.clearInterval; |
3305 | + cleanups.push(function() { |
3306 | + window.setInterval = sysSetInterval; |
3307 | + window.clearInterval = sysClearInterval; |
3308 | + }); |
3309 | + window.setInterval = function(f, interval) { |
3310 | + assert.isFunction(f); |
3311 | + assert.equal(interval, 4); |
3312 | + return 42; |
3313 | + }; |
3314 | + window.clearInterval = function(token) { |
3315 | + assert.equal(token, 42); |
3316 | + done(); |
3317 | + }; |
3318 | + client.onmessage = function(received) { |
3319 | + // First message is the provider type and default series. We can |
3320 | + // close now. |
3321 | + client.close(); |
3322 | + }; |
3323 | + juju.set('deltaInterval', 4); |
3324 | + client.open(); |
3325 | + }); |
3326 | + |
3327 | + it('can add additional units', function(done) { |
3328 | + function testForAddedUnits(received) { |
3329 | + var service = state.db.services.getById('wordpress'), |
3330 | + units = state.db.units.get_units_for_service(service), |
3331 | + data = Y.JSON.parse(received.data), |
3332 | + mock = { |
3333 | + num_units: 2, |
3334 | + service_name: 'wordpress', |
3335 | + op: 'add_unit', |
3336 | + result: ['wordpress/1', 'wordpress/2'] |
3337 | + }; |
3338 | + // Do we have enough total units? |
3339 | + assert.lengthOf(units, 3); |
3340 | + // Does the response object contain the proper data |
3341 | + assert.deepEqual(data, mock); |
3342 | + // Error is undefined |
3343 | + assert.isUndefined(data.err); |
3344 | + done(); |
3345 | + } |
3346 | + // Generate the default services and add units |
3347 | + generateServices(testForAddedUnits); |
3348 | + }); |
3349 | + |
3350 | + it('throws an error when adding units to an invalid service', |
3351 | + function(done) { |
3352 | + state.deploy('cs:wordpress', function(service) { |
3353 | + var data = { |
3354 | + op: 'add_unit', |
3355 | + service_name: 'noservice', |
3356 | + num_units: 2 |
3357 | + }; |
3358 | + //Clear out the delta stream |
3359 | + state.nextChanges(); |
3360 | + client.onmessage = function() { |
3361 | + client.onmessage = function(received) { |
3362 | + var data = Y.JSON.parse(received.data); |
3363 | + |
3364 | + // If there is no error data.err will be undefined |
3365 | + assert.equal(true, !!data.err); |
3366 | + done(); |
3367 | + }; |
3368 | + client.send(Y.JSON.stringify(data)); |
3369 | + }; |
3370 | + client.open(); |
3371 | + }); |
3372 | + } |
3373 | + ); |
3374 | + |
3375 | + it('can add additional units (integration)', function(done) { |
3376 | + function testForAddedUnits(data) { |
3377 | + var service = state.db.services.getById('kumquat'), |
3378 | + units = state.db.units.get_units_for_service(service); |
3379 | + assert.lengthOf(units, 3); |
3380 | + done(); |
3381 | + } |
3382 | + generateIntegrationServices(testForAddedUnits); |
3383 | + }); |
3384 | + |
3385 | + it('can remove units', function(done) { |
3386 | + function removeUnits() { |
3387 | + var data = { |
3388 | + op: 'remove_units', |
3389 | + unit_names: ['wordpress/0', 'wordpress/1'] |
3390 | + }; |
3391 | + client.onmessage = function(rec) { |
3392 | + var data = Y.JSON.parse(rec.data), |
3393 | + mock = { |
3394 | + op: 'remove_units', |
3395 | + result: true, |
3396 | + unit_names: ['wordpress/0', 'wordpress/1'] |
3397 | + }; |
3398 | + // No errors |
3399 | + assert.equal(data.result, true); |
3400 | + // Returned data object contains all information |
3401 | + assert.deepEqual(data, mock); |
3402 | + done(); |
3403 | + }; |
3404 | + client.send(Y.JSON.stringify(data)); |
3405 | + } |
3406 | + // Generate the services base data and then execute the test |
3407 | + generateServices(removeUnits); |
3408 | + }); |
3409 | + |
3410 | + it('can remove units (integration)', function(done) { |
3411 | + function removeUnits() { |
3412 | + var unitNames = ['kumquat/1', 'kumquat/2']; |
3413 | + env.remove_units(unitNames, function(data) { |
3414 | + assert.equal(data.result, true); |
3415 | + assert.deepEqual(data.unit_names, unitNames); |
3416 | + done(); |
3417 | + }); |
3418 | + } |
3419 | + // Generate the services via the integration method then execute the test |
3420 | + generateIntegrationServices(removeUnits); |
3421 | + }); |
3422 | + |
3423 | + it('allows attempting to remove units from an invalid service', |
3424 | + function(done) { |
3425 | + function removeUnit() { |
3426 | + var data = { |
3427 | + op: 'remove_units', |
3428 | + unit_names: ['bar/2'] |
3429 | + }; |
3430 | + client.onmessage = function(rec) { |
3431 | + var data = Y.JSON.parse(rec.data); |
3432 | + assert.equal(data.result, true); |
3433 | + done(); |
3434 | + }; |
3435 | + client.send(Y.JSON.stringify(data)); |
3436 | + } |
3437 | + // Generate the services base data then execute the test. |
3438 | + generateServices(removeUnit); |
3439 | + } |
3440 | + ); |
3441 | + |
3442 | + it('throws an error if unit is a subordinate', function(done) { |
3443 | + function removeUnits() { |
3444 | + var data = { |
3445 | + op: 'remove_units', |
3446 | + unit_names: ['wordpress/1'] |
3447 | + }; |
3448 | + client.onmessage = function(rec) { |
3449 | + var data = Y.JSON.parse(rec.data); |
3450 | + assert.equal(Y.Lang.isArray(data.err), true); |
3451 | + assert.equal(data.err.length, 1); |
3452 | + done(); |
3453 | + }; |
3454 | + state.db.services.getById('wordpress').set('is_subordinate', true); |
3455 | + client.send(Y.JSON.stringify(data)); |
3456 | + } |
3457 | + // Generate the services base data then execute the test. |
3458 | + generateServices(removeUnits); |
3459 | + }); |
3460 | + |
3461 | + it('can get a service', function(done) { |
3462 | + generateServices(function(data) { |
3463 | + // Post deploy of wordpress so we should be able to |
3464 | + // pull its data. |
3465 | + var op = { |
3466 | + op: 'get_service', |
3467 | + service_name: 'wordpress', |
3468 | + request_id: 99 |
3469 | + }; |
3470 | + client.onmessage = function(received) { |
3471 | + var parsed = Y.JSON.parse(received.data); |
3472 | + var service = parsed.result; |
3473 | + assert.equal(service.name, 'wordpress'); |
3474 | + // Error should be undefined. |
3475 | + done(received.error); |
3476 | + }; |
3477 | + client.send(Y.JSON.stringify(op)); |
3478 | + }); |
3479 | + }); |
3480 | + |
3481 | + it('can destroy a service', function(done) { |
3482 | + generateServices(function(data) { |
3483 | + // Post deploy of wordpress so we should be able to |
3484 | + // destroy it. |
3485 | + var op = { |
3486 | + op: 'destroy_service', |
3487 | + service_name: 'wordpress', |
3488 | + request_id: 99 |
3489 | + }; |
3490 | + client.onmessage = function(received) { |
3491 | + var parsed = Y.JSON.parse(received.data); |
3492 | + assert.equal(parsed.result, 'wordpress'); |
3493 | + // Error should be undefined. |
3494 | + done(received.error); |
3495 | + }; |
3496 | + client.send(Y.JSON.stringify(op)); |
3497 | + }); |
3498 | + }); |
3499 | + |
3500 | + it('can destroy a service (integration)', function(done) { |
3501 | + function destroyService(rec) { |
3502 | + function localCb(rec2) { |
3503 | + assert.equal(rec2.result, 'kumquat'); |
3504 | + var service = state.db.services.getById('kumquat'); |
3505 | + assert.isNull(service); |
3506 | + done(); |
3507 | + } |
3508 | + var result = env.destroy_service(rec.service_name, localCb); |
3509 | + } |
3510 | + generateAndExposeIntegrationService(destroyService); |
3511 | + }); |
3512 | + |
3513 | + it('can get a charm', function(done) { |
3514 | + generateServices(function(data) { |
3515 | + // Post deploy of wordpress we should be able to |
3516 | + // pull its data. |
3517 | + var op = { |
3518 | + op: 'get_charm', |
3519 | + charm_url: 'cs:wordpress', |
3520 | + request_id: 99 |
3521 | + }; |
3522 | + client.onmessage = function(received) { |
3523 | + var parsed = Y.JSON.parse(received.data); |
3524 | + var charm = parsed.result; |
3525 | + assert.equal(charm.name, 'wordpress'); |
3526 | + // Error should be undefined. |
3527 | + done(received.error); |
3528 | + }; |
3529 | + client.send(Y.JSON.stringify(op)); |
3530 | + }); |
3531 | + }); |
3532 | + |
3533 | + it('can set service config', function(done) { |
3534 | + generateServices(function(data) { |
3535 | + // Post deploy of wordpress we should be able to |
3536 | + // pull its data. |
3537 | + var op = { |
3538 | + op: 'set_config', |
3539 | + service_name: 'wordpress', |
3540 | + config: {'blog-title': 'Inimical'}, |
3541 | + request_id: 99 |
3542 | + }; |
3543 | + client.onmessage = function(received) { |
3544 | + var parsed = Y.JSON.parse(received.data); |
3545 | + assert.deepEqual(parsed.result, {'blog-title': 'Inimical'}); |
3546 | + var service = state.db.services.getById('wordpress'); |
3547 | + assert.equal(service.get('config')['blog-title'], 'Inimical'); |
3548 | + // Error should be undefined. |
3549 | + done(parsed.error); |
3550 | + }; |
3551 | + client.send(Y.JSON.stringify(op)); |
3552 | + }); |
3553 | + }); |
3554 | + |
3555 | + it('can set service constraints', function(done) { |
3556 | + generateServices(function(data) { |
3557 | + // Post deploy of wordpress we should be able to |
3558 | + // pull its data. |
3559 | + var op = { |
3560 | + op: 'set_constraints', |
3561 | + service_name: 'wordpress', |
3562 | + constraints: ['cpu=2', 'mem=128'], |
3563 | + request_id: 99 |
3564 | + }; |
3565 | + client.onmessage = function(received) { |
3566 | + var service = state.db.services.getById('wordpress'); |
3567 | + var constraints = service.get('constraints'); |
3568 | + assert.equal(constraints.cpu, '2'); |
3569 | + assert.equal(constraints.mem, '128'); |
3570 | + // Error should be undefined. |
3571 | + done(received.error); |
3572 | + }; |
3573 | + client.send(Y.JSON.stringify(op)); |
3574 | + }); |
3575 | + }); |
3576 | + |
3577 | + it('can expose a service', function(done) { |
3578 | + function checkExposedService(rec) { |
3579 | + var data = Y.JSON.parse(rec.data), |
3580 | + mock = { |
3581 | + op: 'expose', |
3582 | + result: true, |
3583 | + service_name: 'wordpress' |
3584 | + }; |
3585 | + var service = state.db.services.getById(mock.service_name); |
3586 | + assert.equal(service.get('exposed'), true); |
3587 | + assert.equal(data.result, true); |
3588 | + assert.deepEqual(data, mock); |
3589 | + done(); |
3590 | + } |
3591 | + generateAndExposeService(checkExposedService); |
3592 | + }); |
3593 | + |
3594 | + it('can expose a service (integration)', function(done) { |
3595 | + function checkExposedService(rec) { |
3596 | + var service = state.db.services.getById('kumquat'); |
3597 | + assert.equal(service.get('exposed'), true); |
3598 | + assert.equal(rec.result, true); |
3599 | + done(); |
3600 | + } |
3601 | + generateAndExposeIntegrationService(checkExposedService); |
3602 | + }); |
3603 | + |
3604 | + it('fails silently when exposing an exposed service', function(done) { |
3605 | + function checkExposedService(rec) { |
3606 | + var data = Y.JSON.parse(rec.data), |
3607 | + service = state.db.services.getById(data.service_name), |
3608 | + command = { |
3609 | + op: 'expose', |
3610 | + service_name: data.service_name |
3611 | + }; |
3612 | + state.nextChanges(); |
3613 | + client.onmessage = function(rec) { |
3614 | + assert.equal(data.err, undefined); |
3615 | + assert.equal(service.get('exposed'), true); |
3616 | + assert.equal(data.result, true); |
3617 | + done(); |
3618 | + }; |
3619 | + client.send(Y.JSON.stringify(command)); |
3620 | + } |
3621 | + generateAndExposeService(checkExposedService); |
3622 | + }); |
3623 | + |
3624 | + it('fails with error when exposing an invalid service name', |
3625 | + function(done) { |
3626 | + state.deploy('cs:wordpress', function(data) { |
3627 | + var command = { |
3628 | + op: 'expose', |
3629 | + service_name: 'foobar' |
3630 | + }; |
3631 | + state.nextChanges(); |
3632 | + client.onmessage = function() { |
3633 | + client.onmessage = function(rec) { |
3634 | + var data = Y.JSON.parse(rec.data); |
3635 | + assert.equal(data.result, false); |
3636 | + assert.equal(data.err, |
3637 | + '"foobar" is an invalid service name.'); |
3638 | + done(); |
3639 | + }; |
3640 | + client.send(Y.JSON.stringify(command)); |
3641 | + }; |
3642 | + client.open(); |
3643 | + }, { unitCount: 1 }); |
3644 | + } |
3645 | + ); |
3646 | + |
3647 | + it('can unexpose a service', function(done) { |
3648 | + function unexposeService(rec) { |
3649 | + var data = Y.JSON.parse(rec.data), |
3650 | + command = { |
3651 | + op: 'unexpose', |
3652 | + service_name: data.service_name |
3653 | + }; |
3654 | + state.nextChanges(); |
3655 | + client.onmessage = function(rec) { |
3656 | + var data = Y.JSON.parse(rec.data), |
3657 | + service = state.db.services.getById(data.service_name), |
3658 | + mock = { |
3659 | + op: 'unexpose', |
3660 | + result: true, |
3661 | + service_name: 'wordpress' |
3662 | + }; |
3663 | + assert.equal(service.get('exposed'), false); |
3664 | + assert.deepEqual(data, mock); |
3665 | + done(); |
3666 | + }; |
3667 | + client.send(Y.JSON.stringify(command)); |
3668 | + } |
3669 | + generateAndExposeService(unexposeService); |
3670 | + }); |
3671 | + |
3672 | + it('can unexpose a service (integration)', function(done) { |
3673 | + function unexposeService(rec) { |
3674 | + function localCb(rec) { |
3675 | + var service = state.db.services.getById('kumquat'); |
3676 | + assert.equal(service.get('exposed'), false); |
3677 | + assert.equal(rec.result, true); |
3678 | + done(); |
3679 | + } |
3680 | + env.unexpose(rec.service_name, localCb); |
3681 | + } |
3682 | + generateAndExposeIntegrationService(unexposeService); |
3683 | + }); |
3684 | + |
3685 | + it('fails silently when unexposing a not exposed service', |
3686 | + function(done) { |
3687 | + state.deploy('cs:wordpress', function(data) { |
3688 | + var command = { |
3689 | + op: 'unexpose', |
3690 | + service_name: data.service.get('name') |
3691 | + }; |
3692 | + state.nextChanges(); |
3693 | + client.onmessage = function() { |
3694 | + client.onmessage = function(rec) { |
3695 | + var data = Y.JSON.parse(rec.data), |
3696 | + service = state.db.services.getById(data.service_name); |
3697 | + assert.equal(service.get('exposed'), false); |
3698 | + assert.equal(data.result, true); |
3699 | + assert.equal(data.err, undefined); |
3700 | + done(); |
3701 | + }; |
3702 | + client.send(Y.JSON.stringify(command)); |
3703 | + }; |
3704 | + client.open(); |
3705 | + }, { unitCount: 1 }); |
3706 | + } |
3707 | + ); |
3708 | + |
3709 | + it('fails with error when unexposing an invalid service name', |
3710 | + function(done) { |
3711 | + function unexposeService(rec) { |
3712 | + var data = Y.JSON.parse(rec.data), |
3713 | + command = { |
3714 | + op: 'unexpose', |
3715 | + service_name: 'foobar' |
3716 | + }; |
3717 | + state.nextChanges(); |
3718 | + client.onmessage = function(rec) { |
3719 | + var data = Y.JSON.parse(rec.data); |
3720 | + assert.equal(data.result, false); |
3721 | + assert.equal(data.err, '"foobar" is an invalid service name.'); |
3722 | + done(); |
3723 | + }; |
3724 | + client.send(Y.JSON.stringify(command)); |
3725 | + } |
3726 | + generateAndExposeService(unexposeService); |
3727 | + } |
3728 | + ); |
3729 | + |
3730 | + it('can add a relation', function(done) { |
3731 | + function localCb() { |
3732 | + state.deploy('cs:mysql', function(service) { |
3733 | + var data = { |
3734 | + op: 'add_relation', |
3735 | + endpoint_a: 'wordpress:db', |
3736 | + endpoint_b: 'mysql:db' |
3737 | + }; |
3738 | + client.onmessage = function(rec) { |
3739 | + var data = Y.JSON.parse(rec.data), |
3740 | + mock = { |
3741 | + endpoint_a: 'wordpress:db', |
3742 | + endpoint_b: 'mysql:db', |
3743 | + op: 'add_relation', |
3744 | + result: { |
3745 | + id: 'relation-0', |
3746 | + 'interface': 'mysql', |
3747 | + scope: 'global', |
3748 | + endpoints: [ |
3749 | + {wordpress: {name: 'db'}}, |
3750 | + {mysql: {name: 'db'}} |
3751 | + ] |
3752 | + } |
3753 | + }; |
3754 | + |
3755 | + assert.equal(data.err, undefined); |
3756 | + assert.equal(typeof data.result, 'object'); |
3757 | + assert.deepEqual(data, mock); |
3758 | + done(); |
3759 | + }; |
3760 | + client.send(Y.JSON.stringify(data)); |
3761 | + }); |
3762 | + } |
3763 | + generateServices(localCb); |
3764 | + }); |
3765 | + |
3766 | + it('can add a relation (integration)', function(done) { |
3767 | + function addRelation() { |
3768 | + function localCb(rec) { |
3769 | + var mock = { |
3770 | + endpoint_a: 'kumquat:db', |
3771 | + endpoint_b: 'mysql:db', |
3772 | + op: 'add_relation', |
3773 | + request_id: rec.request_id, |
3774 | + result: { |
3775 | + id: 'relation-0', |
3776 | + 'interface': 'mysql', |
3777 | + scope: 'global', |
3778 | + request_id: rec.request_id, |
3779 | + endpoints: [ |
3780 | + {kumquat: {name: 'db'}}, |
3781 | + {mysql: {name: 'db'}} |
3782 | + ] |
3783 | + } |
3784 | + }; |
3785 | + |
3786 | + assert.equal(rec.err, undefined); |
3787 | + assert.equal(typeof rec.result, 'object'); |
3788 | + assert.deepEqual(rec.details[0], mock); |
3789 | + done(); |
3790 | + } |
3791 | + var endpointA = [ |
3792 | + 'kumquat', |
3793 | + { name: 'db', |
3794 | + role: 'client' } |
3795 | + ]; |
3796 | + var endpointB = [ |
3797 | + 'mysql', |
3798 | + { name: 'db', |
3799 | + role: 'server' } |
3800 | + ]; |
3801 | + env.add_relation(endpointA, endpointB, localCb); |
3802 | + } |
3803 | + generateIntegrationServices(function() { |
3804 | + env.deploy('cs:mysql', undefined, undefined, undefined, 1, addRelation); |
3805 | + }); |
3806 | + }); |
3807 | + |
3808 | + it('is able to add a relation with a subordinate service', function(done) { |
3809 | + function localCb() { |
3810 | + state.deploy('cs:puppet', function(service) { |
3811 | + var data = { |
3812 | + op: 'add_relation', |
3813 | + endpoint_a: 'wordpress:juju-info', |
3814 | + endpoint_b: 'puppet:juju-info' |
3815 | + }; |
3816 | + |
3817 | + client.onmessage = function(rec) { |
3818 | + var data = Y.JSON.parse(rec.data), |
3819 | + mock = { |
3820 | + endpoint_a: 'wordpress:juju-info', |
3821 | + endpoint_b: 'puppet:juju-info', |
3822 | + op: 'add_relation', |
3823 | + result: { |
3824 | + id: 'relation-0', |
3825 | + 'interface': 'juju-info', |
3826 | + scope: 'container', |
3827 | + endpoints: [ |
3828 | + {puppet: {name: 'juju-info'}}, |
3829 | + {wordpress: {name: 'juju-info'}} |
3830 | + ] |
3831 | + } |
3832 | + }; |
3833 | + assert.equal(data.err, undefined); |
3834 | + assert.equal(typeof data.result, 'object'); |
3835 | + assert.deepEqual(data, mock); |
3836 | + done(); |
3837 | + }; |
3838 | + client.send(Y.JSON.stringify(data)); |
3839 | + }); |
3840 | + } |
3841 | + generateServices(localCb); |
3842 | + }); |
3843 | + |
3844 | + it('throws an error if only one endpoint is supplied', function(done) { |
3845 | + function localCb() { |
3846 | + var data = { |
3847 | + op: 'add_relation', |
3848 | + endpoint_a: 'wordpress:db' |
3849 | + }; |
3850 | + state.nextChanges(); |
3851 | + client.onmessage = function(rec) { |
3852 | + var data = Y.JSON.parse(rec.data); |
3853 | + assert(data.err, 'Two endpoints required to set up relation.'); |
3854 | + done(); |
3855 | + }; |
3856 | + client.send(Y.JSON.stringify(data)); |
3857 | + } |
3858 | + generateServices(localCb); |
3859 | + }); |
3860 | + |
3861 | + it('throws an error if endpoints are not relatable', function(done) { |
3862 | + function localCb() { |
3863 | + var data = { |
3864 | + op: 'add_relation', |
3865 | + endpoint_a: 'wordpress:db', |
3866 | + endpoint_b: 'mysql:foo' |
3867 | + }; |
3868 | + state.nextChanges(); |
3869 | + client.onmessage = function(rec) { |
3870 | + var data = Y.JSON.parse(rec.data); |
3871 | + assert(data.err, 'No matching interfaces.'); |
3872 | + done(); |
3873 | + }; |
3874 | + client.send(Y.JSON.stringify(data)); |
3875 | + } |
3876 | + generateServices(localCb); |
3877 | + }); |
3878 | + |
3879 | + it('can remove a relation', function(done) { |
3880 | + generateAndRelateServices( |
3881 | + ['cs:wordpress', 'cs:mysql'], |
3882 | + ['wordpress:db', 'mysql:db'], |
3883 | + ['wordpress:db', 'mysql:db'], |
3884 | + {result: true, endpoint_a: 'wordpress:db', endpoint_b: 'mysql:db'}, |
3885 | + done); |
3886 | + }); |
3887 | + |
3888 | + it('can remove a relation (integration)', function(done) { |
3889 | + var endpoints = [ |
3890 | + ['kumquat', |
3891 | + { name: 'db', |
3892 | + role: 'client' }], |
3893 | + ['mysql', |
3894 | + { name: 'db', |
3895 | + role: 'server' }] |
3896 | + ]; |
3897 | + env.after('defaultSeriesChange', function() { |
3898 | + function localCb(result) { |
3899 | + var mock = { |
3900 | + endpoint_a: 'kumquat:db', |
3901 | + endpoint_b: 'mysql:db', |
3902 | + op: 'remove_relation', |
3903 | + request_id: 4, |
3904 | + result: true |
3905 | + }; |
3906 | + assert.deepEqual(result.details[0], mock); |
3907 | + done(); |
3908 | + } |
3909 | + env.deploy( |
3910 | + 'cs:wordpress', 'kumquat', {llama: 'pajama'}, null, 1, function() { |
3911 | + env.deploy('cs:mysql', null, null, null, 1, function() { |
3912 | + env.add_relation(endpoints[0], endpoints[1], function() { |
3913 | + env.remove_relation(endpoints[0], endpoints[1], localCb); |
3914 | + }); |
3915 | + }); |
3916 | + } |
3917 | + ); |
3918 | + }); |
3919 | + env.connect(); |
3920 | + }); |
3921 | + |
3922 | + it('throws an error if the charms do not exist', function(done) { |
3923 | + generateAndRelateServices( |
3924 | + ['cs:wordpress', 'cs:mysql'], |
3925 | + ['wordpress:db', 'mysql:db'], |
3926 | + ['no_such', 'charms'], |
3927 | + {err: 'Charm not loaded.', |
3928 | + endpoint_a: 'wordpress:db', endpoint_b: 'mysql:db'}, |
3929 | + done); |
3930 | + }); |
3931 | + |
3932 | + it('throws an error if the relationship does not exist', function(done) { |
3933 | + generateAndRelateServices( |
3934 | + ['cs:wordpress', 'cs:mysql'], |
3935 | + null, |
3936 | + ['wordpress:db', 'mysql:db'], |
3937 | + {err: 'Relationship does not exist', |
3938 | + endpoint_a: 'wordpress:db', endpoint_b: 'mysql:db'}, |
3939 | + done); |
3940 | + }); |
3941 | + |
3942 | + describe('Sandbox Annotations', function() { |
3943 | + |
3944 | + it('should handle service annotation updates', function(done) { |
3945 | + generateServices(function(data) { |
3946 | + // Post deploy of wordpress we should be able to |
3947 | + // pull its data. |
3948 | + var op = { |
3949 | + op: 'update_annotations', |
3950 | + entity: 'wordpress', |
3951 | + data: {'foo': 'bar'}, |
3952 | + request_id: 99 |
3953 | + }; |
3954 | + client.onmessage = function(received) { |
3955 | + var service = state.db.services.getById('wordpress'); |
3956 | + var annotations = service.get('annotations'); |
3957 | + assert.equal(annotations.foo, 'bar'); |
3958 | + // Validate that annotations appear in the delta stream. |
3959 | + client.onmessage = function(delta) { |
3960 | + delta = Y.JSON.parse(delta.data); |
3961 | + assert.equal(delta.op, 'delta'); |
3962 | + var serviceChange = Y.Array.find(delta.result, function(change) { |
3963 | + return change[0] === 'service'; |
3964 | + }); |
3965 | + assert.equal(serviceChange[0], 'service'); |
3966 | + assert.equal(serviceChange[1], 'change'); |
3967 | + assert.deepEqual(serviceChange[2].annotations, {'foo': 'bar'}); |
3968 | + // Error should be undefined. |
3969 | + done(received.error); |
3970 | + }; |
3971 | + juju.sendDelta(); |
3972 | + }; |
3973 | + client.open(); |
3974 | + client.send(Y.JSON.stringify(op)); |
3975 | + }); |
3976 | + }); |
3977 | + |
3978 | + it('should handle environment annotation updates', function(done) { |
3979 | + generateServices(function(data) { |
3980 | + // We only deploy a service here to reuse the env connect/setup |
3981 | + // code. |
3982 | + // Post deploy of wordpress we should be able to |
3983 | + // pull env data. |
3984 | + client.onmessage = function(received) { |
3985 | + var env = state.db.environment; |
3986 | + var annotations = env.get('annotations'); |
3987 | + assert.equal(annotations.foo, 'bar'); |
3988 | + // Validate that annotations appear in the delta stream. |
3989 | + client.onmessage = function(delta) { |
3990 | + delta = Y.JSON.parse(delta.data); |
3991 | + assert.equal(delta.op, 'delta'); |
3992 | + var envChange = Y.Array.find(delta.result, function(change) { |
3993 | + return change[0] === 'annotations'; |
3994 | + }); |
3995 | + assert.equal(envChange[1], 'change'); |
3996 | + assert.deepEqual(envChange[2], {'foo': 'bar'}); |
3997 | + done(); |
3998 | + }; |
3999 | + juju.sendDelta(); |
4000 | + }; |
4001 | + client.open(); |
4002 | + client.send(Y.JSON.stringify({ |
4003 | + op: 'update_annotations', |
4004 | + entity: 'env', |
4005 | + data: {'foo': 'bar'}, |
4006 | + request_id: 99 |
4007 | + })); |
4008 | + }); |
4009 | + }); |
4010 | + |
4011 | + it('should handle unit annotation updates', function(done) { |
4012 | + generateServices(function(data) { |
4013 | + // Post deploy of wordpress we should be able to |
4014 | + // pull its data. |
4015 | + var op = { |
4016 | + op: 'update_annotations', |
4017 | + entity: 'wordpress/0', |
4018 | + data: {'foo': 'bar'}, |
4019 | + request_id: 99 |
4020 | + }; |
4021 | + client.onmessage = function(received) { |
4022 | + var unit = state.db.units.getById('wordpress/0'); |
4023 | + var annotations = unit.annotations; |
4024 | + assert.equal(annotations.foo, 'bar'); |
4025 | + // Error should be undefined. |
4026 | + done(received.error); |
4027 | + }; |
4028 | + client.open(); |
4029 | + client.send(Y.JSON.stringify(op)); |
4030 | + }); |
4031 | + }); |
4032 | + |
4033 | + }); |
4034 | + |
4035 | + it('should allow unit resolved to be called', function(done) { |
4036 | + generateServices(function(data) { |
4037 | + // Post deploy of wordpress we should be able to |
4038 | + // pull its data. |
4039 | + var op = { |
4040 | + op: 'resolved', |
4041 | + unit_name: 'wordpress/0', |
4042 | + request_id: 99 |
4043 | + }; |
4044 | + client.onmessage = function(received) { |
4045 | + var parsed = Y.JSON.parse(received.data); |
4046 | + assert.equal(parsed.result, true); |
4047 | + done(parsed.error); |
4048 | + }; |
4049 | + client.open(); |
4050 | + client.send(Y.JSON.stringify(op)); |
4051 | + }); |
4052 | + }); |
4053 | + |
4054 | + /** |
4055 | + * Utility method to turn _some_ callback |
4056 | + * styled async methods into Promises. |
4057 | + * It does this by supplying a simple |
4058 | + * adaptor that can handle {error:...} |
4059 | + * and {result: ... } returns. |
4060 | + * |
4061 | + * This callback is appended to any calling arguments |
4062 | + * |
4063 | + * @method promise |
4064 | + * @param {Object} context Calling context. |
4065 | + * @param {String} methodName name of method on context to invoke. |
4066 | + * @param {Arguments} arguments Additional arguments passed |
4067 | + * to resolved method. |
4068 | + * @return {Promise} a Y.Promise object. |
4069 | + */ |
4070 | + function promise(context, methodName) { |
4071 | + var slice = Array.prototype.slice; |
4072 | + var args = slice.call(arguments, 2); |
4073 | + var method = context[methodName]; |
4074 | + |
4075 | + return Y.Promise(function(resolve, reject) { |
4076 | + var resultHandler = function(result) { |
4077 | + if (result.err || result.error) { |
4078 | + reject(result.err || result.error); |
4079 | + } else { |
4080 | + resolve(result); |
4081 | + } |
4082 | + }; |
4083 | + |
4084 | + args.push(resultHandler); |
4085 | + var result = method.apply(context, args); |
4086 | + if (result !== undefined) { |
4087 | + // The method returned right away. |
4088 | + return resultHandler(result); |
4089 | + } |
4090 | + }); |
4091 | + } |
4092 | + |
4093 | + it('should support export', function(done) { |
4094 | + client.open(); |
4095 | + promise(state, 'deploy', 'cs:wordpress') |
4096 | + .then(promise(state, 'deploy', 'cs:mysql')) |
4097 | + .then(promise(state, 'addRelation', 'wordpress:db', 'mysql:db')) |
4098 | + .then(function() { |
4099 | + client.onmessage = function(result) { |
4100 | + var data = Y.JSON.parse(result.data).result; |
4101 | + assert.equal(data.services[0].name, 'wordpress'); |
4102 | + done(); |
4103 | + }; |
4104 | + client.send(Y.JSON.stringify({op: 'exportEnvironment'})); |
4105 | + }); |
4106 | + }); |
4107 | + |
4108 | + it('should support import', function(done) { |
4109 | + var fixture = utils.loadFixture('data/sample-fakebackend.json', false); |
4110 | + |
4111 | + client.onmessage = function() { |
4112 | + client.onmessage = function(result) { |
4113 | + var data = Y.JSON.parse(result.data).result; |
4114 | + assert.isTrue(data); |
4115 | + |
4116 | + // Verify that we can now find an expected entry |
4117 | + // in the database. |
4118 | + assert.isNotNull(state.db.services.getById('wordpress')); |
4119 | + |
4120 | + var changes = state.nextChanges(); |
4121 | + // Validate the delta includes imported services. |
4122 | + assert.include(changes.services, 'wordpress'); |
4123 | + assert.include(changes.services, 'mysql'); |
4124 | + // validate relation was added/updated. |
4125 | + assert.include(changes.relations, 'relation-0'); |
4126 | + done(); |
4127 | + }; |
4128 | + client.send(Y.JSON.stringify({op: 'importEnvironment', |
4129 | + envData: fixture})); |
4130 | + }; |
4131 | + client.open(); |
4132 | + }); |
4133 | + |
4134 | + }); |
4135 | + |
4136 | +})(); |
Reviewers: mp+172042_ code.launchpad. net,
Message:
Please take a look.
Description:
Split test_sandbox.js into pieces.
The test_sandbox.js file is too big, 2200+ lines and 75 KByte.
This splits the Go and Python sandbox tests out of it into python. js . Only the
test_sandbox_go.js and test_sandbox_
ClientConnection
tests are left there.
Huge diff unavoidably, but it only moves code around, no other changes,
I promise. :-)
https:/ /code.launchpad .net/~teknico/ juju-gui/ split-sandbox- test-file/ +merge/ 172042
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/10746043/
Affected files: sandbox. js sandbox_ go.js sandbox_ python. js
A [revision details]
M test/index.html
M test/test_
A test/test_
A test/test_