Merge lp:~frankban/juju-quickstart/views-params into lp:juju-quickstart

Proposed by Francesco Banconi
Status: Merged
Merged at revision: 111
Proposed branch: lp:~frankban/juju-quickstart/views-params
Merge into: lp:juju-quickstart
Diff against target: 975 lines (+301/-172)
6 files modified
quickstart/cli/params.py (+51/-0)
quickstart/cli/views.py (+70/-74)
quickstart/manage.py (+11/-4)
quickstart/tests/cli/test_params.py (+87/-0)
quickstart/tests/cli/test_views.py (+71/-90)
quickstart/tests/test_manage.py (+11/-4)
To merge this branch: bzr merge lp:~frankban/juju-quickstart/views-params
Reviewer Review Type Date Requested Status
Juju GUI Hackers Pending
Review via email: mp+245753@code.launchpad.net

Description of the change

Refactor view parameters.

Implement a Params object used to store
common view parameters. This way it will
be easier to add parameters in the future
(e.g. one callable to remove stale jenv files).

New code include the params module and a fix
to the code handling the listing of jenv files
in the index view: now the header message is
only displayed if jenv files actually exist.

The rest of the diff is mechanical: i.e. replacing
the single view arguments with the params named
tuple.

Tests: `make check`.

QA:
Use the quickstart interactive session and
check everything works ok.

https://codereview.appspot.com/194030043/

To post a comment you must log in.
Revision history for this message
Francesco Banconi (frankban) wrote :

Reviewers: mp+245753_code.launchpad.net,

Message:
Please take a look.

Description:
Refactor view parameters.

Implement a Params object used to store
common view parameters. This way it will
be easier to add parameters in the future
(e.g. one callable to remove stale jenv files).

New code include the params module and a fix
to the code handling the listing of jenv files
in the index view: now the header message is
only displayed if jenv files actually exist.

The rest of the diff is mechanical: i.e. replacing
the single view arguments with the params named
tuple.

Tests: `make check`.

QA:
Use the quickstart interactive session and
check everything works ok.

https://code.launchpad.net/~frankban/juju-quickstart/views-params/+merge/245753

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/194030043/

Affected files (+303, -172 lines):
   A [revision details]
   A quickstart/cli/params.py
   M quickstart/cli/views.py
   M quickstart/manage.py
   A quickstart/tests/cli/test_params.py
   M quickstart/tests/cli/test_views.py
   M quickstart/tests/test_manage.py

Revision history for this message
Brad Crittenden (bac) wrote :

LGTM. No QA yet.

https://codereview.appspot.com/194030043/diff/1/quickstart/cli/views.py
File quickstart/cli/views.py (right):

https://codereview.appspot.com/194030043/diff/1/quickstart/cli/views.py#newcode314
quickstart/cli/views.py:314: # and supposed to be working/active. The
user has the ability to select
Maybe 'assumed' would be clearer than 'supposed'?

https://codereview.appspot.com/194030043/diff/1/quickstart/tests/cli/test_params.py
File quickstart/tests/cli/test_params.py (right):

https://codereview.appspot.com/194030043/diff/1/quickstart/tests/cli/test_params.py#newcode87
quickstart/tests/cli/test_params.py:87: self.assertNotEqual(self.params,
params)
Such a nice set of tests! Thanks for the clarity.

https://codereview.appspot.com/194030043/

Revision history for this message
Martin Hilton (martin-hilton) wrote :
Revision history for this message
Brad Crittenden (bac) wrote :

QA-OK. I simply ran quickstart and launched a local provider.
Everything came up as expected.

https://codereview.appspot.com/194030043/

114. By Francesco Banconi

Improve view comment.

Revision history for this message
Francesco Banconi (frankban) wrote :

*** Submitted:

Refactor view parameters.

Implement a Params object used to store
common view parameters. This way it will
be easier to add parameters in the future
(e.g. one callable to remove stale jenv files).

New code include the params module and a fix
to the code handling the listing of jenv files
in the index view: now the header message is
only displayed if jenv files actually exist.

The rest of the diff is mechanical: i.e. replacing
the single view arguments with the params named
tuple.

Tests: `make check`.

QA:
Use the quickstart interactive session and
check everything works ok.

R=bac, martin.hilton
CC=
https://codereview.appspot.com/194030043

https://codereview.appspot.com/194030043/diff/1/quickstart/cli/views.py
File quickstart/cli/views.py (right):

https://codereview.appspot.com/194030043/diff/1/quickstart/cli/views.py#newcode314
quickstart/cli/views.py:314: # and supposed to be working/active. The
user has the ability to select
On 2015/01/07 15:58:26, bac wrote:
> Maybe 'assumed' would be clearer than 'supposed'?

Done.

https://codereview.appspot.com/194030043/

Revision history for this message
Francesco Banconi (frankban) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'quickstart/cli/params.py'
--- quickstart/cli/params.py 1970-01-01 00:00:00 +0000
+++ quickstart/cli/params.py 2015-01-07 16:46:42 +0000
@@ -0,0 +1,51 @@
1# This file is part of the Juju Quickstart Plugin, which lets users set up a
2# Juju environment in very few steps (https://launchpad.net/juju-quickstart).
3# Copyright (C) 2015 Canonical Ltd.
4#
5# This program is free software: you can redistribute it and/or modify it under
6# the terms of the GNU Affero General Public License version 3, as published by
7# the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful, but WITHOUT
10# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
11# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12# Affero General Public License for more details.
13#
14# You should have received a copy of the GNU Affero General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17"""Juju Quickstart view parameters implementation.
18
19This module contains an implementation of a composite parameter object to be
20used in views.
21
22The Params named tuple is intended to both help grouping view parameters
23together and avoid undesired mutations, by allowing explicit copies of the
24whole parameters object (see the Params.copy instance method).
25"""
26
27from __future__ import unicode_literals
28
29from collections import namedtuple
30import copy
31
32
33_PARAMS = ('env_type_db', 'env_db', 'jenv_db', 'save_callable')
34
35
36class Params(namedtuple('Params', _PARAMS)):
37 """View parameters as a named tuple."""
38
39 # Define empty slots to keep memory requirements low by preventing the
40 # creation of instance dictionaries.
41 # See https://docs.python.org/2/library/collections.html
42 __slots__ = ()
43
44 def copy(self):
45 """Return a deep copy of the stored parameters."""
46 return self.__class__(
47 env_type_db=self.env_type_db,
48 env_db=copy.deepcopy(self.env_db),
49 jenv_db=copy.deepcopy(self.jenv_db),
50 save_callable=self.save_callable,
51 )
052
=== modified file 'quickstart/cli/views.py'
--- quickstart/cli/views.py 2014-12-17 11:34:06 +0000
+++ quickstart/cli/views.py 2015-01-07 16:46:42 +0000
@@ -155,36 +155,33 @@
155 raise ui.AppExit((env_db, env_data))155 raise ui.AppExit((env_db, env_data))
156156
157157
158def env_index(app, env_type_db, env_db, jenv_db, save_callable):158def env_index(app, params):
159 """Show the Juju environments list.159 """Show the Juju environments list.
160160
161 The env_detail view is displayed when the user clicks on an environment.161 The env_detail view is displayed when the user clicks on an environment.
162 From here it is also possible to switch to the edit view in order to create162 From here it is also possible to switch to the edit view in order to create
163 a new environment.163 a new environment.
164164
165 Receives:165 Receives a params namedtuple-like object including the following fields:
166 - env_type_db: the environments meta information;166 - env_type_db: the environments meta information;
167 - env_db: the environments database;167 - env_db: the environments database;
168 - jenv_db: the jenv files database;168 - jenv_db: the jenv files database;
169 - save_callable: a function called to save a new environment database.169 - save_callable: a function called to save a new environment database.
170 See quickstart.cli.params.Params.
170 """171 """
171 # XXX frankban 16/12/2014: this function is too long, subdivide it.172 # XXX frankban 16/12/2014: this function is too long, subdivide it.
172 env_db = copy.deepcopy(env_db)173 params = params.copy()
173 jenv_db = copy.deepcopy(jenv_db)
174 # All the environment views return a tuple (new_env_db, env_data).174 # All the environment views return a tuple (new_env_db, env_data).
175 # Set the env_data to None in the case the user quits the application175 # Set the env_data to None in the case the user quits the application
176 # without selecting an environment to use.176 # without selecting an environment to use.
177 app.set_return_value_on_exit((env_db, None))177 app.set_return_value_on_exit((params.env_db, None))
178 detail_view = functools.partial(178 detail_view = functools.partial(env_detail, app, params)
179 env_detail, app, env_type_db, env_db, jenv_db, save_callable)179 jenv_view = functools.partial(jenv_detail, app, params)
180 jenv_view = functools.partial(180 edit_view = functools.partial(env_edit, app, params)
181 jenv_detail, app, env_type_db, env_db, jenv_db, save_callable)
182 edit_view = functools.partial(
183 env_edit, app, env_type_db, env_db, jenv_db, save_callable)
184 # Alphabetically sort the existing environments.181 # Alphabetically sort the existing environments.
185 environments = sorted([182 environments = sorted([
186 envs.get_env_data(env_db, env_name)183 envs.get_env_data(params.env_db, env_name)
187 for env_name in env_db['environments']184 for env_name in params.env_db['environments']
188 ], key=operator.itemgetter('name'))185 ], key=operator.itemgetter('name'))
189186
190 def create_and_start_local_env():187 def create_and_start_local_env():
@@ -193,8 +190,8 @@
193 # database. For this reason, the new environment is set as default.190 # database. For this reason, the new environment is set as default.
194 # Exit the interactive session selecting the newly created environment.191 # Exit the interactive session selecting the newly created environment.
195 env_data = envs.create_local_env_data(192 env_data = envs.create_local_env_data(
196 env_type_db, 'local', is_default=True)193 params.env_type_db, 'local', is_default=True)
197 _save_and_exit(env_db, env_data, save_callable)194 _save_and_exit(params.env_db, env_data, params.save_callable)
198195
199 def create_and_start_maas_env(name, server, api_key):196 def create_and_start_maas_env(name, server, api_key):
200 # Automatically create and use a MAAS environment.197 # Automatically create and use a MAAS environment.
@@ -202,8 +199,8 @@
202 # database. For this reason, the new environment is set as default.199 # database. For this reason, the new environment is set as default.
203 # Exit the interactive session selecting the newly created environment.200 # Exit the interactive session selecting the newly created environment.
204 env_data = envs.create_maas_env_data(201 env_data = envs.create_maas_env_data(
205 env_type_db, name, server, api_key, is_default=True)202 params.env_type_db, name, server, api_key, is_default=True)
206 _save_and_exit(env_db, env_data, save_callable)203 _save_and_exit(params.env_db, env_data, params.save_callable)
207204
208 platform = platform_support.get_platform()205 platform = platform_support.get_platform()
209 supports_local = platform_support.supports_local(platform)206 supports_local = platform_support.supports_local(platform)
@@ -279,7 +276,7 @@
279 focus_position = None276 focus_position = None
280 active_found = default_found = errors_found = False277 active_found = default_found = errors_found = False
281 existing_widgets_num = len(widgets)278 existing_widgets_num = len(widgets)
282 remaining_jenv_db = copy.deepcopy(jenv_db)279 remaining_jenv_db = copy.deepcopy(params.jenv_db)
283 for position, env_data in enumerate(environments):280 for position, env_data in enumerate(environments):
284 bullet = '\N{BULLET}'281 bullet = '\N{BULLET}'
285 # Is this environment the default one?282 # Is this environment the default one?
@@ -294,7 +291,7 @@
294 bullet = ('active', bullet)291 bullet = ('active', bullet)
295 else:292 else:
296 # Check if this environment is valid.293 # Check if this environment is valid.
297 env_metadata = envs.get_env_metadata(env_type_db, env_data)294 env_metadata = envs.get_env_metadata(params.env_type_db, env_data)
298 errors = envs.validate(env_metadata, env_data)295 errors = envs.validate(env_metadata, env_data)
299 if errors:296 if errors:
300 errors_found = True297 errors_found = True
@@ -306,25 +303,28 @@
306303
307 # Alphabetically sort the remaining environments not included in the304 # Alphabetically sort the remaining environments not included in the
308 # environments.yaml file.305 # environments.yaml file.
309 environments = sorted([306 environments = list(sorted([
310 envs.get_env_data(remaining_jenv_db, env_name)307 envs.get_env_data(remaining_jenv_db, env_name)
311 for env_name in remaining_jenv_db['environments']308 for env_name in remaining_jenv_db['environments']
312 ], key=operator.itemgetter('name'))309 ], key=operator.itemgetter('name')))
313310
314 # List the remaining active environments. Those environments are not311 if environments:
315 # included in the environments.yaml file: they are probably imported and312 # List the remaining active environments. Those environments are not
316 # supposed to be working/active. The user has the ability to select them.313 # included in the environments.yaml file: they are probably imported
317 widgets.extend([314 # and assumed to be working/active. The user has the ability to select
318 urwid.Divider(),315 # them.
319 urwid.Text(('highlight', 'Other active environments')),316 widgets.extend([
320 urwid.Text('(imported/not included in your environments.yaml file):'),317 urwid.Divider(),
321 urwid.Divider(),318 urwid.Text(('highlight', 'Other active environments')),
322 ])319 urwid.Text(
323 bullet = ('active', '\N{BULLET}')320 '(imported/not included in your environments.yaml file):'),
324 for env_data in environments:321 urwid.Divider(),
325 env_short_description = jenv.get_env_short_description(env_data)322 ])
326 text = [bullet, ' {}'.format(env_short_description)]323 bullet = ('active', '\N{BULLET}')
327 widgets.append(ui.MenuButton(text, ui.thunk(jenv_view, env_data)))324 for env_data in environments:
325 env_short_description = jenv.get_env_short_description(env_data)
326 text = [bullet, ' {}'.format(env_short_description)]
327 widgets.append(ui.MenuButton(text, ui.thunk(jenv_view, env_data)))
328328
329 # Set up the "create a new environment" section.329 # Set up the "create a new environment" section.
330 widgets.extend([330 widgets.extend([
@@ -342,7 +342,7 @@
342 if not supports_local:342 if not supports_local:
343 filter_function = lambda env_type, _: env_type != 'local'343 filter_function = lambda env_type, _: env_type != 'local'
344 supported_env_types = envs.get_supported_env_types(344 supported_env_types = envs.get_supported_env_types(
345 env_type_db, filter_function=filter_function)345 params.env_type_db, filter_function=filter_function)
346 # Add the buttons used to create new environments.346 # Add the buttons used to create new environments.
347 widgets.extend([347 widgets.extend([
348 ui.MenuButton(348 ui.MenuButton(
@@ -369,42 +369,40 @@
369 app.set_contents(contents)369 app.set_contents(contents)
370370
371371
372def env_detail(app, env_type_db, env_db, jenv_db, save_callable, env_data):372def env_detail(app, params, env_data):
373 """Show details on a Juju environment.373 """Show details on a Juju environment.
374374
375 From this view it is possible to start the environment, set it as default,375 From this view it is possible to start the environment, set it as default,
376 edit/remove the environment.376 edit/remove the environment.
377377
378 Receives:378 Receives a params namedtuple-like object including the following fields:
379 - env_type_db: the environments meta information;379 - env_type_db: the environments meta information;
380 - env_db: the environments database;380 - env_db: the environments database;
381 - jenv_db: the jenv files database;381 - jenv_db: the jenv files database;
382 - save_callable: a function called to save a new environment database;382 - save_callable: a function called to save a new environment database.
383 - env_data: the environment data.383 See quickstart.cli.params.Params.
384 Also receives the current environment data env_data.
384 """385 """
385 env_db = copy.deepcopy(env_db)386 params = params.copy()
386 jenv_db = copy.deepcopy(jenv_db)
387 # All the environment views return a tuple (new_env_db, env_data).387 # All the environment views return a tuple (new_env_db, env_data).
388 # Set the env_data to None in the case the user quits the application388 # Set the env_data to None in the case the user quits the application
389 # without selecting an environment to use.389 # without selecting an environment to use.
390 app.set_return_value_on_exit((env_db, None))390 app.set_return_value_on_exit((params.env_db, None))
391 index_view = functools.partial(391 index_view = functools.partial(env_index, app, params)
392 env_index, app, env_type_db, env_db, jenv_db, save_callable)392 edit_view = functools.partial(env_edit, app, params, env_data)
393 edit_view = functools.partial(
394 env_edit, app, env_type_db, env_db, jenv_db, save_callable, env_data)
395393
396 def use(env_data):394 def use(env_data):
397 # Quit the interactive session returning the (possibly modified)395 # Quit the interactive session returning the (possibly modified)
398 # environment database and the environment data corresponding to the396 # environment database and the environment data corresponding to the
399 # selected environment.397 # selected environment.
400 raise ui.AppExit((env_db, env_data))398 raise ui.AppExit((params.env_db, env_data))
401399
402 def set_default(env_data):400 def set_default(env_data):
403 # Set this environment as the default one, save the env_db and return401 # Set this environment as the default one, save the env_db and return
404 # to the index view.402 # to the index view.
405 env_name = env_data['name']403 env_name = env_data['name']
406 env_db['default'] = env_name404 params.env_db['default'] = env_name
407 save_callable(env_db)405 params.save_callable(params.env_db)
408 app.set_message('{} successfully set as default'.format(env_name))406 app.set_message('{} successfully set as default'.format(env_name))
409 index_view()407 index_view()
410408
@@ -412,8 +410,8 @@
412 # The environment deletion is confirmed: remove the environment from410 # The environment deletion is confirmed: remove the environment from
413 # the database, save the new env_db and return to the index view.411 # the database, save the new env_db and return to the index view.
414 env_name = env_data['name']412 env_name = env_data['name']
415 envs.remove_env(env_db, env_name)413 envs.remove_env(params.env_db, env_name)
416 save_callable(env_db)414 params.save_callable(params.env_db)
417 app.set_message('{} successfully removed'.format(env_name))415 app.set_message('{} successfully removed'.format(env_name))
418 index_view()416 index_view()
419417
@@ -427,7 +425,7 @@
427 ],425 ],
428 )426 )
429427
430 env_metadata = envs.get_env_metadata(env_type_db, env_data)428 env_metadata = envs.get_env_metadata(params.env_type_db, env_data)
431 app.set_title(envs.get_env_short_description(env_data))429 app.set_title(envs.get_env_short_description(env_data))
432 # Validate the environment.430 # Validate the environment.
433 errors = envs.validate(env_metadata, env_data)431 errors = envs.validate(env_metadata, env_data)
@@ -464,34 +462,33 @@
464 app.set_contents(listbox)462 app.set_contents(listbox)
465463
466464
467def jenv_detail(app, env_type_db, env_db, jenv_db, save_callable, env_data):465def jenv_detail(app, params, env_data):
468 """Show details on a Juju imported environment.466 """Show details on a Juju imported environment.
469467
470 The environment is not included in the environments.yaml file, but just468 The environment is not included in the environments.yaml file, but just
471 found in the jenv database.469 found in the jenv database.
472 From this view it is possible to start the environment.470 From this view it is possible to start the environment.
473471
474 Receives:472 Receives a params namedtuple-like object including the following fields:
475 - env_type_db: the environments meta information;473 - env_type_db: the environments meta information;
476 - env_db: the environments database;474 - env_db: the environments database;
477 - jenv_db: the jenv files database;475 - jenv_db: the jenv files database;
478 - save_callable: a function called to save a new environment database;476 - save_callable: a function called to save a new environment database;
479 - env_data: the environment data.477 See quickstart.cli.params.Params.
478 Also receives the current environment data env_data.
480 """479 """
481 env_db = copy.deepcopy(env_db)480 params = params.copy()
482 jenv_db = copy.deepcopy(jenv_db)
483 # All the environment views return a tuple (new_env_db, env_data).481 # All the environment views return a tuple (new_env_db, env_data).
484 # Set the env_data to None in the case the user quits the application482 # Set the env_data to None in the case the user quits the application
485 # without selecting an environment to use.483 # without selecting an environment to use.
486 app.set_return_value_on_exit((env_db, None))484 app.set_return_value_on_exit((params.env_db, None))
487 index_view = functools.partial(485 index_view = functools.partial(env_index, app, params)
488 env_index, app, env_type_db, env_db, jenv_db, save_callable)
489486
490 def use(env_data):487 def use(env_data):
491 # Quit the interactive session returning the (possibly modified)488 # Quit the interactive session returning the (possibly modified)
492 # environment database and the environment data corresponding to the489 # environment database and the environment data corresponding to the
493 # selected environment.490 # selected environment.
494 raise ui.AppExit((env_db, env_data))491 raise ui.AppExit((params.env_db, env_data))
495492
496 app.set_title(jenv.get_env_short_description(env_data))493 app.set_title(jenv.get_env_short_description(env_data))
497 widgets = []494 widgets = []
@@ -519,36 +516,34 @@
519 app.set_status([' \N{RIGHTWARDS ARROW OVER LEFTWARDS ARROW} navigate '])516 app.set_status([' \N{RIGHTWARDS ARROW OVER LEFTWARDS ARROW} navigate '])
520517
521518
522def env_edit(app, env_type_db, env_db, jenv_db, save_callable, env_data):519def env_edit(app, params, env_data):
523 """Create or modify a Juju environment.520 """Create or modify a Juju environment.
524521
525 This view displays an edit form allowing for environment522 This view displays an edit form allowing for environment
526 creation/modification. Saving the form redirects to the environment detail523 creation/modification. Saving the form redirects to the environment detail
527 view if the values are valid.524 view if the values are valid.
528525
529 Receives:526 Receives a params namedtuple-like object including the following fields:
530 - env_type_db: the environments meta information;527 - env_type_db: the environments meta information;
531 - env_db: the environments database;528 - env_db: the environments database;
532 - jenv_db: the jenv files database;529 - jenv_db: the jenv files database;
533 - save_callable: a function called to save a new environment database;530 - save_callable: a function called to save a new environment database;
534 - env_data: the environment data.531 See quickstart.cli.params.Params.
532 Also receives the current environment data env_data.
535533
536 The last value (env_data) indicates whether this view is used to create a534 The last value (env_data) indicates whether this view is used to create a
537 new environment or to change an existing one. In the former case, env_data535 new environment or to change an existing one. In the former case, env_data
538 does not include the "name" key. If instead the environment already exists,536 does not include the "name" key. If instead the environment already exists,
539 env_data includes the "name" key and all the other environment info.537 env_data includes the "name" key and all the other environment info.
540 """538 """
541 env_db = copy.deepcopy(env_db)539 params = params.copy()
542 jenv_db = copy.deepcopy(jenv_db)
543 # All the environment views return a tuple (new_env_db, env_data).540 # All the environment views return a tuple (new_env_db, env_data).
544 # Set the env_data to None in the case the user quits the application541 # Set the env_data to None in the case the user quits the application
545 # without selecting an environment to use.542 # without selecting an environment to use.
546 app.set_return_value_on_exit((env_db, None))543 app.set_return_value_on_exit((params.env_db, None))
547 env_metadata = envs.get_env_metadata(env_type_db, env_data)544 env_metadata = envs.get_env_metadata(params.env_type_db, env_data)
548 index_view = functools.partial(545 index_view = functools.partial(env_index, app, params)
549 env_index, app, env_type_db, env_db, jenv_db, save_callable)546 detail_view = functools.partial(env_detail, app, params)
550 detail_view = functools.partial(
551 env_detail, app, env_type_db, env_db, jenv_db, save_callable)
552 if 'name' in env_data:547 if 'name' in env_data:
553 exists = True548 exists = True
554 title = 'Edit the {} environment'549 title = 'Edit the {} environment'
@@ -580,6 +575,7 @@
580 errors = envs.validate(env_metadata, new_env_data)575 errors = envs.validate(env_metadata, new_env_data)
581 new_name = new_env_data['name']576 new_name = new_env_data['name']
582 initial_name = env_data.get('name')577 initial_name = env_data.get('name')
578 env_db = params.env_db
583 if (new_name != initial_name) and new_name in env_db['environments']:579 if (new_name != initial_name) and new_name in env_db['environments']:
584 errors['name'] = 'an environment with this name already exists'580 errors['name'] = 'an environment with this name already exists'
585 # If errors are found, re-render the form passing the errors. This way581 # If errors are found, re-render the form passing the errors. This way
@@ -594,7 +590,7 @@
594 if not env_db['environments']:590 if not env_db['environments']:
595 env_data['is-default'] = True591 env_data['is-default'] = True
596 envs.set_env_data(env_db, initial_name, env_data)592 envs.set_env_data(env_db, initial_name, env_data)
597 save_callable(env_db)593 params.save_callable(env_db)
598 verb = 'modified' if exists else 'created'594 verb = 'modified' if exists else 'created'
599 app.set_message('{} successfully {}'.format(new_name, verb))595 app.set_message('{} successfully {}'.format(new_name, verb))
600 return detail_view(env_data)596 return detail_view(env_data)
601597
=== modified file 'quickstart/manage.py'
--- quickstart/manage.py 2014-12-16 16:52:49 +0000
+++ quickstart/manage.py 2015-01-07 16:46:42 +0000
@@ -38,7 +38,10 @@
38 settings,38 settings,
39 utils,39 utils,
40)40)
41from quickstart.cli import views41from quickstart.cli import (
42 params,
43 views,
44)
42from quickstart.models import (45from quickstart.models import (
43 charms,46 charms,
44 envs,47 envs,
@@ -210,9 +213,13 @@
210 Exit the application if the user exits the interactive session without213 Exit the application if the user exits the interactive session without
211 selecting an environment to start.214 selecting an environment to start.
212 """215 """
213 save_callable = _create_save_callable(parser, env_file)216 parameters = params.Params(
214 new_env_db, env_data = views.show(217 env_type_db=env_type_db,
215 views.env_index, env_type_db, env_db, jenv_db, save_callable)218 env_db=env_db,
219 jenv_db=jenv_db,
220 save_callable=_create_save_callable(parser, env_file),
221 )
222 new_env_db, env_data = views.show(views.env_index, parameters)
216 if new_env_db != env_db:223 if new_env_db != env_db:
217 print('changes to the environments file have been saved')224 print('changes to the environments file have been saved')
218 if env_data is None:225 if env_data is None:
219226
=== added file 'quickstart/tests/cli/test_params.py'
--- quickstart/tests/cli/test_params.py 1970-01-01 00:00:00 +0000
+++ quickstart/tests/cli/test_params.py 2015-01-07 16:46:42 +0000
@@ -0,0 +1,87 @@
1# This file is part of the Juju Quickstart Plugin, which lets users set up a
2# Juju environment in very few steps (https://launchpad.net/juju-quickstart).
3# Copyright (C) 2015 Canonical Ltd.
4#
5# This program is free software: you can redistribute it and/or modify it under
6# the terms of the GNU Affero General Public License version 3, as published by
7# the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful, but WITHOUT
10# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
11# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12# Affero General Public License for more details.
13#
14# You should have received a copy of the GNU Affero General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17"""Tests for the Juju Quickstart CLI parameters implementation."""
18
19from __future__ import unicode_literals
20
21import unittest
22
23from quickstart.cli import params
24from quickstart.models import envs
25from quickstart.tests import helpers
26
27
28class TestParams(unittest.TestCase):
29
30 def setUp(self):
31 # Store parameters.
32 self.env_type_db = envs.get_env_type_db()
33 self.env_db = helpers.make_env_db()
34 self.jenv_db = helpers.make_jenv_db()
35 self.save_callable = lambda env_db: None
36 # Set up a params object used in tests.
37 self.params = params.Params(
38 env_type_db=self.env_type_db,
39 env_db=self.env_db,
40 jenv_db=self.jenv_db,
41 save_callable=self.save_callable,
42 )
43
44 def test_tuple(self):
45 # The params object can be used as a tuple.
46 env_type_db, env_db, jenv_db, save_callable = self.params
47 self.assertIs(self.env_type_db, env_type_db)
48 self.assertIs(self.env_db, env_db)
49 self.assertIs(self.jenv_db, jenv_db)
50 self.assertIs(self.save_callable, save_callable)
51
52 def test_attributes(self):
53 # Parameters can be accessed as attributes.
54 self.assertIs(self.env_type_db, self.params.env_type_db)
55 self.assertIs(self.env_db, self.params.env_db)
56 self.assertIs(self.jenv_db, self.params.jenv_db)
57 self.assertIs(self.save_callable, self.params.save_callable)
58
59 def test_immutable(self):
60 # It is not possible to replace a stored parameter.
61 with self.assertRaises(AttributeError):
62 self.params.env_db = {}
63
64 def test_copy(self):
65 # Params can be copied.
66 params = self.params.copy()
67 # The original object is not mutated by the copy.
68 self.assertIs(self.env_type_db, self.params.env_type_db)
69 self.assertIs(self.env_db, self.params.env_db)
70 self.assertIs(self.jenv_db, self.params.jenv_db)
71 self.assertIs(self.save_callable, self.params.save_callable)
72 # The new params object stores the same data.
73 self.assertEqual(self.params, params)
74 # But they do not refer to the same object.
75 self.assertIsNot(self.params, params)
76
77 def test_copy_mutations(self):
78 # Internal mutable objects can be still mutated.
79 params = self.params.copy()
80 # Mutate the copy.
81 params.env_db['environments']['lxc'] = {}
82 # The mutation took effect.
83 self.assertNotEqual(self.env_db, params.env_db)
84 # The original object is still preserved.
85 self.assertIs(self.env_db, self.params.env_db)
86 # The two now store different data.
87 self.assertNotEqual(self.params, params)
088
=== modified file 'quickstart/tests/cli/test_views.py'
--- quickstart/tests/cli/test_views.py 2014-12-17 11:47:43 +0000
+++ quickstart/tests/cli/test_views.py 2015-01-07 16:46:42 +0000
@@ -31,6 +31,7 @@
31from quickstart.cli import (31from quickstart.cli import (
32 base,32 base,
33 forms,33 forms,
34 params,
34 ui,35 ui,
35 views,36 views,
36)37)
@@ -166,6 +167,15 @@
166 """167 """
167 return lambda arg: isinstance(arg, cls)168 return lambda arg: isinstance(arg, cls)
168169
170 def make_params(self, env_db, jenv_db):
171 """Create and return view parameters using the given env databases."""
172 return params.Params(
173 env_type_db=self.env_type_db,
174 env_db=env_db,
175 jenv_db=jenv_db,
176 save_callable=self.save_callable,
177 )
178
169179
170class TestEnvIndex(EnvViewTestsMixin, unittest.TestCase):180class TestEnvIndex(EnvViewTestsMixin, unittest.TestCase):
171181
@@ -175,6 +185,7 @@
175 create_maas_caption = (185 create_maas_caption = (
176 '\N{BULLET} automatically create and bootstrap the {} MAAS '186 '\N{BULLET} automatically create and bootstrap the {} MAAS '
177 'environment'.format(MAAS_NAME))187 'environment'.format(MAAS_NAME))
188 active_environments_message = 'Other active environments'
178189
179 def test_view_default_return_value_on_exit(self):190 def test_view_default_return_value_on_exit(self):
180 # The view configures the app so that the return value on user exit is191 # The view configures the app so that the return value on user exit is
@@ -182,8 +193,7 @@
182 # meaning no environment has been selected.193 # meaning no environment has been selected.
183 env_db = helpers.make_env_db()194 env_db = helpers.make_env_db()
184 jenv_db = helpers.make_jenv_db()195 jenv_db = helpers.make_jenv_db()
185 views.env_index(196 views.env_index(self.app, self.make_params(env_db, jenv_db))
186 self.app, self.env_type_db, env_db, jenv_db, self.save_callable)
187 new_env_db, env_data = self.get_on_exit_return_value(self.loop)197 new_env_db, env_data = self.get_on_exit_return_value(self.loop)
188 self.assertEqual(env_db, new_env_db)198 self.assertEqual(env_db, new_env_db)
189 self.assertIsNot(env_db, new_env_db)199 self.assertIsNot(env_db, new_env_db)
@@ -193,8 +203,7 @@
193 # The application title is correctly set up.203 # The application title is correctly set up.
194 env_db = helpers.make_env_db()204 env_db = helpers.make_env_db()
195 jenv_db = helpers.make_jenv_db()205 jenv_db = helpers.make_jenv_db()
196 views.env_index(206 views.env_index(self.app, self.make_params(env_db, jenv_db))
197 self.app, self.env_type_db, env_db, jenv_db, self.save_callable)
198 self.assertEqual(207 self.assertEqual(
199 'Select an existing Juju environment or create a new one',208 'Select an existing Juju environment or create a new one',
200 self.app.get_title())209 self.app.get_title())
@@ -203,8 +212,7 @@
203 # The application title changes if the env_db has no environments.212 # The application title changes if the env_db has no environments.
204 env_db = {'environments': {}}213 env_db = {'environments': {}}
205 jenv_db = helpers.make_jenv_db()214 jenv_db = helpers.make_jenv_db()
206 views.env_index(215 views.env_index(self.app, self.make_params(env_db, jenv_db))
207 self.app, self.env_type_db, env_db, jenv_db, self.save_callable)
208 self.assertEqual(216 self.assertEqual(
209 'No Juju environments already set up: please create one',217 'No Juju environments already set up: please create one',
210 self.app.get_title())218 self.app.get_title())
@@ -215,9 +223,7 @@
215 env_db = helpers.make_env_db()223 env_db = helpers.make_env_db()
216 jenv_db = {'environments': {}}224 jenv_db = {'environments': {}}
217 with local_envs_supported(True):225 with local_envs_supported(True):
218 views.env_index(226 views.env_index(self.app, self.make_params(env_db, jenv_db))
219 self.app, self.env_type_db, env_db, jenv_db,
220 self.save_callable)
221 buttons = self.get_widgets_in_contents(227 buttons = self.get_widgets_in_contents(
222 filter_function=self.is_a(ui.MenuButton))228 filter_function=self.is_a(ui.MenuButton))
223 # A button is created for each existing environment (see details) and229 # A button is created for each existing environment (see details) and
@@ -232,9 +238,7 @@
232 env_db = {'environments': {}}238 env_db = {'environments': {}}
233 jenv_db = helpers.make_jenv_db()239 jenv_db = helpers.make_jenv_db()
234 with local_envs_supported(True):240 with local_envs_supported(True):
235 views.env_index(241 views.env_index(self.app, self.make_params(env_db, jenv_db))
236 self.app, self.env_type_db, env_db, jenv_db,
237 self.save_callable)
238 buttons = self.get_widgets_in_contents(242 buttons = self.get_widgets_in_contents(
239 filter_function=self.is_a(ui.MenuButton))243 filter_function=self.is_a(ui.MenuButton))
240 # A button is created for each existing environment (see details) and244 # A button is created for each existing environment (see details) and
@@ -249,6 +253,20 @@
249 1253 1
250 )254 )
251 self.assertEqual(expected_buttons_number, len(buttons))255 self.assertEqual(expected_buttons_number, len(buttons))
256 # A text widget is displayed as header for the imported environments.
257 widgets = self.get_widgets_in_contents(
258 filter_function=self.is_a(urwid.Text))
259 self.assertEqual(self.active_environments_message, widgets[2].text)
260
261 def test_view_contents_without_imported_envs(self):
262 # If there are no active imported environments the corresponding
263 # header text is not displayed.
264 env_db = jenv_db = {'environments': {}}
265 views.env_index(self.app, self.make_params(env_db, jenv_db))
266 widgets = self.get_widgets_in_contents(
267 filter_function=self.is_a(urwid.Text))
268 texts = [widget.text for widget in widgets]
269 self.assertNotIn(self.active_environments_message, texts)
252270
253 def test_new_local_environment_disabled(self):271 def test_new_local_environment_disabled(self):
254 # The option to create a new local environment is not present if they272 # The option to create a new local environment is not present if they
@@ -256,9 +274,7 @@
256 env_db = helpers.make_env_db()274 env_db = helpers.make_env_db()
257 jenv_db = helpers.make_jenv_db()275 jenv_db = helpers.make_jenv_db()
258 with local_envs_supported(False):276 with local_envs_supported(False):
259 views.env_index(277 views.env_index(self.app, self.make_params(env_db, jenv_db))
260 self.app, self.env_type_db, env_db, jenv_db,
261 self.save_callable)
262 buttons = self.get_widgets_in_contents(278 buttons = self.get_widgets_in_contents(
263 filter_function=self.is_a(ui.MenuButton))279 filter_function=self.is_a(ui.MenuButton))
264 captions = map(cli_helpers.get_button_caption, buttons)280 captions = map(cli_helpers.get_button_caption, buttons)
@@ -273,8 +289,8 @@
273 # The environment detail view is called when clicking an environment.289 # The environment detail view is called when clicking an environment.
274 env_db = helpers.make_env_db()290 env_db = helpers.make_env_db()
275 jenv_db = {'environments': {}}291 jenv_db = {'environments': {}}
276 views.env_index(292 params = self.make_params(env_db, jenv_db)
277 self.app, self.env_type_db, env_db, jenv_db, self.save_callable)293 views.env_index(self.app, params)
278 buttons = self.get_widgets_in_contents(294 buttons = self.get_widgets_in_contents(
279 filter_function=self.is_a(ui.MenuButton))295 filter_function=self.is_a(ui.MenuButton))
280 # The environments are listed in alphabetical order.296 # The environments are listed in alphabetical order.
@@ -288,9 +304,7 @@
288 # When the button is clicked, the detail view is called passing the304 # When the button is clicked, the detail view is called passing the
289 # corresponding environment data.305 # corresponding environment data.
290 cli_helpers.emit(button)306 cli_helpers.emit(button)
291 mock_env_detail.assert_called_once_with(307 mock_env_detail.assert_called_once_with(self.app, params, env_data)
292 self.app, self.env_type_db, env_db, jenv_db,
293 self.save_callable, env_data)
294 # Reset the mock so that it does not include any calls on the next308 # Reset the mock so that it does not include any calls on the next
295 # loop cycle.309 # loop cycle.
296 mock_env_detail.reset_mock()310 mock_env_detail.reset_mock()
@@ -300,10 +314,9 @@
300 # The jenv detail view is called when clicking an imported environment.314 # The jenv detail view is called when clicking an imported environment.
301 env_db = {'environments': {}}315 env_db = {'environments': {}}
302 jenv_db = helpers.make_jenv_db()316 jenv_db = helpers.make_jenv_db()
317 params = self.make_params(env_db, jenv_db)
303 with local_envs_supported(False):318 with local_envs_supported(False):
304 views.env_index(319 views.env_index(self.app, params)
305 self.app, self.env_type_db, env_db, jenv_db,
306 self.save_callable)
307 buttons = self.get_widgets_in_contents(320 buttons = self.get_widgets_in_contents(
308 filter_function=self.is_a(ui.MenuButton))321 filter_function=self.is_a(ui.MenuButton))
309 # The environments are listed in alphabetical order.322 # The environments are listed in alphabetical order.
@@ -318,8 +331,7 @@
318 # passing the corresponding environment data.331 # passing the corresponding environment data.
319 cli_helpers.emit(button)332 cli_helpers.emit(button)
320 mock_jenv_detail.assert_called_once_with(333 mock_jenv_detail.assert_called_once_with(
321 self.app, self.env_type_db, env_db, jenv_db,334 self.app, params, env_data)
322 self.save_callable, env_data)
323 # Reset the mock so that it does not include any calls on the next335 # Reset the mock so that it does not include any calls on the next
324 # loop cycle.336 # loop cycle.
325 mock_jenv_detail.reset_mock()337 mock_jenv_detail.reset_mock()
@@ -330,10 +342,9 @@
330 # environment.342 # environment.
331 env_db = helpers.make_env_db()343 env_db = helpers.make_env_db()
332 jenv_db = {'environments': {}}344 jenv_db = {'environments': {}}
345 params = self.make_params(env_db, jenv_db)
333 with local_envs_supported(True):346 with local_envs_supported(True):
334 views.env_index(347 views.env_index(self.app, params)
335 self.app, self.env_type_db, env_db, jenv_db,
336 self.save_callable)
337 buttons = self.get_widgets_in_contents(348 buttons = self.get_widgets_in_contents(
338 filter_function=self.is_a(ui.MenuButton))349 filter_function=self.is_a(ui.MenuButton))
339 env_types = envs.get_supported_env_types(self.env_type_db)350 env_types = envs.get_supported_env_types(self.env_type_db)
@@ -347,8 +358,7 @@
347 # corresponding environment data.358 # corresponding environment data.
348 cli_helpers.emit(button)359 cli_helpers.emit(button)
349 mock_env_edit.assert_called_once_with(360 mock_env_edit.assert_called_once_with(
350 self.app, self.env_type_db, env_db, jenv_db,361 self.app, params, {
351 self.save_callable, {
352 'type': env_type,362 'type': env_type,
353 'default-series': settings.JUJU_GUI_SUPPORTED_SERIES[-1],363 'default-series': settings.JUJU_GUI_SUPPORTED_SERIES[-1],
354 })364 })
@@ -365,9 +375,7 @@
365 jenv_db = helpers.make_jenv_db()375 jenv_db = helpers.make_jenv_db()
366 with maas_env_detected(False):376 with maas_env_detected(False):
367 with local_envs_supported(True):377 with local_envs_supported(True):
368 views.env_index(378 views.env_index(self.app, self.make_params(env_db, jenv_db))
369 self.app, self.env_type_db, env_db, jenv_db,
370 self.save_callable)
371 buttons = self.get_widgets_in_contents(379 buttons = self.get_widgets_in_contents(
372 filter_function=self.is_a(ui.MenuButton))380 filter_function=self.is_a(ui.MenuButton))
373 # The "create and bootstrap" button is the first one in the contents.381 # The "create and bootstrap" button is the first one in the contents.
@@ -390,9 +398,7 @@
390 env_db = envs.create_empty_env_db()398 env_db = envs.create_empty_env_db()
391 jenv_db = helpers.make_jenv_db()399 jenv_db = helpers.make_jenv_db()
392 with local_envs_supported(False):400 with local_envs_supported(False):
393 views.env_index(401 views.env_index(self.app, self.make_params(env_db, jenv_db))
394 self.app, self.env_type_db, env_db, jenv_db,
395 self.save_callable)
396 buttons = self.get_widgets_in_contents(402 buttons = self.get_widgets_in_contents(
397 filter_function=self.is_a(ui.MenuButton))403 filter_function=self.is_a(ui.MenuButton))
398 # No "create and bootstrap local" buttons are present.404 # No "create and bootstrap local" buttons are present.
@@ -408,9 +414,7 @@
408 env_db = envs.create_empty_env_db()414 env_db = envs.create_empty_env_db()
409 jenv_db = helpers.make_jenv_db()415 jenv_db = helpers.make_jenv_db()
410 with maas_env_detected(True):416 with maas_env_detected(True):
411 views.env_index(417 views.env_index(self.app, self.make_params(env_db, jenv_db))
412 self.app, self.env_type_db, env_db, jenv_db,
413 self.save_callable)
414 buttons = self.get_widgets_in_contents(418 buttons = self.get_widgets_in_contents(
415 filter_function=self.is_a(ui.MenuButton))419 filter_function=self.is_a(ui.MenuButton))
416 # The "create and bootstrap" button is the first one in the contents.420 # The "create and bootstrap" button is the first one in the contents.
@@ -433,9 +437,7 @@
433 env_db = envs.create_empty_env_db()437 env_db = envs.create_empty_env_db()
434 jenv_db = helpers.make_jenv_db()438 jenv_db = helpers.make_jenv_db()
435 with maas_env_detected(False):439 with maas_env_detected(False):
436 views.env_index(440 views.env_index(self.app, self.make_params(env_db, jenv_db))
437 self.app, self.env_type_db, env_db, jenv_db,
438 self.save_callable)
439 buttons = self.get_widgets_in_contents(441 buttons = self.get_widgets_in_contents(
440 filter_function=self.is_a(ui.MenuButton))442 filter_function=self.is_a(ui.MenuButton))
441 # No "create and bootstrap MAAS" buttons are present.443 # No "create and bootstrap MAAS" buttons are present.
@@ -446,8 +448,7 @@
446 # The default environment is already selected in the list.448 # The default environment is already selected in the list.
447 env_db = helpers.make_env_db(default='lxc')449 env_db = helpers.make_env_db(default='lxc')
448 jenv_db = helpers.make_jenv_db()450 jenv_db = helpers.make_jenv_db()
449 views.env_index(451 views.env_index(self.app, self.make_params(env_db, jenv_db))
450 self.app, self.env_type_db, env_db, jenv_db, self.save_callable)
451 env_data = envs.get_env_data(env_db, 'lxc')452 env_data = envs.get_env_data(env_db, 'lxc')
452 env_description = envs.get_env_short_description(env_data)453 env_description = envs.get_env_short_description(env_data)
453 contents = self.app.get_contents()454 contents = self.app.get_contents()
@@ -460,8 +461,7 @@
460 # The status message explains how errors are displayed.461 # The status message explains how errors are displayed.
461 env_db = helpers.make_env_db()462 env_db = helpers.make_env_db()
462 jenv_db = {'environments': {}}463 jenv_db = {'environments': {}}
463 views.env_index(464 views.env_index(self.app, self.make_params(env_db, jenv_db))
464 self.app, self.env_type_db, env_db, jenv_db, self.save_callable)
465 status = self.app.get_status()465 status = self.app.get_status()
466 self.assertEqual(self.base_status + ' \N{BULLET} has errors ', status)466 self.assertEqual(self.base_status + ' \N{BULLET} has errors ', status)
467467
@@ -469,8 +469,7 @@
469 # The status message explains how default environment is represented.469 # The status message explains how default environment is represented.
470 env_db = helpers.make_env_db(default='lxc', exclude_invalid=True)470 env_db = helpers.make_env_db(default='lxc', exclude_invalid=True)
471 jenv_db = {'environments': {}}471 jenv_db = {'environments': {}}
472 views.env_index(472 views.env_index(self.app, self.make_params(env_db, jenv_db))
473 self.app, self.env_type_db, env_db, jenv_db, self.save_callable)
474 status = self.app.get_status()473 status = self.app.get_status()
475 self.assertEqual(self.base_status + ' \N{CHECK MARK} default ', status)474 self.assertEqual(self.base_status + ' \N{CHECK MARK} default ', status)
476475
@@ -478,8 +477,7 @@
478 # The status message explains how active environments are displayed.477 # The status message explains how active environments are displayed.
479 env_db = helpers.make_env_db(exclude_invalid=True)478 env_db = helpers.make_env_db(exclude_invalid=True)
480 jenv_db = helpers.make_jenv_db()479 jenv_db = helpers.make_jenv_db()
481 views.env_index(480 views.env_index(self.app, self.make_params(env_db, jenv_db))
482 self.app, self.env_type_db, env_db, jenv_db, self.save_callable)
483 status = self.app.get_status()481 status = self.app.get_status()
484 self.assertEqual(self.base_status + ' \N{BULLET} active ', status)482 self.assertEqual(self.base_status + ' \N{BULLET} active ', status)
485483
@@ -487,8 +485,7 @@
487 # The status message includes default, active and errors explanations.485 # The status message includes default, active and errors explanations.
488 env_db = helpers.make_env_db(default='lxc')486 env_db = helpers.make_env_db(default='lxc')
489 jenv_db = helpers.make_jenv_db()487 jenv_db = helpers.make_jenv_db()
490 views.env_index(488 views.env_index(self.app, self.make_params(env_db, jenv_db))
491 self.app, self.env_type_db, env_db, jenv_db, self.save_callable)
492 status = self.app.get_status()489 status = self.app.get_status()
493 self.assertEqual(490 self.assertEqual(
494 self.base_status +491 self.base_status +
@@ -501,8 +498,7 @@
501 # The status only includes navigation info if there are no errors.498 # The status only includes navigation info if there are no errors.
502 env_db = helpers.make_env_db(exclude_invalid=True)499 env_db = helpers.make_env_db(exclude_invalid=True)
503 jenv_db = {'environments': {}}500 jenv_db = {'environments': {}}
504 views.env_index(501 views.env_index(self.app, self.make_params(env_db, jenv_db))
505 self.app, self.env_type_db, env_db, jenv_db, self.save_callable)
506 status = self.app.get_status()502 status = self.app.get_status()
507 self.assertEqual(self.base_status, status)503 self.assertEqual(self.base_status, status)
508504
@@ -516,9 +512,8 @@
516 def call_view(self, env_name='lxc'):512 def call_view(self, env_name='lxc'):
517 """Call the view passing the env_data corresponding to env_name."""513 """Call the view passing the env_data corresponding to env_name."""
518 self.env_data = envs.get_env_data(self.env_db, env_name)514 self.env_data = envs.get_env_data(self.env_db, env_name)
519 return views.env_detail(515 self.params = self.make_params(self.env_db, self.jenv_db)
520 self.app, self.env_type_db, self.env_db, self.jenv_db,516 return views.env_detail(self.app, self.params, self.env_data)
521 self.save_callable, self.env_data)
522517
523 def test_view_default_return_value_on_exit(self):518 def test_view_default_return_value_on_exit(self):
524 # The view configures the app so that the return value on user exit is519 # The view configures the app so that the return value on user exit is
@@ -584,9 +579,7 @@
584 # The "back" button is the first one.579 # The "back" button is the first one.
585 back_button = self.get_control_buttons()[0]580 back_button = self.get_control_buttons()[0]
586 cli_helpers.emit(back_button)581 cli_helpers.emit(back_button)
587 mock_env_index.assert_called_once_with(582 mock_env_index.assert_called_once_with(self.app, self.params)
588 self.app, self.env_type_db, self.env_db, self.jenv_db,
589 self.save_callable)
590583
591 def test_use_button(self):584 def test_use_button(self):
592 # The application exits if the "use" button is clicked.585 # The application exits if the "use" button is clicked.
@@ -608,14 +601,13 @@
608 # The "set default" button is the third one.601 # The "set default" button is the third one.
609 set_default_button = self.get_control_buttons()[2]602 set_default_button = self.get_control_buttons()[2]
610 cli_helpers.emit(set_default_button)603 cli_helpers.emit(set_default_button)
611 # The index view has been called passing the modified env_db as third604 # The index view has been called passing the modified env_db in params.
612 # argument.
613 self.assertTrue(mock_env_index.called)605 self.assertTrue(mock_env_index.called)
614 new_env_db = mock_env_index.call_args[0][2]606 params = mock_env_index.call_args[0][1]
615 # The new env_db has a new default.607 # The new env_db has a new default.
616 self.assertEqual(new_env_db['default'], 'ec2-west')608 self.assertEqual(params.env_db['default'], 'ec2-west')
617 # The new env_db has been saved.609 # The new env_db has been saved.
618 self.save_callable.assert_called_once_with(new_env_db)610 self.save_callable.assert_called_once_with(params.env_db)
619611
620 @mock.patch('quickstart.cli.views.env_edit')612 @mock.patch('quickstart.cli.views.env_edit')
621 def test_edit_button(self, mock_env_edit):613 def test_edit_button(self, mock_env_edit):
@@ -625,8 +617,7 @@
625 edit_button = self.get_control_buttons()[3]617 edit_button = self.get_control_buttons()[3]
626 cli_helpers.emit(edit_button)618 cli_helpers.emit(edit_button)
627 mock_env_edit.assert_called_once_with(619 mock_env_edit.assert_called_once_with(
628 self.app, self.env_type_db, self.env_db, self.jenv_db,620 self.app, self.params, self.env_data)
629 self.save_callable, self.env_data)
630621
631 def test_remove_button(self):622 def test_remove_button(self):
632 # A confirmation dialog is displayed if the "remove" button is clicked.623 # A confirmation dialog is displayed if the "remove" button is clicked.
@@ -677,14 +668,13 @@
677 # The "confirm" button is the second one in the dialog.668 # The "confirm" button is the second one in the dialog.
678 confirm_button = buttons[1]669 confirm_button = buttons[1]
679 cli_helpers.emit(confirm_button)670 cli_helpers.emit(confirm_button)
680 # The index view has been called passing the modified env_db as third671 # The index view has been called passing the modified env_db in params.
681 # argument.
682 self.assertTrue(mock_env_index.called)672 self.assertTrue(mock_env_index.called)
683 new_env_db = mock_env_index.call_args[0][2]673 params = mock_env_index.call_args[0][1]
684 # The new env_db no longer includes the "ec2-west" environment.674 # The new env_db no longer includes the "ec2-west" environment.
685 self.assertNotIn('ec2-west', new_env_db['environments'])675 self.assertNotIn('ec2-west', params.env_db['environments'])
686 # The new env_db has been saved.676 # The new env_db has been saved.
687 self.save_callable.assert_called_once_with(new_env_db)677 self.save_callable.assert_called_once_with(params.env_db)
688678
689 def test_status_with_errors(self):679 def test_status_with_errors(self):
690 # The status message explains how field errors are displayed.680 # The status message explains how field errors are displayed.
@@ -709,9 +699,8 @@
709 def call_view(self, env_name='lxc'):699 def call_view(self, env_name='lxc'):
710 """Call the view passing the env_data corresponding to env_name."""700 """Call the view passing the env_data corresponding to env_name."""
711 self.env_data = envs.get_env_data(self.jenv_db, env_name)701 self.env_data = envs.get_env_data(self.jenv_db, env_name)
712 return views.jenv_detail(702 self.params = self.make_params(self.env_db, self.jenv_db)
713 self.app, self.env_type_db, self.env_db, self.jenv_db,703 return views.jenv_detail(self.app, self.params, self.env_data)
714 self.save_callable, self.env_data)
715704
716 def test_view_default_return_value_on_exit(self):705 def test_view_default_return_value_on_exit(self):
717 # The view configures the app so that the return value on user exit is706 # The view configures the app so that the return value on user exit is
@@ -756,9 +745,7 @@
756 # The "back" button is the first one.745 # The "back" button is the first one.
757 back_button = self.get_control_buttons()[0]746 back_button = self.get_control_buttons()[0]
758 cli_helpers.emit(back_button)747 cli_helpers.emit(back_button)
759 mock_env_index.assert_called_once_with(748 mock_env_index.assert_called_once_with(self.app, self.params)
760 self.app, self.env_type_db, self.env_db, self.jenv_db,
761 self.save_callable)
762749
763 def test_use_button(self):750 def test_use_button(self):
764 # The application exits if the "use" button is clicked.751 # The application exits if the "use" button is clicked.
@@ -788,9 +775,8 @@
788 self.env_data = envs.get_env_data(self.env_db, env_name)775 self.env_data = envs.get_env_data(self.env_db, env_name)
789 else:776 else:
790 self.env_data = {'type': env_type}777 self.env_data = {'type': env_type}
791 return views.env_edit(778 self.params = self.make_params(self.env_db, self.jenv_db)
792 self.app, self.env_type_db, self.env_db, self.jenv_db,779 return views.env_edit(self.app, self.params, self.env_data)
793 self.save_callable, self.env_data)
794780
795 def get_form_contents(self):781 def get_form_contents(self):
796 """Return the form contents included in the app page.782 """Return the form contents included in the app page.
@@ -925,8 +911,7 @@
925 'ec2-west successfully modified', self.app.get_message())911 'ec2-west successfully modified', self.app.get_message())
926 # The application displays the environment detail view.912 # The application displays the environment detail view.
927 mock_env_detail.assert_called_once_with(913 mock_env_detail.assert_called_once_with(
928 self.app, self.env_type_db, self.env_db, self.jenv_db,914 self.app, self.params, new_env_data)
929 self.save_callable, new_env_data)
930915
931 @mock.patch('quickstart.cli.views.env_detail')916 @mock.patch('quickstart.cli.views.env_detail')
932 def test_save_empty_db(self, mock_env_detail):917 def test_save_empty_db(self, mock_env_detail):
@@ -945,8 +930,7 @@
945 expected_new_env_data.update({'type': 'local', 'is-default': True})930 expected_new_env_data.update({'type': 'local', 'is-default': True})
946 envs.set_env_data(self.env_db, None, expected_new_env_data)931 envs.set_env_data(self.env_db, None, expected_new_env_data)
947 mock_env_detail.assert_called_once_with(932 mock_env_detail.assert_called_once_with(
948 self.app, self.env_type_db, self.env_db, self.jenv_db,933 self.app, self.params, expected_new_env_data)
949 self.save_callable, expected_new_env_data)
950934
951 def test_save_invalid_form_data(self):935 def test_save_invalid_form_data(self):
952 # Errors are displayed if the user tries to save invalid data.936 # Errors are displayed if the user tries to save invalid data.
@@ -990,9 +974,7 @@
990 # The "cancel" button is the second one.974 # The "cancel" button is the second one.
991 cancel_button = self.get_control_buttons()[1]975 cancel_button = self.get_control_buttons()[1]
992 cli_helpers.emit(cancel_button)976 cli_helpers.emit(cancel_button)
993 mock_env_index.assert_called_once_with(977 mock_env_index.assert_called_once_with(self.app, self.params)
994 self.app, self.env_type_db, self.env_db, self.jenv_db,
995 self.save_callable)
996978
997 @mock.patch('quickstart.cli.views.env_detail')979 @mock.patch('quickstart.cli.views.env_detail')
998 def test_modification_view_cancel_button(self, mock_env_detail):980 def test_modification_view_cancel_button(self, mock_env_detail):
@@ -1003,5 +985,4 @@
1003 cancel_button = self.get_control_buttons()[1]985 cancel_button = self.get_control_buttons()[1]
1004 cli_helpers.emit(cancel_button)986 cli_helpers.emit(cancel_button)
1005 mock_env_detail.assert_called_once_with(987 mock_env_detail.assert_called_once_with(
1006 self.app, self.env_type_db, self.env_db, self.jenv_db,988 self.app, self.params, self.env_data)
1007 self.save_callable, self.env_data)
1008989
=== modified file 'quickstart/tests/test_manage.py'
--- quickstart/tests/test_manage.py 2014-12-16 16:52:49 +0000
+++ quickstart/tests/test_manage.py 2015-01-07 16:46:42 +0000
@@ -35,7 +35,10 @@
35 manage,35 manage,
36 settings,36 settings,
37)37)
38from quickstart.cli import views38from quickstart.cli import (
39 params,
40 views,
41)
39from quickstart.models import (42from quickstart.models import (
40 envs,43 envs,
41 jenv,44 jenv,
@@ -391,9 +394,13 @@
391 with mock.patch('quickstart.manage.views.show', mock_show):394 with mock.patch('quickstart.manage.views.show', mock_show):
392 yield395 yield
393 mock_save_callable.assert_called_once_with(self.parser, self.env_file)396 mock_save_callable.assert_called_once_with(self.parser, self.env_file)
394 mock_show.assert_called_once_with(397 expected_params = params.Params(
395 views.env_index, self.env_type_db, env_db, jenv_db,398 env_type_db=self.env_type_db,
396 mock_save_callable())399 env_db=env_db,
400 jenv_db=jenv_db,
401 save_callable=mock_save_callable(),
402 )
403 mock_show.assert_called_once_with(views.env_index, expected_params)
397404
398 def test_resulting_env_data(self):405 def test_resulting_env_data(self):
399 # The interactive session can be used to select an environment, in406 # The interactive session can be used to select an environment, in

Subscribers

People subscribed via source and target branches