Add integration tests.
authorTim Van Steenburgh <tvansteenburgh@gmail.com>
Wed, 1 Mar 2017 05:44:06 +0000 (00:44 -0500)
committerTim Van Steenburgh <tvansteenburgh@gmail.com>
Wed, 1 Mar 2017 05:44:06 +0000 (00:44 -0500)
- Covers most of what is demonstrated in the examples/ directory.
- The integration tests are not run by default (with the 'tox' command),
  but they are run by TravisCI.
- Each integration test gets its own clean model in which to run, which
  is destroyed at the end of the test. See tests.base.CleanModel.
- Includes charmstore and local bundle tests (local bundle has machine
  and placement directives).

Fixes #27.

16 files changed:
.travis.yml
examples/config.py
examples/relate.py
tests/bundle/bundle.yaml [new file with mode: 0644]
tests/client/__init__.py [deleted file]
tests/client/test_client.py [deleted file]
tests/client/test_connection.py [deleted file]
tests/functional/__init__.py [deleted file]
tests/functional/test_model.py [deleted file]
tests/integration/__init__.py [new file with mode: 0644]
tests/integration/test_application.py [new file with mode: 0644]
tests/integration/test_client.py [new file with mode: 0644]
tests/integration/test_connection.py [new file with mode: 0644]
tests/integration/test_model.py [new file with mode: 0644]
tests/integration/test_unit.py [new file with mode: 0644]
tox.ini

index b7e82d2..9716fea 100644 (file)
@@ -13,6 +13,6 @@ before_install:
 install: pip install tox-travis
 before_script:
   - sudo -E sudo -u $USER -E bash -c "juju bootstrap localhost test"
-script: tox
+script: tox -e py35,integration
 after_script:
   - sudo -E sudo -u $USER -E bash -c "juju destroy-controller --destroy-all-models -y test"
index 579308b..b823758 100644 (file)
@@ -13,7 +13,8 @@ from juju.model import Model
 
 log = logging.getLogger(__name__)
 
-MB = 1024 * 1024
+MB = 1
+
 
 async def run():
     model = Model()
index 01cee37..3aa9c5f 100644 (file)
@@ -78,7 +78,8 @@ async def run():
         application_name='nrpe',
         series='trusty',
         channel='stable',
-        num_units=1,
+        # subordinates must be deployed without units
+        num_units=0,
     )
     my_relation = await model.add_relation(
         'ubuntu',
diff --git a/tests/bundle/bundle.yaml b/tests/bundle/bundle.yaml
new file mode 100644 (file)
index 0000000..19a45ec
--- /dev/null
@@ -0,0 +1,28 @@
+series: xenial
+services:
+  wordpress:
+    charm: "cs:trusty/wordpress-2"
+    num_units: 1
+    annotations:
+      "gui-x": "339.5"
+      "gui-y": "-171"
+    to:
+      - "0"
+  mysql:
+    charm: "cs:trusty/mysql-26"
+    num_units: 1
+    annotations:
+      "gui-x": "79.5"
+      "gui-y": "-142"
+    to:
+      - "1"
+relations:
+  - - "wordpress:db"
+    - "mysql:db"
+machines:
+  "0":
+    series: trusty
+    constraints: "arch=amd64 cores=1 cpu-power=100 mem=1740 root-disk=8192"
+  "1":
+    series: trusty
+    constraints: "arch=amd64 cores=1 cpu-power=100 mem=1740 root-disk=8192"
diff --git a/tests/client/__init__.py b/tests/client/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/client/test_client.py b/tests/client/test_client.py
deleted file mode 100644 (file)
index ca2637f..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-import pytest
-
-from juju.client import client
-
-from .. import base
-
-
-@base.bootstrapped
-@pytest.mark.asyncio
-async def test_user_info(event_loop):
-    async with base.CleanModel() as model:
-        controller_conn = await model.connection.controller()
-
-        um = client.UserManagerFacade()
-        um.connect(controller_conn)
-        result = await um.UserInfo(
-            [client.Entity('user-admin')], True)
-        await controller_conn.close()
-
-        assert isinstance(result, client.UserInfoResults)
-        for r in result.results:
-            assert isinstance(r, client.UserInfoResult)
diff --git a/tests/client/test_connection.py b/tests/client/test_connection.py
deleted file mode 100644 (file)
index 9c61759..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-import pytest
-
-from juju.client.connection import Connection
-from .. import base
-
-
-@base.bootstrapped
-@pytest.mark.asyncio
-async def test_connect_current(event_loop):
-    async with base.CleanModel():
-        conn = await Connection.connect_current()
-
-        assert isinstance(conn, Connection)
-        await conn.close()
diff --git a/tests/functional/__init__.py b/tests/functional/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/functional/test_model.py b/tests/functional/test_model.py
deleted file mode 100644 (file)
index 9ae9204..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-import pytest
-
-from .. import base
-
-MB = 1
-GB = 1024
-
-
-@base.bootstrapped
-@pytest.mark.asyncio
-async def test_add_machine(event_loop):
-    from juju.machine import Machine
-
-    async with base.CleanModel() as model:
-        # add a new default machine
-        machine1 = await model.add_machine()
-
-        # add a machine with constraints, disks, and series
-        machine2 = await model.add_machine(
-            constraints={
-                'mem': 256 * MB,
-            },
-            disks=[{
-                'pool': 'rootfs',
-                'size': 10 * GB,
-                'count': 1,
-            }],
-            series='xenial',
-        )
-
-        # add a lxd container to machine2
-        machine3 = await model.add_machine(
-            'lxd:{}'.format(machine2.id))
-
-        for m in (machine1, machine2, machine3):
-            assert isinstance(m, Machine)
-
-        assert len(model.machines) == 3
-
-        await machine3.destroy(force=True)
-        await machine2.destroy(force=True)
-        res = await machine1.destroy(force=True)
-
-        assert res is None
-        assert len(model.machines) == 0
diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/integration/test_application.py b/tests/integration/test_application.py
new file mode 100644 (file)
index 0000000..1618a5a
--- /dev/null
@@ -0,0 +1,52 @@
+import pytest
+
+from .. import base
+
+MB = 1
+
+
+@base.bootstrapped
+@pytest.mark.asyncio
+async def test_action(event_loop):
+    async with base.CleanModel() as model:
+        ubuntu_app = await model.deploy(
+            'mysql',
+            application_name='mysql',
+            series='trusty',
+            channel='stable',
+            config={
+                'tuning-level': 'safest',
+            },
+            constraints={
+                'mem': 256 * MB,
+            },
+        )
+
+        # update and check app config
+        await ubuntu_app.set_config({'tuning-level': 'fast'})
+        config = await ubuntu_app.get_config()
+        assert config['tuning-level']['value'] == 'fast'
+
+        # update and check app constraints
+        await ubuntu_app.set_constraints({'mem': 512 * MB})
+        constraints = await ubuntu_app.get_constraints()
+        assert constraints['mem'] == 512 * MB
+
+
+@base.bootstrapped
+@pytest.mark.asyncio
+async def test_add_units(event_loop):
+    from juju.unit import Unit
+
+    async with base.CleanModel() as model:
+        app = await model.deploy(
+            'ubuntu-0',
+            application_name='ubuntu',
+            series='trusty',
+            channel='stable',
+        )
+        units = await app.add_units(count=2)
+
+        assert len(units) == 2
+        for unit in units:
+            assert isinstance(unit, Unit)
diff --git a/tests/integration/test_client.py b/tests/integration/test_client.py
new file mode 100644 (file)
index 0000000..ca2637f
--- /dev/null
@@ -0,0 +1,22 @@
+import pytest
+
+from juju.client import client
+
+from .. import base
+
+
+@base.bootstrapped
+@pytest.mark.asyncio
+async def test_user_info(event_loop):
+    async with base.CleanModel() as model:
+        controller_conn = await model.connection.controller()
+
+        um = client.UserManagerFacade()
+        um.connect(controller_conn)
+        result = await um.UserInfo(
+            [client.Entity('user-admin')], True)
+        await controller_conn.close()
+
+        assert isinstance(result, client.UserInfoResults)
+        for r in result.results:
+            assert isinstance(r, client.UserInfoResult)
diff --git a/tests/integration/test_connection.py b/tests/integration/test_connection.py
new file mode 100644 (file)
index 0000000..9c61759
--- /dev/null
@@ -0,0 +1,14 @@
+import pytest
+
+from juju.client.connection import Connection
+from .. import base
+
+
+@base.bootstrapped
+@pytest.mark.asyncio
+async def test_connect_current(event_loop):
+    async with base.CleanModel():
+        conn = await Connection.connect_current()
+
+        assert isinstance(conn, Connection)
+        await conn.close()
diff --git a/tests/integration/test_model.py b/tests/integration/test_model.py
new file mode 100644 (file)
index 0000000..2fe97d0
--- /dev/null
@@ -0,0 +1,97 @@
+import pytest
+
+from .. import base
+
+MB = 1
+GB = 1024
+
+
+@base.bootstrapped
+@pytest.mark.asyncio
+async def test_deploy_local_bundle(event_loop):
+    from pathlib import Path
+    tests_dir = Path(__file__).absolute().parent.parent
+    bundle_path = tests_dir / 'bundle'
+
+    async with base.CleanModel() as model:
+        await model.deploy(str(bundle_path))
+
+        for app in ('wordpress', 'mysql'):
+            assert app in model.applications
+
+
+@base.bootstrapped
+@pytest.mark.asyncio
+async def test_deploy_bundle(event_loop):
+    async with base.CleanModel() as model:
+        await model.deploy('bundle/wiki-simple')
+
+        for app in ('wiki', 'mysql'):
+            assert app in model.applications
+
+
+@base.bootstrapped
+@pytest.mark.asyncio
+async def test_add_machine(event_loop):
+    from juju.machine import Machine
+
+    async with base.CleanModel() as model:
+        # add a new default machine
+        machine1 = await model.add_machine()
+
+        # add a machine with constraints, disks, and series
+        machine2 = await model.add_machine(
+            constraints={
+                'mem': 256 * MB,
+            },
+            disks=[{
+                'pool': 'rootfs',
+                'size': 10 * GB,
+                'count': 1,
+            }],
+            series='xenial',
+        )
+
+        # add a lxd container to machine2
+        machine3 = await model.add_machine(
+            'lxd:{}'.format(machine2.id))
+
+        for m in (machine1, machine2, machine3):
+            assert isinstance(m, Machine)
+
+        assert len(model.machines) == 3
+
+        await machine3.destroy(force=True)
+        await machine2.destroy(force=True)
+        res = await machine1.destroy(force=True)
+
+        assert res is None
+        assert len(model.machines) == 0
+
+
+@base.bootstrapped
+@pytest.mark.asyncio
+async def test_relate(event_loop):
+    from juju.relation import Relation
+
+    async with base.CleanModel() as model:
+        await model.deploy(
+            'ubuntu',
+            application_name='ubuntu',
+            series='trusty',
+            channel='stable',
+        )
+        await model.deploy(
+            'nrpe',
+            application_name='nrpe',
+            series='trusty',
+            channel='stable',
+            # subordinates must be deployed without units
+            num_units=0,
+        )
+        my_relation = await model.add_relation(
+            'ubuntu',
+            'nrpe',
+        )
+
+        assert isinstance(my_relation, Relation)
diff --git a/tests/integration/test_unit.py b/tests/integration/test_unit.py
new file mode 100644 (file)
index 0000000..e9116ce
--- /dev/null
@@ -0,0 +1,46 @@
+import pytest
+
+from .. import base
+
+
+@base.bootstrapped
+@pytest.mark.asyncio
+async def test_run(event_loop):
+    from juju.action import Action
+
+    async with base.CleanModel() as model:
+        app = await model.deploy(
+            'ubuntu-0',
+            application_name='ubuntu',
+            series='trusty',
+            channel='stable',
+        )
+
+        for unit in app.units:
+            action = await unit.run('unit-get public-address')
+            assert isinstance(action, Action)
+            assert 'Stdout' in action.results
+            break
+
+
+@base.bootstrapped
+@pytest.mark.asyncio
+async def test_run_action(event_loop):
+    async def run_action(unit):
+        # unit.run() returns a juju.action.Action instance
+        action = await unit.run_action('add-repo', repo='myrepo')
+        # wait for the action to complete
+        return await action.wait()
+
+    async with base.CleanModel() as model:
+        app = await model.deploy(
+            'git',
+            application_name='git',
+            series='trusty',
+            channel='stable',
+        )
+
+        for unit in app.units:
+            action = await run_action(unit)
+            assert action.results == {'dir': '/var/git/myrepo.git'}
+            break
diff --git a/tox.ini b/tox.ini
index b4fec3d..8c4b0cd 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -11,9 +11,15 @@ skipsdist=True
 usedevelop=True
 passenv =
     HOME
-commands = py.test -ra -s -x -n auto
 deps =
     pytest
     pytest-asyncio
     pytest-xdist
     mock
+
+[testenv:py35]
+# default tox env excludes integration tests
+commands = py.test -ra -s -x -n auto -k 'not integration'
+
+[testenv:integration]
+commands = py.test -ra -s -x -n auto