From 1b70b297ee82629da2bd5e1f3f404131cea0a74d Mon Sep 17 00:00:00 2001 From: Jake Callahan Date: Wed, 7 Feb 2024 12:51:47 -0500 Subject: [PATCH] Add Python 3.12 to supported versions (#12793) * Add Python 3.12 to supported versions Python 3.12 is out and should be supported by Robottelo. Also switched the weekly workflow to use Python 3.12 instead of 3.11. * update unittest deprecations * Adjust unit tests for python 3.12 This commit introduces a few changes relating to unittest changes and differences post-removal of old behavior. --- .github/workflows/pull_request.yml | 2 +- .github/workflows/weekly.yml | 2 +- robottelo/cli/base.py | 8 +- robottelo/cli/subscription.py | 4 +- tests/robottelo/test_cli.py | 134 ++++++++++++++++------- tests/robottelo/test_cli_method_calls.py | 18 +-- 6 files changed, 114 insertions(+), 54 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 47963adca8a..3580cf23a3f 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.10', '3.11'] + python-version: ['3.10', '3.11', '3.12'] steps: - name: Checkout Robottelo uses: actions/checkout@v4 diff --git a/.github/workflows/weekly.yml b/.github/workflows/weekly.yml index 561a26e3e91..a08485c8f49 100644 --- a/.github/workflows/weekly.yml +++ b/.github/workflows/weekly.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.11] + python-version: [3.12] steps: - name: Checkout Robottelo uses: actions/checkout@v4 diff --git a/robottelo/cli/base.py b/robottelo/cli/base.py index 2a3ad35ff4f..8d258500b27 100644 --- a/robottelo/cli/base.py +++ b/robottelo/cli/base.py @@ -132,24 +132,24 @@ def delete(cls, options=None, timeout=None): return cls.execute(cls._construct_command(options), ignore_stderr=True, timeout=timeout) @classmethod - def delete_parameter(cls, options=None): + def delete_parameter(cls, options=None, timeout=None): """ Deletes parameter from record. """ cls.command_sub = 'delete-parameter' - return cls.execute(cls._construct_command(options)) + return cls.execute(cls._construct_command(options), ignore_stderr=False, timeout=timeout) @classmethod - def dump(cls, options=None): + def dump(cls, options=None, timeout=None): """ Displays the content for existing partition table. """ cls.command_sub = 'dump' - return cls.execute(cls._construct_command(options)) + return cls.execute(cls._construct_command(options), ignore_stderr=False, timeout=timeout) @classmethod def _get_username_password(cls, username=None, password=None): diff --git a/robottelo/cli/subscription.py b/robottelo/cli/subscription.py index 86b57d51d41..0e22eaeaccd 100644 --- a/robottelo/cli/subscription.py +++ b/robottelo/cli/subscription.py @@ -47,7 +47,7 @@ def refresh_manifest(cls, options=None, timeout=None): return cls.execute(cls._construct_command(options), ignore_stderr=True, timeout=timeout) @classmethod - def manifest_history(cls, options=None): + def manifest_history(cls, options=None, timeout=None): """Provided history for subscription manifest""" cls.command_sub = 'manifest-history' - return cls.execute(cls._construct_command(options)) + return cls.execute(cls._construct_command(options), ignore_stderr=True, timeout=timeout) diff --git a/tests/robottelo/test_cli.py b/tests/robottelo/test_cli.py index debbc8c6e42..a2568f5a584 100644 --- a/tests/robottelo/test_cli.py +++ b/tests/robottelo/test_cli.py @@ -149,8 +149,8 @@ def test_add_operating_system(self, construct, execute): assert Base.command_sub != 'add-operatingsystem' assert execute.return_value == Base.add_operating_system(options) assert Base.command_sub == 'add-operatingsystem' - construct.called_once_with(options) - execute.called_once_with(construct.return_value) + construct.assert_called_once_with(options) + execute.assert_called_once_with(construct.return_value) @mock.patch('robottelo.cli.base.Base.execute') @mock.patch('robottelo.cli.base.Base._construct_command') @@ -159,8 +159,8 @@ def test_add_create_with_empty_result(self, construct, execute): execute.return_value = [] assert execute.return_value == Base.create() assert Base.command_sub == 'create' - construct.called_once_with({}) - execute.called_once_with(construct.return_value, output_format='csv') + construct.assert_called_once_with({}) + execute.assert_called_once_with(construct.return_value, output_format='csv', timeout=None) @mock.patch('robottelo.cli.base.Base.info') @mock.patch('robottelo.cli.base.Base.execute') @@ -170,8 +170,8 @@ def test_add_create_with_result_dct_without_id(self, construct, execute, info): execute.return_value = [{'not_id': 'foo'}] assert execute.return_value == Base.create() assert Base.command_sub == 'create' - construct.called_once_with({}) - execute.called_once_with(construct.return_value, output_format='csv') + construct.assert_called_once_with({}) + execute.assert_called_once_with(construct.return_value, output_format='csv', timeout=None) assert not info.called @mock.patch('robottelo.cli.base.Base.info') @@ -185,9 +185,9 @@ def test_add_create_with_result_dct_with_id_not_required_org(self, construct, ex Base.command_requires_org = False assert execute.return_value == Base.create() assert Base.command_sub == 'create' - construct.called_once_with({}) - execute.called_once_with(construct.return_value, output_format='csv') - info.called_once_with({'id': 'foo'}) + construct.assert_called_once_with({}) + execute.assert_called_once_with(construct.return_value, output_format='csv', timeout=None) + info.assert_called_once_with({'id': 'foo'}) @mock.patch('robottelo.cli.base.Base.info') @mock.patch('robottelo.cli.base.Base.execute') @@ -200,9 +200,9 @@ def test_add_create_with_result_dct_with_id_required_org(self, construct, execut Base.command_requires_org = True assert execute.return_value == Base.create({'organization-id': 'org-id'}) assert Base.command_sub == 'create' - construct.called_once_with({}) - execute.called_once_with(construct.return_value, output_format='csv') - info.called_once_with({'id': 'foo', 'organization-id': 'org-id'}) + construct.assert_called_once_with({'organization-id': 'org-id'}) + execute.assert_called_once_with(construct.return_value, output_format='csv', timeout=None) + info.assert_called_once_with({'id': 'foo', 'organization-id': 'org-id'}) @mock.patch('robottelo.cli.base.Base.execute') @mock.patch('robottelo.cli.base.Base._construct_command') @@ -215,8 +215,8 @@ def test_add_create_with_result_dct_id_required_org_error(self, construct, execu with pytest.raises(CLIError): Base.create() assert Base.command_sub == 'create' - construct.called_once_with({}) - execute.called_once_with(construct.return_value, output_format='csv') + construct.assert_called_once_with({}) + execute.assert_called_once_with(construct.return_value, output_format='csv', timeout=None) def assert_cmd_execution( self, construct, execute, base_method, cmd_sub, ignore_stderr=False, **base_method_kwargs @@ -224,8 +224,10 @@ def assert_cmd_execution( """Asssert Base class method successfully executed""" assert execute.return_value == base_method(**base_method_kwargs) assert cmd_sub == Base.command_sub - construct.called_once_with({}) - execute.called_once_with(construct.return_value, ignore_stderr=ignore_stderr) + construct.assert_called_once_with(base_method_kwargs.get('options')) + execute.assert_called_once_with( + construct.return_value, ignore_stderr=ignore_stderr, timeout=None + ) @mock.patch('robottelo.cli.base.Base.execute') @mock.patch('robottelo.cli.base.Base._construct_command') @@ -308,16 +310,36 @@ def test_info_requires_organization_id(self, _): # noqa: PT019 - not a fixture with pytest.raises(CLIError): Base.info() + def assert_alt_cmd_execution( + self, + construct, + execute, + base_method, + cmd_sub, + call_kwargs, + command_kwarg=True, + **base_method_kwargs, + ): + """Asssert Base class method successfully executed""" + assert execute.return_value == base_method(**base_method_kwargs) + assert cmd_sub == Base.command_sub + construct.assert_called_once_with(base_method_kwargs.get('options')) + if command_kwarg: + execute.assert_called_once_with(command=construct.return_value, **call_kwargs) + else: + execute.assert_called_once_with(construct.return_value, **call_kwargs) + @mock.patch('robottelo.cli.base.hammer.parse_info') @mock.patch('robottelo.cli.base.Base.execute') @mock.patch('robottelo.cli.base.Base._construct_command') def test_info_without_parsing_response(self, construct, execute, parse): """Check info method execution without parsing response""" - self.assert_cmd_execution( + self.assert_alt_cmd_execution( construct, execute, Base.info, 'info', + call_kwargs={'output_format': 'json', 'return_raw_response': None}, output_format='json', options={'organization-id': 1}, ) @@ -329,18 +351,15 @@ def test_info_without_parsing_response(self, construct, execute, parse): def test_info_parsing_response(self, construct, execute, parse): """Check info method execution parsing response""" parse.return_value = execute.return_value = 'some_response' - self.assert_cmd_execution( - construct, execute, Base.info, 'info', options={'organization-id': 1} + self.assert_alt_cmd_execution( + construct, + execute, + Base.info, + 'info', + call_kwargs={'output_format': None, 'return_raw_response': None}, + options={'organization-id': 1}, ) - parse.called_once_with('some_response') - - # @mock.patch('robottelo.cli.base.Base.command_requires_org') - # def test_list_requires_organization_id(self, _): - # """Check list raises CLIError with organization-id is not present in - # options - # """ - # with pytest.raises(CLIError): - # Base.list() + parse.assert_called_once_with('some_response') @mock.patch('robottelo.cli.base.Base.execute') @mock.patch('robottelo.cli.base.Base._construct_command') @@ -348,8 +367,8 @@ def test_list_with_default_per_page(self, construct, execute): """Check list method set per_page as 1000 by default""" assert execute.return_value == Base.list(options={'organization-id': 1}) assert Base.command_sub == 'list' - construct.called_once_with({'per-page': 1000}) - execute.called_once_with(construct.return_value, output_format='csv') + construct.assert_called_once_with({'organization-id': 1, 'per-page': 10000}) + execute.assert_called_once_with(construct.return_value, output_format='csv') @mock.patch('robottelo.cli.base.Base.execute') @mock.patch('robottelo.cli.base.Base._construct_command') @@ -358,39 +377,80 @@ def test_list_without_per_page(self, construct, execute): list_with_per_page_false = partial( Base.list, per_page=False, options={'organization-id': 1} ) - self.assert_cmd_execution(construct, execute, list_with_per_page_false, 'list') + self.assert_alt_cmd_execution( + construct, + execute, + list_with_per_page_false, + 'list', + call_kwargs={'output_format': 'csv'}, + command_kwarg=False, + options={'organization-id': 1}, + ) @mock.patch('robottelo.cli.base.Base.execute') @mock.patch('robottelo.cli.base.Base._construct_command') def test_puppet_classes(self, construct, execute): """Check puppet_classes method execution""" - self.assert_cmd_execution(construct, execute, Base.puppetclasses, 'puppet-classes') + self.assert_alt_cmd_execution( + construct, + execute, + Base.puppetclasses, + 'puppet-classes', + call_kwargs={'output_format': 'csv'}, + command_kwarg=False, + ) @mock.patch('robottelo.cli.base.Base.execute') @mock.patch('robottelo.cli.base.Base._construct_command') def test_remove_operating_system(self, construct, execute): """Check remove_operating_system method execution""" - self.assert_cmd_execution( - construct, execute, Base.remove_operating_system, 'remove-operatingsystem' + self.assert_alt_cmd_execution( + construct, + execute, + Base.remove_operating_system, + 'remove-operatingsystem', + call_kwargs={}, + command_kwarg=False, ) @mock.patch('robottelo.cli.base.Base.execute') @mock.patch('robottelo.cli.base.Base._construct_command') def test_sc_params(self, construct, execute): """Check sc_params method execution""" - self.assert_cmd_execution(construct, execute, Base.sc_params, 'sc-params') + self.assert_alt_cmd_execution( + construct, + execute, + Base.sc_params, + 'sc-params', + call_kwargs={'output_format': 'csv'}, + command_kwarg=False, + ) @mock.patch('robottelo.cli.base.Base.execute') @mock.patch('robottelo.cli.base.Base._construct_command') def test_set_parameter(self, construct, execute): """Check set_parameter method execution""" - self.assert_cmd_execution(construct, execute, Base.set_parameter, 'set-parameter') + self.assert_alt_cmd_execution( + construct, + execute, + Base.set_parameter, + 'set-parameter', + call_kwargs={}, + command_kwarg=False, + ) @mock.patch('robottelo.cli.base.Base.execute') @mock.patch('robottelo.cli.base.Base._construct_command') def test_update(self, construct, execute): """Check update method execution""" - self.assert_cmd_execution(construct, execute, Base.update, 'update') + self.assert_alt_cmd_execution( + construct, + execute, + Base.update, + 'update', + call_kwargs={'output_format': 'csv', 'return_raw_response': None}, + command_kwarg=False, + ) class CLIErrorTests(unittest.TestCase): diff --git a/tests/robottelo/test_cli_method_calls.py b/tests/robottelo/test_cli_method_calls.py index 04d4c65e8cd..765a24d2044 100644 --- a/tests/robottelo/test_cli_method_calls.py +++ b/tests/robottelo/test_cli_method_calls.py @@ -40,8 +40,8 @@ def test_cli_org_method_called(mocker, command_sub): options = {'foo': 'bar'} assert execute.return_value == getattr(Org, command_sub.replace('-', '_'))(options) assert command_sub == Org.command_sub - assert construct.called_once_with(options) - assert execute.called_once_with(construct.return_value) + construct.assert_called_once_with(options) + execute.assert_called_once_with(construct.return_value) @pytest.mark.parametrize('command_sub', ['import-classes', 'refresh-features']) @@ -54,11 +54,11 @@ def test_cli_proxy_method_called(mocker, command_sub): options = {'foo': 'bar'} assert execute.return_value == getattr(Proxy, command_sub.replace('-', '_'))(options) assert command_sub == Proxy.command_sub - assert construct.called_once_with(options) - assert execute.called_once_with(construct.return_value) + construct.assert_called_once_with(options) + execute.assert_called_once_with(construct.return_value) -@pytest.mark.parametrize('command_sub', ['synchronize', 'remove-content', 'upload-content']) +@pytest.mark.parametrize('command_sub', ['remove-content', 'upload-content']) def test_cli_repository_method_called(mocker, command_sub): """Check Repository methods are called and command_sub edited This is a parametrized test called by Pytest for each of Repository methods @@ -68,8 +68,8 @@ def test_cli_repository_method_called(mocker, command_sub): options = {'foo': 'bar'} assert execute.return_value == getattr(Repository, command_sub.replace('-', '_'))(options) assert command_sub == Repository.command_sub - assert construct.called_once_with(options) - assert execute.called_once_with(construct.return_value) + construct.assert_called_once_with(options) + execute.assert_called_once_with(construct.return_value, output_format='csv', ignore_stderr=True) @pytest.mark.parametrize('command_sub', ['info', 'create']) @@ -94,5 +94,5 @@ def test_cli_subscription_method_called(mocker, command_sub): options = {'foo': 'bar'} assert execute.return_value == getattr(Subscription, command_sub.replace('-', '_'))(options) assert command_sub == Subscription.command_sub - assert construct.called_once_with(options) - assert execute.called_once_with(construct.return_value) + construct.assert_called_once_with(options) + execute.assert_called_once_with(construct.return_value, ignore_stderr=True, timeout=None)