Skip to content

Commit

Permalink
Add Python 3.12 to supported versions (SatelliteQE#12793)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
JacobCallahan authored and shweta83 committed Apr 10, 2024
1 parent 8f4b8e7 commit af71512
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 55 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/weekly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 6 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Version updates managed by dependabot

betelgeuse==1.11.0
broker[docker]==0.4.1
# broker[docker]==0.4.1 - Temporarily disabled, see below
cryptography==42.0.2
deepdiff==6.7.1
dynaconf[vault]==3.2.4
Expand Down Expand Up @@ -30,4 +30,9 @@ wrapanapi==3.6.0
# Get airgun, nailgun and upgrade from master
git+https://github.com/SatelliteQE/airgun.git@master#egg=airgun
git+https://github.com/SatelliteQE/nailgun.git@master#egg=nailgun
# Broker currently is unable to push to PyPi due to [1] and [2]
# In the meantime, we install directly from the repo
# [1] - https://github.com/ParallelSSH/ssh2-python/issues/193
# [2] - https://github.com/pypi/warehouse/issues/7136
git+https://github.com/SatelliteQE/[email protected]#egg=broker
--editable .
8 changes: 4 additions & 4 deletions robottelo/cli/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,26 +132,26 @@ 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'

result = cls.execute(cls._construct_command(options))
result = cls.execute(cls._construct_command(options), ignore_stderr=False, timeout=timeout)

return result

@classmethod
def dump(cls, options=None):
def dump(cls, options=None, timeout=None):
"""
Displays the content for existing partition table.
"""

cls.command_sub = 'dump'

result = cls.execute(cls._construct_command(options))
result = cls.execute(cls._construct_command(options), ignore_stderr=False, timeout=timeout)

return result

Expand Down
4 changes: 2 additions & 2 deletions robottelo/cli/subscription.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
134 changes: 97 additions & 37 deletions tests/robottelo/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ def test_add_operating_system(self, construct, execute):
assert 'add-operatingsystem' != Base.command_sub
assert execute.return_value == Base.add_operating_system(options)
assert 'add-operatingsystem' == Base.command_sub
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')
Expand All @@ -159,8 +159,8 @@ def test_add_create_with_empty_result(self, construct, execute):
execute.return_value = []
assert execute.return_value == Base.create()
assert 'create' == Base.command_sub
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')
Expand All @@ -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 'create' == Base.command_sub
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')
Expand All @@ -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 'create' == Base.command_sub
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')
Expand All @@ -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 'create' == Base.command_sub
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')
Expand All @@ -215,17 +215,19 @@ def test_add_create_with_result_dct_id_required_org_error(self, construct, execu
with pytest.raises(CLIError):
Base.create()
assert 'create' == Base.command_sub
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
):
"""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')
Expand Down Expand Up @@ -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},
)
Expand All @@ -329,27 +351,24 @@ 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')
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 'list' == Base.command_sub
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')
Expand All @@ -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):
Expand Down
18 changes: 9 additions & 9 deletions tests/robottelo/test_cli_method_calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'])
Expand All @@ -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
Expand All @@ -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'])
Expand All @@ -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)

0 comments on commit af71512

Please sign in to comment.