diff --git a/test/test_charm.py b/test/test_charm.py index b471b37f0..47121422b 100644 --- a/test/test_charm.py +++ b/test/test_charm.py @@ -20,6 +20,7 @@ import unittest from pathlib import Path +import pytest import yaml import ops @@ -83,16 +84,16 @@ def _on_start(self, event: ops.EventBase): self.started = True events: typing.List[str] = list(MyCharm.on.events()) # type: ignore - self.assertIn('install', events) - self.assertIn('custom', events) + assert 'install' in events + assert 'custom' in events framework = self.create_framework() charm = MyCharm(framework) charm.on.start.emit() - self.assertEqual(charm.started, True) + assert charm.started - with self.assertRaises(TypeError): + with pytest.raises(TypeError): framework.observe(charm.on.start, charm) # type: ignore def test_observe_decorated_method(self): @@ -127,9 +128,9 @@ def _on_start(self, event: ops.EventBase): charm = MyCharm(framework) charm.on.start.emit() # check that the event has been seen by the decorator - self.assertEqual(1, len(events)) + assert len(events) == 1 # check that the event has been seen by the observer - self.assertIsInstance(charm.seen, ops.StartEvent) + assert isinstance(charm.seen, ops.StartEvent) def test_observer_not_referenced_warning(self): class MyObj(ops.Object): @@ -157,7 +158,7 @@ def _on_start(self, _: ops.StartEvent): def test_empty_action(self): meta = ops.CharmMeta.from_yaml('name: my-charm', '') - self.assertEqual(meta.actions, {}) + assert meta.actions == {} def test_helper_properties(self): framework = self.create_framework() @@ -166,11 +167,11 @@ class MyCharm(ops.CharmBase): pass charm = MyCharm(framework) - self.assertEqual(charm.app, framework.model.app) - self.assertEqual(charm.unit, framework.model.unit) - self.assertEqual(charm.meta, framework.meta) - self.assertEqual(charm.charm_dir, framework.charm_dir) - self.assertIs(charm.config, framework.model.config) + assert charm.app == framework.model.app + assert charm.unit == framework.model.unit + assert charm.meta == framework.meta + assert charm.charm_dir == framework.charm_dir + assert charm.config is framework.model.config def test_relation_events(self): @@ -213,7 +214,7 @@ def on_any_relation(self, event: ops.RelationEvent): charm = MyCharm(self.create_framework()) - self.assertIn('pro_2_relation_broken', repr(charm.on)) + assert 'pro_2_relation_broken' in repr(charm.on) rel = charm.framework.model.get_relation('req1', 1) app = charm.framework.model.get_app('remote') @@ -227,7 +228,7 @@ def on_any_relation(self, event: ops.RelationEvent): charm.on['peer1'].relation_broken.emit(rel, app) charm.on['peer-2'].relation_broken.emit(rel, app) - self.assertEqual(charm.seen, [ + assert charm.seen == [ 'RelationJoinedEvent', 'RelationChangedEvent', 'RelationChangedEvent', @@ -236,11 +237,9 @@ def on_any_relation(self, event: ops.RelationEvent): 'RelationDepartedEvent', 'RelationBrokenEvent', 'RelationBrokenEvent', - ]) + ] def test_storage_events(self): - this = self - class MyCharm(ops.CharmBase): def __init__(self, *args: typing.Any): super().__init__(*args) @@ -252,7 +251,7 @@ def __init__(self, *args: typing.Any): def _on_stor1_attach(self, event: ops.StorageAttachedEvent): self.seen.append(type(event).__name__) - this.assertEqual(event.storage.location, Path("/var/srv/stor1/0")) + assert event.storage.location == Path("/var/srv/stor1/0") def _on_stor2_detach(self, event: ops.StorageDetachingEvent): self.seen.append(type(event).__name__) @@ -332,11 +331,11 @@ def _on_stor4_attach(self, event: ops.StorageAttachedEvent): """, ) - self.assertIsNone(self.meta.storages['stor1'].multiple_range) - self.assertEqual(self.meta.storages['stor2'].multiple_range, (2, 2)) - self.assertEqual(self.meta.storages['stor3'].multiple_range, (2, None)) - self.assertEqual(self.meta.storages['stor-4'].multiple_range, (2, 4)) - self.assertEqual(self.meta.storages['stor-plus'].multiple_range, (10, None)) + assert self.meta.storages['stor1'].multiple_range is None + assert self.meta.storages['stor2'].multiple_range == (2, 2) + assert self.meta.storages['stor3'].multiple_range == (2, None) + assert self.meta.storages['stor-4'].multiple_range == (2, 4) + assert self.meta.storages['stor-plus'].multiple_range == (10, None) charm = MyCharm(self.create_framework()) @@ -347,12 +346,12 @@ def _on_stor4_attach(self, event: ops.StorageAttachedEvent): charm.on['stor-multiple-dashes'].storage_attached.emit( ops.Storage("stor-multiple-dashes", 0, charm.model._backend)) - self.assertEqual(charm.seen, [ + assert charm.seen == [ 'StorageAttachedEvent', 'StorageDetachingEvent', 'StorageAttachedEvent', 'StorageAttachedEvent', - ]) + ] def test_workload_events(self): @@ -386,8 +385,8 @@ def on_any_pebble_custom_notice(self, event: ops.PebbleCustomNoticeEvent): charm = MyCharm(self.create_framework()) - self.assertIn('container_a_pebble_ready', repr(charm.on)) - self.assertIn('containerb_pebble_ready', repr(charm.on)) + assert 'container_a_pebble_ready' in repr(charm.on) + assert 'containerb_pebble_ready' in repr(charm.on) charm.on['container-a'].pebble_ready.emit( charm.framework.model.unit.get_container('container-a')) @@ -399,12 +398,12 @@ def on_any_pebble_custom_notice(self, event: ops.PebbleCustomNoticeEvent): charm.on['containerb'].pebble_custom_notice.emit( charm.framework.model.unit.get_container('containerb'), '2', 'custom', 'y') - self.assertEqual(charm.seen, [ + assert charm.seen == [ 'PebbleReadyEvent', 'PebbleReadyEvent', 'PebbleCustomNoticeEvent', 'PebbleCustomNoticeEvent', - ]) + ] def test_relations_meta(self): # language=YAML @@ -420,18 +419,18 @@ def test_relations_meta(self): optional: true ''') - self.assertEqual(self.meta.requires['database'].interface_name, 'mongodb') - self.assertEqual(self.meta.requires['database'].limit, 1) - self.assertEqual(self.meta.requires['database'].scope, 'container') - self.assertFalse(self.meta.requires['database'].optional) + assert self.meta.requires['database'].interface_name == 'mongodb' + assert self.meta.requires['database'].limit == 1 + assert self.meta.requires['database'].scope == 'container' + assert not self.meta.requires['database'].optional - self.assertEqual(self.meta.requires['metrics'].interface_name, 'prometheus-scraping') - self.assertIsNone(self.meta.requires['metrics'].limit) - self.assertEqual(self.meta.requires['metrics'].scope, 'global') # Default value - self.assertTrue(self.meta.requires['metrics'].optional) + assert self.meta.requires['metrics'].interface_name == 'prometheus-scraping' + assert self.meta.requires['metrics'].limit is None + assert self.meta.requires['metrics'].scope == 'global' # Default value + assert self.meta.requires['metrics'].optional def test_relations_meta_limit_type_validation(self): - with self.assertRaisesRegex(TypeError, "limit should be an int, not "): + with pytest.raises(TypeError, match=r"limit should be an int, not "): # language=YAML self.meta = ops.CharmMeta.from_yaml(''' name: my-charm @@ -442,8 +441,10 @@ def test_relations_meta_limit_type_validation(self): ''') def test_relations_meta_scope_type_validation(self): - with self.assertRaisesRegex(TypeError, - "scope should be one of 'global', 'container'; not 'foobar'"): + with pytest.raises( + TypeError, + match="scope should be one of 'global', 'container'; not 'foobar'" + ): # language=YAML self.meta = ops.CharmMeta.from_yaml(''' name: my-charm @@ -487,8 +488,8 @@ def test_meta_from_charm_root(self): {"interface": "bar"} }})) meta = ops.CharmMeta.from_charm_root(td) - self.assertEqual(meta.name, "bob") - self.assertEqual(meta.requires['foo'].interface_name, "bar") + assert meta.name == "bob" + assert meta.requires['foo'].interface_name == "bar" def test_actions_from_charm_root(self): with tempfile.TemporaryDirectory() as d: @@ -510,10 +511,10 @@ def test_actions_from_charm_root(self): }})) meta = ops.CharmMeta.from_charm_root(td) - self.assertEqual(meta.name, "bob") - self.assertEqual(meta.requires['foo'].interface_name, "bar") - self.assertFalse(meta.actions['foo'].additional_properties) - self.assertEqual(meta.actions['foo'].description, "foos the bar") + assert meta.name == "bob" + assert meta.requires['foo'].interface_name == "bar" + assert not meta.actions['foo'].additional_properties + assert meta.actions['foo'].description == "foos the bar" def _setup_test_action(self): fake_script(self, 'action-get', """echo '{"foo-name": "name", "silent": true}'""") @@ -545,18 +546,18 @@ def _on_start_action(self, event: ops.ActionEvent): charm = MyCharm(framework) events: typing.List[str] = list(MyCharm.on.events()) # type: ignore - self.assertIn('foo_bar_action', events) - self.assertIn('start_action', events) + assert 'foo_bar_action' in events + assert 'start_action' in events action_id = "1234" charm.on.foo_bar_action.emit(id=action_id) - self.assertEqual(charm.seen_action_params, {"foo-name": "name", "silent": True}) - self.assertEqual(fake_script_calls(self), [ + assert charm.seen_action_params == {"foo-name": "name", "silent": True} + assert fake_script_calls(self) == [ ['action-get', '--format=json'], ['action-log', "test-log"], ['action-set', "res=val with spaces", f"id={action_id}"], ['action-fail', "test-fail"], - ]) + ] def test_invalid_action_results(self): @@ -582,7 +583,7 @@ def _on_foo_bar_action(self, event: ops.ActionEvent): {'aBc': 'd'}): charm.res = bad_res - with self.assertRaises(ValueError): + with pytest.raises(ValueError): charm.on.foo_bar_action.emit(id='1') def _test_action_event_defer_fails(self, cmd_type: str): @@ -603,7 +604,7 @@ def _on_start_action(self, event: ops.ActionEvent): framework = self.create_framework() charm = MyCharm(framework) - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): charm.on.start_action.emit(id='2') def test_action_event_defer_fails(self): @@ -618,10 +619,10 @@ def test_containers(self): test2: k: v """) - self.assertIsInstance(meta.containers['test1'], ops.ContainerMeta) - self.assertIsInstance(meta.containers['test2'], ops.ContainerMeta) - self.assertEqual(meta.containers['test1'].name, 'test1') - self.assertEqual(meta.containers['test2'].name, 'test2') + assert isinstance(meta.containers['test1'], ops.ContainerMeta) + assert isinstance(meta.containers['test2'], ops.ContainerMeta) + assert meta.containers['test1'].name == 'test1' + assert meta.containers['test2'].name == 'test2' def test_containers_storage(self): meta = ops.CharmMeta.from_yaml(""" @@ -654,22 +655,22 @@ def test_containers_storage(self): architectures: - arm """) - self.assertIsInstance(meta.containers['test1'], ops.ContainerMeta) - self.assertIsInstance(meta.containers['test1'].mounts["data"], ops.ContainerStorageMeta) - self.assertEqual(meta.containers['test1'].mounts["data"].location, '/test/storagemount') - self.assertEqual(meta.containers['test1'].mounts["other"].location, '/test/otherdata') - self.assertEqual(meta.storages['other'].properties, ['transient']) - self.assertEqual(meta.containers['test1'].resource, 'ubuntu-22.10') + assert isinstance(meta.containers['test1'], ops.ContainerMeta) + assert isinstance(meta.containers['test1'].mounts["data"], ops.ContainerStorageMeta) + assert meta.containers['test1'].mounts["data"].location == '/test/storagemount' + assert meta.containers['test1'].mounts["other"].location == '/test/otherdata' + assert meta.storages['other'].properties == ['transient'] + assert meta.containers['test1'].resource == 'ubuntu-22.10' assert meta.containers['test2'].bases is not None - self.assertEqual(len(meta.containers['test2'].bases), 2) - self.assertEqual(meta.containers['test2'].bases[0].os_name, 'ubuntu') - self.assertEqual(meta.containers['test2'].bases[0].channel, '23.10') - self.assertEqual(meta.containers['test2'].bases[0].architectures, ['amd64']) - self.assertEqual(meta.containers['test2'].bases[1].os_name, 'ubuntu') - self.assertEqual(meta.containers['test2'].bases[1].channel, '23.04/stable/fips') - self.assertEqual(meta.containers['test2'].bases[1].architectures, ['arm']) + assert len(meta.containers['test2'].bases) == 2 + assert meta.containers['test2'].bases[0].os_name == 'ubuntu' + assert meta.containers['test2'].bases[0].channel == '23.10' + assert meta.containers['test2'].bases[0].architectures == ['amd64'] + assert meta.containers['test2'].bases[1].os_name == 'ubuntu' + assert meta.containers['test2'].bases[1].channel == '23.04/stable/fips' + assert meta.containers['test2'].bases[1].architectures == ['arm'] # It's an error to specify both the 'resource' and the 'bases' fields. - with self.assertRaises(ModelError): + with pytest.raises(ModelError): ops.CharmMeta.from_yaml(""" name: invalid-charm containers: @@ -696,14 +697,13 @@ def test_containers_storage_multiple_mounts(self): - storage: data location: /test/otherdata """) - self.assertIsInstance(meta.containers['test1'], ops.ContainerMeta) - self.assertIsInstance(meta.containers['test1'].mounts["data"], ops.ContainerStorageMeta) - self.assertEqual( - meta.containers['test1'].mounts["data"].locations[0], - '/test/storagemount') - self.assertEqual(meta.containers['test1'].mounts["data"].locations[1], '/test/otherdata') - - with self.assertRaises(RuntimeError): + assert isinstance(meta.containers['test1'], ops.ContainerMeta) + assert isinstance(meta.containers['test1'].mounts["data"], ops.ContainerStorageMeta) + assert meta.containers['test1'].mounts["data"].locations[0] == \ + '/test/storagemount' + assert meta.containers['test1'].mounts["data"].locations[1] == '/test/otherdata' + + with pytest.raises(RuntimeError): meta.containers["test1"].mounts["data"].location def test_secret_events(self): @@ -746,12 +746,12 @@ def on_secret_expired(self, event: ops.SecretExpiredEvent): charm.on.secret_remove.emit('secret:remove', 'rem', 7) charm.on.secret_expired.emit('secret:expired', 'exp', 42) - self.assertEqual(charm.seen, [ + assert charm.seen == [ 'SecretChangedEvent', 'SecretRotateEvent', 'SecretRemoveEvent', 'SecretExpiredEvent', - ]) + ] def test_collect_app_status_leader(self): class MyCharm(ops.CharmBase): @@ -771,10 +771,10 @@ def _on_collect_status(self, event: ops.CollectStatusEvent): charm = MyCharm(self.create_framework()) ops.charm._evaluate_status(charm) - self.assertEqual(fake_script_calls(self, True), [ + assert fake_script_calls(self, True) == [ ['is-leader', '--format=json'], ['status-set', '--application=True', 'blocked', 'first'], - ]) + ] def test_collect_app_status_no_statuses(self): class MyCharm(ops.CharmBase): @@ -790,9 +790,9 @@ def _on_collect_status(self, event: ops.CollectStatusEvent): charm = MyCharm(self.create_framework()) ops.charm._evaluate_status(charm) - self.assertEqual(fake_script_calls(self, True), [ + assert fake_script_calls(self, True) == [ ['is-leader', '--format=json'], - ]) + ] def test_collect_app_status_non_leader(self): class MyCharm(ops.CharmBase): @@ -808,9 +808,9 @@ def _on_collect_status(self, event: ops.CollectStatusEvent): charm = MyCharm(self.create_framework()) ops.charm._evaluate_status(charm) - self.assertEqual(fake_script_calls(self, True), [ + assert fake_script_calls(self, True) == [ ['is-leader', '--format=json'], - ]) + ] def test_collect_unit_status(self): class MyCharm(ops.CharmBase): @@ -830,10 +830,10 @@ def _on_collect_status(self, event: ops.CollectStatusEvent): charm = MyCharm(self.create_framework()) ops.charm._evaluate_status(charm) - self.assertEqual(fake_script_calls(self, True), [ + assert fake_script_calls(self, True) == [ ['is-leader', '--format=json'], ['status-set', '--application=False', 'blocked', 'first'], - ]) + ] def test_collect_unit_status_no_statuses(self): class MyCharm(ops.CharmBase): @@ -849,9 +849,9 @@ def _on_collect_status(self, event: ops.CollectStatusEvent): charm = MyCharm(self.create_framework()) ops.charm._evaluate_status(charm) - self.assertEqual(fake_script_calls(self, True), [ + assert fake_script_calls(self, True) == [ ['is-leader', '--format=json'], - ]) + ] def test_collect_app_and_unit_status(self): class MyCharm(ops.CharmBase): @@ -872,11 +872,11 @@ def _on_collect_unit_status(self, event: ops.CollectStatusEvent): charm = MyCharm(self.create_framework()) ops.charm._evaluate_status(charm) - self.assertEqual(fake_script_calls(self, True), [ + assert fake_script_calls(self, True) == [ ['is-leader', '--format=json'], ['status-set', '--application=True', 'active', ''], ['status-set', '--application=False', 'waiting', 'blah'], - ]) + ] def test_add_status_type_error(self): class MyCharm(ops.CharmBase): @@ -890,7 +890,7 @@ def _on_collect_status(self, event: ops.CollectStatusEvent): fake_script(self, 'is-leader', 'echo true') charm = MyCharm(self.create_framework()) - with self.assertRaises(TypeError): + with pytest.raises(TypeError): ops.charm._evaluate_status(charm) def test_collect_status_priority(self): @@ -927,14 +927,14 @@ def _on_collect_status(self, event: ops.CollectStatusEvent): status_set_calls = [call for call in fake_script_calls(self, True) if call[0] == 'status-set'] - self.assertEqual(status_set_calls, [ + assert status_set_calls == [ ['status-set', '--application=True', 'error', ''], ['status-set', '--application=True', 'blocked', ''], ['status-set', '--application=True', 'maintenance', ''], ['status-set', '--application=True', 'waiting', ''], ['status-set', '--application=True', 'active', ''], ['status-set', '--application=True', 'unknown', ''], - ]) + ] class TestCharmMeta(unittest.TestCase): @@ -947,10 +947,10 @@ def test_links(self): issues: https://bugs.example.com docs: https://docs.example.com """) - self.assertEqual(meta.links.websites, ['https://example.com']) - self.assertEqual(meta.links.sources, ['https://git.example.com']) - self.assertEqual(meta.links.issues, ['https://bugs.example.com']) - self.assertEqual(meta.links.documentation, 'https://docs.example.com') + assert meta.links.websites == ['https://example.com'] + assert meta.links.sources == ['https://git.example.com'] + assert meta.links.issues == ['https://bugs.example.com'] + assert meta.links.documentation == 'https://docs.example.com' # Other than documentation, they can also all be lists of items. meta = ops.CharmMeta.from_yaml(""" name: my-charm @@ -964,13 +964,11 @@ def test_links(self): - https://bugs.example.com - https://features.example.com """) - self.assertEqual(meta.links.websites, ['https://example.com', 'https://example.org']) - self.assertEqual( - meta.links.sources, [ - 'https://git.example.com', 'https://bzr.example.com']) - self.assertEqual( - meta.links.issues, [ - 'https://bugs.example.com', 'https://features.example.com']) + assert meta.links.websites == ['https://example.com', 'https://example.org'] + assert meta.links.sources == [ + 'https://git.example.com', 'https://bzr.example.com'] + assert meta.links.issues == [ + 'https://bugs.example.com', 'https://features.example.com'] def test_links_charmcraft_yaml(self): meta = ops.CharmMeta.from_yaml(""" @@ -984,24 +982,24 @@ def test_links_charmcraft_yaml(self): - https://example.com/ contact: Support Team """) - self.assertEqual(meta.links.websites, ['https://example.com/']) - self.assertEqual(meta.links.sources, ['https://git.example.com/issues/']) - self.assertEqual(meta.links.issues, ['https://git.example.com/']) - self.assertEqual(meta.links.documentation, 'https://discourse.example.com/') - self.assertEqual(meta.maintainers, ['Support Team ']) + assert meta.links.websites == ['https://example.com/'] + assert meta.links.sources == ['https://git.example.com/issues/'] + assert meta.links.issues == ['https://git.example.com/'] + assert meta.links.documentation == 'https://discourse.example.com/' + assert meta.maintainers == ['Support Team '] def test_assumes(self): meta = ops.CharmMeta.from_yaml(""" assumes: - juju """) - self.assertEqual(meta.assumes.features, ['juju']) + assert meta.assumes.features == ['juju'] meta = ops.CharmMeta.from_yaml(""" assumes: - juju > 3 - k8s-api """) - self.assertEqual(meta.assumes.features, ['juju > 3', 'k8s-api']) + assert meta.assumes.features == ['juju > 3', 'k8s-api'] meta = ops.CharmMeta.from_yaml(""" assumes: - k8s-api @@ -1013,11 +1011,11 @@ def test_assumes(self): - juju >= 3.1.5 - juju < 4 """) - self.assertEqual(meta.assumes.features, [ + assert meta.assumes.features == [ 'k8s-api', ops.JujuAssumes( [ops.JujuAssumes(['juju >= 2.9.44', 'juju < 3']), ops.JujuAssumes(['juju >= 3.1.5', 'juju < 4'])], ops.JujuAssumesCondition.ANY ), - ]) + ] diff --git a/test/test_framework.py b/test/test_framework.py index bea874520..9ca169892 100644 --- a/test/test_framework.py +++ b/test/test_framework.py @@ -27,6 +27,8 @@ from test.test_helpers import BaseTestCase, fake_script from unittest.mock import patch +import pytest + import ops from ops.framework import _BREAKPOINT_WELCOME_MESSAGE, _event_regex from ops.storage import JujuStorage, NoSnapshotError, SQLiteStorage @@ -46,10 +48,9 @@ def test_deprecated_init(self): # For 0.7, this still works, but it is deprecated. with self.assertLogs(level="WARNING") as cm: framework = ops.Framework(':memory:', None, None, None) # type: ignore - self.assertIn( - "WARNING:ops.framework:deprecated: Framework now takes a Storage not a path", - cm.output) - self.assertIsInstance(framework._storage, SQLiteStorage) + assert "WARNING:ops.framework:deprecated: Framework now takes a Storage not a path" in \ + cm.output + assert isinstance(framework._storage, SQLiteStorage) def test_handle_path(self): cases = [ @@ -59,18 +60,18 @@ def test_handle_path(self): (ops.Handle(ops.Handle(None, "root", "1"), "child", "2"), "root[1]/child[2]"), ] for handle, path in cases: - self.assertEqual(str(handle), path) - self.assertEqual(ops.Handle.from_path(path), handle) + assert str(handle) == path + assert ops.Handle.from_path(path) == handle def test_handle_attrs_readonly(self): handle = ops.Handle(None, 'kind', 'key') - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): handle.parent = 'foo' # type: ignore - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): handle.kind = 'foo' # type: ignore - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): handle.key = 'foo' # type: ignore - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): handle.path = 'foo' # type: ignore def test_restore_unknown(self): @@ -86,8 +87,8 @@ class Foo(ops.Object): try: framework.load_snapshot(handle) except NoSnapshotError as e: - self.assertEqual(e.handle_path, str(handle)) - self.assertEqual(str(e), "no snapshot data found for a_foo[some_key] object") + assert e.handle_path == str(handle) + assert str(e) == "no snapshot data found for a_foo[some_key] object" else: self.fail("exception NoSnapshotError not raised") @@ -118,14 +119,14 @@ def restore(self, snapshot: typing.Dict[str, int]): framework2.register_type(Foo, None, handle.kind) event2 = framework2.load_snapshot(handle) event2 = typing.cast(Foo, event2) - self.assertEqual(event2.my_n, 2) + assert event2.my_n == 2 framework2.save_snapshot(event2) # type: ignore del event2 gc.collect() event3 = framework2.load_snapshot(handle) event3 = typing.cast(Foo, event3) - self.assertEqual(event3.my_n, 3) + assert event3.my_n == 3 framework2.drop_snapshot(event.handle) framework2.commit() @@ -134,7 +135,7 @@ def restore(self, snapshot: typing.Dict[str, int]): framework3 = self.create_framework(tmpdir=self.tmpdir) framework3.register_type(Foo, None, handle.kind) - self.assertRaises(NoSnapshotError, framework3.load_snapshot, handle) + pytest.raises(NoSnapshotError, framework3.load_snapshot, handle) def test_simple_event_observer(self): framework = self.create_framework() @@ -167,17 +168,17 @@ def on_foo(self, event: ops.EventBase): framework.observe(pub.foo, obs.on_any) framework.observe(pub.bar, obs.on_any) - with self.assertRaisesRegex(TypeError, "^Framework.observe requires a method"): + with pytest.raises(TypeError, match="^Framework.observe requires a method"): framework.observe(pub.baz, obs) # type: ignore pub.foo.emit() pub.bar.emit() - self.assertEqual(obs.seen, ["on_any:foo", "on_any:bar"]) - self.assertEqual(obs.reprs, [ + assert obs.seen == ["on_any:foo", "on_any:bar"] + assert obs.reprs == [ "", "", - ]) + ] def test_event_observer_more_args(self): framework = self.create_framework() @@ -226,13 +227,13 @@ def on_qux(self, event: ops.EventBase, *args, **kwargs): # type: ignore pub.baz.emit() pub.qux.emit() - self.assertEqual(obs.seen, ['on_foo:foo', 'on_bar:bar', 'on_baz:baz', 'on_qux:qux']) - self.assertEqual(obs.reprs, [ + assert obs.seen == ['on_foo:foo', 'on_bar:bar', 'on_baz:baz', 'on_qux:qux'] + assert obs.reprs == [ "", "", "", "", - ]) + ] def test_bad_sig_observer(self): @@ -266,11 +267,11 @@ def _on_qux(self, event: ops.EventBase, extra: typing.Optional[typing.Any] = Non pub = MyNotifier(framework, "pub") obs = MyObserver(framework, "obs") - with self.assertRaisesRegex(TypeError, "only 'self' and the 'event'"): + with pytest.raises(TypeError, match="only 'self' and the 'event'"): framework.observe(pub.foo, obs._on_foo) # type: ignore - with self.assertRaisesRegex(TypeError, "only 'self' and the 'event'"): + with pytest.raises(TypeError, match="only 'self' and the 'event'"): framework.observe(pub.bar, obs._on_bar) # type: ignore - with self.assertRaisesRegex(TypeError, "only 'self' and the 'event'"): + with pytest.raises(TypeError, match="only 'self' and the 'event'"): framework.observe(pub.baz, obs._on_baz) # type: ignore framework.observe(pub.qux, obs._on_qux) @@ -304,19 +305,19 @@ def on_commit(self, event: ops.CommitEvent): framework.commit() - self.assertEqual(obs._stored.myinitdata, 41) # type: ignore - self.assertEqual(obs._stored.mydata, 42) # type: ignore - self.assertTrue(obs.seen, [ops.PreCommitEvent, ops.CommitEvent]) + assert obs._stored.myinitdata == 41 # type: ignore + assert obs._stored.mydata == 42 # type: ignore + assert obs.seen, [ops.PreCommitEvent, ops.CommitEvent] framework.close() other_framework = self.create_framework(tmpdir=self.tmpdir) new_obs = PreCommitObserver(other_framework, None) - self.assertEqual(obs._stored.myinitdata, 41) # type: ignore - self.assertEqual(new_obs._stored.mydata, 42) # type: ignore + assert obs._stored.myinitdata == 41 # type: ignore + assert new_obs._stored.mydata == 42 # type: ignore - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): new_obs._stored.myotherdata # type: ignore def test_defer_and_reemit(self): @@ -381,13 +382,13 @@ def on_any(self, event: ops.EventBase): framework.reemit() framework.reemit() - self.assertEqual(" ".join(obs1.seen), "a b a b a b b b") - self.assertEqual(" ".join(obs2.seen), "a b c a b c a b c a c a c c") + assert " ".join(obs1.seen) == "a b a b a b b b" + assert " ".join(obs2.seen) == "a b c a b c a b c a c a c c" # Now the event objects must all be gone from storage. - self.assertRaises(NoSnapshotError, framework.load_snapshot, ev_a_handle) - self.assertRaises(NoSnapshotError, framework.load_snapshot, ev_b_handle) - self.assertRaises(NoSnapshotError, framework.load_snapshot, ev_c_handle) + pytest.raises(NoSnapshotError, framework.load_snapshot, ev_a_handle) + pytest.raises(NoSnapshotError, framework.load_snapshot, ev_b_handle) + pytest.raises(NoSnapshotError, framework.load_snapshot, ev_c_handle) def test_custom_event_data(self): framework = self.create_framework() @@ -433,7 +434,7 @@ def _on_foo(self, event: MyEvent): # from the one modified during the first restore (otherwise # we'd get a foo=3). # - self.assertEqual(obs.seen, ["on_foo:foo=2", "on_foo:foo=2"]) + assert obs.seen == ["on_foo:foo=2", "on_foo:foo=2"] def test_weak_observer(self): framework = self.create_framework() @@ -458,13 +459,13 @@ def _on_foo(self, event: ops.EventBase): framework.observe(pub.on.foo, obs._on_foo) pub.on.foo.emit() - self.assertEqual(observed_events, ["foo"]) + assert observed_events == ["foo"] # Now delete the observer, and note that when we emit the event, it # doesn't update the local slice again del obs gc.collect() pub.on.foo.emit() - self.assertEqual(observed_events, ["foo"]) + assert observed_events == ["foo"] def test_forget_and_multiple_objects(self): framework = self.create_framework() @@ -478,22 +479,22 @@ def restore(self, snapshot: typing.Dict[str, typing.Any]) -> None: o1 = MyObject(framework, "path") # Creating a second object at the same path should fail with RuntimeError - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): o2 = MyObject(framework, "path") # Unless we _forget the object first framework._forget(o1) o2 = MyObject(framework, "path") - self.assertEqual(o1.handle.path, o2.handle.path) + assert o1.handle.path == o2.handle.path # Deleting the tracked object should also work del o2 gc.collect() o3 = MyObject(framework, "path") - self.assertEqual(o1.handle.path, o3.handle.path) + assert o1.handle.path == o3.handle.path framework.close() # Or using a second framework framework_copy = self.create_framework() o_copy = MyObject(framework_copy, "path") - self.assertEqual(o1.handle.path, o_copy.handle.path) + assert o1.handle.path == o_copy.handle.path def test_forget_and_multiple_objects_with_load_snapshot(self): framework = self.create_framework(tmpdir=self.tmpdir) @@ -519,27 +520,27 @@ def restore(self, snapshot: typing.Dict[str, typing.Any]): o2 = framework.load_snapshot(o_handle) o2 = typing.cast(MyObject, o2) # Trying to load_snapshot a second object at the same path should fail with RuntimeError - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): framework.load_snapshot(o_handle) # Unless we _forget the object first framework._forget(o2) o3 = framework.load_snapshot(o_handle) o3 = typing.cast(MyObject, o3) - self.assertEqual(o2.value, o3.value) + assert o2.value == o3.value # A loaded object also prevents direct creation of an object - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): MyObject(framework, "path") framework.close() # But we can create an object, or load a snapshot in a copy of the framework framework_copy1 = self.create_framework(tmpdir=self.tmpdir) o_copy1 = MyObject(framework_copy1, "path") - self.assertEqual(o_copy1.value, "path") + assert o_copy1.value == "path" framework_copy1.close() framework_copy2 = self.create_framework(tmpdir=self.tmpdir) framework_copy2.register_type(MyObject, None, MyObject.handle_kind) o_copy2 = framework_copy2.load_snapshot(o_handle) o_copy2 = typing.cast(MyObject, o_copy2) - self.assertEqual(o_copy2.value, "path") + assert o_copy2.value == "path" def test_events_base(self): framework = self.create_framework() @@ -577,9 +578,9 @@ def _on_bar(self, event: ops.EventBase): # Confirm that events can be emitted and seen. pub.on.foo.emit() - self.assertEqual(obs.seen, ["on_foo:foo"]) + assert obs.seen == ["on_foo:foo"] fqn = f"{pub.on.__class__.__module__}.{pub.on.__class__.__qualname__}" - self.assertEqual(repr(pub.on), f"<{fqn}: bar, foo>") + assert repr(pub.on) == f"<{fqn}: bar, foo>" def test_conflicting_event_attributes(self): class MyEvent(ops.EventBase): @@ -590,30 +591,28 @@ class MyEvent(ops.EventBase): class MyEvents(ops.ObjectEvents): foo = event - with self.assertRaises(RuntimeError) as cm: + with pytest.raises(RuntimeError) as excinfo: class OtherEvents(ops.ObjectEvents): # type: ignore foo = event # Python 3.12+ raises the original exception with a note, but earlier # Python chains the exceptions. - if hasattr(cm.exception, "__notes__"): - cause = str(cm.exception) + if hasattr(excinfo.value, "__notes__"): + cause = str(excinfo.value) else: - cause = str(cm.exception.__cause__) - self.assertEqual( - cause, - "EventSource(MyEvent) reused as MyEvents.foo and OtherEvents.foo") + cause = str(excinfo.value.__cause__) + assert cause == \ + "EventSource(MyEvent) reused as MyEvents.foo and OtherEvents.foo" - with self.assertRaises(RuntimeError) as cm: + with pytest.raises(RuntimeError) as excinfo: class MyNotifier(ops.Object): # type: ignore on = MyEvents() # type: ignore bar = event - if hasattr(cm.exception, "__notes__"): - cause = str(cm.exception) + if hasattr(excinfo.value, "__notes__"): + cause = str(excinfo.value) else: - cause = str(cm.exception.__cause__) - self.assertEqual( - cause, - "EventSource(MyEvent) reused as MyEvents.foo and MyNotifier.bar") + cause = str(excinfo.value.__cause__) + assert cause == \ + "EventSource(MyEvent) reused as MyEvents.foo and MyNotifier.bar" def test_reemit_ignores_unknown_event_type(self): # The event type may have been gone for good, and nobody cares, @@ -643,7 +642,7 @@ def _on_foo(self, event: ops.EventBase): pub.foo.emit() event_handle = obs.seen[0] - self.assertEqual(event_handle.kind, "foo") + assert event_handle.kind == "foo" framework.commit() framework.close() @@ -655,7 +654,7 @@ def _on_foo(self, event: ops.EventBase): # Register the type and check that the event is gone from storage. framework_copy.register_type(MyEvent, event_handle.parent, event_handle.kind) - self.assertRaises(NoSnapshotError, framework_copy.load_snapshot, event_handle) + pytest.raises(NoSnapshotError, framework_copy.load_snapshot, event_handle) def test_auto_register_event_types(self): framework = self.create_framework() @@ -698,7 +697,7 @@ def _on_bar(self, event: ops.EventBase): pub.on.foo.emit() pub.bar.emit() - self.assertEqual(obs.seen, ["on_foo:MyFoo:foo", "on_bar:MyBar:bar"]) + assert obs.seen == ["on_foo:MyFoo:foo", "on_bar:MyBar:bar"] def test_dynamic_event_types(self): framework = self.create_framework() @@ -750,22 +749,22 @@ class NoneEvent(ops.EventBase): pub.on_a.foo.emit() pub.on_b.bar.emit() - self.assertEqual(obs.seen, ["on_foo:MyFoo:foo", "on_bar:MyBar:bar"]) + assert obs.seen == ["on_foo:MyFoo:foo", "on_bar:MyBar:bar"] # Definitions remained local to the specific type. - self.assertRaises(AttributeError, lambda: pub.on_a.bar) - self.assertRaises(AttributeError, lambda: pub.on_b.foo) + pytest.raises(AttributeError, lambda: pub.on_a.bar) + pytest.raises(AttributeError, lambda: pub.on_b.foo) # Try to use an event name which is not a valid python identifier. - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): pub.on_a.define_event("dead-beef", DeadBeefEvent) # Try to use a python keyword for an event name. - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): pub.on_a.define_event("None", NoneEvent) # Try to override an existing attribute. - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): pub.on_a.define_event("foo", MyFoo) def test_event_key_roundtrip(self): @@ -802,7 +801,7 @@ def _on_foo(self, event: MyEvent): obs1 = MyObserver(framework1, "obs") framework1.observe(pub1.foo, obs1._on_foo) pub1.foo.emit('first') - self.assertEqual(obs1.seen, [('1', 'first')]) + assert obs1.seen == [('1', 'first')] framework1.commit() framework1.close() @@ -816,10 +815,10 @@ def _on_foo(self, event: MyEvent): framework2.reemit() # First observer didn't get updated, since framework it was bound to is gone. - self.assertEqual(obs1.seen, [('1', 'first')]) + assert obs1.seen == [('1', 'first')] # Second observer saw the new event plus the reemit of the first event. # (The event key goes up by 2 due to the pre-commit and commit events.) - self.assertEqual(obs2.seen, [('4', 'second'), ('1', 'first')]) + assert obs2.seen == [('4', 'second'), ('1', 'first')] def test_helper_properties(self): framework = self.create_framework() @@ -827,13 +826,13 @@ def test_helper_properties(self): framework.meta = 'test-meta' # type: ignore my_obj = ops.Object(framework, 'my_obj') - self.assertEqual(my_obj.model, framework.model) + assert my_obj.model == framework.model def test_ban_concurrent_frameworks(self): f = self.create_framework(tmpdir=self.tmpdir) - with self.assertRaises(Exception) as cm: + with pytest.raises(Exception) as excinfo: self.create_framework(tmpdir=self.tmpdir) - self.assertIn('database is locked', str(cm.exception)) + assert 'database is locked' in str(excinfo.value) f.close() def test_snapshot_saving_restricted_to_simple_types(self): @@ -851,12 +850,12 @@ def snapshot(self): framework = self.create_framework() framework.register_type(FooEvent, None, handle.kind) - with self.assertRaises(ValueError) as cm: + with pytest.raises(ValueError) as excinfo: framework.save_snapshot(event) expected = ( "unable to save the data for FooEvent, it must contain only simple types: " "{'bar': }") - self.assertEqual(str(cm.exception), expected) + assert str(excinfo.value) == expected def test_unobserved_events_dont_leave_cruft(self): class FooEvent(ops.EventBase): @@ -873,12 +872,12 @@ class Emitter(ops.Object): e = Emitter(framework, 'key') e.on.foo.emit() ev_1_handle = ops.Handle(e.on, "foo", "1") - with self.assertRaises(NoSnapshotError): + with pytest.raises(NoSnapshotError): framework.load_snapshot(ev_1_handle) # Committing will save the framework's state, but no other snapshots should be saved framework.commit() events = framework._storage.list_snapshots() - self.assertEqual(list(events), [framework._stored.handle.path]) + assert list(events) == [framework._stored.handle.path] def test_event_regex(self): examples = [ @@ -892,9 +891,9 @@ def test_event_regex(self): ] regex = re.compile(_event_regex) for e in examples: - self.assertIsNotNone(regex.match(e)) + assert regex.match(e) is not None for e in non_examples: - self.assertIsNone(regex.match(e)) + assert regex.match(e) is None def test_remove_unreferenced_events(self): framework = self.create_framework() @@ -922,25 +921,22 @@ def _on_event(self, event: ops.EventBase): handle = ops.Handle(o.on, 'event', '100') event = Evt(handle) framework.save_snapshot(event) - self.assertEqual(list(framework._storage.list_snapshots()), [handle.path]) + assert list(framework._storage.list_snapshots()) == [handle.path] o.on.event.emit() - self.assertEqual( - list(framework._storage.notices('')), - [('ObjectWithStorage[obj]/on/event[1]', 'ObjectWithStorage[obj]', '_on_event')]) + assert list(framework._storage.notices('')) == \ + [('ObjectWithStorage[obj]/on/event[1]', 'ObjectWithStorage[obj]', '_on_event')] framework.commit() - self.assertEqual( - sorted(framework._storage.list_snapshots()), + assert sorted(framework._storage.list_snapshots()) == \ sorted(['ObjectWithStorage[obj]/on/event[100]', 'StoredStateData[_stored]', 'ObjectWithStorage[obj]/StoredStateData[_stored]', - 'ObjectWithStorage[obj]/on/event[1]'])) + 'ObjectWithStorage[obj]/on/event[1]']) framework.remove_unreferenced_events() - self.assertEqual( - sorted(framework._storage.list_snapshots()), + assert sorted(framework._storage.list_snapshots()) == \ sorted([ 'StoredStateData[_stored]', 'ObjectWithStorage[obj]/StoredStateData[_stored]', - 'ObjectWithStorage[obj]/on/event[1]'])) + 'ObjectWithStorage[obj]/on/event[1]']) def test_wrapped_handler(self): # It's fine to wrap the event handler, as long as the framework can @@ -979,7 +975,7 @@ def _on_event(self, _: ops.EventBase): framework = self.create_framework() charm = BadCharm(framework) - with self.assertRaisesRegex(TypeError, "only 'self' and the 'event'"): + with pytest.raises(TypeError, match="only 'self' and the 'event'"): framework.observe(charm.on.start, charm._on_event) @@ -1015,22 +1011,18 @@ def setUp(self): self.addCleanup(shutil.rmtree, str(self.tmpdir)) def test_stored_dict_repr(self): - self.assertEqual(repr(ops.StoredDict(None, {})), # type: ignore - "ops.framework.StoredDict()") - self.assertEqual(repr(ops.StoredDict(None, {"a": 1})), # type: ignore - "ops.framework.StoredDict({'a': 1})") + assert repr(ops.StoredDict(None, {})) == "ops.framework.StoredDict()" # type: ignore + assert repr(ops.StoredDict(None, {"a": 1}) # type: ignore + ) == "ops.framework.StoredDict({'a': 1})" def test_stored_list_repr(self): - self.assertEqual(repr(ops.StoredList(None, [])), # type: ignore - "ops.framework.StoredList()") - self.assertEqual(repr(ops.StoredList(None, [1, 2, 3])), # type: ignore - 'ops.framework.StoredList([1, 2, 3])') + assert repr(ops.StoredList(None, [])) == "ops.framework.StoredList()" # type: ignore + assert repr(ops.StoredList(None, [1, 2, 3]) # type: ignore + ) == 'ops.framework.StoredList([1, 2, 3])' # type: ignore def test_stored_set_repr(self): - self.assertEqual(repr(ops.StoredSet(None, set())), # type: ignore - 'ops.framework.StoredSet()') - self.assertEqual(repr(ops.StoredSet(None, {1})), # type: ignore - 'ops.framework.StoredSet({1})') + assert repr(ops.StoredSet(None, set())) == 'ops.framework.StoredSet()' # type: ignore + assert repr(ops.StoredSet(None, {1})) == 'ops.framework.StoredSet({1})' # type: ignore def test_basic_state_storage(self): class SomeObject(ops.Object): @@ -1099,14 +1091,14 @@ class _StoredProtocol(typing.Protocol): try: obj._stored.foo # type: ignore except AttributeError as e: - self.assertEqual(str(e), "attribute 'foo' is not stored") + assert str(e) == "attribute 'foo' is not stored" else: self.fail("AttributeError not raised") try: obj._stored.on = "nonono" # type: ignore except AttributeError as e: - self.assertEqual(str(e), "attribute 'on' is reserved and cannot be set") + assert str(e) == "attribute 'on' is reserved and cannot be set" else: self.fail("AttributeError not raised") @@ -1116,7 +1108,7 @@ class _StoredProtocol(typing.Protocol): obj._stored.baz = 4.2 obj._stored.bing = True - self.assertEqual(obj._stored.foo, 42) + assert obj._stored.foo == 42 framework.commit() @@ -1129,10 +1121,10 @@ class _StoredProtocol(typing.Protocol): framework_copy = self.create_framework(tmpdir=self.tmpdir) obj_copy = cls(framework_copy, "1") assert isinstance(obj_copy, _StoredProtocol) - self.assertEqual(obj_copy._stored.foo, 42) - self.assertEqual(obj_copy._stored.bar, "s") - self.assertEqual(obj_copy._stored.baz, 4.2) - self.assertEqual(obj_copy._stored.bing, True) + assert obj_copy._stored.foo == 42 + assert obj_copy._stored.bar == "s" + assert obj_copy._stored.baz == 4.2 + assert obj_copy._stored.bing framework_copy.close() @@ -1163,9 +1155,9 @@ class SubB(Base): b2 = SubB(framework2, None) z2 = Base(framework2, None) - self.assertEqual(a2._stored.foo, 42) - self.assertEqual(b2._stored.foo, "hello") - self.assertEqual(z2._stored.foo, {1}) + assert a2._stored.foo == 42 + assert b2._stored.foo == "hello" + assert z2._stored.foo == {1} def test_two_names_one_state(self): class Mine(ops.Object): @@ -1175,17 +1167,17 @@ class Mine(ops.Object): framework = self.create_framework() obj = Mine(framework, None) - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): obj._stored.foo = 42 - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): obj._stored2.foo = 42 framework.close() # make sure we're not changing the object on failure - self.assertNotIn("_stored", obj.__dict__) - self.assertNotIn("_stored2", obj.__dict__) + assert "_stored" not in obj.__dict__ + assert "_stored2" not in obj.__dict__ def test_same_name_two_classes(self): class Base(ops.Object): @@ -1205,13 +1197,13 @@ class B(Base): # exception, but that's an implementation detail a._stored.foo = 42 - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): b._stored.foo = "xyzzy" framework.close() # make sure we're not changing the object on failure - self.assertNotIn("_stored", b.__dict__) + assert "_stored" not in b.__dict__ def test_mutable_types_invalid(self): framework = self.create_framework() @@ -1225,9 +1217,8 @@ class CustomObject: pass obj._stored.foo = CustomObject() except AttributeError as e: - self.assertEqual( - str(e), - "attribute 'foo' cannot be a CustomObject: must be int/float/dict/list/etc") + assert str(e) == \ + "attribute 'foo' cannot be a CustomObject: must be int/float/dict/list/etc" else: self.fail('AttributeError not raised') @@ -1413,7 +1404,7 @@ def save_snapshot(self, value: typing.Union[ops.StoredStateData, ops.EventBase]) obj = SomeObject(framework, '1') obj._stored.a = get_a() - self.assertTrue(isinstance(obj._stored, ops.BoundStoredState)) + assert isinstance(obj._stored, ops.BoundStoredState) op(obj._stored.a, b) validate_op(obj._stored.a, expected_res) @@ -1421,13 +1412,13 @@ def save_snapshot(self, value: typing.Union[ops.StoredStateData, ops.EventBase]) obj._stored.a = get_a() framework.commit() # We should see an update for initializing a - self.assertEqual(framework.snapshots, [ + assert framework.snapshots == [ (ops.StoredStateData, {'a': get_a()}), - ]) + ] del obj gc.collect() obj_copy1 = SomeObject(framework, '1') - self.assertEqual(obj_copy1._stored.a, get_a()) + assert obj_copy1._stored.a == get_a() op(obj_copy1._stored.a, b) validate_op(obj_copy1._stored.a, expected_res) @@ -1446,7 +1437,7 @@ def save_snapshot(self, value: typing.Union[ops.StoredStateData, ops.EventBase]) # event counter, but shouldn't update the stored state of my object framework.snapshots.clear() framework_copy.commit() - self.assertEqual(framework_copy.snapshots, []) + assert framework_copy.snapshots == [] framework_copy.close() def test_comparison_operations(self): @@ -1539,8 +1530,8 @@ class SomeObject(ops.Object): for i, (a, b, op, op_ab, op_ba) in enumerate(test_operations): obj = SomeObject(framework, str(i)) obj._stored.a = a - self.assertEqual(op(obj._stored.a, b), op_ab) - self.assertEqual(op(b, obj._stored.a), op_ba) + assert op(obj._stored.a, b) == op_ab + assert op(b, obj._stored.a) == op_ba def test_set_operations(self): test_operations: typing.List[SetOperationsTestCase] = [( @@ -1591,14 +1582,14 @@ class SomeObject(ops.Object): old_b = set(b) result = operation(a, b) - self.assertEqual(result, expected) + assert result == expected # Common sanity checks - self.assertIsNot(obj._stored.set._under, result) - self.assertIsNot(result, a) - self.assertIsNot(result, b) - self.assertEqual(a, old_a) - self.assertEqual(b, old_b) + assert obj._stored.set._under is not result + assert result is not a + assert result is not b + assert a == old_a + assert b == old_b def test_set_default(self): framework = self.create_framework() @@ -1607,20 +1598,20 @@ class StatefulObject(ops.Object): _stored = ops.StoredState() parent = StatefulObject(framework, 'key') parent._stored.set_default(foo=1) - self.assertEqual(parent._stored.foo, 1) + assert parent._stored.foo == 1 parent._stored.set_default(foo=2) # foo was already set, so it doesn't get replaced - self.assertEqual(parent._stored.foo, 1) + assert parent._stored.foo == 1 parent._stored.set_default(foo=3, bar=4) - self.assertEqual(parent._stored.foo, 1) - self.assertEqual(parent._stored.bar, 4) + assert parent._stored.foo == 1 + assert parent._stored.bar == 4 # reloading the state still leaves things at the default values framework.commit() del parent parent = StatefulObject(framework, 'key') parent._stored.set_default(foo=5, bar=6) - self.assertEqual(parent._stored.foo, 1) - self.assertEqual(parent._stored.bar, 4) + assert parent._stored.foo == 1 + assert parent._stored.bar == 4 # TODO: jam 2020-01-30 is there a clean way to tell that # parent._stored._data.dirty is False? @@ -1656,8 +1647,8 @@ def test_ignored(self, fake_stderr: io.StringIO): pass else: self.fail("No warning logs should be generated") - self.assertEqual(mock.call_count, 0) - self.assertEqual(fake_stderr.getvalue(), "") + assert mock.call_count == 0 + assert fake_stderr.getvalue() == "" def test_pdb_properly_called(self, fake_stderr: io.StringIO): # The debugger needs to leave the user in the frame where the breakpoint is executed, @@ -1669,8 +1660,8 @@ def test_pdb_properly_called(self, fake_stderr: io.StringIO): this_frame = inspect.currentframe() framework.breakpoint() - self.assertEqual(mock.call_count, 1) - self.assertEqual(mock.call_args, ((this_frame,), {})) + assert mock.call_count == 1 + assert mock.call_args == ((this_frame,), {}) def test_welcome_message(self, fake_stderr: io.StringIO): # Check that an initial message is shown to the user when code is interrupted. @@ -1678,7 +1669,7 @@ def test_welcome_message(self, fake_stderr: io.StringIO): framework = self.create_framework() with patch('pdb.Pdb.set_trace'): framework.breakpoint() - self.assertEqual(fake_stderr.getvalue(), _BREAKPOINT_WELCOME_MESSAGE) + assert fake_stderr.getvalue() == _BREAKPOINT_WELCOME_MESSAGE def test_welcome_message_not_multiple(self, fake_stderr: io.StringIO): # Check that an initial message is NOT shown twice if the breakpoint is exercised @@ -1687,9 +1678,9 @@ def test_welcome_message_not_multiple(self, fake_stderr: io.StringIO): framework = self.create_framework() with patch('pdb.Pdb.set_trace'): framework.breakpoint() - self.assertEqual(fake_stderr.getvalue(), _BREAKPOINT_WELCOME_MESSAGE) + assert fake_stderr.getvalue() == _BREAKPOINT_WELCOME_MESSAGE framework.breakpoint() - self.assertEqual(fake_stderr.getvalue(), _BREAKPOINT_WELCOME_MESSAGE) + assert fake_stderr.getvalue() == _BREAKPOINT_WELCOME_MESSAGE def test_breakpoint_builtin_sanity(self, fake_stderr: io.StringIO): # this just checks that calling breakpoint() works as expected @@ -1702,8 +1693,8 @@ def test_breakpoint_builtin_sanity(self, fake_stderr: io.StringIO): this_frame = inspect.currentframe() breakpoint() - self.assertEqual(mock.call_count, 1) - self.assertEqual(mock.call_args, ((this_frame,), {})) + assert mock.call_count == 1 + assert mock.call_args == ((this_frame,), {}) def test_builtin_breakpoint_hooked(self, fake_stderr: io.StringIO): # Verify that the proper hook is set. @@ -1713,7 +1704,7 @@ def test_builtin_breakpoint_hooked(self, fake_stderr: io.StringIO): self.addCleanup(setattr, sys, 'breakpointhook', old_breakpointhook) with patch('pdb.Pdb.set_trace') as mock: breakpoint() - self.assertEqual(mock.call_count, 1) + assert mock.call_count == 1 def test_breakpoint_builtin_unset(self, fake_stderr: io.StringIO): # if no JUJU_DEBUG_AT, no call to pdb is done @@ -1726,7 +1717,7 @@ def test_breakpoint_builtin_unset(self, fake_stderr: io.StringIO): with patch('pdb.Pdb.set_trace') as mock: breakpoint() - self.assertEqual(mock.call_count, 0) + assert mock.call_count == 0 def test_breakpoint_names(self, fake_stderr: io.StringIO): framework = self.create_framework() @@ -1766,9 +1757,9 @@ def test_breakpoint_names(self, fake_stderr: io.StringIO): msg = 'breakpoint names must look like "foo" or "foo-bar"' for name in bad_names: with self.subTest(name=name): - with self.assertRaises(ValueError) as cm: + with pytest.raises(ValueError) as excinfo: framework.breakpoint(name) - self.assertEqual(str(cm.exception), msg) + assert str(excinfo.value) == msg reserved_names = [ 'all', @@ -1777,9 +1768,9 @@ def test_breakpoint_names(self, fake_stderr: io.StringIO): msg = 'breakpoint names "all" and "hook" are reserved' for name in reserved_names: with self.subTest(name=name): - with self.assertRaises(ValueError) as cm: + with pytest.raises(ValueError) as excinfo: framework.breakpoint(name) - self.assertEqual(str(cm.exception), msg) + assert str(excinfo.value) == msg not_really_names = [ 123, @@ -1788,9 +1779,9 @@ def test_breakpoint_names(self, fake_stderr: io.StringIO): ] for name in not_really_names: with self.subTest(name=name): - with self.assertRaises(TypeError) as cm: + with pytest.raises(TypeError) as excinfo: framework.breakpoint(name) # type: ignore - self.assertEqual(str(cm.exception), 'breakpoint names must be strings') + assert str(excinfo.value) == 'breakpoint names must be strings' def check_trace_set( self, @@ -1802,7 +1793,7 @@ def check_trace_set( framework = self.create_framework() with patch('pdb.Pdb.set_trace') as mock: framework.breakpoint(breakpoint_name) - self.assertEqual(mock.call_count, call_count) + assert mock.call_count == call_count def test_unnamed_indicated_all(self, fake_stderr: io.StringIO): # If 'all' is indicated, unnamed breakpoints will always activate. @@ -1820,18 +1811,18 @@ def test_named_indicated_unnamed(self, fake_stderr: io.StringIO): # Some breakpoint was indicated, but the framework call was unnamed with self.assertLogs(level="WARNING") as cm: self.check_trace_set('some-breakpoint', None, 0) - self.assertEqual(cm.output, [ + assert cm.output == [ "WARNING:ops.framework:Breakpoint None skipped " "(not found in the requested breakpoints: {'some-breakpoint'})" - ]) + ] def test_named_indicated_somethingelse(self, fake_stderr: io.StringIO): # Some breakpoint was indicated, but the framework call was with a different name with self.assertLogs(level="WARNING") as cm: self.check_trace_set('some-breakpoint', 'other-name', 0) - self.assertEqual(cm.output, [ + assert cm.output == [ "WARNING:ops.framework:Breakpoint 'other-name' skipped " - "(not found in the requested breakpoints: {'some-breakpoint'})"]) + "(not found in the requested breakpoints: {'some-breakpoint'})"] def test_named_indicated_ingroup(self, fake_stderr: io.StringIO): # A multiple breakpoint was indicated, and the framework call used a name among those. @@ -1852,22 +1843,22 @@ def test_envvar_parsing_missing(self): with patch.dict(os.environ): os.environ.pop('JUJU_DEBUG_AT', None) framework = self.create_framework() - self.assertEqual(framework._juju_debug_at, set()) + assert framework._juju_debug_at == set() def test_envvar_parsing_empty(self): with patch.dict(os.environ, {'JUJU_DEBUG_AT': ''}): framework = self.create_framework() - self.assertEqual(framework._juju_debug_at, set()) + assert framework._juju_debug_at == set() def test_envvar_parsing_simple(self): with patch.dict(os.environ, {'JUJU_DEBUG_AT': 'hook'}): framework = self.create_framework() - self.assertEqual(framework._juju_debug_at, {'hook'}) + assert framework._juju_debug_at == {'hook'} def test_envvar_parsing_multiple(self): with patch.dict(os.environ, {'JUJU_DEBUG_AT': 'foo,bar,all'}): framework = self.create_framework() - self.assertEqual(framework._juju_debug_at, {'foo', 'bar', 'all'}) + assert framework._juju_debug_at == {'foo', 'bar', 'all'} def test_basic_interruption_enabled(self): framework = self.create_framework() @@ -1885,14 +1876,14 @@ def test_basic_interruption_enabled(self): # Check that the pdb module was used correctly and that the callback method was NOT # called (as we intercepted the normal pdb behaviour! this is to check that the # framework didn't call the callback directly) - self.assertEqual(mock.call_count, 1) + assert mock.call_count == 1 expected_callback, expected_event = mock.call_args[0] - self.assertEqual(expected_callback, observer.callback_method) - self.assertIsInstance(expected_event, ops.EventBase) - self.assertFalse(observer.called) + assert expected_callback == observer.callback_method + assert isinstance(expected_event, ops.EventBase) + assert not observer.called # Verify proper message was given to the user. - self.assertEqual(fake_stderr.getvalue(), _BREAKPOINT_WELCOME_MESSAGE) + assert fake_stderr.getvalue() == _BREAKPOINT_WELCOME_MESSAGE def test_interruption_enabled_with_all(self): test_model = self.create_model() @@ -1911,8 +1902,8 @@ class CustomEvents(ops.ObjectEvents): with patch('pdb.runcall') as mock: publisher.foobar_action.emit(id='1') - self.assertEqual(mock.call_count, 1) - self.assertFalse(observer.called) + assert mock.call_count == 1 + assert not observer.called def test_actions_are_interrupted(self): test_model = self.create_model() @@ -1931,8 +1922,8 @@ class CustomEvents(ops.ObjectEvents): with patch('pdb.runcall') as mock: publisher.foobar_action.emit(id='2') - self.assertEqual(mock.call_count, 1) - self.assertFalse(observer.called) + assert mock.call_count == 1 + assert not observer.called def test_internal_events_not_interrupted(self): class MyNotifier(ops.Object): @@ -1949,8 +1940,8 @@ class MyNotifier(ops.Object): with patch('pdb.runcall') as mock: publisher.bar.emit() - self.assertEqual(mock.call_count, 0) - self.assertTrue(observer.called) + assert mock.call_count == 0 + assert observer.called def test_envvar_mixed(self): framework = self.create_framework() @@ -1964,8 +1955,8 @@ def test_envvar_mixed(self): with patch('pdb.runcall') as mock: publisher.install.emit() - self.assertEqual(mock.call_count, 1) - self.assertFalse(observer.called) + assert mock.call_count == 1 + assert not observer.called def test_no_registered_method(self): framework = self.create_framework() @@ -1977,8 +1968,8 @@ def test_no_registered_method(self): with patch('pdb.runcall') as mock: publisher.install.emit() - self.assertEqual(mock.call_count, 0) - self.assertFalse(observer.called) + assert mock.call_count == 0 + assert not observer.called def test_envvar_nohook(self): framework = self.create_framework() @@ -1992,8 +1983,8 @@ def test_envvar_nohook(self): with patch('pdb.runcall') as mock: publisher.install.emit() - self.assertEqual(mock.call_count, 0) - self.assertTrue(observer.called) + assert mock.call_count == 0 + assert observer.called def test_envvar_missing(self): framework = self.create_framework() @@ -2006,8 +1997,8 @@ def test_envvar_missing(self): with patch('pdb.runcall') as mock: publisher.install.emit() - self.assertEqual(mock.call_count, 0) - self.assertTrue(observer.called) + assert mock.call_count == 0 + assert observer.called def test_welcome_message_not_multiple(self): framework = self.create_framework() @@ -2021,7 +2012,7 @@ def test_welcome_message_not_multiple(self): fake_stderr = typing.cast(io.StringIO, fake_stderr) with patch('pdb.runcall') as mock: publisher.install.emit() - self.assertEqual(fake_stderr.getvalue(), _BREAKPOINT_WELCOME_MESSAGE) + assert fake_stderr.getvalue() == _BREAKPOINT_WELCOME_MESSAGE publisher.install.emit() - self.assertEqual(fake_stderr.getvalue(), _BREAKPOINT_WELCOME_MESSAGE) - self.assertEqual(mock.call_count, 2) + assert fake_stderr.getvalue() == _BREAKPOINT_WELCOME_MESSAGE + assert mock.call_count == 2 diff --git a/test/test_helpers.py b/test/test_helpers.py index 09adc9c6a..5f9b0e316 100644 --- a/test/test_helpers.py +++ b/test/test_helpers.py @@ -82,28 +82,28 @@ def test_fake_script_works(self): # subprocess.getoutput goes via the shell, so it needs to be # something both sh and CMD understand output = subprocess.getoutput('foo a "b c " && bar "d e" f') - self.assertEqual(output, 'foo runs\nbar runs') - self.assertEqual(fake_script_calls(self), [ + assert output == 'foo runs\nbar runs' + assert fake_script_calls(self) == [ ['foo', 'a', 'b c '], ['bar', 'd e', 'f'], - ]) + ] def test_fake_script_clear(self): fake_script(self, 'foo', 'echo foo runs') output = subprocess.getoutput('foo a "b c"') - self.assertEqual(output, 'foo runs') + assert output == 'foo runs' - self.assertEqual(fake_script_calls(self, clear=True), [['foo', 'a', 'b c']]) + assert fake_script_calls(self, clear=True) == [['foo', 'a', 'b c']] fake_script(self, 'bar', 'echo bar runs') output = subprocess.getoutput('bar "d e" f') - self.assertEqual(output, 'bar runs') + assert output == 'bar runs' - self.assertEqual(fake_script_calls(self, clear=True), [['bar', 'd e', 'f']]) + assert fake_script_calls(self, clear=True) == [['bar', 'd e', 'f']] - self.assertEqual(fake_script_calls(self, clear=True), []) + assert fake_script_calls(self, clear=True) == [] class BaseTestCase(unittest.TestCase): diff --git a/test/test_infra.py b/test/test_infra.py index 3ea47fb95..bd6e8f7fd 100644 --- a/test/test_infra.py +++ b/test/test_infra.py @@ -65,4 +65,4 @@ def check(self, name: str): else: environ['PYTHONPATH'] = os.getcwd() proc = subprocess.run([sys.executable, testfile], env=environ) - self.assertEqual(proc.returncode, 0) + assert proc.returncode == 0 diff --git a/test/test_jujuversion.py b/test/test_jujuversion.py index 25692622e..644cb747c 100644 --- a/test/test_jujuversion.py +++ b/test/test_jujuversion.py @@ -15,6 +15,8 @@ import os import unittest.mock # in this file, importing just 'patch' would be confusing +import pytest + import ops @@ -35,61 +37,61 @@ def test_parsing(self): for vs, major, minor, tag, patch, build in test_cases: v = ops.JujuVersion(vs) - self.assertEqual(v.major, major) - self.assertEqual(v.minor, minor) - self.assertEqual(v.tag, tag) - self.assertEqual(v.patch, patch) - self.assertEqual(v.build, build) + assert v.major == major + assert v.minor == minor + assert v.tag == tag + assert v.patch == patch + assert v.build == build @unittest.mock.patch('os.environ', new={}) # type: ignore def test_from_environ(self): # JUJU_VERSION is not set v = ops.JujuVersion.from_environ() - self.assertEqual(v, ops.JujuVersion('0.0.0')) + assert v == ops.JujuVersion('0.0.0') os.environ['JUJU_VERSION'] = 'no' - with self.assertRaisesRegex(RuntimeError, 'not a valid Juju version'): + with pytest.raises(RuntimeError, match='not a valid Juju version'): ops.JujuVersion.from_environ() os.environ['JUJU_VERSION'] = '2.8.0' v = ops.JujuVersion.from_environ() - self.assertEqual(v, ops.JujuVersion('2.8.0')) + assert v == ops.JujuVersion('2.8.0') def test_has_app_data(self): - self.assertTrue(ops.JujuVersion('2.8.0').has_app_data()) - self.assertTrue(ops.JujuVersion('2.7.0').has_app_data()) - self.assertFalse(ops.JujuVersion('2.6.9').has_app_data()) + assert ops.JujuVersion('2.8.0').has_app_data() + assert ops.JujuVersion('2.7.0').has_app_data() + assert not ops.JujuVersion('2.6.9').has_app_data() def test_is_dispatch_aware(self): - self.assertTrue(ops.JujuVersion('2.8.0').is_dispatch_aware()) - self.assertFalse(ops.JujuVersion('2.7.9').is_dispatch_aware()) + assert ops.JujuVersion('2.8.0').is_dispatch_aware() + assert not ops.JujuVersion('2.7.9').is_dispatch_aware() def test_has_controller_storage(self): - self.assertTrue(ops.JujuVersion('2.8.0').has_controller_storage()) - self.assertFalse(ops.JujuVersion('2.7.9').has_controller_storage()) + assert ops.JujuVersion('2.8.0').has_controller_storage() + assert not ops.JujuVersion('2.7.9').has_controller_storage() def test_has_secrets(self): - self.assertTrue(ops.JujuVersion('3.0.3').has_secrets) - self.assertTrue(ops.JujuVersion('3.1.0').has_secrets) - self.assertFalse(ops.JujuVersion('3.0.2').has_secrets) - self.assertFalse(ops.JujuVersion('2.9.30').has_secrets) + assert ops.JujuVersion('3.0.3').has_secrets + assert ops.JujuVersion('3.1.0').has_secrets + assert not ops.JujuVersion('3.0.2').has_secrets + assert not ops.JujuVersion('2.9.30').has_secrets def test_supports_open_port_on_k8s(self): - self.assertTrue(ops.JujuVersion('3.0.3').supports_open_port_on_k8s) - self.assertTrue(ops.JujuVersion('3.3.0').supports_open_port_on_k8s) - self.assertFalse(ops.JujuVersion('3.0.2').supports_open_port_on_k8s) - self.assertFalse(ops.JujuVersion('2.9.30').supports_open_port_on_k8s) + assert ops.JujuVersion('3.0.3').supports_open_port_on_k8s + assert ops.JujuVersion('3.3.0').supports_open_port_on_k8s + assert not ops.JujuVersion('3.0.2').supports_open_port_on_k8s + assert not ops.JujuVersion('2.9.30').supports_open_port_on_k8s def test_supports_exec_service_context(self): - self.assertFalse(ops.JujuVersion('2.9.30').supports_exec_service_context) - self.assertTrue(ops.JujuVersion('4.0.0').supports_exec_service_context) - self.assertFalse(ops.JujuVersion('3.0.0').supports_exec_service_context) - self.assertFalse(ops.JujuVersion('3.1.5').supports_exec_service_context) - self.assertTrue(ops.JujuVersion('3.1.6').supports_exec_service_context) - self.assertFalse(ops.JujuVersion('3.2.0').supports_exec_service_context) - self.assertTrue(ops.JujuVersion('3.2.2').supports_exec_service_context) - self.assertTrue(ops.JujuVersion('3.3.0').supports_exec_service_context) - self.assertTrue(ops.JujuVersion('3.4.0').supports_exec_service_context) + assert not ops.JujuVersion('2.9.30').supports_exec_service_context + assert ops.JujuVersion('4.0.0').supports_exec_service_context + assert not ops.JujuVersion('3.0.0').supports_exec_service_context + assert not ops.JujuVersion('3.1.5').supports_exec_service_context + assert ops.JujuVersion('3.1.6').supports_exec_service_context + assert not ops.JujuVersion('3.2.0').supports_exec_service_context + assert ops.JujuVersion('3.2.2').supports_exec_service_context + assert ops.JujuVersion('3.3.0').supports_exec_service_context + assert ops.JujuVersion('3.4.0').supports_exec_service_context def test_parsing_errors(self): invalid_versions = [ @@ -107,7 +109,7 @@ def test_parsing_errors(self): "1.21-alpha123dev3", # Non-numeric string after the patch number. ] for v in invalid_versions: - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): ops.JujuVersion(v) def test_equality(self): @@ -136,8 +138,8 @@ def test_equality(self): ] for a, b, expected in test_cases: - self.assertEqual(ops.JujuVersion(a) == ops.JujuVersion(b), expected) - self.assertEqual(ops.JujuVersion(a) == b, expected) + assert (ops.JujuVersion(a) == ops.JujuVersion(b)) == expected + assert (ops.JujuVersion(a) == b) == expected def test_comparison(self): test_cases = [ @@ -167,12 +169,12 @@ def test_comparison(self): for a, b, expected_strict, expected_weak in test_cases: with self.subTest(a=a, b=b): - self.assertEqual(ops.JujuVersion(a) < ops.JujuVersion(b), expected_strict) - self.assertEqual(ops.JujuVersion(a) <= ops.JujuVersion(b), expected_weak) - self.assertEqual(ops.JujuVersion(b) > ops.JujuVersion(a), expected_strict) - self.assertEqual(ops.JujuVersion(b) >= ops.JujuVersion(a), expected_weak) + assert (ops.JujuVersion(a) < ops.JujuVersion(b)) == expected_strict + assert (ops.JujuVersion(a) <= ops.JujuVersion(b)) == expected_weak + assert (ops.JujuVersion(b) > ops.JujuVersion(a)) == expected_strict + assert (ops.JujuVersion(b) >= ops.JujuVersion(a)) == expected_weak # Implicit conversion. - self.assertEqual(ops.JujuVersion(a) < b, expected_strict) - self.assertEqual(ops.JujuVersion(a) <= b, expected_weak) - self.assertEqual(b > ops.JujuVersion(a), expected_strict) - self.assertEqual(b >= ops.JujuVersion(a), expected_weak) + assert (ops.JujuVersion(a) < b) == expected_strict + assert (ops.JujuVersion(a) <= b) == expected_weak + assert (b > ops.JujuVersion(a)) == expected_strict + assert (b >= ops.JujuVersion(a)) == expected_weak diff --git a/test/test_lib.py b/test/test_lib.py index 1a68bc30b..90a4dbde2 100644 --- a/test/test_lib.py +++ b/test/test_lib.py @@ -79,13 +79,12 @@ def _mkdtemp(self) -> str: def test_single(self): tmpdir = self._mkdtemp() - self.assertEqual(list(ops.lib._find_all_specs([tmpdir])), []) + assert list(ops.lib._find_all_specs([tmpdir])) == [] _mklib(tmpdir, "foo", "bar").write_text("") - self.assertEqual( - _flatten(ops.lib._find_all_specs([tmpdir])), - [os.path.join(tmpdir, 'foo', 'opslib', 'bar')]) + assert _flatten(ops.lib._find_all_specs([tmpdir])) == \ + [os.path.join(tmpdir, 'foo', 'opslib', 'bar')] def test_multi(self): tmp_dir_a = self._mkdtemp() @@ -113,7 +112,7 @@ def test_multi(self): os.path.join(tmp_dir_b, "baz", "opslib", "quux"), ] - self.assertEqual(_flatten(ops.lib._find_all_specs(dirs)), expected) + assert _flatten(ops.lib._find_all_specs(dirs)) == expected def test_cwd(self): tmpcwd = self._mkdtemp() @@ -123,14 +122,13 @@ def test_cwd(self): dirs = [""] - self.assertEqual(list(ops.lib._find_all_specs(dirs)), []) + assert list(ops.lib._find_all_specs(dirs)) == [] _mklib(tmpcwd, "foo", "bar").write_text("") paths = _flatten(ops.lib._find_all_specs(dirs)) - self.assertEqual( - [os.path.relpath(p) for p in paths], - [os.path.join('foo', 'opslib', 'bar')]) + assert [os.path.relpath(p) for p in paths] == \ + [os.path.join('foo', 'opslib', 'bar')] def test_bogus_topdir(self): """Check that having one bogus dir in sys.path doesn't cause the finder to abort.""" @@ -138,19 +136,18 @@ def test_bogus_topdir(self): dirs = [tmpdir, "/bogus"] - self.assertEqual(list(ops.lib._find_all_specs(dirs)), []) + assert list(ops.lib._find_all_specs(dirs)) == [] _mklib(tmpdir, "foo", "bar").write_text("") - self.assertEqual( - _flatten(ops.lib._find_all_specs(dirs)), - [os.path.join(tmpdir, 'foo', 'opslib', 'bar')]) + assert _flatten(ops.lib._find_all_specs(dirs)) == \ + [os.path.join(tmpdir, 'foo', 'opslib', 'bar')] def test_bogus_opsdir(self): """Check that having one bogus opslib doesn't cause the finder to abort.""" tmpdir = self._mkdtemp() - self.assertEqual(list(ops.lib._find_all_specs([tmpdir])), []) + assert list(ops.lib._find_all_specs([tmpdir])) == [] _mklib(tmpdir, "foo", "bar").write_text('') @@ -158,19 +155,18 @@ def test_bogus_opsdir(self): path.mkdir() (path / 'opslib').write_text('') - self.assertEqual( - _flatten(ops.lib._find_all_specs([tmpdir])), - [os.path.join(tmpdir, 'foo', 'opslib', 'bar')]) + assert _flatten(ops.lib._find_all_specs([tmpdir])) == \ + [os.path.join(tmpdir, 'foo', 'opslib', 'bar')] def test_namespace(self): """Check that namespace packages are ignored.""" tmpdir = self._mkdtemp() - self.assertEqual(list(ops.lib._find_all_specs([tmpdir])), []) + assert list(ops.lib._find_all_specs([tmpdir])) == [] _mklib(tmpdir, "foo", "bar") # no __init__.py => a namespace package - self.assertEqual(list(ops.lib._find_all_specs([tmpdir])), []) + assert list(ops.lib._find_all_specs([tmpdir])) == [] class TestLibParser(TestCase): @@ -194,9 +190,9 @@ def test_simple(self): LIBANANA = True ''') lib = ops.lib._parse_lib(m) - self.assertEqual(lib, ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 2, 42)) + assert lib == ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 2, 42) # also check the repr while we're at it - self.assertEqual(repr(lib), '<_Lib foo by alice@example.com, API 2, patch 42>') + assert repr(lib) == '<_Lib foo by alice@example.com, API 2, patch 42>' def test_libauthor_has_dashes(self): m = self._mkmod('foo', ''' @@ -207,9 +203,9 @@ def test_libauthor_has_dashes(self): LIBANANA = True ''') lib = ops.lib._parse_lib(m) - self.assertEqual(lib, ops.lib._Lib(_dummy_spec, "foo", "alice-someone@example.com", 2, 42)) + assert lib == ops.lib._Lib(_dummy_spec, "foo", "alice-someone@example.com", 2, 42) # also check the repr while we're at it - self.assertEqual(repr(lib), '<_Lib foo by alice-someone@example.com, API 2, patch 42>') + assert repr(lib) == '<_Lib foo by alice-someone@example.com, API 2, patch 42>' def test_lib_definitions_without_spaces(self): m = self._mkmod('foo', ''' @@ -220,9 +216,9 @@ def test_lib_definitions_without_spaces(self): LIBANANA=True ''') lib = ops.lib._parse_lib(m) - self.assertEqual(lib, ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 2, 42)) + assert lib == ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 2, 42) # also check the repr while we're at it - self.assertEqual(repr(lib), '<_Lib foo by alice@example.com, API 2, patch 42>') + assert repr(lib) == '<_Lib foo by alice@example.com, API 2, patch 42>' def test_lib_definitions_trailing_comments(self): m = self._mkmod('foo', ''' @@ -233,9 +229,9 @@ def test_lib_definitions_trailing_comments(self): LIBANANA = True ''') lib = ops.lib._parse_lib(m) - self.assertEqual(lib, ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 2, 42)) + assert lib == ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 2, 42) # also check the repr while we're at it - self.assertEqual(repr(lib), '<_Lib foo by alice@example.com, API 2, patch 42>') + assert repr(lib) == '<_Lib foo by alice@example.com, API 2, patch 42>' def test_incomplete(self): """Check that if anything is missing, nothing is returned.""" @@ -244,7 +240,7 @@ def test_incomplete(self): LIBAPI = 2 LIBPATCH = 42 ''') - self.assertIsNone(ops.lib._parse_lib(m)) + assert ops.lib._parse_lib(m) is None def test_too_long(self): """Check that if the file is too long, nothing is returned.""" @@ -254,19 +250,19 @@ def test_too_long(self): LIBPATCH = 42 LIBAUTHOR = "alice@example.com" ''') - self.assertIsNone(ops.lib._parse_lib(m)) + assert ops.lib._parse_lib(m) is None def test_no_origin(self): """Check that _parse_lib doesn't choke when given a spec with no origin.""" # 'just don't crash' lib = ops.lib._parse_lib(ModuleSpec(name='hi', loader=None, origin=None)) - self.assertIsNone(lib) + assert lib is None def test_bogus_origin(self): """Check that if the origin is messed up, we don't crash.""" # 'just don't crash' lib = ops.lib._parse_lib(ModuleSpec(name='hi', loader=None, origin='/')) - self.assertIsNone(lib) + assert lib is None def test_bogus_lib(self): """Check our behaviour when the lib is messed up.""" @@ -277,7 +273,7 @@ def test_bogus_lib(self): LIBPATCH = 42 LIBAUTHOR = "alice@example.com" ''') - self.assertIsNone(ops.lib._parse_lib(m)) + assert ops.lib._parse_lib(m) is None def test_name_is_number(self): """Check our behaviour when the name in the lib is a number.""" @@ -287,7 +283,7 @@ def test_name_is_number(self): LIBPATCH = 42 LIBAUTHOR = "alice@example.com" ''') - self.assertIsNone(ops.lib._parse_lib(m)) + assert ops.lib._parse_lib(m) is None def test_api_is_string(self): """Check our behaviour when the api in the lib is a string.""" @@ -297,7 +293,7 @@ def test_api_is_string(self): LIBPATCH = 42 LIBAUTHOR = "alice@example.com" ''') - self.assertIsNone(ops.lib._parse_lib(m)) + assert ops.lib._parse_lib(m) is None def test_patch_is_string(self): """Check our behaviour when the patch in the lib is a string.""" @@ -307,7 +303,7 @@ def test_patch_is_string(self): LIBPATCH = '42' LIBAUTHOR = "alice@example.com" ''') - self.assertIsNone(ops.lib._parse_lib(m)) + assert ops.lib._parse_lib(m) is None def test_author_is_number(self): """Check our behaviour when the author in the lib is a number.""" @@ -317,7 +313,7 @@ def test_author_is_number(self): LIBPATCH = 42 LIBAUTHOR = 43 ''') - self.assertIsNone(ops.lib._parse_lib(m)) + assert ops.lib._parse_lib(m) is None def test_other_encoding(self): """Check that we don't crash when a library is not UTF-8.""" @@ -325,7 +321,7 @@ def test_other_encoding(self): # This should never be the case, but we need to show type checkers # that it's not. if m.origin is None: - self.assertIsNotNone(m.origin) + assert m.origin is not None return with open(m.origin, 'w', encoding='latin-1') as f: f.write(dedent(''' @@ -335,42 +331,36 @@ def test_other_encoding(self): LIBAUTHOR = "alice@example.com" LIBANANA = "Ñoño" ''')) - self.assertIsNone(ops.lib._parse_lib(m)) + assert ops.lib._parse_lib(m) is None class TestLib(TestCase): def test_lib_comparison(self): - self.assertNotEqual( - ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 1, 0), - ops.lib._Lib(_dummy_spec, "bar", "bob@example.com", 0, 1)) - self.assertEqual( - ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 1, 1), - ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 1, 1)) - - self.assertLess( - ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 1, 0), - ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 1, 1)) - self.assertLess( - ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 0, 1), - ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 1, 1)) - self.assertLess( - ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 1, 1), - ops.lib._Lib(_dummy_spec, "foo", "bob@example.com", 1, 1)) - self.assertLess( - ops.lib._Lib(_dummy_spec, "bar", "alice@example.com", 1, 1), - ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 1, 1)) - - with self.assertRaises(TypeError): + assert ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 1, 0) != \ + ops.lib._Lib(_dummy_spec, "bar", "bob@example.com", 0, 1) + assert ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 1, 1) == \ + ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 1, 1) + + assert ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 1, 0) < \ + ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 1, 1) + assert ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 0, 1) < \ + ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 1, 1) + assert ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 1, 1) < \ + ops.lib._Lib(_dummy_spec, "foo", "bob@example.com", 1, 1) + assert ops.lib._Lib(_dummy_spec, "bar", "alice@example.com", 1, 1) < \ + ops.lib._Lib(_dummy_spec, "foo", "alice@example.com", 1, 1) + + with pytest.raises(TypeError): 42 < ops.lib._Lib(_dummy_spec, "bar", "alice@example.com", 1, 1) # type:ignore # noqa: B015, SIM300 - with self.assertRaises(TypeError): + with pytest.raises(TypeError): ops.lib._Lib(_dummy_spec, "bar", "alice@example.com", 1, 1) < 42 # type: ignore # noqa: B015 # these two might be surprising in that they don't raise an exception, # but they are correct: our __eq__ bailing means Python falls back to # its default of checking object identity. - self.assertNotEqual(ops.lib._Lib(_dummy_spec, "bar", "alice@example.com", 1, 1), 42) - self.assertNotEqual(42, ops.lib._Lib(_dummy_spec, "bar", "alice@example.com", 1, 1)) + assert ops.lib._Lib(_dummy_spec, "bar", "alice@example.com", 1, 1) != 42 + assert ops.lib._Lib(_dummy_spec, "bar", "alice@example.com", 1, 1) != 42 def test_lib_order(self): a = ops.lib._Lib(_dummy_spec, "bar", "alice@example.com", 1, 0) @@ -383,22 +373,22 @@ def test_lib_order(self): with self.subTest(i): libs = [a, b, c, d, e] shuffle(libs) - self.assertEqual(sorted(libs), [a, b, c, d, e]) + assert sorted(libs) == [a, b, c, d, e] def test_use_bad_args_types(self): - with self.assertRaises(TypeError): + with pytest.raises(TypeError): ops.lib.use(1, 2, 'bob@example.com') # type: ignore - with self.assertRaises(TypeError): + with pytest.raises(TypeError): ops.lib.use('foo', '2', 'bob@example.com') # type: ignore - with self.assertRaises(TypeError): + with pytest.raises(TypeError): ops.lib.use('foo', 2, ops.lib.use) # type: ignore def test_use_bad_args_values(self): - with self.assertRaises(ValueError): + with pytest.raises(ValueError): ops.lib.use('--help', 2, 'alice@example.com') - with self.assertRaises(ValueError): + with pytest.raises(ValueError): ops.lib.use('foo', -2, 'alice@example.com') - with self.assertRaises(ValueError): + with pytest.raises(ValueError): ops.lib.use('foo', 1, 'example.com') @@ -427,10 +417,10 @@ def test_use_finds_subs(self): # ops.lib.use done by charm author baz = ops.lib.use('baz', 2, 'alice@example.com') - self.assertEqual(baz.LIBNAME, 'baz') - self.assertEqual(baz.LIBAPI, 2) - self.assertEqual(baz.LIBPATCH, 42) - self.assertEqual(baz.LIBAUTHOR, 'alice@example.com') + assert baz.LIBNAME == 'baz' + assert baz.LIBAPI == 2 + assert baz.LIBPATCH == 42 + assert baz.LIBAUTHOR == 'alice@example.com' def test_use_finds_best_same_toplevel(self): """Test that ops.lib.use("baz") works when there are two baz in the same toplevel.""" @@ -467,10 +457,10 @@ def test_use_finds_best_same_toplevel(self): # ops.lib.use done by charm author baz = ops.lib.use('baz', 2, 'alice@example.com') - self.assertEqual(baz.LIBNAME, 'baz') - self.assertEqual(baz.LIBAPI, 2) - self.assertEqual(baz.LIBPATCH, max(patch_a, patch_b)) - self.assertEqual(baz.LIBAUTHOR, 'alice@example.com') + assert baz.LIBNAME == 'baz' + assert baz.LIBAPI == 2 + assert max(patch_a, patch_b) == baz.LIBPATCH + assert baz.LIBAUTHOR == 'alice@example.com' def test_use_finds_best_diff_toplevel(self): """Test that ops.lib.use("baz") works when there are two baz in the different toplevels.""" @@ -505,13 +495,13 @@ def test_use_finds_best_diff_toplevel(self): # ops.lib.use done by charm author baz = ops.lib.use('baz', 2, 'alice@example.com') - self.assertEqual(baz.LIBNAME, 'baz') - self.assertEqual(baz.LIBAPI, 2) - self.assertEqual(baz.LIBPATCH, max(patch_a, patch_b)) - self.assertEqual(baz.LIBAUTHOR, 'alice@example.com') + assert baz.LIBNAME == 'baz' + assert baz.LIBAPI == 2 + assert max(patch_a, patch_b) == baz.LIBPATCH + assert baz.LIBAUTHOR == 'alice@example.com' def test_none_found(self): - with self.assertRaises(ImportError): + with pytest.raises(ImportError): ops.lib.use('foo', 1, 'alice@example.com') def test_from_scratch(self): @@ -530,7 +520,7 @@ def test_from_scratch(self): # sanity check that ops.lib.use works baz = ops.lib.use('baz', 2, 'alice@example.com') - self.assertEqual(baz.LIBAPI, 2) + assert baz.LIBAPI == 2 def _test_submodule(self, *, relative: bool = False): tmpdir = self._mkdtemp() @@ -554,8 +544,8 @@ def _test_submodule(self, *, relative: bool = False): # sanity check that ops.lib.use works baz = ops.lib.use('baz', 2, 'alice@example.com') - self.assertEqual(baz.LIBAPI, 2) - self.assertEqual(baz.quux.this, 42) + assert baz.LIBAPI == 2 + assert baz.quux.this == 42 def test_submodule_absolute(self): self._test_submodule(relative=False) @@ -579,12 +569,12 @@ def test_others_found(self): # sanity check that ops.lib.use works baz = ops.lib.use('baz', 2, 'alice@example.com') - self.assertEqual(baz.LIBAPI, 2) + assert baz.LIBAPI == 2 - with self.assertRaises(ImportError): + with pytest.raises(ImportError): ops.lib.use('baz', 1, 'alice@example.com') - with self.assertRaises(ImportError): + with pytest.raises(ImportError): ops.lib.use('baz', 2, 'bob@example.com') @@ -595,5 +585,5 @@ def test_autoimport_deprecated(self): def test_use_deprecated(self): with self.assertWarns(DeprecationWarning): - with self.assertRaises(ImportError): + with pytest.raises(ImportError): ops.lib.use('foo', 1, 'bob@example.com') diff --git a/test/test_log.py b/test/test_log.py index 969bdd9cc..1f6da2fdc 100644 --- a/test/test_log.py +++ b/test/test_log.py @@ -14,6 +14,7 @@ import io import logging +import re import typing import unittest from unittest.mock import patch @@ -50,8 +51,8 @@ def test_default_logging(self): ops.log.setup_root_logging(self.backend) logger = logging.getLogger() - self.assertEqual(logger.level, logging.DEBUG) - self.assertIsInstance(logger.handlers[-1], ops.log.JujuLogHandler) + assert logger.level == logging.DEBUG + assert isinstance(logger.handlers[-1], ops.log.JujuLogHandler) test_cases = [ (logger.critical, 'critical', ('CRITICAL', 'critical')), @@ -65,16 +66,16 @@ def test_default_logging(self): with self.subTest(message): method(message) calls = self.backend.calls(clear=True) - self.assertEqual(calls, [result]) + assert calls == [result] def test_handler_filtering(self): logger = logging.getLogger() logger.setLevel(logging.INFO) logger.addHandler(ops.log.JujuLogHandler(self.backend, logging.WARNING)) logger.info('foo') - self.assertEqual(self.backend.calls(), []) + assert self.backend.calls() == [] logger.warning('bar') - self.assertEqual(self.backend.calls(), [('WARNING', 'bar')]) + assert self.backend.calls() == [('WARNING', 'bar')] def test_no_stderr_without_debug(self): buffer = io.StringIO() @@ -85,14 +86,13 @@ def test_no_stderr_without_debug(self): logger.info('info message') logger.warning('warning message') logger.critical('critical message') - self.assertEqual( - self.backend.calls(), + assert self.backend.calls() == \ [('DEBUG', 'debug message'), ('INFO', 'info message'), ('WARNING', 'warning message'), ('CRITICAL', 'critical message'), - ]) - self.assertEqual(buffer.getvalue(), "") + ] + assert buffer.getvalue() == "" def test_debug_logging(self): buffer = io.StringIO() @@ -103,19 +103,18 @@ def test_debug_logging(self): logger.info('info message') logger.warning('warning message') logger.critical('critical message') - self.assertEqual( - self.backend.calls(), + assert self.backend.calls() == \ [('DEBUG', 'debug message'), ('INFO', 'info message'), ('WARNING', 'warning message'), ('CRITICAL', 'critical message'), - ]) - self.assertRegex( - buffer.getvalue(), + ] + assert re.search( r"\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d,\d\d\d DEBUG debug message\n" r"\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d,\d\d\d INFO info message\n" r"\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d,\d\d\d WARNING warning message\n" - r"\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d,\d\d\d CRITICAL critical message\n" + r"\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d,\d\d\d CRITICAL critical message\n", + buffer.getvalue() ) def test_reduced_logging(self): @@ -125,7 +124,7 @@ def test_reduced_logging(self): logger.debug('debug') logger.info('info') logger.warning('warning') - self.assertEqual(self.backend.calls(), [('WARNING', 'warning')]) + assert self.backend.calls() == [('WARNING', 'warning')] def test_long_string_logging(self): buffer = io.StringIO() @@ -135,7 +134,7 @@ def test_long_string_logging(self): logger = logging.getLogger() logger.debug('l' * MAX_LOG_LINE_LEN) - self.assertEqual(len(self.backend.calls()), 1) + assert len(self.backend.calls()) == 1 self.backend.calls(clear=True) @@ -143,13 +142,13 @@ def test_long_string_logging(self): logger.debug('l' * (MAX_LOG_LINE_LEN + 9)) calls = self.backend.calls() - self.assertEqual(len(calls), 3) + assert len(calls) == 3 # Verify that we note that we are splitting the log message. - self.assertTrue("Splitting into multiple chunks" in calls[0][1]) + assert "Splitting into multiple chunks" in calls[0][1] # Verify that it got split into the expected chunks. - self.assertTrue(len(calls[1][1]) == MAX_LOG_LINE_LEN) - self.assertTrue(len(calls[2][1]) == 9) + assert len(calls[1][1]) == MAX_LOG_LINE_LEN + assert len(calls[2][1]) == 9 if __name__ == '__main__': diff --git a/test/test_main.py b/test/test_main.py index c22d0b56c..e81f343ee 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -28,6 +28,8 @@ from pathlib import Path from unittest.mock import patch +import pytest + import ops from ops.main import _should_use_controller_storage from ops.storage import SQLiteStorage @@ -102,8 +104,8 @@ class MyCharm(ops.CharmBase): with patch('pdb.Pdb.set_trace') as mock: breakpoint() - self.assertEqual(mock.call_count, 1) - self.assertIn('Starting pdb to debug charm operator', fake_stderr.getvalue()) + assert mock.call_count == 1 + assert 'Starting pdb to debug charm operator' in fake_stderr.getvalue() def test_no_debug_breakpoint(self): class MyCharm(ops.CharmBase): @@ -113,7 +115,7 @@ class MyCharm(ops.CharmBase): with patch('pdb.Pdb.set_trace') as mock: breakpoint() - self.assertEqual(mock.call_count, 0) + assert mock.call_count == 0 def _check( self, @@ -149,7 +151,7 @@ def __init__(self, *args): # type: ignore with warnings.catch_warnings(record=True) as warn_cm: self._check(MyCharm) - self.assertEqual(warn_cm, []) + assert warn_cm == [] def test_init_signature_old_key_argument(self): class MyCharm(ops.CharmBase): @@ -158,7 +160,7 @@ def __init__(self, framework: ops.Framework, somekey: typing.Any): super().__init__(framework, somekey) # type: ignore # Support for "key" has been deprecated since ops 0.7 and was removed in 2.0 - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self._check(MyCharm) def test_init_signature_only_framework(self): @@ -169,29 +171,29 @@ def __init__(self, framework: ops.Framework): with warnings.catch_warnings(record=True) as warn_cm: self._check(MyCharm) - self.assertEqual(warn_cm, []) + assert warn_cm == [] def test_storage_no_storage(self): # here we patch juju_backend_available so it refuses to set it up with patch('ops.storage.juju_backend_available') as juju_backend_available: juju_backend_available.return_value = False - with self.assertRaisesRegex( + with pytest.raises( RuntimeError, - 'charm set use_juju_for_storage=True, but Juju .* does not support it'): + match='charm set use_juju_for_storage=True, but Juju .* does not support it'): self._check(ops.CharmBase, use_juju_for_storage=True) def test_storage_with_storage(self): # here we patch juju_backend_available, so it gets set up and falls over when used with patch('ops.storage.juju_backend_available') as juju_backend_available: juju_backend_available.return_value = True - with self.assertRaisesRegex(FileNotFoundError, 'state-get'): + with pytest.raises(FileNotFoundError, match='state-get'): self._check(ops.CharmBase, use_juju_for_storage=True) def test_controller_storage_deprecated(self): with patch('ops.storage.juju_backend_available') as juju_backend_available: juju_backend_available.return_value = True with self.assertWarnsRegex(DeprecationWarning, 'Controller storage'): - with self.assertRaisesRegex(FileNotFoundError, 'state-get'): + with pytest.raises(FileNotFoundError, match='state-get'): self._check(ops.CharmBase, use_juju_for_storage=True) @@ -231,23 +233,23 @@ def __init__(self, framework: ops.Framework): mock_charmdir.return_value = tmpdir ops.main(MyCharm) # type: ignore - self.assertEqual(mock_charm_event.call_count, 1) + assert mock_charm_event.call_count == 1 return mock_charm_event.call_args[0][1] def test_most_legacy(self): """Without dispatch, sys.argv[0] is used.""" event = self._check() - self.assertEqual(event, 'config_changed') + assert event == 'config_changed' def test_with_dispatch(self): """With dispatch, dispatch is used.""" event = self._check(with_dispatch=True, dispatch_path='hooks/potatos') - self.assertEqual(event, 'potatos') + assert event == 'potatos' def test_with_dispatch_path_but_no_dispatch(self): """Dispatch path overwrites sys.argv[0] even if no actual dispatch.""" event = self._check(with_dispatch=False, dispatch_path='hooks/foo') - self.assertEqual(event, 'foo') + assert event == 'foo' _event_test = typing.List[typing.Tuple[ @@ -465,18 +467,17 @@ def test_event_reemitted(self): # First run "install" to make sure all hooks are set up. state = self._simulate_event(EventSpec(ops.InstallEvent, 'install')) assert isinstance(state, ops.BoundStoredState) - self.assertEqual(list(state.observed_event_types), ['InstallEvent']) + assert list(state.observed_event_types) == ['InstallEvent'] state = self._simulate_event(EventSpec(ops.ConfigChangedEvent, 'config-changed')) assert isinstance(state, ops.BoundStoredState) - self.assertEqual(list(state.observed_event_types), ['ConfigChangedEvent']) + assert list(state.observed_event_types) == ['ConfigChangedEvent'] # Re-emit should pick the deferred config-changed. state = self._simulate_event(EventSpec(ops.UpdateStatusEvent, 'update-status')) assert isinstance(state, ops.BoundStoredState) - self.assertEqual( - list(state.observed_event_types), - ['ConfigChangedEvent', 'UpdateStatusEvent']) + assert list(state.observed_event_types) == \ + ['ConfigChangedEvent', 'UpdateStatusEvent'] def test_no_reemission_on_collect_metrics(self): fake_script(typing.cast(unittest.TestCase, self), 'add-metric', 'exit 0') @@ -484,17 +485,17 @@ def test_no_reemission_on_collect_metrics(self): # First run "install" to make sure all hooks are set up. state = self._simulate_event(EventSpec(ops.InstallEvent, 'install')) assert isinstance(state, ops.BoundStoredState) - self.assertEqual(list(state.observed_event_types), ['InstallEvent']) + assert list(state.observed_event_types) == ['InstallEvent'] state = self._simulate_event(EventSpec(ops.ConfigChangedEvent, 'config-changed')) assert isinstance(state, ops.BoundStoredState) - self.assertEqual(list(state.observed_event_types), ['ConfigChangedEvent']) + assert list(state.observed_event_types) == ['ConfigChangedEvent'] # Re-emit should not pick the deferred config-changed because # collect-metrics runs in a restricted context. state = self._simulate_event(EventSpec(ops.CollectMetricsEvent, 'collect-metrics')) assert isinstance(state, ops.BoundStoredState) - self.assertEqual(list(state.observed_event_types), ['CollectMetricsEvent']) + assert list(state.observed_event_types) == ['CollectMetricsEvent'] def test_multiple_events_handled(self): self._prepare_actions() @@ -653,16 +654,16 @@ def test_multiple_events_handled(self): handled_events = getattr(state, state_key, []) # Make sure that a handler for that event was called once. - self.assertEqual(len(handled_events), 1) + assert len(handled_events) == 1 # Make sure the event handled by the Charm has the right type. handled_event_type = handled_events[0] - self.assertEqual(handled_event_type, event_spec.event_type.__name__) + assert handled_event_type == event_spec.event_type.__name__ - self.assertEqual(list(state.observed_event_types), [event_spec.event_type.__name__]) + assert list(state.observed_event_types) == [event_spec.event_type.__name__] if expected_event_data: - self.assertEqual(getattr(state, f"{event_spec.event_name}_data"), - expected_event_data) + assert getattr(state, f"{event_spec.event_name}_data") == \ + expected_event_data def test_event_not_implemented(self): """Make sure events without implementation do not cause non-zero exit.""" @@ -701,7 +702,7 @@ def test_collect_metrics(self): ] calls = fake_script_calls(typing.cast(unittest.TestCase, self)) - self.assertEqual(calls, expected) + assert calls == expected def test_custom_event(self): self._simulate_event(EventSpec(ops.InstallEvent, 'install')) @@ -720,9 +721,9 @@ def test_custom_event(self): ['is-leader', '--format=json'], ] # Remove the "[key]>" suffix from the end of the event string - self.assertRegex(calls[2][-1], re.escape(custom_event_prefix) + '.*') + assert re.match(re.escape(custom_event_prefix) + '.*', calls[2][-1]) calls[2][-1] = custom_event_prefix - self.assertEqual(calls, expected) + assert calls == expected def test_logger(self): fake_script(typing.cast(unittest.TestCase, self), 'action-get', "echo '{}'") @@ -750,30 +751,28 @@ def test_logger(self): for event_spec, calls in test_cases: self._simulate_event(event_spec) - self.assertIn(calls, - fake_script_calls(typing.cast(unittest.TestCase, self), clear=True)) + assert calls in \ + fake_script_calls(typing.cast(unittest.TestCase, self), clear=True) def test_excepthook(self): - with self.assertRaises(subprocess.CalledProcessError): + with pytest.raises(subprocess.CalledProcessError): self._simulate_event(EventSpec(ops.InstallEvent, 'install', set_in_env={'TRY_EXCEPTHOOK': '1'})) calls = [' '.join(i) for i in fake_script_calls(typing.cast(unittest.TestCase, self))] - self.assertEqual(calls.pop(0), ' '.join(VERSION_LOGLINE)) - self.assertRegex(calls.pop(0), 'Using local storage: not a Kubernetes podspec charm') - self.assertRegex(calls.pop(0), 'Initializing SQLite local storage: ') + assert calls.pop(0) == ' '.join(VERSION_LOGLINE) + assert re.search('Using local storage: not a Kubernetes podspec charm', calls.pop(0)) + assert re.search('Initializing SQLite local storage: ', calls.pop(0)) self.maxDiff = None - self.assertRegex( - calls[0], + assert re.search( '(?ms)juju-log --log-level ERROR -- Uncaught exception while in charm code:\n' 'Traceback .most recent call last.:\n' ' .*' ' raise RuntimeError."failing as requested".\n' - 'RuntimeError: failing as requested' - ) - self.assertEqual(len(calls), 1, f"expected 1 call, but got extra: {calls[1:]}") + 'RuntimeError: failing as requested', calls[0]) + assert len(calls) == 1, f"expected 1 call, but got extra: {calls[1:]}" def test_sets_model_name(self): self._prepare_actions() @@ -785,7 +784,7 @@ def test_sets_model_name(self): model_name='test-model-name', set_in_env={'JUJU_ACTION_UUID': '1'})) assert isinstance(state, ops.BoundStoredState) - self.assertEqual(state._on_get_model_name_action, ['test-model-name']) + assert state._on_get_model_name_action == ['test-model-name'] def test_has_valid_status(self): self._prepare_actions() @@ -798,8 +797,8 @@ def test_has_valid_status(self): env_var='JUJU_ACTION_NAME', set_in_env={'JUJU_ACTION_UUID': '1'})) assert isinstance(state, ops.BoundStoredState) - self.assertEqual(state.status_name, 'unknown') - self.assertEqual(state.status_message, '') + assert state.status_name == 'unknown' + assert state.status_message == '' fake_script( typing.cast(unittest.TestCase, self), 'status-get', @@ -809,8 +808,8 @@ def test_has_valid_status(self): env_var='JUJU_ACTION_NAME', set_in_env={'JUJU_ACTION_UUID': '1'})) assert isinstance(state, ops.BoundStoredState) - self.assertEqual(state.status_name, 'blocked') - self.assertEqual(state.status_message, 'help meeee') + assert state.status_name == 'blocked' + assert state.status_message == 'help meeee' class TestMainWithNoDispatch(_TestMain, unittest.TestCase): @@ -887,20 +886,20 @@ def test_setup_event_links(self): initial_hooks = {f"hooks/{ev.event_name}" for ev in initial_events} def _assess_event_links(event_spec: EventSpec): - self.assertTrue(self.hooks_dir / event_spec.event_name in self.hooks_dir.iterdir()) + assert self.hooks_dir / event_spec.event_name in self.hooks_dir.iterdir() for event_hook in all_event_hooks: hook_path = self.JUJU_CHARM_DIR / event_hook - self.assertTrue(hook_path.exists(), f"Missing hook: {event_hook}") + assert hook_path.exists(), f"Missing hook: {event_hook}" if self.hooks_are_symlinks: - self.assertTrue(hook_path.is_symlink()) - self.assertEqual(os.readlink(str(hook_path)), self.charm_exec_path) + assert hook_path.is_symlink() + assert os.readlink(str(hook_path)) == self.charm_exec_path elif event_hook in initial_hooks: - self.assertFalse(hook_path.is_symlink()) + assert not hook_path.is_symlink() else: # hooks are not symlinks, and this hook is not one of the initial ones # check that it's a symlink to the initial ones - self.assertTrue(hook_path.is_symlink()) - self.assertEqual(os.readlink(str(hook_path)), event_spec.event_name) + assert hook_path.is_symlink() + assert os.readlink(str(hook_path)) == event_spec.event_name for initial_event in initial_events: self._setup_charm_dir() @@ -915,7 +914,7 @@ def test_setup_action_links(self): self._simulate_event(EventSpec(ops.InstallEvent, 'install')) # foo-bar is one of the actions defined in actions.yaml action_hook = self.JUJU_CHARM_DIR / 'actions' / 'foo-bar' - self.assertTrue(action_hook.exists()) + assert action_hook.exists() class TestMainWithNoDispatchButJujuIsDispatchAware(TestMainWithNoDispatch): @@ -958,10 +957,10 @@ def test_setup_event_links(self): } def _assess_event_links(event_spec: EventSpec): - self.assertNotIn(self.hooks_dir / event_spec.event_name, self.hooks_dir.iterdir()) + assert self.hooks_dir / event_spec.event_name not in self.hooks_dir.iterdir() for event_hook in all_event_hooks: - self.assertFalse((self.JUJU_CHARM_DIR / event_hook).exists(), - f"Spurious hook: {event_hook}") + assert not (self.JUJU_CHARM_DIR / event_hook).exists(), \ + f"Spurious hook: {event_hook}" for initial_event in initial_events: self._setup_charm_dir() @@ -977,9 +976,9 @@ def test_hook_and_dispatch(self): assert isinstance(state, ops.BoundStoredState) # the script was called, *and*, the .on. was called - self.assertEqual(fake_script_calls(typing.cast( - unittest.TestCase, self)), [['install', '']]) - self.assertEqual(list(state.observed_event_types), ['InstallEvent']) + assert fake_script_calls(typing.cast( + unittest.TestCase, self)) == [['install', '']] + assert list(state.observed_event_types) == ['InstallEvent'] self.fake_script_path = old_path hook = Path('hooks/install') @@ -996,16 +995,15 @@ def test_hook_and_dispatch(self): ['is-leader', '--format=json'], ] calls = fake_script_calls(typing.cast(unittest.TestCase, self)) - self.assertRegex(' '.join(calls.pop(-3)), - 'Initializing SQLite local storage: ') - self.assertEqual(calls, expected) + assert re.search('Initializing SQLite local storage: ', ' '.join(calls.pop(-3))) + assert calls == expected def test_non_executable_hook_and_dispatch(self): (self.hooks_dir / "install").write_text("") state = self._simulate_event(EventSpec(ops.InstallEvent, 'install')) assert isinstance(state, ops.BoundStoredState) - self.assertEqual(list(state.observed_event_types), ['InstallEvent']) + assert list(state.observed_event_types) == ['InstallEvent'] expected = [ VERSION_LOGLINE, @@ -1018,9 +1016,8 @@ def test_non_executable_hook_and_dispatch(self): ['is-leader', '--format=json'], ] calls = fake_script_calls(typing.cast(unittest.TestCase, self)) - self.assertRegex(' '.join(calls.pop(-3)), - 'Initializing SQLite local storage: ') - self.assertEqual(calls, expected) + assert re.search('Initializing SQLite local storage: ', ' '.join(calls.pop(-3))) + assert calls == expected def test_hook_and_dispatch_with_failing_hook(self): self.stdout = self.stderr = tempfile.TemporaryFile() @@ -1030,14 +1027,14 @@ def test_hook_and_dispatch_with_failing_hook(self): self.fake_script_path = self.hooks_dir fake_script(typing.cast(unittest.TestCase, self), 'install', 'exit 42') event = EventSpec(ops.InstallEvent, 'install') - with self.assertRaises(subprocess.CalledProcessError): + with pytest.raises(subprocess.CalledProcessError): self._simulate_event(event) self.fake_script_path = old_path self.stdout.seek(0) - self.assertEqual(self.stdout.read(), b'') + assert self.stdout.read() == b'' self.stderr.seek(0) - self.assertEqual(self.stderr.read(), b'') + assert self.stderr.read() == b'' calls = fake_script_calls(typing.cast(unittest.TestCase, self)) hook = Path('hooks/install') expected = [ @@ -1046,7 +1043,7 @@ def test_hook_and_dispatch_with_failing_hook(self): ['juju-log', '--log-level', 'WARNING', '--', f'Legacy {hook} exited with status 42.'], ] - self.assertEqual(calls, expected) + assert calls == expected def test_hook_and_dispatch_but_hook_is_dispatch(self): event = EventSpec(ops.InstallEvent, 'install') @@ -1063,8 +1060,8 @@ def test_hook_and_dispatch_but_hook_is_dispatch(self): }.items(): with self.subTest(path=path, rel=rel, ind=ind): # sanity check - self.assertEqual(path.is_absolute(), not rel) - self.assertEqual(path.with_suffix('').name == 'dispatch', ind) + assert path.is_absolute() == (not rel) + assert (path.with_suffix('').name == 'dispatch') == ind try: hook_path.symlink_to(path) @@ -1072,8 +1069,8 @@ def test_hook_and_dispatch_but_hook_is_dispatch(self): assert isinstance(state, ops.BoundStoredState) # the .on. was only called once - self.assertEqual(list(state.observed_event_types), ['InstallEvent']) - self.assertEqual(list(state.on_install), ['InstallEvent']) + assert list(state.observed_event_types) == ['InstallEvent'] + assert list(state.on_install) == ['InstallEvent'] finally: hook_path.unlink() @@ -1087,8 +1084,8 @@ def test_hook_and_dispatch_but_hook_is_dispatch_copy(self): assert isinstance(state, ops.BoundStoredState) # the .on. was only called once - self.assertEqual(list(state.observed_event_types), ['InstallEvent']) - self.assertEqual(list(state.on_install), ['InstallEvent']) + assert list(state.observed_event_types) == ['InstallEvent'] + assert list(state.on_install) == ['InstallEvent'] hook = Path('hooks/install') expected = [ VERSION_LOGLINE, @@ -1106,9 +1103,9 @@ def test_hook_and_dispatch_but_hook_is_dispatch_copy(self): ['is-leader', '--format=json'], ] calls = fake_script_calls(typing.cast(unittest.TestCase, self)) - self.assertRegex(' '.join(calls.pop(-3)), 'Initializing SQLite local storage: ') + assert re.search('Initializing SQLite local storage: ', ' '.join(calls.pop(-3))) - self.assertEqual(calls, expected) + assert calls == expected class TestMainWithDispatch(_TestMainWithDispatch, unittest.TestCase): @@ -1172,15 +1169,15 @@ def test_crash_action(self): self.stderr = tempfile.TemporaryFile('w+t') self.addCleanup(self.stderr.close) fake_script(typing.cast(unittest.TestCase, self), 'action-get', "echo '{}'") - with self.assertRaises(subprocess.CalledProcessError): + with pytest.raises(subprocess.CalledProcessError): self._simulate_event(EventSpec( ops.ActionEvent, 'keyerror_action', env_var='JUJU_ACTION_NAME', set_in_env={'JUJU_ACTION_UUID': '1'})) self.stderr.seek(0) stderr = self.stderr.read() - self.assertIn('KeyError', stderr) - self.assertIn("'foo' not found in 'bar'", stderr) + assert 'KeyError' in stderr + assert "'foo' not found in 'bar'" in stderr class TestMainWithDispatchAsScript(_TestMainWithDispatch, unittest.TestCase): @@ -1247,19 +1244,19 @@ class TestStorageHeuristics(unittest.TestCase): def test_fallback_to_current_juju_version__too_old(self): meta = ops.CharmMeta.from_yaml("series: [kubernetes]") with patch.dict(os.environ, {"JUJU_VERSION": "1.0"}): - self.assertFalse(_should_use_controller_storage(Path("/xyzzy"), meta)) + assert not _should_use_controller_storage(Path("/xyzzy"), meta) def test_fallback_to_current_juju_version__new_enough(self): meta = ops.CharmMeta.from_yaml("series: [kubernetes]") with patch.dict(os.environ, {"JUJU_VERSION": "2.8"}): - self.assertTrue(_should_use_controller_storage(Path("/xyzzy"), meta)) + assert _should_use_controller_storage(Path("/xyzzy"), meta) def test_not_if_not_in_k8s(self): meta = ops.CharmMeta.from_yaml("series: [ecs]") with patch.dict(os.environ, {"JUJU_VERSION": "2.8"}): - self.assertFalse(_should_use_controller_storage(Path("/xyzzy"), meta)) + assert not _should_use_controller_storage(Path("/xyzzy"), meta) def test_not_if_already_local(self): meta = ops.CharmMeta.from_yaml("series: [kubernetes]") with patch.dict(os.environ, {"JUJU_VERSION": "2.8"}), tempfile.NamedTemporaryFile() as fd: - self.assertFalse(_should_use_controller_storage(Path(fd.name), meta)) + assert not _should_use_controller_storage(Path(fd.name), meta) diff --git a/test/test_model.py b/test/test_model.py index c1f990c65..fc00272ef 100644 --- a/test/test_model.py +++ b/test/test_model.py @@ -18,6 +18,7 @@ import json import os import pathlib +import re import tempfile import typing import unittest @@ -71,27 +72,27 @@ def ensure_relation( relation_id: typing.Optional[int] = None) -> ops.Relation: """Wrapper around self.model.get_relation that enforces that None is not returned.""" rel_db1 = self.model.get_relation(name, relation_id) - self.assertIsNotNone(rel_db1) + assert rel_db1 is not None assert rel_db1 is not None # Type checkers understand this, but not the previous line. return rel_db1 def test_model_attributes(self): - self.assertIs(self.model.app, self.model.unit.app) - self.assertIsNone(self.model.name) + assert self.model.app is self.model.unit.app + assert self.model.name is None def test_unit_immutable(self): - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): self.model.unit = object() # type: ignore def test_app_immutable(self): - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): self.model.app = object() # type: ignore def test_model_name_from_backend(self): self.harness.set_model_name('default') m = ops.Model(ops.CharmMeta(), self.harness._backend) - self.assertEqual(m.name, 'default') - with self.assertRaises(AttributeError): + assert m.name == 'default' + with pytest.raises(AttributeError): m.name = "changes-disallowed" # type: ignore def test_relations_keys(self): @@ -105,9 +106,9 @@ def test_relations_keys(self): self.model.relations._invalidate('db1') self.resetBackendCalls() for relation in self.model.relations['db1']: - self.assertIn(self.model.unit, relation.data) + assert self.model.unit in relation.data unit_from_rel = next(filter(lambda u: u.name == 'myapp/0', relation.data.keys())) - self.assertIs(self.model.unit, unit_from_rel) + assert self.model.unit is unit_from_rel self.assertBackendCalls([ ('relation_ids', 'db1'), @@ -116,7 +117,7 @@ def test_relations_keys(self): ]) def test_relations_immutable(self): - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): self.model.relations = {} # type: ignore def test_get_relation(self): @@ -128,31 +129,31 @@ def test_get_relation(self): relation_id_db0_b = self.harness.add_relation('db0', 'another') self.resetBackendCalls() - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): # You have to specify it by just the integer ID self.model.get_relation('db1', f'db1:{relation_id_db1}') # type: ignore rel_db1 = self.model.get_relation('db1', relation_id_db1) - self.assertIsInstance(rel_db1, ops.Relation) + assert isinstance(rel_db1, ops.Relation) self.assertBackendCalls([ ('relation_ids', 'db1'), ('relation_list', relation_id_db1), ]) dead_rel = self.ensure_relation('db1', 7) - self.assertIsInstance(dead_rel, ops.Relation) - self.assertEqual(set(dead_rel.data.keys()), {self.model.unit, self.model.unit.app}) - self.assertEqual(dead_rel.data[self.model.unit], {}) + assert isinstance(dead_rel, ops.Relation) + assert set(dead_rel.data.keys()) == {self.model.unit, self.model.unit.app} + assert dead_rel.data[self.model.unit] == {} self.assertBackendCalls([ ('relation_list', 7), ('relation_remote_app_name', 7), ('relation_get', 7, 'myapp/0', False), ]) - self.assertIsNone(self.model.get_relation('db2')) + assert self.model.get_relation('db2') is None self.assertBackendCalls([ ('relation_ids', 'db2'), ]) - self.assertIs(self.model.get_relation('db1'), rel_db1) - with self.assertRaises(ops.TooManyRelatedAppsError): + assert self.model.get_relation('db1') is rel_db1 + with pytest.raises(ops.TooManyRelatedAppsError): self.model.get_relation('db0') self.assertBackendCalls([ @@ -166,7 +167,7 @@ def test_get_relation(self): def test_peer_relation_app(self): self.harness.add_relation('db2', 'myapp') rel_dbpeer = self.ensure_relation('db2') - self.assertIs(rel_dbpeer.app, self.model.app) + assert rel_dbpeer.app is self.model.app def test_remote_units_is_our(self): relation_id = self.harness.add_relation('db1', 'remoteapp1') @@ -175,8 +176,8 @@ def test_remote_units_is_our(self): self.resetBackendCalls() for u in self.ensure_relation('db1').units: - self.assertFalse(u._is_our_unit) - self.assertFalse(u.app._is_our_app) + assert not u._is_our_unit + assert not u.app._is_our_app self.assertBackendCalls([ ('relation_ids', 'db1'), @@ -184,21 +185,21 @@ def test_remote_units_is_our(self): ]) def test_our_unit_is_our(self): - self.assertTrue(self.model.unit._is_our_unit) - self.assertTrue(self.model.unit.app._is_our_app) + assert self.model.unit._is_our_unit + assert self.model.unit.app._is_our_app def test_invalid_type_relation_data(self): relation_id = self.harness.add_relation('db1', 'remoteapp1') self.harness.add_relation_unit(relation_id, 'remoteapp1/0') - with self.assertRaises(ops.RelationDataError): + with pytest.raises(ops.RelationDataError): with self.harness._event_context('foo_event'): self.harness.update_relation_data( relation_id, 'remoteapp1/0', {42: 'remoteapp1-0'}) # type: ignore - with self.assertRaises(ops.RelationDataError): + with pytest.raises(ops.RelationDataError): with self.harness._event_context('foo_event'): self.harness.update_relation_data( relation_id, @@ -231,12 +232,12 @@ def test_unit_relation_data(self): self.resetBackendCalls() random_unit = self.model.get_unit('randomunit/0') - with self.assertRaises(KeyError): + with pytest.raises(KeyError): self.ensure_relation('db1').data[random_unit] remoteapp1_0 = next(filter(lambda u: u.name == 'remoteapp1/0', self.ensure_relation('db1').units)) - self.assertEqual(self.ensure_relation('db1').data[remoteapp1_0], - {'host': 'remoteapp1-0'}) + assert self.ensure_relation('db1').data[remoteapp1_0] == \ + {'host': 'remoteapp1-0'} self.assertBackendCalls([ ('relation_ids', 'db1'), @@ -256,14 +257,14 @@ def test_remote_app_relation_data(self): rel_db1 = self.ensure_relation('db1') # Try to get relation data for an invalid remote application. random_app = self.model._cache.get(ops.Application, 'randomapp') - with self.assertRaises(KeyError): + with pytest.raises(KeyError): rel_db1.data[random_app] remoteapp1 = rel_db1.app assert remoteapp1 is not None - self.assertEqual(remoteapp1.name, 'remoteapp1') - self.assertEqual(rel_db1.data[remoteapp1], - {'secret': 'cafedeadbeef'}) + assert remoteapp1.name == 'remoteapp1' + assert rel_db1.data[remoteapp1] == \ + {'secret': 'cafedeadbeef'} self.assertBackendCalls([ ('relation_ids', 'db1'), @@ -286,13 +287,13 @@ def test_relation_data_modify_remote(self): remoteapp1_0 = next(filter(lambda u: u.name == 'remoteapp1/0', self.ensure_relation('db1').units)) # Force memory cache to be loaded. - self.assertIn('host', rel_db1.data[remoteapp1_0]) - self.assertEqual(repr(rel_db1.data[remoteapp1_0]), "{'host': 'remoteapp1/0'}") + assert 'host' in rel_db1.data[remoteapp1_0] + assert repr(rel_db1.data[remoteapp1_0]) == "{'host': 'remoteapp1/0'}" with self.harness._event_context('foo_event'): - with self.assertRaises(ops.RelationDataError): + with pytest.raises(ops.RelationDataError): rel_db1.data[remoteapp1_0]['foo'] = 'bar' - self.assertNotIn('foo', rel_db1.data[remoteapp1_0]) + assert 'foo' not in rel_db1.data[remoteapp1_0] self.assertBackendCalls([ ('relation_ids', 'db1'), @@ -303,12 +304,11 @@ def test_relation_data_modify_remote(self): # this will fire more backend calls with self.harness._event_context('foo_event'): data_repr = repr(rel_db1.data) - self.assertEqual( - data_repr, + assert data_repr == \ ('{: {}, ' ': , ' ": {'host': 'remoteapp1/0'}, " - ": {'secret': 'cafedeadbeef'}}")) + ": {'secret': 'cafedeadbeef'}}") def test_relation_data_modify_our(self): relation_id = self.harness.add_relation('db1', 'remoteapp1') @@ -321,9 +321,9 @@ def test_relation_data_modify_our(self): # invalidate the cache to ensure it will be reloaded rel_db1.data[self.model.unit]._invalidate() # Force memory cache to be loaded. - self.assertIn('host', rel_db1.data[self.model.unit]) + assert 'host' in rel_db1.data[self.model.unit] rel_db1.data[self.model.unit]['host'] = 'bar' - self.assertEqual(rel_db1.data[self.model.unit]['host'], 'bar') + assert rel_db1.data[self.model.unit]['host'] == 'bar' self.assertBackendCalls([ ('relation_get', relation_id, 'myapp/0', False), @@ -340,11 +340,11 @@ def test_app_relation_data_modify_local_as_leader(self): local_app = self.model.unit.app rel_db1 = self.ensure_relation('db1') - self.assertEqual(rel_db1.data[local_app], {'password': 'deadbeefcafe'}) + assert rel_db1.data[local_app] == {'password': 'deadbeefcafe'} rel_db1.data[local_app]['password'] = 'foo' - self.assertEqual(rel_db1.data[local_app]['password'], 'foo') + assert rel_db1.data[local_app]['password'] == 'foo' self.assertBackendCalls( [('relation_ids', 'db1'), @@ -362,11 +362,11 @@ def test_app_relation_data_modify_local_as_minion(self): local_app = self.model.unit.app rel_db1 = self.ensure_relation('db1') - self.assertEqual(rel_db1.data[local_app], {'password': 'deadbeefcafe'}) + assert rel_db1.data[local_app] == {'password': 'deadbeefcafe'} with self.harness._event_context('foo_event'): # if we were inside an event context, we'd get: - with self.assertRaises(ops.RelationDataError): + with pytest.raises(ops.RelationDataError): rel_db1.data[local_app]['password'] = 'foobar' self.assertBackendCalls([('relation_ids', 'db1'), @@ -383,7 +383,7 @@ def test_relation_data_access_peer_leader(self): self.harness.set_leader(True) relation = self.harness.model.get_relation('db2') assert relation is not None and relation.app is not None - self.assertEqual(relation.data[relation.app]['foo'], 'bar') + assert relation.data[relation.app]['foo'] == 'bar' def test_relation_data_access_peer_minion(self): r_id = self.harness.add_relation('db2', 'myapp') @@ -394,7 +394,7 @@ def test_relation_data_access_peer_minion(self): self.harness.set_leader(False) relation = self.harness.model.get_relation('db2') assert relation is not None and relation.app is not None - self.assertEqual(relation.data[relation.app]['foo'], 'bar') + assert relation.data[relation.app]['foo'] == 'bar' def test_relation_data_del_key(self): relation_id = self.harness.add_relation('db1', 'remoteapp1') @@ -405,10 +405,10 @@ def test_relation_data_del_key(self): rel_db1 = self.ensure_relation('db1') # Force memory cache to be loaded. - self.assertIn('host', rel_db1.data[self.model.unit]) + assert 'host' in rel_db1.data[self.model.unit] del rel_db1.data[self.model.unit]['host'] - self.assertNotIn('host', rel_db1.data[self.model.unit]) - self.assertEqual({}, self.harness.get_relation_data(relation_id, 'myapp/0')) + assert 'host' not in rel_db1.data[self.model.unit] + assert self.harness.get_relation_data(relation_id, 'myapp/0') == {} self.assertBackendCalls([ ('relation_ids', 'db1'), @@ -426,13 +426,13 @@ def test_relation_data_del_missing_key(self): rel_db1 = self.ensure_relation('db1') # Force memory cache to be loaded. - self.assertIn('host', rel_db1.data[self.model.unit]) + assert 'host' in rel_db1.data[self.model.unit] with self.harness._event_context('foo_event'): rel_db1.data[self.model.unit]['port'] = '' # Same as a delete, should not fail. - self.assertNotIn('port', rel_db1.data[self.model.unit]) + assert 'port' not in rel_db1.data[self.model.unit] with self.harness._event_context('foo_event'): - self.assertEqual({'host': 'bar'}, - self.harness.get_relation_data(relation_id, 'myapp/0')) + assert self.harness.get_relation_data(relation_id, 'myapp/0') == \ + {'host': 'bar'} self.assertBackendCalls([ ('relation_ids', 'db1'), @@ -465,15 +465,15 @@ def broken_update_relation_data( rel_db1 = self.ensure_relation('db1') # Force memory cache to be loaded. - self.assertIn('host', rel_db1.data[self.model.unit]) + assert 'host' in rel_db1.data[self.model.unit] with self.harness._event_context('foo_event'): - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): rel_db1.data[self.model.unit]['host'] = 'bar' - self.assertEqual(rel_db1.data[self.model.unit]['host'], 'myapp-0') - with self.assertRaises(ops.ModelError): + assert rel_db1.data[self.model.unit]['host'] == 'myapp-0' + with pytest.raises(ops.ModelError): del rel_db1.data[self.model.unit]['host'] - self.assertIn('host', rel_db1.data[self.model.unit]) + assert 'host' in rel_db1.data[self.model.unit] self.assertBackendCalls([ ('relation_ids', 'db1'), @@ -500,12 +500,12 @@ def test_relation_data_type_check(self): (1, 1), (None, None) ): - with self.assertRaises(ops.RelationDataError): + with pytest.raises(ops.RelationDataError): with self.harness.framework._event_context('foo_event'): rel_db1.data[self.model.unit][key] = value # type: ignore # No data has actually been changed - self.assertEqual(dict(rel_db1.data[self.model.unit]), {'host': 'myapp-0'}) + assert dict(rel_db1.data[self.model.unit]) == {'host': 'myapp-0'} self.assertBackendCalls([ ('relation_ids', 'db1'), @@ -542,20 +542,20 @@ def test_relation_local_app_data_readability_leader(self): with self.harness._event_context('foo_event'): self.resetBackendCalls() - self.assertEqual(rel_db1.data[local_app]['local'], 'data') + assert rel_db1.data[local_app]['local'] == 'data' self.assertBackendCalls([('is_leader',), ('relation_get', 1, 'myapp', True)]) self.resetBackendCalls() - self.assertEqual(repr(rel_db1.data[local_app]), repr({'local': 'data'})) + assert repr(rel_db1.data[local_app]) == repr({'local': 'data'}) # we don't get the data, because we're lazy self.assertBackendCalls([('is_leader',)]) # as well as relation data repr() in general: - self.assertIsInstance(repr(rel_db1.data), str) + assert isinstance(repr(rel_db1.data), str) def test_relation_local_app_data_readability_follower(self): relation_id = self.harness.add_relation('db1', 'remoteapp1') @@ -583,7 +583,7 @@ def test_relation_local_app_data_readability_follower(self): with self.harness._event_context('foo_event'): self.resetBackendCalls() - with self.assertRaises(ops.RelationDataError): + with pytest.raises(ops.RelationDataError): # 'local' is there, but still: rel_db1.data[local_app]['local'] @@ -591,11 +591,11 @@ def test_relation_local_app_data_readability_follower(self): self.assertBackendCalls([('is_leader', )]) # we can't see it but repr() works - self.assertEqual(repr(rel_db1.data[local_app]), '') + assert repr(rel_db1.data[local_app]) == '' self.assertBackendCalls([('is_leader', )]) # as well as relation data repr() in general: - self.assertIsInstance(repr(rel_db1.data), str) + assert isinstance(repr(rel_db1.data), str) expected_backend_calls = [ ('relation_get', 1, 'myapp/0', False), @@ -608,8 +608,8 @@ def test_relation_local_app_data_readability_follower(self): def test_relation_no_units(self): self.harness.add_relation('db1', 'remoteapp1') rel = self.ensure_relation('db1') - self.assertEqual(rel.units, set()) - self.assertIs(rel.app, self.model.get_app('remoteapp1')) + assert rel.units == set() + assert rel.app is self.model.get_app('remoteapp1') self.assertBackendCalls([ ('relation_ids', 'db1'), ('relation_list', 1), @@ -619,19 +619,19 @@ def test_relation_no_units(self): def test_config(self): self.harness._get_backend_calls(reset=True) self.harness.update_config({'foo': 'foo', 'bar': 1, 'qux': True}) - self.assertEqual(self.model.config, { + assert self.model.config == { 'foo': 'foo', 'bar': 1, 'qux': True, - }) - with self.assertRaises(TypeError): + } + with pytest.raises(TypeError): # Confirm that we cannot modify config values. self.model.config['foo'] = 'bar' # type: ignore self.assertBackendCalls([('config_get',)]) def test_config_immutable(self): - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): self.model.config = {} # type: ignore def test_is_leader(self): @@ -643,16 +643,16 @@ def test_is_leader(self): def check_remote_units(): # Cannot determine leadership for remote units. for u in self.ensure_relation('db1').units: - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): u.is_leader() - self.assertTrue(self.model.unit.is_leader()) + assert self.model.unit.is_leader() check_remote_units() # Create a new model and backend to drop a cached is-leader output. self.harness.set_leader(False) - self.assertFalse(self.model.unit.is_leader()) + assert not self.model.unit.is_leader() check_remote_units() @@ -670,53 +670,53 @@ def test_workload_version(self): ]) def test_workload_version_invalid(self): - with self.assertRaises(TypeError) as cm: + with pytest.raises(TypeError) as excinfo: self.model.unit.set_workload_version(5) # type: ignore - self.assertEqual(str(cm.exception), "workload version must be a str, not int: 5") + assert str(excinfo.value) == "workload version must be a str, not int: 5" self.assertBackendCalls([]) def test_resources(self): - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.harness.model.resources.fetch('foo') self.harness.add_resource('foo', 'foo contents\n') self.harness.add_resource('bar', '') - with self.assertRaises(NameError): + with pytest.raises(NameError): self.harness.model.resources.fetch('qux') - self.assertEqual(self.harness.model.resources.fetch('foo').name, 'foo.txt') - self.assertEqual(self.harness.model.resources.fetch('bar').name, 'bar.txt') + assert self.harness.model.resources.fetch('foo').name == 'foo.txt' + assert self.harness.model.resources.fetch('bar').name == 'bar.txt' def test_resources_immutable(self): - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): self.model.resources = object() # type: ignore def test_pod_spec(self): self.harness.set_leader(True) self.harness.model.pod.set_spec({'foo': 'bar'}) - self.assertEqual(self.harness.get_pod_spec(), ({'foo': 'bar'}, None)) + assert self.harness.get_pod_spec() == ({'foo': 'bar'}, None) self.harness.model.pod.set_spec({'bar': 'foo'}, {'qux': 'baz'}) - self.assertEqual(self.harness.get_pod_spec(), ({'bar': 'foo'}, {'qux': 'baz'})) + assert self.harness.get_pod_spec() == ({'bar': 'foo'}, {'qux': 'baz'}) # no leader -> no set pod spec self.harness.set_leader(False) - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.harness.model.pod.set_spec({'foo': 'bar'}) def test_pod_immutable(self): - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): self.model.pod = object() # type: ignore def test_base_status_instance_raises(self): - with self.assertRaises(TypeError): + with pytest.raises(TypeError): ops.StatusBase('test') class NoNameStatus(ops.StatusBase): pass - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): ops.StatusBase.register_status(NoNameStatus) # type: ignore def test_status_repr(self): @@ -728,7 +728,7 @@ def test_status_repr(self): 'UnknownStatus()': ops.UnknownStatus(), } for expected, status in test_cases.items(): - self.assertEqual(repr(status), expected) + assert repr(status) == expected def test_status_eq(self): status_types = [ @@ -738,18 +738,18 @@ def test_status_eq(self): ops.WaitingStatus, ] - self.assertEqual(ops.UnknownStatus(), ops.UnknownStatus()) + assert ops.UnknownStatus() == ops.UnknownStatus() for (i, t1) in enumerate(status_types): - self.assertNotEqual(t1(''), ops.UnknownStatus()) + assert t1('') != ops.UnknownStatus() for (j, t2) in enumerate(status_types): - self.assertNotEqual(t1('one'), t2('two')) + assert t1('one') != t2('two') if i == j: - self.assertEqual(t1('one'), t2('one')) + assert t1('one') == t2('one') else: - self.assertNotEqual(t1('one'), t2('one')) + assert t1('one') != t2('one') def test_active_message_default(self): - self.assertEqual(ops.ActiveStatus().message, '') + assert ops.ActiveStatus().message == '' def test_local_set_valid_unit_status(self): self.harness._get_backend_calls(reset=True) @@ -774,9 +774,9 @@ def test_local_set_valid_unit_status(self): for test_case, target_status, backend_call in test_cases: with self.subTest(test_case): self.model.unit.status = target_status - self.assertEqual(self.model.unit.status, target_status) + assert self.model.unit.status == target_status self.model.unit._invalidate() - self.assertEqual(self.model.unit.status, target_status) + assert self.model.unit.status == target_status self.assertBackendCalls([backend_call, ('status_get', {'is_app': False})]) def test_local_set_valid_app_status(self): @@ -802,9 +802,9 @@ def test_local_set_valid_app_status(self): for test_case, target_status, backend_call in test_cases: with self.subTest(test_case): self.model.app.status = target_status - self.assertEqual(self.model.app.status, target_status) + assert self.model.app.status == target_status self.model.app._invalidate() - self.assertEqual(self.model.app.status, target_status) + assert self.model.app.status == target_status # There is a backend call to check if we can set the value, # and then another check each time we assert the status above expected_calls = [ @@ -816,18 +816,18 @@ def test_local_set_valid_app_status(self): def test_set_app_status_non_leader_raises(self): self.harness.set_leader(False) - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): self.model.app.status - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): self.model.app.status = ops.ActiveStatus() def test_set_unit_status_invalid(self): - with self.assertRaises(ops.InvalidStatusError): + with pytest.raises(ops.InvalidStatusError): self.model.unit.status = 'blocked' # type: ignore def test_set_app_status_invalid(self): - with self.assertRaises(ops.InvalidStatusError): + with pytest.raises(ops.InvalidStatusError): self.model.app.status = 'blocked' # type: ignore def test_remote_unit_status(self): @@ -839,7 +839,7 @@ def test_remote_unit_status(self): self.resetBackendCalls() # Remote unit status is always unknown. - self.assertEqual(remote_unit.status, ops.UnknownStatus()) + assert remote_unit.status == ops.UnknownStatus() test_statuses = ( ops.UnknownStatus(), @@ -851,7 +851,7 @@ def test_remote_unit_status(self): for target_status in test_statuses: with self.subTest(target_status.name): - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): remote_unit.status = target_status self.assertBackendCalls([]) @@ -864,7 +864,7 @@ def test_remote_app_status(self): # Remote application status is always unknown. assert remoteapp1 is not None - self.assertIsInstance(remoteapp1.status, ops.UnknownStatus) + assert isinstance(remoteapp1.status, ops.UnknownStatus) test_statuses = ( ops.UnknownStatus(), @@ -875,7 +875,7 @@ def test_remote_app_status(self): ) for target_status in test_statuses: with self.subTest(target_status.name): - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): remoteapp1.status = target_status self.assertBackendCalls([]) @@ -908,9 +908,9 @@ def test_storage(self): ''') fake_script(self, 'storage-add', '') - self.assertEqual(len(model.storages), 2) - self.assertEqual(model.storages.keys(), meta.storages.keys()) - self.assertIn('disks', model.storages) + assert len(model.storages) == 2 + assert model.storages.keys() == meta.storages.keys() + assert 'disks' in model.storages with pytest.raises(KeyError, match='Did you mean'): model.storages['does-not-exist'] @@ -920,35 +920,35 @@ def test_storage(self): 1: {'name': 'disks', 'location': pathlib.Path('/var/srv/disks/1')}, } for storage in model.storages['disks']: - self.assertEqual(storage.name, 'disks') - self.assertIn(storage.id, test_cases) - self.assertEqual(storage.name, test_cases[storage.id]['name']) - self.assertEqual(storage.location, test_cases[storage.id]['location']) + assert storage.name == 'disks' + assert storage.id in test_cases + assert storage.name == test_cases[storage.id]['name'] + assert storage.location == test_cases[storage.id]['location'] - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['storage-list', 'disks', '--format=json'], ['storage-get', '-s', 'disks/0', 'location', '--format=json'], ['storage-get', '-s', 'disks/1', 'location', '--format=json'], - ]) + ] self.assertSequenceEqual(model.storages['data'], []) model.storages.request('data', count=3) - self.assertEqual(fake_script_calls(self), [ + assert fake_script_calls(self) == [ ['storage-list', 'data', '--format=json'], ['storage-add', 'data=3'], - ]) + ] # Try to add storage not present in charm metadata. - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): model.storages.request('deadbeef') # Invalid count parameter types. for count_v in [None, False, 2.0, 'a', b'beef', object]: - with self.assertRaises(TypeError): + with pytest.raises(TypeError): model.storages.request('data', count_v) # type: ignore def test_storages_immutable(self): - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): self.model.storages = {} # type: ignore def resetBackendCalls(self): # noqa: N802 @@ -959,15 +959,15 @@ def assertBackendCalls( # noqa: N802 expected: typing.List[typing.Tuple[typing.Any, ...]], *, reset: bool = True): - self.assertEqual(expected, self.harness._get_backend_calls(reset=reset)) + assert expected == self.harness._get_backend_calls(reset=reset) def test_run_error(self): model = ops.Model(ops.CharmMeta(), _ModelBackend('myapp/0')) fake_script(self, 'status-get', """echo 'ERROR cannot get status' >&2; exit 1""") - with self.assertRaises(ops.ModelError) as cm: + with pytest.raises(ops.ModelError) as excinfo: _ = model.unit.status.message - self.assertEqual(str(cm.exception), 'ERROR cannot get status\n') - self.assertEqual(cm.exception.args[0], 'ERROR cannot get status\n') + assert str(excinfo.value) == 'ERROR cannot get status\n' + assert excinfo.value.args[0] == 'ERROR cannot get status\n' @patch("grp.getgrgid") @patch("pwd.getpwuid") @@ -1363,53 +1363,53 @@ def test_mocked_get_services(self): }) s = c.get_service('baz') # So far, so good - self.assertTrue(s) - self.assertTrue('baz' in c.get_services()) + assert s + assert 'baz' in c.get_services() def test_planned_units(self): rel_id = self.peer_rel_id # Test that we always count ourself. - self.assertEqual(self.app.planned_units(), 1) + assert self.app.planned_units() == 1 # Add some units, and verify count. self.harness.add_relation_unit(rel_id, 'myapp/1') self.harness.add_relation_unit(rel_id, 'myapp/2') - self.assertEqual(self.app.planned_units(), 3) + assert self.app.planned_units() == 3 self.harness.add_relation_unit(rel_id, 'myapp/3') - self.assertEqual(self.app.planned_units(), 4) + assert self.app.planned_units() == 4 # And remove a unit self.harness.remove_relation_unit(rel_id, 'myapp/2') - self.assertEqual(self.app.planned_units(), 3) + assert self.app.planned_units() == 3 def test_planned_units_user_set(self): self.harness.set_planned_units(1) - self.assertEqual(self.app.planned_units(), 1) + assert self.app.planned_units() == 1 self.harness.set_planned_units(2) - self.assertEqual(self.app.planned_units(), 2) + assert self.app.planned_units() == 2 self.harness.set_planned_units(100) - self.assertEqual(self.app.planned_units(), 100) + assert self.app.planned_units() == 100 def test_planned_units_garbage_values(self): # Planned units should be a positive integer, or zero. - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.harness.set_planned_units(-1) # Verify that we didn't set our value before raising the error. - self.assertTrue(self.harness._backend._planned_units is None) + assert self.harness._backend._planned_units is None # Verify that we still get the default value back from .planned_units. - self.assertEqual(self.app.planned_units(), 1) + assert self.app.planned_units() == 1 - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.harness.set_planned_units("foo") # type: ignore - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.harness.set_planned_units(-3423000102312321090) def test_planned_units_override(self): @@ -1427,11 +1427,11 @@ def test_planned_units_override(self): self.harness.add_relation_unit(peer_id, 'myapp/2') self.harness.add_relation_unit(peer_id, 'myapp/3') - self.assertEqual(self.app.planned_units(), 10) + assert self.app.planned_units() == 10 # Verify that we can clear the override. self.harness.reset_planned_units() - self.assertEqual(self.app.planned_units(), 4) # self + 3 peers + assert self.app.planned_units() == 4 # self + 3 peers class TestContainers(unittest.TestCase): @@ -1449,20 +1449,20 @@ def setUp(self): def test_unit_containers(self): containers = self.model.unit.containers - self.assertEqual(sorted(containers), ['c1', 'c2']) - self.assertEqual(len(containers), 2) - self.assertIn('c1', containers) - self.assertIn('c2', containers) - self.assertNotIn('c3', containers) + assert sorted(containers) == ['c1', 'c2'] + assert len(containers) == 2 + assert 'c1' in containers + assert 'c2' in containers + assert 'c3' not in containers for name in ['c1', 'c2']: container = containers[name] - self.assertIsInstance(container, ops.Container) - self.assertEqual(container.name, name) - self.assertIsInstance(container.pebble, pebble.Client) - with self.assertRaises(KeyError): + assert isinstance(container, ops.Container) + assert container.name == name + assert isinstance(container.pebble, pebble.Client) + with pytest.raises(KeyError): containers['c3'] - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): other_unit = self.model.get_unit('other') other_unit.containers @@ -1470,13 +1470,13 @@ def test_unit_get_container(self): unit = self.model.unit for name in ['c1', 'c2']: container = unit.get_container(name) - self.assertIsInstance(container, ops.Container) - self.assertEqual(container.name, name) - self.assertIsInstance(container.pebble, pebble.Client) - with self.assertRaises(ops.ModelError): + assert isinstance(container, ops.Container) + assert container.name == name + assert isinstance(container.pebble, pebble.Client) + with pytest.raises(ops.ModelError): unit.get_container('c3') - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): other_unit = self.model.get_unit('other') other_unit.get_container('foo') @@ -1495,52 +1495,52 @@ def setUp(self): self.pebble: MockPebbleClient = self.container.pebble # type: ignore def test_socket_path(self): - self.assertEqual(self.pebble.socket_path, '/charm/containers/c1/pebble.socket') + assert self.pebble.socket_path == '/charm/containers/c1/pebble.socket' def test_autostart(self): self.container.autostart() - self.assertEqual(self.pebble.requests, [('autostart',)]) + assert self.pebble.requests == [('autostart',)] def test_replan(self): self.container.replan() - self.assertEqual(self.pebble.requests, [('replan',)]) + assert self.pebble.requests == [('replan',)] def test_can_connect(self): self.pebble.responses.append(pebble.SystemInfo.from_dict({'version': '1.0.0'})) - self.assertTrue(self.container.can_connect()) - self.assertEqual(self.pebble.requests, [('get_system_info',)]) + assert self.container.can_connect() + assert self.pebble.requests == [('get_system_info',)] def test_start(self): self.container.start('foo') self.container.start('foo', 'bar') - self.assertEqual(self.pebble.requests, [ + assert self.pebble.requests == [ ('start', ('foo',)), ('start', ('foo', 'bar')), - ]) + ] def test_start_no_arguments(self): - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.container.start() def test_stop(self): self.container.stop('foo') self.container.stop('foo', 'bar') - self.assertEqual(self.pebble.requests, [ + assert self.pebble.requests == [ ('stop', ('foo',)), ('stop', ('foo', 'bar')), - ]) + ] def test_stop_no_arguments(self): - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.container.stop() def test_restart(self): self.container.restart('foo') self.container.restart('foo', 'bar') - self.assertEqual(self.pebble.requests, [ + assert self.pebble.requests == [ ('restart', ('foo',)), ('restart', ('foo', 'bar')), - ]) + ] def test_restart_fallback(self): def restart_services(service_names: str): @@ -1557,7 +1557,7 @@ def restart_services(service_names: str): ]) self.container.restart('foo', 'bar') - self.assertEqual(self.pebble.requests, [ + assert self.pebble.requests == [ # This is the first request, which in real life fails with APIError on older versions ('restart', ('foo', 'bar')), # Next the code should loop over the started services, and stop them @@ -1565,19 +1565,19 @@ def restart_services(service_names: str): ('stop', ('foo',)), # Then start all the specified services ('start', ('foo', 'bar')) - ]) + ] def test_restart_fallback_non_400_error(self): def restart_services(service_names: str): raise pebble.APIError({}, 500, "", "") self.pebble.restart_services = restart_services - with self.assertRaises(pebble.APIError) as cm: + with pytest.raises(pebble.APIError) as excinfo: self.container.restart('foo') - self.assertEqual(cm.exception.code, 500) + assert excinfo.value.code == 500 def test_restart_no_arguments(self): - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.container.restart() def test_type_errors(self): @@ -1592,10 +1592,10 @@ def test_type_errors(self): model = ops.Model(meta, backend) container = model.unit.containers['c1'] - with self.assertRaises(TypeError): + with pytest.raises(TypeError): container.start(['foo']) # type: ignore - with self.assertRaises(TypeError): + with pytest.raises(TypeError): container.stop(['foo']) # type: ignore def test_add_layer(self): @@ -1603,24 +1603,24 @@ def test_add_layer(self): self.container.add_layer('b', {'summary': 'dict'}) self.container.add_layer('c', pebble.Layer('summary: Layer')) self.container.add_layer('d', 'summary: str\n', combine=True) - self.assertEqual(self.pebble.requests, [ + assert self.pebble.requests == [ ('add_layer', 'a', 'summary: str\n', False), ('add_layer', 'b', 'summary: dict\n', False), ('add_layer', 'c', 'summary: Layer\n', False), ('add_layer', 'd', 'summary: str\n', True), - ]) + ] # combine is a keyword-only arg (should be combine=True) - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.container.add_layer('x', {}, True) # type: ignore def test_get_plan(self): plan_yaml = 'services:\n foo:\n override: replace\n command: bar' self.pebble.responses.append(pebble.Plan(plan_yaml)) plan = self.container.get_plan() - self.assertEqual(self.pebble.requests, [('get_plan',)]) - self.assertIsInstance(plan, pebble.Plan) - self.assertEqual(plan.to_yaml(), yaml.safe_dump(yaml.safe_load(plan_yaml))) + assert self.pebble.requests == [('get_plan',)] + assert isinstance(plan, pebble.Plan) + assert plan.to_yaml() == yaml.safe_dump(yaml.safe_load(plan_yaml)) @staticmethod def _make_service(name: str, startup: str, current: str): @@ -1634,52 +1634,52 @@ def test_get_services(self): ] self.pebble.responses.append(two_services) services = self.container.get_services() - self.assertEqual(len(services), 2) - self.assertEqual(set(services), {'s1', 's2'}) - self.assertEqual(services['s1'].name, 's1') - self.assertEqual(services['s1'].startup, pebble.ServiceStartup.ENABLED) - self.assertEqual(services['s1'].current, pebble.ServiceStatus.ACTIVE) - self.assertEqual(services['s2'].name, 's2') - self.assertEqual(services['s2'].startup, pebble.ServiceStartup.DISABLED) - self.assertEqual(services['s2'].current, pebble.ServiceStatus.INACTIVE) + assert len(services) == 2 + assert set(services) == {'s1', 's2'} + assert services['s1'].name == 's1' + assert services['s1'].startup == pebble.ServiceStartup.ENABLED + assert services['s1'].current == pebble.ServiceStatus.ACTIVE + assert services['s2'].name == 's2' + assert services['s2'].startup == pebble.ServiceStartup.DISABLED + assert services['s2'].current == pebble.ServiceStatus.INACTIVE self.pebble.responses.append(two_services) services = self.container.get_services('s1', 's2') - self.assertEqual(len(services), 2) - self.assertEqual(set(services), {'s1', 's2'}) - self.assertEqual(services['s1'].name, 's1') - self.assertEqual(services['s1'].startup, pebble.ServiceStartup.ENABLED) - self.assertEqual(services['s1'].current, pebble.ServiceStatus.ACTIVE) - self.assertEqual(services['s2'].name, 's2') - self.assertEqual(services['s2'].startup, pebble.ServiceStartup.DISABLED) - self.assertEqual(services['s2'].current, pebble.ServiceStatus.INACTIVE) - - self.assertEqual(self.pebble.requests, [ + assert len(services) == 2 + assert set(services) == {'s1', 's2'} + assert services['s1'].name == 's1' + assert services['s1'].startup == pebble.ServiceStartup.ENABLED + assert services['s1'].current == pebble.ServiceStatus.ACTIVE + assert services['s2'].name == 's2' + assert services['s2'].startup == pebble.ServiceStartup.DISABLED + assert services['s2'].current == pebble.ServiceStatus.INACTIVE + + assert self.pebble.requests == [ ('get_services', None), ('get_services', ('s1', 's2')), - ]) + ] def test_get_service(self): # Single service returned successfully self.pebble.responses.append([self._make_service('s1', 'enabled', 'active')]) s = self.container.get_service('s1') - self.assertEqual(self.pebble.requests, [('get_services', ('s1', ))]) - self.assertEqual(s.name, 's1') - self.assertEqual(s.startup, pebble.ServiceStartup.ENABLED) - self.assertEqual(s.current, pebble.ServiceStatus.ACTIVE) + assert self.pebble.requests == [('get_services', ('s1', ))] + assert s.name == 's1' + assert s.startup == pebble.ServiceStartup.ENABLED + assert s.current == pebble.ServiceStatus.ACTIVE # If Pebble returns no services, should be a ops.ModelError self.pebble.responses.append([]) - with self.assertRaises(ops.ModelError) as cm: + with pytest.raises(ops.ModelError) as excinfo: self.container.get_service('s2') - self.assertEqual(str(cm.exception), "service 's2' not found") + assert str(excinfo.value) == "service 's2' not found" # If Pebble returns more than one service, RuntimeError is raised self.pebble.responses.append([ self._make_service('s1', 'enabled', 'active'), self._make_service('s2', 'disabled', 'inactive'), ]) - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): self.container.get_service('s1') def test_get_checks(self): @@ -1701,31 +1701,31 @@ def test_get_checks(self): self.pebble.responses.append(response_checks) checks = self.container.get_checks() - self.assertEqual(len(checks), 2) - self.assertEqual(checks['c1'].name, 'c1') - self.assertEqual(checks['c1'].level, pebble.CheckLevel.UNSET) - self.assertEqual(checks['c1'].status, pebble.CheckStatus.UP) - self.assertEqual(checks['c1'].failures, 0) - self.assertEqual(checks['c1'].threshold, 3) - self.assertEqual(checks['c2'].name, 'c2') - self.assertEqual(checks['c2'].level, pebble.CheckLevel.ALIVE) - self.assertEqual(checks['c2'].status, pebble.CheckStatus.DOWN) - self.assertEqual(checks['c2'].failures, 2) - self.assertEqual(checks['c2'].threshold, 2) + assert len(checks) == 2 + assert checks['c1'].name == 'c1' + assert checks['c1'].level == pebble.CheckLevel.UNSET + assert checks['c1'].status == pebble.CheckStatus.UP + assert checks['c1'].failures == 0 + assert checks['c1'].threshold == 3 + assert checks['c2'].name == 'c2' + assert checks['c2'].level == pebble.CheckLevel.ALIVE + assert checks['c2'].status == pebble.CheckStatus.DOWN + assert checks['c2'].failures == 2 + assert checks['c2'].threshold == 2 self.pebble.responses.append(response_checks[1:2]) checks = self.container.get_checks('c1', 'c2', level=pebble.CheckLevel.ALIVE) - self.assertEqual(len(checks), 1) - self.assertEqual(checks['c2'].name, 'c2') - self.assertEqual(checks['c2'].level, pebble.CheckLevel.ALIVE) - self.assertEqual(checks['c2'].status, pebble.CheckStatus.DOWN) - self.assertEqual(checks['c2'].failures, 2) - self.assertEqual(checks['c2'].threshold, 2) - - self.assertEqual(self.pebble.requests, [ + assert len(checks) == 1 + assert checks['c2'].name == 'c2' + assert checks['c2'].level == pebble.CheckLevel.ALIVE + assert checks['c2'].status == pebble.CheckStatus.DOWN + assert checks['c2'].failures == 2 + assert checks['c2'].threshold == 2 + + assert self.pebble.requests == [ ('get_checks', None, None), ('get_checks', pebble.CheckLevel.ALIVE, ('c1', 'c2')), - ]) + ] def test_get_check(self): # Single check returned successfully @@ -1738,18 +1738,18 @@ def test_get_check(self): }) # type: ignore ]) c = self.container.get_check('c1') - self.assertEqual(self.pebble.requests, [('get_checks', None, ('c1', ))]) - self.assertEqual(c.name, 'c1') - self.assertEqual(c.level, pebble.CheckLevel.UNSET) - self.assertEqual(c.status, pebble.CheckStatus.UP) - self.assertEqual(c.failures, 0) - self.assertEqual(c.threshold, 3) + assert self.pebble.requests == [('get_checks', None, ('c1', ))] + assert c.name == 'c1' + assert c.level == pebble.CheckLevel.UNSET + assert c.status == pebble.CheckStatus.UP + assert c.failures == 0 + assert c.threshold == 3 # If Pebble returns no checks, should be a ops.ModelError self.pebble.responses.append([]) - with self.assertRaises(ops.ModelError) as cm: + with pytest.raises(ops.ModelError) as excinfo: self.container.get_check('c2') - self.assertEqual(str(cm.exception), "check 'c2' not found") + assert str(excinfo.value) == "check 'c2' not found" # If Pebble returns more than one check, RuntimeError is raised self.pebble.responses.append([ @@ -1767,110 +1767,110 @@ def test_get_check(self): 'threshold': 2, }), ]) - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): self.container.get_check('c1') def test_pull(self): self.pebble.responses.append('dummy1') got = self.container.pull('/path/1') - self.assertEqual(got, 'dummy1') - self.assertEqual(self.pebble.requests, [ + assert got == 'dummy1' + assert self.pebble.requests == [ ('pull', '/path/1', 'utf-8'), - ]) + ] self.pebble.requests = [] self.pebble.responses.append(b'dummy2') got = self.container.pull('/path/2', encoding=None) - self.assertEqual(got, b'dummy2') - self.assertEqual(self.pebble.requests, [ + assert got == b'dummy2' + assert self.pebble.requests == [ ('pull', '/path/2', None), - ]) + ] def test_push(self): self.container.push('/path/1', 'content1') - self.assertEqual(self.pebble.requests, [ + assert self.pebble.requests == [ ('push', '/path/1', 'content1', 'utf-8', False, None, None, None, None, None), - ]) + ] self.pebble.requests = [] self.container.push('/path/2', b'content2', make_dirs=True, permissions=0o600, user_id=12, user='bob', group_id=34, group='staff') - self.assertEqual(self.pebble.requests, [ + assert self.pebble.requests == [ ('push', '/path/2', b'content2', 'utf-8', True, 0o600, 12, 'bob', 34, 'staff'), - ]) + ] def test_list_files(self): self.pebble.responses.append('dummy1') ret = self.container.list_files('/path/1') - self.assertEqual(ret, 'dummy1') - self.assertEqual(self.pebble.requests, [ + assert ret == 'dummy1' + assert self.pebble.requests == [ ('list_files', '/path/1', None, False), - ]) + ] self.pebble.requests = [] self.pebble.responses.append('dummy2') ret = self.container.list_files('/path/2', pattern='*.txt', itself=True) - self.assertEqual(ret, 'dummy2') - self.assertEqual(self.pebble.requests, [ + assert ret == 'dummy2' + assert self.pebble.requests == [ ('list_files', '/path/2', '*.txt', True), - ]) + ] def test_make_dir(self): self.container.make_dir('/path/1') - self.assertEqual(self.pebble.requests, [ + assert self.pebble.requests == [ ('make_dir', '/path/1', False, None, None, None, None, None), - ]) + ] self.pebble.requests = [] self.container.make_dir('/path/2', make_parents=True, permissions=0o700, user_id=12, user='bob', group_id=34, group='staff') - self.assertEqual(self.pebble.requests, [ + assert self.pebble.requests == [ ('make_dir', '/path/2', True, 0o700, 12, 'bob', 34, 'staff'), - ]) + ] def test_remove_path(self): self.container.remove_path('/path/1') - self.assertEqual(self.pebble.requests, [ + assert self.pebble.requests == [ ('remove_path', '/path/1', False), - ]) + ] self.pebble.requests = [] self.container.remove_path('/path/2', recursive=True) - self.assertEqual(self.pebble.requests, [ + assert self.pebble.requests == [ ('remove_path', '/path/2', True), - ]) + ] def test_can_connect_simple(self): self.pebble.responses.append(pebble.SystemInfo.from_dict({'version': '1.0.0'})) - self.assertTrue(self.container.can_connect()) + assert self.container.can_connect() def test_can_connect_connection_error(self): def raise_error(): raise pebble.ConnectionError('connection error!') self.pebble.get_system_info = raise_error with self.assertLogs('ops', level='DEBUG') as cm: - self.assertFalse(self.container.can_connect()) - self.assertEqual(len(cm.output), 1) - self.assertRegex(cm.output[0], r'DEBUG:ops.model:.*: connection error!') + assert not self.container.can_connect() + assert len(cm.output) == 1 + assert re.search(r'DEBUG:ops.model:.*: connection error!', cm.output[0]) def test_can_connect_file_not_found_error(self): def raise_error(): raise FileNotFoundError('file not found!') self.pebble.get_system_info = raise_error with self.assertLogs('ops', level='DEBUG') as cm: - self.assertFalse(self.container.can_connect()) - self.assertEqual(len(cm.output), 1) - self.assertRegex(cm.output[0], r'DEBUG:ops.model:.*: file not found!') + assert not self.container.can_connect() + assert len(cm.output) == 1 + assert re.search(r'DEBUG:ops.model:.*: file not found!', cm.output[0]) def test_can_connect_api_error(self): def raise_error(): raise pebble.APIError({'body': ''}, 404, 'status', 'api error!') self.pebble.get_system_info = raise_error with self.assertLogs('ops') as cm: - self.assertFalse(self.container.can_connect()) - self.assertEqual(len(cm.output), 1) - self.assertRegex(cm.output[0], r'WARNING:ops.model:.*: api error!') + assert not self.container.can_connect() + assert len(cm.output) == 1 + assert re.search(r'WARNING:ops.model:.*: api error!', cm.output[0]) @patch('model.JujuVersion.from_environ', new=lambda: ops.model.JujuVersion('3.1.6')) def test_exec(self): @@ -1893,7 +1893,7 @@ def test_exec(self): encoding="encoding", combine_stderr=True, ) - self.assertEqual(self.pebble.requests, [ + assert self.pebble.requests == [ ('exec', ['echo', 'foo'], dict( service_context='srv1', environment={'K1': 'V1', 'K2': 'V2'}, @@ -1909,28 +1909,28 @@ def test_exec(self): encoding="encoding", combine_stderr=True, )) - ]) - self.assertEqual(p, 'fake_exec_process') + ] + assert p == 'fake_exec_process' @patch('model.JujuVersion.from_environ', new=lambda: ops.model.JujuVersion('3.1.5')) def test_exec_service_context_not_supported(self): - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): self.container.exec(['foo'], service_context='srv1') def test_send_signal(self): - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.container.send_signal('SIGHUP') self.container.send_signal('SIGHUP', 's1') - self.assertEqual(self.pebble.requests, [ + assert self.pebble.requests == [ ('send_signal', 'SIGHUP', ('s1',)), - ]) + ] self.pebble.requests = [] self.container.send_signal('SIGHUP', 's1', 's2') - self.assertEqual(self.pebble.requests, [ + assert self.pebble.requests == [ ('send_signal', 'SIGHUP', ('s1', 's2')), - ]) + ] self.pebble.requests = [] def test_get_notice(self): @@ -1946,19 +1946,19 @@ def test_get_notice(self): })) notice = self.container.get_notice('123') - self.assertEqual(notice.id, '123') - self.assertEqual(notice.type, pebble.NoticeType.CUSTOM) - self.assertEqual(notice.key, 'example.com/a') + assert notice.id == '123' + assert notice.type == pebble.NoticeType.CUSTOM + assert notice.key == 'example.com/a' - self.assertEqual(self.pebble.requests, [ + assert self.pebble.requests == [ ('get_notice', '123'), - ]) + ] def test_get_notice_not_found(self): def raise_error(id: str): raise pebble.APIError({'body': ''}, 404, 'status', 'api error!') self.pebble.get_notice = raise_error - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.container.get_notice('123') def test_get_notices(self): @@ -1981,17 +1981,17 @@ def test_get_notices(self): types=[pebble.NoticeType.CUSTOM], keys=['example.com/a', 'example.com/b'], ) - self.assertEqual(len(notices), 1) - self.assertEqual(notices[0].id, '124') - self.assertEqual(notices[0].type, pebble.NoticeType.CUSTOM) - self.assertEqual(notices[0].key, 'example.com/b') + assert len(notices) == 1 + assert notices[0].id == '124' + assert notices[0].type == pebble.NoticeType.CUSTOM + assert notices[0].key == 'example.com/b' - self.assertEqual(self.pebble.requests, [('get_notices', dict( + assert self.pebble.requests == [('get_notices', dict( user_id=1000, users=pebble.NoticesUsers.ALL, types=[pebble.NoticeType.CUSTOM], keys=['example.com/a', 'example.com/b'], - ))]) + ))] class MockPebbleBackend(_ModelBackend): @@ -2183,19 +2183,19 @@ def ensure_relation(self, name: str = 'db1', relation_id: typing.Optional[int] = def ensure_binding(self, binding_key: typing.Union[str, ops.Relation]): """Wrapper around self.model.get_binding that enforces that None is not returned.""" binding = self.model.get_binding(binding_key) - self.assertIsNotNone(binding) + assert binding is not None assert binding is not None # Type checkers understand this, but not the previous line. return binding def _check_binding_data(self, binding_name: str, binding: ops.Binding): - self.assertEqual(binding.name, binding_name) - self.assertEqual(binding.network.bind_address, ipaddress.ip_address('192.0.2.2')) - self.assertEqual(binding.network.ingress_address, ipaddress.ip_address('192.0.2.2')) + assert binding.name == binding_name + assert binding.network.bind_address == ipaddress.ip_address('192.0.2.2') + assert binding.network.ingress_address == ipaddress.ip_address('192.0.2.2') # /32 and /128 CIDRs are valid one-address networks for IPv{4,6}Network types respectively. - self.assertEqual(binding.network.egress_subnets, [ipaddress.ip_network('192.0.2.2/32'), - ipaddress.ip_network('192.0.3.0/24'), - ipaddress.ip_network('dead:beef::/64'), - ipaddress.ip_network('2001:db8::3/128')]) + assert binding.network.egress_subnets == [ipaddress.ip_network('192.0.2.2/32'), + ipaddress.ip_network('192.0.3.0/24'), + ipaddress.ip_network('dead:beef::/64'), + ipaddress.ip_network('2001:db8::3/128')] for (i, (name, address, subnet)) in enumerate([ ('lo', '192.0.2.2', '192.0.2.0/24'), @@ -2203,9 +2203,9 @@ def _check_binding_data(self, binding_name: str, binding: ops.Binding): ('tun', '192.0.3.3', '192.0.3.3/32'), ('tun', '2001:db8::3', '2001:db8::3/128'), ('tun', 'fe80::1:1', 'fe80::/64')]): - self.assertEqual(binding.network.interfaces[i].name, name) - self.assertEqual(binding.network.interfaces[i].address, ipaddress.ip_address(address)) - self.assertEqual(binding.network.interfaces[i].subnet, ipaddress.ip_network(subnet)) + assert binding.network.interfaces[i].name == name + assert binding.network.interfaces[i].address == ipaddress.ip_address(address) + assert binding.network.interfaces[i].subnet == ipaddress.ip_network(subnet) for (i, (name, address, subnet)) in enumerate([ ('lo', '192.0.2.2', '192.0.2.0/24'), @@ -2213,14 +2213,14 @@ def _check_binding_data(self, binding_name: str, binding: ops.Binding): ('tun', '192.0.3.3', '192.0.3.3/32'), ('tun', '2001:db8::3', '2001:db8::3/128'), ('tun', 'fe80::1:1', 'fe80::/64')]): - self.assertEqual(binding.network.interfaces[i].name, name) - self.assertEqual(binding.network.interfaces[i].address, ipaddress.ip_address(address)) - self.assertEqual(binding.network.interfaces[i].subnet, ipaddress.ip_network(subnet)) + assert binding.network.interfaces[i].name == name + assert binding.network.interfaces[i].address == ipaddress.ip_address(address) + assert binding.network.interfaces[i].subnet == ipaddress.ip_network(subnet) def test_invalid_keys(self): # Basic validation for passing invalid keys. for name in (object, 0): - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.model.get_binding(name) # type: ignore def test_dead_relations(self): @@ -2237,11 +2237,11 @@ def test_dead_relations(self): ''') # Validate the behavior for dead relations. binding = ops.Binding('db0', 42, self.model._backend) - self.assertEqual(binding.network.bind_address, ipaddress.ip_address('192.0.2.2')) - self.assertEqual(fake_script_calls(self, clear=True), [ + assert binding.network.bind_address == ipaddress.ip_address('192.0.2.2') + assert fake_script_calls(self, clear=True) == [ ['network-get', 'db0', '-r', '42', '--format=json'], ['network-get', 'db0', '--format=json'], - ]) + ] def test_broken_relations(self): meta = ops.CharmMeta() @@ -2267,9 +2267,9 @@ def test_broken_relations(self): fi """) fake_script(self, 'relation-list', """echo '""'""") - self.assertTrue(model.relations['db0']) - self.assertFalse(model.relations['db1']) - self.assertTrue(model.relations['db2']) + assert model.relations['db0'] + assert not model.relations['db1'] + assert model.relations['db2'] def test_binding_by_relation_name(self): fake_script(self, 'network-get', @@ -2279,7 +2279,7 @@ def test_binding_by_relation_name(self): binding = self.ensure_binding(binding_name) self._check_binding_data(binding_name, binding) - self.assertEqual(fake_script_calls(self, clear=True), expected_calls) + assert fake_script_calls(self, clear=True) == expected_calls def test_binding_by_relation(self): fake_script(self, 'network-get', @@ -2293,7 +2293,7 @@ def test_binding_by_relation(self): ] binding = self.ensure_binding(self.ensure_relation(binding_name)) self._check_binding_data(binding_name, binding) - self.assertEqual(fake_script_calls(self, clear=True), expected_calls) + assert fake_script_calls(self, clear=True) == expected_calls def test_binding_no_iface_name(self): network_get_out_obj = { @@ -2324,10 +2324,10 @@ def test_binding_no_iface_name(self): expected_calls = [['network-get', 'db0', '--format=json']] binding = self.ensure_binding(binding_name) - self.assertEqual(binding.name, 'db0') - self.assertEqual(binding.network.bind_address, ipaddress.ip_address('10.1.89.35')) - self.assertEqual(binding.network.ingress_address, ipaddress.ip_address('10.152.183.158')) - self.assertEqual(fake_script_calls(self, clear=True), expected_calls) + assert binding.name == 'db0' + assert binding.network.bind_address == ipaddress.ip_address('10.1.89.35') + assert binding.network.ingress_address == ipaddress.ip_address('10.152.183.158') + assert fake_script_calls(self, clear=True) == expected_calls def test_missing_bind_addresses(self): network_data = json.dumps({}) @@ -2335,7 +2335,7 @@ def test_missing_bind_addresses(self): f'''[ "$1" = db0 ] && echo '{network_data}' || exit 1''') binding_name = 'db0' binding = self.ensure_binding(self.ensure_relation(binding_name)) - self.assertEqual(binding.network.interfaces, []) + assert binding.network.interfaces == [] def test_empty_bind_addresses(self): network_data = json.dumps({'bind-addresses': [{}]}) @@ -2343,7 +2343,7 @@ def test_empty_bind_addresses(self): f'''[ "$1" = db0 ] && echo '{network_data}' || exit 1''') binding_name = 'db0' binding = self.ensure_binding(self.ensure_relation(binding_name)) - self.assertEqual(binding.network.interfaces, []) + assert binding.network.interfaces == [] def test_no_bind_addresses(self): network_data = json.dumps({'bind-addresses': [{'addresses': None}]}) @@ -2351,7 +2351,7 @@ def test_no_bind_addresses(self): f'''[ "$1" = db0 ] && echo '{network_data}' || exit 1''') binding_name = 'db0' binding = self.ensure_binding(self.ensure_relation(binding_name)) - self.assertEqual(binding.network.interfaces, []) + assert binding.network.interfaces == [] def test_empty_interface_info(self): network_data = json.dumps({ @@ -2364,10 +2364,10 @@ def test_empty_interface_info(self): f'''[ "$1" = db0 ] && echo '{network_data}' || exit 1''') binding_name = 'db0' binding = self.ensure_binding(self.ensure_relation(binding_name)) - self.assertEqual(len(binding.network.interfaces), 1) + assert len(binding.network.interfaces) == 1 interface = binding.network.interfaces[0] - self.assertIsNone(interface.address) - self.assertIsNone(interface.subnet) + assert interface.address is None + assert interface.subnet is None def test_missing_ingress_addresses(self): network_data = json.dumps({ @@ -2377,8 +2377,8 @@ def test_missing_ingress_addresses(self): f'''[ "$1" = db0 ] && echo '{network_data}' || exit 1''') binding_name = 'db0' binding = self.ensure_binding(self.ensure_relation(binding_name)) - self.assertEqual(binding.network.ingress_addresses, []) - self.assertEqual(binding.network.ingress_address, None) + assert binding.network.ingress_addresses == [] + assert binding.network.ingress_address is None def test_missing_egress_subnets(self): network_data = json.dumps({ @@ -2389,7 +2389,7 @@ def test_missing_egress_subnets(self): f'''[ "$1" = db0 ] && echo '{network_data}' || exit 1''') binding_name = 'db0' binding = self.ensure_binding(self.ensure_relation(binding_name)) - self.assertEqual(binding.network.egress_subnets, []) + assert binding.network.egress_subnets == [] def test_unresolved_ingress_addresses(self): # sometimes juju fails to resolve an url to an IP, in which case @@ -2403,7 +2403,7 @@ def test_unresolved_ingress_addresses(self): f'''[ "$1" = db0 ] && echo '{network_data}' || exit 1''') binding_name = 'db0' binding = self.ensure_binding(self.ensure_relation(binding_name)) - self.assertEqual(binding.network.ingress_addresses, ['foo.bar.baz.com']) + assert binding.network.ingress_addresses == ['foo.bar.baz.com'] _MetricAndLabelPair = typing.Tuple[typing.Dict[str, float], typing.Dict[str, str]] @@ -2429,18 +2429,18 @@ def backend(self): def test_relation_get_set_is_app_arg(self): # No is_app provided. - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.backend.relation_set(1, 'fookey', 'barval') # type: ignore - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.backend.relation_get(1, 'fooentity') # type: ignore # Invalid types for is_app. for is_app_v in [None, 1, 2.0, 'a', b'beef']: - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.backend.relation_set(1, 'fookey', 'barval', is_app=is_app_v) # type: ignore - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.backend.relation_get(1, 'fooentity', is_app=is_app_v) # type: ignore def test_is_leader_refresh(self): @@ -2449,20 +2449,20 @@ def test_is_leader_refresh(self): ''') model = ops.Model(meta, self.backend) fake_script(self, 'is-leader', 'echo false') - self.assertFalse(model.unit.is_leader()) + assert not model.unit.is_leader() # Change the leadership status fake_script(self, 'is-leader', 'echo true') # If you don't force it, we don't check, so we won't see the change - self.assertFalse(model.unit.is_leader()) + assert not model.unit.is_leader() # If we force a recheck, then we notice self.backend._leader_check_time = None - self.assertTrue(model.unit.is_leader()) + assert model.unit.is_leader() # Force a recheck without changing the leadership status. fake_script(self, 'is-leader', 'echo true') self.backend._leader_check_time = None - self.assertTrue(model.unit.is_leader()) + assert model.unit.is_leader() def test_relation_tool_errors(self): self.addCleanup(os.environ.pop, 'JUJU_VERSION', None) @@ -2514,9 +2514,9 @@ def test_relation_tool_errors(self): for i, (do_fake, run, exception, calls) in enumerate(test_cases): with self.subTest(i): do_fake() - with self.assertRaises(exception): + with pytest.raises(exception): run() - self.assertEqual(fake_script_calls(self, clear=True), calls) + assert fake_script_calls(self, clear=True) == calls def test_relation_get_juju_version_quirks(self): self.addCleanup(os.environ.pop, 'JUJU_VERSION', None) @@ -2528,15 +2528,15 @@ def test_relation_get_juju_version_quirks(self): with self.subTest(v): os.environ['JUJU_VERSION'] = v rel_data = self.backend.relation_get(1, 'foo/0', is_app=True) - self.assertEqual(rel_data, {"foo": "bar"}) + assert rel_data == {"foo": "bar"} calls = [' '.join(i) for i in fake_script_calls(self, clear=True)] - self.assertEqual(calls, ['relation-get -r 1 - foo/0 --app --format=json']) + assert calls == ['relation-get -r 1 - foo/0 --app --format=json'] # before 2.7.0, it just fails (no --app support) os.environ['JUJU_VERSION'] = '2.6.9' - with self.assertRaisesRegex(RuntimeError, 'not supported on Juju version 2.6.9'): + with pytest.raises(RuntimeError, match='not supported on Juju version 2.6.9'): self.backend.relation_get(1, 'foo/0', is_app=True) - self.assertEqual(fake_script_calls(self), []) + assert fake_script_calls(self) == [] def test_relation_set_juju_version_quirks(self): self.addCleanup(os.environ.pop, 'JUJU_VERSION', None) @@ -2552,27 +2552,27 @@ def test_relation_set_juju_version_quirks(self): os.environ['JUJU_VERSION'] = v self.backend.relation_set(1, 'foo', 'bar', is_app=True) calls = [' '.join(i) for i in fake_script_calls(self, clear=True)] - self.assertEqual(calls, ['relation-set -r 1 --app --file -']) + assert calls == ['relation-set -r 1 --app --file -'] t.seek(0) content = t.read() finally: t.close() decoded = content.decode('utf-8').replace('\r\n', '\n') - self.assertEqual(decoded, 'foo: bar\n') + assert decoded == 'foo: bar\n' # before 2.7.0, it just fails always (no --app support) os.environ['JUJU_VERSION'] = '2.6.9' - with self.assertRaisesRegex(RuntimeError, 'not supported on Juju version 2.6.9'): + with pytest.raises(RuntimeError, match='not supported on Juju version 2.6.9'): self.backend.relation_set(1, 'foo', 'bar', is_app=True) - self.assertEqual(fake_script_calls(self), []) + assert fake_script_calls(self) == [] def test_status_get(self): # taken from actual Juju output content = '{"message": "", "status": "unknown", "status-data": {}}' fake_script(self, 'status-get', f"echo '{content}'") s = self.backend.status_get(is_app=False) - self.assertEqual(s['status'], "unknown") - self.assertEqual(s['message'], "") + assert s['status'] == "unknown" + assert s['message'] == "" # taken from actual Juju output content = dedent(""" { @@ -2592,12 +2592,12 @@ def test_status_get(self): """) fake_script(self, 'status-get', f"echo '{content}'") s = self.backend.status_get(is_app=True) - self.assertEqual(s['status'], "maintenance") - self.assertEqual(s['message'], "installing") - self.assertEqual(fake_script_calls(self, clear=True), [ + assert s['status'] == "maintenance" + assert s['message'] == "installing" + assert fake_script_calls(self, clear=True) == [ ['status-get', '--include-data', '--application=False', '--format=json'], ['status-get', '--include-data', '--application=True', '--format=json'], - ]) + ] def test_status_is_app_forced_kwargs(self): fake_script(self, 'status-get', 'exit 1') @@ -2611,7 +2611,7 @@ def test_status_is_app_forced_kwargs(self): ) for case in test_cases: - with self.assertRaises(TypeError): + with pytest.raises(TypeError): case() def test_local_set_invalid_status(self): @@ -2623,27 +2623,27 @@ def test_local_set_invalid_status(self): fake_script(self, 'status-set', 'exit 1') fake_script(self, 'is-leader', 'echo true') - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): model.unit.status = ops.UnknownStatus() - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): model.unit.status = ops.ErrorStatus() - self.assertEqual(fake_script_calls(self, True), [ + assert fake_script_calls(self, True) == [ ['status-set', '--application=False', 'unknown', ''], ['status-set', '--application=False', 'error', ''], - ]) + ] - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): model.app.status = ops.UnknownStatus() - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): model.app.status = ops.ErrorStatus() # A leadership check is needed for application status. - self.assertEqual(fake_script_calls(self, True), [ + assert fake_script_calls(self, True) == [ ['is-leader', '--format=json'], ['status-set', '--application=True', 'unknown', ''], ['status-set', '--application=True', 'error', ''], - ]) + ] def test_local_get_status(self): for name, expected_cls in ( @@ -2666,9 +2666,9 @@ def test_local_get_status(self): }) fake_script(self, 'status-get', f"echo '{content}'") - self.assertIsInstance(model.unit.status, expected_cls) - self.assertEqual(model.unit.status.name, name) - self.assertEqual(model.unit.status.message, "foo") + assert isinstance(model.unit.status, expected_cls) + assert model.unit.status.name == name + assert model.unit.status.message == "foo" content = json.dumps({ "application-status": { @@ -2680,39 +2680,39 @@ def test_local_get_status(self): fake_script(self, 'status-get', f"echo '{content}'") fake_script(self, 'is-leader', 'echo true') - self.assertIsInstance(model.app.status, expected_cls) - self.assertEqual(model.app.status.name, name) - self.assertEqual(model.app.status.message, "bar") + assert isinstance(model.app.status, expected_cls) + assert model.app.status.name == name + assert model.app.status.message == "bar" def test_status_set_is_app_not_bool_raises(self): for is_app_v in [None, 1, 2.0, 'a', b'beef', object]: - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.backend.status_set(ops.ActiveStatus, is_app=is_app_v) # type: ignore def test_storage_tool_errors(self): fake_script(self, 'storage-list', 'echo fooerror >&2 ; exit 1') - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.backend.storage_list('foobar') - self.assertEqual(fake_script_calls(self, clear=True), - [['storage-list', 'foobar', '--format=json']]) + assert fake_script_calls(self, clear=True) == \ + [['storage-list', 'foobar', '--format=json']] fake_script(self, 'storage-get', 'echo fooerror >&2 ; exit 1') - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.backend.storage_get('foobar', 'someattr') - self.assertEqual(fake_script_calls(self, clear=True), - [['storage-get', '-s', 'foobar', 'someattr', '--format=json']]) + assert fake_script_calls(self, clear=True) == \ + [['storage-get', '-s', 'foobar', 'someattr', '--format=json']] fake_script(self, 'storage-add', 'echo fooerror >&2 ; exit 1') - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.backend.storage_add('foobar', count=2) - self.assertEqual(fake_script_calls(self, clear=True), - [['storage-add', 'foobar=2']]) + assert fake_script_calls(self, clear=True) == \ + [['storage-add', 'foobar=2']] fake_script(self, 'storage-add', 'echo fooerror >&2 ; exit 1') - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.backend.storage_add('foobar', count=object), # type: ignore - self.assertEqual(fake_script_calls(self, clear=True), []) + assert fake_script_calls(self, clear=True) == [] fake_script(self, 'storage-add', 'echo fooerror >&2 ; exit 1') - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.backend.storage_add('foobar', count=True) - self.assertEqual(fake_script_calls(self, clear=True), []) + assert fake_script_calls(self, clear=True) == [] def test_network_get(self): network_get_out = '''{ @@ -2739,14 +2739,14 @@ def test_network_get(self): fake_script(self, 'network-get', f'''[ "$1" = deadbeef ] && echo '{network_get_out}' || exit 1''') network_info = self.backend.network_get('deadbeef') - self.assertEqual(network_info, json.loads(network_get_out)) - self.assertEqual(fake_script_calls(self, clear=True), - [['network-get', 'deadbeef', '--format=json']]) + assert network_info == json.loads(network_get_out) + assert fake_script_calls(self, clear=True) == \ + [['network-get', 'deadbeef', '--format=json']] network_info = self.backend.network_get('deadbeef', 1) - self.assertEqual(network_info, json.loads(network_get_out)) - self.assertEqual(fake_script_calls(self, clear=True), - [['network-get', 'deadbeef', '-r', '1', '--format=json']]) + assert network_info == json.loads(network_get_out) + assert fake_script_calls(self, clear=True) == \ + [['network-get', 'deadbeef', '-r', '1', '--format=json']] def test_network_get_errors(self): err_no_endpoint = 'ERROR no network config found for binding "$2"' @@ -2766,22 +2766,22 @@ def test_network_get_errors(self): )] for do_fake, run, exception, calls in test_cases: do_fake() - with self.assertRaises(exception): + with pytest.raises(exception): run() - self.assertEqual(fake_script_calls(self, clear=True), calls) + assert fake_script_calls(self, clear=True) == calls def test_action_get_error(self): fake_script(self, 'action-get', '') fake_script(self, 'action-get', 'echo fooerror >&2 ; exit 1') - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.backend.action_get() calls = [['action-get', '--format=json']] - self.assertEqual(fake_script_calls(self, clear=True), calls) + assert fake_script_calls(self, clear=True) == calls def test_action_set_error(self): fake_script(self, 'action-get', '') fake_script(self, 'action-set', 'echo fooerror >&2 ; exit 1') - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.backend.action_set(OrderedDict([('foo', 'bar'), ('dead', 'beef cafe')])) self.assertCountEqual( ["action-set", "dead=beef cafe", "foo=bar"], fake_script_calls(self, clear=True)[0]) @@ -2789,17 +2789,17 @@ def test_action_set_error(self): def test_action_log_error(self): fake_script(self, 'action-get', '') fake_script(self, 'action-log', 'echo fooerror >&2 ; exit 1') - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.backend.action_log('log-message') calls = [["action-log", "log-message"]] - self.assertEqual(fake_script_calls(self, clear=True), calls) + assert fake_script_calls(self, clear=True) == calls def test_action_get(self): fake_script(self, 'action-get', """echo '{"foo-name": "bar", "silent": false}'""") params = self.backend.action_get() - self.assertEqual(params['foo-name'], 'bar') - self.assertEqual(params['silent'], False) - self.assertEqual(fake_script_calls(self), [['action-get', '--format=json']]) + assert params['foo-name'] == 'bar' + assert not params['silent'] + assert fake_script_calls(self) == [['action-get', '--format=json']] def test_action_set(self): fake_script(self, 'action-get', 'exit 1') @@ -2808,13 +2808,13 @@ def test_action_set(self): self.assertCountEqual(['action-set', 'x=dead beef', 'y=1'], fake_script_calls(self)[0]) def test_action_set_key_validation(self): - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.backend.action_set({'X': 'dead beef', 'y': 1}) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.backend.action_set({'some&key': 'dead beef', 'y': 1}) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.backend.action_set({'someKey': 'dead beef', 'y': 1}) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.backend.action_set({'some_key': 'dead beef', 'y': 1}) def test_action_set_nested(self): @@ -2839,51 +2839,51 @@ def test_action_set_dotted_dict(self): def test_action_set_duplicated_keys(self): fake_script(self, 'action-get', 'exit 1') fake_script(self, 'action-set', 'exit 0') - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.backend.action_set({'a.b': 1, 'a': {'b': 2}, 'd': 3}) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.backend.action_set({'a': {'b': 1, 'c': 2, 'd': {'e': 3}}, 'f': 4, 'a.d.e': 'foo'}) def test_action_fail(self): fake_script(self, 'action-get', 'exit 1') fake_script(self, 'action-fail', 'exit 0') self.backend.action_fail('error 42') - self.assertEqual(fake_script_calls(self), [['action-fail', 'error 42']]) + assert fake_script_calls(self) == [['action-fail', 'error 42']] def test_action_log(self): fake_script(self, 'action-get', 'exit 1') fake_script(self, 'action-log', 'exit 0') self.backend.action_log('progress: 42%') - self.assertEqual(fake_script_calls(self), [['action-log', 'progress: 42%']]) + assert fake_script_calls(self) == [['action-log', 'progress: 42%']] def test_application_version_set(self): fake_script(self, 'application-version-set', 'exit 0') self.backend.application_version_set('1.2b3') - self.assertEqual(fake_script_calls(self), [['application-version-set', '--', '1.2b3']]) + assert fake_script_calls(self) == [['application-version-set', '--', '1.2b3']] def test_application_version_set_invalid(self): fake_script(self, 'application-version-set', 'exit 0') - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.backend.application_version_set(2) # type: ignore - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.backend.application_version_set() # type: ignore - self.assertEqual(fake_script_calls(self), []) + assert fake_script_calls(self) == [] def test_juju_log(self): fake_script(self, 'juju-log', 'exit 0') self.backend.juju_log('WARNING', 'foo') - self.assertEqual(fake_script_calls(self, clear=True), - [['juju-log', '--log-level', 'WARNING', '--', 'foo']]) + assert fake_script_calls(self, clear=True) == \ + [['juju-log', '--log-level', 'WARNING', '--', 'foo']] - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.backend.juju_log('DEBUG') # type: ignore - self.assertEqual(fake_script_calls(self, clear=True), []) + assert fake_script_calls(self, clear=True) == [] fake_script(self, 'juju-log', 'exit 1') - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.backend.juju_log('BAR', 'foo') - self.assertEqual(fake_script_calls(self, clear=True), - [['juju-log', '--log-level', 'BAR', '--', 'foo']]) + assert fake_script_calls(self, clear=True) == \ + [['juju-log', '--log-level', 'BAR', '--', 'foo']] def test_valid_metrics(self): fake_script(self, 'add-metric', 'exit 0') @@ -2899,7 +2899,7 @@ def test_valid_metrics(self): )] for metrics, labels, expected_calls in test_cases: self.backend.add_metrics(metrics, labels) - self.assertEqual(fake_script_calls(self, clear=True), expected_calls) + assert fake_script_calls(self, clear=True) == expected_calls def test_invalid_metric_names(self): invalid_inputs: typing.List[_MetricAndLabelPair] = [ @@ -2917,7 +2917,7 @@ def test_invalid_metric_names(self): ({'BAЯ': 4.2}, {}), ] for metrics, labels in invalid_inputs: - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.backend.add_metrics(metrics, labels) def test_invalid_metric_values(self): @@ -2929,7 +2929,7 @@ def test_invalid_metric_values(self): ({'foo': '1O'}, {}), # type: ignore ] for metrics, labels in invalid_inputs: - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.backend.add_metrics(metrics, labels) def test_invalid_metric_labels(self): @@ -2940,7 +2940,7 @@ def test_invalid_metric_labels(self): ({'foo': 4.2}, {'BAЯ': 'baz'}), ] for metrics, labels in invalid_inputs: - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.backend.add_metrics(metrics, labels) def test_invalid_metric_label_values(self): @@ -2950,7 +2950,7 @@ def test_invalid_metric_label_values(self): ({'foo': 4.2}, {'bar': 'b=az'}), ] for metrics, labels in invalid_inputs: - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.backend.add_metrics(metrics, labels) def test_relation_remote_app_name_env(self): @@ -2959,9 +2959,9 @@ def test_relation_remote_app_name_env(self): os.environ['JUJU_RELATION_ID'] = 'x:5' os.environ['JUJU_REMOTE_APP'] = 'remoteapp1' - self.assertEqual(self.backend.relation_remote_app_name(5), 'remoteapp1') + assert self.backend.relation_remote_app_name(5) == 'remoteapp1' os.environ['JUJU_RELATION_ID'] = '5' - self.assertEqual(self.backend.relation_remote_app_name(5), 'remoteapp1') + assert self.backend.relation_remote_app_name(5) == 'remoteapp1' def test_relation_remote_app_name_script_success(self): self.addCleanup(os.environ.pop, 'JUJU_RELATION_ID', None) @@ -2971,49 +2971,49 @@ def test_relation_remote_app_name_script_success(self): fake_script(self, 'relation-list', r""" echo '"remoteapp2"' """) - self.assertEqual(self.backend.relation_remote_app_name(1), 'remoteapp2') - self.assertEqual(fake_script_calls(self, clear=True), [ + assert self.backend.relation_remote_app_name(1) == 'remoteapp2' + assert fake_script_calls(self, clear=True) == [ ['relation-list', '-r', '1', '--app', '--format=json'], - ]) + ] # JUJU_RELATION_ID set but JUJU_REMOTE_APP unset os.environ['JUJU_RELATION_ID'] = 'x:5' - self.assertEqual(self.backend.relation_remote_app_name(5), 'remoteapp2') + assert self.backend.relation_remote_app_name(5) == 'remoteapp2' # JUJU_RELATION_ID unset but JUJU_REMOTE_APP set del os.environ['JUJU_RELATION_ID'] os.environ['JUJU_REMOTE_APP'] = 'remoteapp1' - self.assertEqual(self.backend.relation_remote_app_name(5), 'remoteapp2') + assert self.backend.relation_remote_app_name(5) == 'remoteapp2' # Both set, but JUJU_RELATION_ID a different relation os.environ['JUJU_RELATION_ID'] = 'x:6' - self.assertEqual(self.backend.relation_remote_app_name(5), 'remoteapp2') + assert self.backend.relation_remote_app_name(5) == 'remoteapp2' def test_relation_remote_app_name_script_errors(self): fake_script(self, 'relation-list', r""" echo "ERROR invalid value \"6\" for option -r: relation not found" >&2 # NOQA exit 2 """) - self.assertIs(self.backend.relation_remote_app_name(6), None) - self.assertEqual(fake_script_calls(self, clear=True), [ + assert self.backend.relation_remote_app_name(6) is None + assert fake_script_calls(self, clear=True) == [ ['relation-list', '-r', '6', '--app', '--format=json'], - ]) + ] fake_script(self, 'relation-list', r""" echo "ERROR option provided but not defined: --app" >&2 exit 2 """) - self.assertIs(self.backend.relation_remote_app_name(6), None) - self.assertEqual(fake_script_calls(self, clear=True), [ + assert self.backend.relation_remote_app_name(6) is None + assert fake_script_calls(self, clear=True) == [ ['relation-list', '-r', '6', '--app', '--format=json'], - ]) + ] def test_planned_units(self): # no units fake_script(self, 'goal-state', """ echo '{"units":{}, "relations":{}}' """) - self.assertEqual(self.backend.planned_units(), 0) + assert self.backend.planned_units() == 0 # only active units fake_script(self, 'goal-state', """ @@ -3024,7 +3024,7 @@ def test_planned_units(self): }, "relations": {} }'""") - self.assertEqual(self.backend.planned_units(), 2) + assert self.backend.planned_units() == 2 # active and dying units fake_script(self, 'goal-state', """ @@ -3035,7 +3035,7 @@ def test_planned_units(self): }, "relations": {} }'""") - self.assertEqual(self.backend.planned_units(), 1) + assert self.backend.planned_units() == 1 class TestLazyMapping(unittest.TestCase): @@ -3049,13 +3049,13 @@ def _load(self): return {'foo': 'bar'} map = MyLazyMap() - self.assertEqual(map['foo'], 'bar') - self.assertEqual(loaded, [1]) - self.assertEqual(map['foo'], 'bar') - self.assertEqual(loaded, [1]) + assert map['foo'] == 'bar' + assert loaded == [1] + assert map['foo'] == 'bar' + assert loaded == [1] map._invalidate() - self.assertEqual(map['foo'], 'bar') - self.assertEqual(loaded, [1, 1]) + assert map['foo'] == 'bar' + assert loaded == [1, 1] class TestSecrets(unittest.TestCase): @@ -3068,12 +3068,12 @@ def test_app_add_secret_simple(self): fake_script(self, 'secret-add', 'echo secret:123') secret = self.app.add_secret({'foo': 'x'}) - self.assertIsInstance(secret, ops.Secret) - self.assertEqual(secret.id, 'secret:123') - self.assertIsNone(secret.label) + assert isinstance(secret, ops.Secret) + assert secret.id == 'secret:123' + assert secret.label is None - self.assertEqual(fake_script_calls(self, clear=True), - [['secret-add', '--owner', 'application', 'foo=x']]) + assert fake_script_calls(self, clear=True) == \ + [['secret-add', '--owner', 'application', 'foo=x']] def test_app_add_secret_args(self): fake_script(self, 'secret-add', 'echo secret:234') @@ -3081,25 +3081,25 @@ def test_app_add_secret_args(self): expire = datetime.datetime(2022, 12, 9, 16, 17, 0) secret = self.app.add_secret({'foo': 'x', 'bar': 'y'}, label='lbl', description='desc', expire=expire, rotate=ops.SecretRotate.HOURLY) - self.assertEqual(secret.id, 'secret:234') - self.assertEqual(secret.label, 'lbl') - self.assertEqual(secret.get_content(), {'foo': 'x', 'bar': 'y'}) + assert secret.id == 'secret:234' + assert secret.label == 'lbl' + assert secret.get_content() == {'foo': 'x', 'bar': 'y'} - self.assertEqual(fake_script_calls(self, clear=True), - [['secret-add', '--label', 'lbl', '--description', 'desc', - '--expire', '2022-12-09T16:17:00', '--rotate', 'hourly', - '--owner', 'application', 'foo=x', 'bar=y']]) + assert fake_script_calls(self, clear=True) == \ + [['secret-add', '--label', 'lbl', '--description', 'desc', + '--expire', '2022-12-09T16:17:00', '--rotate', 'hourly', + '--owner', 'application', 'foo=x', 'bar=y']] def test_unit_add_secret_simple(self): fake_script(self, 'secret-add', 'echo secret:345') secret = self.unit.add_secret({'foo': 'x'}) - self.assertIsInstance(secret, ops.Secret) - self.assertEqual(secret.id, 'secret:345') - self.assertIsNone(secret.label) + assert isinstance(secret, ops.Secret) + assert secret.id == 'secret:345' + assert secret.label is None - self.assertEqual(fake_script_calls(self, clear=True), - [['secret-add', '--owner', 'unit', 'foo=x']]) + assert fake_script_calls(self, clear=True) == \ + [['secret-add', '--owner', 'unit', 'foo=x']] def test_unit_add_secret_args(self): fake_script(self, 'secret-add', 'echo secret:456') @@ -3107,14 +3107,14 @@ def test_unit_add_secret_args(self): expire = datetime.datetime(2022, 12, 9, 16, 22, 0) secret = self.unit.add_secret({'foo': 'w', 'bar': 'z'}, label='l2', description='xyz', expire=expire, rotate=ops.SecretRotate.YEARLY) - self.assertEqual(secret.id, 'secret:456') - self.assertEqual(secret.label, 'l2') - self.assertEqual(secret.get_content(), {'foo': 'w', 'bar': 'z'}) + assert secret.id == 'secret:456' + assert secret.label == 'l2' + assert secret.get_content() == {'foo': 'w', 'bar': 'z'} - self.assertEqual(fake_script_calls(self, clear=True), - [['secret-add', '--label', 'l2', '--description', 'xyz', - '--expire', '2022-12-09T16:22:00', '--rotate', 'yearly', - '--owner', 'unit', 'foo=w', 'bar=z']]) + assert fake_script_calls(self, clear=True) == \ + [['secret-add', '--label', 'l2', '--description', 'xyz', + '--expire', '2022-12-09T16:22:00', '--rotate', 'yearly', + '--owner', 'unit', 'foo=w', 'bar=z']] def test_unit_add_secret_errors(self): # Additional add_secret tests are done in TestApplication @@ -3123,8 +3123,7 @@ def test_unit_add_secret_errors(self): ({'foo': 'x'}, {'expire': 7}, TypeError), ] for content, kwargs, exc_type in errors: - msg = f'expected {exc_type.__name__} when adding secret content {content}' - with self.assertRaises(exc_type, msg=msg): + with pytest.raises(exc_type): self.unit.add_secret(content, **kwargs) # type: ignore def test_add_secret_errors(self): @@ -3144,89 +3143,88 @@ def test_add_secret_errors(self): ({'foo': 'x'}, {'expire': 7}, TypeError), ] for content, kwargs, exc_type in errors: - msg = f'expected {exc_type.__name__} when adding secret content {content}' - with self.assertRaises(exc_type, msg=msg): + with pytest.raises(exc_type): self.app.add_secret(content, **kwargs) # type: ignore - with self.assertRaises(exc_type, msg=msg): + with pytest.raises(exc_type): self.unit.add_secret(content, **kwargs) # type: ignore def test_get_secret_id(self): fake_script(self, 'secret-get', """echo '{"foo": "g"}'""") secret = self.model.get_secret(id='123') - self.assertEqual(secret.id, 'secret:123') - self.assertIsNone(secret.label) - self.assertEqual(secret.get_content(), {'foo': 'g'}) + assert secret.id == 'secret:123' + assert secret.label is None + assert secret.get_content() == {'foo': 'g'} - self.assertEqual(fake_script_calls(self, clear=True), - [['secret-get', 'secret:123', '--format=json']]) + assert fake_script_calls(self, clear=True) == \ + [['secret-get', 'secret:123', '--format=json']] def test_get_secret_label(self): fake_script(self, 'secret-get', """echo '{"foo": "g"}'""") secret = self.model.get_secret(label='lbl') - self.assertIsNone(secret.id) - self.assertEqual(secret.label, 'lbl') - self.assertEqual(secret.get_content(), {'foo': 'g'}) + assert secret.id is None + assert secret.label == 'lbl' + assert secret.get_content() == {'foo': 'g'} - self.assertEqual(fake_script_calls(self, clear=True), - [['secret-get', '--label', 'lbl', '--format=json']]) + assert fake_script_calls(self, clear=True) == \ + [['secret-get', '--label', 'lbl', '--format=json']] def test_get_secret_id_and_label(self): fake_script(self, 'secret-get', """echo '{"foo": "h"}'""") secret = self.model.get_secret(id='123', label='l') - self.assertEqual(secret.id, 'secret:123') - self.assertEqual(secret.label, 'l') - self.assertEqual(secret.get_content(), {'foo': 'h'}) + assert secret.id == 'secret:123' + assert secret.label == 'l' + assert secret.get_content() == {'foo': 'h'} - self.assertEqual(fake_script_calls(self, clear=True), - [['secret-get', 'secret:123', '--label', 'l', '--format=json']]) + assert fake_script_calls(self, clear=True) == \ + [['secret-get', 'secret:123', '--label', 'l', '--format=json']] def test_get_secret_no_args(self): - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.model.get_secret() def test_get_secret_not_found(self): script = """echo 'ERROR secret "123" not found' >&2; exit 1""" fake_script(self, 'secret-get', script) - with self.assertRaises(ops.SecretNotFoundError): + with pytest.raises(ops.SecretNotFoundError): self.model.get_secret(id='123') def test_get_secret_other_error(self): script = """echo 'ERROR other error' >&2; exit 1""" fake_script(self, 'secret-get', script) - with self.assertRaises(ops.ModelError) as cm: + with pytest.raises(ops.ModelError) as excinfo: self.model.get_secret(id='123') - self.assertNotIsInstance(cm.exception, ops.SecretNotFoundError) + self.assertNotIsInstance(excinfo.value, ops.SecretNotFoundError) def test_secret_unique_identifier(self): fake_script(self, 'secret-get', """echo '{"foo": "g"}'""") secret = self.model.get_secret(label='lbl') - self.assertIsNone(secret.id) - self.assertIsNone(secret.unique_identifier) + assert secret.id is None + assert secret.unique_identifier is None secret = self.model.get_secret(id='123') - self.assertEqual(secret.id, 'secret:123') - self.assertEqual(secret.unique_identifier, '123') + assert secret.id == 'secret:123' + assert secret.unique_identifier == '123' secret = self.model.get_secret(id='secret:124') - self.assertEqual(secret.id, 'secret:124') - self.assertEqual(secret.unique_identifier, '124') + assert secret.id == 'secret:124' + assert secret.unique_identifier == '124' secret = self.model.get_secret(id='secret://modeluuid/125') - self.assertEqual(secret.id, 'secret://modeluuid/125') - self.assertEqual(secret.unique_identifier, '125') + assert secret.id == 'secret://modeluuid/125' + assert secret.unique_identifier == '125' - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['secret-get', '--label', 'lbl', '--format=json'], ['secret-get', 'secret:123', '--format=json'], ['secret-get', 'secret:124', '--format=json'], ['secret-get', 'secret://modeluuid/125', '--format=json'], - ]) + ] class TestSecretInfo(unittest.TestCase): @@ -3239,15 +3237,15 @@ def test_init(self): rotation=ops.SecretRotate.MONTHLY, rotates=datetime.datetime(2023, 1, 9, 14, 10, 0), ) - self.assertEqual(info.id, 'secret:3') - self.assertEqual(info.label, 'lbl') - self.assertEqual(info.revision, 7) - self.assertEqual(info.expires, datetime.datetime(2022, 12, 9, 14, 10, 0)) - self.assertEqual(info.rotation, ops.SecretRotate.MONTHLY) - self.assertEqual(info.rotates, datetime.datetime(2023, 1, 9, 14, 10, 0)) + assert info.id == 'secret:3' + assert info.label == 'lbl' + assert info.revision == 7 + assert info.expires == datetime.datetime(2022, 12, 9, 14, 10, 0) + assert info.rotation == ops.SecretRotate.MONTHLY + assert info.rotates == datetime.datetime(2023, 1, 9, 14, 10, 0) - self.assertTrue(repr(info).startswith('SecretInfo(')) - self.assertTrue(repr(info).endswith(')')) + assert repr(info).startswith('SecretInfo(') + assert repr(info).endswith(')') def test_from_dict(self): utc = datetime.timezone.utc @@ -3258,28 +3256,28 @@ def test_from_dict(self): 'rotation': 'yearly', 'rotates': '2023-01-09T14:10:00Z', }) - self.assertEqual(info.id, 'secret:4') - self.assertEqual(info.label, 'fromdict') - self.assertEqual(info.revision, 8) - self.assertEqual(info.expires, datetime.datetime(2022, 12, 9, 14, 10, 0, tzinfo=utc)) - self.assertEqual(info.rotation, ops.SecretRotate.YEARLY) - self.assertEqual(info.rotates, datetime.datetime(2023, 1, 9, 14, 10, 0, tzinfo=utc)) + assert info.id == 'secret:4' + assert info.label == 'fromdict' + assert info.revision == 8 + assert info.expires == datetime.datetime(2022, 12, 9, 14, 10, 0, tzinfo=utc) + assert info.rotation == ops.SecretRotate.YEARLY + assert info.rotates == datetime.datetime(2023, 1, 9, 14, 10, 0, tzinfo=utc) info = ops.SecretInfo.from_dict('secret:4', { 'label': 'fromdict', 'revision': 8, 'rotation': 'badvalue', }) - self.assertEqual(info.id, 'secret:4') - self.assertEqual(info.label, 'fromdict') - self.assertEqual(info.revision, 8) - self.assertIsNone(info.expires) - self.assertIsNone(info.rotation) - self.assertIsNone(info.rotates) + assert info.id == 'secret:4' + assert info.label == 'fromdict' + assert info.revision == 8 + assert info.expires is None + assert info.rotation is None + assert info.rotates is None info = ops.SecretInfo.from_dict('5', {'revision': 9}) - self.assertEqual(info.id, 'secret:5') - self.assertEqual(info.revision, 9) + assert info.id == 'secret:5' + assert info.revision == 9 class TestSecretClass(unittest.TestCase): @@ -3296,57 +3294,57 @@ def make_secret(self, def test_id_and_label(self): secret = self.make_secret(id=' abc ', label='lbl') - self.assertEqual(secret.id, 'secret:abc') - self.assertEqual(secret.label, 'lbl') + assert secret.id == 'secret:abc' + assert secret.label == 'lbl' secret = self.make_secret(id='x') - self.assertEqual(secret.id, 'secret:x') - self.assertIsNone(secret.label) + assert secret.id == 'secret:x' + assert secret.label is None secret = self.make_secret(label='y') - self.assertIsNone(secret.id) - self.assertEqual(secret.label, 'y') + assert secret.id is None + assert secret.label == 'y' def test_get_content_cached(self): fake_script(self, 'secret-get', """exit 1""") secret = self.make_secret(id='x', label='y', content={'foo': 'bar'}) content = secret.get_content() # will use cached content, not run secret-get - self.assertEqual(content, {'foo': 'bar'}) + assert content == {'foo': 'bar'} - self.assertEqual(fake_script_calls(self, clear=True), []) + assert fake_script_calls(self, clear=True) == [] def test_get_content_refresh(self): fake_script(self, 'secret-get', """echo '{"foo": "refreshed"}'""") secret = self.make_secret(id='y', content={'foo': 'bar'}) content = secret.get_content(refresh=True) - self.assertEqual(content, {'foo': 'refreshed'}) + assert content == {'foo': 'refreshed'} - self.assertEqual(fake_script_calls(self, clear=True), - [['secret-get', 'secret:y', '--refresh', '--format=json']]) + assert fake_script_calls(self, clear=True) == \ + [['secret-get', 'secret:y', '--refresh', '--format=json']] def test_get_content_uncached(self): fake_script(self, 'secret-get', """echo '{"foo": "notcached"}'""") secret = self.make_secret(id='z') content = secret.get_content() - self.assertEqual(content, {'foo': 'notcached'}) + assert content == {'foo': 'notcached'} - self.assertEqual(fake_script_calls(self, clear=True), - [['secret-get', 'secret:z', '--format=json']]) + assert fake_script_calls(self, clear=True) == \ + [['secret-get', 'secret:z', '--format=json']] def test_get_content_copies_dict(self): fake_script(self, 'secret-get', """echo '{"foo": "bar"}'""") secret = self.make_secret(id='z') content = secret.get_content() - self.assertEqual(content, {'foo': 'bar'}) + assert content == {'foo': 'bar'} content['new'] = 'value' - self.assertEqual(secret.get_content(), {'foo': 'bar'}) + assert secret.get_content() == {'foo': 'bar'} - self.assertEqual(fake_script_calls(self, clear=True), - [['secret-get', 'secret:z', '--format=json']]) + assert fake_script_calls(self, clear=True) == \ + [['secret-get', 'secret:z', '--format=json']] def test_set_content_invalidates_cache(self): fake_script(self, 'secret-get', """echo '{"foo": "bar"}'""") @@ -3354,27 +3352,27 @@ def test_set_content_invalidates_cache(self): secret = self.make_secret(id='z') old_content = secret.get_content() - self.assertEqual(old_content, {'foo': 'bar'}) + assert old_content == {'foo': 'bar'} secret.set_content({'new': 'content'}) fake_script(self, 'secret-get', """echo '{"new": "content"}'""") new_content = secret.get_content() - self.assertEqual(new_content, {'new': 'content'}) + assert new_content == {'new': 'content'} - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['secret-get', 'secret:z', '--format=json'], ['secret-set', 'secret:z', 'new=content'], ['secret-get', 'secret:z', '--format=json'], - ]) + ] def test_peek_content(self): fake_script(self, 'secret-get', """echo '{"foo": "peeked"}'""") secret = self.make_secret(id='a', label='b') content = secret.peek_content() - self.assertEqual(content, {'foo': 'peeked'}) + assert content == {'foo': 'peeked'} - self.assertEqual(fake_script_calls(self, clear=True), - [['secret-get', 'secret:a', '--label', 'b', '--peek', '--format=json']]) + assert fake_script_calls(self, clear=True) == \ + [['secret-get', 'secret:a', '--label', 'b', '--peek', '--format=json']] def test_get_info(self): fake_script(self, 'secret-info-get', """echo '{"x": {"label": "y", "revision": 7}}'""") @@ -3382,31 +3380,30 @@ def test_get_info(self): # Secret with ID only secret = self.make_secret(id='x') info = secret.get_info() - self.assertEqual(info.id, 'secret:x') - self.assertEqual(info.label, 'y') - self.assertEqual(info.revision, 7) + assert info.id == 'secret:x' + assert info.label == 'y' + assert info.revision == 7 # Secret with label only secret = self.make_secret(label='y') info = secret.get_info() - self.assertEqual(info.id, 'secret:x') - self.assertEqual(info.label, 'y') - self.assertEqual(info.revision, 7) + assert info.id == 'secret:x' + assert info.label == 'y' + assert info.revision == 7 # Secret with ID and label secret = self.make_secret(id='x', label='y') info = secret.get_info() - self.assertEqual(info.id, 'secret:x') - self.assertEqual(info.label, 'y') - self.assertEqual(info.revision, 7) + assert info.id == 'secret:x' + assert info.label == 'y' + assert info.revision == 7 - self.assertEqual( - fake_script_calls(self, clear=True), + assert fake_script_calls(self, clear=True) == \ [ ['secret-info-get', 'secret:x', '--format=json'], ['secret-info-get', '--label', 'y', '--format=json'], ['secret-info-get', 'secret:x', '--format=json'], - ]) + ] def test_set_content(self): fake_script(self, 'secret-set', """exit 0""") @@ -3417,18 +3414,18 @@ def test_set_content(self): # If secret doesn't have an ID, we'll run secret-info-get to fetch it secret = self.make_secret(label='y') - self.assertIsNone(secret.id) + assert secret.id is None secret.set_content({'bar': 'foo'}) - self.assertEqual(secret.id, 'secret:z') + assert secret.id == 'secret:z' - with self.assertRaises(ValueError): + with pytest.raises(ValueError): secret.set_content({'s': 't'}) # ensure it validates content (key too short) - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['secret-set', 'secret:x', 'foo=bar'], ['secret-info-get', '--label', 'y', '--format=json'], ['secret-set', 'secret:z', 'bar=foo'], - ]) + ] def test_set_info(self): fake_script(self, 'secret-set', """exit 0""") @@ -3445,18 +3442,18 @@ def test_set_info(self): # If secret doesn't have an ID, we'll run secret-info-get to fetch it secret = self.make_secret(label='y') - self.assertIsNone(secret.id) + assert secret.id is None secret.set_info(label='lbl') - self.assertEqual(secret.id, 'secret:z') + assert secret.id == 'secret:z' - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['secret-set', 'secret:x', '--label', 'lab', '--description', 'desc', '--expire', '2022-12-09T16:59:00', '--rotate', 'monthly'], ['secret-info-get', '--label', 'y', '--format=json'], ['secret-set', 'secret:z', '--label', 'lbl'], - ]) + ] - with self.assertRaises(TypeError): + with pytest.raises(TypeError): secret.set_info() # no args provided def test_grant(self): @@ -3477,12 +3474,12 @@ def test_grant(self): # If secret doesn't have an ID, we'll run secret-info-get to fetch it secret = self.make_secret(label='y') - self.assertIsNone(secret.id) + assert secret.id is None rel345 = ops.Relation('test', 345, True, unit, backend, cache) secret.grant(rel345) - self.assertEqual(secret.id, 'secret:z') + assert secret.id == 'secret:z' - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['relation-list', '-r', '123', '--format=json'], ['relation-list', '-r', '234', '--format=json'], ['secret-grant', 'secret:x', '--relation', '123'], @@ -3490,7 +3487,7 @@ def test_grant(self): ['relation-list', '-r', '345', '--format=json'], ['secret-info-get', '--label', 'y', '--format=json'], ['secret-grant', 'secret:z', '--relation', '345'], - ]) + ] def test_revoke(self): fake_script(self, 'relation-list', """echo '[]'""") @@ -3507,12 +3504,12 @@ def test_revoke(self): # If secret doesn't have an ID, we'll run secret-info-get to fetch it secret = self.make_secret(label='y') - self.assertIsNone(secret.id) + assert secret.id is None rel345 = ops.Relation('test', 345, True, unit, self.model._backend, self.model._cache) secret.revoke(rel345) - self.assertEqual(secret.id, 'secret:z') + assert secret.id == 'secret:z' - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['relation-list', '-r', '123', '--format=json'], ['relation-list', '-r', '234', '--format=json'], ['secret-revoke', 'secret:x', '--relation', '123'], @@ -3520,7 +3517,7 @@ def test_revoke(self): ['relation-list', '-r', '345', '--format=json'], ['secret-info-get', '--label', 'y', '--format=json'], ['secret-revoke', 'secret:z', '--relation', '345'], - ]) + ] def test_remove_revision(self): fake_script(self, 'secret-remove', """exit 0""") @@ -3531,15 +3528,15 @@ def test_remove_revision(self): # If secret doesn't have an ID, we'll run secret-info-get to fetch it secret = self.make_secret(label='y') - self.assertIsNone(secret.id) + assert secret.id is None secret.remove_revision(234) - self.assertEqual(secret.id, 'secret:z') + assert secret.id == 'secret:z' - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['secret-remove', 'secret:x', '--revision', '123'], ['secret-info-get', '--label', 'y', '--format=json'], ['secret-remove', 'secret:z', '--revision', '234'], - ]) + ] def test_remove_all_revisions(self): fake_script(self, 'secret-remove', """exit 0""") @@ -3550,15 +3547,15 @@ def test_remove_all_revisions(self): # If secret doesn't have an ID, we'll run secret-info-get to fetch it secret = self.make_secret(label='y') - self.assertIsNone(secret.id) + assert secret.id is None secret.remove_all_revisions() - self.assertEqual(secret.id, 'secret:z') + assert secret.id == 'secret:z' - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['secret-remove', 'secret:x'], ['secret-info-get', '--label', 'y', '--format=json'], ['secret-remove', 'secret:z'], - ]) + ] class TestPorts(unittest.TestCase): @@ -3573,22 +3570,22 @@ def test_open_port(self): self.unit.open_port('UDP', 4000) # type: ignore self.unit.open_port('icmp') - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['open-port', '8080/tcp'], ['open-port', '4000/udp'], ['open-port', 'icmp'], - ]) + ] def test_open_port_error(self): fake_script(self, 'open-port', "echo 'ERROR bad protocol' >&2; exit 1") - with self.assertRaises(ops.ModelError) as cm: + with pytest.raises(ops.ModelError) as excinfo: self.unit.open_port('ftp', 8080) # type: ignore - self.assertEqual(str(cm.exception), 'ERROR bad protocol\n') + assert str(excinfo.value) == 'ERROR bad protocol\n' - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['open-port', '8080/ftp'], - ]) + ] def test_close_port(self): fake_script(self, 'close-port', 'exit 0') @@ -3597,63 +3594,63 @@ def test_close_port(self): self.unit.close_port('UDP', 4000) # type: ignore self.unit.close_port('icmp') - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['close-port', '8080/tcp'], ['close-port', '4000/udp'], ['close-port', 'icmp'], - ]) + ] def test_close_port_error(self): fake_script(self, 'close-port', "echo 'ERROR bad protocol' >&2; exit 1") - with self.assertRaises(ops.ModelError) as cm: + with pytest.raises(ops.ModelError) as excinfo: self.unit.close_port('ftp', 8080) # type: ignore - self.assertEqual(str(cm.exception), 'ERROR bad protocol\n') + assert str(excinfo.value) == 'ERROR bad protocol\n' - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['close-port', '8080/ftp'], - ]) + ] def test_opened_ports(self): fake_script(self, 'opened-ports', """echo 8080/tcp; echo icmp""") ports_set = self.unit.opened_ports() - self.assertIsInstance(ports_set, set) + assert isinstance(ports_set, set) ports = sorted(ports_set, key=lambda p: (p.protocol, p.port)) - self.assertEqual(len(ports), 2) - self.assertIsInstance(ports[0], ops.Port) - self.assertEqual(ports[0].protocol, 'icmp') - self.assertIsNone(ports[0].port) - self.assertIsInstance(ports[1], ops.Port) - self.assertEqual(ports[1].protocol, 'tcp') - self.assertEqual(ports[1].port, 8080) - - self.assertEqual(fake_script_calls(self, clear=True), [ + assert len(ports) == 2 + assert isinstance(ports[0], ops.Port) + assert ports[0].protocol == 'icmp' + assert ports[0].port is None + assert isinstance(ports[1], ops.Port) + assert ports[1].protocol == 'tcp' + assert ports[1].port == 8080 + + assert fake_script_calls(self, clear=True) == [ ['opened-ports', ''], - ]) + ] def test_opened_ports_warnings(self): fake_script(self, 'opened-ports', """echo 8080/tcp; echo 1234/ftp; echo 1000-2000/udp""") with self.assertLogs('ops.model', level='WARNING') as cm: ports_set = self.unit.opened_ports() - self.assertEqual(len(cm.output), 2) - self.assertRegex(cm.output[0], r'WARNING:ops.model:.*protocol.*') - self.assertRegex(cm.output[1], r'WARNING:ops.model:.*range.*') + assert len(cm.output) == 2 + assert re.search(r'WARNING:ops.model:.*protocol.*', cm.output[0]) + assert re.search(r'WARNING:ops.model:.*range.*', cm.output[1]) - self.assertIsInstance(ports_set, set) + assert isinstance(ports_set, set) ports = sorted(ports_set, key=lambda p: (p.protocol, p.port)) - self.assertEqual(len(ports), 2) - self.assertIsInstance(ports[0], ops.Port) - self.assertEqual(ports[0].protocol, 'tcp') - self.assertEqual(ports[0].port, 8080) - self.assertIsInstance(ports[1], ops.Port) - self.assertEqual(ports[1].protocol, 'udp') - self.assertEqual(ports[1].port, 1000) - - self.assertEqual(fake_script_calls(self, clear=True), [ + assert len(ports) == 2 + assert isinstance(ports[0], ops.Port) + assert ports[0].protocol == 'tcp' + assert ports[0].port == 8080 + assert isinstance(ports[1], ops.Port) + assert ports[1].protocol == 'udp' + assert ports[1].port == 1000 + + assert fake_script_calls(self, clear=True) == [ ['opened-ports', ''], - ]) + ] def test_set_ports_all_open(self): fake_script(self, 'open-port', 'exit 0') @@ -3661,12 +3658,12 @@ def test_set_ports_all_open(self): fake_script(self, 'opened-ports', 'exit 0') self.unit.set_ports(8000, 8025) calls = fake_script_calls(self, clear=True) - self.assertEqual(calls.pop(0), ['opened-ports', '']) + assert calls.pop(0) == ['opened-ports', ''] calls.sort() # We make no guarantee on the order the ports are opened. - self.assertEqual(calls, [ + assert calls == [ ['open-port', '8000/tcp'], ['open-port', '8025/tcp'], - ]) + ] def test_set_ports_mixed(self): # Two open ports, leave one alone and open another one. @@ -3674,11 +3671,11 @@ def test_set_ports_mixed(self): fake_script(self, 'close-port', 'exit 0') fake_script(self, 'opened-ports', 'echo 8025/tcp; echo 8028/tcp') self.unit.set_ports(ops.Port('udp', 8022), 8028) - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['opened-ports', ''], ['close-port', '8025/tcp'], ['open-port', '8022/udp'], - ]) + ] def test_set_ports_replace(self): fake_script(self, 'open-port', 'exit 0') @@ -3686,33 +3683,33 @@ def test_set_ports_replace(self): fake_script(self, 'opened-ports', 'echo 8025/tcp; echo 8028/tcp') self.unit.set_ports(8001, 8002) calls = fake_script_calls(self, clear=True) - self.assertEqual(calls.pop(0), ['opened-ports', '']) + assert calls.pop(0) == ['opened-ports', ''] calls.sort() - self.assertEqual(calls, [ + assert calls == [ ['close-port', '8025/tcp'], ['close-port', '8028/tcp'], ['open-port', '8001/tcp'], ['open-port', '8002/tcp'], - ]) + ] def test_set_ports_close_all(self): fake_script(self, 'open-port', 'exit 0') fake_script(self, 'close-port', 'exit 0') fake_script(self, 'opened-ports', 'echo 8022/udp') self.unit.set_ports() - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['opened-ports', ''], ['close-port', '8022/udp'], - ]) + ] def test_set_ports_noop(self): fake_script(self, 'open-port', 'exit 0') fake_script(self, 'close-port', 'exit 0') fake_script(self, 'opened-ports', 'echo 8000/tcp') self.unit.set_ports(ops.Port('tcp', 8000)) - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['opened-ports', ''], - ]) + ] class TestUnit(unittest.TestCase): @@ -3723,18 +3720,18 @@ def setUp(self): def test_reboot(self): fake_script(self, 'juju-reboot', 'exit 0') self.unit.reboot() - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['juju-reboot', ''], - ]) - with self.assertRaises(SystemExit): + ] + with pytest.raises(SystemExit): self.unit.reboot(now=True) - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['juju-reboot', '--now'], - ]) + ] - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): self.model.get_unit('other').reboot() - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): self.model.get_unit('other').reboot(now=True) @@ -3761,34 +3758,30 @@ def get_notice(self, id: str): workload = typing.cast(ops.Container, FakeWorkload()) n = ops.model.LazyNotice(workload, '123', 'custom', 'example.com/a') - self.assertEqual(n.id, '123') - self.assertEqual(n.type, ops.pebble.NoticeType.CUSTOM) - self.assertEqual(n.key, 'example.com/a') - self.assertEqual(calls, 0) + assert n.id == '123' + assert n.type == ops.pebble.NoticeType.CUSTOM + assert n.key == 'example.com/a' + assert calls == 0 - self.assertEqual(n.occurrences, 7) - self.assertEqual(calls, 1) + assert n.occurrences == 7 + assert calls == 1 - self.assertEqual(n.user_id, 1000) - self.assertEqual(n.last_data, {'key': 'val'}) - self.assertEqual(calls, 1) + assert n.user_id == 1000 + assert n.last_data == {'key': 'val'} + assert calls == 1 - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): assert n.not_exist def test_repr(self): workload = typing.cast(ops.Container, None) n = ops.model.LazyNotice(workload, '123', 'custom', 'example.com/a') - self.assertEqual( - repr(n), - "LazyNotice(id='123', type=NoticeType.CUSTOM, key='example.com/a')", - ) + assert repr(n) == \ + "LazyNotice(id='123', type=NoticeType.CUSTOM, key='example.com/a')" n = ops.model.LazyNotice(workload, '123', 'foobar', 'example.com/a') - self.assertEqual( - repr(n), - "LazyNotice(id='123', type='foobar', key='example.com/a')", - ) + assert repr(n) == \ + "LazyNotice(id='123', type='foobar', key='example.com/a')" class TestCloudCredential(unittest.TestCase): @@ -3797,9 +3790,9 @@ def test_from_dict(self): 'auth-type': 'certificate', } cloud_cred = ops.CloudCredential.from_dict(d) - self.assertEqual(cloud_cred.auth_type, d['auth-type']) - self.assertEqual(cloud_cred.attributes, {}) - self.assertEqual(cloud_cred.redacted, []) + assert cloud_cred.auth_type == d['auth-type'] + assert cloud_cred.attributes == {} + assert cloud_cred.redacted == [] def test_from_dict_full(self): d = { @@ -3812,9 +3805,9 @@ def test_from_dict_full(self): 'redacted': ['foo'] } cloud_cred = ops.CloudCredential.from_dict(d) - self.assertEqual(cloud_cred.auth_type, d['auth-type']) - self.assertEqual(cloud_cred.attributes, d['attrs']) - self.assertEqual(cloud_cred.redacted, d['redacted']) + assert cloud_cred.auth_type == d['auth-type'] + assert cloud_cred.attributes == d['attrs'] + assert cloud_cred.redacted == d['redacted'] class TestCloudSpec(unittest.TestCase): @@ -3825,16 +3818,16 @@ def test_from_dict(self): 'name': 'localhost', } ) - self.assertEqual(cloud_spec.type, 'lxd') - self.assertEqual(cloud_spec.name, 'localhost') - self.assertEqual(cloud_spec.region, None) - self.assertEqual(cloud_spec.endpoint, None) - self.assertEqual(cloud_spec.identity_endpoint, None) - self.assertEqual(cloud_spec.storage_endpoint, None) - self.assertIsNone(cloud_spec.credential) - self.assertEqual(cloud_spec.ca_certificates, []) - self.assertEqual(cloud_spec.skip_tls_verify, False) - self.assertEqual(cloud_spec.is_controller_cloud, False) + assert cloud_spec.type == 'lxd' + assert cloud_spec.name == 'localhost' + assert cloud_spec.region is None + assert cloud_spec.endpoint is None + assert cloud_spec.identity_endpoint is None + assert cloud_spec.storage_endpoint is None + assert cloud_spec.credential is None + assert cloud_spec.ca_certificates == [] + assert not cloud_spec.skip_tls_verify + assert not cloud_spec.is_controller_cloud def test_from_dict_full(self): cred = { @@ -3859,16 +3852,16 @@ def test_from_dict_full(self): 'is-controller-cloud': True, } cloud_spec = ops.CloudSpec.from_dict(d) - self.assertEqual(cloud_spec.type, d['type']) - self.assertEqual(cloud_spec.name, d['name']) - self.assertEqual(cloud_spec.region, d['region']) - self.assertEqual(cloud_spec.endpoint, d['endpoint']) - self.assertEqual(cloud_spec.credential, ops.CloudCredential.from_dict(cred)) - self.assertEqual(cloud_spec.identity_endpoint, d['identity-endpoint']) - self.assertEqual(cloud_spec.storage_endpoint, d['storage-endpoint']) - self.assertEqual(cloud_spec.ca_certificates, d['cacertificates']) - self.assertEqual(cloud_spec.skip_tls_verify, False) - self.assertEqual(cloud_spec.is_controller_cloud, True) + assert cloud_spec.type == d['type'] + assert cloud_spec.name == d['name'] + assert cloud_spec.region == d['region'] + assert cloud_spec.endpoint == d['endpoint'] + assert cloud_spec.credential == ops.CloudCredential.from_dict(cred) + assert cloud_spec.identity_endpoint == d['identity-endpoint'] + assert cloud_spec.storage_endpoint == d['storage-endpoint'] + assert cloud_spec.ca_certificates == d['cacertificates'] + assert not cloud_spec.skip_tls_verify + assert cloud_spec.is_controller_cloud def test_from_dict_no_credential(self): d = { @@ -3883,16 +3876,16 @@ def test_from_dict_no_credential(self): 'is-controller-cloud': True, } cloud_spec = ops.CloudSpec.from_dict(d) - self.assertEqual(cloud_spec.type, d['type']) - self.assertEqual(cloud_spec.name, d['name']) - self.assertEqual(cloud_spec.region, d['region']) - self.assertEqual(cloud_spec.endpoint, d['endpoint']) - self.assertIsNone(cloud_spec.credential) - self.assertEqual(cloud_spec.identity_endpoint, d['identity-endpoint']) - self.assertEqual(cloud_spec.storage_endpoint, d['storage-endpoint']) - self.assertEqual(cloud_spec.ca_certificates, d['cacertificates']) - self.assertEqual(cloud_spec.skip_tls_verify, False) - self.assertEqual(cloud_spec.is_controller_cloud, True) + assert cloud_spec.type == d['type'] + assert cloud_spec.name == d['name'] + assert cloud_spec.region == d['region'] + assert cloud_spec.endpoint == d['endpoint'] + assert cloud_spec.credential is None + assert cloud_spec.identity_endpoint == d['identity-endpoint'] + assert cloud_spec.storage_endpoint == d['storage-endpoint'] + assert cloud_spec.ca_certificates == d['cacertificates'] + assert not cloud_spec.skip_tls_verify + assert cloud_spec.is_controller_cloud class TestGetCloudSpec(unittest.TestCase): @@ -3902,19 +3895,19 @@ def setUp(self): def test_success(self): fake_script(self, 'credential-get', """echo '{"type": "lxd", "name": "localhost"}'""") cloud_spec = self.model.get_cloud_spec() - self.assertEqual(cloud_spec.type, 'lxd') - self.assertEqual(cloud_spec.name, 'localhost') - self.assertEqual(fake_script_calls(self, clear=True), - [['credential-get', '--format=json']]) + assert cloud_spec.type == 'lxd' + assert cloud_spec.name == 'localhost' + assert fake_script_calls(self, clear=True) == \ + [['credential-get', '--format=json']] def test_error(self): fake_script( self, 'credential-get', """echo 'ERROR cannot access cloud credentials' >&2; exit 1""") - with self.assertRaises(ops.ModelError) as cm: + with pytest.raises(ops.ModelError) as excinfo: self.model.get_cloud_spec() - self.assertEqual(str(cm.exception), 'ERROR cannot access cloud credentials\n') + assert str(excinfo.value) == 'ERROR cannot access cloud credentials\n' if __name__ == "__main__": diff --git a/test/test_pebble.py b/test/test_pebble.py index 5be22663c..08470dfae 100644 --- a/test/test_pebble.py +++ b/test/test_pebble.py @@ -51,30 +51,30 @@ class TestTypes(unittest.TestCase): def test_error(self): error = pebble.Error('error') - self.assertIsInstance(error, Exception) + assert isinstance(error, Exception) def test_timeout_error(self): error = pebble.TimeoutError('timeout!') - self.assertIsInstance(error, pebble.Error) - self.assertIsInstance(error, TimeoutError) - self.assertEqual(str(error), 'timeout!') + assert isinstance(error, pebble.Error) + assert isinstance(error, TimeoutError) + assert str(error) == 'timeout!' def test_connection_error(self): error = pebble.ConnectionError('connerr!') - self.assertIsInstance(error, pebble.Error) - self.assertEqual(str(error), 'connerr!') + assert isinstance(error, pebble.Error) + assert str(error) == 'connerr!' def test_protocol_error(self): error = pebble.ProtocolError('protoerr!') - self.assertIsInstance(error, pebble.Error) - self.assertEqual(str(error), 'protoerr!') + assert isinstance(error, pebble.Error) + assert str(error) == 'protoerr!' def test_path_error(self): error = pebble.PathError('not-found', 'thing not found') - self.assertIsInstance(error, pebble.Error) - self.assertEqual(error.kind, 'not-found') - self.assertEqual(error.message, 'thing not found') - self.assertEqual(str(error), 'not-found - thing not found') + assert isinstance(error, pebble.Error) + assert error.kind == 'not-found' + assert error.message == 'thing not found' + assert str(error) == 'not-found - thing not found' def test_api_error(self): body = { @@ -86,12 +86,12 @@ def test_api_error(self): "type": "error" } error = pebble.APIError(body, 400, "Bad Request", "no services") - self.assertIsInstance(error, pebble.Error) - self.assertEqual(error.body, body) - self.assertEqual(error.code, 400) - self.assertEqual(error.status, 'Bad Request') - self.assertEqual(error.message, 'no services') - self.assertEqual(str(error), 'no services') + assert isinstance(error, pebble.Error) + assert error.body == body + assert error.code == 400 + assert error.status == 'Bad Request' + assert error.message == 'no services' + assert str(error) == 'no services' def test_change_error(self): change = pebble.Change( @@ -107,10 +107,10 @@ def test_change_error(self): ) assert change.err is not None error = pebble.ChangeError(change.err, change) - self.assertIsInstance(error, pebble.Error) - self.assertEqual(error.err, 'Some error') - self.assertEqual(error.change, change) - self.assertEqual(str(error), 'Some error') + assert isinstance(error, pebble.Error) + assert error.err == 'Some error' + assert error.change == change + assert str(error) == 'Some error' def test_change_error_with_task_logs(self): change = pebble.Change( @@ -157,42 +157,42 @@ def test_change_error_with_task_logs(self): ) assert change.err is not None error = pebble.ChangeError(change.err, change) - self.assertIsInstance(error, pebble.Error) - self.assertEqual(error.err, 'Some error') - self.assertEqual(error.change, change) - self.assertEqual(str(error), """Some error + assert isinstance(error, pebble.Error) + assert error.err == 'Some error' + assert error.change == change + assert str(error) == """Some error ----- Logs from task 0 ----- LINE1 LINE2 ----- Logs from task 2 ----- single log ------""") +-----""" def test_warning_state(self): - self.assertEqual(list(pebble.WarningState), [ + assert list(pebble.WarningState) == [ pebble.WarningState.ALL, pebble.WarningState.PENDING, - ]) - self.assertEqual(pebble.WarningState.ALL.value, 'all') - self.assertEqual(pebble.WarningState.PENDING.value, 'pending') + ] + assert pebble.WarningState.ALL.value == 'all' + assert pebble.WarningState.PENDING.value == 'pending' def test_change_state(self): - self.assertEqual(list(pebble.ChangeState), [ + assert list(pebble.ChangeState) == [ pebble.ChangeState.ALL, pebble.ChangeState.IN_PROGRESS, pebble.ChangeState.READY, - ]) - self.assertEqual(pebble.ChangeState.ALL.value, 'all') - self.assertEqual(pebble.ChangeState.IN_PROGRESS.value, 'in-progress') - self.assertEqual(pebble.ChangeState.READY.value, 'ready') + ] + assert pebble.ChangeState.ALL.value == 'all' + assert pebble.ChangeState.IN_PROGRESS.value == 'in-progress' + assert pebble.ChangeState.READY.value == 'ready' def test_system_info_init(self): info = pebble.SystemInfo(version='1.2.3') - self.assertEqual(info.version, '1.2.3') + assert info.version == '1.2.3' def test_system_info_from_dict(self): info = pebble.SystemInfo.from_dict({'version': '3.2.1'}) - self.assertEqual(info.version, '3.2.1') + assert info.version == '3.2.1' def test_warning_init(self): warning = pebble.Warning( @@ -203,12 +203,12 @@ def test_warning_init(self): expire_after='1s', repeat_after='2s', ) - self.assertEqual(warning.message, 'Beware!') - self.assertEqual(warning.first_added, datetime_utc(2021, 1, 1, 1, 1, 1)) - self.assertEqual(warning.last_added, datetime_utc(2021, 1, 26, 2, 3, 4)) - self.assertEqual(warning.last_shown, None) - self.assertEqual(warning.expire_after, '1s') - self.assertEqual(warning.repeat_after, '2s') + assert warning.message == 'Beware!' + assert warning.first_added == datetime_utc(2021, 1, 1, 1, 1, 1) + assert warning.last_added == datetime_utc(2021, 1, 26, 2, 3, 4) + assert warning.last_shown is None + assert warning.expire_after == '1s' + assert warning.repeat_after == '2s' def test_warning_from_dict(self): d: pebble._WarningDict = { @@ -219,34 +219,34 @@ def test_warning_from_dict(self): 'repeat-after': '2s', } warning = pebble.Warning.from_dict(d) - self.assertEqual(warning.message, 'Look out...') - self.assertEqual(warning.first_added, datetime_nzdt(2020, 12, 25, 17, 18, 54, 16274)) - self.assertEqual(warning.last_added, datetime_nzdt(2021, 1, 26, 17, 1, 2, 123450)) - self.assertEqual(warning.last_shown, None) - self.assertEqual(warning.expire_after, '1s') - self.assertEqual(warning.repeat_after, '2s') + assert warning.message == 'Look out...' + assert warning.first_added == datetime_nzdt(2020, 12, 25, 17, 18, 54, 16274) + assert warning.last_added == datetime_nzdt(2021, 1, 26, 17, 1, 2, 123450) + assert warning.last_shown is None + assert warning.expire_after == '1s' + assert warning.repeat_after == '2s' d['last-shown'] = None warning = pebble.Warning.from_dict(d) - self.assertEqual(warning.last_shown, None) + assert warning.last_shown is None d['last-shown'] = '2021-08-04T03:02:01.000000000+13:00' warning = pebble.Warning.from_dict(d) - self.assertEqual(warning.last_shown, datetime_nzdt(2021, 8, 4, 3, 2, 1)) + assert warning.last_shown == datetime_nzdt(2021, 8, 4, 3, 2, 1) d['first-added'] = '2020-02-03T02:00:40.000000+00:00' d['last-added'] = '2021-03-04T03:01:41.100000+00:00' d['last-shown'] = '2022-04-05T06:02:42.200000+00:00' warning = pebble.Warning.from_dict(d) - self.assertEqual(warning.first_added, datetime_utc(2020, 2, 3, 2, 0, 40, 0)) - self.assertEqual(warning.last_added, datetime_utc(2021, 3, 4, 3, 1, 41, 100000)) - self.assertEqual(warning.last_shown, datetime_utc(2022, 4, 5, 6, 2, 42, 200000)) + assert warning.first_added == datetime_utc(2020, 2, 3, 2, 0, 40, 0) + assert warning.last_added == datetime_utc(2021, 3, 4, 3, 1, 41, 100000) + assert warning.last_shown == datetime_utc(2022, 4, 5, 6, 2, 42, 200000) def test_task_progress_init(self): tp = pebble.TaskProgress(label='foo', done=3, total=7) - self.assertEqual(tp.label, 'foo') - self.assertEqual(tp.done, 3) - self.assertEqual(tp.total, 7) + assert tp.label == 'foo' + assert tp.done == 3 + assert tp.total == 7 def test_task_progress_from_dict(self): tp = pebble.TaskProgress.from_dict({ @@ -254,13 +254,13 @@ def test_task_progress_from_dict(self): 'done': 3, 'total': 7, }) - self.assertEqual(tp.label, 'foo') - self.assertEqual(tp.done, 3) - self.assertEqual(tp.total, 7) + assert tp.label == 'foo' + assert tp.done == 3 + assert tp.total == 7 def test_task_id(self): task_id = pebble.TaskID('1234') - self.assertEqual(task_id, '1234') + assert task_id == '1234' def test_task_init(self): task = pebble.Task( @@ -273,17 +273,17 @@ def test_task_init(self): spawn_time=datetime_nzdt(2021, 1, 28, 14, 37, 3, 270218), ready_time=datetime_nzdt(2021, 1, 28, 14, 37, 2, 247158), ) - self.assertEqual(task.id, '42') - self.assertEqual(task.kind, 'start') - self.assertEqual(task.summary, 'Start service "svc"') - self.assertEqual(task.status, 'Done') - self.assertEqual(task.log, []) - self.assertEqual(task.progress.label, 'foo') - self.assertEqual(task.progress.done, 3) - self.assertEqual(task.progress.total, 7) - self.assertEqual(task.spawn_time, datetime_nzdt(2021, 1, 28, 14, 37, 3, 270218)) - self.assertEqual(task.ready_time, datetime_nzdt(2021, 1, 28, 14, 37, 2, 247158)) - self.assertEqual(task.data, {}) + assert task.id == '42' + assert task.kind == 'start' + assert task.summary == 'Start service "svc"' + assert task.status == 'Done' + assert task.log == [] + assert task.progress.label == 'foo' + assert task.progress.done == 3 + assert task.progress.total == 7 + assert task.spawn_time == datetime_nzdt(2021, 1, 28, 14, 37, 3, 270218) + assert task.ready_time == datetime_nzdt(2021, 1, 28, 14, 37, 2, 247158) + assert task.data == {} def test_task_from_dict(self): d: pebble._TaskDict = { @@ -301,27 +301,27 @@ def test_task_from_dict(self): "data": {"exit-code": 42}, } task = pebble.Task.from_dict(d) - self.assertEqual(task.id, '78') - self.assertEqual(task.kind, 'start') - self.assertEqual(task.summary, 'Start service "svc"') - self.assertEqual(task.status, 'Done') - self.assertEqual(task.log, []) - self.assertEqual(task.progress.label, '') - self.assertEqual(task.progress.done, 1) - self.assertEqual(task.progress.total, 1) - self.assertEqual(task.ready_time, datetime_nzdt(2021, 1, 28, 14, 37, 3, 270219)) - self.assertEqual(task.spawn_time, datetime_nzdt(2021, 1, 28, 14, 37, 2, 247158)) - self.assertEqual(task.data, {'exit-code': 42}) + assert task.id == '78' + assert task.kind == 'start' + assert task.summary == 'Start service "svc"' + assert task.status == 'Done' + assert task.log == [] + assert task.progress.label == '' + assert task.progress.done == 1 + assert task.progress.total == 1 + assert task.ready_time == datetime_nzdt(2021, 1, 28, 14, 37, 3, 270219) + assert task.spawn_time == datetime_nzdt(2021, 1, 28, 14, 37, 2, 247158) + assert task.data == {'exit-code': 42} d['ready-time'] = '2021-01-28T14:37:03.270218778+00:00' d['spawn-time'] = '2021-01-28T14:37:02.247158162+00:00' task = pebble.Task.from_dict(d) - self.assertEqual(task.ready_time, datetime_utc(2021, 1, 28, 14, 37, 3, 270219)) - self.assertEqual(task.spawn_time, datetime_utc(2021, 1, 28, 14, 37, 2, 247158)) + assert task.ready_time == datetime_utc(2021, 1, 28, 14, 37, 3, 270219) + assert task.spawn_time == datetime_utc(2021, 1, 28, 14, 37, 2, 247158) def test_change_id(self): change_id = pebble.ChangeID('1234') - self.assertEqual(change_id, '1234') + assert change_id == '1234' def test_change_init(self): change = pebble.Change( @@ -335,16 +335,16 @@ def test_change_init(self): summary='Autostart service "svc"', tasks=[], ) - self.assertEqual(change.id, '70') - self.assertEqual(change.kind, 'autostart') - self.assertEqual(change.err, 'SILLY') - self.assertEqual(change.ready, True) - self.assertEqual(change.ready_time, datetime_nzdt(2021, 1, 28, 14, 37, 4, 291517)) - self.assertEqual(change.spawn_time, datetime_nzdt(2021, 1, 28, 14, 37, 2, 247202)) - self.assertEqual(change.status, 'Done') - self.assertEqual(change.summary, 'Autostart service "svc"') - self.assertEqual(change.tasks, []) - self.assertEqual(change.data, {}) + assert change.id == '70' + assert change.kind == 'autostart' + assert change.err == 'SILLY' + assert change.ready + assert change.ready_time == datetime_nzdt(2021, 1, 28, 14, 37, 4, 291517) + assert change.spawn_time == datetime_nzdt(2021, 1, 28, 14, 37, 2, 247202) + assert change.status == 'Done' + assert change.summary == 'Autostart service "svc"' + assert change.tasks == [] + assert change.data == {} def test_change_from_dict(self): d: 'pebble._ChangeDict' = { @@ -360,25 +360,25 @@ def test_change_from_dict(self): "data": {"exit-code": 42}, } change = pebble.Change.from_dict(d) - self.assertEqual(change.id, '70') - self.assertEqual(change.kind, 'autostart') - self.assertEqual(change.err, 'SILLY') - self.assertEqual(change.ready, True) - self.assertEqual(change.ready_time, datetime_nzdt(2021, 1, 28, 14, 37, 4, 291518)) - self.assertEqual(change.spawn_time, datetime_nzdt(2021, 1, 28, 14, 37, 2, 247202)) - self.assertEqual(change.status, 'Done') - self.assertEqual(change.summary, 'Autostart service "svc"') - self.assertEqual(change.tasks, []) - self.assertEqual(change.data, {'exit-code': 42}) + assert change.id == '70' + assert change.kind == 'autostart' + assert change.err == 'SILLY' + assert change.ready + assert change.ready_time == datetime_nzdt(2021, 1, 28, 14, 37, 4, 291518) + assert change.spawn_time == datetime_nzdt(2021, 1, 28, 14, 37, 2, 247202) + assert change.status == 'Done' + assert change.summary == 'Autostart service "svc"' + assert change.tasks == [] + assert change.data == {'exit-code': 42} d['ready-time'] = '2021-01-28T14:37:04.291517768+00:00' d['spawn-time'] = '2021-01-28T14:37:02.247202105+00:00' change = pebble.Change.from_dict(d) - self.assertEqual(change.ready_time, datetime_utc(2021, 1, 28, 14, 37, 4, 291518)) - self.assertEqual(change.spawn_time, datetime_utc(2021, 1, 28, 14, 37, 2, 247202)) + assert change.ready_time == datetime_utc(2021, 1, 28, 14, 37, 4, 291518) + assert change.spawn_time == datetime_utc(2021, 1, 28, 14, 37, 2, 247202) def test_file_type(self): - self.assertEqual(list(pebble.FileType), [ + assert list(pebble.FileType) == [ pebble.FileType.FILE, pebble.FileType.DIRECTORY, pebble.FileType.SYMLINK, @@ -386,29 +386,29 @@ def test_file_type(self): pebble.FileType.NAMED_PIPE, pebble.FileType.DEVICE, pebble.FileType.UNKNOWN, - ]) - self.assertEqual(pebble.FileType.FILE.value, 'file') - self.assertEqual(pebble.FileType.DIRECTORY.value, 'directory') - self.assertEqual(pebble.FileType.SYMLINK.value, 'symlink') - self.assertEqual(pebble.FileType.SOCKET.value, 'socket') - self.assertEqual(pebble.FileType.NAMED_PIPE.value, 'named-pipe') - self.assertEqual(pebble.FileType.DEVICE.value, 'device') - self.assertEqual(pebble.FileType.UNKNOWN.value, 'unknown') + ] + assert pebble.FileType.FILE.value == 'file' + assert pebble.FileType.DIRECTORY.value == 'directory' + assert pebble.FileType.SYMLINK.value == 'symlink' + assert pebble.FileType.SOCKET.value == 'socket' + assert pebble.FileType.NAMED_PIPE.value == 'named-pipe' + assert pebble.FileType.DEVICE.value == 'device' + assert pebble.FileType.UNKNOWN.value == 'unknown' def test_file_info_init(self): info = pebble.FileInfo('/etc/hosts', 'hosts', pebble.FileType.FILE, 123, 0o644, datetime_nzdt(2021, 1, 28, 14, 37, 4, 291518), 12, 'bob', 34, 'staff') - self.assertEqual(info.path, '/etc/hosts') - self.assertEqual(info.name, 'hosts') - self.assertEqual(info.type, pebble.FileType.FILE) - self.assertEqual(info.size, 123) - self.assertEqual(info.permissions, 0o644) - self.assertEqual(info.last_modified, datetime_nzdt(2021, 1, 28, 14, 37, 4, 291518)) - self.assertEqual(info.user_id, 12) - self.assertEqual(info.user, 'bob') - self.assertEqual(info.group_id, 34) - self.assertEqual(info.group, 'staff') + assert info.path == '/etc/hosts' + assert info.name == 'hosts' + assert info.type == pebble.FileType.FILE + assert info.size == 123 + assert info.permissions == 0o644 + assert info.last_modified == datetime_nzdt(2021, 1, 28, 14, 37, 4, 291518) + assert info.user_id == 12 + assert info.user == 'bob' + assert info.group_id == 34 + assert info.group == 'staff' def test_file_info_from_dict(self): d: pebble._FileInfoDict = { @@ -419,15 +419,15 @@ def test_file_info_from_dict(self): 'last-modified': '2021-01-28T14:37:04.291517768+13:00', } info = pebble.FileInfo.from_dict(d) - self.assertEqual(info.path, '/etc') - self.assertEqual(info.name, 'etc') - self.assertEqual(info.type, pebble.FileType.DIRECTORY) - self.assertEqual(info.permissions, 0o644) - self.assertEqual(info.last_modified, datetime_nzdt(2021, 1, 28, 14, 37, 4, 291518)) - self.assertIs(info.user_id, None) - self.assertIs(info.user, None) - self.assertIs(info.group_id, None) - self.assertIs(info.group, None) + assert info.path == '/etc' + assert info.name == 'etc' + assert info.type == pebble.FileType.DIRECTORY + assert info.permissions == 0o644 + assert info.last_modified == datetime_nzdt(2021, 1, 28, 14, 37, 4, 291518) + assert info.user_id is None + assert info.user is None + assert info.group_id is None + assert info.group is None d['type'] = 'foobar' d['size'] = 123 @@ -436,12 +436,12 @@ def test_file_info_from_dict(self): d['group-id'] = 34 d['group'] = 'staff' info = pebble.FileInfo.from_dict(d) - self.assertEqual(info.type, 'foobar') - self.assertEqual(info.size, 123) - self.assertEqual(info.user_id, 12) - self.assertEqual(info.user, 'bob') - self.assertEqual(info.group_id, 34) - self.assertEqual(info.group, 'staff') + assert info.type == 'foobar' + assert info.size == 123 + assert info.user_id == 12 + assert info.user == 'bob' + assert info.group_id == 34 + assert info.group == 'staff' def test_notice_from_dict(self): notice = pebble.Notice.from_dict({ @@ -457,7 +457,7 @@ def test_notice_from_dict(self): 'repeat-after': '30m', 'expire-after': '24h', }) - self.assertEqual(notice, pebble.Notice( + assert notice == pebble.Notice( id='123', user_id=1000, type=pebble.NoticeType.CUSTOM, @@ -469,7 +469,7 @@ def test_notice_from_dict(self): last_data={'k1': 'v1', 'k2': 'v2'}, repeat_after=datetime.timedelta(minutes=30), expire_after=datetime.timedelta(hours=24), - )) + ) notice = pebble.Notice.from_dict({ 'id': '124', @@ -480,7 +480,7 @@ def test_notice_from_dict(self): 'last-repeated': '2023-12-07T17:01:04.123456789Z', 'occurrences': 8, }) - self.assertEqual(notice, pebble.Notice( + assert notice == pebble.Notice( id='124', user_id=None, type='other', @@ -489,13 +489,13 @@ def test_notice_from_dict(self): last_occurred=datetime_utc(2023, 12, 7, 17, 1, 3, 123457), last_repeated=datetime_utc(2023, 12, 7, 17, 1, 4, 123457), occurrences=8, - )) + ) class TestPlan(unittest.TestCase): def test_services(self): plan = pebble.Plan('') - self.assertEqual(plan.services, {}) + assert plan.services == {} plan = pebble.Plan(""" services: @@ -504,18 +504,18 @@ def test_services(self): command: echo foo """) - self.assertEqual(len(plan.services), 1) - self.assertEqual(plan.services['foo'].name, 'foo') - self.assertEqual(plan.services['foo'].override, 'replace') - self.assertEqual(plan.services['foo'].command, 'echo foo') + assert len(plan.services) == 1 + assert plan.services['foo'].name == 'foo' + assert plan.services['foo'].override == 'replace' + assert plan.services['foo'].command == 'echo foo' # Should be read-only ("can't set attribute") - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): plan.services = {} # type: ignore def test_checks(self): plan = pebble.Plan('') - self.assertEqual(plan.checks, {}) + assert plan.checks == {} plan = pebble.Plan(""" checks: @@ -525,18 +525,18 @@ def test_checks(self): url: https://example.com/ """) - self.assertEqual(len(plan.checks), 1) - self.assertEqual(plan.checks['bar'].name, 'bar') - self.assertEqual(plan.checks['bar'].override, 'replace') - self.assertEqual(plan.checks['bar'].http, {'url': 'https://example.com/'}) + assert len(plan.checks) == 1 + assert plan.checks['bar'].name == 'bar' + assert plan.checks['bar'].override == 'replace' + assert plan.checks['bar'].http == {'url': 'https://example.com/'} # Should be read-only ("can't set attribute") - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): plan.checks = {} # type: ignore def test_log_targets(self): plan = pebble.Plan('') - self.assertEqual(plan.log_targets, {}) + assert plan.log_targets == {} location = "https://example.com:3100/loki/api/v1/push" plan = pebble.Plan(f""" @@ -547,21 +547,21 @@ def test_log_targets(self): location: {location} """) - self.assertEqual(len(plan.log_targets), 1) - self.assertEqual(plan.log_targets['baz'].name, 'baz') - self.assertEqual(plan.log_targets['baz'].override, 'replace') - self.assertEqual(plan.log_targets['baz'].type, "loki") - self.assertEqual(plan.log_targets['baz'].location, location) + assert len(plan.log_targets) == 1 + assert plan.log_targets['baz'].name == 'baz' + assert plan.log_targets['baz'].override == 'replace' + assert plan.log_targets['baz'].type == "loki" + assert plan.log_targets['baz'].location == location # Should be read-only ("can't set attribute") - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): plan.log_targets = {} # type: ignore def test_yaml(self): # Starting with nothing, we get the empty result plan = pebble.Plan('') - self.assertEqual(plan.to_yaml(), '{}\n') - self.assertEqual(str(plan), '{}\n') + assert plan.to_yaml() == '{}\n' + assert str(plan) == '{}\n' # With a service, we return validated yaml content. raw = '''\ @@ -583,15 +583,15 @@ def test_yaml(self): ''' plan = pebble.Plan(raw) reformed = yaml.safe_dump(yaml.safe_load(raw)) - self.assertEqual(plan.to_yaml(), reformed) - self.assertEqual(str(plan), reformed) + assert plan.to_yaml() == reformed + assert str(plan) == reformed def test_plandict(self): # Starting with nothing, we get the empty result. plan = pebble.Plan({}) - self.assertEqual(plan.to_dict(), {}) + assert plan.to_dict() == {} plan = pebble.Plan() - self.assertEqual(plan.to_dict(), {}) + assert plan.to_dict() == {} # With a service, we return validated yaml content. raw: pebble.PlanDict = { @@ -615,7 +615,7 @@ def test_plandict(self): }, } plan = pebble.Plan(raw) - self.assertEqual(plan.to_dict(), raw) + assert plan.to_dict() == raw def test_service_equality(self): plan = pebble.Plan(""" @@ -631,12 +631,12 @@ def test_service_equality(self): "command": "echo foo" }) old_services = {"foo": old_service} - self.assertEqual(plan.services, old_services) + assert plan.services == old_services services_as_dict = { "foo": {"override": "replace", "command": "echo foo"} } - self.assertEqual(plan.services, services_as_dict) + assert plan.services == services_as_dict def test_plan_equality(self): plan1 = pebble.Plan(''' @@ -645,14 +645,14 @@ def test_plan_equality(self): override: replace command: echo foo ''') - self.assertNotEqual(plan1, "foo") + assert plan1 != "foo" plan2 = pebble.Plan(''' services: foo: command: echo foo override: replace ''') - self.assertEqual(plan1, plan2) + assert plan1 == plan2 plan1_as_dict = { "services": { "foo": { @@ -661,7 +661,7 @@ def test_plan_equality(self): }, }, } - self.assertEqual(plan1, plan1_as_dict) + assert plan1 == plan1_as_dict plan3 = pebble.Plan(''' services: foo: @@ -669,7 +669,7 @@ def test_plan_equality(self): command: echo bar ''') # Different command. - self.assertNotEqual(plan1, plan3) + assert plan1 != plan3 plan4 = pebble.Plan(''' services: foo: @@ -705,7 +705,7 @@ def test_plan_equality(self): location: https://example.com:3100/loki/api/v1/push ''') # Different checks.bar.http - self.assertNotEqual(plan4, plan5) + assert plan4 != plan5 plan6 = pebble.Plan(''' services: foo: @@ -724,7 +724,7 @@ def test_plan_equality(self): location: https://example.com:3200/loki/api/v1/push ''') # Reordered elements. - self.assertNotEqual(plan4, plan6) + assert plan4 != plan6 plan7 = pebble.Plan(''' services: foo: @@ -744,17 +744,17 @@ def test_plan_equality(self): ''') # Reordered sections. - self.assertEqual(plan4, plan7) + assert plan4 == plan7 class TestLayer(unittest.TestCase): def _assert_empty(self, layer: pebble.Layer): - self.assertEqual(layer.summary, '') - self.assertEqual(layer.description, '') - self.assertEqual(layer.services, {}) - self.assertEqual(layer.checks, {}) - self.assertEqual(layer.log_targets, {}) - self.assertEqual(layer.to_dict(), {}) + assert layer.summary == '' + assert layer.description == '' + assert layer.services == {} + assert layer.checks == {} + assert layer.log_targets == {} + assert layer.to_dict() == {} def test_no_args(self): s = pebble.Layer() @@ -790,22 +790,22 @@ def test_dict(self): } } s = pebble.Layer(d) - self.assertEqual(s.summary, 'Sum Mary') - self.assertEqual(s.description, 'The quick brown fox!') - self.assertEqual(s.services['foo'].name, 'foo') - self.assertEqual(s.services['foo'].summary, 'Foo') - self.assertEqual(s.services['foo'].command, 'echo foo') - self.assertEqual(s.services['bar'].name, 'bar') - self.assertEqual(s.services['bar'].summary, 'Bar') - self.assertEqual(s.services['bar'].command, 'echo bar') - self.assertEqual(s.log_targets['baz'].name, 'baz') - self.assertEqual(s.log_targets['baz'].override, 'merge') - self.assertEqual(s.log_targets['baz'].type, 'loki') - self.assertEqual(s.log_targets['baz'].location, 'https://example.com') - self.assertEqual(s.log_targets['baz'].services, ['foo']) - self.assertEqual(s.log_targets['baz'].labels, {'key': 'value $VAR'}) - - self.assertEqual(s.to_dict(), d) + assert s.summary == 'Sum Mary' + assert s.description == 'The quick brown fox!' + assert s.services['foo'].name == 'foo' + assert s.services['foo'].summary == 'Foo' + assert s.services['foo'].command == 'echo foo' + assert s.services['bar'].name == 'bar' + assert s.services['bar'].summary == 'Bar' + assert s.services['bar'].command == 'echo bar' + assert s.log_targets['baz'].name == 'baz' + assert s.log_targets['baz'].override == 'merge' + assert s.log_targets['baz'].type == 'loki' + assert s.log_targets['baz'].location == 'https://example.com' + assert s.log_targets['baz'].services == ['foo'] + assert s.log_targets['baz'].labels == {'key': 'value $VAR'} + + assert s.to_dict() == d def test_yaml(self): s = pebble.Layer('') @@ -838,30 +838,30 @@ def test_yaml(self): summary: Sum Mary """ s = pebble.Layer(yaml) - self.assertEqual(s.summary, 'Sum Mary') - self.assertEqual(s.description, 'The quick brown fox!') - self.assertEqual(s.services['foo'].name, 'foo') - self.assertEqual(s.services['foo'].summary, 'Foo') - self.assertEqual(s.services['foo'].command, 'echo foo') - self.assertEqual(s.services['bar'].name, 'bar') - self.assertEqual(s.services['bar'].summary, 'Bar') - self.assertEqual(s.services['bar'].command, 'echo bar') - self.assertEqual(s.services['bar'].environment, - {'ENV1': 'value1', 'ENV2': 'value2'}) - self.assertEqual(s.services['bar'].user, 'bob') - self.assertEqual(s.services['bar'].user_id, 1000) - self.assertEqual(s.services['bar'].group, 'staff') - self.assertEqual(s.services['bar'].group_id, 2000) - - self.assertEqual(s.checks['chk'].name, 'chk') - self.assertEqual(s.checks['chk'].http, {'url': 'https://example.com/'}) - - self.assertEqual(s.log_targets['baz'].name, 'baz') - self.assertEqual(s.log_targets['baz'].override, 'replace') - self.assertEqual(s.log_targets['baz'].location, 'https://example.com:3100') - - self.assertEqual(s.to_yaml(), yaml) - self.assertEqual(str(s), yaml) + assert s.summary == 'Sum Mary' + assert s.description == 'The quick brown fox!' + assert s.services['foo'].name == 'foo' + assert s.services['foo'].summary == 'Foo' + assert s.services['foo'].command == 'echo foo' + assert s.services['bar'].name == 'bar' + assert s.services['bar'].summary == 'Bar' + assert s.services['bar'].command == 'echo bar' + assert s.services['bar'].environment == \ + {'ENV1': 'value1', 'ENV2': 'value2'} + assert s.services['bar'].user == 'bob' + assert s.services['bar'].user_id == 1000 + assert s.services['bar'].group == 'staff' + assert s.services['bar'].group_id == 2000 + + assert s.checks['chk'].name == 'chk' + assert s.checks['chk'].http == {'url': 'https://example.com/'} + + assert s.log_targets['baz'].name == 'baz' + assert s.log_targets['baz'].override == 'replace' + assert s.log_targets['baz'].location == 'https://example.com:3100' + + assert s.to_yaml() == yaml + assert str(s) == yaml def test_layer_service_equality(self): s = pebble.Layer({}) @@ -884,7 +884,7 @@ def test_layer_service_equality(self): s = pebble.Layer(d) t = pebble.Layer(d) - self.assertEqual(s.services, t.services) + assert s.services == t.services def test_layer_equality(self): s = pebble.Layer({}) @@ -905,43 +905,43 @@ def test_layer_equality(self): } } t = pebble.Layer(d) - self.assertNotEqual(s, t) - self.assertNotEqual(t, {}) - self.assertEqual(t, d) + assert s != t + assert t != {} + assert t == d s = pebble.Layer(d) - self.assertEqual(s, t) - self.assertNotEqual(s, {}) - self.assertEqual(s, d) + assert s == t + assert s != {} + assert s == d - self.assertNotEqual(s, 5) + assert s != 5 class TestService(unittest.TestCase): def _assert_empty(self, service: pebble.Service, name: str): - self.assertEqual(service.name, name) - self.assertEqual(service.summary, '') - self.assertEqual(service.description, '') - self.assertEqual(service.startup, '') - self.assertEqual(service.override, '') - self.assertEqual(service.command, '') - self.assertEqual(service.after, []) - self.assertEqual(service.before, []) - self.assertEqual(service.requires, []) - self.assertEqual(service.environment, {}) - self.assertEqual(service.user, '') - self.assertIs(service.user_id, None) - self.assertEqual(service.group, '') - self.assertIs(service.group_id, None) - self.assertEqual(service.working_dir, '') - self.assertEqual(service.on_success, '') - self.assertEqual(service.on_failure, '') - self.assertEqual(service.on_check_failure, {}) - self.assertEqual(service.backoff_delay, '') - self.assertIs(service.backoff_factor, None) - self.assertEqual(service.backoff_limit, '') - self.assertIs(service.kill_delay, '') - self.assertEqual(service.to_dict(), {}) + assert service.name == name + assert service.summary == '' + assert service.description == '' + assert service.startup == '' + assert service.override == '' + assert service.command == '' + assert service.after == [] + assert service.before == [] + assert service.requires == [] + assert service.environment == {} + assert service.user == '' + assert service.user_id is None + assert service.group == '' + assert service.group_id is None + assert service.working_dir == '' + assert service.on_success == '' + assert service.on_failure == '' + assert service.on_check_failure == {} + assert service.backoff_delay == '' + assert service.backoff_factor is None + assert service.backoff_limit == '' + assert service.kill_delay == '' + assert service.to_dict() == {} def test_name_only(self): s = pebble.Service('Name 0') @@ -975,29 +975,29 @@ def test_dict(self): 'kill-delay': '420s', } s = pebble.Service('Name 2', d) - self.assertEqual(s.name, 'Name 2') - self.assertEqual(s.description, 'The lazy quick brown') - self.assertEqual(s.startup, 'Start Up') - self.assertEqual(s.override, 'override') - self.assertEqual(s.command, 'echo sum mary') - self.assertEqual(s.after, ['a1', 'a2']) - self.assertEqual(s.before, ['b1', 'b2']) - self.assertEqual(s.requires, ['r1', 'r2']) - self.assertEqual(s.environment, {'k1': 'v1', 'k2': 'v2'}) - self.assertEqual(s.user, 'bob') - self.assertEqual(s.user_id, 1000) - self.assertEqual(s.group, 'staff') - self.assertEqual(s.group_id, 2000) - self.assertEqual(s.working_dir, '/working/dir') - self.assertEqual(s.on_success, 'restart') - self.assertEqual(s.on_failure, 'ignore') - self.assertEqual(s.on_check_failure, {'chk1': 'halt'}) - self.assertEqual(s.backoff_delay, '1s') - self.assertEqual(s.backoff_factor, 4) - self.assertEqual(s.backoff_limit, '10s') - self.assertEqual(s.kill_delay, '420s') - - self.assertEqual(s.to_dict(), d) + assert s.name == 'Name 2' + assert s.description == 'The lazy quick brown' + assert s.startup == 'Start Up' + assert s.override == 'override' + assert s.command == 'echo sum mary' + assert s.after == ['a1', 'a2'] + assert s.before == ['b1', 'b2'] + assert s.requires == ['r1', 'r2'] + assert s.environment == {'k1': 'v1', 'k2': 'v2'} + assert s.user == 'bob' + assert s.user_id == 1000 + assert s.group == 'staff' + assert s.group_id == 2000 + assert s.working_dir == '/working/dir' + assert s.on_success == 'restart' + assert s.on_failure == 'ignore' + assert s.on_check_failure == {'chk1': 'halt'} + assert s.backoff_delay == '1s' + assert s.backoff_factor == 4 + assert s.backoff_limit == '10s' + assert s.kill_delay == '420s' + + assert s.to_dict() == d # Ensure pebble.Service has made copies of mutable objects s.after.append('a3') @@ -1005,15 +1005,15 @@ def test_dict(self): s.requires.append('r3') s.environment['k3'] = 'v3' s.on_check_failure['chk2'] = 'ignore' - self.assertEqual(s.after, ['a1', 'a2', 'a3']) - self.assertEqual(s.before, ['b1', 'b2', 'b3']) - self.assertEqual(s.requires, ['r1', 'r2', 'r3']) - self.assertEqual(s.environment, {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}) - self.assertEqual(d['after'], ['a1', 'a2']) - self.assertEqual(d['before'], ['b1', 'b2']) - self.assertEqual(d['requires'], ['r1', 'r2']) - self.assertEqual(d['environment'], {'k1': 'v1', 'k2': 'v2'}) - self.assertEqual(d['on-check-failure'], {'chk1': 'halt'}) + assert s.after == ['a1', 'a2', 'a3'] + assert s.before == ['b1', 'b2', 'b3'] + assert s.requires == ['r1', 'r2', 'r3'] + assert s.environment == {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'} + assert d['after'] == ['a1', 'a2'] + assert d['before'] == ['b1', 'b2'] + assert d['requires'] == ['r1', 'r2'] + assert d['environment'] == {'k1': 'v1', 'k2': 'v2'} + assert d['on-check-failure'] == {'chk1': 'halt'} def test_equality(self): d: pebble.ServiceDict = { @@ -1033,7 +1033,7 @@ def test_equality(self): } one = pebble.Service("Name 1", d) two = pebble.Service("Name 1", d) - self.assertEqual(one, two) + assert one == two as_dict = { 'summary': 'Sum Mary', @@ -1050,22 +1050,22 @@ def test_equality(self): 'group': 'staff', 'group-id': 2000, } - self.assertEqual(one, as_dict) + assert one == as_dict - self.assertNotEqual(one, 5) + assert one != 5 class TestCheck(unittest.TestCase): def _assert_empty(self, check: pebble.Check, name: str): - self.assertEqual(check.name, name) - self.assertEqual(check.override, '') - self.assertEqual(check.level, pebble.CheckLevel.UNSET) - self.assertEqual(check.period, '') - self.assertEqual(check.timeout, '') - self.assertIs(check.threshold, None) - self.assertIs(check.http, None) - self.assertIs(check.tcp, None) - self.assertIs(check.exec, None) + assert check.name == name + assert check.override == '' + assert check.level == pebble.CheckLevel.UNSET + assert check.period == '' + assert check.timeout == '' + assert check.threshold is None + assert check.http is None + assert check.tcp is None + assert check.exec is None def test_name_only(self): check = pebble.Check('chk') @@ -1085,26 +1085,26 @@ def test_dict(self): 'exec': {'command': 'echo foo'}, } check = pebble.Check('chk-http', d) - self.assertEqual(check.name, 'chk-http') - self.assertEqual(check.override, 'replace') - self.assertEqual(check.level, pebble.CheckLevel.ALIVE) - self.assertEqual(check.period, '10s') - self.assertEqual(check.timeout, '3s') - self.assertEqual(check.threshold, 5) - self.assertEqual(check.http, {'url': 'https://example.com/'}) - self.assertEqual(check.tcp, {'port': 80}) - self.assertEqual(check.exec, {'command': 'echo foo'}) - - self.assertEqual(check.to_dict(), d) + assert check.name == 'chk-http' + assert check.override == 'replace' + assert check.level == pebble.CheckLevel.ALIVE + assert check.period == '10s' + assert check.timeout == '3s' + assert check.threshold == 5 + assert check.http == {'url': 'https://example.com/'} + assert check.tcp == {'port': 80} + assert check.exec == {'command': 'echo foo'} + + assert check.to_dict() == d # Ensure pebble.Check has made copies of mutable objects assert check.http is not None and check.tcp is not None and check.exec is not None check.http['url'] = 'https://www.google.com' - self.assertEqual(d['http'], {'url': 'https://example.com/'}) + assert d['http'] == {'url': 'https://example.com/'} check.tcp['port'] = 81 - self.assertEqual(d['tcp'], {'port': 80}) + assert d['tcp'] == {'port': 80} check.exec['command'] = 'foo' - self.assertEqual(d['exec'], {'command': 'echo foo'}) + assert d['exec'] == {'command': 'echo foo'} def test_level_raw(self): d: pebble.CheckDict = { @@ -1116,7 +1116,7 @@ def test_level_raw(self): 'http': {'url': 'https://example.com/'}, } check = pebble.Check('chk-http', d) - self.assertEqual(check.level, 'foobar!') # remains a string + assert check.level == 'foobar!' # remains a string def test_equality(self): d: pebble.CheckDict = { @@ -1129,25 +1129,25 @@ def test_equality(self): } one = pebble.Check('one', d) two = pebble.Check('two', d) - self.assertEqual(one, two) - self.assertEqual(one, d) - self.assertEqual(two, d) - self.assertEqual(one, one.to_dict()) - self.assertEqual(two, two.to_dict()) + assert one == two + assert one == d + assert two == d + assert one == one.to_dict() + assert two == two.to_dict() d['level'] = 'ready' - self.assertNotEqual(one, d) + assert one != d - self.assertNotEqual(one, 5) + assert one != 5 class TestLogTarget(unittest.TestCase): def _assert_empty(self, target: pebble.LogTarget, name: str): - self.assertEqual(target.name, name) - self.assertEqual(target.override, '') - self.assertEqual(target.type, '') - self.assertEqual(target.location, '') - self.assertEqual(target.services, []) - self.assertIs(target.labels, None) + assert target.name == name + assert target.override == '' + assert target.type == '' + assert target.location == '' + assert target.services == [] + assert target.labels is None def test_name_only(self): target = pebble.LogTarget('tgt') @@ -1162,22 +1162,22 @@ def test_dict(self): 'labels': {'key': 'val', 'key2': 'val2'} } target = pebble.LogTarget('tgt', d) - self.assertEqual(target.name, 'tgt') - self.assertEqual(target.override, 'replace') - self.assertEqual(target.type, 'loki') - self.assertEqual(target.location, 'https://example.com:3100/loki/api/v1/push') - self.assertEqual(target.services, ['+all']) - self.assertEqual(target.labels, {'key': 'val', 'key2': 'val2'}) + assert target.name == 'tgt' + assert target.override == 'replace' + assert target.type == 'loki' + assert target.location == 'https://example.com:3100/loki/api/v1/push' + assert target.services == ['+all'] + assert target.labels == {'key': 'val', 'key2': 'val2'} - self.assertEqual(target.to_dict(), d) + assert target.to_dict() == d # Ensure pebble.Target has made copies of mutable objects. assert target.services is not None and target.labels is not None target.services[0] = '-all' - self.assertEqual(d['services'], ['+all']) + assert d['services'] == ['+all'] target.labels['key'] = 'val3' assert d['labels'] is not None - self.assertEqual(d['labels']['key'], 'val') + assert d['labels']['key'] == 'val' def test_equality(self): d: pebble.LogTargetDict = { @@ -1189,93 +1189,93 @@ def test_equality(self): } one = pebble.LogTarget('one', d) two = pebble.LogTarget('two', d) - self.assertEqual(one, two) - self.assertEqual(one, d) - self.assertEqual(two, d) - self.assertEqual(one, one.to_dict()) - self.assertEqual(two, two.to_dict()) + assert one == two + assert one == d + assert two == d + assert one == one.to_dict() + assert two == two.to_dict() d['override'] = 'merge' - self.assertNotEqual(one, d) - self.assertNotEqual(one, 5) + assert one != d + assert one != 5 class TestServiceInfo(unittest.TestCase): def test_service_startup(self): - self.assertEqual(list(pebble.ServiceStartup), [ + assert list(pebble.ServiceStartup) == [ pebble.ServiceStartup.ENABLED, pebble.ServiceStartup.DISABLED, - ]) - self.assertEqual(pebble.ServiceStartup.ENABLED.value, 'enabled') - self.assertEqual(pebble.ServiceStartup.DISABLED.value, 'disabled') + ] + assert pebble.ServiceStartup.ENABLED.value == 'enabled' + assert pebble.ServiceStartup.DISABLED.value == 'disabled' def test_service_status(self): - self.assertEqual(list(pebble.ServiceStatus), [ + assert list(pebble.ServiceStatus) == [ pebble.ServiceStatus.ACTIVE, pebble.ServiceStatus.INACTIVE, pebble.ServiceStatus.ERROR, - ]) - self.assertEqual(pebble.ServiceStatus.ACTIVE.value, 'active') - self.assertEqual(pebble.ServiceStatus.INACTIVE.value, 'inactive') - self.assertEqual(pebble.ServiceStatus.ERROR.value, 'error') + ] + assert pebble.ServiceStatus.ACTIVE.value == 'active' + assert pebble.ServiceStatus.INACTIVE.value == 'inactive' + assert pebble.ServiceStatus.ERROR.value == 'error' def test_service_info(self): s = pebble.ServiceInfo('svc1', pebble.ServiceStartup.ENABLED, pebble.ServiceStatus.ACTIVE) - self.assertEqual(s.name, 'svc1') - self.assertEqual(s.startup, pebble.ServiceStartup.ENABLED) - self.assertEqual(s.current, pebble.ServiceStatus.ACTIVE) + assert s.name == 'svc1' + assert s.startup == pebble.ServiceStartup.ENABLED + assert s.current == pebble.ServiceStatus.ACTIVE s = pebble.ServiceInfo( 'svc1', pebble.ServiceStartup.ENABLED, pebble.ServiceStatus.ACTIVE) - self.assertEqual(s.name, 'svc1') - self.assertEqual(s.startup, pebble.ServiceStartup.ENABLED) - self.assertEqual(s.current, pebble.ServiceStatus.ACTIVE) + assert s.name == 'svc1' + assert s.startup == pebble.ServiceStartup.ENABLED + assert s.current == pebble.ServiceStatus.ACTIVE s = pebble.ServiceInfo.from_dict({ 'name': 'svc2', 'startup': 'disabled', 'current': 'inactive', }) - self.assertEqual(s.name, 'svc2') - self.assertEqual(s.startup, pebble.ServiceStartup.DISABLED) - self.assertEqual(s.current, pebble.ServiceStatus.INACTIVE) + assert s.name == 'svc2' + assert s.startup == pebble.ServiceStartup.DISABLED + assert s.current == pebble.ServiceStatus.INACTIVE s = pebble.ServiceInfo.from_dict({ 'name': 'svc2', 'startup': 'thingy', 'current': 'bob', }) - self.assertEqual(s.name, 'svc2') - self.assertEqual(s.startup, 'thingy') - self.assertEqual(s.current, 'bob') + assert s.name == 'svc2' + assert s.startup == 'thingy' + assert s.current == 'bob' def test_is_running(self): s = pebble.ServiceInfo('s', pebble.ServiceStartup.ENABLED, pebble.ServiceStatus.ACTIVE) - self.assertTrue(s.is_running()) + assert s.is_running() for current in [pebble.ServiceStatus.INACTIVE, pebble.ServiceStatus.ERROR, 'other']: s = pebble.ServiceInfo('s', pebble.ServiceStartup.ENABLED, current) - self.assertFalse(s.is_running()) + assert not s.is_running() class TestCheckInfo(unittest.TestCase): def test_check_level(self): - self.assertEqual(list(pebble.CheckLevel), [ + assert list(pebble.CheckLevel) == [ pebble.CheckLevel.UNSET, pebble.CheckLevel.ALIVE, pebble.CheckLevel.READY, - ]) - self.assertEqual(pebble.CheckLevel.UNSET.value, '') - self.assertEqual(pebble.CheckLevel.ALIVE.value, 'alive') - self.assertEqual(pebble.CheckLevel.READY.value, 'ready') + ] + assert pebble.CheckLevel.UNSET.value == '' + assert pebble.CheckLevel.ALIVE.value == 'alive' + assert pebble.CheckLevel.READY.value == 'ready' def test_check_status(self): - self.assertEqual(list(pebble.CheckStatus), [ + assert list(pebble.CheckStatus) == [ pebble.CheckStatus.UP, pebble.CheckStatus.DOWN, - ]) - self.assertEqual(pebble.CheckStatus.UP.value, 'up') - self.assertEqual(pebble.CheckStatus.DOWN.value, 'down') + ] + assert pebble.CheckStatus.UP.value == 'up' + assert pebble.CheckStatus.DOWN.value == 'down' def test_check_info(self): check = pebble.CheckInfo( @@ -1284,11 +1284,11 @@ def test_check_info(self): status=pebble.CheckStatus.UP, threshold=3, ) - self.assertEqual(check.name, 'chk1') - self.assertEqual(check.level, pebble.CheckLevel.READY) - self.assertEqual(check.status, pebble.CheckStatus.UP) - self.assertEqual(check.failures, 0) - self.assertEqual(check.threshold, 3) + assert check.name == 'chk1' + assert check.level == pebble.CheckLevel.READY + assert check.status == pebble.CheckStatus.UP + assert check.failures == 0 + assert check.threshold == 3 check = pebble.CheckInfo( name='chk2', @@ -1297,11 +1297,11 @@ def test_check_info(self): failures=5, threshold=3, ) - self.assertEqual(check.name, 'chk2') - self.assertEqual(check.level, pebble.CheckLevel.ALIVE) - self.assertEqual(check.status, pebble.CheckStatus.DOWN) - self.assertEqual(check.failures, 5) - self.assertEqual(check.threshold, 3) + assert check.name == 'chk2' + assert check.level == pebble.CheckLevel.ALIVE + assert check.status == pebble.CheckStatus.DOWN + assert check.failures == 5 + assert check.threshold == 3 d: pebble._CheckInfoDict = { 'name': 'chk3', @@ -1309,11 +1309,11 @@ def test_check_info(self): 'threshold': 3, } check = pebble.CheckInfo.from_dict(d) - self.assertEqual(check.name, 'chk3') - self.assertEqual(check.level, pebble.CheckLevel.UNSET) - self.assertEqual(check.status, pebble.CheckStatus.UP) - self.assertEqual(check.failures, 0) - self.assertEqual(check.threshold, 3) + assert check.name == 'chk3' + assert check.level == pebble.CheckLevel.UNSET + assert check.status == pebble.CheckStatus.UP + assert check.failures == 0 + assert check.threshold == 3 check = pebble.CheckInfo.from_dict({ 'name': 'chk4', @@ -1322,11 +1322,11 @@ def test_check_info(self): 'failures': 3, 'threshold': 3, }) - self.assertEqual(check.name, 'chk4') - self.assertEqual(check.level, pebble.CheckLevel.UNSET) - self.assertEqual(check.status, pebble.CheckStatus.DOWN) - self.assertEqual(check.failures, 3) - self.assertEqual(check.threshold, 3) + assert check.name == 'chk4' + assert check.level == pebble.CheckLevel.UNSET + assert check.status == pebble.CheckStatus.DOWN + assert check.failures == 3 + assert check.threshold == 3 _bytes_generator = typing.Generator[bytes, typing.Any, typing.Any] @@ -1570,15 +1570,15 @@ def handle_body(data: bytes, done: bool = False): if not test.error: self.fail(f'unexpected error: {err}') break - self.assertEqual(test.error, str(err)) + assert test.error == str(err) else: if test.error: self.fail(f'missing expected error: {test.error!r}') msg = f'test case {i + 1} ({test.name}), chunk size {chunk_size}' - self.assertEqual(test.want_headers, headers, msg) - self.assertEqual(test.want_bodies, bodies, msg) - self.assertEqual(test.want_bodies_done, bodies_done, msg) + assert test.want_headers == headers, msg + assert test.want_bodies == bodies, msg + assert test.want_bodies_done == bodies_done, msg class TestClient(unittest.TestCase): @@ -1593,7 +1593,7 @@ def setUp(self): def test_client_init(self): pebble.Client(socket_path='foo') # test that constructor runs - with self.assertRaises(TypeError): + with pytest.raises(TypeError): pebble.Client() # type: ignore (socket_path arg required) def test_get_system_info(self): @@ -1607,10 +1607,10 @@ def test_get_system_info(self): "type": "sync" }) info = self.client.get_system_info() - self.assertEqual(info.version, '1.2.3') - self.assertEqual(self.client.requests, [ + assert info.version == '1.2.3' + assert self.client.requests == [ ('GET', '/v1/system-info', None, None), - ]) + ] def test_get_warnings(self): empty: typing.Dict[str, typing.Any] = { @@ -1621,16 +1621,16 @@ def test_get_warnings(self): } self.client.responses.append(empty) warnings = self.client.get_warnings() - self.assertEqual(warnings, []) + assert warnings == [] self.client.responses.append(empty) warnings = self.client.get_warnings(select=pebble.WarningState.ALL) - self.assertEqual(warnings, []) + assert warnings == [] - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/warnings', {'select': 'pending'}, None), ('GET', '/v1/warnings', {'select': 'all'}, None), - ]) + ] def test_ack_warnings(self): self.client.responses.append({ @@ -1640,36 +1640,36 @@ def test_ack_warnings(self): "type": "sync" }) num = self.client.ack_warnings(datetime_nzdt(2021, 1, 28, 15, 11, 0)) - self.assertEqual(num, 0) - self.assertEqual(self.client.requests, [ + assert num == 0 + assert self.client.requests == [ ('POST', '/v1/warnings', None, { 'action': 'okay', 'timestamp': '2021-01-28T15:11:00+13:00', }), - ]) + ] def assert_mock_change(self, change: pebble.Change): - self.assertEqual(change.id, '70') - self.assertEqual(change.kind, 'autostart') - self.assertEqual(change.summary, 'Autostart service "svc"') - self.assertEqual(change.status, 'Done') - self.assertEqual(len(change.tasks), 1) - self.assertEqual(change.tasks[0].id, '78') - self.assertEqual(change.tasks[0].kind, 'start') - self.assertEqual(change.tasks[0].summary, 'Start service "svc"') - self.assertEqual(change.tasks[0].status, 'Done') - self.assertEqual(change.tasks[0].log, []) - self.assertEqual(change.tasks[0].progress.done, 1) - self.assertEqual(change.tasks[0].progress.label, '') - self.assertEqual(change.tasks[0].progress.total, 1) - self.assertEqual(change.tasks[0].ready_time, - datetime_nzdt(2021, 1, 28, 14, 37, 3, 270219)) - self.assertEqual(change.tasks[0].spawn_time, - datetime_nzdt(2021, 1, 28, 14, 37, 2, 247158)) - self.assertEqual(change.ready, True) - self.assertEqual(change.err, None) - self.assertEqual(change.ready_time, datetime_nzdt(2021, 1, 28, 14, 37, 4, 291518)) - self.assertEqual(change.spawn_time, datetime_nzdt(2021, 1, 28, 14, 37, 2, 247202)) + assert change.id == '70' + assert change.kind == 'autostart' + assert change.summary == 'Autostart service "svc"' + assert change.status == 'Done' + assert len(change.tasks) == 1 + assert change.tasks[0].id == '78' + assert change.tasks[0].kind == 'start' + assert change.tasks[0].summary == 'Start service "svc"' + assert change.tasks[0].status == 'Done' + assert change.tasks[0].log == [] + assert change.tasks[0].progress.done == 1 + assert change.tasks[0].progress.label == '' + assert change.tasks[0].progress.total == 1 + assert change.tasks[0].ready_time == \ + datetime_nzdt(2021, 1, 28, 14, 37, 3, 270219) + assert change.tasks[0].spawn_time == \ + datetime_nzdt(2021, 1, 28, 14, 37, 2, 247158) + assert change.ready + assert change.err is None + assert change.ready_time == datetime_nzdt(2021, 1, 28, 14, 37, 4, 291518) + assert change.spawn_time == datetime_nzdt(2021, 1, 28, 14, 37, 2, 247202) def test_get_changes(self): empty: typing.Dict[str, typing.Any] = { @@ -1680,15 +1680,15 @@ def test_get_changes(self): } self.client.responses.append(empty) changes = self.client.get_changes() - self.assertEqual(changes, []) + assert changes == [] self.client.responses.append(empty) changes = self.client.get_changes(select=pebble.ChangeState.ALL) - self.assertEqual(changes, []) + assert changes == [] self.client.responses.append(empty) changes = self.client.get_changes(select=pebble.ChangeState.ALL, service='foo') - self.assertEqual(changes, []) + assert changes == [] self.client.responses.append({ "result": [ @@ -1699,15 +1699,15 @@ def test_get_changes(self): "type": "sync" }) changes = self.client.get_changes() - self.assertEqual(len(changes), 1) + assert len(changes) == 1 self.assert_mock_change(changes[0]) - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/changes', {'select': 'in-progress'}, None), ('GET', '/v1/changes', {'select': 'all'}, None), ('GET', '/v1/changes', {'select': 'all', 'for': 'foo'}, None), ('GET', '/v1/changes', {'select': 'in-progress'}, None), - ]) + ] def test_get_change(self): self.client.responses.append({ @@ -1718,9 +1718,9 @@ def test_get_change(self): }) change = self.client.get_change(pebble.ChangeID('70')) self.assert_mock_change(change) - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/changes/70', None, None), - ]) + ] def test_get_change_str(self): self.client.responses.append({ @@ -1731,9 +1731,9 @@ def test_get_change_str(self): }) change = self.client.get_change('70') # type: ignore self.assert_mock_change(change) - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/changes/70', None, None), - ]) + ] def test_abort_change(self): self.client.responses.append({ @@ -1744,9 +1744,9 @@ def test_abort_change(self): }) change = self.client.abort_change(pebble.ChangeID('70')) self.assert_mock_change(change) - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/changes/70', None, {'action': 'abort'}), - ]) + ] def _services_action_helper( self, @@ -1769,11 +1769,11 @@ def _services_action_helper( "type": "sync" }) change_id = api_func() - self.assertEqual(change_id, '70') - self.assertEqual(self.client.requests, [ + assert change_id == '70' + assert self.client.requests == [ ('POST', '/v1/services', None, {'action': action, 'services': services}), ('GET', '/v1/changes/70/wait', {'timeout': '4.000s'}, None), - ]) + ] def _services_action_async_helper( self, action: str, api_func: typing.Callable[..., str], services: typing.List[str]): @@ -1785,10 +1785,10 @@ def _services_action_async_helper( "type": "async" }) change_id = api_func(timeout=0) - self.assertEqual(change_id, '70') - self.assertEqual(self.client.requests, [ + assert change_id == '70' + assert self.client.requests == [ ('POST', '/v1/services', None, {'action': action, 'services': services}), - ]) + ] def test_autostart_services(self): self._services_action_helper('autostart', self.client.autostart_services, []) @@ -1807,13 +1807,13 @@ def api_func(): return self.client.start_services(['svc']) self._services_action_helper('start', api_func, ['svc']) - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.client.start_services(1) # type: ignore - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.client.start_services([1]) # type: ignore - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.client.start_services([['foo']]) # type: ignore def test_start_services_async(self): @@ -1826,13 +1826,13 @@ def api_func(): return self.client.stop_services(['svc']) self._services_action_helper('stop', api_func, ['svc']) - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.client.stop_services(1) # type: ignore - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.client.stop_services([1]) # type: ignore - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.client.stop_services([['foo']]) # type: ignore def test_stop_services_async(self): @@ -1845,13 +1845,13 @@ def api_func(): return self.client.restart_services(['svc']) self._services_action_helper('restart', api_func, ['svc']) - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.client.restart_services(1) # type: ignore - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.client.restart_services([1]) # type: ignore - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.client.restart_services([['foo']]) # type: ignore def test_restart_services_async(self): @@ -1875,17 +1875,17 @@ def test_change_error(self): "status-code": 200, "type": "sync" }) - with self.assertRaises(pebble.ChangeError) as cm: + with pytest.raises(pebble.ChangeError) as excinfo: self.client.autostart_services() - self.assertIsInstance(cm.exception, pebble.Error) - self.assertEqual(cm.exception.err, 'Some kind of service error') - self.assertIsInstance(cm.exception.change, pebble.Change) - self.assertEqual(cm.exception.change.id, '70') + assert isinstance(excinfo.value, pebble.Error) + assert excinfo.value.err == 'Some kind of service error' + assert isinstance(excinfo.value.change, pebble.Change) + assert excinfo.value.change.id == '70' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/services', None, {'action': 'autostart', 'services': []}), ('GET', '/v1/changes/70/wait', {'timeout': '4.000s'}, None), - ]) + ] def test_wait_change_success(self, timeout: typing.Optional[float] = 30.0): change = build_mock_change_dict() @@ -1897,12 +1897,12 @@ def test_wait_change_success(self, timeout: typing.Optional[float] = 30.0): }) response = self.client.wait_change(pebble.ChangeID('70'), timeout=timeout) - self.assertEqual(response.id, '70') - self.assertTrue(response.ready) + assert response.id == '70' + assert response.ready - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/changes/70/wait', {'timeout': '4.000s'}, None), - ]) + ] def test_wait_change_success_timeout_none(self): self.test_wait_change_success(timeout=None) @@ -1923,15 +1923,15 @@ def timeout_response(n: float): }) response = self.client.wait_change(pebble.ChangeID('70')) - self.assertEqual(response.id, '70') - self.assertTrue(response.ready) + assert response.id == '70' + assert response.ready - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/changes/70/wait', {'timeout': '4.000s'}, None), ('GET', '/v1/changes/70/wait', {'timeout': '4.000s'}, None), - ]) + ] - self.assertEqual(self.time.time(), 4) + assert self.time.time() == 4 def test_wait_change_success_polled(self, timeout: typing.Optional[float] = 30.0): # Trigger polled mode @@ -1948,17 +1948,17 @@ def test_wait_change_success_polled(self, timeout: typing.Optional[float] = 30.0 }) response = self.client.wait_change(pebble.ChangeID('70'), timeout=timeout, delay=1) - self.assertEqual(response.id, '70') - self.assertTrue(response.ready) + assert response.id == '70' + assert response.ready - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/changes/70/wait', {'timeout': '4.000s'}, None), ('GET', '/v1/changes/70', None, None), ('GET', '/v1/changes/70', None, None), ('GET', '/v1/changes/70', None, None), - ]) + ] - self.assertEqual(self.time.time(), 2) + assert self.time.time() == 2 def test_wait_change_success_polled_timeout_none(self): self.test_wait_change_success_polled(timeout=None) @@ -1971,17 +1971,17 @@ def timeout_response(n: float): self.client.responses.append(lambda: timeout_response(4)) self.client.responses.append(lambda: timeout_response(2)) - with self.assertRaises(pebble.TimeoutError) as cm: + with pytest.raises(pebble.TimeoutError) as excinfo: self.client.wait_change(pebble.ChangeID('70'), timeout=6) - self.assertIsInstance(cm.exception, pebble.Error) - self.assertIsInstance(cm.exception, TimeoutError) + assert isinstance(excinfo.value, pebble.Error) + assert isinstance(excinfo.value, TimeoutError) - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/changes/70/wait', {'timeout': '4.000s'}, None), ('GET', '/v1/changes/70/wait', {'timeout': '2.000s'}, None), - ]) + ] - self.assertEqual(self.time.time(), 6) + assert self.time.time() == 6 def test_wait_change_timeout_polled(self): # Trigger polled mode @@ -1997,19 +1997,19 @@ def test_wait_change_timeout_polled(self): "type": "sync" }) - with self.assertRaises(pebble.TimeoutError) as cm: + with pytest.raises(pebble.TimeoutError) as excinfo: self.client.wait_change(pebble.ChangeID('70'), timeout=3, delay=1) - self.assertIsInstance(cm.exception, pebble.Error) - self.assertIsInstance(cm.exception, TimeoutError) + assert isinstance(excinfo.value, pebble.Error) + assert isinstance(excinfo.value, TimeoutError) - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/changes/70/wait', {'timeout': '3.000s'}, None), ('GET', '/v1/changes/70', None, None), ('GET', '/v1/changes/70', None, None), ('GET', '/v1/changes/70', None, None), - ]) + ] - self.assertEqual(self.time.time(), 3) + assert self.time.time() == 3 def test_wait_change_error(self): change = build_mock_change_dict() @@ -2022,12 +2022,12 @@ def test_wait_change_error(self): }) # wait_change() itself shouldn't raise an error response = self.client.wait_change(pebble.ChangeID('70')) - self.assertEqual(response.id, '70') - self.assertEqual(response.err, 'Some kind of service error') + assert response.id == '70' + assert response.err == 'Some kind of service error' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/changes/70/wait', {'timeout': '4.000s'}, None), - ]) + ] def test_wait_change_socket_timeout(self): def timeout_response(n: float): @@ -2036,10 +2036,10 @@ def timeout_response(n: float): self.client.responses.append(lambda: timeout_response(3)) - with self.assertRaises(pebble.TimeoutError) as cm: + with pytest.raises(pebble.TimeoutError) as excinfo: self.client.wait_change(pebble.ChangeID('70'), timeout=3) - self.assertIsInstance(cm.exception, pebble.Error) - self.assertIsInstance(cm.exception, TimeoutError) + assert isinstance(excinfo.value, pebble.Error) + assert isinstance(excinfo.value, TimeoutError) def test_add_layer(self): okay_response = { @@ -2075,21 +2075,21 @@ def build_expected(label: str, combine: bool): 'layer': layer_yaml, } - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/layers', None, build_expected('a', False)), ('POST', '/v1/layers', None, build_expected('b', False)), ('POST', '/v1/layers', None, build_expected('c', False)), ('POST', '/v1/layers', None, build_expected('d', True)), - ]) + ] def test_add_layer_invalid_type(self): - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.client.add_layer('foo', 42) # type: ignore - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.client.add_layer(42, 'foo') # type: ignore # combine is a keyword-only arg (should be combine=True) - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.client.add_layer('foo', {}, True) # type: ignore def test_get_plan(self): @@ -2106,14 +2106,14 @@ def test_get_plan(self): "type": "sync" }) plan = self.client.get_plan() - self.assertEqual(plan.to_yaml(), plan_yaml) - self.assertEqual(len(plan.services), 1) - self.assertEqual(plan.services['foo'].command, 'echo bar') - self.assertEqual(plan.services['foo'].override, 'replace') + assert plan.to_yaml() == plan_yaml + assert len(plan.services) == 1 + assert plan.services['foo'].command == 'echo bar' + assert plan.services['foo'].override == 'replace' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/plan', {'format': 'yaml'}, None), - ]) + ] def test_get_services_all(self): self.client.responses.append({ @@ -2134,17 +2134,17 @@ def test_get_services_all(self): "type": "sync" }) services = self.client.get_services() - self.assertEqual(len(services), 2) - self.assertEqual(services[0].name, 'svc1') - self.assertEqual(services[0].startup, pebble.ServiceStartup.DISABLED) - self.assertEqual(services[0].current, pebble.ServiceStatus.INACTIVE) - self.assertEqual(services[1].name, 'svc2') - self.assertEqual(services[1].startup, pebble.ServiceStartup.ENABLED) - self.assertEqual(services[1].current, pebble.ServiceStatus.ACTIVE) - - self.assertEqual(self.client.requests, [ + assert len(services) == 2 + assert services[0].name == 'svc1' + assert services[0].startup == pebble.ServiceStartup.DISABLED + assert services[0].current == pebble.ServiceStatus.INACTIVE + assert services[1].name == 'svc2' + assert services[1].startup == pebble.ServiceStartup.ENABLED + assert services[1].current == pebble.ServiceStatus.ACTIVE + + assert self.client.requests == [ ('GET', '/v1/services', None, None), - ]) + ] def test_get_services_names(self): self.client.responses.append({ @@ -2165,13 +2165,13 @@ def test_get_services_names(self): "type": "sync" }) services = self.client.get_services(['svc1', 'svc2']) - self.assertEqual(len(services), 2) - self.assertEqual(services[0].name, 'svc1') - self.assertEqual(services[0].startup, pebble.ServiceStartup.DISABLED) - self.assertEqual(services[0].current, pebble.ServiceStatus.INACTIVE) - self.assertEqual(services[1].name, 'svc2') - self.assertEqual(services[1].startup, pebble.ServiceStartup.ENABLED) - self.assertEqual(services[1].current, pebble.ServiceStatus.ACTIVE) + assert len(services) == 2 + assert services[0].name == 'svc1' + assert services[0].startup == pebble.ServiceStartup.DISABLED + assert services[0].current == pebble.ServiceStatus.INACTIVE + assert services[1].name == 'svc2' + assert services[1].startup == pebble.ServiceStartup.ENABLED + assert services[1].current == pebble.ServiceStatus.ACTIVE self.client.responses.append({ "result": [ @@ -2186,15 +2186,15 @@ def test_get_services_names(self): "type": "sync" }) services = self.client.get_services(['svc2']) - self.assertEqual(len(services), 1) - self.assertEqual(services[0].name, 'svc2') - self.assertEqual(services[0].startup, pebble.ServiceStartup.ENABLED) - self.assertEqual(services[0].current, pebble.ServiceStatus.ACTIVE) + assert len(services) == 1 + assert services[0].name == 'svc2' + assert services[0].startup == pebble.ServiceStartup.ENABLED + assert services[0].current == pebble.ServiceStatus.ACTIVE - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/services', {'names': 'svc1,svc2'}, None), ('GET', '/v1/services', {'names': 'svc2'}, None), - ]) + ] def test_pull_boundary_spanning_chunk(self): self.client.responses.append(( @@ -2220,12 +2220,12 @@ def test_pull_boundary_spanning_chunk(self): self.client._chunk_size = 13 with self.client.pull('/etc/hosts') as infile: content = infile.read() - self.assertEqual(content, '127.0.0.1 localhost # 😀\nfoo\r\nbar') + assert content == '127.0.0.1 localhost # 😀\nfoo\r\nbar' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/files', {'action': 'read', 'path': '/etc/hosts'}, {'Accept': 'multipart/form-data'}, None), - ]) + ] def test_pull_text(self): self.client.responses.append(( @@ -2250,12 +2250,12 @@ def test_pull_text(self): with self.client.pull('/etc/hosts') as infile: content = infile.read() - self.assertEqual(content, '127.0.0.1 localhost # 😀\nfoo\r\nbar') + assert content == '127.0.0.1 localhost # 😀\nfoo\r\nbar' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/files', {'action': 'read', 'path': '/etc/hosts'}, {'Accept': 'multipart/form-data'}, None), - ]) + ] def test_pull_binary(self): self.client.responses.append(( @@ -2280,12 +2280,12 @@ def test_pull_binary(self): with self.client.pull('/etc/hosts', encoding=None) as infile: content = infile.read() - self.assertEqual(content, b'127.0.0.1 localhost # \xf0\x9f\x98\x80\nfoo\r\nbar') + assert content == b'127.0.0.1 localhost # \xf0\x9f\x98\x80\nfoo\r\nbar' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/files', {'action': 'read', 'path': '/etc/hosts'}, {'Accept': 'multipart/form-data'}, None), - ]) + ] def test_pull_path_error(self): self.client.responses.append(( @@ -2306,29 +2306,29 @@ def test_pull_path_error(self): """, )) - with self.assertRaises(pebble.PathError) as cm: + with pytest.raises(pebble.PathError) as excinfo: self.client.pull('/etc/hosts') - self.assertIsInstance(cm.exception, pebble.Error) - self.assertEqual(cm.exception.kind, 'not-found') - self.assertEqual(cm.exception.message, 'not found') + assert isinstance(excinfo.value, pebble.Error) + assert excinfo.value.kind == 'not-found' + assert excinfo.value.message == 'not found' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/files', {'action': 'read', 'path': '/etc/hosts'}, {'Accept': 'multipart/form-data'}, None), - ]) + ] def test_pull_protocol_errors(self): self.client.responses.append(({'Content-Type': 'c/t'}, b'')) - with self.assertRaises(pebble.ProtocolError) as cm: + with pytest.raises(pebble.ProtocolError) as excinfo: self.client.pull('/etc/hosts') - self.assertIsInstance(cm.exception, pebble.Error) - self.assertEqual(str(cm.exception), - "expected Content-Type 'multipart/form-data', got 'c/t'") + assert isinstance(excinfo.value, pebble.Error) + assert str(excinfo.value) == \ + "expected Content-Type 'multipart/form-data', got 'c/t'" self.client.responses.append(({'Content-Type': 'multipart/form-data'}, b'')) - with self.assertRaises(pebble.ProtocolError) as cm: + with pytest.raises(pebble.ProtocolError) as excinfo: self.client.pull('/etc/hosts') - self.assertEqual(str(cm.exception), "invalid boundary ''") + assert str(excinfo.value) == "invalid boundary ''" self.client.responses.append(( {'Content-Type': 'multipart/form-data; boundary=01234567890123456789012345678901'}, @@ -2349,9 +2349,9 @@ def test_pull_protocol_errors(self): --01234567890123456789012345678901--\r """, )) - with self.assertRaises(pebble.ProtocolError) as cm: + with pytest.raises(pebble.ProtocolError) as excinfo: self.client.pull('/etc/hosts') - self.assertEqual(str(cm.exception), "path not expected: '/bad'") + assert str(excinfo.value) == "path not expected: '/bad'" self.client.responses.append(( {'Content-Type': 'multipart/form-data; boundary=01234567890123456789012345678901'}, @@ -2363,9 +2363,9 @@ def test_pull_protocol_errors(self): --01234567890123456789012345678901--\r """, )) - with self.assertRaises(pebble.ProtocolError) as cm: + with pytest.raises(pebble.ProtocolError) as excinfo: self.client.pull('/etc/hosts') - self.assertEqual(str(cm.exception), 'no "response" field in multipart body') + assert str(excinfo.value) == 'no "response" field in multipart body' def test_push_str(self): self._test_push_str('content 😀\nfoo\r\nbar') @@ -2390,20 +2390,20 @@ def _test_push_str(self, source: typing.Union[str, typing.IO[str]]): self.client.push('/foo/bar', source) - self.assertEqual(len(self.client.requests), 1) + assert len(self.client.requests) == 1 request = self.client.requests[0] - self.assertEqual(request[:3], ('POST', '/v1/files', None)) + assert request[:3] == ('POST', '/v1/files', None) headers, body = request[3:] content_type = headers['Content-Type'] req, filename, content = self._parse_write_multipart(content_type, body) - self.assertEqual(filename, '/foo/bar') - self.assertEqual(content, b'content \xf0\x9f\x98\x80\nfoo\r\nbar') - self.assertEqual(req, { + assert filename == '/foo/bar' + assert content == b'content \xf0\x9f\x98\x80\nfoo\r\nbar' + assert req == { 'action': 'write', 'files': [{'path': '/foo/bar'}], - }) + } def test_push_bytes(self): self._test_push_bytes(b'content \xf0\x9f\x98\x80\nfoo\r\nbar') @@ -2428,19 +2428,19 @@ def _test_push_bytes(self, source: typing.Union[bytes, typing.IO[bytes]]): self.client.push('/foo/bar', source) - self.assertEqual(len(self.client.requests), 1) + assert len(self.client.requests) == 1 request = self.client.requests[0] - self.assertEqual(request[:3], ('POST', '/v1/files', None)) + assert request[:3] == ('POST', '/v1/files', None) headers, body = request[3:] content_type = headers['Content-Type'] req, filename, content = self._parse_write_multipart(content_type, body) - self.assertEqual(filename, '/foo/bar') - self.assertEqual(content, b'content \xf0\x9f\x98\x80\nfoo\r\nbar') - self.assertEqual(req, { + assert filename == '/foo/bar' + assert content == b'content \xf0\x9f\x98\x80\nfoo\r\nbar' + assert req == { 'action': 'write', 'files': [{'path': '/foo/bar'}], - }) + } def test_push_all_options(self): self.client.responses.append(( @@ -2460,16 +2460,16 @@ def test_push_all_options(self): self.client.push('/foo/bar', 'content', make_dirs=True, permissions=0o600, user_id=12, user='bob', group_id=34, group='staff') - self.assertEqual(len(self.client.requests), 1) + assert len(self.client.requests) == 1 request = self.client.requests[0] - self.assertEqual(request[:3], ('POST', '/v1/files', None)) + assert request[:3] == ('POST', '/v1/files', None) headers, body = request[3:] content_type = headers['Content-Type'] req, filename, content = self._parse_write_multipart(content_type, body) - self.assertEqual(filename, '/foo/bar') - self.assertEqual(content, b'content') - self.assertEqual(req, { + assert filename == '/foo/bar' + assert content == b'content' + assert req == { 'action': 'write', 'files': [{ 'path': '/foo/bar', @@ -2480,7 +2480,7 @@ def test_push_all_options(self): 'group-id': 34, 'group': 'staff', }], - }) + } def test_push_uid_gid(self): self.client.responses.append(( @@ -2499,23 +2499,23 @@ def test_push_uid_gid(self): self.client.push('/foo/bar', 'content', user_id=12, group_id=34) - self.assertEqual(len(self.client.requests), 1) + assert len(self.client.requests) == 1 request = self.client.requests[0] - self.assertEqual(request[:3], ('POST', '/v1/files', None)) + assert request[:3] == ('POST', '/v1/files', None) headers, body = request[3:] content_type = headers['Content-Type'] req, filename, content = self._parse_write_multipart(content_type, body) - self.assertEqual(filename, '/foo/bar') - self.assertEqual(content, b'content') - self.assertEqual(req, { + assert filename == '/foo/bar' + assert content == b'content' + assert req == { 'action': 'write', 'files': [{ 'path': '/foo/bar', 'user-id': 12, 'group-id': 34, }], - }) + } def test_push_path_error(self): self.client.responses.append(( @@ -2532,31 +2532,31 @@ def test_push_path_error(self): """, )) - with self.assertRaises(pebble.PathError) as cm: + with pytest.raises(pebble.PathError) as excinfo: self.client.push('/foo/bar', 'content') - self.assertEqual(cm.exception.kind, 'not-found') - self.assertEqual(cm.exception.message, 'not found') + assert excinfo.value.kind == 'not-found' + assert excinfo.value.message == 'not found' - self.assertEqual(len(self.client.requests), 1) + assert len(self.client.requests) == 1 request = self.client.requests[0] - self.assertEqual(request[:3], ('POST', '/v1/files', None)) + assert request[:3] == ('POST', '/v1/files', None) headers, body = request[3:] content_type = headers['Content-Type'] req, filename, content = self._parse_write_multipart(content_type, body) - self.assertEqual(filename, '/foo/bar') - self.assertEqual(content, b'content') - self.assertEqual(req, { + assert filename == '/foo/bar' + assert content == b'content' + assert req == { 'action': 'write', 'files': [{'path': '/foo/bar'}], - }) + } def _parse_write_multipart(self, content_type: str, body: _bytes_generator): message = email.message.Message() message['Content-Type'] = content_type - self.assertEqual(message.get_content_type(), 'multipart/form-data') + assert message.get_content_type() == 'multipart/form-data' boundary = message.get_param('boundary') assert isinstance(boundary, str) @@ -2612,31 +2612,31 @@ def test_list_files_path(self): }) infos = self.client.list_files('/etc') - self.assertEqual(len(infos), 2) - self.assertEqual(infos[0].path, '/etc/hosts') - self.assertEqual(infos[0].name, 'hosts') - self.assertEqual(infos[0].type, pebble.FileType.FILE) - self.assertEqual(infos[0].size, 123) - self.assertEqual(infos[0].permissions, 0o644) - self.assertEqual(infos[0].last_modified, datetime_nzdt(2021, 1, 28, 14, 37, 4, 291518)) - self.assertEqual(infos[0].user_id, 12) - self.assertEqual(infos[0].user, 'bob') - self.assertEqual(infos[0].group_id, 34) - self.assertEqual(infos[0].group, 'staff') - self.assertEqual(infos[1].path, '/etc/nginx') - self.assertEqual(infos[1].name, 'nginx') - self.assertEqual(infos[1].type, pebble.FileType.DIRECTORY) - self.assertEqual(infos[1].size, None) - self.assertEqual(infos[1].permissions, 0o755) - self.assertEqual(infos[1].last_modified, datetime_nzdt(2020, 1, 1, 1, 1, 1, 0)) - self.assertIs(infos[1].user_id, None) - self.assertIs(infos[1].user, None) - self.assertIs(infos[1].group_id, None) - self.assertIs(infos[1].group, None) - - self.assertEqual(self.client.requests, [ + assert len(infos) == 2 + assert infos[0].path == '/etc/hosts' + assert infos[0].name == 'hosts' + assert infos[0].type == pebble.FileType.FILE + assert infos[0].size == 123 + assert infos[0].permissions == 0o644 + assert infos[0].last_modified == datetime_nzdt(2021, 1, 28, 14, 37, 4, 291518) + assert infos[0].user_id == 12 + assert infos[0].user == 'bob' + assert infos[0].group_id == 34 + assert infos[0].group == 'staff' + assert infos[1].path == '/etc/nginx' + assert infos[1].name == 'nginx' + assert infos[1].type == pebble.FileType.DIRECTORY + assert infos[1].size is None + assert infos[1].permissions == 0o755 + assert infos[1].last_modified == datetime_nzdt(2020, 1, 1, 1, 1, 1, 0) + assert infos[1].user_id is None + assert infos[1].user is None + assert infos[1].group_id is None + assert infos[1].group is None + + assert self.client.requests == [ ('GET', '/v1/files', {'action': 'list', 'path': '/etc'}, None), - ]) + ] def test_list_files_pattern(self): self.client.responses.append({ @@ -2648,10 +2648,10 @@ def test_list_files_pattern(self): infos = self.client.list_files('/etc', pattern='*.conf') - self.assertEqual(len(infos), 0) - self.assertEqual(self.client.requests, [ + assert len(infos) == 0 + assert self.client.requests == [ ('GET', '/v1/files', {'action': 'list', 'path': '/etc', 'pattern': '*.conf'}, None), - ]) + ] def test_list_files_itself(self): self.client.responses.append({ @@ -2663,10 +2663,10 @@ def test_list_files_itself(self): infos = self.client.list_files('/etc', itself=True) - self.assertEqual(len(infos), 0) - self.assertEqual(self.client.requests, [ + assert len(infos) == 0 + assert self.client.requests == [ ('GET', '/v1/files', {'action': 'list', 'path': '/etc', 'itself': 'true'}, None), - ]) + ] def test_make_dir_basic(self): self.client.responses.append({ @@ -2679,9 +2679,9 @@ def test_make_dir_basic(self): req = {'action': 'make-dirs', 'dirs': [{ 'path': '/foo/bar', }]} - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/files', None, req), - ]) + ] def test_make_dir_all_options(self): self.client.responses.append({ @@ -2702,9 +2702,9 @@ def test_make_dir_all_options(self): 'group-id': 34, 'group': 'staff', }]} - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/files', None, req), - ]) + ] def test_make_dir_error(self): self.client.responses.append({ @@ -2719,11 +2719,11 @@ def test_make_dir_error(self): 'status-code': 200, 'type': 'sync', }) - with self.assertRaises(pebble.PathError) as cm: + with pytest.raises(pebble.PathError) as excinfo: self.client.make_dir('/foo/bar') - self.assertIsInstance(cm.exception, pebble.Error) - self.assertEqual(cm.exception.kind, 'permission-denied') - self.assertEqual(cm.exception.message, 'permission denied') + assert isinstance(excinfo.value, pebble.Error) + assert excinfo.value.kind == 'permission-denied' + assert excinfo.value.message == 'permission denied' def test_remove_path_basic(self): self.client.responses.append({ @@ -2736,9 +2736,9 @@ def test_remove_path_basic(self): req = {'action': 'remove', 'paths': [{ 'path': '/boo/far', }]} - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/files', None, req), - ]) + ] def test_remove_path_recursive(self): self.client.responses.append({ @@ -2753,9 +2753,9 @@ def test_remove_path_recursive(self): 'path': '/boo/far', 'recursive': True, }]} - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/files', None, req), - ]) + ] def test_remove_path_error(self): self.client.responses.append({ @@ -2770,11 +2770,11 @@ def test_remove_path_error(self): 'status-code': 200, 'type': 'sync', }) - with self.assertRaises(pebble.PathError) as cm: + with pytest.raises(pebble.PathError) as excinfo: self.client.remove_path('/boo/far') - self.assertIsInstance(cm.exception, pebble.Error) - self.assertEqual(cm.exception.kind, 'generic-file-error') - self.assertEqual(cm.exception.message, 'some other error') + assert isinstance(excinfo.value, pebble.Error) + assert excinfo.value.kind == 'generic-file-error' + assert excinfo.value.message == 'some other error' def test_send_signal_name(self): self.client.responses.append({ @@ -2786,9 +2786,9 @@ def test_send_signal_name(self): self.client.send_signal('SIGHUP', ['s1', 's2']) - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/signals', None, {'signal': 'SIGHUP', 'services': ['s1', 's2']}), - ]) + ] @unittest.skipUnless(hasattr(signal, 'SIGHUP'), 'signal constants not present') def test_send_signal_number(self): @@ -2801,15 +2801,15 @@ def test_send_signal_number(self): self.client.send_signal(signal.SIGHUP, ['s1', 's2']) - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/signals', None, {'signal': 'SIGHUP', 'services': ['s1', 's2']}), - ]) + ] def test_send_signal_type_error(self): - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.client.send_signal('SIGHUP', 'should-be-a-list') - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.client.send_signal('SIGHUP', [1, 2]) # type: ignore def test_get_checks_all(self): @@ -2833,21 +2833,21 @@ def test_get_checks_all(self): "type": "sync" }) checks = self.client.get_checks() - self.assertEqual(len(checks), 2) - self.assertEqual(checks[0].name, 'chk1') - self.assertEqual(checks[0].level, pebble.CheckLevel.UNSET) - self.assertEqual(checks[0].status, pebble.CheckStatus.UP) - self.assertEqual(checks[0].failures, 0) - self.assertEqual(checks[0].threshold, 2) - self.assertEqual(checks[1].name, 'chk2') - self.assertEqual(checks[1].level, pebble.CheckLevel.ALIVE) - self.assertEqual(checks[1].status, pebble.CheckStatus.DOWN) - self.assertEqual(checks[1].failures, 5) - self.assertEqual(checks[1].threshold, 3) - - self.assertEqual(self.client.requests, [ + assert len(checks) == 2 + assert checks[0].name == 'chk1' + assert checks[0].level == pebble.CheckLevel.UNSET + assert checks[0].status == pebble.CheckStatus.UP + assert checks[0].failures == 0 + assert checks[0].threshold == 2 + assert checks[1].name == 'chk2' + assert checks[1].level == pebble.CheckLevel.ALIVE + assert checks[1].status == pebble.CheckStatus.DOWN + assert checks[1].failures == 5 + assert checks[1].threshold == 3 + + assert self.client.requests == [ ('GET', '/v1/checks', {}, None), - ]) + ] def test_get_checks_filters(self): self.client.responses.append({ @@ -2864,16 +2864,16 @@ def test_get_checks_filters(self): "type": "sync" }) checks = self.client.get_checks(level=pebble.CheckLevel.READY, names=['chk2']) - self.assertEqual(len(checks), 1) - self.assertEqual(checks[0].name, 'chk2') - self.assertEqual(checks[0].level, pebble.CheckLevel.READY) - self.assertEqual(checks[0].status, pebble.CheckStatus.UP) - self.assertEqual(checks[0].failures, 0) - self.assertEqual(checks[0].threshold, 3) - - self.assertEqual(self.client.requests, [ + assert len(checks) == 1 + assert checks[0].name == 'chk2' + assert checks[0].level == pebble.CheckLevel.READY + assert checks[0].status == pebble.CheckStatus.UP + assert checks[0].failures == 0 + assert checks[0].threshold == 3 + + assert self.client.requests == [ ('GET', '/v1/checks', {'level': 'ready', 'names': ['chk2']}, None), - ]) + ] def test_checklevel_conversion(self): self.client.responses.append({ @@ -2890,16 +2890,16 @@ def test_checklevel_conversion(self): "type": "sync" }) checks = self.client.get_checks(level=pebble.CheckLevel.READY, names=['chk2']) - self.assertEqual(len(checks), 1) - self.assertEqual(checks[0].name, 'chk2') - self.assertEqual(checks[0].level, 'foobar!') # stays a raw string - self.assertEqual(checks[0].status, pebble.CheckStatus.UP) - self.assertEqual(checks[0].failures, 0) - self.assertEqual(checks[0].threshold, 3) - - self.assertEqual(self.client.requests, [ + assert len(checks) == 1 + assert checks[0].name == 'chk2' + assert checks[0].level == 'foobar!' # stays a raw string + assert checks[0].status == pebble.CheckStatus.UP + assert checks[0].failures == 0 + assert checks[0].threshold == 3 + + assert self.client.requests == [ ('GET', '/v1/checks', {'level': 'ready', 'names': ['chk2']}, None), - ]) + ] def test_notify_basic(self): self.client.responses.append({ @@ -2912,15 +2912,15 @@ def test_notify_basic(self): }) notice_id = self.client.notify(pebble.NoticeType.CUSTOM, 'example.com/a') - self.assertEqual(notice_id, '123') + assert notice_id == '123' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/notices', None, { 'action': 'add', 'key': 'example.com/a', 'type': 'custom', }), - ]) + ] def test_notify_other_args(self): self.client.responses.append({ @@ -2935,9 +2935,9 @@ def test_notify_other_args(self): notice_id = self.client.notify(pebble.NoticeType.CUSTOM, 'example.com/a', data={'k': 'v'}, repeat_after=datetime.timedelta(hours=3)) - self.assertEqual(notice_id, '321') + assert notice_id == '321' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/notices', None, { 'action': 'add', 'key': 'example.com/a', @@ -2945,7 +2945,7 @@ def test_notify_other_args(self): 'data': {'k': 'v'}, 'repeat-after': '10800.000s', }), - ]) + ] def test_get_notice(self): self.client.responses.append({ @@ -2967,22 +2967,22 @@ def test_get_notice(self): notice = self.client.get_notice('123') # No need to re-test full Notice.from_dict behaviour. - self.assertEqual(notice.id, '123') + assert notice.id == '123' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/notices/123', None, None), - ]) + ] def test_get_notice_not_found(self): self.client.responses.append(pebble.APIError({}, 404, 'Not Found', 'not found')) - with self.assertRaises(pebble.APIError) as cm: + with pytest.raises(pebble.APIError) as excinfo: self.client.get_notice('1') - self.assertEqual(cm.exception.code, 404) + assert excinfo.value.code == 404 - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/notices/1', None, None), - ]) + ] def test_get_notices_all(self): self.client.responses.append({ @@ -3010,13 +3010,13 @@ def test_get_notices_all(self): }) checks = self.client.get_notices() - self.assertEqual(len(checks), 2) - self.assertEqual(checks[0].id, '123') - self.assertEqual(checks[1].id, '124') + assert len(checks) == 2 + assert checks[0].id == '123' + assert checks[1].id == '124' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/notices', {}, None), - ]) + ] def test_get_notices_filters(self): self.client.responses.append({ @@ -3049,9 +3049,9 @@ def test_get_notices_filters(self): types=[pebble.NoticeType.CUSTOM], keys=['example.com/a', 'example.com/b'], ) - self.assertEqual(len(notices), 2) - self.assertEqual(notices[0].id, '123') - self.assertEqual(notices[1].id, '124') + assert len(notices) == 2 + assert notices[0].id == '123' + assert notices[1].id == '124' query = { 'user-id': '1000', @@ -3059,18 +3059,18 @@ def test_get_notices_filters(self): 'types': ['custom'], 'keys': ['example.com/a', 'example.com/b'], } - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('GET', '/v1/notices', query, None), - ]) + ] class TestSocketClient(unittest.TestCase): def test_socket_not_found(self): client = pebble.Client(socket_path='does_not_exist') - with self.assertRaises(pebble.ConnectionError) as cm: + with pytest.raises(pebble.ConnectionError) as excinfo: client.get_system_info() - self.assertIsInstance(cm.exception, pebble.Error) - self.assertIn("Could not connect to Pebble", str(cm.exception)) + assert isinstance(excinfo.value, pebble.Error) + assert "Could not connect to Pebble" in str(excinfo.value) def test_real_client(self): shutdown, socket_path = fake_pebble.start_server() @@ -3078,17 +3078,17 @@ def test_real_client(self): try: client = pebble.Client(socket_path=socket_path) info = client.get_system_info() - self.assertEqual(info.version, '3.14.159') + assert info.version == '3.14.159' change_id = client.start_services(['foo'], timeout=0) - self.assertEqual(change_id, '1234') + assert change_id == '1234' - with self.assertRaises(pebble.APIError) as cm: + with pytest.raises(pebble.APIError) as excinfo: client.start_services(['bar'], timeout=0) - self.assertIsInstance(cm.exception, pebble.Error) - self.assertEqual(cm.exception.code, 400) - self.assertEqual(cm.exception.status, 'Bad Request') - self.assertEqual(cm.exception.message, 'service "bar" does not exist') + assert isinstance(excinfo.value, pebble.Error) + assert excinfo.value.code == 400 + assert excinfo.value.status == 'Bad Request' + assert excinfo.value.message == 'service "bar" does not exist' finally: shutdown() @@ -3097,30 +3097,30 @@ def test_real_client(self): class TestExecError(unittest.TestCase): def test_init(self): e = pebble.ExecError(['foo'], 42, 'out', 'err') - self.assertEqual(e.command, ['foo']) - self.assertEqual(e.exit_code, 42) - self.assertEqual(e.stdout, 'out') - self.assertEqual(e.stderr, 'err') + assert e.command == ['foo'] + assert e.exit_code == 42 + assert e.stdout == 'out' + assert e.stderr == 'err' def test_str(self): e = pebble.ExecError[str](['x'], 1, None, None) - self.assertEqual(str(e), "non-zero exit code 1 executing ['x']") + assert str(e) == "non-zero exit code 1 executing ['x']" e = pebble.ExecError(['x'], 1, 'only-out', None) - self.assertEqual(str(e), "non-zero exit code 1 executing ['x'], stdout='only-out'") + assert str(e) == "non-zero exit code 1 executing ['x'], stdout='only-out'" e = pebble.ExecError(['x'], 1, None, 'only-err') - self.assertEqual(str(e), "non-zero exit code 1 executing ['x'], stderr='only-err'") + assert str(e) == "non-zero exit code 1 executing ['x'], stderr='only-err'" e = pebble.ExecError(['a', 'b'], 1, 'out', 'err') - self.assertEqual(str(e), "non-zero exit code 1 executing ['a', 'b'], " - + "stdout='out', stderr='err'") + assert str(e) == "non-zero exit code 1 executing ['a', 'b'], " \ + + "stdout='out', stderr='err'" def test_str_truncated(self): e = pebble.ExecError(['foo'], 2, 'longout', 'longerr') e.STR_MAX_OUTPUT = 5 # type: ignore - self.assertEqual(str(e), "non-zero exit code 2 executing ['foo'], " - + "stdout='longo' [truncated], stderr='longe' [truncated]") + assert str(e) == "non-zero exit code 2 executing ['foo'], " \ + + "stdout='longo' [truncated], stderr='longe' [truncated]" class MockWebsocket: @@ -3202,57 +3202,56 @@ def build_exec_data(self, } def test_arg_errors(self): - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.client.exec('foo') # type: ignore - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.client.exec([]) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.client.exec(['foo'], stdin='s', encoding=None) # type: ignore - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.client.exec(['foo'], stdin=b's') - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.client.exec(['foo'], stdin=123) # type: ignore - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.client.exec(['foo'], stdout=io.StringIO(), stderr=io.StringIO(), combine_stderr=True) def test_no_wait_call(self): self.add_responses('123', 0) - with self.assertWarns(ResourceWarning) as cm: + with pytest.warns(ResourceWarning) as record: process = self.client.exec(['true']) del process - self.assertEqual(str(cm.warning), 'ExecProcess instance garbage collected ' - + 'without call to wait() or wait_output()') + self.assertEqual(str(record[0].message), 'ExecProcess instance garbage collected ' + + 'without call to wait() or wait_output()') def test_wait_exit_zero(self): self.add_responses('123', 0) process = self.client.exec(['true']) - self.assertIsNotNone(process.stdout) - self.assertIsNotNone(process.stderr) + assert process.stdout is not None + assert process.stderr is not None process.wait() - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['true'])), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) + ] def test_wait_exit_nonzero(self): self.add_responses('456', 1) process = self.client.exec(['false']) - with self.assertRaises(pebble.ExecError) as cm: + with pytest.raises(pebble.ExecError) as excinfo: process.wait() - exc = typing.cast(pebble.ExecError[str], cm.exception) - self.assertEqual(exc.command, ['false']) - self.assertEqual(exc.exit_code, 1) - self.assertIsNone(exc.stdout) - self.assertIsNone(exc.stderr) + assert excinfo.value.command == ['false'] + assert excinfo.value.exit_code == 1 + assert excinfo.value.stdout is None # type: ignore + assert excinfo.value.stderr is None # type: ignore - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['false'])), ('GET', '/v1/changes/456/wait', {'timeout': '4.000s'}, None), - ]) + ] def test_wait_timeout(self): self.add_responses('123', 0) @@ -3260,10 +3259,10 @@ def test_wait_timeout(self): process = self.client.exec(['true'], timeout=2) process.wait() - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['true'], timeout=2)), ('GET', '/v1/changes/123/wait', {'timeout': '3.000s'}, None), - ]) + ] def test_wait_other_args(self): self.add_responses('123', 0) @@ -3279,7 +3278,7 @@ def test_wait_other_args(self): ) process.wait() - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data( command=['true'], environment={'K1': 'V1', 'K2': 'V2'}, @@ -3290,21 +3289,21 @@ def test_wait_other_args(self): group='staff', )), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) + ] def test_wait_change_error(self): self.add_responses('123', 0, change_err='change error!') process = self.client.exec(['true']) - with self.assertRaises(pebble.ChangeError) as cm: + with pytest.raises(pebble.ChangeError) as excinfo: process.wait() - self.assertEqual(cm.exception.err, 'change error!') - self.assertEqual(cm.exception.change.id, '123') + assert excinfo.value.err == 'change error!' + assert excinfo.value.change.id == '123' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['true'])), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) + ] def test_send_signal(self): _, _, control = self.add_responses('123', 0) @@ -3319,22 +3318,22 @@ def test_send_signal(self): process.wait() - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['server'])), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) + ] - self.assertEqual(len(control.sends), num_sends) - self.assertEqual(control.sends[0][0], 'TXT') - self.assertEqual(json.loads(control.sends[0][1]), - {'command': 'signal', 'signal': {'name': 'SIGHUP'}}) + assert len(control.sends) == num_sends + assert control.sends[0][0] == 'TXT' + assert json.loads(control.sends[0][1]) == \ + {'command': 'signal', 'signal': {'name': 'SIGHUP'}} if hasattr(signal, 'SIGHUP'): - self.assertEqual(control.sends[1][0], 'TXT') - self.assertEqual(json.loads(control.sends[1][1]), - {'command': 'signal', 'signal': {'name': signal.Signals(1).name}}) - self.assertEqual(control.sends[2][0], 'TXT') - self.assertEqual(json.loads(control.sends[2][1]), - {'command': 'signal', 'signal': {'name': 'SIGHUP'}}) + assert control.sends[1][0] == 'TXT' + assert json.loads(control.sends[1][1]) == \ + {'command': 'signal', 'signal': {'name': signal.Signals(1).name}} + assert control.sends[2][0] == 'TXT' + assert json.loads(control.sends[2][1]) == \ + {'command': 'signal', 'signal': {'name': 'SIGHUP'}} def test_wait_output(self): stdio, stderr, _ = self.add_responses('123', 0) @@ -3344,14 +3343,14 @@ def test_wait_output(self): process = self.client.exec(['python3', '--version']) out, err = process.wait_output() - self.assertEqual(out, 'Python 3.8.10\n') - self.assertEqual(err, '') + assert out == 'Python 3.8.10\n' + assert err == '' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['python3', '--version'])), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) - self.assertEqual(stdio.sends, []) + ] + assert stdio.sends == [] def test_wait_output_combine_stderr(self): stdio, _, _ = self.add_responses('123', 0) @@ -3360,16 +3359,16 @@ def test_wait_output_combine_stderr(self): process = self.client.exec(['sleep', 'x'], combine_stderr=True) out, err = process.wait_output() - self.assertEqual(out, 'invalid time interval\n') - self.assertIsNone(err) - self.assertIsNone(process.stderr) + assert out == 'invalid time interval\n' + assert err is None + assert process.stderr is None exec_data = self.build_exec_data(['sleep', 'x'], combine_stderr=True) - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, exec_data), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) - self.assertEqual(stdio.sends, []) + ] + assert stdio.sends == [] def test_wait_output_bytes(self): stdio, stderr, _ = self.add_responses('123', 0) @@ -3379,14 +3378,14 @@ def test_wait_output_bytes(self): process = self.client.exec(['python3', '--version'], encoding=None) out, err = process.wait_output() - self.assertEqual(out, b'Python 3.8.10\n') - self.assertEqual(err, b'') + assert out == b'Python 3.8.10\n' + assert err == b'' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['python3', '--version'])), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) - self.assertEqual(stdio.sends, []) + ] + assert stdio.sends == [] def test_wait_output_exit_nonzero(self): stdio, stderr, _ = self.add_responses('123', 0) @@ -3396,14 +3395,14 @@ def test_wait_output_exit_nonzero(self): process = self.client.exec(['ls', 'x']) out, err = process.wait_output() - self.assertEqual(out, '') - self.assertEqual(err, 'file not found: x\n') + assert out == '' + assert err == 'file not found: x\n' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['ls', 'x'])), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) - self.assertEqual(stdio.sends, []) + ] + assert stdio.sends == [] def test_wait_output_exit_nonzero_combine_stderr(self): stdio, _, _ = self.add_responses('123', 0) @@ -3412,15 +3411,15 @@ def test_wait_output_exit_nonzero_combine_stderr(self): process = self.client.exec(['ls', 'x'], combine_stderr=True) out, err = process.wait_output() - self.assertEqual(out, 'file not found: x\n') - self.assertIsNone(err) + assert out == 'file not found: x\n' + assert err is None exec_data = self.build_exec_data(['ls', 'x'], combine_stderr=True) - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, exec_data), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) - self.assertEqual(stdio.sends, []) + ] + assert stdio.sends == [] def test_wait_output_send_stdin(self): stdio, stderr, _ = self.add_responses('123', 0) @@ -3430,17 +3429,17 @@ def test_wait_output_send_stdin(self): process = self.client.exec(['awk', '{ print toupper($) }'], stdin='foo\nbar\n') out, err = process.wait_output() - self.assertEqual(out, 'FOO\nBAR\n') - self.assertEqual(err, '') + assert out == 'FOO\nBAR\n' + assert err == '' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['awk', '{ print toupper($) }'])), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) - self.assertEqual(stdio.sends, [ + ] + assert stdio.sends == [ ('BIN', b'foo\nbar\n'), ('TXT', '{"command":"end"}'), - ]) + ] def test_wait_output_send_stdin_bytes(self): stdio, stderr, _ = self.add_responses('123', 0) @@ -3451,17 +3450,17 @@ def test_wait_output_send_stdin_bytes(self): process = self.client.exec(['awk', '{ print toupper($) }'], stdin=b'foo\nbar\n', encoding=None) out, err = process.wait_output() - self.assertEqual(out, b'FOO\nBAR\n') - self.assertEqual(err, b'') + assert out == b'FOO\nBAR\n' + assert err == b'' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['awk', '{ print toupper($) }'])), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) - self.assertEqual(stdio.sends, [ + ] + assert stdio.sends == [ ('BIN', b'foo\nbar\n'), ('TXT', '{"command":"end"}'), - ]) + ] def test_wait_output_no_stdout(self): stdio, stderr, _ = self.add_responses('123', 0) @@ -3469,7 +3468,7 @@ def test_wait_output_no_stdout(self): stderr.receives.append('{"command":"end"}') stdout_buffer = io.BytesIO() process = self.client.exec(["echo", "FOOBAR"], stdout=stdout_buffer, encoding=None) - with self.assertRaises(TypeError): + with pytest.raises(TypeError): process.wait_output() def test_wait_output_bad_command(self): @@ -3483,19 +3482,19 @@ def test_wait_output_bad_command(self): with self.assertLogs('ops.pebble', level='WARNING') as cm: process = self.client.exec(['python3', '--version']) out, err = process.wait_output() - self.assertEqual(cm.output, [ + assert cm.output == [ "WARNING:ops.pebble:Cannot decode I/O command (invalid JSON)", "WARNING:ops.pebble:Invalid I/O command 'foo'", - ]) + ] - self.assertEqual(out, 'Python 3.8.10\n') - self.assertEqual(err, '') + assert out == 'Python 3.8.10\n' + assert err == '' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['python3', '--version'])), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) - self.assertEqual(stdio.sends, []) + ] + assert stdio.sends == [] def test_wait_passed_output(self): io_ws, stderr, _ = self.add_responses('123', 0) @@ -3508,14 +3507,14 @@ def test_wait_passed_output(self): err = io.StringIO() process = self.client.exec(['echo', 'foo'], stdout=out, stderr=err) process.wait() - self.assertEqual(out.getvalue(), 'foo\n') - self.assertEqual(err.getvalue(), 'some error\n') + assert out.getvalue() == 'foo\n' + assert err.getvalue() == 'some error\n' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['echo', 'foo'])), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) - self.assertEqual(io_ws.sends, []) + ] + assert io_ws.sends == [] def test_wait_passed_output_combine_stderr(self): io_ws, _, _ = self.add_responses('123', 0) @@ -3526,15 +3525,15 @@ def test_wait_passed_output_combine_stderr(self): out = io.StringIO() process = self.client.exec(['echo', 'foo'], stdout=out, combine_stderr=True) process.wait() - self.assertEqual(out.getvalue(), 'foo\nsome error\n') - self.assertIsNone(process.stderr) + assert out.getvalue() == 'foo\nsome error\n' + assert process.stderr is None exec_data = self.build_exec_data(['echo', 'foo'], combine_stderr=True) - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, exec_data), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) - self.assertEqual(io_ws.sends, []) + ] + assert io_ws.sends == [] def test_wait_passed_output_bytes(self): io_ws, stderr, _ = self.add_responses('123', 0) @@ -3547,14 +3546,14 @@ def test_wait_passed_output_bytes(self): err = io.BytesIO() process = self.client.exec(['echo', 'foo'], stdout=out, stderr=err, encoding=None) process.wait() - self.assertEqual(out.getvalue(), b'foo\n') - self.assertEqual(err.getvalue(), b'some error\n') + assert out.getvalue() == b'foo\n' + assert err.getvalue() == b'some error\n' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['echo', 'foo'])), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) - self.assertEqual(io_ws.sends, []) + ] + assert io_ws.sends == [] def test_wait_passed_output_bad_command(self): io_ws, stderr, _ = self.add_responses('123', 0) @@ -3571,19 +3570,19 @@ def test_wait_passed_output_bad_command(self): with self.assertLogs('ops.pebble', level='WARNING') as cm: process = self.client.exec(['echo', 'foo'], stdout=out, stderr=err) process.wait() - self.assertEqual(cm.output, [ + assert cm.output == [ "WARNING:ops.pebble:Cannot decode I/O command (invalid JSON)", "WARNING:ops.pebble:Invalid I/O command 'foo'", - ]) + ] - self.assertEqual(out.getvalue(), 'foo\n') - self.assertEqual(err.getvalue(), 'some error\n') + assert out.getvalue() == 'foo\n' + assert err.getvalue() == 'some error\n' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['echo', 'foo'])), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) - self.assertEqual(io_ws.sends, []) + ] + assert io_ws.sends == [] def test_wait_file_io(self): fin = tempfile.TemporaryFile(mode='w+', encoding='utf-8') @@ -3603,18 +3602,18 @@ def test_wait_file_io(self): process.wait() out.seek(0) - self.assertEqual(out.read(), 'foo\n') + assert out.read() == 'foo\n' err.seek(0) - self.assertEqual(err.read(), 'some error\n') + assert err.read() == 'some error\n' - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['echo', 'foo'])), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) - self.assertEqual(io_ws.sends, [ + ] + assert io_ws.sends == [ ('BIN', b'foo\n'), ('TXT', '{"command":"end"}'), - ]) + ] finally: fin.close() out.close() @@ -3629,21 +3628,21 @@ def test_wait_returned_io(self): process = self.client.exec(['awk', '{ print toupper($) }']) assert process.stdout is not None and process.stdin is not None process.stdin.write('Foo Bar\n') - self.assertEqual(process.stdout.read(4), 'FOO ') + assert process.stdout.read(4) == 'FOO ' process.stdin.write('bazz\n') - self.assertEqual(process.stdout.read(), 'BAR\nBAZZ\n') + assert process.stdout.read() == 'BAR\nBAZZ\n' process.stdin.close() - self.assertEqual(process.stdout.read(), '') + assert process.stdout.read() == '' process.wait() - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['awk', '{ print toupper($) }'])), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) - self.assertEqual(stdio.sends, [ + ] + assert stdio.sends == [ ('BIN', b'Foo Bar\nbazz\n'), # TextIOWrapper groups the writes together ('TXT', '{"command":"end"}'), - ]) + ] def test_wait_returned_io_bytes(self): stdio = self.add_responses('123', 0)[0] @@ -3654,23 +3653,23 @@ def test_wait_returned_io_bytes(self): process = self.client.exec(['awk', '{ print toupper($) }'], encoding=None) assert process.stdout is not None and process.stdin is not None process.stdin.write(b'Foo Bar\n') - self.assertEqual(process.stdout.read(4), b'FOO ') - self.assertEqual(process.stdout.read(), b'BAR\n') + assert process.stdout.read(4) == b'FOO ' + assert process.stdout.read() == b'BAR\n' process.stdin.write(b'bazz\n') - self.assertEqual(process.stdout.read(), b'BAZZ\n') + assert process.stdout.read() == b'BAZZ\n' process.stdin.close() - self.assertEqual(process.stdout.read(), b'') + assert process.stdout.read() == b'' process.wait() - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['awk', '{ print toupper($) }'])), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) - self.assertEqual(stdio.sends, [ + ] + assert stdio.sends == [ ('BIN', b'Foo Bar\n'), ('BIN', b'bazz\n'), ('TXT', '{"command":"end"}'), - ]) + ] def test_connect_websocket_error(self): class Client(MockClient): @@ -3679,15 +3678,15 @@ def _connect_websocket(self, change_id: str, websocket_id: str): self.client = Client() self.add_responses('123', 0, change_err='change error!') - with self.assertRaises(pebble.ChangeError) as cm: + with pytest.raises(pebble.ChangeError) as excinfo: self.client.exec(['foo']) - self.assertEqual(str(cm.exception), 'change error!') + assert str(excinfo.value) == 'change error!' self.client = Client() self.add_responses('123', 0) - with self.assertRaises(pebble.ConnectionError) as cm: + with pytest.raises(pebble.ConnectionError) as excinfo: self.client.exec(['foo']) - self.assertIn(str(cm.exception), 'unexpected error connecting to websockets: conn!') + assert str(excinfo.value) in 'unexpected error connecting to websockets: conn!' def test_websocket_send_raises(self): stdio, stderr, _ = self.add_responses('123', 0) @@ -3704,15 +3703,15 @@ def send_binary(b: bytes): process = self.client.exec(['cat'], stdin='foo\nbar\n') out, err = process.wait_output() - self.assertEqual(out, '') - self.assertEqual(err, '') - self.assertTrue(raised) + assert out == '' + assert err == '' + assert raised - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['cat'])), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) - self.assertEqual(stdio.sends, []) + ] + assert stdio.sends == [] # You'd normally use pytest.mark.filterwarnings as a decorator, but # PytestUnhandledThreadExceptionWarning isn't present on older Python versions. @@ -3734,18 +3733,18 @@ def recv(): process = self.client.exec(['cat'], stdin='foo\nbar\n') out, err = process.wait_output() - self.assertEqual(out, '') - self.assertEqual(err, '') - self.assertTrue(raised) + assert out == '' + assert err == '' + assert raised - self.assertEqual(self.client.requests, [ + assert self.client.requests == [ ('POST', '/v1/exec', None, self.build_exec_data(['cat'])), ('GET', '/v1/changes/123/wait', {'timeout': '4.000s'}, None), - ]) - self.assertEqual(stdio.sends, [ + ] + assert stdio.sends == [ ('BIN', b'foo\nbar\n'), ('TXT', '{"command":"end"}'), - ]) + ] if hasattr(pytest, 'PytestUnhandledThreadExceptionWarning'): test_websocket_recv_raises = pytest.mark.filterwarnings( diff --git a/test/test_private.py b/test/test_private.py index 45ef60307..54ec5fefe 100644 --- a/test/test_private.py +++ b/test/test_private.py @@ -16,6 +16,7 @@ import io import unittest +import pytest import yaml as base_yaml from ops._private import timeconv, yaml @@ -28,24 +29,24 @@ class YAMLTest: class TestYAML(unittest.TestCase): def test_safe_load(self): d = yaml.safe_load('foo: bar\nbaz: 123\n') - self.assertEqual(len(d), 2) - self.assertEqual(d['foo'], 'bar') - self.assertEqual(d['baz'], 123) + assert len(d) == 2 + assert d['foo'] == 'bar' + assert d['baz'] == 123 # Should error -- it's not safe to load an instance of a user-defined class - with self.assertRaises(base_yaml.YAMLError): + with pytest.raises(base_yaml.YAMLError): yaml.safe_load('!!python/object:test.test_helpers.YAMLTest {}') def test_safe_dump(self): s = yaml.safe_dump({'foo': 'bar', 'baz': 123}) - self.assertEqual(s, 'baz: 123\nfoo: bar\n') + assert s == 'baz: 123\nfoo: bar\n' f = io.StringIO() yaml.safe_dump({'foo': 'bar', 'baz': 123}, stream=f) - self.assertEqual(f.getvalue(), 'baz: 123\nfoo: bar\n') + assert f.getvalue() == 'baz: 123\nfoo: bar\n' # Should error -- it's not safe to dump an instance of a user-defined class - with self.assertRaises(base_yaml.YAMLError): + with pytest.raises(base_yaml.YAMLError): yaml.safe_dump(YAMLTest()) @@ -54,51 +55,51 @@ def test_parse_rfc3339(self): nzdt = datetime.timezone(datetime.timedelta(hours=13)) utc = datetime.timezone.utc - self.assertEqual(timeconv.parse_rfc3339('2020-12-25T13:45:50+13:00'), - datetime.datetime(2020, 12, 25, 13, 45, 50, 0, tzinfo=nzdt)) + assert timeconv.parse_rfc3339('2020-12-25T13:45:50+13:00') == \ + datetime.datetime(2020, 12, 25, 13, 45, 50, 0, tzinfo=nzdt) - self.assertEqual(timeconv.parse_rfc3339('2020-12-25T13:45:50.123456789+13:00'), - datetime.datetime(2020, 12, 25, 13, 45, 50, 123457, tzinfo=nzdt)) + assert timeconv.parse_rfc3339('2020-12-25T13:45:50.123456789+13:00') == \ + datetime.datetime(2020, 12, 25, 13, 45, 50, 123457, tzinfo=nzdt) - self.assertEqual(timeconv.parse_rfc3339('2021-02-10T04:36:22Z'), - datetime.datetime(2021, 2, 10, 4, 36, 22, 0, tzinfo=utc)) + assert timeconv.parse_rfc3339('2021-02-10T04:36:22Z') == \ + datetime.datetime(2021, 2, 10, 4, 36, 22, 0, tzinfo=utc) - self.assertEqual(timeconv.parse_rfc3339('2021-02-10t04:36:22z'), - datetime.datetime(2021, 2, 10, 4, 36, 22, 0, tzinfo=utc)) + assert timeconv.parse_rfc3339('2021-02-10t04:36:22z') == \ + datetime.datetime(2021, 2, 10, 4, 36, 22, 0, tzinfo=utc) - self.assertEqual(timeconv.parse_rfc3339('2021-02-10T04:36:22.118970777Z'), - datetime.datetime(2021, 2, 10, 4, 36, 22, 118971, tzinfo=utc)) + assert timeconv.parse_rfc3339('2021-02-10T04:36:22.118970777Z') == \ + datetime.datetime(2021, 2, 10, 4, 36, 22, 118971, tzinfo=utc) - self.assertEqual(timeconv.parse_rfc3339('2020-12-25T13:45:50.123456789+00:00'), - datetime.datetime(2020, 12, 25, 13, 45, 50, 123457, tzinfo=utc)) + assert timeconv.parse_rfc3339('2020-12-25T13:45:50.123456789+00:00') == \ + datetime.datetime(2020, 12, 25, 13, 45, 50, 123457, tzinfo=utc) - self.assertEqual(timeconv.parse_rfc3339('2006-08-28T13:20:00.9999999Z'), - datetime.datetime(2006, 8, 28, 13, 20, 0, 999999, tzinfo=utc)) + assert timeconv.parse_rfc3339('2006-08-28T13:20:00.9999999Z') == \ + datetime.datetime(2006, 8, 28, 13, 20, 0, 999999, tzinfo=utc) - self.assertEqual(timeconv.parse_rfc3339('2006-12-31T23:59:59.9999999Z'), - datetime.datetime(2006, 12, 31, 23, 59, 59, 999999, tzinfo=utc)) + assert timeconv.parse_rfc3339('2006-12-31T23:59:59.9999999Z') == \ + datetime.datetime(2006, 12, 31, 23, 59, 59, 999999, tzinfo=utc) tzinfo = datetime.timezone(datetime.timedelta(hours=-11, minutes=-30)) - self.assertEqual(timeconv.parse_rfc3339('2020-12-25T13:45:50.123456789-11:30'), - datetime.datetime(2020, 12, 25, 13, 45, 50, 123457, tzinfo=tzinfo)) + assert timeconv.parse_rfc3339('2020-12-25T13:45:50.123456789-11:30') == \ + datetime.datetime(2020, 12, 25, 13, 45, 50, 123457, tzinfo=tzinfo) tzinfo = datetime.timezone(datetime.timedelta(hours=4)) - self.assertEqual(timeconv.parse_rfc3339('2000-01-02T03:04:05.006000+04:00'), - datetime.datetime(2000, 1, 2, 3, 4, 5, 6000, tzinfo=tzinfo)) + assert timeconv.parse_rfc3339('2000-01-02T03:04:05.006000+04:00') == \ + datetime.datetime(2000, 1, 2, 3, 4, 5, 6000, tzinfo=tzinfo) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): timeconv.parse_rfc3339('') - with self.assertRaises(ValueError): + with pytest.raises(ValueError): timeconv.parse_rfc3339('foobar') - with self.assertRaises(ValueError): + with pytest.raises(ValueError): timeconv.parse_rfc3339('2021-99-99T04:36:22Z') - with self.assertRaises(ValueError): + with pytest.raises(ValueError): timeconv.parse_rfc3339('2021-02-10T04:36:22.118970777x') - with self.assertRaises(ValueError): + with pytest.raises(ValueError): timeconv.parse_rfc3339('2021-02-10T04:36:22.118970777-99:99') def test_parse_duration(self): @@ -154,8 +155,8 @@ def test_parse_duration(self): for input, expected in cases: output = timeconv.parse_duration(input) - self.assertEqual(output, expected, - f'parse_duration({input!r}): expected {expected!r}, got {output!r}') + assert output == expected, \ + f'parse_duration({input!r}): expected {expected!r}, got {output!r}' def test_parse_duration_errors(self): cases = [ @@ -180,5 +181,5 @@ def test_parse_duration_errors(self): '3.4.5s', ] for input in cases: - with self.assertRaises(ValueError): + with pytest.raises(ValueError): timeconv.parse_duration(input) diff --git a/test/test_real_pebble.py b/test/test_real_pebble.py index d5074788e..dde3dd30b 100644 --- a/test/test_real_pebble.py +++ b/test/test_real_pebble.py @@ -39,6 +39,8 @@ import urllib.request import uuid +import pytest + from ops import pebble from .test_testing import PebbleNoticesMixin, PebbleStorageAPIsTestMixin @@ -90,25 +92,25 @@ def test_checks_and_health(self): # Checks should all be "up" initially checks = self.client.get_checks() - self.assertEqual(len(checks), 3) - self.assertEqual(checks[0].name, 'bad') - self.assertEqual(checks[0].level, pebble.CheckLevel.READY) - self.assertEqual(checks[0].status, pebble.CheckStatus.UP) - self.assertEqual(checks[1].name, 'good') - self.assertEqual(checks[1].level, pebble.CheckLevel.ALIVE) - self.assertEqual(checks[1].status, pebble.CheckStatus.UP) - self.assertEqual(checks[2].name, 'other') - self.assertEqual(checks[2].level, pebble.CheckLevel.UNSET) - self.assertEqual(checks[2].status, pebble.CheckStatus.UP) + assert len(checks) == 3 + assert checks[0].name == 'bad' + assert checks[0].level == pebble.CheckLevel.READY + assert checks[0].status == pebble.CheckStatus.UP + assert checks[1].name == 'good' + assert checks[1].level == pebble.CheckLevel.ALIVE + assert checks[1].status == pebble.CheckStatus.UP + assert checks[2].name == 'other' + assert checks[2].level == pebble.CheckLevel.UNSET + assert checks[2].status == pebble.CheckStatus.UP # And /v1/health should return "healthy" health = self._get_health() - self.assertEqual(health, { + assert health == { 'result': {'healthy': True}, 'status': 'OK', 'status-code': 200, 'type': 'sync', - }) + } # After two retries the "bad" check should go down for _ in range(5): @@ -119,31 +121,31 @@ def test_checks_and_health(self): time.sleep(0.06) else: assert False, 'timed out waiting for "bad" check to go down' - self.assertEqual(bad_check.failures, 2) - self.assertEqual(bad_check.threshold, 2) + assert bad_check.failures == 2 + assert bad_check.threshold == 2 good_check = [c for c in checks if c.name == 'good'][0] - self.assertEqual(good_check.status, pebble.CheckStatus.UP) + assert good_check.status == pebble.CheckStatus.UP # And /v1/health should return "unhealthy" (with status HTTP 502) - with self.assertRaises(urllib.error.HTTPError) as cm: + with pytest.raises(urllib.error.HTTPError) as excinfo: self._get_health() - self.assertEqual(cm.exception.code, 502) - health = json.loads(cm.exception.read()) - self.assertEqual(health, { + assert excinfo.value.code == 502 + health = json.loads(excinfo.value.read()) + assert health == { 'result': {'healthy': False}, 'status': 'Bad Gateway', 'status-code': 502, 'type': 'sync', - }) + } # Then test filtering by check level and by name checks = self.client.get_checks(level=pebble.CheckLevel.ALIVE) - self.assertEqual(len(checks), 1) - self.assertEqual(checks[0].name, 'good') + assert len(checks) == 1 + assert checks[0].name == 'good' checks = self.client.get_checks(names=['good', 'bad']) - self.assertEqual(len(checks), 2) - self.assertEqual(checks[0].name, 'bad') - self.assertEqual(checks[1].name, 'good') + assert len(checks) == 2 + assert checks[0].name == 'bad' + assert checks[1].name == 'good' def _get_health(self): f = urllib.request.urlopen('http://localhost:4000/v1/health') @@ -153,41 +155,41 @@ def test_exec_wait(self): process = self.client.exec(['true']) process.wait() - with self.assertRaises(pebble.ExecError) as cm: + with pytest.raises(pebble.ExecError) as excinfo: process = self.client.exec(['/bin/sh', '-c', 'exit 42']) process.wait() - self.assertEqual(cm.exception.exit_code, 42) + assert excinfo.value.exit_code == 42 def test_exec_wait_output(self): process = self.client.exec(['/bin/sh', '-c', 'echo OUT; echo ERR >&2']) out, err = process.wait_output() - self.assertEqual(out, 'OUT\n') - self.assertEqual(err, 'ERR\n') + assert out == 'OUT\n' + assert err == 'ERR\n' process = self.client.exec(['/bin/sh', '-c', 'echo OUT; echo ERR >&2'], encoding=None) out, err = process.wait_output() - self.assertEqual(out, b'OUT\n') - self.assertEqual(err, b'ERR\n') + assert out == b'OUT\n' + assert err == b'ERR\n' - with self.assertRaises(pebble.ExecError) as cm: + with pytest.raises(pebble.ExecError) as excinfo: process = self.client.exec(['/bin/sh', '-c', 'echo OUT; echo ERR >&2; exit 42']) process.wait_output() - exc = typing.cast(pebble.ExecError[str], cm.exception) - self.assertEqual(exc.exit_code, 42) - self.assertEqual(exc.stdout, 'OUT\n') - self.assertEqual(exc.stderr, 'ERR\n') + exc = typing.cast(pebble.ExecError[str], excinfo.value) + assert exc.exit_code == 42 + assert exc.stdout == 'OUT\n' + assert exc.stderr == 'ERR\n' def test_exec_send_stdin(self): process = self.client.exec(['awk', '{ print toupper($0) }'], stdin='foo\nBar\n') out, err = process.wait_output() - self.assertEqual(out, 'FOO\nBAR\n') - self.assertEqual(err, '') + assert out == 'FOO\nBAR\n' + assert err == '' process = self.client.exec(['awk', '{ print toupper($0) }'], stdin=b'foo\nBar\n', encoding=None) out, err = process.wait_output() - self.assertEqual(out, b'FOO\nBAR\n') - self.assertEqual(err, b'') + assert out == b'FOO\nBAR\n' + assert err == b'' def test_push_pull(self): fname = os.path.join(tempfile.gettempdir(), f'pebbletest-{uuid.uuid4()}') @@ -195,28 +197,28 @@ def test_push_pull(self): self.client.push(fname, content) with self.client.pull(fname) as f: data = f.read() - self.assertEqual(data, content) + assert data == content os.remove(fname) def test_exec_timeout(self): process = self.client.exec(['sleep', '0.2'], timeout=0.1) - with self.assertRaises(pebble.ChangeError) as cm: + with pytest.raises(pebble.ChangeError) as excinfo: process.wait() - self.assertIn('timed out', cm.exception.err) + assert 'timed out' in excinfo.value.err def test_exec_working_dir(self): with tempfile.TemporaryDirectory() as temp_dir: process = self.client.exec(['pwd'], working_dir=temp_dir) out, err = process.wait_output() - self.assertEqual(out, f"{temp_dir}\n") - self.assertEqual(err, '') + assert out == f"{temp_dir}\n" + assert err == '' def test_exec_environment(self): process = self.client.exec(['/bin/sh', '-c', 'echo $ONE.$TWO.$THREE'], environment={'ONE': '1', 'TWO': '2'}) out, err = process.wait_output() - self.assertEqual(out, '1.2.\n') - self.assertEqual(err, '') + assert out == '1.2.\n' + assert err == '' def test_exec_streaming(self): process = self.client.exec(['cat']) @@ -238,7 +240,7 @@ def stdin_thread(): process.wait() - self.assertEqual(reads, ['one\n', '2\n', 'THREE\n']) + assert reads == ['one\n', '2\n', 'THREE\n'] def test_exec_streaming_bytes(self): process = self.client.exec(['cat'], encoding=None) @@ -260,7 +262,7 @@ def stdin_thread(): process.wait() - self.assertEqual(reads, [b'one\n', b'2\n', b'THREE\n']) + assert reads == [b'one\n', b'2\n', b'THREE\n'] def test_log_forwarding(self): self.client.add_layer("log-forwarder", { @@ -281,12 +283,12 @@ def test_log_forwarding(self): }, }, combine=True) plan = self.client.get_plan() - self.assertEqual(len(plan.log_targets), 1) - self.assertEqual(plan.log_targets["pretend-loki"].type, "loki") - self.assertEqual(plan.log_targets["pretend-loki"].override, "replace") - self.assertEqual(plan.log_targets["pretend-loki"].location, "https://example.com") - self.assertEqual(plan.log_targets["pretend-loki"].services, ["all"]) - self.assertEqual(plan.log_targets["pretend-loki"].labels, {"foo": "bar"}) + assert len(plan.log_targets) == 1 + assert plan.log_targets["pretend-loki"].type == "loki" + assert plan.log_targets["pretend-loki"].override == "replace" + assert plan.log_targets["pretend-loki"].location == "https://example.com" + assert plan.log_targets["pretend-loki"].services == ["all"] + assert plan.log_targets["pretend-loki"].labels == {"foo": "bar"} @unittest.skipUnless(os.getenv('RUN_REAL_PEBBLE_TESTS'), 'RUN_REAL_PEBBLE_TESTS not set') diff --git a/test/test_storage.py b/test/test_storage.py index 517526676..9d954176c 100644 --- a/test/test_storage.py +++ b/test/test_storage.py @@ -26,6 +26,7 @@ from test.test_helpers import BaseTestCase, fake_script, fake_script_calls from textwrap import dedent +import pytest import yaml import ops @@ -78,7 +79,7 @@ def restore(self, snapshot: typing.Dict[str, typing.Any]): del s gc.collect() res = f.load_snapshot(handle) - self.assertEqual(data, res.content) # type: ignore + assert data == res.content # type: ignore def test_emit_event(self): f = self.create_framework() @@ -118,63 +119,62 @@ def restore(self, snapshot: typing.Dict[str, typing.Any]) -> None: s = Sample(f, 'key') f.register_type(Sample, None, Sample.handle_kind) s.on.event.emit('foo') - self.assertEqual('foo', s.observed_content) + assert s.observed_content == 'foo' s.on.event.emit(1) - self.assertEqual(1, s.observed_content) + assert s.observed_content == 1 s.on.event.emit(None) - self.assertEqual(None, s.observed_content) + assert s.observed_content is None def test_save_and_overwrite_snapshot(self): store = self.create_storage() store.save_snapshot('foo', {1: 2}) - self.assertEqual({1: 2}, store.load_snapshot('foo')) + assert store.load_snapshot('foo') == {1: 2} store.save_snapshot('foo', {'three': 4}) - self.assertEqual({'three': 4}, store.load_snapshot('foo')) + assert store.load_snapshot('foo') == {'three': 4} def test_drop_snapshot(self): store = self.create_storage() store.save_snapshot('foo', {1: 2}) - self.assertEqual({1: 2}, store.load_snapshot('foo')) + assert store.load_snapshot('foo') == {1: 2} store.drop_snapshot('foo') - with self.assertRaises(ops.storage.NoSnapshotError): + with pytest.raises(ops.storage.NoSnapshotError): store.load_snapshot('foo') def test_save_snapshot_empty_string(self): store = self.create_storage() - with self.assertRaises(ops.storage.NoSnapshotError): + with pytest.raises(ops.storage.NoSnapshotError): store.load_snapshot('foo') store.save_snapshot('foo', '') - self.assertEqual('', store.load_snapshot('foo')) + assert store.load_snapshot('foo') == '' store.drop_snapshot('foo') - with self.assertRaises(ops.storage.NoSnapshotError): + with pytest.raises(ops.storage.NoSnapshotError): store.load_snapshot('foo') def test_save_snapshot_none(self): store = self.create_storage() - with self.assertRaises(ops.storage.NoSnapshotError): + with pytest.raises(ops.storage.NoSnapshotError): store.load_snapshot('bar') store.save_snapshot('bar', None) - self.assertEqual(None, store.load_snapshot('bar')) + assert store.load_snapshot('bar') is None store.drop_snapshot('bar') - with self.assertRaises(ops.storage.NoSnapshotError): + with pytest.raises(ops.storage.NoSnapshotError): store.load_snapshot('bar') def test_save_snapshot_zero(self): store = self.create_storage() - with self.assertRaises(ops.storage.NoSnapshotError): + with pytest.raises(ops.storage.NoSnapshotError): store.load_snapshot('zero') store.save_snapshot('zero', 0) - self.assertEqual(0, store.load_snapshot('zero')) + assert store.load_snapshot('zero') == 0 store.drop_snapshot('zero') - with self.assertRaises(ops.storage.NoSnapshotError): + with pytest.raises(ops.storage.NoSnapshotError): store.load_snapshot('zero') def test_save_notice(self): store = self.create_storage() store.save_notice('event', 'observer', 'method') - self.assertEqual( - list(store.notices('event')), - [('event', 'observer', 'method')]) + assert list(store.notices('event')) == \ + [('event', 'observer', 'method')] def test_all_notices(self): notices = [('e1', 'o1', 'm1'), ('e1', 'o2', 'm2'), ('e2', 'o3', 'm3')] @@ -183,36 +183,35 @@ def test_all_notices(self): store.save_notice(*notice) # passing in the arg, you get the ones that match - self.assertEqual(list(store.notices('e1')), notices[:2]) - self.assertEqual(list(store.notices('e2')), notices[2:]) + assert list(store.notices('e1')) == notices[:2] + assert list(store.notices('e2')) == notices[2:] # the match is exact - self.assertEqual(list(store.notices('e%')), []) - self.assertEqual(list(store.notices('e*')), []) - self.assertEqual(list(store.notices('e.')), []) - self.assertEqual(list(store.notices('e')), []) + assert list(store.notices('e%')) == [] + assert list(store.notices('e*')) == [] + assert list(store.notices('e.')) == [] + assert list(store.notices('e')) == [] # no arg, or non-arg, means all - self.assertEqual(list(store.notices()), notices) - self.assertEqual(list(store.notices(None)), notices) - self.assertEqual(list(store.notices('')), notices) + assert list(store.notices()) == notices + assert list(store.notices(None)) == notices + assert list(store.notices('')) == notices def test_load_notices(self): store = self.create_storage() - self.assertEqual(list(store.notices('path')), []) + assert list(store.notices('path')) == [] def test_save_one_load_another_notice(self): store = self.create_storage() store.save_notice('event', 'observer', 'method') - self.assertEqual(list(store.notices('other')), []) + assert list(store.notices('other')) == [] def test_save_load_drop_load_notices(self): store = self.create_storage() store.save_notice('event', 'observer', 'method') store.save_notice('event', 'observer', 'method2') - self.assertEqual( - list(store.notices('event')), + assert list(store.notices('event')) == \ [('event', 'observer', 'method'), ('event', 'observer', 'method2'), - ]) + ] class TestSQLiteStorage(StoragePermutations, BaseTestCase): @@ -224,7 +223,7 @@ def test_permissions_new(self): with tempfile.TemporaryDirectory() as temp_dir: filename = os.path.join(temp_dir, ".unit-state.db") storage = ops.storage.SQLiteStorage(filename) - self.assertEqual(stat.S_IMODE(os.stat(filename).st_mode), stat.S_IRUSR | stat.S_IWUSR) + assert stat.S_IMODE(os.stat(filename).st_mode) == stat.S_IRUSR | stat.S_IWUSR storage.close() def test_permissions_existing(self): @@ -234,7 +233,7 @@ def test_permissions_existing(self): # Set the file to access that will need fixing for user, group, and other. os.chmod(filename, 0o744) storage = ops.storage.SQLiteStorage(filename) - self.assertEqual(stat.S_IMODE(os.stat(filename).st_mode), stat.S_IRUSR | stat.S_IWUSR) + assert stat.S_IMODE(os.stat(filename).st_mode) == stat.S_IRUSR | stat.S_IWUSR storage.close() @unittest.mock.patch("os.path.exists") @@ -245,7 +244,7 @@ def test_permissions_race(self, exists: unittest.mock.MagicMock): # Create an existing file, but the mock will simulate a race condition saying that it # does not exist. open(filename, "w").close() - self.assertRaises(RuntimeError, ops.storage.SQLiteStorage, filename) + pytest.raises(RuntimeError, ops.storage.SQLiteStorage, filename) @unittest.mock.patch("os.chmod") def test_permissions_failure(self, chmod: unittest.mock.MagicMock): @@ -253,7 +252,7 @@ def test_permissions_failure(self, chmod: unittest.mock.MagicMock): with tempfile.TemporaryDirectory() as temp_dir: filename = os.path.join(temp_dir, ".unit-state.db") open(filename, "w").close() - self.assertRaises(RuntimeError, ops.storage.SQLiteStorage, filename) + pytest.raises(RuntimeError, ops.storage.SQLiteStorage, filename) def setup_juju_backend(test_case: unittest.TestCase, state_file: pathlib.Path): @@ -339,29 +338,29 @@ class TestSimpleLoader(BaseTestCase): def test_is_c_loader(self): loader = ops.storage._SimpleLoader(io.StringIO('')) if getattr(yaml, 'CSafeLoader', None) is not None: - self.assertIsInstance(loader, yaml.CSafeLoader) + assert isinstance(loader, yaml.CSafeLoader) else: - self.assertIsInstance(loader, yaml.SafeLoader) + assert isinstance(loader, yaml.SafeLoader) def test_is_c_dumper(self): dumper = ops.storage._SimpleDumper(io.StringIO('')) if getattr(yaml, 'CSafeDumper', None) is not None: - self.assertIsInstance(dumper, yaml.CSafeDumper) + assert isinstance(dumper, yaml.CSafeDumper) else: - self.assertIsInstance(dumper, yaml.SafeDumper) + assert isinstance(dumper, yaml.SafeDumper) def test_handles_tuples(self): raw = yaml.dump((1, 'tuple'), Dumper=ops.storage._SimpleDumper) parsed = yaml.load(raw, Loader=ops.storage._SimpleLoader) # noqa: S506 - self.assertEqual(parsed, (1, 'tuple')) + assert parsed == (1, 'tuple') def assertRefused(self, obj: typing.Any): # noqa: N802 # We shouldn't allow them to be written - with self.assertRaises(yaml.representer.RepresenterError): + with pytest.raises(yaml.representer.RepresenterError): yaml.dump(obj, Dumper=ops.storage._SimpleDumper) # If they did somehow end up written, we shouldn't be able to load them raw = yaml.dump(obj, Dumper=yaml.Dumper) - with self.assertRaises(yaml.constructor.ConstructorError): + with pytest.raises(yaml.constructor.ConstructorError): yaml.load(raw, Loader=ops.storage._SimpleLoader) # noqa: S506 def test_forbids_some_types(self): @@ -380,12 +379,12 @@ class Foo: class TestJujuStateBackend(BaseTestCase): def test_is_not_available(self): - self.assertFalse(ops.storage.juju_backend_available()) + assert not ops.storage.juju_backend_available() def test_is_available(self): fake_script(self, 'state-get', 'echo ""') - self.assertTrue(ops.storage.juju_backend_available()) - self.assertEqual(fake_script_calls(self, clear=True), []) + assert ops.storage.juju_backend_available() + assert fake_script_calls(self, clear=True) == [] def test_set_encodes_args(self): t = tempfile.NamedTemporaryFile() @@ -395,17 +394,17 @@ def test_set_encodes_args(self): """).format(pathlib.Path(t.name).as_posix())) backend = ops.storage._JujuStorageBackend() backend.set('key', {'foo': 2}) - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['state-set', '--file', '-'], - ]) + ] t.seek(0) content = t.read() finally: t.close() - self.assertEqual(content.decode('utf-8'), dedent("""\ + assert content.decode('utf-8') == dedent("""\ "key": | {foo: 2} - """)) + """) def test_get(self): fake_script(self, 'state-get', dedent(""" @@ -413,10 +412,10 @@ def test_get(self): """)) backend = ops.storage._JujuStorageBackend() value = backend.get('key') - self.assertEqual(value, {'foo': 'bar'}) - self.assertEqual(fake_script_calls(self, clear=True), [ + assert value == {'foo': 'bar'} + assert fake_script_calls(self, clear=True) == [ ['state-get', 'key'], - ]) + ] def test_set_and_get_complex_value(self): t = tempfile.NamedTemporaryFile() @@ -434,19 +433,19 @@ def test_set_and_get_complex_value(self): 'seven': b'1234', } backend.set('Class[foo]/_stored', complex_val) - self.assertEqual(fake_script_calls(self, clear=True), [ + assert fake_script_calls(self, clear=True) == [ ['state-set', '--file', '-'], - ]) + ] t.seek(0) content = t.read() finally: t.close() outer = yaml.safe_load(content) key = 'Class[foo]/_stored' - self.assertEqual(list(outer.keys()), [key]) + assert list(outer.keys()) == [key] inner = yaml.load(outer[key], Loader=ops.storage._SimpleLoader) # noqa: S506 - self.assertEqual(complex_val, inner) - self.assertEqual(content.decode('utf-8'), dedent("""\ + assert complex_val == inner + assert content.decode('utf-8') == dedent("""\ "Class[foo]/_stored": | foo: 2 3: [1, 2, '3'] @@ -455,7 +454,7 @@ def test_set_and_get_complex_value(self): six: !!python/tuple [a, b] seven: !!binary | MTIzNA== - """)) + """) # Note that the content is yaml in a string, embedded inside YAML to declare the Key: # Value of where to store the entry. fake_script(self, 'state-get', dedent(""" @@ -469,7 +468,7 @@ def test_set_and_get_complex_value(self): " """)) out = backend.get('Class[foo]/_stored') - self.assertEqual(out, complex_val) + assert out == complex_val # TODO: Add tests for things we don't want to support. eg, YAML that has custom types should # be properly forbidden. diff --git a/test/test_testing.py b/test/test_testing.py index b99863086..b313e86b1 100644 --- a/test/test_testing.py +++ b/test/test_testing.py @@ -33,6 +33,7 @@ import uuid from unittest.mock import MagicMock, patch +import pytest import yaml import ops @@ -80,7 +81,7 @@ class TestHarness(unittest.TestCase): def test_add_relation_no_meta_fails(self): harness = ops.testing.Harness(ops.CharmBase, meta="name: mycharm") self.addCleanup(harness.cleanup) - with self.assertRaises(ops.RelationNotFoundError): + with pytest.raises(ops.RelationNotFoundError): harness.add_relation('db', 'postgresql') def test_add_relation(self): @@ -92,13 +93,13 @@ def test_add_relation(self): ''') self.addCleanup(harness.cleanup) rel_id = harness.add_relation('db', 'postgresql') - self.assertIsInstance(rel_id, int) + assert isinstance(rel_id, int) backend = harness._backend - self.assertEqual(backend.relation_ids('db'), [rel_id]) - self.assertEqual(backend.relation_list(rel_id), []) + assert backend.relation_ids('db') == [rel_id] + assert backend.relation_list(rel_id) == [] # Make sure the initial data bags for our app and unit are empty. - self.assertEqual(backend.relation_get(rel_id, 'test-app', is_app=True), {}) - self.assertEqual(backend.relation_get(rel_id, 'test-app/0', is_app=False), {}) + assert backend.relation_get(rel_id, 'test-app', is_app=True) == {} + assert backend.relation_get(rel_id, 'test-app/0', is_app=False) == {} def test_add_relation_with_app_data(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -109,12 +110,12 @@ def test_add_relation_with_app_data(self): ''') self.addCleanup(harness.cleanup) rel_id = harness.add_relation('db', 'postgresql', app_data={'x': '1', 'y': '2'}) - self.assertIsInstance(rel_id, int) + assert isinstance(rel_id, int) backend = harness._backend - self.assertEqual(backend.relation_ids('db'), [rel_id]) - self.assertEqual(backend.relation_list(rel_id), ['postgresql/0']) - self.assertEqual(harness.get_relation_data(rel_id, 'postgresql'), {'x': '1', 'y': '2'}) - self.assertEqual(harness.get_relation_data(rel_id, 'postgresql/0'), {}) + assert backend.relation_ids('db') == [rel_id] + assert backend.relation_list(rel_id) == ['postgresql/0'] + assert harness.get_relation_data(rel_id, 'postgresql') == {'x': '1', 'y': '2'} + assert harness.get_relation_data(rel_id, 'postgresql/0') == {} def test_add_relation_with_unit_data(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -125,12 +126,12 @@ def test_add_relation_with_unit_data(self): ''') self.addCleanup(harness.cleanup) rel_id = harness.add_relation('db', 'postgresql', unit_data={'a': '1', 'b': '2'}) - self.assertIsInstance(rel_id, int) + assert isinstance(rel_id, int) backend = harness._backend - self.assertEqual(backend.relation_ids('db'), [rel_id]) - self.assertEqual(backend.relation_list(rel_id), ['postgresql/0']) - self.assertEqual(harness.get_relation_data(rel_id, 'postgresql'), {}) - self.assertEqual(harness.get_relation_data(rel_id, 'postgresql/0'), {'a': '1', 'b': '2'}) + assert backend.relation_ids('db') == [rel_id] + assert backend.relation_list(rel_id) == ['postgresql/0'] + assert harness.get_relation_data(rel_id, 'postgresql') == {} + assert harness.get_relation_data(rel_id, 'postgresql/0') == {'a': '1', 'b': '2'} def test_can_connect_default(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -144,18 +145,18 @@ def test_can_connect_default(self): harness.begin() c = harness.model.unit.get_container('foo') - self.assertFalse(c.can_connect()) - with self.assertRaises(pebble.ConnectionError): + assert not c.can_connect() + with pytest.raises(pebble.ConnectionError): c.get_plan() harness.set_can_connect('foo', True) - self.assertTrue(c.can_connect()) + assert c.can_connect() harness.set_can_connect('foo', False) - self.assertFalse(c.can_connect()) + assert not c.can_connect() harness.container_pebble_ready('foo') - self.assertTrue(c.can_connect()) + assert c.can_connect() c.get_plan() # shouldn't raise ConnectionError def test_can_connect_begin_with_initial_hooks(self): @@ -182,16 +183,16 @@ def _on_pebble_ready(self, event: ops.PebbleReadyEvent): self.addCleanup(harness.cleanup) harness.begin_with_initial_hooks() - self.assertEqual(dict(pebble_ready_calls), {'foo': 1, 'bar': 1}) - self.assertTrue(harness.model.unit.containers['foo'].can_connect()) - self.assertTrue(harness.model.unit.containers['bar'].can_connect()) + assert dict(pebble_ready_calls) == {'foo': 1, 'bar': 1} + assert harness.model.unit.containers['foo'].can_connect() + assert harness.model.unit.containers['bar'].can_connect() harness.set_can_connect('foo', False) - self.assertFalse(harness.model.unit.containers['foo'].can_connect()) + assert not harness.model.unit.containers['foo'].can_connect() harness.set_can_connect('foo', True) container = harness.model.unit.containers['foo'] - self.assertTrue(container.can_connect()) + assert container.can_connect() container.get_plan() # shouldn't raise ConnectionError def test_add_relation_and_unit(self): @@ -203,15 +204,14 @@ def test_add_relation_and_unit(self): ''') self.addCleanup(harness.cleanup) rel_id = harness.add_relation('db', 'postgresql') - self.assertIsInstance(rel_id, int) + assert isinstance(rel_id, int) harness.add_relation_unit(rel_id, 'postgresql/0') harness.update_relation_data(rel_id, 'postgresql/0', {'foo': 'bar'}) backend = harness._backend - self.assertEqual(backend.relation_ids('db'), [rel_id]) - self.assertEqual(backend.relation_list(rel_id), ['postgresql/0']) - self.assertEqual( - backend.relation_get(rel_id, 'postgresql/0', is_app=False), - {'foo': 'bar'}) + assert backend.relation_ids('db') == [rel_id] + assert backend.relation_list(rel_id) == ['postgresql/0'] + assert backend.relation_get(rel_id, 'postgresql/0', is_app=False) == \ + {'foo': 'bar'} def test_add_relation_with_remote_app_data(self): # language=YAML @@ -225,10 +225,10 @@ def test_add_relation_with_remote_app_data(self): remote_app = 'postgresql' rel_id = harness.add_relation('db', remote_app) harness.update_relation_data(rel_id, 'postgresql', {'app': 'data'}) - self.assertIsInstance(rel_id, int) + assert isinstance(rel_id, int) backend = harness._backend - self.assertEqual([rel_id], backend.relation_ids('db')) - self.assertEqual({'app': 'data'}, backend.relation_get(rel_id, remote_app, is_app=True)) + assert [rel_id] == backend.relation_ids('db') + assert backend.relation_get(rel_id, remote_app, is_app=True) == {'app': 'data'} def test_add_relation_with_our_initial_data(self): @@ -255,21 +255,21 @@ def _on_db_relation_changed(self, event: ops.EventBase): harness.update_relation_data(rel_id, 'test-app', {'k': 'v1'}) harness.update_relation_data(rel_id, 'test-app/0', {'ingress-address': '192.0.2.1'}) backend = harness._backend - self.assertEqual({'k': 'v1'}, backend.relation_get(rel_id, 'test-app', is_app=True)) - self.assertEqual({'ingress-address': '192.0.2.1'}, - backend.relation_get(rel_id, 'test-app/0', is_app=False)) + assert backend.relation_get(rel_id, 'test-app', is_app=True) == {'k': 'v1'} + assert backend.relation_get(rel_id, 'test-app/0', is_app=False) == \ + {'ingress-address': '192.0.2.1'} harness.begin() - self.assertEqual({'k': 'v1'}, backend.relation_get(rel_id, 'test-app', is_app=True)) - self.assertEqual({'ingress-address': '192.0.2.1'}, - backend.relation_get(rel_id, 'test-app/0', is_app=False)) + assert backend.relation_get(rel_id, 'test-app', is_app=True) == {'k': 'v1'} + assert backend.relation_get(rel_id, 'test-app/0', is_app=False) == \ + {'ingress-address': '192.0.2.1'} # Make sure no relation-changed events are emitted for our own data bags. - self.assertEqual([], harness.charm.observed_events) + assert harness.charm.observed_events == [] # A remote unit can still update our app relation data bag since our unit is not a leader. harness.update_relation_data(rel_id, 'test-app', {'k': 'v2'}) # And we get an event - self.assertEqual([], harness.charm.observed_events) + assert harness.charm.observed_events == [] # We can also update our own relation data, even if it is a bit 'cheaty' harness.update_relation_data(rel_id, 'test-app/0', {'ingress-address': '192.0.2.2'}) # But no event happens @@ -278,7 +278,7 @@ def _on_db_relation_changed(self, event: ops.EventBase): harness.set_leader(True) harness.update_relation_data(rel_id, 'test-app', {'k': 'v3'}) harness.update_relation_data(rel_id, 'test-app/0', {'ingress-address': '192.0.2.2'}) - self.assertEqual([], harness.charm.observed_events) + assert harness.charm.observed_events == [] def test_add_peer_relation_with_initial_data_leader(self): @@ -309,29 +309,29 @@ def _on_cluster_relation_changed(self, event: ops.EventBase): harness.update_relation_data(rel_id, 'test-app', {'k': 'v'}) harness.update_relation_data(rel_id, 'test-app/0', {'ingress-address': '192.0.2.1'}) backend = harness._backend - self.assertEqual({'k': 'v'}, backend.relation_get(rel_id, 'test-app', is_app=True)) - self.assertEqual({'ingress-address': '192.0.2.1'}, - backend.relation_get(rel_id, 'test-app/0', is_app=False)) + assert backend.relation_get(rel_id, 'test-app', is_app=True) == {'k': 'v'} + assert backend.relation_get(rel_id, 'test-app/0', is_app=False) == \ + {'ingress-address': '192.0.2.1'} harness.begin() - self.assertEqual({'k': 'v'}, backend.relation_get(rel_id, 'test-app', is_app=True)) - self.assertEqual({'ingress-address': '192.0.2.1'}, - backend.relation_get(rel_id, 'test-app/0', is_app=False)) + assert backend.relation_get(rel_id, 'test-app', is_app=True) == {'k': 'v'} + assert backend.relation_get(rel_id, 'test-app/0', is_app=False) == \ + {'ingress-address': '192.0.2.1'} # Make sure no relation-changed events are emitted for our own data bags. - self.assertEqual([], harness.charm.observed_events) + assert harness.charm.observed_events == [] # Updating our app relation data bag and our unit data bag does not trigger events harness.update_relation_data(rel_id, 'test-app', {'k': 'v2'}) harness.update_relation_data(rel_id, 'test-app/0', {'ingress-address': '192.0.2.2'}) - self.assertEqual([], harness.charm.observed_events) + assert harness.charm.observed_events == [] # If our unit becomes a minion, updating app relation data indirectly becomes possible # and our charm gets notifications. harness.set_leader(False) harness.update_relation_data(rel_id, 'test-app', {'k': 'v3'}) - self.assertEqual({'k': 'v3'}, backend.relation_get(rel_id, 'test-app', is_app=True)) - self.assertTrue(len(harness.charm.observed_events), 1) - self.assertIsInstance(harness.charm.observed_events[0], ops.RelationEvent) + assert backend.relation_get(rel_id, 'test-app', is_app=True) == {'k': 'v3'} + assert len(harness.charm.observed_events), 1 + assert isinstance(harness.charm.observed_events[0], ops.RelationEvent) def test_remove_relation(self): harness = ops.testing.Harness(RelationEventCharm, meta=''' @@ -345,33 +345,33 @@ def test_remove_relation(self): harness.charm.observe_relation_events('db') # First create a relation rel_id = harness.add_relation('db', 'postgresql') - self.assertIsInstance(rel_id, int) + assert isinstance(rel_id, int) harness.add_relation_unit(rel_id, 'postgresql/0') backend = harness._backend # Check relation was created - self.assertEqual(backend.relation_ids('db'), [rel_id]) - self.assertEqual(backend.relation_list(rel_id), ['postgresql/0']) + assert backend.relation_ids('db') == [rel_id] + assert backend.relation_list(rel_id) == ['postgresql/0'] harness.charm.get_changes(reset=True) # created event ignored # Now remove relation harness.remove_relation(rel_id) # Check relation no longer exists - self.assertEqual(backend.relation_ids('db'), []) - self.assertRaises(ops.RelationNotFoundError, backend.relation_list, rel_id) + assert backend.relation_ids('db') == [] + pytest.raises(ops.RelationNotFoundError, backend.relation_list, rel_id) # Check relation broken event is raised with correct data changes = harness.charm.get_changes() - self.assertEqual(changes[0], - {'name': 'relation-departed', - 'relation': 'db', - 'data': {'app': 'postgresql', - 'unit': 'postgresql/0', - 'departing_unit': 'postgresql/0', - 'relation_id': 0}}) - self.assertEqual(changes[1], - {'name': 'relation-broken', - 'relation': 'db', - 'data': {'app': 'postgresql', - 'unit': None, - 'relation_id': rel_id}}) + assert changes[0] == \ + {'name': 'relation-departed', + 'relation': 'db', + 'data': {'app': 'postgresql', + 'unit': 'postgresql/0', + 'departing_unit': 'postgresql/0', + 'relation_id': 0}} + assert changes[1] == \ + {'name': 'relation-broken', + 'relation': 'db', + 'data': {'app': 'postgresql', + 'unit': None, + 'relation_id': rel_id}} def test_remove_specific_relation_id(self): harness = ops.testing.Harness(RelationEventCharm, meta=''' @@ -386,46 +386,46 @@ def test_remove_specific_relation_id(self): # Create the first relation rel_id_1 = harness.add_relation('db', 'postgresql') - self.assertIsInstance(rel_id_1, int) + assert isinstance(rel_id_1, int) harness.add_relation_unit(rel_id_1, 'postgresql/0') backend = harness._backend # Check relation was created - self.assertIn(rel_id_1, backend.relation_ids('db')) - self.assertEqual(backend.relation_list(rel_id_1), ['postgresql/0']) + assert rel_id_1 in backend.relation_ids('db') + assert backend.relation_list(rel_id_1) == ['postgresql/0'] harness.charm.get_changes(reset=True) # created event ignored # Create the second relation rel_id_2 = harness.add_relation('db', 'postgresql') - self.assertIsInstance(rel_id_2, int) + assert isinstance(rel_id_2, int) harness.add_relation_unit(rel_id_2, 'postgresql/1') backend = harness._backend # Check relation was created and both relations exist - self.assertIn(rel_id_1, backend.relation_ids('db')) - self.assertIn(rel_id_2, backend.relation_ids('db')) - self.assertEqual(backend.relation_list(rel_id_2), ['postgresql/1']) + assert rel_id_1 in backend.relation_ids('db') + assert rel_id_2 in backend.relation_ids('db') + assert backend.relation_list(rel_id_2) == ['postgresql/1'] harness.charm.get_changes(reset=True) # created event ignored # Now remove second relation harness.remove_relation(rel_id_2) # Check second relation no longer exists but first does - self.assertEqual(backend.relation_ids('db'), [rel_id_1]) - self.assertRaises(ops.RelationNotFoundError, backend.relation_list, rel_id_2) + assert backend.relation_ids('db') == [rel_id_1] + pytest.raises(ops.RelationNotFoundError, backend.relation_list, rel_id_2) # Check relation broken event is raised with correct data changes = harness.charm.get_changes() - self.assertEqual(changes[0], - {'name': 'relation-departed', - 'relation': 'db', - 'data': {'app': 'postgresql', - 'unit': 'postgresql/1', - 'departing_unit': 'postgresql/1', - 'relation_id': rel_id_2}}) - self.assertEqual(changes[1], - {'name': 'relation-broken', - 'relation': 'db', - 'data': {'app': 'postgresql', - 'unit': None, - 'relation_id': rel_id_2}}) + assert changes[0] == \ + {'name': 'relation-departed', + 'relation': 'db', + 'data': {'app': 'postgresql', + 'unit': 'postgresql/1', + 'departing_unit': 'postgresql/1', + 'relation_id': rel_id_2}} + assert changes[1] == \ + {'name': 'relation-broken', + 'relation': 'db', + 'data': {'app': 'postgresql', + 'unit': None, + 'relation_id': rel_id_2}} def test_removing_invalid_relation_id_raises_exception(self): harness = ops.testing.Harness(RelationEventCharm, meta=''' @@ -439,15 +439,15 @@ def test_removing_invalid_relation_id_raises_exception(self): harness.charm.observe_relation_events('db') # First create a relation rel_id = harness.add_relation('db', 'postgresql') - self.assertIsInstance(rel_id, int) + assert isinstance(rel_id, int) harness.add_relation_unit(rel_id, 'postgresql/0') backend = harness._backend # Check relation was created - self.assertEqual(backend.relation_ids('db'), [rel_id]) - self.assertEqual(backend.relation_list(rel_id), ['postgresql/0']) + assert backend.relation_ids('db') == [rel_id] + assert backend.relation_list(rel_id) == ['postgresql/0'] harness.charm.get_changes(reset=True) # created event ignored # Check exception is raised if relation id is invalid - with self.assertRaises(ops.RelationNotFoundError): + with pytest.raises(ops.RelationNotFoundError): harness.remove_relation(rel_id + 1) def test_remove_relation_unit(self): @@ -462,45 +462,45 @@ def test_remove_relation_unit(self): harness.charm.observe_relation_events('db') # First add a relation and unit rel_id = harness.add_relation('db', 'postgresql') - self.assertIsInstance(rel_id, int) + assert isinstance(rel_id, int) harness.add_relation_unit(rel_id, 'postgresql/0') harness.update_relation_data(rel_id, 'postgresql/0', {'foo': 'bar'}) # Check relation and unit were created backend = harness._backend - self.assertEqual(backend.relation_ids('db'), [rel_id]) - self.assertEqual(backend.relation_list(rel_id), ['postgresql/0']) + assert backend.relation_ids('db') == [rel_id] + assert backend.relation_list(rel_id) == ['postgresql/0'] harness.charm.get_changes(reset=True) # ignore relation created events relation = harness.charm.model.get_relation('db') assert relation is not None - self.assertEqual(len(relation.units), 1) + assert len(relation.units) == 1 # Check relation data is correct rel_unit = harness.charm.model.get_unit('postgresql/0') - self.assertEqual(relation.data[rel_unit]['foo'], 'bar') + assert relation.data[rel_unit]['foo'] == 'bar' # Instruct the charm to record the relation data it sees in the list of changes harness.charm.record_relation_data_on_events = True # Now remove unit harness.remove_relation_unit(rel_id, 'postgresql/0') # Check relation still exists - self.assertEqual(backend.relation_ids('db'), [rel_id]) + assert backend.relation_ids('db') == [rel_id] # Check removed unit does not exist - self.assertEqual(backend.relation_list(rel_id), []) + assert backend.relation_list(rel_id) == [] # Check the unit is actually removed from the relations the model knows about rel = harness.charm.model.get_relation('db') assert rel is not None - self.assertEqual(len(rel.units), 0) - self.assertFalse(rel_unit in rel.data) + assert len(rel.units) == 0 + assert rel_unit not in rel.data # Check relation departed was raised with correct data - self.assertEqual({'name': 'relation-departed', - 'relation': 'db', - 'data': {'app': 'postgresql', - 'unit': 'postgresql/0', - 'departing_unit': 'postgresql/0', - 'relation_id': 0, - 'relation_data': {'test-app/0': {}, - 'test-app': {}, - 'postgresql/0': {'foo': 'bar'}, - 'postgresql': {}}}}, - harness.charm.get_changes()[0]) + assert harness.charm.get_changes()[0] == \ + {'name': 'relation-departed', + 'relation': 'db', + 'data': {'app': 'postgresql', + 'unit': 'postgresql/0', + 'departing_unit': 'postgresql/0', + 'relation_id': 0, + 'relation_data': {'test-app/0': {}, + 'test-app': {}, + 'postgresql/0': {'foo': 'bar'}, + 'postgresql': {}}}} def test_removing_relation_removes_remote_app_data(self): # language=YAML @@ -517,17 +517,17 @@ def test_removing_relation_removes_remote_app_data(self): remote_app = 'postgresql' rel_id = harness.add_relation('db', remote_app) harness.update_relation_data(rel_id, 'postgresql', {'app': 'data'}) - self.assertIsInstance(rel_id, int) + assert isinstance(rel_id, int) # Check relation app data exists backend = harness._backend - self.assertEqual(backend.relation_ids('db'), [rel_id]) - self.assertEqual({'app': 'data'}, backend.relation_get(rel_id, remote_app, is_app=True)) + assert backend.relation_ids('db') == [rel_id] + assert backend.relation_get(rel_id, remote_app, is_app=True) == {'app': 'data'} harness.remove_relation(rel_id) # Check relation and app data are removed - self.assertEqual(backend.relation_ids('db'), []) + assert backend.relation_ids('db') == [] with harness._event_context('foo'): - self.assertRaises(ops.RelationNotFoundError, backend.relation_get, - rel_id, remote_app, is_app=True) + pytest.raises(ops.RelationNotFoundError, backend.relation_get, + rel_id, remote_app, is_app=True) def test_removing_relation_refreshes_charm_model(self): # language=YAML @@ -544,15 +544,15 @@ def test_removing_relation_refreshes_charm_model(self): remote_app = 'postgresql' rel_id = harness.add_relation('db', remote_app) harness.update_relation_data(rel_id, 'postgresql', {'app': 'data'}) - self.assertIsInstance(rel_id, int) - self.assertIsNotNone(self._find_relation_in_model_by_id(harness, rel_id)) + assert isinstance(rel_id, int) + assert self._find_relation_in_model_by_id(harness, rel_id) is not None # Check relation app data exists backend = harness._backend - self.assertEqual(backend.relation_ids('db'), [rel_id]) - self.assertEqual({'app': 'data'}, backend.relation_get(rel_id, remote_app, is_app=True)) + assert backend.relation_ids('db') == [rel_id] + assert backend.relation_get(rel_id, remote_app, is_app=True) == {'app': 'data'} harness.remove_relation(rel_id) - self.assertIsNone(self._find_relation_in_model_by_id(harness, rel_id)) + assert self._find_relation_in_model_by_id(harness, rel_id) is None def test_remove_relation_marks_relation_as_inactive(self): relations: typing.List[str] = [] @@ -578,8 +578,8 @@ def _db_relation_broken(self, event: ops.RelationBrokenEvent): harness.begin() rel_id = harness.add_relation('db', 'postgresql') harness.remove_relation(rel_id) - self.assertTrue(is_broken, 'event.relation.active not False in relation-broken event') - self.assertFalse(relations, 'Model.relations contained broken relation') + assert is_broken, 'event.relation.active not False in relation-broken event' + assert not relations, 'Model.relations contained broken relation' def _find_relation_in_model_by_id( self, @@ -603,35 +603,34 @@ def test_removing_relation_unit_removes_data_also(self): harness.charm.observe_relation_events('db') # Add a relation and unit with data rel_id = harness.add_relation('db', 'postgresql') - self.assertIsInstance(rel_id, int) + assert isinstance(rel_id, int) harness.add_relation_unit(rel_id, 'postgresql/0') harness.update_relation_data(rel_id, 'postgresql/0', {'foo': 'bar'}) # Check relation, unit and data exist backend = harness._backend - self.assertEqual(backend.relation_ids('db'), [rel_id]) - self.assertEqual(backend.relation_list(rel_id), ['postgresql/0']) - self.assertEqual( - backend.relation_get(rel_id, 'postgresql/0', is_app=False), - {'foo': 'bar'}) + assert backend.relation_ids('db') == [rel_id] + assert backend.relation_list(rel_id) == ['postgresql/0'] + assert backend.relation_get(rel_id, 'postgresql/0', is_app=False) == \ + {'foo': 'bar'} harness.charm.get_changes(reset=True) # ignore relation created events # Remove unit but not relation harness.remove_relation_unit(rel_id, 'postgresql/0') # Check relation exists but unit and data are removed - self.assertEqual(backend.relation_ids('db'), [rel_id]) - self.assertEqual(backend.relation_list(rel_id), []) - self.assertRaises(KeyError, - backend.relation_get, - rel_id, - 'postgresql/0', - is_app=False) + assert backend.relation_ids('db') == [rel_id] + assert backend.relation_list(rel_id) == [] + pytest.raises(KeyError, + backend.relation_get, + rel_id, + 'postgresql/0', + is_app=False) # Check relation departed was raised with correct data - self.assertEqual(harness.charm.get_changes()[0], - {'name': 'relation-departed', - 'relation': 'db', - 'data': {'app': 'postgresql', - 'unit': 'postgresql/0', - 'departing_unit': 'postgresql/0', - 'relation_id': rel_id}}) + assert harness.charm.get_changes()[0] == \ + {'name': 'relation-departed', + 'relation': 'db', + 'data': {'app': 'postgresql', + 'unit': 'postgresql/0', + 'departing_unit': 'postgresql/0', + 'relation_id': rel_id}} def test_removing_relation_unit_does_not_remove_other_unit_and_data(self): harness = ops.testing.Harness(RelationEventCharm, meta=''' @@ -645,39 +644,36 @@ def test_removing_relation_unit_does_not_remove_other_unit_and_data(self): harness.charm.observe_relation_events('db') # Add a relation with two units with data rel_id = harness.add_relation('db', 'postgresql') - self.assertIsInstance(rel_id, int) + assert isinstance(rel_id, int) harness.add_relation_unit(rel_id, 'postgresql/0') harness.add_relation_unit(rel_id, 'postgresql/1') harness.update_relation_data(rel_id, 'postgresql/0', {'foo0': 'bar0'}) harness.update_relation_data(rel_id, 'postgresql/1', {'foo1': 'bar1'}) # Check both unit and data are present backend = harness._backend - self.assertEqual(backend.relation_ids('db'), [rel_id]) - self.assertEqual(backend.relation_list(rel_id), - ['postgresql/0', 'postgresql/1']) - self.assertEqual( - backend.relation_get(rel_id, 'postgresql/0', is_app=False), - {'foo0': 'bar0'}) - self.assertEqual( - backend.relation_get(rel_id, 'postgresql/1', is_app=False), - {'foo1': 'bar1'}) + assert backend.relation_ids('db') == [rel_id] + assert backend.relation_list(rel_id) == \ + ['postgresql/0', 'postgresql/1'] + assert backend.relation_get(rel_id, 'postgresql/0', is_app=False) == \ + {'foo0': 'bar0'} + assert backend.relation_get(rel_id, 'postgresql/1', is_app=False) == \ + {'foo1': 'bar1'} harness.charm.get_changes(reset=True) # ignore relation created events # Remove only one unit harness.remove_relation_unit(rel_id, 'postgresql/1') # Check other unit and data still exists - self.assertEqual(backend.relation_list(rel_id), - ['postgresql/0']) - self.assertEqual( - backend.relation_get(rel_id, 'postgresql/0', is_app=False), - {'foo0': 'bar0'}) + assert backend.relation_list(rel_id) == \ + ['postgresql/0'] + assert backend.relation_get(rel_id, 'postgresql/0', is_app=False) == \ + {'foo0': 'bar0'} # Check relation departed was raised with correct data - self.assertEqual(harness.charm.get_changes()[0], - {'name': 'relation-departed', - 'relation': 'db', - 'data': {'app': 'postgresql', - 'unit': 'postgresql/1', - 'departing_unit': 'postgresql/1', - 'relation_id': rel_id}}) + assert harness.charm.get_changes()[0] == \ + {'name': 'relation-departed', + 'relation': 'db', + 'data': {'app': 'postgresql', + 'unit': 'postgresql/1', + 'departing_unit': 'postgresql/1', + 'relation_id': rel_id}} def test_relation_events(self): harness = ops.testing.Harness(RelationEventCharm, meta=''' @@ -689,47 +685,43 @@ def test_relation_events(self): self.addCleanup(harness.cleanup) harness.begin() harness.charm.observe_relation_events('db') - self.assertEqual(harness.charm.get_changes(), []) + assert harness.charm.get_changes() == [] rel_id = harness.add_relation('db', 'postgresql') - self.assertEqual( - harness.charm.get_changes(), + assert harness.charm.get_changes() == \ [{'name': 'relation-created', 'relation': 'db', 'data': { 'app': 'postgresql', 'unit': None, 'relation_id': rel_id, - }}]) + }}] harness.add_relation_unit(rel_id, 'postgresql/0') - self.assertEqual( - harness.charm.get_changes(), + assert harness.charm.get_changes() == \ [{'name': 'relation-joined', 'relation': 'db', 'data': { 'app': 'postgresql', 'unit': 'postgresql/0', 'relation_id': rel_id, - }}]) + }}] harness.update_relation_data(rel_id, 'postgresql', {'foo': 'bar'}) - self.assertEqual( - harness.charm.get_changes(), + assert harness.charm.get_changes() == \ [{'name': 'relation-changed', 'relation': 'db', 'data': { 'app': 'postgresql', 'unit': None, 'relation_id': rel_id, - }}]) + }}] harness.update_relation_data(rel_id, 'postgresql/0', {'baz': 'bing'}) - self.assertEqual( - harness.charm.get_changes(), + assert harness.charm.get_changes() == \ [{'name': 'relation-changed', 'relation': 'db', 'data': { 'app': 'postgresql', 'unit': 'postgresql/0', 'relation_id': rel_id, - }}]) + }}] def test_get_relation_data(self): charm_meta = ''' @@ -742,11 +734,11 @@ def test_get_relation_data(self): self.addCleanup(harness.cleanup) rel_id = harness.add_relation('db', 'postgresql') harness.update_relation_data(rel_id, 'postgresql', {'remote': 'data'}) - self.assertEqual(harness.get_relation_data(rel_id, 'test-app'), {}) - self.assertEqual(harness.get_relation_data(rel_id, 'test-app/0'), {}) - self.assertEqual(harness.get_relation_data(rel_id, 'test-app/1'), None) - self.assertEqual(harness.get_relation_data(rel_id, 'postgresql'), {'remote': 'data'}) - with self.assertRaises(KeyError): + assert harness.get_relation_data(rel_id, 'test-app') == {} + assert harness.get_relation_data(rel_id, 'test-app/0') == {} + assert harness.get_relation_data(rel_id, 'test-app/1') is None + assert harness.get_relation_data(rel_id, 'postgresql') == {'remote': 'data'} + with pytest.raises(KeyError): # unknown relation id harness.get_relation_data(99, 'postgresql') @@ -755,11 +747,11 @@ def test_get_relation_data(self): t_app = ops.Application('test-app', meta, harness._backend, t_cache) t_unit0 = ops.Unit('test-app/0', meta, harness._backend, t_cache) t_unit1 = ops.Unit('test-app/1', meta, harness._backend, t_cache) - self.assertEqual(harness.get_relation_data(rel_id, t_app), {}) - self.assertEqual(harness.get_relation_data(rel_id, t_unit0), {}) - self.assertEqual(harness.get_relation_data(rel_id, t_unit1), None) + assert harness.get_relation_data(rel_id, t_app) == {} + assert harness.get_relation_data(rel_id, t_unit0) == {} + assert harness.get_relation_data(rel_id, t_unit1) is None pg_app = ops.Application('postgresql', meta, harness._backend, t_cache) - self.assertEqual(harness.get_relation_data(rel_id, pg_app), {'remote': 'data'}) + assert harness.get_relation_data(rel_id, pg_app) == {'remote': 'data'} def test_create_harness_twice(self): metadata = ''' @@ -779,8 +771,8 @@ def test_create_harness_twice(self): rel_id = harness2.add_relation('db', 'postgresql') harness2.update_relation_data(rel_id, 'postgresql', {'key': 'value'}) # Helper2 should see the event triggered by harness2, but helper1 should see no events. - self.assertEqual(helper1.changes, []) - self.assertEqual(helper2.changes, [(rel_id, 'postgresql')]) + assert helper1.changes == [] + assert helper2.changes == [(rel_id, 'postgresql')] def test_begin_twice(self): # language=YAML @@ -792,7 +784,7 @@ def test_begin_twice(self): ''') self.addCleanup(harness.cleanup) harness.begin() - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): harness.begin() def test_update_relation_exposes_new_data(self): @@ -808,10 +800,10 @@ def test_update_relation_exposes_new_data(self): rel_id = harness.add_relation('db', 'postgresql') harness.add_relation_unit(rel_id, 'postgresql/0') harness.update_relation_data(rel_id, 'postgresql/0', {'initial': 'data'}) - self.assertEqual(viewer.changes, [{'initial': 'data'}]) + assert viewer.changes == [{'initial': 'data'}] harness.update_relation_data(rel_id, 'postgresql/0', {'new': 'value'}) - self.assertEqual(viewer.changes, [{'initial': 'data'}, - {'initial': 'data', 'new': 'value'}]) + assert viewer.changes == [{'initial': 'data'}, + {'initial': 'data', 'new': 'value'}] def test_update_relation_no_local_unit_change_event(self): # language=YAML @@ -831,13 +823,13 @@ def test_update_relation_no_local_unit_change_event(self): # there should be no event for updating our own data harness.update_relation_data(rel_id, 'my-charm/0', {'new': 'other'}) # but the data will be updated. - self.assertEqual({'key': 'value', 'new': 'other'}, rel.data[harness.charm.model.unit]) + assert rel.data[harness.charm.model.unit] == {'key': 'value', 'new': 'other'} rel.data[harness.charm.model.unit]['new'] = 'value' # Our unit data bag got updated. - self.assertEqual(rel.data[harness.charm.model.unit]['new'], 'value') + assert rel.data[harness.charm.model.unit]['new'] == 'value' # But there were no changed events registered by our unit. - self.assertEqual([], helper.changes) + assert helper.changes == [] def test_update_peer_relation_no_local_unit_change_event(self): # language=YAML @@ -858,25 +850,25 @@ def test_update_peer_relation_no_local_unit_change_event(self): rel = harness.charm.model.get_relation('db') assert rel is not None harness.update_relation_data(rel_id, 'postgresql/0', {'key': 'v1'}) - self.assertEqual({'key': 'v1'}, rel.data[harness.charm.model.unit]) + assert rel.data[harness.charm.model.unit] == {'key': 'v1'} # Make sure there was no event - self.assertEqual([], helper.changes) + assert helper.changes == [] rel.data[harness.charm.model.unit]['key'] = 'v2' # Our unit data bag got updated. - self.assertEqual({'key': 'v2'}, dict(rel.data[harness.charm.model.unit])) + assert dict(rel.data[harness.charm.model.unit]) == {'key': 'v2'} # But there were no changed events registered by our unit. - self.assertEqual([], helper.changes) + assert helper.changes == [] # Same for when our unit is a leader. harness.set_leader(is_leader=True) harness.update_relation_data(rel_id, 'postgresql/0', {'key': 'v3'}) - self.assertEqual({'key': 'v3'}, dict(rel.data[harness.charm.model.unit])) - self.assertEqual([], helper.changes) + assert dict(rel.data[harness.charm.model.unit]) == {'key': 'v3'} + assert helper.changes == [] rel.data[harness.charm.model.unit]['key'] = 'v4' - self.assertEqual(rel.data[harness.charm.model.unit]['key'], 'v4') - self.assertEqual([], helper.changes) + assert rel.data[harness.charm.model.unit]['key'] == 'v4' + assert helper.changes == [] def test_update_peer_relation_app_data(self): # language=YAML @@ -895,20 +887,20 @@ def test_update_peer_relation_app_data(self): assert rel is not None rel.data[harness.charm.app]['key'] = 'value' harness.update_relation_data(rel_id, 'postgresql', {'key': 'v1'}) - self.assertEqual({'key': 'v1'}, rel.data[harness.charm.app]) - self.assertEqual([], helper.changes) + assert rel.data[harness.charm.app] == {'key': 'v1'} + assert helper.changes == [] rel.data[harness.charm.app]['key'] = 'v2' # Our unit data bag got updated. - self.assertEqual(rel.data[harness.charm.model.app]['key'], 'v2') + assert rel.data[harness.charm.model.app]['key'] == 'v2' # But there were no changed events registered by our unit. - self.assertEqual([], helper.changes) + assert helper.changes == [] # If our unit is not a leader unit we get an update about peer app relation data changes. harness.set_leader(is_leader=False) harness.update_relation_data(rel_id, 'postgresql', {'k2': 'v2'}) - self.assertEqual(rel.data[harness.charm.model.app]['k2'], 'v2') - self.assertEqual(helper.changes, [(0, 'postgresql')]) + assert rel.data[harness.charm.model.app]['k2'] == 'v2' + assert helper.changes == [(0, 'postgresql')] def test_update_relation_no_local_app_change_event(self): # language=YAML @@ -925,17 +917,17 @@ def test_update_relation_no_local_app_change_event(self): rel_id = harness.add_relation('db', 'postgresql') # TODO: remove this as soon as https://github.com/canonical/operator/issues/175 is fixed. harness.add_relation_unit(rel_id, 'postgresql/0') - self.assertEqual(helper.changes, []) + assert helper.changes == [] harness.update_relation_data(rel_id, 'my-charm', {'new': 'value'}) rel = harness.charm.model.get_relation('db') assert rel is not None - self.assertEqual(rel.data[harness.charm.app]['new'], 'value') + assert rel.data[harness.charm.app]['new'] == 'value' # Our app data bag got updated. - self.assertEqual(rel.data[harness.charm.model.app]['new'], 'value') + assert rel.data[harness.charm.model.app]['new'] == 'value' # But there were no changed events registered by our unit. - self.assertEqual(helper.changes, []) + assert helper.changes == [] def test_update_relation_remove_data(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -951,7 +943,7 @@ def test_update_relation_remove_data(self): harness.add_relation_unit(rel_id, 'postgresql/0') harness.update_relation_data(rel_id, 'postgresql/0', {'initial': 'data'}) harness.update_relation_data(rel_id, 'postgresql/0', {'initial': ''}) - self.assertEqual(viewer.changes, [{'initial': 'data'}, {}]) + assert viewer.changes == [{'initial': 'data'}, {}] def test_no_event_on_empty_update_relation_unit_app(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -967,7 +959,7 @@ def test_no_event_on_empty_update_relation_unit_app(self): harness.add_relation_unit(rel_id, 'postgresql/0') harness.update_relation_data(rel_id, 'postgresql', {'initial': 'data'}) harness.update_relation_data(rel_id, 'postgresql', {}) - self.assertEqual(viewer.changes, [{'initial': 'data'}]) + assert viewer.changes == [{'initial': 'data'}] def test_no_event_on_no_diff_update_relation_unit_app(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -983,7 +975,7 @@ def test_no_event_on_no_diff_update_relation_unit_app(self): harness.add_relation_unit(rel_id, 'postgresql/0') harness.update_relation_data(rel_id, 'postgresql', {'initial': 'data'}) harness.update_relation_data(rel_id, 'postgresql', {'initial': 'data'}) - self.assertEqual(viewer.changes, [{'initial': 'data'}]) + assert viewer.changes == [{'initial': 'data'}] def test_no_event_on_empty_update_relation_unit_bag(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -999,7 +991,7 @@ def test_no_event_on_empty_update_relation_unit_bag(self): harness.add_relation_unit(rel_id, 'postgresql/0') harness.update_relation_data(rel_id, 'postgresql/0', {'initial': 'data'}) harness.update_relation_data(rel_id, 'postgresql/0', {}) - self.assertEqual(viewer.changes, [{'initial': 'data'}]) + assert viewer.changes == [{'initial': 'data'}] def test_no_event_on_no_diff_update_relation_unit_bag(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -1015,10 +1007,10 @@ def test_no_event_on_no_diff_update_relation_unit_bag(self): harness.add_relation_unit(rel_id, 'postgresql/0') harness.update_relation_data(rel_id, 'postgresql/0', {'initial': 'data'}) harness.update_relation_data(rel_id, 'postgresql/0', {'initial': 'data'}) - self.assertEqual(viewer.changes, [{'initial': 'data'}]) + assert viewer.changes == [{'initial': 'data'}] def test_empty_config_raises(self): - with self.assertRaises(TypeError): + with pytest.raises(TypeError): ops.testing.Harness(RecordingCharm, config='') def test_update_config(self): @@ -1034,28 +1026,25 @@ def test_update_config(self): self.addCleanup(harness.cleanup) harness.begin() harness.update_config(key_values={'a': 'foo', 'b': 2}) - self.assertEqual( - harness.charm.changes, - [{'name': 'config-changed', 'data': {'a': 'foo', 'b': 2}}]) + assert harness.charm.changes == \ + [{'name': 'config-changed', 'data': {'a': 'foo', 'b': 2}}] harness.update_config(key_values={'b': 3}) - self.assertEqual( - harness.charm.changes, + assert harness.charm.changes == \ [{'name': 'config-changed', 'data': {'a': 'foo', 'b': 2}}, - {'name': 'config-changed', 'data': {'a': 'foo', 'b': 3}}]) + {'name': 'config-changed', 'data': {'a': 'foo', 'b': 3}}] # you can set config values to the empty string, you can use unset to actually remove items harness.update_config(key_values={'a': ''}, unset=set('b')) - self.assertEqual( - harness.charm.changes, + assert harness.charm.changes == \ [{'name': 'config-changed', 'data': {'a': 'foo', 'b': 2}}, {'name': 'config-changed', 'data': {'a': 'foo', 'b': 3}}, {'name': 'config-changed', 'data': {'a': ''}}, - ]) + ] def test_update_config_undefined_option(self): harness = ops.testing.Harness(RecordingCharm) self.addCleanup(harness.cleanup) harness.begin() - with self.assertRaises(ValueError): + with pytest.raises(ValueError): harness.update_config(key_values={'nonexistent': 'foo'}) def test_update_config_bad_type(self): @@ -1068,15 +1057,15 @@ def test_update_config_bad_type(self): ''') self.addCleanup(harness.cleanup) harness.begin() - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): # cannot cast to bool harness.update_config(key_values={'a': 'foo'}) - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): # cannot cast to float harness.update_config(key_values={'a': 42.42}) - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): # cannot cast to int harness.update_config(key_values={'a': 42}) @@ -1084,7 +1073,7 @@ def test_update_config_bad_type(self): harness.update_config(key_values={'a': False}) def test_bad_config_option_type(self): - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): ops.testing.Harness(RecordingCharm, config=''' options: a: @@ -1105,11 +1094,10 @@ def test_config_secret_option(self): harness.begin() secret_id = harness.add_user_secret({'key': 'value'}) harness.update_config(key_values={'a': secret_id}) - self.assertEqual(harness.charm.changes, - [{'name': 'config-changed', 'data': {'a': secret_id}}]) + assert harness.charm.changes == [{'name': 'config-changed', 'data': {'a': secret_id}}] def test_no_config_option_type(self): - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): ops.testing.Harness(RecordingCharm, config=''' options: a: @@ -1118,7 +1106,7 @@ def test_no_config_option_type(self): ''') def test_uncastable_config_option_type(self): - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): ops.testing.Harness(RecordingCharm, config=''' options: a: @@ -1138,16 +1126,15 @@ def test_update_config_unset_boolean(self): self.addCleanup(harness.cleanup) harness.begin() # Check the default was set correctly - self.assertEqual(harness.charm.config, {'a': False}) + assert harness.charm.config == {'a': False} # Set the boolean value to True harness.update_config(key_values={'a': True}) - self.assertEqual(harness.charm.changes, [{'name': 'config-changed', 'data': {'a': True}}]) + assert harness.charm.changes == [{'name': 'config-changed', 'data': {'a': True}}] # Unset the boolean value harness.update_config(unset={'a'}) - self.assertEqual( - harness.charm.changes, + assert harness.charm.changes == \ [{'name': 'config-changed', 'data': {'a': True}}, - {'name': 'config-changed', 'data': {'a': False}}]) + {'name': 'config-changed', 'data': {'a': False}}] def test_set_leader(self): harness = ops.testing.Harness(RecordingCharm) @@ -1155,20 +1142,20 @@ def test_set_leader(self): # No event happens here harness.set_leader(False) harness.begin() - self.assertFalse(harness.charm.model.unit.is_leader()) + assert not harness.charm.model.unit.is_leader() harness.set_leader(True) - self.assertEqual(harness.charm.get_changes(reset=True), [{'name': 'leader-elected'}]) - self.assertTrue(harness.charm.model.unit.is_leader()) + assert harness.charm.get_changes(reset=True) == [{'name': 'leader-elected'}] + assert harness.charm.model.unit.is_leader() harness.set_leader(False) - self.assertFalse(harness.charm.model.unit.is_leader()) + assert not harness.charm.model.unit.is_leader() # No hook event when you lose leadership. # TODO: verify if Juju always triggers `leader-settings-changed` if you # lose leadership. - self.assertEqual(harness.charm.get_changes(reset=True), []) + assert harness.charm.get_changes(reset=True) == [] harness.disable_hooks() harness.set_leader(True) # No hook event if you have disabled them - self.assertEqual(harness.charm.get_changes(reset=True), []) + assert harness.charm.get_changes(reset=True) == [] def test_relation_set_app_not_leader(self): harness = ops.testing.Harness(RecordingCharm, meta=''' @@ -1185,13 +1172,13 @@ def test_relation_set_app_not_leader(self): rel = harness.charm.model.get_relation('db') assert rel is not None with harness._event_context('foo'): - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): rel.data[harness.charm.app]['foo'] = 'bar' # The data has not actually been changed - self.assertEqual(harness.get_relation_data(rel_id, 'test-charm'), {}) + assert harness.get_relation_data(rel_id, 'test-charm') == {} harness.set_leader(True) rel.data[harness.charm.app]['foo'] = 'bar' - self.assertEqual(harness.get_relation_data(rel_id, 'test-charm'), {'foo': 'bar'}) + assert harness.get_relation_data(rel_id, 'test-charm') == {'foo': 'bar'} def test_hooks_enabled_and_disabled(self): harness = ops.testing.Harness( @@ -1212,18 +1199,16 @@ def test_hooks_enabled_and_disabled(self): # By default, after begin the charm is set up to receive events. harness.begin() harness.update_config({'value': 'second'}) - self.assertEqual( - harness.charm.get_changes(reset=True), - [{'name': 'config-changed', 'data': {'value': 'second'}}]) + assert harness.charm.get_changes(reset=True) == \ + [{'name': 'config-changed', 'data': {'value': 'second'}}] # Once disabled, we won't see config-changed when we make an update harness.disable_hooks() harness.update_config({'third': '3'}) - self.assertEqual(harness.charm.get_changes(reset=True), []) + assert harness.charm.get_changes(reset=True) == [] harness.enable_hooks() harness.update_config({'value': 'fourth'}) - self.assertEqual( - harness.charm.get_changes(reset=True), - [{'name': 'config-changed', 'data': {'value': 'fourth', 'third': '3'}}]) + assert harness.charm.get_changes(reset=True) == \ + [{'name': 'config-changed', 'data': {'value': 'fourth', 'third': '3'}}] def test_hooks_disabled_contextmanager(self): harness = ops.testing.Harness(RecordingCharm, meta=''' @@ -1241,17 +1226,15 @@ def test_hooks_disabled_contextmanager(self): # By default, after begin the charm is set up to receive events. harness.begin() harness.update_config({'value': 'second'}) - self.assertEqual( - harness.charm.get_changes(reset=True), - [{'name': 'config-changed', 'data': {'value': 'second'}}]) + assert harness.charm.get_changes(reset=True) == \ + [{'name': 'config-changed', 'data': {'value': 'second'}}] # Once disabled, we won't see config-changed when we make an update with harness.hooks_disabled(): harness.update_config({'third': '3'}) - self.assertEqual(harness.charm.get_changes(reset=True), []) + assert harness.charm.get_changes(reset=True) == [] harness.update_config({'value': 'fourth'}) - self.assertEqual( - harness.charm.get_changes(reset=True), - [{'name': 'config-changed', 'data': {'value': 'fourth', 'third': '3'}}]) + assert harness.charm.get_changes(reset=True) == \ + [{'name': 'config-changed', 'data': {'value': 'fourth', 'third': '3'}}] def test_hooks_disabled_nested_contextmanager(self): harness = ops.testing.Harness(RecordingCharm, meta=''' @@ -1270,7 +1253,7 @@ def test_hooks_disabled_nested_contextmanager(self): with harness.hooks_disabled(): harness.update_config({'fifth': '5'}) harness.update_config({'sixth': '6'}) - self.assertEqual(harness.charm.get_changes(reset=True), []) + assert harness.charm.get_changes(reset=True) == [] def test_hooks_disabled_noop(self): harness = ops.testing.Harness(RecordingCharm, meta=''' @@ -1289,7 +1272,7 @@ def test_hooks_disabled_noop(self): with harness.hooks_disabled(): harness.update_config({'seventh': '7'}) harness.update_config({'eighth': '8'}) - self.assertEqual(harness.charm.get_changes(reset=True), []) + assert harness.charm.get_changes(reset=True) == [] def test_metadata_from_directory(self): tmp = pathlib.Path(tempfile.mkdtemp()) @@ -1304,9 +1287,9 @@ def test_metadata_from_directory(self): ''')) harness = self._get_dummy_charm_harness(tmp) harness.begin() - self.assertEqual(list(harness.model.relations), ['db']) + assert list(harness.model.relations) == ['db'] # The charm_dir also gets set - self.assertEqual(harness.framework.charm_dir, tmp) + assert harness.framework.charm_dir == tmp def test_metadata_from_directory_charmcraft_yaml(self): tmp = pathlib.Path(tempfile.mkdtemp()) @@ -1329,9 +1312,9 @@ def test_metadata_from_directory_charmcraft_yaml(self): ''')) harness = self._get_dummy_charm_harness(tmp) harness.begin() - self.assertEqual(list(harness.model.relations), ['db']) + assert list(harness.model.relations) == ['db'] # The charm_dir also gets set - self.assertEqual(harness.framework.charm_dir, tmp) + assert harness.framework.charm_dir == tmp def test_config_from_directory(self): tmp = pathlib.Path(tempfile.mkdtemp()) @@ -1362,16 +1345,16 @@ def test_config_from_directory(self): type: string ''')) harness = self._get_dummy_charm_harness(tmp) - self.assertEqual(harness.model.config['opt_str'], 'val') - self.assertEqual(harness.model.config['opt_str_empty'], '') - self.assertIs(harness.model.config['opt_bool'], True) - self.assertEqual(harness.model.config['opt_int'], 1) - self.assertIsInstance(harness.model.config['opt_int'], int) - self.assertEqual(harness.model.config['opt_float'], 1.0) - self.assertIsInstance(harness.model.config['opt_float'], float) - self.assertFalse('opt_null' in harness.model.config) - self.assertIsNone(harness._backend._config._defaults['opt_null']) - self.assertIsNone(harness._backend._config._defaults['opt_no_default']) + assert harness.model.config['opt_str'] == 'val' + assert harness.model.config['opt_str_empty'] == '' + assert harness.model.config['opt_bool'] is True + assert harness.model.config['opt_int'] == 1 + assert isinstance(harness.model.config['opt_int'], int) + assert harness.model.config['opt_float'] == 1.0 + assert isinstance(harness.model.config['opt_float'], float) + assert 'opt_null' not in harness.model.config + assert harness._backend._config._defaults['opt_null'] is None + assert harness._backend._config._defaults['opt_no_default'] is None def test_config_from_directory_charmcraft_yaml(self): tmp = pathlib.Path(tempfile.mkdtemp()) @@ -1397,9 +1380,9 @@ def test_config_from_directory_charmcraft_yaml(self): default: 1 ''')) harness = self._get_dummy_charm_harness(tmp) - self.assertEqual(harness.model.config['opt_str'], 'val') - self.assertEqual(harness.model.config['opt_int'], 1) - self.assertIsInstance(harness.model.config['opt_int'], int) + assert harness.model.config['opt_str'] == 'val' + assert harness.model.config['opt_int'] == 1 + assert isinstance(harness.model.config['opt_int'], int) def test_set_model_name(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -1407,7 +1390,7 @@ def test_set_model_name(self): ''') self.addCleanup(harness.cleanup) harness.set_model_name('foo') - self.assertEqual('foo', harness.model.name) + assert harness.model.name == 'foo' def test_set_model_name_after_begin(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -1416,9 +1399,9 @@ def test_set_model_name_after_begin(self): self.addCleanup(harness.cleanup) harness.set_model_name('bar') harness.begin() - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): harness.set_model_name('foo') - self.assertEqual(harness.model.name, 'bar') + assert harness.model.name == 'bar' def test_set_model_uuid_after_begin(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -1428,9 +1411,9 @@ def test_set_model_uuid_after_begin(self): harness.set_model_name('bar') harness.set_model_uuid('96957e90-e006-11eb-ba80-0242ac130004') harness.begin() - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): harness.set_model_uuid('af0479ea-e006-11eb-ba80-0242ac130004') - self.assertEqual(harness.model.uuid, '96957e90-e006-11eb-ba80-0242ac130004') + assert harness.model.uuid == '96957e90-e006-11eb-ba80-0242ac130004' def test_set_model_info_after_begin(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -1439,18 +1422,18 @@ def test_set_model_info_after_begin(self): self.addCleanup(harness.cleanup) harness.set_model_info('foo', '96957e90-e006-11eb-ba80-0242ac130004') harness.begin() - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): harness.set_model_info('bar', 'af0479ea-e006-11eb-ba80-0242ac130004') - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): harness.set_model_info('bar') - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): harness.set_model_info(uuid='af0479ea-e006-11eb-ba80-0242ac130004') - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): harness.set_model_name('bar') - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): harness.set_model_uuid('af0479ea-e006-11eb-ba80-0242ac130004') - self.assertEqual(harness.model.name, 'foo') - self.assertEqual(harness.model.uuid, '96957e90-e006-11eb-ba80-0242ac130004') + assert harness.model.name == 'foo' + assert harness.model.uuid == '96957e90-e006-11eb-ba80-0242ac130004' def test_add_storage_before_harness_begin(self): harness = ops.testing.Harness(StorageTester, meta=''' @@ -1469,9 +1452,9 @@ def test_add_storage_before_harness_begin(self): stor_ids = harness.add_storage("test", count=3) for s in stor_ids: # before begin, adding storage does not attach it. - self.assertNotIn(s, harness._backend.storage_list("test")) + assert s not in harness._backend.storage_list("test") - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): harness._backend.storage_get("test/0", "location")[-6:] def test_add_storage_then_harness_begin(self): @@ -1490,16 +1473,16 @@ def test_add_storage_then_harness_begin(self): harness.add_storage("test", count=3) - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): harness._backend.storage_get("test/0", "location")[-6:] harness.begin_with_initial_hooks() - self.assertEqual(len(harness.charm.observed_events), 3) + assert len(harness.charm.observed_events) == 3 for i in range(3): - self.assertTrue(isinstance(harness.charm.observed_events[i], ops.StorageAttachedEvent)) + assert isinstance(harness.charm.observed_events[i], ops.StorageAttachedEvent) want = str(pathlib.PurePath('test', '0')) - self.assertEqual(want, harness._backend.storage_get("test/0", "location")[-6:]) + assert want == harness._backend.storage_get("test/0", "location")[-6:] def test_add_storage_not_attached_default(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -1527,11 +1510,10 @@ def test_add_storage_without_metadata_key_fails(self): ''') self.addCleanup(harness.cleanup) - with self.assertRaises(RuntimeError) as cm: + with pytest.raises(RuntimeError) as excinfo: harness.add_storage("test") - self.assertEqual( - cm.exception.args[0], - "the key 'test' is not specified as a storage key in metadata") + assert excinfo.value.args[0] == \ + "the key 'test' is not specified as a storage key in metadata" def test_add_storage_after_harness_begin(self): harness = ops.testing.Harness(StorageTester, meta=''' @@ -1550,8 +1532,8 @@ def test_add_storage_after_harness_begin(self): # Set up initial storage harness.add_storage("test")[0] harness.begin_with_initial_hooks() - self.assertEqual(len(harness.charm.observed_events), 1) - self.assertTrue(isinstance(harness.charm.observed_events[0], ops.StorageAttachedEvent)) + assert len(harness.charm.observed_events) == 1 + assert isinstance(harness.charm.observed_events[0], ops.StorageAttachedEvent) # Add additional storage stor_ids = harness.add_storage("test", count=3, attach=True) @@ -1560,15 +1542,15 @@ def test_add_storage_after_harness_begin(self): # (Should we consider changing its return type?) added_indices = {self._extract_storage_index(stor_id) for stor_id in stor_ids} - self.assertTrue(added_indices.issubset(set(harness._backend.storage_list("test")))) + assert added_indices.issubset(set(harness._backend.storage_list("test"))) for i in ['1', '2', '3']: storage_name = f"test/{i}" want = str(pathlib.PurePath('test', i)) - self.assertTrue(harness._backend.storage_get(storage_name, "location").endswith(want)) - self.assertEqual(len(harness.charm.observed_events), 4) + assert harness._backend.storage_get(storage_name, "location").endswith(want) + assert len(harness.charm.observed_events) == 4 for i in range(1, 4): - self.assertTrue(isinstance(harness.charm.observed_events[i], ops.StorageAttachedEvent)) + assert isinstance(harness.charm.observed_events[i], ops.StorageAttachedEvent) def test_detach_storage(self): harness = ops.testing.Harness(StorageTester, meta=''' @@ -1585,30 +1567,29 @@ def test_detach_storage(self): # Set up initial storage stor_id = harness.add_storage("test")[0] harness.begin_with_initial_hooks() - self.assertEqual(len(harness.charm.observed_events), 1) - self.assertTrue(isinstance(harness.charm.observed_events[0], ops.StorageAttachedEvent)) + assert len(harness.charm.observed_events) == 1 + assert isinstance(harness.charm.observed_events[0], ops.StorageAttachedEvent) # Detach storage harness.detach_storage(stor_id) - self.assertEqual(len(harness.charm.observed_events), 2) - self.assertTrue(isinstance(harness.charm.observed_events[1], ops.StorageDetachingEvent)) + assert len(harness.charm.observed_events) == 2 + assert isinstance(harness.charm.observed_events[1], ops.StorageDetachingEvent) # Verify backend functions return appropriate values. # Real backend would return info only for actively attached storage units. - self.assertNotIn(stor_id, harness._backend.storage_list("test")) - with self.assertRaises(ops.ModelError) as cm: + assert stor_id not in harness._backend.storage_list("test") + with pytest.raises(ops.ModelError) as excinfo: harness._backend.storage_get("test/0", "location") # Error message modeled after output of # "storage-get -s location" on real deployment - self.assertEqual( - cm.exception.args[0], - 'ERROR invalid value "test/0" for option -s: storage not found') + assert excinfo.value.args[0] == \ + 'ERROR invalid value "test/0" for option -s: storage not found' # Retry detach # Since already detached, no more hooks should fire harness.detach_storage(stor_id) - self.assertEqual(len(harness.charm.observed_events), 2) - self.assertTrue(isinstance(harness.charm.observed_events[1], ops.StorageDetachingEvent)) + assert len(harness.charm.observed_events) == 2 + assert isinstance(harness.charm.observed_events[1], ops.StorageDetachingEvent) def test_detach_storage_before_harness_begin(self): harness = ops.testing.Harness(StorageTester, meta=''' @@ -1623,10 +1604,10 @@ def test_detach_storage_before_harness_begin(self): self.addCleanup(harness.cleanup) stor_id = harness.add_storage("test")[0] - with self.assertRaises(RuntimeError) as cm: + with pytest.raises(RuntimeError) as excinfo: harness.detach_storage(f"test/{stor_id}") - self.assertEqual(cm.exception.args[0], - "cannot detach storage before Harness is initialised") + assert excinfo.value.args[0] == \ + "cannot detach storage before Harness is initialised" def test_storage_with_hyphens_works(self): harness = ops.testing.Harness(StorageTester, meta=''' @@ -1647,7 +1628,7 @@ def test_storage_with_hyphens_works(self): helper = StorageWithHyphensHelper(harness.charm, "helper") harness.add_storage("test-with-hyphens", attach=True)[0] - self.assertEqual(len(helper.changes), 1) + assert len(helper.changes) == 1 def test_attach_storage(self): harness = ops.testing.Harness(StorageTester, meta=''' @@ -1664,30 +1645,30 @@ def test_attach_storage(self): # Set up initial storage stor_id = harness.add_storage("test")[0] harness.begin_with_initial_hooks() - self.assertEqual(len(harness.charm.observed_events), 1) - self.assertTrue(isinstance(harness.charm.observed_events[0], ops.StorageAttachedEvent)) + assert len(harness.charm.observed_events) == 1 + assert isinstance(harness.charm.observed_events[0], ops.StorageAttachedEvent) # Detach storage harness.detach_storage(stor_id) - self.assertEqual(len(harness.charm.observed_events), 2) - self.assertTrue(isinstance(harness.charm.observed_events[1], ops.StorageDetachingEvent)) + assert len(harness.charm.observed_events) == 2 + assert isinstance(harness.charm.observed_events[1], ops.StorageDetachingEvent) # Re-attach storage harness.attach_storage(stor_id) - self.assertEqual(len(harness.charm.observed_events), 3) - self.assertTrue(isinstance(harness.charm.observed_events[2], ops.StorageAttachedEvent)) + assert len(harness.charm.observed_events) == 3 + assert isinstance(harness.charm.observed_events[2], ops.StorageAttachedEvent) # Verify backend functions return appropriate values. # Real backend would return info only for actively attached storage units. - self.assertIn(self._extract_storage_index(stor_id), harness._backend.storage_list("test")) + assert self._extract_storage_index(stor_id) in harness._backend.storage_list("test") want = str(pathlib.PurePath('test', '0')) - self.assertEqual(want, harness._backend.storage_get("test/0", "location")[-6:]) + assert want == harness._backend.storage_get("test/0", "location")[-6:] # Retry attach # Since already detached, no more hooks should fire harness.attach_storage(stor_id) - self.assertEqual(len(harness.charm.observed_events), 3) - self.assertTrue(isinstance(harness.charm.observed_events[2], ops.StorageAttachedEvent)) + assert len(harness.charm.observed_events) == 3 + assert isinstance(harness.charm.observed_events[2], ops.StorageAttachedEvent) def test_attach_storage_before_harness_begin(self): harness = ops.testing.Harness(StorageTester, meta=''' @@ -1704,7 +1685,7 @@ def test_attach_storage_before_harness_begin(self): # We deliberately don't guard against attaching storage before the harness begins, # as there are legitimate reasons to do so. stor_id = harness.add_storage("test")[0] - self.assertTrue(stor_id) + assert stor_id def test_remove_storage_before_harness_begin(self): harness = ops.testing.Harness(StorageTester, meta=''' @@ -1731,8 +1712,8 @@ def test_remove_storage_before_harness_begin(self): harness.begin_with_initial_hooks() # Only one hook will fire; one won't since it was removed - self.assertEqual(len(harness.charm.observed_events), 1) - self.assertTrue(isinstance(harness.charm.observed_events[0], ops.StorageAttachedEvent)) + assert len(harness.charm.observed_events) == 1 + assert isinstance(harness.charm.observed_events[0], ops.StorageAttachedEvent) def test_remove_storage_without_metadata_key_fails(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -1745,11 +1726,10 @@ def test_remove_storage_without_metadata_key_fails(self): # Doesn't really make sense since we already can't add storage which isn't in the metadata, # but included for completeness. - with self.assertRaises(RuntimeError) as cm: + with pytest.raises(RuntimeError) as excinfo: harness.remove_storage("test/0") - self.assertEqual( - cm.exception.args[0], - "the key 'test' is not specified as a storage key in metadata") + assert excinfo.value.args[0] == \ + "the key 'test' is not specified as a storage key in metadata" def test_remove_storage_after_harness_begin(self): harness = ops.testing.Harness(StorageTester, meta=''' @@ -1767,17 +1747,17 @@ def test_remove_storage_after_harness_begin(self): stor_ids = harness.add_storage("test", count=2) harness.begin_with_initial_hooks() - self.assertEqual(len(harness.charm.observed_events), 2) - self.assertTrue(isinstance(harness.charm.observed_events[0], ops.StorageAttachedEvent)) - self.assertTrue(isinstance(harness.charm.observed_events[1], ops.StorageAttachedEvent)) + assert len(harness.charm.observed_events) == 2 + assert isinstance(harness.charm.observed_events[0], ops.StorageAttachedEvent) + assert isinstance(harness.charm.observed_events[1], ops.StorageAttachedEvent) harness.remove_storage(stor_ids[1]) - self.assertEqual(len(harness.charm.observed_events), 3) - self.assertTrue(isinstance(harness.charm.observed_events[2], ops.StorageDetachingEvent)) + assert len(harness.charm.observed_events) == 3 + assert isinstance(harness.charm.observed_events[2], ops.StorageDetachingEvent) attached_storage_ids = harness._backend.storage_list("test") - self.assertIn(self._extract_storage_index(stor_ids[0]), attached_storage_ids) - self.assertNotIn(self._extract_storage_index(stor_ids[1]), attached_storage_ids) + assert self._extract_storage_index(stor_ids[0]) in attached_storage_ids + assert self._extract_storage_index(stor_ids[1]) not in attached_storage_ids def _extract_storage_index(self, stor_id: str): return int(stor_id.split('/')[-1]) @@ -1800,10 +1780,10 @@ def test_remove_detached_storage(self): harness.begin_with_initial_hooks() harness.detach_storage(stor_ids[0]) harness.remove_storage(stor_ids[0]) # Already detached, so won't fire a hook - self.assertEqual(len(harness.charm.observed_events), 3) - self.assertTrue(isinstance(harness.charm.observed_events[0], ops.StorageAttachedEvent)) - self.assertTrue(isinstance(harness.charm.observed_events[1], ops.StorageAttachedEvent)) - self.assertTrue(isinstance(harness.charm.observed_events[2], ops.StorageDetachingEvent)) + assert len(harness.charm.observed_events) == 3 + assert isinstance(harness.charm.observed_events[0], ops.StorageAttachedEvent) + assert isinstance(harness.charm.observed_events[1], ops.StorageAttachedEvent) + assert isinstance(harness.charm.observed_events[2], ops.StorageDetachingEvent) def test_actions_from_directory(self): tmp = pathlib.Path(tempfile.mkdtemp()) @@ -1816,9 +1796,9 @@ def test_actions_from_directory(self): ''')) harness = self._get_dummy_charm_harness(tmp) harness.begin() - self.assertEqual(list(harness.framework.meta.actions), ['test']) + assert list(harness.framework.meta.actions) == ['test'] # The charm_dir also gets set - self.assertEqual(harness.framework.charm_dir, tmp) + assert harness.framework.charm_dir == tmp def test_actions_from_directory_charmcraft_yaml(self): tmp = pathlib.Path(tempfile.mkdtemp()) @@ -1840,9 +1820,9 @@ def test_actions_from_directory_charmcraft_yaml(self): ''')) harness = self._get_dummy_charm_harness(tmp) harness.begin() - self.assertEqual(list(harness.framework.meta.actions), ['test']) + assert list(harness.framework.meta.actions) == ['test'] # The charm_dir also gets set - self.assertEqual(harness.framework.charm_dir, tmp) + assert harness.framework.charm_dir == tmp def _get_dummy_charm_harness(self, tmp: pathlib.Path): self._write_dummy_charm(tmp) @@ -1882,7 +1862,7 @@ def test_actions_passed_in(self): description: a dummy test action ''') self.addCleanup(harness.cleanup) - self.assertEqual(list(harness.framework.meta.actions), ['test-action']) + assert list(harness.framework.meta.actions) == ['test-action'] def test_event_context(self): class MyCharm(ops.CharmBase): @@ -1905,7 +1885,7 @@ def event_handler(self, evt: ops.RelationEvent): event.relation = rel with harness._event_context('my_relation_joined'): - with self.assertRaises(ops.RelationDataError): + with pytest.raises(ops.RelationDataError): harness.charm.event_handler(event) def test_event_context_inverse(self): @@ -1941,8 +1921,8 @@ def mock_join_db(event: ops.EventBase): harness.add_relation_unit(rel_id, 'remote/0') rel = harness.charm.model.get_relation('db', rel_id) assert rel is not None - self.assertEqual({'foo': 'bar'}, - harness.get_relation_data(rel_id, 'test-charm')) + assert harness.get_relation_data(rel_id, 'test-charm') == \ + {'foo': 'bar'} # now we're outside of the hook context: assert not harness._backend._hook_is_running @@ -1964,7 +1944,7 @@ def test_relation_set_deletes(self): rel = harness.charm.model.get_relation('db', rel_id) assert rel is not None del rel.data[harness.charm.model.unit]['foo'] - self.assertEqual({}, harness.get_relation_data(rel_id, 'test-charm/0')) + assert harness.get_relation_data(rel_id, 'test-charm/0') == {} def test_relation_set_nonstring(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -1978,7 +1958,7 @@ def test_relation_set_nonstring(self): harness.set_leader(False) rel_id = harness.add_relation('db', 'postgresql') for invalid_value in (1, 1.2, {}, [], set(), True, object(), type): # type: ignore - with self.assertRaises(ops.RelationDataError): + with pytest.raises(ops.RelationDataError): harness.update_relation_data(rel_id, 'test-charm/0', {'foo': invalid_value}) # type: ignore @@ -1988,9 +1968,9 @@ def test_set_workload_version(self): ''') self.addCleanup(harness.cleanup) harness.begin() - self.assertIsNone(harness.get_workload_version()) + assert harness.get_workload_version() is None harness.charm.model.unit.set_workload_version('1.2.3') - self.assertEqual(harness.get_workload_version(), '1.2.3') + assert harness.get_workload_version() == '1.2.3' def test_get_backend_calls(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -2002,50 +1982,45 @@ def test_get_backend_calls(self): self.addCleanup(harness.cleanup) harness.begin() # No calls to the backend yet - self.assertEqual(harness._get_backend_calls(), []) + assert harness._get_backend_calls() == [] rel_id = harness.add_relation('db', 'postgresql') - self.assertEqual( - [ - ('relation_ids', 'db'), - ('relation_list', rel_id), - ('relation_remote_app_name', 0), - ], - harness._get_backend_calls()) + assert harness._get_backend_calls() == [ + ('relation_ids', 'db'), + ('relation_list', rel_id), + ('relation_remote_app_name', 0), + ] # update_relation_data ensures the cached data for the relation is wiped harness.update_relation_data(rel_id, 'test-charm/0', {'foo': 'bar'}) test_charm_unit = harness.model.get_unit('test-charm/0') - self.assertEqual( - [ - ('relation_get', 0, 'test-charm/0', False), - ('update_relation_data', 0, test_charm_unit, 'foo', 'bar') - ], - harness._get_backend_calls(reset=True)) + assert harness._get_backend_calls(reset=True) == [ + ('relation_get', 0, 'test-charm/0', False), + ('update_relation_data', 0, test_charm_unit, 'foo', 'bar') + ] + # add_relation_unit resets the relation_list, but doesn't trigger backend calls harness.add_relation_unit(rel_id, 'postgresql/0') - self.assertEqual([], harness._get_backend_calls(reset=False)) + assert harness._get_backend_calls(reset=False) == [] # however, update_relation_data does, because we are preparing relation-changed harness.update_relation_data(rel_id, 'postgresql/0', {'foo': 'bar'}) pgql_unit = harness.model.get_unit('postgresql/0') - self.assertEqual( - harness._get_backend_calls(reset=False), [ - ('relation_ids', 'db'), - ('relation_list', rel_id), - ('relation_get', 0, 'postgresql/0', False), - ('update_relation_data', 0, pgql_unit, 'foo', 'bar') - ]) + assert harness._get_backend_calls(reset=False) == [ + ('relation_ids', 'db'), + ('relation_list', rel_id), + ('relation_get', 0, 'postgresql/0', False), + ('update_relation_data', 0, pgql_unit, 'foo', 'bar') + ] # If we check again, they are still there, but now we reset it - self.assertEqual( - harness._get_backend_calls(reset=True), [ - ('relation_ids', 'db'), - ('relation_list', rel_id), - ('relation_get', 0, 'postgresql/0', False), - ('update_relation_data', 0, pgql_unit, 'foo', 'bar') - ]) + assert harness._get_backend_calls(reset=True) == [ + ('relation_ids', 'db'), + ('relation_list', rel_id), + ('relation_get', 0, 'postgresql/0', False), + ('update_relation_data', 0, pgql_unit, 'foo', 'bar') + ] # And the calls are gone - self.assertEqual(harness._get_backend_calls(), []) + assert harness._get_backend_calls() == [] def test_get_backend_calls_with_kwargs(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -2060,16 +2035,13 @@ def test_get_backend_calls_with_kwargs(self): # Reset the list, because we don't care what it took to get here harness._get_backend_calls(reset=True) unit.status = ops.ActiveStatus() - self.assertEqual( - [('status_set', 'active', '', {'is_app': False})], harness._get_backend_calls()) + assert harness._get_backend_calls() == [('status_set', 'active', '', {'is_app': False})] harness.set_leader(True) app = harness.charm.model.app harness._get_backend_calls(reset=True) app.status = ops.ActiveStatus('message') - self.assertEqual( - [('is_leader',), - ('status_set', 'active', 'message', {'is_app': True})], - harness._get_backend_calls()) + assert harness._get_backend_calls() == [ + ('is_leader',), ('status_set', 'active', 'message', {'is_app': True})] def test_unit_status(self): harness = ops.testing.Harness(ops.CharmBase, meta='name: test-app') @@ -2077,10 +2049,10 @@ def test_unit_status(self): harness.set_leader(True) harness.begin() # default status - self.assertEqual(harness.model.unit.status, ops.MaintenanceStatus('')) + assert harness.model.unit.status == ops.MaintenanceStatus('') status = ops.ActiveStatus('message') harness.model.unit.status = status - self.assertEqual(harness.model.unit.status, status) + assert harness.model.unit.status == status def test_app_status(self): harness = ops.testing.Harness(ops.CharmBase, meta='name: test-app') @@ -2088,10 +2060,10 @@ def test_app_status(self): harness.set_leader(True) harness.begin() # default status - self.assertEqual(harness.model.app.status, ops.UnknownStatus()) + assert harness.model.app.status == ops.UnknownStatus() status = ops.ActiveStatus('message') harness.model.app.status = status - self.assertEqual(harness.model.app.status, status) + assert harness.model.app.status == status def test_populate_oci_resources(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -2107,16 +2079,16 @@ def test_populate_oci_resources(self): self.addCleanup(harness.cleanup) harness.populate_oci_resources() path = harness.model.resources.fetch('image') - self.assertEqual(path.name, 'contents.yaml') - self.assertEqual(path.parent.name, 'image') + assert path.name == 'contents.yaml' + assert path.parent.name == 'image' with path.open('r') as resource_file: contents = yaml.safe_load(resource_file.read()) - self.assertEqual(contents['registrypath'], 'registrypath') - self.assertEqual(contents['username'], 'username') - self.assertEqual(contents['password'], 'password') + assert contents['registrypath'] == 'registrypath' + assert contents['username'] == 'username' + assert contents['password'] == 'password' path = harness.model.resources.fetch('image2') - self.assertEqual(path.name, 'contents.yaml') - self.assertEqual(path.parent.name, 'image2') + assert path.name == 'contents.yaml' + assert path.parent.name == 'image2' def test_resource_folder_cleanup(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -2129,11 +2101,11 @@ def test_resource_folder_cleanup(self): self.addCleanup(harness.cleanup) harness.populate_oci_resources() path = harness.model.resources.fetch('image') - self.assertTrue(path.exists()) + assert path.exists() harness.cleanup() - self.assertFalse(path.exists()) - self.assertFalse(path.parent.exists()) - self.assertFalse(path.parent.parent.exists()) + assert not path.exists() + assert not path.parent.exists() + assert not path.parent.parent.exists() def test_container_isdir_and_exists(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -2150,18 +2122,18 @@ def test_container_isdir_and_exists(self): dir_path = '/tmp/foo/dir' # noqa: S108 file_path = '/tmp/foo/file' # noqa: S108 - self.assertFalse(c.isdir(dir_path)) - self.assertFalse(c.exists(dir_path)) - self.assertFalse(c.isdir(file_path)) - self.assertFalse(c.exists(file_path)) + assert not c.isdir(dir_path) + assert not c.exists(dir_path) + assert not c.isdir(file_path) + assert not c.exists(file_path) c.make_dir(dir_path, make_parents=True) c.push(file_path, 'data') - self.assertTrue(c.isdir(dir_path)) - self.assertTrue(c.exists(dir_path)) - self.assertFalse(c.isdir(file_path)) - self.assertTrue(c.exists(file_path)) + assert c.isdir(dir_path) + assert c.exists(dir_path) + assert not c.isdir(file_path) + assert c.exists(file_path) def test_add_oci_resource_custom(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -2181,9 +2153,9 @@ def test_add_oci_resource_custom(self): resource = harness.model.resources.fetch('image') with resource.open('r') as resource_file: contents = yaml.safe_load(resource_file.read()) - self.assertEqual(contents['registrypath'], 'custompath') - self.assertEqual(contents['username'], 'custom_username') - self.assertEqual(contents['password'], 'custom_password') + assert contents['registrypath'] == 'custompath' + assert contents['username'] == 'custom_username' + assert contents['password'] == 'custom_password' def test_add_oci_resource_no_image(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -2194,11 +2166,11 @@ def test_add_oci_resource_no_image(self): description: "Image to deploy." ''') self.addCleanup(harness.cleanup) - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): harness.add_oci_resource("image") - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): harness.add_oci_resource("missing-resource") - self.assertEqual(len(harness._backend._resources_map), 0) + assert len(harness._backend._resources_map) == 0 def test_add_resource_unknown(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -2209,7 +2181,7 @@ def test_add_resource_unknown(self): description: "Image to deploy." ''') self.addCleanup(harness.cleanup) - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): harness.add_resource('unknown', 'content') def test_add_resource_but_oci(self): @@ -2221,7 +2193,7 @@ def test_add_resource_but_oci(self): description: "Image to deploy." ''') self.addCleanup(harness.cleanup) - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): harness.add_resource('image', 'content') def test_add_resource_string(self): @@ -2236,10 +2208,10 @@ def test_add_resource_string(self): self.addCleanup(harness.cleanup) harness.add_resource('image', 'foo contents\n') path = harness.model.resources.fetch('image') - self.assertEqual(path.name, 'foo.txt') - self.assertEqual(path.parent.name, 'image') + assert path.name == 'foo.txt' + assert path.parent.name == 'image' with path.open('rt') as f: - self.assertEqual('foo contents\n', f.read()) + assert f.read() == 'foo contents\n' def test_add_resource_bytes(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -2254,10 +2226,10 @@ def test_add_resource_bytes(self): raw_contents = b'\xff\xff\x00blah\n' harness.add_resource('image', raw_contents) path = harness.model.resources.fetch('image') - self.assertEqual(path.name, 'foo.zip') - self.assertEqual(path.parent.name, 'image') + assert path.name == 'foo.zip' + assert path.parent.name == 'image' with path.open('rb') as f: - self.assertEqual(raw_contents, f.read()) + assert raw_contents == f.read() def test_add_resource_unknown_filename(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -2270,8 +2242,8 @@ def test_add_resource_unknown_filename(self): self.addCleanup(harness.cleanup) harness.add_resource('image', 'foo contents\n') path = harness.model.resources.fetch('image') - self.assertEqual(path.name, 'image') - self.assertEqual(path.parent.name, 'image') + assert path.name == 'image' + assert path.parent.name == 'image' def test_get_pod_spec(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -2282,7 +2254,7 @@ def test_get_pod_spec(self): container_spec = {'container': 'spec'} k8s_resources = {'k8s': 'spec'} harness.model.pod.set_spec(container_spec, k8s_resources) - self.assertEqual(harness.get_pod_spec(), (container_spec, k8s_resources)) + assert harness.get_pod_spec() == (container_spec, k8s_resources) def test_begin_with_initial_hooks_no_relations(self): harness = ops.testing.Harness(RecordingCharm, meta=''' @@ -2296,19 +2268,17 @@ def test_begin_with_initial_hooks_no_relations(self): self.addCleanup(harness.cleanup) harness.update_config({'foo': 'bar'}) harness.set_leader(True) - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): _ = harness.charm harness.begin_with_initial_hooks() - self.assertIsNotNone(harness.charm) - self.assertEqual( - harness.charm.changes, + assert harness.charm is not None + assert harness.charm.changes == \ [ {'name': 'install'}, {'name': 'leader-elected'}, {'name': 'config-changed', 'data': {'foo': 'bar'}}, {'name': 'start'}, ] - ) def test_begin_with_initial_hooks_no_relations_not_leader(self): harness = ops.testing.Harness(RecordingCharm, meta=''' @@ -2321,19 +2291,17 @@ def test_begin_with_initial_hooks_no_relations_not_leader(self): ''') self.addCleanup(harness.cleanup) harness.update_config({'foo': 'bar'}) - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): _ = harness.charm harness.begin_with_initial_hooks() - self.assertIsNotNone(harness.charm) - self.assertEqual( - harness.charm.changes, + assert harness.charm is not None + assert harness.charm.changes == \ [ {'name': 'install'}, {'name': 'leader-settings-changed'}, {'name': 'config-changed', 'data': {'foo': 'bar'}}, {'name': 'start'}, ] - ) def test_begin_with_initial_hooks_with_peer_relation(self): class PeerCharm(RelationEventCharm): @@ -2353,15 +2321,14 @@ def __init__(self, framework: ops.Framework): ''') self.addCleanup(harness.cleanup) harness.update_config({'foo': 'bar'}) - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): _ = harness.charm harness.begin_with_initial_hooks() - self.assertIsNotNone(harness.charm) + assert harness.charm is not None rel = harness.model.get_relation('peer') assert rel is not None rel_id = rel.id - self.assertEqual( - harness.charm.changes, + assert harness.charm.changes == \ [ {'name': 'install'}, {'name': 'relation-created', @@ -2374,7 +2341,7 @@ def __init__(self, framework: ops.Framework): {'name': 'leader-settings-changed'}, {'name': 'config-changed', 'data': {'foo': 'bar'}}, {'name': 'start'}, - ]) + ] # With a single unit, no peer-relation-joined is fired def test_begin_with_initial_hooks_peer_relation_pre_defined(self): @@ -2393,8 +2360,7 @@ def __init__(self, framework: ops.Framework): harness.begin_with_initial_hooks() # If the peer relation is already defined by the user, we don't create the relation a # second time, but we do still fire relation-created. - self.assertEqual( - harness.charm.changes, + assert harness.charm.changes == \ [ {'name': 'install'}, {'name': 'relation-created', @@ -2407,7 +2373,7 @@ def __init__(self, framework: ops.Framework): {'name': 'leader-settings-changed'}, {'name': 'config-changed', 'data': {}}, {'name': 'start'}, - ]) + ] def test_begin_with_initial_hooks_relation_charm_with_no_relation(self): class CharmWithDB(RelationEventCharm): @@ -2423,14 +2389,13 @@ def __init__(self, framework: ops.Framework): self.addCleanup(harness.cleanup) harness.set_leader() harness.begin_with_initial_hooks() - self.assertEqual( - harness.charm.changes, + assert harness.charm.changes == \ [ {'name': 'install'}, {'name': 'leader-elected'}, {'name': 'config-changed', 'data': {}}, {'name': 'start'}, - ]) + ] def test_begin_with_initial_hooks_with_one_relation(self): class CharmWithDB(RelationEventCharm): @@ -2449,8 +2414,7 @@ def __init__(self, framework: ops.Framework): harness.add_relation_unit(rel_id, 'postgresql/0') harness.update_relation_data(rel_id, 'postgresql/0', {'new': 'data'}) harness.begin_with_initial_hooks() - self.assertEqual( - harness.charm.changes, + assert harness.charm.changes == \ [ {'name': 'install'}, {'name': 'relation-created', @@ -2477,7 +2441,7 @@ def __init__(self, framework: ops.Framework): 'unit': 'postgresql/0', 'app': 'postgresql', }}, - ]) + ] def test_begin_with_initial_hooks_with_application_data(self): class CharmWithDB(RelationEventCharm): @@ -2497,7 +2461,7 @@ def __init__(self, framework: ops.Framework): harness.update_relation_data(rel_id, 'postgresql/0', {'new': 'data'}) harness.update_relation_data(rel_id, 'postgresql', {'app': 'data'}) harness.begin_with_initial_hooks() - self.assertEqual( + assert harness.charm.changes == \ [ {'name': 'install'}, {'name': 'relation-created', @@ -2531,8 +2495,7 @@ def __init__(self, framework: ops.Framework): 'unit': 'postgresql/0', 'app': 'postgresql', }}, - ], - harness.charm.changes) + ] def test_begin_with_initial_hooks_with_multiple_units(self): class CharmWithDB(RelationEventCharm): @@ -2553,8 +2516,7 @@ def __init__(self, framework: ops.Framework): # We intentionally add 0 after 1 to assert that the code triggers them in order harness.add_relation_unit(rel_id, 'postgresql/0') harness.begin_with_initial_hooks() - self.assertEqual( - harness.charm.changes, + assert harness.charm.changes == \ [ {'name': 'install'}, {'name': 'relation-created', @@ -2595,7 +2557,7 @@ def __init__(self, framework: ops.Framework): 'unit': 'postgresql/1', 'app': 'postgresql', }}, - ]) + ] def test_begin_with_initial_hooks_multiple_relation_same_endpoint(self): class CharmWithDB(RelationEventCharm): @@ -2620,7 +2582,7 @@ def __init__(self, framework: ops.Framework): {'name': 'install'}, ] # The first events are always the same - self.assertEqual(changes[:len(expected_prefix)], expected_prefix) + assert changes[:len(expected_prefix)] == expected_prefix changes = changes[len(expected_prefix):] # However, the order of relation-created events can be in any order expected_relation_created = [ @@ -2643,14 +2605,14 @@ def __init__(self, framework: ops.Framework): # change the order expected_relation_created = [expected_relation_created[1], expected_relation_created[0]] - self.assertEqual(changes[:2], expected_relation_created) + assert changes[:2] == expected_relation_created changes = changes[2:] expected_middle: typing.List[typing.Dict[str, typing.Any]] = [ {'name': 'leader-elected'}, {'name': 'config-changed', 'data': {}}, {'name': 'start'}, ] - self.assertEqual(changes[:len(expected_middle)], expected_middle) + assert changes[:len(expected_middle)] == expected_middle changes = changes[len(expected_middle):] a_first = [ {'name': 'relation-joined', @@ -2684,7 +2646,7 @@ def __init__(self, framework: ops.Framework): ] if changes != a_first: b_first = [a_first[2], a_first[3], a_first[0], a_first[1]] - self.assertEqual(changes, b_first) + assert changes == b_first def test_begin_with_initial_hooks_unknown_status(self): # Verify that a charm that does not set a status in the install hook will have an @@ -2701,13 +2663,11 @@ def test_begin_with_initial_hooks_unknown_status(self): backend = harness._backend harness.begin_with_initial_hooks() - self.assertEqual( - backend.status_get(is_app=False), - {'status': 'unknown', 'message': ''}) + assert backend.status_get(is_app=False) == \ + {'status': 'unknown', 'message': ''} - self.assertEqual( - backend.status_get(is_app=True), - {'status': 'unknown', 'message': ''}) + assert backend.status_get(is_app=True) == \ + {'status': 'unknown', 'message': ''} def test_begin_with_initial_hooks_install_sets_status(self): harness = ops.testing.Harness(RecordingCharm, meta=''' @@ -2724,9 +2684,8 @@ def test_begin_with_initial_hooks_install_sets_status(self): harness.update_config(key_values={"set_status": True}) harness.begin_with_initial_hooks() - self.assertEqual( - backend.status_get(is_app=False), - {'status': 'maintenance', 'message': 'Status set on install'}) + assert backend.status_get(is_app=False) == \ + {'status': 'maintenance', 'message': 'Status set on install'} def test_get_pebble_container_plan(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -2739,7 +2698,7 @@ def test_get_pebble_container_plan(self): harness.begin() harness.set_can_connect('foo', True) initial_plan = harness.get_container_pebble_plan('foo') - self.assertEqual(initial_plan.to_yaml(), '{}\n') + assert initial_plan.to_yaml() == '{}\n' container = harness.model.unit.get_container('foo') container.pebble.add_layer('test-ab', '''\ summary: test-layer @@ -2757,7 +2716,7 @@ def test_get_pebble_container_plan(self): command: /bin/echo hello from c ''') plan = container.pebble.get_plan() - self.assertEqual(plan.to_yaml(), textwrap.dedent('''\ + assert plan.to_yaml() == textwrap.dedent('''\ services: a: command: /bin/echo hello from a @@ -2765,9 +2724,9 @@ def test_get_pebble_container_plan(self): command: /bin/echo hello from b c: command: /bin/echo hello from c - ''')) + ''') harness_plan = harness.get_container_pebble_plan('foo') - self.assertEqual(harness_plan.to_yaml(), plan.to_yaml()) + assert harness_plan.to_yaml() == plan.to_yaml() def test_add_layer_with_log_targets_to_plan(self): layer_yaml = '''\ @@ -2798,9 +2757,9 @@ def test_add_layer_with_log_targets_to_plan(self): plan = container.get_plan() - self.assertIsNotNone(plan.services.get('foo')) - self.assertIsNotNone(plan.checks.get('bar')) - self.assertIsNotNone(plan.log_targets.get('baz')) + assert plan.services.get('foo') is not None + assert plan.checks.get('bar') is not None + assert plan.log_targets.get('baz') is not None def test_get_pebble_container_plan_unknown(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -2812,10 +2771,10 @@ def test_get_pebble_container_plan_unknown(self): self.addCleanup(harness.cleanup) harness.begin() harness.set_can_connect('foo', True) - with self.assertRaises(KeyError): + with pytest.raises(KeyError): harness.get_container_pebble_plan('unknown') plan = harness.get_container_pebble_plan('foo') - self.assertEqual(plan.to_yaml(), "{}\n") + assert plan.to_yaml() == "{}\n" def test_container_pebble_ready(self): harness = ops.testing.Harness(ContainerEventCharm, meta=''' @@ -2830,14 +2789,12 @@ def test_container_pebble_ready(self): harness.begin() harness.charm.observe_container_events('foo') harness.container_pebble_ready('foo') - self.assertEqual( - harness.charm.changes, + assert harness.charm.changes == \ [ {'name': 'pebble-ready', 'container': 'foo', }, ] - ) def test_get_filesystem_root(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -2847,11 +2804,11 @@ def test_get_filesystem_root(self): resource: foo-image ''') foo_root = harness.get_filesystem_root("foo") - self.assertTrue(foo_root.exists()) - self.assertTrue(foo_root.is_dir()) + assert foo_root.exists() + assert foo_root.is_dir() harness.begin() container = harness.charm.unit.get_container("foo") - self.assertEqual(foo_root, harness.get_filesystem_root(container)) + assert foo_root == harness.get_filesystem_root(container) def test_evaluate_status(self): class TestCharm(ops.CharmBase): @@ -2873,29 +2830,29 @@ def _on_collect_unit_status(self, event: ops.CollectStatusEvent): harness.begin() # Tests for the behaviour of status evaluation are in test_charm.py harness.evaluate_status() - self.assertEqual(harness.model.app.status, ops.BlockedStatus('blocked app')) - self.assertEqual(harness.model.unit.status, ops.BlockedStatus('blocked unit')) + assert harness.model.app.status == ops.BlockedStatus('blocked app') + assert harness.model.unit.status == ops.BlockedStatus('blocked unit') harness.charm.app_status_to_add = ops.ActiveStatus('active app') harness.charm.unit_status_to_add = ops.ActiveStatus('active unit') harness.evaluate_status() - self.assertEqual(harness.model.app.status, ops.ActiveStatus('active app')) - self.assertEqual(harness.model.unit.status, ops.ActiveStatus('active unit')) + assert harness.model.app.status == ops.ActiveStatus('active app') + assert harness.model.unit.status == ops.ActiveStatus('active unit') def test_invalid_status_set(self): harness = ops.testing.Harness(ops.CharmBase) harness.set_leader(True) harness.begin() - with self.assertRaises(ops.model.ModelError): + with pytest.raises(ops.model.ModelError): harness.model.app.status = ops.UnknownStatus() - with self.assertRaises(ops.model.ModelError): + with pytest.raises(ops.model.ModelError): harness.model.app.status = ops.ErrorStatus() harness.model.app.status = ops.ActiveStatus() - with self.assertRaises(ops.model.ModelError): + with pytest.raises(ops.model.ModelError): harness.model.unit.status = ops.UnknownStatus() - with self.assertRaises(ops.model.ModelError): + with pytest.raises(ops.model.ModelError): harness.model.unit.status = ops.ErrorStatus() harness.model.unit.status = ops.ActiveStatus() @@ -2917,17 +2874,17 @@ def test_add_network_defaults(self): binding = self.harness.model.get_binding('db') assert binding is not None - self.assertEqual(binding.name, 'db') + assert binding.name == 'db' network = binding.network - self.assertEqual(network.bind_address, ipaddress.IPv4Address('10.0.0.10')) - self.assertEqual(network.ingress_address, ipaddress.IPv4Address('10.0.0.10')) - self.assertEqual(network.ingress_addresses, [ipaddress.IPv4Address('10.0.0.10')]) - self.assertEqual(network.egress_subnets, [ipaddress.IPv4Network('10.0.0.0/24')]) - self.assertEqual(len(network.interfaces), 1) + assert network.bind_address == ipaddress.IPv4Address('10.0.0.10') + assert network.ingress_address == ipaddress.IPv4Address('10.0.0.10') + assert network.ingress_addresses == [ipaddress.IPv4Address('10.0.0.10')] + assert network.egress_subnets == [ipaddress.IPv4Network('10.0.0.0/24')] + assert len(network.interfaces) == 1 interface = network.interfaces[0] - self.assertEqual(interface.name, 'eth0') - self.assertEqual(interface.address, ipaddress.IPv4Address('10.0.0.10')) - self.assertEqual(interface.subnet, ipaddress.IPv4Network('10.0.0.0/24')) + assert interface.name == 'eth0' + assert interface.address == ipaddress.IPv4Address('10.0.0.10') + assert interface.subnet == ipaddress.IPv4Network('10.0.0.0/24') def test_add_network_all_args(self): relation_id = self.harness.add_relation('db', 'postgresql') @@ -2943,20 +2900,20 @@ def test_add_network_all_args(self): assert relation is not None binding = self.harness.model.get_binding(relation) assert binding is not None - self.assertEqual(binding.name, 'db') + assert binding.name == 'db' network = binding.network - self.assertEqual(network.bind_address, ipaddress.IPv4Address('10.0.0.10')) - self.assertEqual(network.ingress_address, ipaddress.IPv4Address('10.0.0.1')) - self.assertEqual(network.ingress_addresses, - [ipaddress.IPv4Address('10.0.0.1'), ipaddress.IPv4Address('10.0.0.2')]) - self.assertEqual(network.egress_subnets, - [ipaddress.IPv4Network('10.0.0.0/8'), - ipaddress.IPv4Network('10.10.0.0/16')]) - self.assertEqual(len(network.interfaces), 1) + assert network.bind_address == ipaddress.IPv4Address('10.0.0.10') + assert network.ingress_address == ipaddress.IPv4Address('10.0.0.1') + assert network.ingress_addresses == \ + [ipaddress.IPv4Address('10.0.0.1'), ipaddress.IPv4Address('10.0.0.2')] + assert network.egress_subnets == \ + [ipaddress.IPv4Network('10.0.0.0/8'), + ipaddress.IPv4Network('10.10.0.0/16')] + assert len(network.interfaces) == 1 interface = network.interfaces[0] - self.assertEqual(interface.name, 'eth1') - self.assertEqual(interface.address, ipaddress.IPv4Address('10.0.0.10')) - self.assertEqual(interface.subnet, ipaddress.IPv4Network('10.0.0.0/8')) + assert interface.name == 'eth1' + assert interface.address == ipaddress.IPv4Address('10.0.0.10') + assert interface.subnet == ipaddress.IPv4Network('10.0.0.0/8') def test_add_network_specific_endpoint(self): self.harness.add_network('10.0.0.1') @@ -2964,15 +2921,15 @@ def test_add_network_specific_endpoint(self): binding = self.harness.model.get_binding('db') assert binding is not None - self.assertEqual(binding.name, 'db') + assert binding.name == 'db' network = binding.network - self.assertEqual(network.bind_address, ipaddress.IPv4Address('10.0.2.1')) + assert network.bind_address == ipaddress.IPv4Address('10.0.2.1') # Ensure binding for the other interface is still on the default value foo_binding = self.harness.model.get_binding('foo') assert foo_binding is not None - self.assertEqual(foo_binding.network.bind_address, - ipaddress.IPv4Address('10.0.0.1')) + assert foo_binding.network.bind_address == \ + ipaddress.IPv4Address('10.0.0.1') def test_add_network_specific_relation(self): self.harness.add_network('10.0.0.1') @@ -2984,15 +2941,15 @@ def test_add_network_specific_relation(self): assert relation is not None binding = self.harness.model.get_binding(relation) assert binding is not None - self.assertEqual(binding.name, 'db') + assert binding.name == 'db' network = binding.network - self.assertEqual(network.bind_address, ipaddress.IPv4Address('35.0.0.1')) + assert network.bind_address == ipaddress.IPv4Address('35.0.0.1') # Ensure binding for the other interface is still on the default value foo_binding = self.harness.model.get_binding('foo') assert foo_binding is not None - self.assertEqual(foo_binding.network.bind_address, - ipaddress.IPv4Address('10.0.0.1')) + assert foo_binding.network.bind_address == \ + ipaddress.IPv4Address('10.0.0.1') def test_add_network_endpoint_fallback(self): relation_id = self.harness.add_relation('db', 'postgresql') @@ -3002,38 +2959,38 @@ def test_add_network_endpoint_fallback(self): assert relation is not None binding = self.harness.model.get_binding(relation) assert binding is not None - self.assertEqual(binding.name, 'db') + assert binding.name == 'db' network = binding.network - self.assertEqual(network.bind_address, ipaddress.IPv4Address('10.0.0.10')) + assert network.bind_address == ipaddress.IPv4Address('10.0.0.10') def test_add_network_default_fallback(self): self.harness.add_network('10.0.0.10') binding = self.harness.model.get_binding('db') assert binding is not None - self.assertEqual(binding.name, 'db') + assert binding.name == 'db' network = binding.network - self.assertEqual(network.bind_address, ipaddress.IPv4Address('10.0.0.10')) + assert network.bind_address == ipaddress.IPv4Address('10.0.0.10') def test_add_network_ipv6(self): self.harness.add_network('2001:0db8::a:0:0:1') binding = self.harness.model.get_binding('db') assert binding is not None - self.assertEqual(binding.name, 'db') + assert binding.name == 'db' network = binding.network - self.assertEqual(network.bind_address, ipaddress.IPv6Address('2001:0db8::a:0:0:1')) - self.assertEqual(network.ingress_address, ipaddress.IPv6Address('2001:0db8::a:0:0:1')) - self.assertEqual(network.ingress_addresses, [ipaddress.IPv6Address('2001:0db8::a:0:0:1')]) - self.assertEqual(network.egress_subnets, [ipaddress.IPv6Network('2001:0db8::0:0:0:0/64')]) - self.assertEqual(len(network.interfaces), 1) + assert network.bind_address == ipaddress.IPv6Address('2001:0db8::a:0:0:1') + assert network.ingress_address == ipaddress.IPv6Address('2001:0db8::a:0:0:1') + assert network.ingress_addresses == [ipaddress.IPv6Address('2001:0db8::a:0:0:1')] + assert network.egress_subnets == [ipaddress.IPv6Network('2001:0db8::0:0:0:0/64')] + assert len(network.interfaces) == 1 interface = network.interfaces[0] - self.assertEqual(interface.name, 'eth0') - self.assertEqual(interface.address, ipaddress.IPv6Address('2001:0db8::a:0:0:1')) - self.assertEqual(interface.subnet, ipaddress.IPv6Network('2001:0db8::0:0:0:0/64')) + assert interface.name == 'eth0' + assert interface.address == ipaddress.IPv6Address('2001:0db8::a:0:0:1') + assert interface.subnet == ipaddress.IPv6Network('2001:0db8::0:0:0:0/64') def test_network_get_relation_not_found(self): - with self.assertRaises(ops.RelationNotFoundError): + with pytest.raises(ops.RelationNotFoundError): binding = self.harness.model.get_binding('db') assert binding is not None binding.network @@ -3045,22 +3002,22 @@ def test_add_relation_network_get(self): assert binding.network def test_add_network_endpoint_not_in_meta(self): - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.harness.add_network('35.0.0.1', endpoint='xyz') def test_add_network_relation_id_set_endpoint_not_set(self): relation_id = self.harness.add_relation('db', 'postgresql') - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.harness.add_network('35.0.0.1', relation_id=relation_id) def test_add_network_relation_id_incorrect(self): relation_id = self.harness.add_relation('db', 'postgresql') - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.harness.add_network('35.0.0.1', endpoint='db', relation_id=relation_id + 1) def test_add_network_endpoint_and_relation_id_do_not_correspond(self): relation_id = self.harness.add_relation('db', 'postgresql') - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.harness.add_network('35.0.0.1', endpoint='foo', relation_id=relation_id) @@ -3263,7 +3220,7 @@ def test_conforms_to_model_backend(self): backend = harness._backend mb_methods = get_public_methods(_ModelBackend) backend_methods = get_public_methods(backend) - self.assertEqual(mb_methods, backend_methods) + assert mb_methods == backend_methods def test_model_uuid_is_uuid_v4(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -3271,7 +3228,7 @@ def test_model_uuid_is_uuid_v4(self): ''') self.addCleanup(harness.cleanup) backend = harness._backend - self.assertEqual(uuid.UUID(backend.model_uuid).version, 4) + assert uuid.UUID(backend.model_uuid).version == 4 def test_status_set_get_unit(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -3280,12 +3237,10 @@ def test_status_set_get_unit(self): self.addCleanup(harness.cleanup) backend = harness._backend backend.status_set('blocked', 'message', is_app=False) - self.assertEqual( - backend.status_get(is_app=False), - {'status': 'blocked', 'message': 'message'}) - self.assertEqual( - backend.status_get(is_app=True), - {'status': 'unknown', 'message': ''}) + assert backend.status_get(is_app=False) == \ + {'status': 'blocked', 'message': 'message'} + assert backend.status_get(is_app=True) == \ + {'status': 'unknown', 'message': ''} def test_status_set_get_app(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -3294,12 +3249,10 @@ def test_status_set_get_app(self): self.addCleanup(harness.cleanup) backend = harness._backend backend.status_set('blocked', 'message', is_app=True) - self.assertEqual( - backend.status_get(is_app=True), - {'status': 'blocked', 'message': 'message'}) - self.assertEqual( - backend.status_get(is_app=False), - {'status': 'maintenance', 'message': ''}) + assert backend.status_get(is_app=True) == \ + {'status': 'blocked', 'message': 'message'} + assert backend.status_get(is_app=False) == \ + {'status': 'maintenance', 'message': ''} def test_relation_ids_unknown_relation(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -3311,9 +3264,9 @@ def test_relation_ids_unknown_relation(self): self.addCleanup(harness.cleanup) backend = harness._backend # With no relations added, we just get an empty list for the interface - self.assertEqual(backend.relation_ids('db'), []) + assert backend.relation_ids('db') == [] # But an unknown interface raises a ModelError - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): backend.relation_ids('unknown') def test_relation_get_unknown_relation_id(self): @@ -3322,7 +3275,7 @@ def test_relation_get_unknown_relation_id(self): ''') self.addCleanup(harness.cleanup) backend = harness._backend - with self.assertRaises(ops.RelationNotFoundError): + with pytest.raises(ops.RelationNotFoundError): backend.relation_get(1234, 'unit/0', False) def test_relation_list_unknown_relation_id(self): @@ -3331,7 +3284,7 @@ def test_relation_list_unknown_relation_id(self): ''') self.addCleanup(harness.cleanup) backend = harness._backend - with self.assertRaises(ops.RelationNotFoundError): + with pytest.raises(ops.RelationNotFoundError): backend.relation_list(1234) def test_lazy_resource_directory(self): @@ -3345,12 +3298,11 @@ def test_lazy_resource_directory(self): self.addCleanup(harness.cleanup) harness.populate_oci_resources() backend = harness._backend - self.assertIsNone(backend._resource_dir) + assert backend._resource_dir is None path = backend.resource_get('image') - self.assertIsNotNone(backend._resource_dir) - self.assertTrue( - str(path).startswith(str(backend._resource_dir.name)), - msg=f'expected {path} to be a subdirectory of {backend._resource_dir.name}') + assert backend._resource_dir is not None + assert str(path).startswith(str(backend._resource_dir.name)), \ + f'expected {path} to be a subdirectory of {backend._resource_dir.name}' def test_resource_get_no_resource(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -3362,11 +3314,10 @@ def test_resource_get_no_resource(self): ''') self.addCleanup(harness.cleanup) backend = harness._backend - with self.assertRaises(ops.ModelError) as cm: + with pytest.raises(ops.ModelError) as excinfo: backend.resource_get('foo') - self.assertIn( - "units/unit-test-app-0/resources/foo: resource#test-app/foo not found", - str(cm.exception)) + assert "units/unit-test-app-0/resources/foo: resource#test-app/foo not found" in \ + str(excinfo.value) def test_relation_remote_app_name(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -3378,15 +3329,15 @@ def test_relation_remote_app_name(self): self.addCleanup(harness.cleanup) backend = harness._backend - self.assertIs(backend.relation_remote_app_name(1), None) + assert backend.relation_remote_app_name(1) is None rel_id = harness.add_relation('db', 'postgresql') - self.assertEqual(backend.relation_remote_app_name(rel_id), 'postgresql') + assert backend.relation_remote_app_name(rel_id) == 'postgresql' harness.add_relation_unit(rel_id, 'postgresql/0') harness.add_relation_unit(rel_id, 'postgresql/1') - self.assertEqual(backend.relation_remote_app_name(rel_id), 'postgresql') + assert backend.relation_remote_app_name(rel_id) == 'postgresql' - self.assertIs(backend.relation_remote_app_name(7), None) + assert backend.relation_remote_app_name(7) is None def test_get_pebble_methods(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' @@ -3396,7 +3347,7 @@ def test_get_pebble_methods(self): backend = harness._backend client = backend.get_pebble('/custom/socket/path') - self.assertIsInstance(client, _TestingPebbleClient) + assert isinstance(client, _TestingPebbleClient) def test_reboot(self): class RebootingCharm(ops.CharmBase): @@ -3415,19 +3366,19 @@ def _reboot(self, event: ops.RemoveEvent): name: test-app ''') self.addCleanup(harness.cleanup) - self.assertEqual(harness.reboot_count, 0) + assert harness.reboot_count == 0 backend = harness._backend backend.reboot() - self.assertEqual(harness.reboot_count, 1) - with self.assertRaises(SystemExit): + assert harness.reboot_count == 1 + with pytest.raises(SystemExit): backend.reboot(now=True) - self.assertEqual(harness.reboot_count, 2) + assert harness.reboot_count == 2 harness.begin() - with self.assertRaises(SystemExit): + with pytest.raises(SystemExit): harness.charm.on.install.emit() - self.assertEqual(harness.reboot_count, 3) + assert harness.reboot_count == 3 harness.charm.on.remove.emit() - self.assertEqual(harness.reboot_count, 4) + assert harness.reboot_count == 4 class _TestingPebbleClientMixin: @@ -3450,16 +3401,16 @@ class TestTestingPebbleClient(unittest.TestCase, _TestingPebbleClientMixin): def test_methods_match_pebble_client(self): client = self.get_testing_client() - self.assertIsNotNone(client) + assert client is not None pebble_client_methods = get_public_methods(pebble.Client) testing_client_methods = get_public_methods(client) - self.assertEqual(pebble_client_methods, testing_client_methods) + assert pebble_client_methods == testing_client_methods def test_add_layer(self): client = self.get_testing_client() plan = client.get_plan() - self.assertIsInstance(plan, pebble.Plan) - self.assertEqual('{}\n', plan.to_yaml()) + assert isinstance(plan, pebble.Plan) + assert plan.to_yaml() == '{}\n' client.add_layer('foo', pebble.Layer('''\ summary: Foo description: | @@ -3477,7 +3428,7 @@ def test_add_layer(self): ''')) plan = client.get_plan() # The YAML should be normalized - self.assertEqual(textwrap.dedent('''\ + assert textwrap.dedent('''\ services: serv: command: /bin/echo hello @@ -3489,13 +3440,13 @@ def test_add_layer(self): override: replace startup: enabled summary: Serv - '''), plan.to_yaml()) + ''') == plan.to_yaml() def test_add_layer_merge(self): client = self.get_testing_client() plan = client.get_plan() - self.assertIsInstance(plan, pebble.Plan) - self.assertEqual('{}\n', plan.to_yaml()) + assert isinstance(plan, pebble.Plan) + assert plan.to_yaml() == '{}\n' client.add_layer('foo', pebble.Layer('''\ summary: Foo description: | @@ -3531,7 +3482,7 @@ def test_add_layer_merge(self): plan = client.get_plan() # The YAML should be normalized self.maxDiff = None - self.assertEqual(textwrap.dedent('''\ + assert textwrap.dedent('''\ services: serv: after: @@ -3560,7 +3511,7 @@ def test_add_layer_merge(self): summary: Serv user: user1 user-id: userID1 - '''), plan.to_yaml()) + ''') == plan.to_yaml() client.add_layer('foo', pebble.Layer('''\ summary: Foo @@ -3600,7 +3551,7 @@ def test_add_layer_merge(self): '''), combine=True) plan = client.get_plan() # The YAML should be normalized - self.assertEqual(textwrap.dedent('''\ + assert textwrap.dedent('''\ services: serv: after: @@ -3636,13 +3587,13 @@ def test_add_layer_merge(self): summary: Serv user: user2 user-id: userID2 - '''), plan.to_yaml()) + ''') == plan.to_yaml() def test_add_layer_not_combined(self): client = self.get_testing_client() plan = client.get_plan() - self.assertIsInstance(plan, pebble.Plan) - self.assertEqual('{}\n', plan.to_yaml()) + assert isinstance(plan, pebble.Plan) + assert plan.to_yaml() == '{}\n' service = textwrap.dedent('''\ summary: Foo description: | @@ -3663,7 +3614,7 @@ def test_add_layer_not_combined(self): # pebble raises an HTTP exception. See https://github.com/canonical/operator/issues/514 # that this should be cleaned up into a clearer error type, however, they should get an # error - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): client.add_layer('foo', pebble.Layer(service)) def test_add_layer_three_services(self): @@ -3698,7 +3649,7 @@ def test_add_layer_three_services(self): plan = client.get_plan() self.maxDiff = 1000 # Alphabetical services, and the YAML should be normalized - self.assertEqual(textwrap.dedent('''\ + assert textwrap.dedent('''\ services: bar: command: /bin/echo bar @@ -3715,7 +3666,7 @@ def test_add_layer_three_services(self): override: replace startup: enabled summary: Foo - '''), plan.to_yaml()) + ''') == plan.to_yaml() def test_add_layer_combine_no_override(self): client = self.get_testing_client() @@ -3729,7 +3680,7 @@ def test_add_layer_combine_no_override(self): # TODO: jam 2021-04-19 Pebble currently raises a HTTP Error 500 Internal Service Error # if you don't supply an override directive. That needs to be fixed and this test # should be updated. https://github.com/canonical/operator/issues/514 - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): client.add_layer('foo', '''\ summary: foo services: @@ -3757,7 +3708,7 @@ def test_add_layer_combine_override_replace(self): command: '/bin/echo foo new' override: replace ''', combine=True) - self.assertEqual(textwrap.dedent('''\ + assert textwrap.dedent('''\ services: bar: command: /bin/echo bar @@ -3765,7 +3716,7 @@ def test_add_layer_combine_override_replace(self): foo: command: /bin/echo foo new override: replace - '''), client.get_plan().to_yaml()) + ''') == client.get_plan().to_yaml() def test_add_layer_combine_override_merge(self): client = self.get_testing_client() @@ -3787,7 +3738,7 @@ def test_add_layer_combine_override_merge(self): command: '/bin/echo foob' override: merge ''', combine=True) - self.assertEqual(textwrap.dedent('''\ + assert textwrap.dedent('''\ services: bar: command: /bin/echo bar @@ -3796,7 +3747,7 @@ def test_add_layer_combine_override_merge(self): command: /bin/echo foob override: merge summary: Foo - '''), client.get_plan().to_yaml()) + ''') == client.get_plan().to_yaml() def test_add_layer_combine_override_unknown(self): client = self.get_testing_client() @@ -3810,7 +3761,7 @@ def test_add_layer_combine_override_unknown(self): summary: Foo command: '/bin/echo foo' ''') - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): client.add_layer('foo', '''\ summary: foo services: @@ -3823,7 +3774,7 @@ def test_add_layer_combine_override_unknown(self): def test_get_services_none(self): client = self.get_testing_client() service_info = client.get_services() - self.assertEqual([], service_info) + assert service_info == [] def test_get_services_not_started(self): client = self.get_testing_client() @@ -3839,18 +3790,18 @@ def test_get_services_not_started(self): command: '/bin/echo bar' ''') infos = client.get_services() - self.assertEqual(len(infos), 2) + assert len(infos) == 2 bar_info = infos[0] - self.assertEqual('bar', bar_info.name) + assert bar_info.name == 'bar' # Default when not specified is DISABLED - self.assertEqual(pebble.ServiceStartup.DISABLED, bar_info.startup) - self.assertEqual(pebble.ServiceStatus.INACTIVE, bar_info.current) - self.assertFalse(bar_info.is_running()) + assert bar_info.startup == pebble.ServiceStartup.DISABLED + assert bar_info.current == pebble.ServiceStatus.INACTIVE + assert not bar_info.is_running() foo_info = infos[1] - self.assertEqual('foo', foo_info.name) - self.assertEqual(pebble.ServiceStartup.ENABLED, foo_info.startup) - self.assertEqual(pebble.ServiceStatus.INACTIVE, foo_info.current) - self.assertFalse(foo_info.is_running()) + assert foo_info.name == 'foo' + assert foo_info.startup == pebble.ServiceStartup.ENABLED + assert foo_info.current == pebble.ServiceStatus.INACTIVE + assert not foo_info.is_running() def test_get_services_autostart(self): client = self.get_testing_client() @@ -3867,18 +3818,18 @@ def test_get_services_autostart(self): ''') client.autostart_services() infos = client.get_services() - self.assertEqual(len(infos), 2) + assert len(infos) == 2 bar_info = infos[0] - self.assertEqual('bar', bar_info.name) + assert bar_info.name == 'bar' # Default when not specified is DISABLED - self.assertEqual(pebble.ServiceStartup.DISABLED, bar_info.startup) - self.assertEqual(pebble.ServiceStatus.INACTIVE, bar_info.current) - self.assertFalse(bar_info.is_running()) + assert bar_info.startup == pebble.ServiceStartup.DISABLED + assert bar_info.current == pebble.ServiceStatus.INACTIVE + assert not bar_info.is_running() foo_info = infos[1] - self.assertEqual('foo', foo_info.name) - self.assertEqual(pebble.ServiceStartup.ENABLED, foo_info.startup) - self.assertEqual(pebble.ServiceStatus.ACTIVE, foo_info.current) - self.assertTrue(foo_info.is_running()) + assert foo_info.name == 'foo' + assert foo_info.startup == pebble.ServiceStartup.ENABLED + assert foo_info.current == pebble.ServiceStatus.ACTIVE + assert foo_info.is_running() def test_get_services_start_stop(self): client = self.get_testing_client() @@ -3895,23 +3846,23 @@ def test_get_services_start_stop(self): ''') client.start_services(['bar']) infos = client.get_services() - self.assertEqual(len(infos), 2) + assert len(infos) == 2 bar_info = infos[0] - self.assertEqual('bar', bar_info.name) + assert bar_info.name == 'bar' # Even though bar defaults to DISABLED, we explicitly started it - self.assertEqual(pebble.ServiceStartup.DISABLED, bar_info.startup) - self.assertEqual(pebble.ServiceStatus.ACTIVE, bar_info.current) + assert bar_info.startup == pebble.ServiceStartup.DISABLED + assert bar_info.current == pebble.ServiceStatus.ACTIVE # foo would be started by autostart, but we only called start_services foo_info = infos[1] - self.assertEqual('foo', foo_info.name) - self.assertEqual(pebble.ServiceStartup.ENABLED, foo_info.startup) - self.assertEqual(pebble.ServiceStatus.INACTIVE, foo_info.current) + assert foo_info.name == 'foo' + assert foo_info.startup == pebble.ServiceStartup.ENABLED + assert foo_info.current == pebble.ServiceStatus.INACTIVE client.stop_services(['bar']) infos = client.get_services() bar_info = infos[0] - self.assertEqual('bar', bar_info.name) - self.assertEqual(pebble.ServiceStartup.DISABLED, bar_info.startup) - self.assertEqual(pebble.ServiceStatus.INACTIVE, bar_info.current) + assert bar_info.name == 'bar' + assert bar_info.startup == pebble.ServiceStartup.DISABLED + assert bar_info.current == pebble.ServiceStatus.INACTIVE def test_get_services_bad_request(self): client = self.get_testing_client() @@ -3928,7 +3879,7 @@ def test_get_services_bad_request(self): ''') # It is a common mistake to pass just a name vs a list of names, so catch it with a # TypeError - with self.assertRaises(TypeError): + with pytest.raises(TypeError): client.get_services('foo') def test_get_services_subset(self): @@ -3945,11 +3896,11 @@ def test_get_services_subset(self): command: '/bin/echo bar' ''') infos = client.get_services(['foo']) - self.assertEqual(len(infos), 1) + assert len(infos) == 1 foo_info = infos[0] - self.assertEqual('foo', foo_info.name) - self.assertEqual(pebble.ServiceStartup.ENABLED, foo_info.startup) - self.assertEqual(pebble.ServiceStatus.INACTIVE, foo_info.current) + assert foo_info.name == 'foo' + assert foo_info.startup == pebble.ServiceStartup.ENABLED + assert foo_info.current == pebble.ServiceStatus.INACTIVE def test_get_services_unknown(self): client = self.get_testing_client() @@ -3968,26 +3919,26 @@ def test_get_services_unknown(self): # pebble_cli.py service just returns an empty list # pebble service unknown says "No matching services" (but exits 0) infos = client.get_services(['unknown']) - self.assertEqual(infos, []) + assert infos == [] def test_invalid_start_service(self): client = self.get_testing_client() # TODO: jam 2021-04-20 This should become a better error - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): client.start_services(['unknown']) def test_start_service_str(self): # Start service takes a list of names, but it is really easy to accidentally pass just a # name client = self.get_testing_client() - with self.assertRaises(TypeError): + with pytest.raises(TypeError): client.start_services('unknown') def test_stop_service_str(self): # Start service takes a list of names, but it is really easy to accidentally pass just a # name client = self.get_testing_client() - with self.assertRaises(TypeError): + with pytest.raises(TypeError): client.stop_services('unknown') def test_mixed_start_service(self): @@ -4001,15 +3952,15 @@ def test_mixed_start_service(self): command: '/bin/echo foo' ''') # TODO: jam 2021-04-20 better error type - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): client.start_services(['foo', 'unknown']) # foo should not be started infos = client.get_services() - self.assertEqual(len(infos), 1) + assert len(infos) == 1 foo_info = infos[0] - self.assertEqual('foo', foo_info.name) - self.assertEqual(pebble.ServiceStartup.ENABLED, foo_info.startup) - self.assertEqual(pebble.ServiceStatus.INACTIVE, foo_info.current) + assert foo_info.name == 'foo' + assert foo_info.startup == pebble.ServiceStartup.ENABLED + assert foo_info.current == pebble.ServiceStatus.INACTIVE def test_stop_services_unknown(self): client = self.get_testing_client() @@ -4023,15 +3974,15 @@ def test_stop_services_unknown(self): ''') client.autostart_services() # TODO: jam 2021-04-20 better error type - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): client.stop_services(['foo', 'unknown']) # foo should still be running infos = client.get_services() - self.assertEqual(len(infos), 1) + assert len(infos) == 1 foo_info = infos[0] - self.assertEqual('foo', foo_info.name) - self.assertEqual(pebble.ServiceStartup.ENABLED, foo_info.startup) - self.assertEqual(pebble.ServiceStatus.ACTIVE, foo_info.current) + assert foo_info.name == 'foo' + assert foo_info.startup == pebble.ServiceStartup.ENABLED + assert foo_info.current == pebble.ServiceStatus.ACTIVE def test_start_started_service(self): # Pebble maintains idempotency even if you start a service @@ -4053,16 +4004,16 @@ def test_start_started_service(self): client.start_services(['bar', 'foo']) # foo and bar are both started infos = client.get_services() - self.assertEqual(len(infos), 2) + assert len(infos) == 2 bar_info = infos[0] - self.assertEqual('bar', bar_info.name) + assert bar_info.name == 'bar' # Default when not specified is DISABLED - self.assertEqual(pebble.ServiceStartup.DISABLED, bar_info.startup) - self.assertEqual(pebble.ServiceStatus.ACTIVE, bar_info.current) + assert bar_info.startup == pebble.ServiceStartup.DISABLED + assert bar_info.current == pebble.ServiceStatus.ACTIVE foo_info = infos[1] - self.assertEqual('foo', foo_info.name) - self.assertEqual(pebble.ServiceStartup.ENABLED, foo_info.startup) - self.assertEqual(pebble.ServiceStatus.ACTIVE, foo_info.current) + assert foo_info.name == 'foo' + assert foo_info.startup == pebble.ServiceStartup.ENABLED + assert foo_info.current == pebble.ServiceStatus.ACTIVE def test_stop_stopped_service(self): # Pebble maintains idempotency even if you stop a service @@ -4084,16 +4035,16 @@ def test_stop_stopped_service(self): client.stop_services(['foo', 'bar']) # foo and bar are both stopped infos = client.get_services() - self.assertEqual(len(infos), 2) + assert len(infos) == 2 bar_info = infos[0] - self.assertEqual('bar', bar_info.name) + assert bar_info.name == 'bar' # Default when not specified is DISABLED - self.assertEqual(pebble.ServiceStartup.DISABLED, bar_info.startup) - self.assertEqual(pebble.ServiceStatus.INACTIVE, bar_info.current) + assert bar_info.startup == pebble.ServiceStartup.DISABLED + assert bar_info.current == pebble.ServiceStatus.INACTIVE foo_info = infos[1] - self.assertEqual('foo', foo_info.name) - self.assertEqual(pebble.ServiceStartup.ENABLED, foo_info.startup) - self.assertEqual(pebble.ServiceStatus.INACTIVE, foo_info.current) + assert foo_info.name == 'foo' + assert foo_info.startup == pebble.ServiceStartup.ENABLED + assert foo_info.current == pebble.ServiceStatus.INACTIVE @ unittest.skipUnless(is_linux, 'Pebble runs on Linux') def test_send_signal(self): @@ -4116,23 +4067,23 @@ def test_send_signal(self): client.send_signal("SIGINT", ("foo",)) # Send a valid signal but omit service name - with self.assertRaises(TypeError): + with pytest.raises(TypeError): client.send_signal("SIGINT", tuple()) # Send an invalid signal to a running service - with self.assertRaises(pebble.APIError): + with pytest.raises(pebble.APIError): client.send_signal("sigint", ("foo",)) # Send a valid signal to a stopped service - with self.assertRaises(pebble.APIError): + with pytest.raises(pebble.APIError): client.send_signal("SIGINT", ("bar",)) # Send a valid signal to a non-existing service - with self.assertRaises(pebble.APIError): + with pytest.raises(pebble.APIError): client.send_signal("SIGINT", ("baz",)) # Send a valid signal to a multiple services, one of which is not running - with self.assertRaises(pebble.APIError): + with pytest.raises(pebble.APIError): client.send_signal("SIGINT", ("foo", "bar",)) @@ -4178,7 +4129,7 @@ def _test_push_and_pull_data(self, client.push(f"{self.prefix}/test", original_data, encoding=encoding) with client.pull(f"{self.prefix}/test", encoding=encoding) as infile: received_data = infile.read() - self.assertEqual(original_data, received_data) + assert original_data == received_data # We also support file-like objects as input, so let's test that case as well. if encoding is None: @@ -4191,7 +4142,7 @@ def _test_push_and_pull_data(self, client.push(f"{self.prefix}/test", small_file, encoding=encoding) with client.pull(f"{self.prefix}/test", encoding=encoding) as infile: received_data = infile.read() - self.assertEqual(original_data, received_data) + assert original_data == received_data def test_push_bytes_ignore_encoding(self): # push() encoding param should be ignored if source is bytes @@ -4199,7 +4150,7 @@ def test_push_bytes_ignore_encoding(self): client.push(f"{self.prefix}/test", b'\x00\x01', encoding='utf-8') with client.pull(f"{self.prefix}/test", encoding=None) as infile: received_data = infile.read() - self.assertEqual(received_data, b'\x00\x01') + assert received_data == b'\x00\x01' def test_push_bytesio_ignore_encoding(self): # push() encoding param should be ignored if source is binary stream @@ -4207,7 +4158,7 @@ def test_push_bytesio_ignore_encoding(self): client.push(f"{self.prefix}/test", io.BytesIO(b'\x00\x01'), encoding='utf-8') with client.pull(f"{self.prefix}/test", encoding=None) as infile: received_data = infile.read() - self.assertEqual(received_data, b'\x00\x01') + assert received_data == b'\x00\x01' def test_push_and_pull_larger_file(self): # Intent: to ensure things work appropriately with larger files. @@ -4220,15 +4171,15 @@ def test_push_and_pull_larger_file(self): client.push(f"{self.prefix}/test", original_data) with client.pull(f"{self.prefix}/test", encoding=None) as infile: received_data = infile.read() - self.assertEqual(original_data, received_data) + assert original_data == received_data def test_push_to_non_existent_subdir(self): data = 'data' client = self.client - with self.assertRaises(pebble.PathError) as cm: + with pytest.raises(pebble.PathError) as excinfo: client.push(f"{self.prefix}/nonexistent_dir/test", data, make_dirs=False) - self.assertEqual(cm.exception.kind, 'not-found') + assert excinfo.value.kind == 'not-found' client.push(f"{self.prefix}/nonexistent_dir/test", data, make_dirs=True) @@ -4236,9 +4187,9 @@ def test_push_as_child_of_file_raises_error(self): data = 'data' client = self.client client.push(f"{self.prefix}/file", data) - with self.assertRaises(pebble.PathError) as cm: + with pytest.raises(pebble.PathError) as excinfo: client.push(f"{self.prefix}/file/file", data) - self.assertEqual(cm.exception.kind, 'generic-file-error') + assert excinfo.value.kind == 'generic-file-error' def test_push_with_permission_mask(self): data = 'data' @@ -4251,9 +4202,9 @@ def test_push_with_permission_mask(self): 0o1000, # Exceeds 0o777 -1, # Less than 0o000 ): - with self.assertRaises(pebble.PathError) as cm: + with pytest.raises(pebble.PathError) as excinfo: client.push(f"{self.prefix}/file", data, permissions=bad_permission) - self.assertEqual(cm.exception.kind, 'generic-file-error') + assert excinfo.value.kind == 'generic-file-error' def test_push_files_and_list(self): data = 'data' @@ -4269,16 +4220,16 @@ def test_push_files_and_list(self): client.push(f"{self.prefix}/file3", data) files = client.list_files(f"{self.prefix}/") - self.assertEqual({file.path for file in files}, - {self.prefix + file for file in ('/file1', '/file2', '/file3')}) + assert {file.path for file in files} == \ + {self.prefix + file for file in ('/file1', '/file2', '/file3')} # Let's pull the first file again and check its details file = [f for f in files if f.path == f"{self.prefix}/file1"][0] - self.assertEqual(file.name, 'file1') - self.assertEqual(file.type, pebble.FileType.FILE) - self.assertEqual(file.size, 4) - self.assertIsInstance(file.last_modified, datetime.datetime) - self.assertEqual(file.permissions, 0o620) + assert file.name == 'file1' + assert file.type == pebble.FileType.FILE + assert file.size == 4 + assert isinstance(file.last_modified, datetime.datetime) + assert file.permissions == 0o620 # Skipping ownership checks here; ownership will be checked in purely-mocked tests def test_push_and_list_file(self): @@ -4286,35 +4237,35 @@ def test_push_and_list_file(self): client = self.client client.push(f"{self.prefix}/file", data) files = client.list_files(f"{self.prefix}/") - self.assertEqual({file.path for file in files}, {f"{self.prefix}/file"}) + assert {file.path for file in files} == {f"{self.prefix}/file"} def test_push_file_with_relative_path_fails(self): client = self.client - with self.assertRaises(pebble.PathError) as cm: + with pytest.raises(pebble.PathError) as excinfo: client.push('file', '') - self.assertEqual(cm.exception.kind, 'generic-file-error') + assert excinfo.value.kind == 'generic-file-error' def test_pull_not_found(self): - with self.assertRaises(pebble.PathError) as cm: + with pytest.raises(pebble.PathError) as excinfo: self.client.pull("/not/found") - self.assertEqual(cm.exception.kind, "not-found") - self.assertIn("/not/found", cm.exception.message) + assert excinfo.value.kind == "not-found" + assert "/not/found" in excinfo.value.message def test_pull_directory(self): self.client.make_dir(f"{self.prefix}/subdir") - with self.assertRaises(pebble.PathError) as cm: + with pytest.raises(pebble.PathError) as excinfo: self.client.pull(f"{self.prefix}/subdir") - self.assertEqual(cm.exception.kind, "generic-file-error") - self.assertIn(f"{self.prefix}/subdir", cm.exception.message) + assert excinfo.value.kind == "generic-file-error" + assert f"{self.prefix}/subdir" in excinfo.value.message def test_list_files_not_found_raises(self): client = self.client - with self.assertRaises(pebble.APIError) as cm: + with pytest.raises(pebble.APIError) as excinfo: client.list_files("/not/existing/file/") - self.assertEqual(cm.exception.code, 404) - self.assertEqual(cm.exception.status, 'Not Found') - self.assertEqual(cm.exception.message, 'stat /not/existing/file/: no ' - 'such file or directory') + assert excinfo.value.code == 404 + assert excinfo.value.status == 'Not Found' + assert excinfo.value.message == 'stat /not/existing/file/: no ' \ + 'such file or directory' def test_list_directory_object_itself(self): client = self.client @@ -4322,19 +4273,19 @@ def test_list_directory_object_itself(self): # Test with root dir # (Special case; we won't prefix this, even when using the real Pebble server.) files = client.list_files('/', itself=True) - self.assertEqual(len(files), 1) + assert len(files) == 1 dir_ = files[0] - self.assertEqual(dir_.path, '/') - self.assertEqual(dir_.name, '/') - self.assertEqual(dir_.type, pebble.FileType.DIRECTORY) + assert dir_.path == '/' + assert dir_.name == '/' + assert dir_.type == pebble.FileType.DIRECTORY # Test with subdirs client.make_dir(f"{self.prefix}/subdir") files = client.list_files(f"{self.prefix}/subdir", itself=True) - self.assertEqual(len(files), 1) + assert len(files) == 1 dir_ = files[0] - self.assertEqual(dir_.name, 'subdir') - self.assertEqual(dir_.type, pebble.FileType.DIRECTORY) + assert dir_.name == 'subdir' + assert dir_.type == pebble.FileType.DIRECTORY def test_push_files_and_list_by_pattern(self): # Note: glob pattern deltas do exist between golang and Python, but here, @@ -4349,51 +4300,48 @@ def test_push_files_and_list_by_pattern(self): ): client.push(self.prefix + filename, data) files = client.list_files(f"{self.prefix}/", pattern='file*.gz') - self.assertEqual({file.path for file in files}, - {self.prefix + file for file in ('/file1.gz', '/file2.tar.gz')}) + assert {file.path for file in files} == \ + {self.prefix + file for file in ('/file1.gz', '/file2.tar.gz')} def test_make_directory(self): client = self.client client.make_dir(f"{self.prefix}/subdir") - self.assertEqual( - client.list_files(f"{self.prefix}/", pattern='subdir')[0].path, - f"{self.prefix}/subdir") + assert client.list_files(f"{self.prefix}/", pattern='subdir')[0].path == \ + f"{self.prefix}/subdir" client.make_dir(f"{self.prefix}/subdir/subdir") - self.assertEqual( - client.list_files(f"{self.prefix}/subdir", pattern='subdir')[0].path, - f"{self.prefix}/subdir/subdir") + assert client.list_files(f"{self.prefix}/subdir", pattern='subdir')[0].path == \ + f"{self.prefix}/subdir/subdir" def test_make_directory_recursively(self): client = self.client - with self.assertRaises(pebble.PathError) as cm: + with pytest.raises(pebble.PathError) as excinfo: client.make_dir(f"{self.prefix}/subdir/subdir", make_parents=False) - self.assertEqual(cm.exception.kind, 'not-found') + assert excinfo.value.kind == 'not-found' client.make_dir(f"{self.prefix}/subdir/subdir", make_parents=True) - self.assertEqual( - client.list_files(f"{self.prefix}/subdir", pattern='subdir')[0].path, - f"{self.prefix}/subdir/subdir") + assert client.list_files(f"{self.prefix}/subdir", pattern='subdir')[0].path == \ + f"{self.prefix}/subdir/subdir" def test_make_directory_with_relative_path_fails(self): client = self.client - with self.assertRaises(pebble.PathError) as cm: + with pytest.raises(pebble.PathError) as excinfo: client.make_dir('dir') - self.assertEqual(cm.exception.kind, 'generic-file-error') + assert excinfo.value.kind == 'generic-file-error' def test_make_subdir_of_file_fails(self): client = self.client client.push(f"{self.prefix}/file", 'data') # Direct child case - with self.assertRaises(pebble.PathError) as cm: + with pytest.raises(pebble.PathError) as excinfo: client.make_dir(f"{self.prefix}/file/subdir") - self.assertEqual(cm.exception.kind, 'generic-file-error') + assert excinfo.value.kind == 'generic-file-error' # Recursive creation case, in case its flow is different - with self.assertRaises(pebble.PathError) as cm: + with pytest.raises(pebble.PathError) as excinfo: client.make_dir(f"{self.prefix}/file/subdir/subdir", make_parents=True) - self.assertEqual(cm.exception.kind, 'generic-file-error') + assert excinfo.value.kind == 'generic-file-error' def test_make_dir_with_permission_mask(self): client = self.client @@ -4401,10 +4349,8 @@ def test_make_dir_with_permission_mask(self): client.make_dir(f"{self.prefix}/dir2", permissions=0o777) files = client.list_files(f"{self.prefix}/", pattern='dir*') - self.assertEqual([f for f in files if f.path == f"{self.prefix}/dir1"] - [0].permissions, 0o700) - self.assertEqual([f for f in files if f.path == f"{self.prefix}/dir2"] - [0].permissions, 0o777) + assert [f for f in files if f.path == f"{self.prefix}/dir1"][0].permissions == 0o700 + assert [f for f in files if f.path == f"{self.prefix}/dir2"][0].permissions == 0o777 # If permissions are outside of the range 0o000 through 0o777, an exception should be # raised. @@ -4412,9 +4358,9 @@ def test_make_dir_with_permission_mask(self): 0o1000, # Exceeds 0o777 -1, # Less than 0o000 )): - with self.assertRaises(pebble.PathError) as cm: + with pytest.raises(pebble.PathError) as excinfo: client.make_dir(f"{self.prefix}/dir3_{i}", permissions=bad_permission) - self.assertEqual(cm.exception.kind, 'generic-file-error') + assert excinfo.value.kind == 'generic-file-error' def test_remove_path(self): client = self.client @@ -4430,17 +4376,17 @@ def test_remove_path(self): client.remove_path(f"{self.prefix}/empty_dir") # Remove non-empty directory, recursive=False: error - with self.assertRaises(pebble.PathError) as cm: + with pytest.raises(pebble.PathError) as excinfo: client.remove_path(f"{self.prefix}/dir", recursive=False) - self.assertEqual(cm.exception.kind, 'generic-file-error') + assert excinfo.value.kind == 'generic-file-error' # Remove non-empty directory, recursive=True: succeeds (and removes child objects) client.remove_path(f"{self.prefix}/dir", recursive=True) # Remove non-existent path, recursive=False: error - with self.assertRaises(pebble.PathError) as cm: + with pytest.raises(pebble.PathError) as excinfo: client.remove_path(f"{self.prefix}/dir/does/not/exist/asdf", recursive=False) - self.assertEqual(cm.exception.kind, 'not-found') + assert excinfo.value.kind == 'not-found' # Remove non-existent path, recursive=True: succeeds client.remove_path(f"{self.prefix}/dir/does/not/exist/asdf", recursive=True) @@ -4508,37 +4454,37 @@ def test_container_storage_mounts(self): c1_fname = 'foo.txt' c1_fpath = os.path.join('/mounts/foo', c1_fname) c1.push(c1_fpath, '42') - self.assertTrue(c1.exists(c1_fpath)) + assert c1.exists(c1_fpath) fpath = os.path.join(str(harness.model.storages['store1'][0].location), 'foo.txt') with open(fpath) as f: - self.assertEqual('42', f.read()) + assert f.read() == '42' # check that the file is not visible in c2 which has a different storage mount c2 = harness.model.unit.get_container('c2') c2_fpath = os.path.join('/mounts/foo', c1_fname) - self.assertFalse(c2.exists(c2_fpath)) + assert not c2.exists(c2_fpath) # check that the file is visible in c3 which has the same storage mount c3 = harness.model.unit.get_container('c3') c3_fpath = os.path.join('/mounts/bar', c1_fname) - self.assertTrue(c3.exists(c3_fpath)) + assert c3.exists(c3_fpath) with c3.pull(c3_fpath) as f: - self.assertEqual('42', f.read()) + assert f.read() == '42' # test all other container file ops with c1.pull(c1_fpath) as f: - self.assertEqual('42', f.read()) + assert f.read() == '42' files = c1.list_files(c1_fpath) - self.assertEqual([c1_fpath], [fi.path for fi in files]) + assert [c1_fpath] == [fi.path for fi in files] c1.remove_path(c1_fpath) - self.assertFalse(c1.exists(c1_fpath)) + assert not c1.exists(c1_fpath) # test detaching storage c1.push(c1_fpath, '42') - self.assertTrue(c1.exists(c1_fpath)) + assert c1.exists(c1_fpath) store1_id = harness.model.storages['store1'][0].full_id harness.remove_storage(store1_id) - self.assertFalse(c1.exists(c1_fpath)) + assert not c1.exists(c1_fpath) def _select_testing_user_group(self): user = [u for u in pwd.getpwall() if u.pw_uid != os.getuid()][0] @@ -4584,7 +4530,7 @@ def test_push_with_ownership(self): for idx, case in enumerate(cases): client.push(f"{self.prefix}/file{idx}", data, **case) file_ = client.list_files(f"{self.prefix}/file{idx}")[0] - self.assertEqual(file_.path, f"{self.prefix}/file{idx}") + assert file_.path == f"{self.prefix}/file{idx}" def test_make_dir_with_ownership(self): client = self.client @@ -4624,7 +4570,7 @@ def test_make_dir_with_ownership(self): for idx, case in enumerate(cases): client.make_dir(f"{self.prefix}/dir{idx}", **case) dir_ = client.list_files(f"{self.prefix}/dir{idx}", itself=True)[0] - self.assertEqual(dir_.path, f"{self.prefix}/dir{idx}") + assert dir_.path == f"{self.prefix}/dir{idx}" @patch("grp.getgrgid") @patch("pwd.getpwuid") @@ -4634,9 +4580,9 @@ def test_list_files_unnamed(self, getpwuid: MagicMock, getgrgid: MagicMock): data = 'data' self.client.push(f"{self.prefix}/file", data) files = self.client.list_files(f"{self.prefix}/") - self.assertEqual(len(files), 1) - self.assertIs(files[0].user, None) - self.assertIs(files[0].group, None) + assert len(files) == 1 + assert files[0].user is None + assert files[0].group is None class TestFilesystem(unittest.TestCase, _TestingPebbleClientMixin): @@ -4662,13 +4608,13 @@ def tearDown(self) -> None: def test_push(self): self.container.push("/foo", source="foo") - self.assertTrue((self.root / "foo").is_file()) - self.assertEqual((self.root / "foo").read_text(), "foo") + assert (self.root / "foo").is_file() + assert (self.root / "foo").read_text() == "foo" def test_push_create_parent(self): self.container.push("/foo/bar", source="bar", make_dirs=True) - self.assertTrue((self.root / "foo").is_dir()) - self.assertEqual((self.root / "foo" / "bar").read_text(), "bar") + assert (self.root / "foo").is_dir() + assert (self.root / "foo" / "bar").read_text() == "bar" def test_push_path(self): with tempfile.TemporaryDirectory() as temp: @@ -4679,22 +4625,22 @@ def test_push_path(self): (tempdir / "foo/baz").mkdir(parents=True) self.container.push_path(tempdir / "foo", "/tmp") # noqa: S108 - self.assertTrue((self.root / "tmp").is_dir()) - self.assertTrue((self.root / "tmp/foo").is_dir()) - self.assertTrue((self.root / "tmp/foo/bar").is_dir()) - self.assertTrue((self.root / "tmp/foo/baz").is_dir()) - self.assertEqual((self.root / "tmp/foo/test").read_text(), "test") - self.assertEqual((self.root / "tmp/foo/bar/foobar").read_text(), "foobar") + assert (self.root / "tmp").is_dir() + assert (self.root / "tmp/foo").is_dir() + assert (self.root / "tmp/foo/bar").is_dir() + assert (self.root / "tmp/foo/baz").is_dir() + assert (self.root / "tmp/foo/test").read_text() == "test" + assert (self.root / "tmp/foo/bar/foobar").read_text() == "foobar" def test_make_dir(self): self.container.make_dir("/tmp") # noqa: S108 - self.assertTrue((self.root / "tmp").is_dir()) + assert (self.root / "tmp").is_dir() self.container.make_dir("/foo/bar/foobar", make_parents=True) - self.assertTrue((self.root / "foo/bar/foobar").is_dir()) + assert (self.root / "foo/bar/foobar").is_dir() def test_pull(self): (self.root / "foo").write_text("foo") - self.assertEqual(self.container.pull("/foo").read(), "foo") + assert self.container.pull("/foo").read() == "foo" def test_pull_path(self): (self.root / "foo").mkdir() @@ -4704,39 +4650,39 @@ def test_pull_path(self): with tempfile.TemporaryDirectory() as temp: tempdir = pathlib.Path(temp) self.container.pull_path("/", tempdir) - self.assertTrue((tempdir / "foo").is_dir()) - self.assertEqual((tempdir / "foo/bar").read_text(), "bar") - self.assertTrue((tempdir / "foobar").is_dir()) - self.assertEqual((tempdir / "test").read_text(), "test") + assert (tempdir / "foo").is_dir() + assert (tempdir / "foo/bar").read_text() == "bar" + assert (tempdir / "foobar").is_dir() + assert (tempdir / "test").read_text() == "test" def test_list_files(self): (self.root / "foo").mkdir() self.assertSequenceEqual(self.container.list_files("/foo"), []) - self.assertEqual(len(self.container.list_files("/")), 1) + assert len(self.container.list_files("/")) == 1 file_info = self.container.list_files("/")[0] - self.assertEqual(file_info.path, "/foo") - self.assertEqual(file_info.type, FileType.DIRECTORY) - self.assertEqual(self.container.list_files("/foo", itself=True)[0].path, "/foo") + assert file_info.path == "/foo" + assert file_info.type == FileType.DIRECTORY + assert self.container.list_files("/foo", itself=True)[0].path == "/foo" (self.root / "foo/bar").write_text("foobar") - self.assertEqual(len(self.container.list_files("/foo")), 1) - self.assertEqual(len(self.container.list_files("/foo", pattern="*ar")), 1) - self.assertEqual(len(self.container.list_files("/foo", pattern="*oo")), 0) + assert len(self.container.list_files("/foo")) == 1 + assert len(self.container.list_files("/foo", pattern="*ar")) == 1 + assert len(self.container.list_files("/foo", pattern="*oo")) == 0 file_info = self.container.list_files("/foo")[0] - self.assertEqual(file_info.path, "/foo/bar") - self.assertEqual(file_info.type, FileType.FILE) + assert file_info.path == "/foo/bar" + assert file_info.type == FileType.FILE root_info = self.container.list_files("/", itself=True)[0] - self.assertEqual(root_info.path, "/") - self.assertEqual(root_info.name, "/") + assert root_info.path == "/" + assert root_info.name == "/" def test_storage_mount(self): storage_id = self.harness.add_storage("test-storage", 1, attach=True)[0] - self.assertTrue((self.root / "mounts/foo").exists()) + assert (self.root / "mounts/foo").exists() (self.root / "mounts/foo/bar").write_text("foobar") - self.assertEqual(self.container.pull("/mounts/foo/bar").read(), "foobar") + assert self.container.pull("/mounts/foo/bar").read() == "foobar" self.harness.detach_storage(storage_id) - self.assertFalse((self.root / "mounts/foo/bar").is_file()) + assert not (self.root / "mounts/foo/bar").is_file() self.harness.attach_storage(storage_id) - self.assertTrue((self.root / "mounts/foo/bar").read_text(), "foobar") + assert (self.root / "mounts/foo/bar").read_text(), "foobar" def _make_storage_attach_harness(self, meta: typing.Optional[str] = None): class MyCharm(ops.CharmBase): @@ -4771,28 +4717,28 @@ def test_storage_attach_begin_no_emit(self): harness = self._make_storage_attach_harness() harness.add_storage('test-storage', attach=True) harness.begin() - self.assertNotIn('test-storage/0', harness.charm.attached) + assert 'test-storage/0' not in harness.charm.attached def test_storage_attach_begin_with_hooks_emits(self): """`attach` doesn't emit storage-attached before `begin_with_initial_hooks`.""" harness = self._make_storage_attach_harness() harness.add_storage('test-storage', attach=True) harness.begin_with_initial_hooks() - self.assertIn('test-storage/0', harness.charm.attached) - self.assertTrue(harness.charm.locations[0]) + assert 'test-storage/0' in harness.charm.attached + assert harness.charm.locations[0] def test_storage_add_with_later_attach(self): harness = self._make_storage_attach_harness() harness.begin() storage_ids = harness.add_storage('test-storage', attach=False) - self.assertNotIn('test-storage/0', harness.charm.attached) + assert 'test-storage/0' not in harness.charm.attached for s_id in storage_ids: harness.attach_storage(s_id) # It's safe to call `attach_storage` more than once, and this will # only trigger the event once - this is the same as executing # `juju attach-storage ` more than once. harness.attach_storage(s_id) - self.assertEqual(harness.charm.attached.count('test-storage/0'), 1) + assert harness.charm.attached.count('test-storage/0') == 1 def test_storage_machine_charm_metadata(self): meta = ''' @@ -4805,7 +4751,7 @@ def test_storage_machine_charm_metadata(self): harness = self._make_storage_attach_harness(meta) harness.begin() harness.add_storage('test-storage', attach=True) - self.assertIn('test-storage/0', harness.charm.attached) + assert 'test-storage/0' in harness.charm.attached def test_storage_multiple_storage_instances(self): meta = ''' @@ -4820,13 +4766,12 @@ def test_storage_multiple_storage_instances(self): harness = self._make_storage_attach_harness(meta) harness.begin() harness.add_storage('test-storage', 2, attach=True) - self.assertEqual(harness.charm.attached, ['test-storage/0', 'test-storage/1']) - self.assertNotEqual(harness.charm.locations[0], harness.charm.locations[1]) + assert harness.charm.attached == ['test-storage/0', 'test-storage/1'] + assert harness.charm.locations[0] != harness.charm.locations[1] harness.add_storage('test-storage', 2, attach=True) - self.assertEqual( - harness.charm.attached, [ - 'test-storage/0', 'test-storage/1', 'test-storage/2', 'test-storage/3']) - self.assertEqual(len(set(harness.charm.locations)), 4) + assert harness.charm.attached == [ + 'test-storage/0', 'test-storage/1', 'test-storage/2', 'test-storage/3'] + assert len(set(harness.charm.locations)) == 4 class TestSecrets(unittest.TestCase): @@ -4841,8 +4786,8 @@ def test_add_model_secret_by_app_name_str(self): secret_id = harness.add_model_secret('database', {'password': 'hunter2'}) harness.grant_secret(secret_id, 'webapp') secret = harness.model.get_secret(id=secret_id) - self.assertEqual(secret.id, secret_id) - self.assertEqual(secret.get_content(), {'password': 'hunter2'}) + assert secret.id == secret_id + assert secret.get_content() == {'password': 'hunter2'} def test_add_model_secret_by_app_instance(self): harness = ops.testing.Harness(ops.CharmBase, meta=yaml.safe_dump( @@ -4856,8 +4801,8 @@ def test_add_model_secret_by_app_instance(self): secret_id = harness.add_model_secret(app, {'password': 'hunter3'}) harness.grant_secret(secret_id, 'webapp') secret = harness.model.get_secret(id=secret_id) - self.assertEqual(secret.id, secret_id) - self.assertEqual(secret.get_content(), {'password': 'hunter3'}) + assert secret.id == secret_id + assert secret.get_content() == {'password': 'hunter3'} def test_add_model_secret_by_unit_instance(self): harness = ops.testing.Harness(ops.CharmBase, meta=yaml.safe_dump( @@ -4871,8 +4816,8 @@ def test_add_model_secret_by_unit_instance(self): secret_id = harness.add_model_secret(unit, {'password': 'hunter4'}) harness.grant_secret(secret_id, 'webapp') secret = harness.model.get_secret(id=secret_id) - self.assertEqual(secret.id, secret_id) - self.assertEqual(secret.get_content(), {'password': 'hunter4'}) + assert secret.id == secret_id + assert secret.get_content() == {'password': 'hunter4'} def test_get_secret_as_owner(self): harness = ops.testing.Harness(ops.CharmBase, meta=yaml.safe_dump( @@ -4883,13 +4828,13 @@ def test_get_secret_as_owner(self): # App secret. secret_id = harness.charm.app.add_secret({'password': 'hunter5'}).id secret = harness.model.get_secret(id=secret_id) - self.assertEqual(secret.id, secret_id) - self.assertEqual(secret.get_content(), {'password': 'hunter5'}) + assert secret.id == secret_id + assert secret.get_content() == {'password': 'hunter5'} # Unit secret. secret_id = harness.charm.unit.add_secret({'password': 'hunter6'}).id secret = harness.model.get_secret(id=secret_id) - self.assertEqual(secret.id, secret_id) - self.assertEqual(secret.get_content(), {'password': 'hunter6'}) + assert secret.id == secret_id + assert secret.get_content() == {'password': 'hunter6'} def test_get_secret_and_refresh(self): harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp') @@ -4899,11 +4844,11 @@ def test_get_secret_and_refresh(self): secret = harness.charm.app.add_secret({'password': 'hunter6'}) secret.set_content({"password": "hunter7"}) retrieved_secret = harness.model.get_secret(id=secret.id) - self.assertEqual(retrieved_secret.id, secret.id) - self.assertEqual(retrieved_secret.get_content(), {'password': 'hunter6'}) - self.assertEqual(retrieved_secret.peek_content(), {'password': 'hunter7'}) - self.assertEqual(retrieved_secret.get_content(refresh=True), {'password': 'hunter7'}) - self.assertEqual(retrieved_secret.get_content(), {'password': 'hunter7'}) + assert retrieved_secret.id == secret.id + assert retrieved_secret.get_content() == {'password': 'hunter6'} + assert retrieved_secret.peek_content() == {'password': 'hunter7'} + assert retrieved_secret.get_content(refresh=True) == {'password': 'hunter7'} + assert retrieved_secret.get_content() == {'password': 'hunter7'} def test_get_secret_removed(self): harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp') @@ -4913,7 +4858,7 @@ def test_get_secret_removed(self): secret = harness.charm.app.add_secret({'password': 'hunter8'}) secret.set_content({"password": "hunter9"}) secret.remove_revision(secret.get_info().revision) - with self.assertRaises(ops.SecretNotFoundError): + with pytest.raises(ops.SecretNotFoundError): harness.model.get_secret(id=secret.id) def test_get_secret_by_label(self): @@ -4922,18 +4867,18 @@ def test_get_secret_by_label(self): harness.begin() secret_id = harness.charm.app.add_secret({'password': 'hunter9'}, label="my-pass").id secret = harness.model.get_secret(label="my-pass") - self.assertEqual(secret.label, "my-pass") - self.assertEqual(secret.get_content(), {'password': 'hunter9'}) + assert secret.label == "my-pass" + assert secret.get_content() == {'password': 'hunter9'} secret = harness.model.get_secret(id=secret_id, label="other-name") - self.assertEqual(secret.get_content(), {'password': 'hunter9'}) + assert secret.get_content() == {'password': 'hunter9'} secret = harness.model.get_secret(label="other-name") - self.assertEqual(secret.get_content(), {'password': 'hunter9'}) + assert secret.get_content() == {'password': 'hunter9'} def test_add_model_secret_invalid_content(self): harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp') self.addCleanup(harness.cleanup) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): harness.add_model_secret('database', {'x': 'y'}) # key too short def test_set_secret_content(self): @@ -4950,22 +4895,22 @@ def test_set_secret_content(self): harness.framework.observe(harness.charm.on.secret_changed, harness.charm.record_event) harness.set_secret_content(secret_id, {'foo': '2'}) - self.assertEqual(len(harness.charm.events), 1) + assert len(harness.charm.events) == 1 event = harness.charm.events[0] # Not assertIsInstance to help type checkers. assert isinstance(event, ops.SecretChangedEvent) - self.assertEqual(event.secret.get_content(), {'foo': '1'}) - self.assertEqual(event.secret.get_content(refresh=True), {'foo': '2'}) - self.assertEqual(event.secret.get_content(), {'foo': '2'}) + assert event.secret.get_content() == {'foo': '1'} + assert event.secret.get_content(refresh=True) == {'foo': '2'} + assert event.secret.get_content() == {'foo': '2'} - self.assertEqual(harness.get_secret_revisions(secret_id), [1, 2]) + assert harness.get_secret_revisions(secret_id) == [1, 2] def test_set_secret_content_wrong_owner(self): harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp') self.addCleanup(harness.cleanup) secret = harness.model.app.add_secret({'foo': 'bar'}) - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): assert secret.id is not None harness.set_secret_content(secret.id, {'bar': 'foo'}) @@ -4973,7 +4918,7 @@ def test_set_secret_content_invalid_secret_id(self): harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp') self.addCleanup(harness.cleanup) - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): harness.set_secret_content('asdf', {'foo': 'bar'}) def test_set_secret_content_invalid_content(self): @@ -4981,7 +4926,7 @@ def test_set_secret_content_invalid_content(self): self.addCleanup(harness.cleanup) secret_id = harness.add_model_secret('database', {'foo': 'bar'}) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): harness.set_secret_content(secret_id, {'x': 'y'}) def test_grant_secret_and_revoke_secret(self): @@ -4995,11 +4940,11 @@ def test_grant_secret_and_revoke_secret(self): secret_id = harness.add_model_secret('database', {'password': 'hunter2'}) harness.grant_secret(secret_id, 'webapp') secret = harness.model.get_secret(id=secret_id) - self.assertEqual(secret.id, secret_id) - self.assertEqual(secret.get_content(), {'password': 'hunter2'}) + assert secret.id == secret_id + assert secret.get_content() == {'password': 'hunter2'} harness.revoke_secret(secret_id, 'webapp') - with self.assertRaises(ops.SecretNotFoundError): + with pytest.raises(ops.SecretNotFoundError): harness.model.get_secret(id=secret_id) def test_grant_secret_wrong_app(self): @@ -5012,7 +4957,7 @@ def test_grant_secret_wrong_app(self): secret_id = harness.add_model_secret('database', {'password': 'hunter2'}) harness.grant_secret(secret_id, 'otherapp') - with self.assertRaises(ops.SecretNotFoundError): + with pytest.raises(ops.SecretNotFoundError): harness.model.get_secret(id=secret_id) def test_grant_secret_wrong_unit(self): @@ -5025,7 +4970,7 @@ def test_grant_secret_wrong_unit(self): secret_id = harness.add_model_secret('database', {'password': 'hunter2'}) harness.grant_secret(secret_id, 'webapp/1') # should be webapp/0 - with self.assertRaises(ops.SecretNotFoundError): + with pytest.raises(ops.SecretNotFoundError): harness.model.get_secret(id=secret_id) def test_grant_secret_no_relation(self): @@ -5033,7 +4978,7 @@ def test_grant_secret_no_relation(self): self.addCleanup(harness.cleanup) secret_id = harness.add_model_secret('database', {'password': 'hunter2'}) - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): harness.grant_secret(secret_id, 'webapp') def test_get_secret_grants(self): @@ -5049,16 +4994,16 @@ def test_get_secret_grants(self): harness.set_leader(True) secret = harness.model.app.add_secret({'foo': 'x'}) assert secret.id is not None - self.assertEqual(harness.get_secret_grants(secret.id, relation_id), set()) + assert harness.get_secret_grants(secret.id, relation_id) == set() rel = harness.model.get_relation('db') assert rel is not None secret.grant(rel) - self.assertEqual(harness.get_secret_grants(secret.id, relation_id), {'webapp'}) + assert harness.get_secret_grants(secret.id, relation_id) == {'webapp'} secret.revoke(rel) - self.assertEqual(harness.get_secret_grants(secret.id, relation_id), set()) + assert harness.get_secret_grants(secret.id, relation_id) == set() secret.grant(rel, unit=harness.model.get_unit('webapp/0')) - self.assertEqual(harness.get_secret_grants(secret.id, relation_id), {'webapp/0'}) + assert harness.get_secret_grants(secret.id, relation_id) == {'webapp/0'} def test_trigger_secret_rotation(self): harness = ops.testing.Harness(EventRecorder, meta='name: database') @@ -5071,19 +5016,19 @@ def test_trigger_secret_rotation(self): harness.trigger_secret_rotation(secret.id) harness.trigger_secret_rotation(secret.id, label='override') - self.assertEqual(len(harness.charm.events), 2) + assert len(harness.charm.events) == 2 event = harness.charm.events[0] # Not assertIsInstance to help type checkers. assert isinstance(event, ops.SecretRotateEvent) - self.assertEqual(event.secret.label, 'lbl') - self.assertEqual(event.secret.get_content(), {'foo': 'x'}) + assert event.secret.label == 'lbl' + assert event.secret.get_content() == {'foo': 'x'} event = harness.charm.events[1] # Not assertIsInstance to help type checkers. assert isinstance(event, ops.SecretRotateEvent) - self.assertEqual(event.secret.label, 'override') - self.assertEqual(event.secret.get_content(), {'foo': 'x'}) + assert event.secret.label == 'override' + assert event.secret.get_content() == {'foo': 'x'} - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): harness.trigger_secret_rotation('nosecret') def test_trigger_secret_rotation_on_user_secret(self): @@ -5108,21 +5053,21 @@ def test_trigger_secret_removal(self): harness.trigger_secret_removal(secret.id, 1) harness.trigger_secret_removal(secret.id, 42, label='override') - self.assertEqual(len(harness.charm.events), 2) + assert len(harness.charm.events) == 2 event = harness.charm.events[0] # Not assertIsInstance to help type checkers. assert isinstance(event, ops.SecretRemoveEvent) - self.assertEqual(event.secret.label, 'lbl') - self.assertEqual(event.revision, 1) - self.assertEqual(event.secret.get_content(), {'foo': 'x'}) + assert event.secret.label == 'lbl' + assert event.revision == 1 + assert event.secret.get_content() == {'foo': 'x'} event = harness.charm.events[1] # Not assertIsInstance to help type checkers. assert isinstance(event, ops.SecretRemoveEvent) - self.assertEqual(event.secret.label, 'override') - self.assertEqual(event.revision, 42) - self.assertEqual(event.secret.get_content(), {'foo': 'x'}) + assert event.secret.label == 'override' + assert event.revision == 42 + assert event.secret.get_content() == {'foo': 'x'} - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): harness.trigger_secret_removal('nosecret', 1) def test_trigger_secret_expiration(self): @@ -5136,21 +5081,21 @@ def test_trigger_secret_expiration(self): harness.trigger_secret_removal(secret.id, 1) harness.trigger_secret_removal(secret.id, 42, label='override') - self.assertEqual(len(harness.charm.events), 2) + assert len(harness.charm.events) == 2 event = harness.charm.events[0] # Not assertIsInstance to help type checkers. assert isinstance(event, ops.SecretRemoveEvent) - self.assertEqual(event.secret.label, 'lbl') - self.assertEqual(event.revision, 1) - self.assertEqual(event.secret.get_content(), {'foo': 'x'}) + assert event.secret.label == 'lbl' + assert event.revision == 1 + assert event.secret.get_content() == {'foo': 'x'} event = harness.charm.events[1] # Not assertIsInstance to help type checkers. assert isinstance(event, ops.SecretRemoveEvent) - self.assertEqual(event.secret.label, 'override') - self.assertEqual(event.revision, 42) - self.assertEqual(event.secret.get_content(), {'foo': 'x'}) + assert event.secret.label == 'override' + assert event.revision == 42 + assert event.secret.get_content() == {'foo': 'x'} - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): harness.trigger_secret_removal('nosecret', 1) def test_trigger_secret_expiration_on_user_secret(self): @@ -5172,9 +5117,9 @@ def test_secret_permissions_unit(self): # The charm can always manage a local unit secret. secret_id = harness.charm.unit.add_secret({"password": "1234"}).id secret = harness.charm.model.get_secret(id=secret_id) - self.assertEqual(secret.get_content(), {"password": "1234"}) + assert secret.get_content() == {"password": "1234"} info = secret.get_info() - self.assertEqual(info.id, secret_id) + assert info.id == secret_id secret.set_content({"password": "5678"}) secret.remove_all_revisions() @@ -5187,9 +5132,9 @@ def test_secret_permissions_leader(self): harness.set_leader(True) secret_id = harness.charm.app.add_secret({"password": "1234"}).id secret = harness.charm.model.get_secret(id=secret_id) - self.assertEqual(secret.get_content(), {"password": "1234"}) + assert secret.get_content() == {"password": "1234"} info = secret.get_info() - self.assertEqual(info.id, secret_id) + assert info.id == secret_id secret.set_content({"password": "5678"}) secret.remove_all_revisions() @@ -5202,12 +5147,12 @@ def test_secret_permissions_nonleader(self): harness.set_leader(False) secret_id = harness.charm.app.add_secret({"password": "1234"}).id secret = harness.charm.model.get_secret(id=secret_id) - self.assertEqual(secret.get_content(), {"password": "1234"}) - with self.assertRaises(ops.model.SecretNotFoundError): + assert secret.get_content() == {"password": "1234"} + with pytest.raises(ops.model.SecretNotFoundError): secret.get_info() - with self.assertRaises(ops.model.SecretNotFoundError): + with pytest.raises(ops.model.SecretNotFoundError): secret.set_content({"password": "5678"}) - with self.assertRaises(ops.model.SecretNotFoundError): + with pytest.raises(ops.model.SecretNotFoundError): secret.remove_all_revisions() def test_add_user_secret(self): @@ -5296,51 +5241,51 @@ def test_ports(self): unit.open_port('icmp') ports_set = unit.opened_ports() - self.assertIsInstance(ports_set, set) + assert isinstance(ports_set, set) ports = sorted(ports_set, key=lambda p: (p.protocol, p.port)) - self.assertEqual(len(ports), 3) - self.assertIsInstance(ports[0], ops.Port) - self.assertEqual(ports[0].protocol, 'icmp') - self.assertIsNone(ports[0].port) - self.assertEqual(ports[1].protocol, 'tcp') - self.assertEqual(ports[1].port, 8080) - self.assertIsInstance(ports[1], ops.Port) - self.assertEqual(ports[2].protocol, 'udp') - self.assertEqual(ports[2].port, 4000) + assert len(ports) == 3 + assert isinstance(ports[0], ops.Port) + assert ports[0].protocol == 'icmp' + assert ports[0].port is None + assert ports[1].protocol == 'tcp' + assert ports[1].port == 8080 + assert isinstance(ports[1], ops.Port) + assert ports[2].protocol == 'udp' + assert ports[2].port == 4000 unit.close_port('tcp', 8080) unit.close_port('tcp', 8080) # closing same port again has no effect unit.close_port('udp', 4000) ports_set = unit.opened_ports() - self.assertIsInstance(ports_set, set) + assert isinstance(ports_set, set) ports = sorted(ports_set, key=lambda p: (p.protocol, p.port)) - self.assertEqual(len(ports), 1) - self.assertIsInstance(ports[0], ops.Port) - self.assertEqual(ports[0].protocol, 'icmp') - self.assertIsNone(ports[0].port) + assert len(ports) == 1 + assert isinstance(ports[0], ops.Port) + assert ports[0].protocol == 'icmp' + assert ports[0].port is None unit.close_port('icmp') ports_set = unit.opened_ports() - self.assertEqual(ports_set, set()) + assert ports_set == set() def test_errors(self): harness = ops.testing.Harness(ops.CharmBase, meta='name: webapp') self.addCleanup(harness.cleanup) unit = harness.model.unit - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): unit.open_port('icmp', 8080) # icmp cannot have port - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): unit.open_port('ftp', 8080) # invalid protocol # type: ignore - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): unit.open_port('tcp') # tcp must have port - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): unit.open_port('udp') # udp must have port - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): unit.open_port('tcp', 0) # port out of range - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): unit.open_port('tcp', 65536) # port out of range @@ -5365,65 +5310,65 @@ def test_register_handler(self): self.harness.handle_exec(self.container, ["foo", "bar"], result="foobar") stdout, _ = self.container.exec(["foo", "bar", "foobar", "--help"]).wait_output() - self.assertEqual(stdout, "foobar2") + assert stdout == "foobar2" stdout, _ = self.container.exec(["foo", "bar", "--help"]).wait_output() - self.assertEqual(stdout, "foobar") + assert stdout == "foobar" stdout, _ = self.container.exec(["foo", "bar"]).wait_output() - self.assertEqual(stdout, "foobar") + assert stdout == "foobar" stdout, _ = self.container.exec(["foo", "--help"]).wait_output() - self.assertEqual(stdout, "foo") + assert stdout == "foo" def test_re_register_handler(self): self.harness.handle_exec(self.container, ["foo", "bar"], result="foobar") self.harness.handle_exec(self.container, ["foo"], result="foo") stdout, _ = self.container.exec(["foo", "bar"]).wait_output() - self.assertEqual(stdout, "foobar") + assert stdout == "foobar" self.harness.handle_exec(self.container, ["foo", "bar"], result="hello") stdout, _ = self.container.exec(["foo", "bar"]).wait_output() - self.assertEqual(stdout, "hello") + assert stdout == "hello" self.harness.handle_exec(self.container.name, ["foo"], result="hello2") stdout, _ = self.container.exec(["foo"]).wait_output() - self.assertEqual(stdout, "hello2") + assert stdout == "hello2" - with self.assertRaises(pebble.APIError): + with pytest.raises(pebble.APIError): self.container.exec(["abc"]).wait() def test_register_match_all_prefix(self): self.harness.handle_exec(self.container, [], result="hello") stdout, _ = self.container.exec(["foo", "bar"]).wait_output() - self.assertEqual(stdout, "hello") + assert stdout == "hello" stdout, _ = self.container.exec(["ls"]).wait_output() - self.assertEqual(stdout, "hello") + assert stdout == "hello" def test_register_with_result(self): self.harness.handle_exec(self.container, ["foo"], result=10) - with self.assertRaises(pebble.ExecError) as exc: + with pytest.raises(pebble.ExecError) as excinfo: self.container.exec(["foo"]).wait() - self.assertEqual(exc.exception.exit_code, 10) + assert excinfo.value.exit_code == 10 self.harness.handle_exec(self.container, ["foo"], result="hello") stdout, stderr = self.container.exec(["foo"]).wait_output() - self.assertEqual(stdout, "hello") - self.assertEqual(stderr, "") - with self.assertRaises(ValueError): + assert stdout == "hello" + assert stderr == "" + with pytest.raises(ValueError): self.container.exec(["foo"], encoding=None).wait_output() self.harness.handle_exec(self.container, ["foo"], result=b"hello2") stdout, stderr = self.container.exec(["foo"], encoding=None).wait_output() - self.assertEqual(stdout, b"hello2") - self.assertEqual(stderr, b"") + assert stdout == b"hello2" + assert stderr == b"" stdout, stderr = self.container.exec(["foo"]).wait_output() - self.assertEqual(stdout, "hello2") - self.assertEqual(stderr, "") + assert stdout == "hello2" + assert stderr == "" def test_register_with_handler(self): args_history: typing.List[ops.testing.ExecArgs] = [] @@ -5436,55 +5381,55 @@ def handler(args: ops.testing.ExecArgs): self.harness.handle_exec(self.container, ["foo"], handler=handler) self.container.exec(["foo", "bar"]).wait() - self.assertEqual(len(args_history), 1) - self.assertEqual(args_history[-1].command, ["foo", "bar"]) + assert len(args_history) == 1 + assert args_history[-1].command == ["foo", "bar"] return_value = ExecResult(exit_code=1) - with self.assertRaises(pebble.ExecError): + with pytest.raises(pebble.ExecError): self.container.exec(["foo", "bar"]).wait() return_value = ExecResult(stdout="hello", stderr="error") stdout, stderr = self.container.exec(["foo"]).wait_output() - self.assertEqual(stdout, "hello") - self.assertEqual(stderr, "error") - self.assertEqual(len(args_history), 3) + assert stdout == "hello" + assert stderr == "error" + assert len(args_history) == 3 self.container.exec(["foo"], environment={"bar": "foobar"}).wait_output() - self.assertDictEqual(args_history[-1].environment, {"bar": "foobar"}) + assert args_history[-1].environment == {"bar": "foobar"} return_value = ExecResult(stdout=b"hello") stdout, _ = self.container.exec(["foo"], encoding=None).wait_output() - self.assertIsNone(args_history[-1].encoding) - self.assertEqual(stdout, b"hello") + assert args_history[-1].encoding is None + assert stdout == b"hello" self.container.exec(["foo"], working_dir="/test").wait_output() - self.assertEqual(args_history[-1].working_dir, "/test") + assert args_history[-1].working_dir == "/test" self.container.exec(["foo"], user="foo", user_id=1, group="bar", group_id=2).wait() - self.assertEqual(args_history[-1].user, "foo") - self.assertEqual(args_history[-1].user_id, 1) - self.assertEqual(args_history[-1].group, "bar") - self.assertEqual(args_history[-1].group_id, 2) + assert args_history[-1].user == "foo" + assert args_history[-1].user_id == 1 + assert args_history[-1].group == "bar" + assert args_history[-1].group_id == 2 def test_exec_timeout(self): def handler(_: ops.testing.ExecArgs): raise TimeoutError self.harness.handle_exec(self.container, [], handler=handler) - with self.assertRaises(TimeoutError): + with pytest.raises(TimeoutError): self.container.exec(["ls"], timeout=1).wait() - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): self.container.exec(["ls"]).wait() def test_combined_error(self): return_value = ExecResult(stdout="foobar") self.harness.handle_exec(self.container, [], handler=lambda _: return_value) stdout, stderr = self.container.exec(["ls"], combine_stderr=True).wait_output() - self.assertEqual(stdout, "foobar") - self.assertEqual(stderr, "") + assert stdout == "foobar" + assert stderr == "" return_value = ExecResult(stdout="foobar", stderr="error") - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.container.exec(["ls"], combine_stderr=True).wait_output() def test_exec_stdin(self): @@ -5495,12 +5440,12 @@ def handler(args: ops.testing.ExecArgs): self.harness.handle_exec(self.container, [], handler=handler) proc = self.container.exec(["ls"], stdin="test") - self.assertIsNone(proc.stdin) - self.assertEqual(args_history[-1].stdin, "test") + assert proc.stdin is None + assert args_history[-1].stdin == "test" proc = self.container.exec(["ls"]) - self.assertIsNotNone(proc.stdin) - self.assertIsNone(args_history[-1].stdin) + assert proc.stdin is not None + assert args_history[-1].stdin is None def test_exec_stdout_stderr(self): self.harness.handle_exec( @@ -5509,18 +5454,18 @@ def test_exec_stdout_stderr(self): stdout = io.StringIO() stderr = io.StringIO() proc = self.container.exec(["ls"], stderr=stderr, stdout=stdout) - self.assertIsNone(proc.stdout) - self.assertIsNone(proc.stderr) + assert proc.stdout is None + assert proc.stderr is None proc.wait() - self.assertEqual(stdout.getvalue(), "output") - self.assertEqual(stderr.getvalue(), "error") + assert stdout.getvalue() == "output" + assert stderr.getvalue() == "error" proc = self.container.exec(["ls"]) assert proc.stdout is not None # Not assertIsNotNone to help type checkers. assert proc.stderr is not None # Not assertIsNotNone to help type checkers. proc.wait() - self.assertEqual(proc.stdout.read(), "output") - self.assertEqual(proc.stderr.read(), "error") + assert proc.stdout.read() == "output" + assert proc.stderr.read() == "error" self.harness.handle_exec( self.container, [], result=ExecResult( @@ -5528,24 +5473,24 @@ def test_exec_stdout_stderr(self): stdout = io.StringIO() stderr = io.StringIO() proc = self.container.exec(["ls"], stderr=stderr, stdout=stdout) - self.assertEqual(stdout.getvalue(), "output") - self.assertEqual(stderr.getvalue(), "error") + assert stdout.getvalue() == "output" + assert stderr.getvalue() == "error" proc = self.container.exec(["ls"]) assert proc.stdout is not None # Not assertIsNotNone to help type checkers. assert proc.stderr is not None # Not assertIsNotNone to help type checkers. - self.assertEqual(proc.stdout.read(), "output") - self.assertEqual(proc.stderr.read(), "error") + assert proc.stdout.read() == "output" + assert proc.stderr.read() == "error" stdout = io.BytesIO() stderr = io.BytesIO() proc = self.container.exec(["ls"], stderr=stderr, stdout=stdout, encoding=None) - self.assertEqual(stdout.getvalue(), b"output") - self.assertEqual(stderr.getvalue(), b"error") + assert stdout.getvalue() == b"output" + assert stderr.getvalue() == b"error" proc = self.container.exec(["ls"], encoding=None) assert proc.stdout is not None # Not assertIsNotNone to help type checkers. assert proc.stderr is not None # Not assertIsNotNone to help type checkers. - self.assertEqual(proc.stdout.read(), b"output") - self.assertEqual(proc.stderr.read(), b"error") + assert proc.stdout.read() == b"output" + assert proc.stderr.read() == b"error" def test_exec_service_context(self): service: ops.pebble.ServiceDict = { @@ -5572,12 +5517,12 @@ def handler(args: ops.testing.ExecArgs): self.harness.handle_exec(self.container, ["ls"], handler=handler) self.container.exec(["ls"], service_context="test").wait() - self.assertEqual(args_history[-1].working_dir, "/tmp") # noqa: S108 - self.assertEqual(args_history[-1].user, "foo") - self.assertEqual(args_history[-1].user_id, 1) - self.assertEqual(args_history[-1].group, "bar") - self.assertEqual(args_history[-1].group_id, 2) - self.assertDictEqual(args_history[-1].environment, {"foo": "bar", "foobar": "barfoo"}) + assert args_history[-1].working_dir == "/tmp" # noqa: S108 + assert args_history[-1].user == "foo" + assert args_history[-1].user_id == 1 + assert args_history[-1].group == "bar" + assert args_history[-1].group_id == 2 + assert args_history[-1].environment == {"foo": "bar", "foobar": "barfoo"} self.container.exec(["ls"], service_context="test", @@ -5587,12 +5532,12 @@ def handler(args: ops.testing.ExecArgs): group="test_group", group_id=4, environment={"foo": "hello"}).wait() - self.assertEqual(args_history[-1].working_dir, "/test") - self.assertEqual(args_history[-1].user, "test") - self.assertEqual(args_history[-1].user_id, 3) - self.assertEqual(args_history[-1].group, "test_group") - self.assertEqual(args_history[-1].group_id, 4) - self.assertDictEqual(args_history[-1].environment, {"foo": "hello", "foobar": "barfoo"}) + assert args_history[-1].working_dir == "/test" + assert args_history[-1].user == "test" + assert args_history[-1].user_id == 3 + assert args_history[-1].group == "test_group" + assert args_history[-1].group_id == 4 + assert args_history[-1].environment == {"foo": "hello", "foobar": "barfoo"} class TestActions(unittest.TestCase): @@ -5662,43 +5607,43 @@ def test_before_begin(self): harness = ops.testing.Harness(ops.CharmBase, meta=''' name: test ''') - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): harness.run_action("fail") def test_invalid_action(self): # This action isn't in the metadata at all. - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): self.harness.run_action("another-action") # Also check that we're not exposing the action with the dash to underscore replacement. - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): self.harness.run_action("log_and_results") def test_run_action(self): out = self.harness.run_action("simple") - self.assertEqual(out.logs, []) - self.assertEqual(out.results, {}) - self.assertTrue(self.harness.charm.simple_was_called) + assert out.logs == [] + assert out.results == {} + assert self.harness.charm.simple_was_called def test_fail_action(self): self._action_results.clear() self._action_results["partial"] = "foo" - with self.assertRaises(ops.testing.ActionFailed) as cm: + with pytest.raises(ops.testing.ActionFailed) as excinfo: self.harness.run_action("fail") - self.assertEqual(cm.exception.message, "something went wrong") - self.assertEqual(cm.exception.output.logs, ["some progress", "more progress"]) - self.assertEqual(cm.exception.output.results, {"partial": "foo"}) + assert excinfo.value.message == "something went wrong" + assert excinfo.value.output.logs == ["some progress", "more progress"] + assert excinfo.value.output.results == {"partial": "foo"} def test_required_param(self): - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): self.harness.run_action("unobserved-param-tester") - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): self.harness.run_action("unobserved-param-tester", {"bar": "baz"}) self.harness.run_action("unobserved-param-tester", {"foo": "baz"}) self.harness.run_action("unobserved-param-tester", {"foo": "baz", "bar": "qux"}) def test_additional_params(self): self.harness.run_action("simple", {"foo": "bar"}) - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.harness.run_action("unobserved-param-tester", {"foo": "bar", "qux": "baz"}) self.harness.run_action("simple", { "string": "hello", @@ -5710,29 +5655,29 @@ def test_additional_params(self): def test_logs_and_results(self): out = self.harness.run_action("log-and-results") - self.assertEqual(out.logs, ["Step 1", "Step 2"]) - self.assertEqual(out.results, {"result1": "foo-default", "result2": None}) + assert out.logs == ["Step 1", "Step 2"] + assert out.results == {"result1": "foo-default", "result2": None} out = self.harness.run_action("log-and-results", {"foo": "baz", "bar": 28}) - self.assertEqual(out.results, {"result1": "baz", "result2": 28}) + assert out.results == {"result1": "baz", "result2": 28} def test_bad_results(self): # We can't have results that collide when flattened. self._action_results.clear() self._action_results["a"] = {"b": 1} self._action_results["a.b"] = 2 - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.harness.run_action("results") # There are some result key names we cannot use. prohibited_keys = "stdout", "stdout-encoding", "stderr", "stderr-encoding" for key in prohibited_keys: self._action_results.clear() self._action_results[key] = "foo" - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): self.harness.run_action("results") # There are some additional rules around what result keys are valid. self._action_results.clear() self._action_results["A"] = "foo" - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.harness.run_action("results") @@ -5756,17 +5701,17 @@ def test_notify_basics(self): id3 = harness.pebble_notify('bar', 'example.com/n1') id1b = harness.pebble_notify('foo', 'example.com/n1') - self.assertIsInstance(id1a, str) - self.assertNotEqual(id1a, '') - self.assertEqual(id1a, id1b) + assert isinstance(id1a, str) + assert id1a != '' + assert id1a == id1b - self.assertIsInstance(id2, str) - self.assertNotEqual(id2, '') - self.assertNotEqual(id2, id1a) + assert isinstance(id2, str) + assert id2 != '' + assert id2 != id1a - self.assertIsInstance(id3, str) - self.assertNotEqual(id3, '') - self.assertNotEqual(id3, id2) + assert isinstance(id3, str) + assert id3 != '' + assert id3 != id2 expected_changes = [{ 'name': 'pebble-custom-notice', @@ -5793,7 +5738,7 @@ def test_notify_basics(self): 'notice_type': 'custom', 'notice_key': 'example.com/n1', }] - self.assertEqual(harness.charm.changes, expected_changes) + assert harness.charm.changes == expected_changes def test_notify_no_repeat(self): """Ensure event doesn't get triggered when notice occurs but doesn't repeat.""" @@ -5812,7 +5757,7 @@ def test_notify_no_repeat(self): id1b = harness.pebble_notify('foo', 'example.com/n1', repeat_after=datetime.timedelta(days=1)) - self.assertEqual(id1a, id1b) + assert id1a == id1b expected_changes = [{ 'name': 'pebble-custom-notice', @@ -5821,7 +5766,7 @@ def test_notify_no_repeat(self): 'notice_type': 'custom', 'notice_key': 'example.com/n1', }] - self.assertEqual(harness.charm.changes, expected_changes) + assert harness.charm.changes == expected_changes def test_notify_no_begin(self): num_notices = 0 @@ -5846,9 +5791,9 @@ def _on_pebble_custom_notice(self, event: ops.PebbleCustomNoticeEvent): id = harness.pebble_notify('c1', 'example.com/n1') - self.assertIsInstance(id, str) - self.assertNotEqual(id, '') - self.assertEqual(num_notices, 0) + assert isinstance(id, str) + assert id != '' + assert num_notices == 0 class PebbleNoticesMixin: @@ -5870,27 +5815,27 @@ def test_get_notice_by_id(self): client.notify(pebble.NoticeType.CUSTOM, key2, data={'k': 'v', 'foo': 'bar'}) notice = client.get_notice(id1) - self.assertEqual(notice.id, id1) - self.assertEqual(notice.type, pebble.NoticeType.CUSTOM) - self.assertEqual(notice.key, key1) - self.assertEqual(notice.first_occurred, notice.last_occurred) - self.assertEqual(notice.first_occurred, notice.last_repeated) - self.assertEqual(notice.occurrences, 1) - self.assertEqual(notice.last_data, {}) - self.assertIsNone(notice.repeat_after) - self.assertEqual(notice.expire_after, datetime.timedelta(days=7)) + assert notice.id == id1 + assert notice.type == pebble.NoticeType.CUSTOM + assert notice.key == key1 + assert notice.first_occurred == notice.last_occurred + assert notice.first_occurred == notice.last_repeated + assert notice.occurrences == 1 + assert notice.last_data == {} + assert notice.repeat_after is None + assert notice.expire_after == datetime.timedelta(days=7) notice = client.get_notice(id2) - self.assertEqual(notice.id, id2) - self.assertEqual(notice.type, pebble.NoticeType.CUSTOM) - self.assertEqual(notice.key, key2) - self.assertLess(notice.first_occurred, notice.last_occurred) - self.assertLess(notice.first_occurred, notice.last_repeated) - self.assertEqual(notice.last_occurred, notice.last_repeated) - self.assertEqual(notice.occurrences, 2) - self.assertEqual(notice.last_data, {'k': 'v', 'foo': 'bar'}) - self.assertIsNone(notice.repeat_after) - self.assertEqual(notice.expire_after, datetime.timedelta(days=7)) + assert notice.id == id2 + assert notice.type == pebble.NoticeType.CUSTOM + assert notice.key == key2 + assert notice.first_occurred < notice.last_occurred + assert notice.first_occurred < notice.last_repeated + assert notice.last_occurred == notice.last_repeated + assert notice.occurrences == 2 + assert notice.last_data == {'k': 'v', 'foo': 'bar'} + assert notice.repeat_after is None + assert notice.expire_after == datetime.timedelta(days=7) def test_get_notices(self): client = self.client @@ -5906,25 +5851,25 @@ def test_get_notices(self): client.notify(pebble.NoticeType.CUSTOM, key3) notices = client.get_notices() - self.assertGreaterEqual(len(notices), 3) + assert len(notices) >= 3 notices = client.get_notices(keys=[key1, key2, key3]) - self.assertEqual(len(notices), 3) - self.assertEqual(notices[0].key, key1) - self.assertEqual(notices[1].key, key2) - self.assertEqual(notices[2].key, key3) - self.assertLess(notices[0].last_repeated, notices[1].last_repeated) - self.assertLess(notices[1].last_repeated, notices[2].last_repeated) + assert len(notices) == 3 + assert notices[0].key == key1 + assert notices[1].key == key2 + assert notices[2].key == key3 + assert notices[0].last_repeated < notices[1].last_repeated + assert notices[1].last_repeated < notices[2].last_repeated notices = client.get_notices(keys=[key2]) - self.assertEqual(len(notices), 1) - self.assertEqual(notices[0].key, key2) + assert len(notices) == 1 + assert notices[0].key == key2 notices = client.get_notices(keys=[key1, key3]) - self.assertEqual(len(notices), 2) - self.assertEqual(notices[0].key, key1) - self.assertEqual(notices[1].key, key3) - self.assertLess(notices[0].last_repeated, notices[1].last_repeated) + assert len(notices) == 2 + assert notices[0].key == key1 + assert notices[1].key == key3 + assert notices[0].last_repeated < notices[1].last_repeated class TestNotices(unittest.TestCase, _TestingPebbleClientMixin, PebbleNoticesMixin): @@ -5960,11 +5905,11 @@ def _on_start(self, event: ops.StartEvent): harness.set_cloud_spec(ops.CloudSpec.from_dict(cloud_spec_dict)) harness.begin() harness.charm.on.start.emit() - self.assertEqual(harness.charm.cloud_spec, ops.CloudSpec.from_dict(cloud_spec_dict)) + assert harness.charm.cloud_spec == ops.CloudSpec.from_dict(cloud_spec_dict) def test_get_cloud_spec_without_set_error(self): harness = ops.testing.Harness(ops.CharmBase) self.addCleanup(harness.cleanup) harness.begin() - with self.assertRaises(ops.ModelError): + with pytest.raises(ops.ModelError): harness.model.get_cloud_spec()