-
Notifications
You must be signed in to change notification settings - Fork 129
test: Extend the type checking of the ops-scenario tests #2234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9a4bab3
0f3eb00
14b2923
34adbbd
8350e8d
0a8c1d5
63c039a
58e19b0
d82d82a
eceb259
49893fc
86ff536
eab3f63
5f62371
c43500e
f45fcb8
c070335
b95c0c7
85b2b0f
efc5473
95f9ada
3397086
8cf2cac
0edbff3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,8 @@ | |
|
|
||
| from __future__ import annotations | ||
|
|
||
| from typing import Any | ||
|
|
||
| import pytest | ||
| from scenario import Context | ||
| from scenario.state import State, _Action, _next_action_id | ||
|
|
@@ -13,7 +15,7 @@ | |
|
|
||
|
|
||
| @pytest.fixture(scope='function') | ||
| def mycharm(): | ||
| def mycharm() -> type[CharmBase]: | ||
| class MyCharm(CharmBase): | ||
| _evt_handler = None | ||
|
|
||
|
|
@@ -22,16 +24,16 @@ def __init__(self, framework: Framework): | |
| for evt in self.on.events().values(): | ||
| self.framework.observe(evt, self._on_event) | ||
|
|
||
| def _on_event(self, event): | ||
| def _on_event(self, event: ActionEvent): | ||
| if handler := self._evt_handler: | ||
| handler(event) | ||
|
|
||
| return MyCharm | ||
|
|
||
|
|
||
| @pytest.mark.parametrize('baz_value', (True, False)) | ||
| def test_action_event(mycharm, baz_value): | ||
| ctx = Context( | ||
| def test_action_event(mycharm: type[CharmBase], baz_value: bool): | ||
| ctx: Context[CharmBase] = Context( | ||
| mycharm, | ||
| meta={'name': 'foo'}, | ||
| actions={'foo': {'params': {'bar': {'type': 'number'}, 'baz': {'type': 'boolean'}}}}, | ||
|
|
@@ -47,11 +49,11 @@ def test_action_event(mycharm, baz_value): | |
|
|
||
| def test_action_no_results(): | ||
| class MyCharm(CharmBase): | ||
| def __init__(self, framework): | ||
| def __init__(self, framework: Framework): | ||
| super().__init__(framework) | ||
| framework.observe(self.on.act_action, self._on_act_action) | ||
|
|
||
| def _on_act_action(self, _): | ||
| def _on_act_action(self, _: ActionEvent): | ||
| pass | ||
|
|
||
| ctx = Context(MyCharm, meta={'name': 'foo'}, actions={'act': {}}) | ||
|
|
@@ -61,27 +63,27 @@ def _on_act_action(self, _): | |
|
|
||
|
|
||
| @pytest.mark.parametrize('res_value', ('one', 1, [2], ['bar'], (1,), {1, 2})) | ||
| def test_action_event_results_invalid(mycharm, res_value): | ||
| def test_action_event_results_invalid(mycharm: type[CharmBase], res_value: object): | ||
| def handle_evt(charm: CharmBase, evt: ActionEvent): | ||
| with pytest.raises((TypeError, AttributeError)): | ||
| evt.set_results(res_value) | ||
| evt.set_results(res_value) # type: ignore | ||
|
|
||
| mycharm._evt_handler = handle_evt | ||
| mycharm._evt_handler = handle_evt # type: ignore | ||
|
|
||
| ctx = Context(mycharm, meta={'name': 'foo'}, actions={'foo': {}}) | ||
| ctx.run(ctx.on.action('foo'), State()) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize('res_value', ({'a': {'b': {'c'}}}, {'d': 'e'})) | ||
| def test_action_event_results_valid(mycharm, res_value): | ||
| def handle_evt(_: CharmBase, evt): | ||
| def test_action_event_results_valid(mycharm: type[CharmBase], res_value: dict[str, Any]): | ||
| def handle_evt(_: CharmBase, evt: ActionEvent): | ||
| if not isinstance(evt, ActionEvent): | ||
| return | ||
| evt.set_results(res_value) | ||
| evt.log('foo') | ||
| evt.log('bar') | ||
|
|
||
| mycharm._evt_handler = handle_evt | ||
| mycharm._evt_handler = handle_evt # type: ignore | ||
|
|
||
| ctx = Context(mycharm, meta={'name': 'foo'}, actions={'foo': {}}) | ||
|
|
||
|
|
@@ -91,7 +93,7 @@ def handle_evt(_: CharmBase, evt): | |
|
|
||
|
|
||
| @pytest.mark.parametrize('res_value', ({'a': {'b': {'c'}}}, {'d': 'e'})) | ||
| def test_action_event_outputs(mycharm, res_value): | ||
| def test_action_event_outputs(mycharm: type[CharmBase], res_value: dict[str, Any]): | ||
| def handle_evt(_: CharmBase, evt: ActionEvent): | ||
| if not isinstance(evt, ActionEvent): | ||
| return | ||
|
|
@@ -101,7 +103,7 @@ def handle_evt(_: CharmBase, evt: ActionEvent): | |
| evt.log('log2') | ||
| evt.fail('failed becozz') | ||
|
|
||
| mycharm._evt_handler = handle_evt | ||
| mycharm._evt_handler = handle_evt # type: ignore | ||
|
|
||
| ctx = Context(mycharm, meta={'name': 'foo'}, actions={'foo': {}}) | ||
| with pytest.raises(ActionFailed) as exc_info: | ||
|
|
@@ -111,27 +113,27 @@ def handle_evt(_: CharmBase, evt: ActionEvent): | |
| assert ctx.action_logs == ['log1', 'log2'] | ||
|
|
||
|
|
||
| def test_action_event_fail(mycharm): | ||
| def test_action_event_fail(mycharm: type[CharmBase]): | ||
| def handle_evt(_: CharmBase, evt: ActionEvent): | ||
| if not isinstance(evt, ActionEvent): | ||
| return | ||
| evt.fail('action failed!') | ||
|
|
||
| mycharm._evt_handler = handle_evt | ||
| mycharm._evt_handler = handle_evt # type: ignore | ||
|
|
||
| ctx = Context(mycharm, meta={'name': 'foo'}, actions={'foo': {}}) | ||
| with pytest.raises(ActionFailed) as exc_info: | ||
| ctx.run(ctx.on.action('foo'), State()) | ||
| assert exc_info.value.message == 'action failed!' | ||
|
|
||
|
|
||
| def test_action_event_fail_context_manager(mycharm): | ||
| def test_action_event_fail_context_manager(mycharm: type[CharmBase]): | ||
| def handle_evt(_: CharmBase, evt: ActionEvent): | ||
| if not isinstance(evt, ActionEvent): | ||
| return | ||
| evt.fail('action failed!') | ||
|
|
||
| mycharm._evt_handler = handle_evt | ||
| mycharm._evt_handler = handle_evt # type: ignore | ||
|
|
||
| ctx = Context(mycharm, meta={'name': 'foo'}, actions={'foo': {}}) | ||
| with pytest.raises(ActionFailed) as exc_info: | ||
|
|
@@ -142,11 +144,11 @@ def handle_evt(_: CharmBase, evt: ActionEvent): | |
|
|
||
| def test_action_continues_after_fail(): | ||
| class MyCharm(CharmBase): | ||
| def __init__(self, framework): | ||
| def __init__(self, framework: Framework): | ||
| super().__init__(framework) | ||
| framework.observe(self.on.foo_action, self._on_foo_action) | ||
|
|
||
| def _on_foo_action(self, event): | ||
| def _on_foo_action(self, event: ActionEvent): | ||
| event.log('starting') | ||
| event.set_results({'initial': 'result'}) | ||
| event.fail('oh no!') | ||
|
|
@@ -160,44 +162,44 @@ def _on_foo_action(self, event): | |
| assert ctx.action_results == {'initial': 'result', 'final': 'result'} | ||
|
|
||
|
|
||
| def test_action_event_has_id(mycharm): | ||
| def test_action_event_has_id(mycharm: type[CharmBase]): | ||
| def handle_evt(_: CharmBase, evt: ActionEvent): | ||
| if not isinstance(evt, ActionEvent): | ||
| return | ||
| assert isinstance(evt.id, str) and evt.id != '' | ||
|
|
||
| mycharm._evt_handler = handle_evt | ||
| mycharm._evt_handler = handle_evt # type: ignore | ||
|
|
||
| ctx = Context(mycharm, meta={'name': 'foo'}, actions={'foo': {}}) | ||
| ctx.run(ctx.on.action('foo'), State()) | ||
|
|
||
|
|
||
| def test_action_event_has_override_id(mycharm): | ||
| def test_action_event_has_override_id(mycharm: type[CharmBase]): | ||
| uuid = '0ddba11-cafe-ba1d-5a1e-dec0debad' | ||
|
|
||
| def handle_evt(charm: CharmBase, evt: ActionEvent): | ||
| if not isinstance(evt, ActionEvent): | ||
| return | ||
| assert evt.id == uuid | ||
|
|
||
| mycharm._evt_handler = handle_evt | ||
| mycharm._evt_handler = handle_evt # type: ignore | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I drop something like this at the top of the file: class MyCharm(CharmBase):
_evt_handler = Callable[[CharmBase, ActionEvent], None]And update the test argument to WDYT about that, or perhaps a Protocol?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess you disagree with this comment? I think we'd get value out of being able to turn on static type checking for these files now, and deferring further iteration til later. |
||
|
|
||
| ctx = Context(mycharm, meta={'name': 'foo'}, actions={'foo': {}}) | ||
| ctx.run(ctx.on.action('foo', id=uuid), State()) | ||
|
|
||
|
|
||
| def test_two_actions_same_context(): | ||
| class MyCharm(CharmBase): | ||
| def __init__(self, framework): | ||
| def __init__(self, framework: Framework): | ||
| super().__init__(framework) | ||
| framework.observe(self.on.foo_action, self._on_foo_action) | ||
| framework.observe(self.on.bar_action, self._on_bar_action) | ||
|
|
||
| def _on_foo_action(self, event): | ||
| def _on_foo_action(self, event: ActionEvent): | ||
| event.log('foo') | ||
| event.set_results({'foo': 'result'}) | ||
|
|
||
| def _on_bar_action(self, event): | ||
| def _on_bar_action(self, event: ActionEvent): | ||
| event.log('bar') | ||
| event.set_results({'bar': 'result'}) | ||
|
|
||
|
|
@@ -213,7 +215,7 @@ def _on_bar_action(self, event): | |
|
|
||
| def test_positional_arguments(): | ||
| with pytest.raises(TypeError): | ||
| _Action('foo', {}) | ||
| _Action('foo', {}) # type: ignore | ||
|
|
||
|
|
||
| def test_default_arguments(): | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still not to happy about these
type: ignoreall over but I guess the change is an improvement overall and silencing can be removed later.