| import unittest |
| |
| import mock |
| |
| import asynctest |
| |
| from juju.client.jujudata import FileJujuData |
| from juju.model import Model |
| |
| |
| def _make_delta(entity, type_, data=None): |
| from juju.client.client import Delta |
| from juju.delta import get_entity_delta |
| |
| delta = Delta([entity, type_, data]) |
| return get_entity_delta(delta) |
| |
| |
| class TestObserver(unittest.TestCase): |
| def _make_observer(self, *args): |
| from juju.model import _Observer |
| return _Observer(*args) |
| |
| def test_cares_about_id(self): |
| id_ = 'foo' |
| |
| o = self._make_observer( |
| None, None, None, id_, None) |
| |
| delta = _make_delta( |
| 'application', 'change', dict(name=id_)) |
| |
| self.assertTrue(o.cares_about(delta)) |
| |
| def test_cares_about_type(self): |
| type_ = 'application' |
| |
| o = self._make_observer( |
| None, type_, None, None, None) |
| |
| delta = _make_delta( |
| type_, 'change', dict(name='foo')) |
| |
| self.assertTrue(o.cares_about(delta)) |
| |
| def test_cares_about_action(self): |
| action = 'change' |
| |
| o = self._make_observer( |
| None, None, action, None, None) |
| |
| delta = _make_delta( |
| 'application', action, dict(name='foo')) |
| |
| self.assertTrue(o.cares_about(delta)) |
| |
| def test_cares_about_predicate(self): |
| def predicate(delta): |
| return delta.data.get('fizz') == 'bang' |
| |
| o = self._make_observer( |
| None, None, None, None, predicate) |
| |
| delta = _make_delta( |
| 'application', 'change', dict(fizz='bang')) |
| |
| self.assertTrue(o.cares_about(delta)) |
| |
| |
| class TestModelState(unittest.TestCase): |
| def test_apply_delta(self): |
| from juju.model import Model |
| from juju.application import Application |
| |
| model = Model() |
| model._connector = mock.MagicMock() |
| delta = _make_delta('application', 'add', dict(name='foo')) |
| |
| # test add |
| prev, new = model.state.apply_delta(delta) |
| self.assertEqual( |
| len(model.state.state[delta.entity][delta.get_id()]), 1) |
| self.assertIsNone(prev) |
| self.assertIsInstance(new, Application) |
| |
| # test remove |
| delta.type = 'remove' |
| prev, new = model.state.apply_delta(delta) |
| # length of the entity history deque is now 3: |
| # - 1 for the first delta |
| # - 1 for the second delta |
| # - 1 for the None sentinel appended after the 'remove' |
| self.assertEqual( |
| len(model.state.state[delta.entity][delta.get_id()]), 3) |
| self.assertIsInstance(new, Application) |
| # new object is falsy because its data is None |
| self.assertFalse(new) |
| self.assertIsInstance(prev, Application) |
| self.assertTrue(prev) |
| |
| |
| def test_get_series(): |
| from juju.model import Model |
| model = Model() |
| entity = { |
| 'Meta': { |
| 'supported-series': { |
| 'SupportedSeries': [ |
| 'xenial', |
| 'trusty', |
| ], |
| }, |
| }, |
| } |
| assert model._get_series('cs:trusty/ubuntu', entity) == 'trusty' |
| assert model._get_series('xenial/ubuntu', entity) == 'xenial' |
| assert model._get_series('~foo/xenial/ubuntu', entity) == 'xenial' |
| assert model._get_series('~foo/ubuntu', entity) == 'xenial' |
| assert model._get_series('ubuntu', entity) == 'xenial' |
| assert model._get_series('cs:ubuntu', entity) == 'xenial' |
| |
| |
| class TestContextManager(asynctest.TestCase): |
| @asynctest.patch('juju.model.Model.disconnect') |
| @asynctest.patch('juju.model.Model.connect') |
| async def test_normal_use(self, mock_connect, mock_disconnect): |
| from juju.model import Model |
| |
| async with Model() as model: |
| self.assertTrue(isinstance(model, Model)) |
| |
| self.assertTrue(mock_connect.called) |
| self.assertTrue(mock_disconnect.called) |
| |
| @asynctest.patch('juju.model.Model.disconnect') |
| @asynctest.patch('juju.model.Model.connect') |
| async def test_exception(self, mock_connect, mock_disconnect): |
| from juju.model import Model |
| |
| class SomeException(Exception): |
| pass |
| |
| with self.assertRaises(SomeException): |
| async with Model(): |
| raise SomeException() |
| |
| self.assertTrue(mock_connect.called) |
| self.assertTrue(mock_disconnect.called) |
| |
| async def test_no_current_connection(self): |
| from juju.model import Model |
| from juju.errors import JujuConnectionError |
| |
| class NoControllerJujuData(FileJujuData): |
| def current_controller(self): |
| return "" |
| |
| with self.assertRaises(JujuConnectionError): |
| async with Model(jujudata=NoControllerJujuData()): |
| pass |
| |
| |
| @asynctest.patch('juju.model.Model._after_connect') |
| class TestModelConnect(asynctest.TestCase): |
| @asynctest.patch('juju.client.connector.Connector.connect_model') |
| async def test_no_args(self, mock_connect_model, _): |
| m = Model() |
| await m.connect() |
| mock_connect_model.assert_called_once_with(None) |
| |
| @asynctest.patch('juju.client.connector.Connector.connect_model') |
| async def test_with_model_name(self, mock_connect_model, _): |
| m = Model() |
| await m.connect(model_name='foo') |
| mock_connect_model.assert_called_once_with('foo') |
| |
| @asynctest.patch('juju.client.connector.Connector.connect_model') |
| async def test_with_endpoint_but_no_uuid(self, mock_connect_model, _): |
| m = Model() |
| with self.assertRaises(TypeError): |
| await m.connect(endpoint='0.1.2.3:4566') |
| self.assertEqual(mock_connect_model.call_count, 0) |
| |
| @asynctest.patch('juju.client.connector.Connector.connect') |
| async def test_with_endpoint_and_uuid_no_auth(self, mock_connect, _): |
| m = Model() |
| with self.assertRaises(TypeError): |
| await m.connect(endpoint='0.1.2.3:4566', uuid='some-uuid') |
| self.assertEqual(mock_connect.call_count, 0) |
| |
| @asynctest.patch('juju.client.connector.Connector.connect') |
| async def test_with_endpoint_and_uuid_with_userpass(self, mock_connect, _): |
| m = Model() |
| with self.assertRaises(TypeError): |
| await m.connect(endpoint='0.1.2.3:4566', |
| uuid='some-uuid', |
| username='user') |
| await m.connect(endpoint='0.1.2.3:4566', |
| uuid='some-uuid', |
| username='user', |
| password='pass') |
| mock_connect.assert_called_once_with(endpoint='0.1.2.3:4566', |
| uuid='some-uuid', |
| username='user', |
| password='pass') |
| |
| @asynctest.patch('juju.client.connector.Connector.connect') |
| async def test_with_endpoint_and_uuid_with_bakery(self, mock_connect, _): |
| m = Model() |
| await m.connect(endpoint='0.1.2.3:4566', |
| uuid='some-uuid', |
| bakery_client='bakery') |
| mock_connect.assert_called_once_with(endpoint='0.1.2.3:4566', |
| uuid='some-uuid', |
| bakery_client='bakery') |
| |
| @asynctest.patch('juju.client.connector.Connector.connect') |
| async def test_with_endpoint_and_uuid_with_macaroon(self, mock_connect, _): |
| m = Model() |
| with self.assertRaises(TypeError): |
| await m.connect(endpoint='0.1.2.3:4566', |
| uuid='some-uuid', |
| username='user') |
| await m.connect(endpoint='0.1.2.3:4566', |
| uuid='some-uuid', |
| macaroons=['macaroon']) |
| mock_connect.assert_called_with(endpoint='0.1.2.3:4566', |
| uuid='some-uuid', |
| macaroons=['macaroon']) |
| await m.connect(endpoint='0.1.2.3:4566', |
| uuid='some-uuid', |
| bakery_client='bakery', |
| macaroons=['macaroon']) |
| mock_connect.assert_called_with(endpoint='0.1.2.3:4566', |
| uuid='some-uuid', |
| bakery_client='bakery', |
| macaroons=['macaroon']) |
| |
| @asynctest.patch('juju.client.connector.Connector.connect_model') |
| @asynctest.patch('juju.client.connector.Connector.connect') |
| async def test_with_posargs(self, mock_connect, mock_connect_model, _): |
| m = Model() |
| await m.connect('foo') |
| mock_connect_model.assert_called_once_with('foo') |
| with self.assertRaises(TypeError): |
| await m.connect('endpoint', 'uuid') |
| with self.assertRaises(TypeError): |
| await m.connect('endpoint', 'uuid', 'user') |
| await m.connect('endpoint', 'uuid', 'user', 'pass') |
| mock_connect.assert_called_once_with(endpoint='endpoint', |
| uuid='uuid', |
| username='user', |
| password='pass') |
| await m.connect('endpoint', 'uuid', 'user', 'pass', 'cacert', 'bakery', |
| 'macaroons', 'loop', 'max_frame_size') |
| mock_connect.assert_called_with(endpoint='endpoint', |
| uuid='uuid', |
| username='user', |
| password='pass', |
| cacert='cacert', |
| bakery_client='bakery', |
| macaroons='macaroons', |
| loop='loop', |
| max_frame_size='max_frame_size') |