Repository: conjure-up/conjure-up

Issue:
test failure if no juju found on system
travis-ci caught this when no juju is found:

```
======================================================================
ERROR: call finish
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/travis/build/conjure-up/conjure-up/test/test_controllers_clouds_gui.py", line 94, in test_finish
    self.controller.finish(self.cloudname)
  File "/home/travis/build/conjure-up/conjure-up/conjureup/controllers/clouds/gui.py", line 31, in finish
    existing_controller = common.get_controller_in_cloud(cloud)
  File "/home/travis/build/conjure-up/conjure-up/conjureup/controllers/clouds/common.py", line 63, in get_controller_in_cloud
    controllers = juju.get_controllers()['controllers'].items()
  File "/home/travis/build/conjure-up/conjure-up/conjureup/juju.py", line 515, in get_controllers
    "Unable to list controllers: {}".format(sh.stderr.decode('utf8')))
LookupError: Unable to list controllers: /bin/sh: 1: juju: not found


======================================================================
ERROR: call finish
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/travis/build/conjure-up/conjure-up/test/test_controllers_clouds_tui.py", line 97, in test_finish
    self.controller.finish()
  File "/home/travis/build/conjure-up/conjure-up/conjureup/controllers/clouds/tui.py", line 20, in finish
    existing_controller = common.get_controller_in_cloud(app.argv.cloud)
  File "/home/travis/build/conjure-up/conjure-up/conjureup/controllers/clouds/common.py", line 63, in get_controller_in_cloud
    controllers = juju.get_controllers()['controllers'].items()
  File "/home/travis/build/conjure-up/conjure-up/conjureup/juju.py", line 515, in get_controllers
    "Unable to list controllers: {}".format(sh.stderr.decode('utf8')))
LookupError: Unable to list controllers: /bin/sh: 1: juju: not found
```

Discussion:

Patch:
diff --git a/conjureup/controllers/clouds/gui.py b/conjureup/controllers/clouds/gui.py
index bef8da0..37898cb 100644
--- a/conjureup/controllers/clouds/gui.py
+++ b/conjureup/controllers/clouds/gui.py
@@ -5,7 +5,8 @@ from conjureup import controllers
 from conjureup.app_config import app
 from conjureup import juju
 import petname
-from . import common
+from conjureup.controllers.clouds.common import (get_controller_in_cloud,
+                                                 list_clouds)
 
 
 class CloudsController:
@@ -28,7 +29,7 @@ class CloudsController:
         cloud: Cloud to create the controller/model on.
         """
         utils.pollinate(app.session_id, 'CS')
-        existing_controller = common.get_controller_in_cloud(cloud)
+        existing_controller = get_controller_in_cloud(cloud)
 
         if existing_controller is None:
             return controllers.use('newcloud').render(cloud)
@@ -46,7 +47,7 @@ class CloudsController:
         return controllers.use('variants').render()
 
     def render(self):
-        clouds = common.list_clouds()
+        clouds = list_clouds()
         excerpt = app.config.get(
             'description',
             "Please select from a list of available clouds")
diff --git a/conjureup/controllers/clouds/tui.py b/conjureup/controllers/clouds/tui.py
index d7d7e87..cc8a7b1 100644
--- a/conjureup/controllers/clouds/tui.py
+++ b/conjureup/controllers/clouds/tui.py
@@ -2,7 +2,8 @@ from conjureup import controllers
 from conjureup import juju
 from conjureup import utils
 from conjureup.app_config import app
-from . import common
+from conjureup.controllers.clouds.common import get_controller_in_cloud
+
 import petname
 import sys
 import os
@@ -17,7 +18,7 @@ class CloudsController:
                          "/usr/share/conjure-up/run-lxd-config",
                          back)
 
-        existing_controller = common.get_controller_in_cloud(app.argv.cloud)
+        existing_controller = get_controller_in_cloud(app.argv.cloud)
         if existing_controller is None:
             return controllers.use('newcloud').render(app.argv.cloud)
 


Test patch:
diff --git a/test/test_controllers_clouds_gui.py b/test/test_controllers_clouds_gui.py
index 3202389..2736bc7 100644
--- a/test/test_controllers_clouds_gui.py
+++ b/test/test_controllers_clouds_gui.py
@@ -19,8 +19,8 @@
 
 
 import unittest
-#  from unittest.mock import ANY, call, MagicMock, patch, sentinel
-from unittest.mock import patch, MagicMock
+
+from unittest.mock import call, patch, MagicMock
 
 from conjureup.controllers.clouds.gui import CloudsController
 
@@ -44,17 +44,17 @@ class CloudsGUIRenderTestCase(unittest.TestCase):
             'conjureup.controllers.clouds.gui.app')
         mock_app = self.app_patcher.start()
         mock_app.ui = MagicMock(name="app.ui")
-        self.common_patcher = patch(
-            'conjureup.controllers.clouds.gui.common')
-        self.mock_common = self.common_patcher.start()
-        self.mock_common.list_clouds.return_value = ['test1', 'test2']
+        self.list_clouds_patcher = patch(
+            'conjureup.controllers.clouds.gui.list_clouds')
+        self.mock_list_clouds = self.list_clouds_patcher.start()
+        self.mock_list_clouds.return_value = ['test1', 'test2']
 
     def tearDown(self):
         self.utils_patcher.stop()
         self.finish_patcher.stop()
         self.view_patcher.stop()
         self.app_patcher.stop()
-        self.common_patcher.start()
+        self.list_clouds_patcher.start()
 
     def test_render(self):
         "call render"
@@ -82,13 +82,32 @@ class CloudsGUIFinishTestCase(unittest.TestCase):
         self.mock_app.ui = MagicMock(name="app.ui")
 
         self.cloudname = 'testcloudname'
+        self.juju_patcher = patch(
+            'conjureup.controllers.clouds.gui.juju')
+        self.mock_juju = self.juju_patcher.start()
+
+        self.gcc_patcher = patch(
+            'conjureup.controllers.clouds.gui.get_controller_in_cloud')
+        self.mock_gcc = self.gcc_patcher.start()
 
     def tearDown(self):
         self.controllers_patcher.stop()
         self.utils_patcher.stop()
         self.render_patcher.stop()
         self.app_patcher.stop()
-
-    def test_finish(self):
-        "call finish"
-        self.controller.finish(self.cloudname)
+        self.juju_patcher.stop()
+        self.gcc_patcher.stop()
+
+    def test_finish_w_controller(self):
+        "clouds.finish with an existing controller"
+        self.mock_gcc.return_value = 'testcontroller'
+        self.controller.finish('testcloud')
+        self.mock_juju.assert_has_calls([
+            call.switch_controller('testcontroller')])
+
+    def test_finish_no_controller(self):
+        "clouds.finish without existing controller"
+        self.mock_gcc.return_value = None
+        self.controller.finish('testcloud')
+        self.mock_controllers.use.assert_has_calls([
+            call('newcloud'), call().render('testcloud')])
diff --git a/test/test_controllers_clouds_tui.py b/test/test_controllers_clouds_tui.py
index 222b244..44a4c20 100644
--- a/test/test_controllers_clouds_tui.py
+++ b/test/test_controllers_clouds_tui.py
@@ -19,8 +19,8 @@
 
 
 import unittest
-#  from unittest.mock import ANY, call, MagicMock, patch, sentinel
-from unittest.mock import patch, MagicMock
+
+from unittest.mock import call, patch, MagicMock
 
 from conjureup.controllers.clouds.tui import CloudsController
 
@@ -85,13 +85,33 @@ class CloudsTUIFinishTestCase(unittest.TestCase):
             'conjureup.controllers.clouds.tui.app')
         self.mock_app = self.app_patcher.start()
         self.mock_app.ui = MagicMock(name="app.ui")
+        self.juju_patcher = patch(
+            'conjureup.controllers.clouds.tui.juju')
+        self.mock_juju = self.juju_patcher.start()
+
+        self.gcc_patcher = patch(
+            'conjureup.controllers.clouds.tui.get_controller_in_cloud')
+        self.mock_gcc = self.gcc_patcher.start()
 
     def tearDown(self):
         self.controllers_patcher.stop()
         self.utils_patcher.stop()
         self.render_patcher.stop()
         self.app_patcher.stop()
+        self.juju_patcher.stop()
+        self.gcc_patcher.stop()
+
+    def test_finish_w_controller(self):
+        "clouds.finish with an existing controller"
+        self.mock_gcc.return_value = 'testcontroller'
+        self.controller.finish()
+        self.mock_juju.assert_has_calls([
+            call.switch_controller('testcontroller')])
 
-    def test_finish(self):
-        "call finish"
+    def test_finish_no_controller(self):
+        "clouds.finish without existing controller"
+        self.mock_gcc.return_value = None
+        self.mock_app.argv.cloud = 'testcloud'
         self.controller.finish()
+        self.mock_controllers.use.assert_has_calls([
+            call('newcloud'), call().render('testcloud')])
diff --git a/test/test_controllers_deploy_tui.py b/test/test_controllers_deploy_tui.py
index 7606d20..6c8bc6d 100644
--- a/test/test_controllers_deploy_tui.py
+++ b/test/test_controllers_deploy_tui.py
@@ -92,12 +92,22 @@ class DeployTUIFinishTestCase(unittest.TestCase):
             'conjureup.controllers.deploy.tui.app')
         self.mock_app = self.app_patcher.start()
         self.mock_app.ui = MagicMock(name="app.ui")
+        self.juju_patcher = patch(
+            'conjureup.controllers.deploy.tui.juju')
+        self.mock_juju = self.juju_patcher.start()
+        self.mock_juju.JUJU_ASYNC_QUEUE = sentinel.JUJU_ASYNC_QUEUE
+
+        self.concurrent_patcher = patch(
+            'conjureup.controllers.deploy.tui.concurrent')
+        self.mock_concurrent = self.concurrent_patcher.start()
 
     def tearDown(self):
         self.controllers_patcher.stop()
         self.utils_patcher.stop()
         self.render_patcher.stop()
         self.app_patcher.stop()
+        self.juju_patcher.stop()
+        self.concurrent_patcher.stop()
 
     def test_finish(self):
         "call finish"