-
Notifications
You must be signed in to change notification settings - Fork 342
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[PR #961/898ade59 backport][stable-4] Unit test cleanup This is a backport of PR #961 as merged into main (898ade5). SUMMARY Speaking to mattclay, pytest based unit tests are generally considered preferred over unittest based unit tests. For the sake of having "good" examples in amazon.aws, migrates unittest based tests over to pytest Additionally: Moves tests about to reflect module_utils Cleans up the boto3/botocore test skipping uses "pytest.raises" rather than try/except blocks Cleans up unused variables Cleans up unused imports fixes s3_object unit test (was trying to import from the old location, redirects don't handle this) ISSUE TYPE Feature Pull Request COMPONENT NAME tests/units ADDITIONAL INFORMATION Reviewed-by: Mark Chappell <None>
- Loading branch information
1 parent
f29ec54
commit d30bcb5
Showing
27 changed files
with
752 additions
and
818 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# (c) 2022 Red Hat Inc. | ||
# | ||
# This file is part of Ansible | ||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) | ||
|
||
from __future__ import (absolute_import, division, print_function) | ||
__metaclass__ = type | ||
|
||
import pytest | ||
|
||
from ansible_collections.amazon.aws.plugins.module_utils.arn import is_outpost_arn | ||
|
||
outpost_arn_test_inputs = [ | ||
("arn:aws:outposts:us-east-1:123456789012:outpost/op-1234567890abcdef0", True), | ||
("arn:aws:outposts:us-east-1:123456789012:outpost/op-1234567890abcdef0123", False), | ||
("arn:aws:outpost:us-east-1: 123456789012:outpost/ op-1234567890abcdef0", False), | ||
("ars:aws:outposts:us-east-1: 123456789012:outpost/ op-1234567890abcdef0", False), | ||
("arn:was:outposts:us-east-1: 123456789012:outpost/ op-1234567890abcdef0", False), | ||
] | ||
|
||
|
||
@pytest.mark.parametrize("outpost_arn, result", outpost_arn_test_inputs) | ||
def test_is_outpost_arn(outpost_arn, result): | ||
assert is_outpost_arn(outpost_arn) == result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
214 changes: 214 additions & 0 deletions
214
tests/unit/module_utils/botocore/test_is_boto3_error_code.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
# -*- coding: utf-8 -*- | ||
# (c) 2020 Red Hat Inc. | ||
# | ||
# This file is part of Ansible | ||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) | ||
|
||
from __future__ import (absolute_import, division, print_function) | ||
__metaclass__ = type | ||
|
||
import pytest | ||
|
||
try: | ||
import botocore | ||
except ImportError: | ||
# Handled by HAS_BOTO3 | ||
pass | ||
|
||
from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code | ||
from ansible_collections.amazon.aws.plugins.module_utils.botocore import HAS_BOTO3 | ||
|
||
if not HAS_BOTO3: | ||
pytestmark = pytest.mark.skip("test_is_boto3_error_code.py requires the python modules 'boto3' and 'botocore'") | ||
|
||
|
||
class Boto3ErrorCodeTestSuite(): | ||
|
||
def _make_denied_exception(self): | ||
return botocore.exceptions.ClientError( | ||
{ | ||
"Error": { | ||
"Code": "AccessDenied", | ||
"Message": "User: arn:aws:iam::123456789012:user/ExampleUser " | ||
+ "is not authorized to perform: iam:GetUser on resource: user ExampleUser" | ||
}, | ||
"ResponseMetadata": { | ||
"RequestId": "01234567-89ab-cdef-0123-456789abcdef" | ||
} | ||
}, 'getUser') | ||
|
||
def _make_unexpected_exception(self): | ||
return botocore.exceptions.ClientError( | ||
{ | ||
"Error": { | ||
"Code": "SomeThingWentWrong", | ||
"Message": "Boom!" | ||
}, | ||
"ResponseMetadata": { | ||
"RequestId": "01234567-89ab-cdef-0123-456789abcdef" | ||
} | ||
}, 'someCall') | ||
|
||
def _make_encoded_exception(self): | ||
return botocore.exceptions.ClientError( | ||
{ | ||
"Error": { | ||
"Code": "PermissionDenied", | ||
"Message": "You are not authorized to perform this operation. Encoded authorization failure message: " + | ||
"fEwXX6llx3cClm9J4pURgz1XPnJPrYexEbrJcLhFkwygMdOgx_-aEsj0LqRM6Kxt2HVI6prUhDwbJqBo9U2V7iRKZ" + | ||
"T6ZdJvHH02cXmD0Jwl5vrTsf0PhBcWYlH5wl2qME7xTfdolEUr4CzumCiti7ETiO-RDdHqWlasBOW5bWsZ4GSpPdU" + | ||
"06YAX0TfwVBs48uU5RpCHfz1uhSzez-3elbtp9CmTOHLt5pzJodiovccO55BQKYLPtmJcs6S9YLEEogmpI4Cb1D26" + | ||
"fYahDh51jEmaohPnW5pb1nQe2yPEtuIhtRzNjhFCOOMwY5DBzNsymK-Gj6eJLm7FSGHee4AHLU_XmZMe_6bcLAiOx" + | ||
"6Zdl65Kdd0hLcpwVxyZMi27HnYjAdqRlV3wuCW2PkhAW14qZQLfiuHZDEwnPe2PBGSlFcCmkQvJvX-YLoA7Uyc2wf" + | ||
"NX5RJm38STwfiJSkQaNDhHKTWKiLOsgY4Gze6uZoG7zOcFXFRyaA4cbMmI76uyBO7j-9uQUCtBYqYto8x_9CUJcxI" + | ||
"VC5SPG_C1mk-WoDMew01f0qy-bNaCgmJ9TOQGd08FyuT1SaMpCC0gX6mHuOnEgkFw3veBIowMpp9XcM-yc42fmIOp" + | ||
"FOdvQO6uE9p55Qc-uXvsDTTvT3A7EeFU8a_YoAIt9UgNYM6VTvoprLz7dBI_P6C-bdPPZCY2amm-dJNVZelT6TbJB" + | ||
"H_Vxh0fzeiSUBersy_QzB0moc-vPWgnB-IkgnYLV-4L3K0L2" | ||
}, | ||
"ResponseMetadata": { | ||
"RequestId": "01234567-89ab-cdef-0123-456789abcdef" | ||
} | ||
}, 'someCall') | ||
|
||
def _make_botocore_exception(self): | ||
return botocore.exceptions.EndpointConnectionError(endpoint_url='junk.endpoint') | ||
|
||
### | ||
# Test that is_boto3_error_code does what's expected when used in a try/except block | ||
# (where we don't explicitly pass an exception to the function) | ||
### | ||
|
||
def _do_try_code(self, exception, codes): | ||
try: | ||
raise exception | ||
except is_boto3_error_code(codes) as e: | ||
return e | ||
|
||
def test_is_boto3_error_code_single__raise__client(self): | ||
# 'AccessDenied' error, should be caught in our try/except in _do_try_code | ||
thrown_exception = self._make_denied_exception() | ||
codes_to_catch = 'AccessDenied' | ||
|
||
caught_exception = self._do_try_code(thrown_exception, codes_to_catch) | ||
assert caught_exception == thrown_exception | ||
|
||
def test_is_boto3_error_code_single__raise__unexpected(self): | ||
# 'SomeThingWentWrong' error, shouldn't be caught because the Code doesn't match | ||
thrown_exception = self._make_unexpected_exception() | ||
codes_to_catch = 'AccessDenied' | ||
|
||
with pytest.raises(botocore.exceptions.ClientError) as context: | ||
self._do_try_code(thrown_exception, codes_to_catch) | ||
assert context.value == thrown_exception | ||
|
||
def test_is_boto3_error_code_single__raise__botocore(self): | ||
# BotoCoreExceptions don't have an error code, so shouldn't be caught (and shouldn't throw | ||
# some other error due to the missing 'Code' data on the exception) | ||
thrown_exception = self._make_botocore_exception() | ||
codes_to_catch = 'AccessDenied' | ||
|
||
with pytest.raises(botocore.exceptions.BotoCoreError) as context: | ||
self._do_try_code(thrown_exception, codes_to_catch) | ||
|
||
assert context.value == thrown_exception | ||
|
||
def test_is_boto3_error_code_multiple__raise__client(self): | ||
# 'AccessDenied' error, should be caught in our try/except in _do_try_code | ||
# test with multiple possible codes to catch | ||
thrown_exception = self._make_denied_exception() | ||
codes_to_catch = ['AccessDenied', 'NotAccessDenied'] | ||
|
||
caught_exception = self._do_try_code(thrown_exception, codes_to_catch) | ||
assert caught_exception == thrown_exception | ||
|
||
thrown_exception = self._make_denied_exception() | ||
codes_to_catch = ['NotAccessDenied', 'AccessDenied'] | ||
|
||
caught_exception = self._do_try_code(thrown_exception, codes_to_catch) | ||
assert caught_exception == thrown_exception | ||
|
||
def test_is_boto3_error_code_multiple__raise__unexpected(self): | ||
# 'SomeThingWentWrong' error, shouldn't be caught because the Code doesn't match | ||
# test with multiple possible codes to catch | ||
thrown_exception = self._make_unexpected_exception() | ||
codes_to_catch = ['NotAccessDenied', 'AccessDenied'] | ||
|
||
with pytest.raises(botocore.exceptions.ClientError) as context: | ||
self._do_try_code(thrown_exception, codes_to_catch) | ||
assert context.value == thrown_exception | ||
|
||
def test_is_boto3_error_code_multiple__raise__botocore(self): | ||
# BotoCoreErrors don't have an error code, so shouldn't be caught (and shouldn't throw | ||
# some other error due to the missing 'Code' data on the exception) | ||
# test with multiple possible codes to catch | ||
thrown_exception = self._make_botocore_exception() | ||
codes_to_catch = ['NotAccessDenied', 'AccessDenied'] | ||
|
||
with pytest.raises(botocore.exceptions.BotoCoreError) as context: | ||
self._do_try_code(thrown_exception, codes_to_catch) | ||
assert context.value == thrown_exception | ||
|
||
### | ||
# Test that is_boto3_error_code returns what we expect when explicitly passed an exception | ||
### | ||
|
||
def test_is_boto3_error_code_single__pass__client(self): | ||
passed_exception = self._make_denied_exception() | ||
returned_exception = is_boto3_error_code('AccessDenied', e=passed_exception) | ||
assert isinstance(passed_exception, returned_exception) | ||
assert issubclass(returned_exception, botocore.exceptions.ClientError) | ||
assert not issubclass(returned_exception, botocore.exceptions.BotoCoreError) | ||
assert issubclass(returned_exception, Exception) | ||
assert returned_exception.__name__ != "NeverEverRaisedException" | ||
|
||
def test_is_boto3_error_code_single__pass__unexpected(self): | ||
passed_exception = self._make_unexpected_exception() | ||
returned_exception = is_boto3_error_code('AccessDenied', e=passed_exception) | ||
assert not isinstance(passed_exception, returned_exception) | ||
assert not issubclass(returned_exception, botocore.exceptions.ClientError) | ||
assert not issubclass(returned_exception, botocore.exceptions.BotoCoreError) | ||
assert issubclass(returned_exception, Exception) | ||
assert returned_exception.__name__ == "NeverEverRaisedException" | ||
|
||
def test_is_boto3_error_code_single__pass__botocore(self): | ||
passed_exception = self._make_botocore_exception() | ||
returned_exception = is_boto3_error_code('AccessDenied', e=passed_exception) | ||
assert not isinstance(passed_exception, returned_exception) | ||
assert not issubclass(returned_exception, botocore.exceptions.ClientError) | ||
assert not issubclass(returned_exception, botocore.exceptions.BotoCoreError) | ||
assert issubclass(returned_exception, Exception) | ||
assert returned_exception.__name__ == "NeverEverRaisedException" | ||
|
||
def test_is_boto3_error_code_multiple__pass__client(self): | ||
passed_exception = self._make_denied_exception() | ||
returned_exception = is_boto3_error_code(['NotAccessDenied', 'AccessDenied'], e=passed_exception) | ||
assert isinstance(passed_exception, returned_exception) | ||
assert issubclass(returned_exception, botocore.exceptions.ClientError) | ||
assert not issubclass(returned_exception, botocore.exceptions.BotoCoreError) | ||
assert issubclass(returned_exception, Exception) | ||
assert returned_exception.__name__ != "NeverEverRaisedException" | ||
|
||
returned_exception = is_boto3_error_code(['AccessDenied', 'NotAccessDenied'], e=passed_exception) | ||
assert isinstance(passed_exception, returned_exception) | ||
assert issubclass(returned_exception, botocore.exceptions.ClientError) | ||
assert not issubclass(returned_exception, botocore.exceptions.BotoCoreError) | ||
assert issubclass(returned_exception, Exception) | ||
assert returned_exception.__name__ != "NeverEverRaisedException" | ||
|
||
def test_is_boto3_error_code_multiple__pass__unexpected(self): | ||
passed_exception = self._make_unexpected_exception() | ||
returned_exception = is_boto3_error_code(['NotAccessDenied', 'AccessDenied'], e=passed_exception) | ||
assert not isinstance(passed_exception, returned_exception) | ||
assert not issubclass(returned_exception, botocore.exceptions.ClientError) | ||
assert not issubclass(returned_exception, botocore.exceptions.BotoCoreError) | ||
assert issubclass(returned_exception, Exception) | ||
assert returned_exception.__name__ == "NeverEverRaisedException" | ||
|
||
def test_is_boto3_error_code_multiple__pass__botocore(self): | ||
passed_exception = self._make_botocore_exception() | ||
returned_exception = is_boto3_error_code(['NotAccessDenied', 'AccessDenied'], e=passed_exception) | ||
assert not isinstance(passed_exception, returned_exception) | ||
assert not issubclass(returned_exception, botocore.exceptions.ClientError) | ||
assert not issubclass(returned_exception, botocore.exceptions.BotoCoreError) | ||
assert issubclass(returned_exception, Exception) | ||
assert returned_exception.__name__ == "NeverEverRaisedException" |
Oops, something went wrong.