Skip to content

fix(test) - Reduce dependency on AWS config file #201

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

Merged
merged 1 commit into from
Oct 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ disable=
too-few-public-methods, # triggers when inheriting
ungrouped-imports, # clashes with isort
duplicate-code, # broken, setup.py
wrong-import-order, # isort has precedence

[BASIC]

Expand Down
10 changes: 0 additions & 10 deletions tests/lib/boto3_proxy_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
from cloudformation_cli_python_lib.boto3_proxy import SessionProxy, _get_boto_session
from cloudformation_cli_python_lib.utils import Credentials

import botocore # pylint: disable=C0411


def test_get_boto_session_returns_proxy():
proxy = _get_boto_session(Credentials("", "", ""))
Expand All @@ -15,14 +13,6 @@ def test_get_boto_session_returns_none():
assert proxy is None


def test_can_create_boto_client():
proxy = _get_boto_session(Credentials("", "", ""))
client = proxy.client(
"s3", region_name="us-west-2"
) # just in case AWS_REGION not set in test environment
assert isinstance(client, botocore.client.BaseClient)


def test_can_retrieve_boto_session():
proxy = _get_boto_session(Credentials("", "", ""))
session = proxy.session
Expand Down
14 changes: 10 additions & 4 deletions tests/lib/cipher_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def test_create_kms_cipher():
with patch(
"cloudformation_cli_python_lib.cipher.aws_encryption_sdk.StrictAwsKmsMasterKeyProvider",
autospec=True,
):
), patch("boto3.client", autospec=True):
cipher = KmsCipher("encryptionKeyArn", "encryptionKeyRole")
assert cipher

Expand All @@ -35,7 +35,9 @@ def test_decrypt_credentials_success():
autospec=True,
), patch(
"cloudformation_cli_python_lib.cipher.aws_encryption_sdk.EncryptionSDKClient.decrypt"
) as mock_decrypt:
) as mock_decrypt, patch(
"boto3.client", autospec=True
):
mock_decrypt.return_value = (
b'{"accessKeyId": "IASAYK835GAIFHAHEI23", "secretAccessKey": "66iOGPN5LnpZorcLr8Kh25u8AbjHVllv5poh2O0", "sessionToken": "lameHS2vQOknSHWhdFYTxm2eJc1JMn9YBNI4nV4mXue945KPL6DHfW8EsUQT5zwssYEC1NvYP9yD6Y5s5lKR3chflOHPFsIe6eqg"}', # noqa: B950
Mock(),
Expand All @@ -56,7 +58,9 @@ def test_decrypt_credentials_fail():
"cloudformation_cli_python_lib.cipher.aws_encryption_sdk.EncryptionSDKClient.decrypt"
) as mock_decrypt, pytest.raises(
_EncryptionError
) as excinfo:
) as excinfo, patch(
"boto3.client", autospec=True
):
mock_decrypt.side_effect = AWSEncryptionSDKClientError()
cipher = KmsCipher("encryptionKeyArn", "encryptionKeyRole")
cipher.decrypt_credentials(
Expand All @@ -73,7 +77,9 @@ def test_decrypt_credentials_returns_null_fail():
"cloudformation_cli_python_lib.cipher.aws_encryption_sdk.EncryptionSDKClient.decrypt"
) as mock_decrypt, pytest.raises(
_EncryptionError
) as excinfo:
) as excinfo, patch(
"boto3.client", autospec=True
):
mock_decrypt.return_value = (
b"null",
Mock(),
Expand Down
56 changes: 28 additions & 28 deletions tests/lib/log_delivery_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
from uuid import uuid4

import pytest
from boto3.session import Session
from cloudformation_cli_python_lib.boto3_proxy import SessionProxy
from cloudformation_cli_python_lib.log_delivery import (
HookProviderLogHandler,
ProviderFilter,
Expand All @@ -18,6 +16,13 @@
RequestData,
)

import botocore.errorfactory
import botocore.session

logs_model = botocore.session.get_session().get_service_model("logs")
factory = botocore.errorfactory.ClientExceptionsFactory()
logs_exceptions = factory.create_client_exceptions(logs_model)


@pytest.fixture
def mock_logger():
Expand Down Expand Up @@ -100,38 +105,36 @@ def mock_handler_set_formatter():


@pytest.fixture
def mock_provider_handler():
def mock_provider_handler(mock_session):
plh = ProviderLogHandler(
group="test-group",
stream="test-stream",
session=SessionProxy(
Session(
aws_access_key_id="", aws_secret_access_key="", aws_session_token=""
)
),
session=mock_session,
)
# not mocking the whole client because that replaces generated exception classes to
# be replaced with mocks
for method in ["create_log_group", "create_log_stream", "put_log_events"]:
setattr(plh.client, method, Mock(auto_spec=True))

# set exceptions instead of using Mock
plh.client.exceptions = logs_exceptions
return plh


@pytest.fixture
def mock_hook_provider_handler():
def mock_hook_provider_handler(mock_session):
plh = HookProviderLogHandler(
group="test-hook-group",
stream="test-hook-stream",
session=SessionProxy(
Session(
aws_access_key_id="", aws_secret_access_key="", aws_session_token=""
)
),
session=mock_session,
)
# not mocking the whole client because that replaces generated exception classes to
# be replaced with mocks
for method in ["create_log_group", "create_log_stream", "put_log_events"]:
setattr(plh.client, method, Mock(auto_spec=True))

# set exceptions instead of using Mock
plh.client.exceptions = logs_exceptions
return plh


Expand Down Expand Up @@ -286,12 +289,11 @@ def test__put_log_event_success(mock_provider_handler, sequence_token):


def test__put_log_event_invalid_token(mock_provider_handler):
exc = mock_provider_handler.client.exceptions
mock_put = mock_provider_handler.client.put_log_events
mock_put.return_value = {"nextSequenceToken": "some-other-seq"}
mock_put.side_effect = [
exc.InvalidSequenceTokenException({}, operation_name="Test"),
exc.DataAlreadyAcceptedException({}, operation_name="Test"),
logs_exceptions.InvalidSequenceTokenException({}, operation_name="Test"),
logs_exceptions.DataAlreadyAcceptedException({}, operation_name="Test"),
DEFAULT,
]
mock_provider_handler._put_log_event(
Expand All @@ -309,8 +311,7 @@ def test_emit_existing_cwl_group_stream(mock_provider_handler):


def test_emit_no_group_stream(mock_provider_handler):
exc = mock_provider_handler.client.exceptions.ResourceNotFoundException
group_exc = exc(
group_exc = logs_exceptions.ResourceNotFoundException(
{"Error": {"Message": "log group does not exist"}},
operation_name="PutLogRecords",
)
Expand All @@ -326,7 +327,7 @@ def test_emit_no_group_stream(mock_provider_handler):
mock_provider_handler._create_log_stream.assert_called_once()

# create_group should not be called again if the group already exists
stream_exc = exc(
stream_exc = logs_exceptions.ResourceNotFoundException(
{"Error": {"Message": "log stream does not exist"}},
operation_name="PutLogRecords",
)
Expand Down Expand Up @@ -474,8 +475,9 @@ def test_hook_log_stream_create_success(mock_hook_provider_handler):
@pytest.mark.parametrize("create_method", ["_create_log_group", "_create_log_stream"])
def test__hook_create_already_exists(mock_hook_provider_handler, create_method):
mock_logs_method = getattr(mock_hook_provider_handler.client, create_method[1:])
exc = mock_hook_provider_handler.client.exceptions.ResourceAlreadyExistsException
mock_logs_method.side_effect = exc({}, operation_name="Test")
mock_logs_method.side_effect = logs_exceptions.ResourceAlreadyExistsException(
{}, operation_name="Test"
)
# should not raise an exception if the log group already exists
getattr(mock_hook_provider_handler, create_method)()
mock_logs_method.assert_called_once()
Expand All @@ -493,12 +495,11 @@ def test__hook_put_log_event_success(mock_hook_provider_handler, sequence_token)


def test__hook_put_log_event_invalid_token(mock_hook_provider_handler):
exc = mock_hook_provider_handler.client.exceptions
mock_put = mock_hook_provider_handler.client.put_log_events
mock_put.return_value = {"nextSequenceToken": "some-other-seq"}
mock_put.side_effect = [
exc.InvalidSequenceTokenException({}, operation_name="Test"),
exc.DataAlreadyAcceptedException({}, operation_name="Test"),
logs_exceptions.InvalidSequenceTokenException({}, operation_name="Test"),
logs_exceptions.DataAlreadyAcceptedException({}, operation_name="Test"),
DEFAULT,
]
mock_hook_provider_handler._put_log_event(
Expand All @@ -516,8 +517,7 @@ def test_hook_emit_existing_cwl_group_stream(mock_hook_provider_handler):


def test_hook_emit_no_group_stream(mock_hook_provider_handler):
exc = mock_hook_provider_handler.client.exceptions.ResourceNotFoundException
group_exc = exc(
group_exc = logs_exceptions.ResourceNotFoundException(
{"Error": {"Message": "log group does not exist"}},
operation_name="PutLogRecords",
)
Expand All @@ -533,7 +533,7 @@ def test_hook_emit_no_group_stream(mock_hook_provider_handler):
mock_hook_provider_handler._create_log_stream.assert_called_once()

# create_group should not be called again if the group already exists
stream_exc = exc(
stream_exc = logs_exceptions.ResourceNotFoundException(
{"Error": {"Message": "log stream does not exist"}},
operation_name="PutLogRecords",
)
Expand Down
41 changes: 27 additions & 14 deletions tests/lib/metrics_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from datetime import datetime
from unittest.mock import Mock, call, patch

import boto3
import pytest
from cloudformation_cli_python_lib.interface import (
Action,
Expand All @@ -18,7 +17,13 @@
format_dimensions,
)

from botocore.stub import Stubber # pylint: disable=C0411
import botocore.errorfactory
import botocore.session

cloudwatch_model = botocore.session.get_session().get_service_model("cloudwatch")
factory = botocore.errorfactory.ClientExceptionsFactory()
cloudwatch_exceptions = factory.create_client_exceptions(cloudwatch_model)


ACCOUNT_ID = "123456789012"
RESOURCE_TYPE = "Aa::Bb::Cc"
Expand All @@ -42,11 +47,16 @@ def test_format_dimensions():


def test_put_metric_catches_error(mock_session):
client = boto3.client("cloudwatch")
stubber = Stubber(client)

stubber.add_client_error("put_metric_data", "InternalServiceError")
stubber.activate()
client = mock_session.client("cloudwatch")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

big fan of stubber but haven't gotten it working with the mocked session. This also brings more consistency as these are the only tests using stubber.

client.exceptions = cloudwatch_exceptions
mock_put = client.put_metric_data
mock_put.return_value = {}
mock_put.side_effect = [
cloudwatch_exceptions.InternalServiceFault(
{"Error": {"Code": "InternalServiceError", "Message": ""}},
operation_name="PutMetricData",
),
]

mock_session.client.return_value = client

Expand All @@ -67,7 +77,6 @@ def test_put_metric_catches_error(mock_session):
datetime.now(),
)

stubber.deactivate()
expected_calls = [
call.error(
"An error occurred while publishing metrics: %s",
Expand Down Expand Up @@ -197,11 +206,16 @@ def test_publish_log_delivery_exception_metric(mock_session):


def test_put_hook_metric_catches_error(mock_session):
client = boto3.client("cloudwatch")
stubber = Stubber(client)

stubber.add_client_error("put_metric_data", "InternalServiceError")
stubber.activate()
client = mock_session.client("cloudwatch")
client.exceptions = cloudwatch_exceptions
mock_put = client.put_metric_data
mock_put.return_value = {}
mock_put.side_effect = [
cloudwatch_exceptions.InternalServiceFault(
{"Error": {"Code": "InternalServiceError", "Message": ""}},
operation_name="PutMetricData",
),
]

mock_session.client.return_value = client

Expand All @@ -222,7 +236,6 @@ def test_put_hook_metric_catches_error(mock_session):
datetime.now(),
)

stubber.deactivate()
expected_calls = [
call.error(
"An error occurred while publishing metrics: %s",
Expand Down