summaryrefslogtreecommitdiffstats
path: root/ansible_collections/amazon/aws/tests/unit
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-05 16:18:37 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-05 16:18:37 +0000
commit5d7eda1e172f8e396536a8fbd6f85b4b991290e8 (patch)
treeb18be36b43a1abdab0d40ecc8e4c8de2dbcd65c0 /ansible_collections/amazon/aws/tests/unit
parentAdding debian version 9.5.1+dfsg-1. (diff)
downloadansible-5d7eda1e172f8e396536a8fbd6f85b4b991290e8.tar.xz
ansible-5d7eda1e172f8e396536a8fbd6f85b4b991290e8.zip
Merging upstream version 10.0.0+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/amazon/aws/tests/unit')
-rw-r--r--ansible_collections/amazon/aws/tests/unit/module_utils/botocore/test_is_boto3_error_code.py68
-rw-r--r--ansible_collections/amazon/aws/tests/unit/module_utils/iam/test_iam_resource_transforms.py6
-rw-r--r--ansible_collections/amazon/aws/tests/unit/module_utils/modules/ansible_aws_module/test_passthrough.py12
-rw-r--r--ansible_collections/amazon/aws/tests/unit/module_utils/policy/test_sort_json_policy_dict.py61
-rw-r--r--ansible_collections/amazon/aws/tests/unit/module_utils/test_elbv2.py137
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/test_lambda_event.py544
6 files changed, 758 insertions, 70 deletions
diff --git a/ansible_collections/amazon/aws/tests/unit/module_utils/botocore/test_is_boto3_error_code.py b/ansible_collections/amazon/aws/tests/unit/module_utils/botocore/test_is_boto3_error_code.py
index 9f3e4194b..a5ce452fc 100644
--- a/ansible_collections/amazon/aws/tests/unit/module_utils/botocore/test_is_boto3_error_code.py
+++ b/ansible_collections/amazon/aws/tests/unit/module_utils/botocore/test_is_boto3_error_code.py
@@ -209,3 +209,71 @@ class TestIsBoto3ErrorCode:
assert not issubclass(returned_exception, botocore.exceptions.BotoCoreError)
assert issubclass(returned_exception, Exception)
assert returned_exception.__name__ == "NeverEverRaisedException"
+
+ def test_is_boto3_error_code_tuple__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_tuple__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_tuple__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"
+
+ def test_is_boto3_error_code_set__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_set__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_set__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"
diff --git a/ansible_collections/amazon/aws/tests/unit/module_utils/iam/test_iam_resource_transforms.py b/ansible_collections/amazon/aws/tests/unit/module_utils/iam/test_iam_resource_transforms.py
index 28090f993..0a6830311 100644
--- a/ansible_collections/amazon/aws/tests/unit/module_utils/iam/test_iam_resource_transforms.py
+++ b/ansible_collections/amazon/aws/tests/unit/module_utils/iam/test_iam_resource_transforms.py
@@ -451,10 +451,10 @@ class TestIamResourceToAnsibleDict:
OUTPUT = {
"arn": "arn:aws:iam::123456789012:role/ansible-test-76640355",
"assume_role_policy_document": {
- "statement": [
- {"action": "sts:AssumeRole", "effect": "Deny", "principal": {"service": "ec2.amazonaws.com"}}
+ "Statement": [
+ {"Action": "sts:AssumeRole", "Effect": "Deny", "Principal": {"Service": "ec2.amazonaws.com"}}
],
- "version": "2012-10-17",
+ "Version": "2012-10-17",
},
"assume_role_policy_document_raw": {
"Statement": [
diff --git a/ansible_collections/amazon/aws/tests/unit/module_utils/modules/ansible_aws_module/test_passthrough.py b/ansible_collections/amazon/aws/tests/unit/module_utils/modules/ansible_aws_module/test_passthrough.py
index c61de1391..688514f59 100644
--- a/ansible_collections/amazon/aws/tests/unit/module_utils/modules/ansible_aws_module/test_passthrough.py
+++ b/ansible_collections/amazon/aws/tests/unit/module_utils/modules/ansible_aws_module/test_passthrough.py
@@ -70,7 +70,7 @@ def test_region(monkeypatch, stdin):
aws_module = utils_module.AnsibleAWSModule(argument_spec=dict())
assert aws_module.region is sentinel.RETURNED_REGION
- assert get_aws_region.call_args == call(aws_module, True)
+ assert get_aws_region.call_args == call(aws_module)
@pytest.mark.parametrize("stdin", [{}], indirect=["stdin"])
@@ -129,7 +129,7 @@ def test_client_no_wrapper(monkeypatch, stdin):
aws_module = utils_module.AnsibleAWSModule(argument_spec=dict())
assert aws_module.client(sentinel.PARAM_SERVICE) is sentinel.BOTO3_CONN
- assert get_aws_connection_info.call_args == call(aws_module, boto3=True)
+ assert get_aws_connection_info.call_args == call(aws_module)
assert boto3_conn.call_args == call(
aws_module,
conn_type="client",
@@ -153,7 +153,7 @@ def test_client_wrapper(monkeypatch, stdin):
wrapped_conn = aws_module.client(sentinel.PARAM_SERVICE, sentinel.PARAM_WRAPPER)
assert wrapped_conn.client is sentinel.BOTO3_CONN
assert wrapped_conn.retry is sentinel.PARAM_WRAPPER
- assert get_aws_connection_info.call_args == call(aws_module, boto3=True)
+ assert get_aws_connection_info.call_args == call(aws_module)
assert boto3_conn.call_args == call(
aws_module,
conn_type="client",
@@ -166,7 +166,7 @@ def test_client_wrapper(monkeypatch, stdin):
wrapped_conn = aws_module.client(sentinel.PARAM_SERVICE, sentinel.PARAM_WRAPPER, region=sentinel.PARAM_REGION)
assert wrapped_conn.client is sentinel.BOTO3_CONN
assert wrapped_conn.retry is sentinel.PARAM_WRAPPER
- assert get_aws_connection_info.call_args == call(aws_module, boto3=True)
+ assert get_aws_connection_info.call_args == call(aws_module)
assert boto3_conn.call_args == call(
aws_module,
conn_type="client",
@@ -188,7 +188,7 @@ def test_resource(monkeypatch, stdin):
aws_module = utils_module.AnsibleAWSModule(argument_spec=dict())
assert aws_module.resource(sentinel.PARAM_SERVICE) is sentinel.BOTO3_CONN
- assert get_aws_connection_info.call_args == call(aws_module, boto3=True)
+ assert get_aws_connection_info.call_args == call(aws_module)
assert boto3_conn.call_args == call(
aws_module,
conn_type="resource",
@@ -199,7 +199,7 @@ def test_resource(monkeypatch, stdin):
# Check that we can override parameters
assert aws_module.resource(sentinel.PARAM_SERVICE, region=sentinel.PARAM_REGION) is sentinel.BOTO3_CONN
- assert get_aws_connection_info.call_args == call(aws_module, boto3=True)
+ assert get_aws_connection_info.call_args == call(aws_module)
assert boto3_conn.call_args == call(
aws_module,
conn_type="resource",
diff --git a/ansible_collections/amazon/aws/tests/unit/module_utils/policy/test_sort_json_policy_dict.py b/ansible_collections/amazon/aws/tests/unit/module_utils/policy/test_sort_json_policy_dict.py
deleted file mode 100644
index 8829f332c..000000000
--- a/ansible_collections/amazon/aws/tests/unit/module_utils/policy/test_sort_json_policy_dict.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# (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 ansible_collections.amazon.aws.plugins.module_utils.policy import sort_json_policy_dict
-
-
-def test_nothing_to_sort():
- simple_dict = {"key1": "a"}
- nested_dict = {"key1": {"key2": "a"}}
- very_nested_dict = {"key1": {"key2": {"key3": "a"}}}
- assert sort_json_policy_dict(simple_dict) == simple_dict
- assert sort_json_policy_dict(nested_dict) == nested_dict
- assert sort_json_policy_dict(very_nested_dict) == very_nested_dict
-
-
-def test_basic_sort():
- simple_dict = {"key1": [1, 2, 3, 4], "key2": [9, 8, 7, 6]}
- sorted_dict = {"key1": [1, 2, 3, 4], "key2": [6, 7, 8, 9]}
- assert sort_json_policy_dict(simple_dict) == sorted_dict
- assert sort_json_policy_dict(sorted_dict) == sorted_dict
- simple_dict = {"key1": ["a", "b", "c", "d"], "key2": ["z", "y", "x", "w"]}
- sorted_dict = {"key1": ["a", "b", "c", "d"], "key2": ["w", "x", "y", "z"]}
- assert sort_json_policy_dict(sorted_dict) == sorted_dict
-
-
-def test_nested_list_sort():
- nested_dict = {"key1": {"key2": [9, 8, 7, 6]}}
- sorted_dict = {"key1": {"key2": [6, 7, 8, 9]}}
- assert sort_json_policy_dict(nested_dict) == sorted_dict
- assert sort_json_policy_dict(sorted_dict) == sorted_dict
- nested_dict = {"key1": {"key2": ["z", "y", "x", "w"]}}
- sorted_dict = {"key1": {"key2": ["w", "x", "y", "z"]}}
- assert sort_json_policy_dict(nested_dict) == sorted_dict
- assert sort_json_policy_dict(sorted_dict) == sorted_dict
-
-
-def test_nested_dict_list_sort():
- nested_dict = {"key1": {"key2": {"key3": [9, 8, 7, 6]}}}
- sorted_dict = {"key1": {"key2": {"key3": [6, 7, 8, 9]}}}
- assert sort_json_policy_dict(nested_dict) == sorted_dict
- assert sort_json_policy_dict(sorted_dict) == sorted_dict
- nested_dict = {"key1": {"key2": {"key3": ["z", "y", "x", "w"]}}}
- sorted_dict = {"key1": {"key2": {"key3": ["w", "x", "y", "z"]}}}
- assert sort_json_policy_dict(nested_dict) == sorted_dict
- assert sort_json_policy_dict(sorted_dict) == sorted_dict
-
-
-def test_list_of_dict_sort():
- nested_dict = {"key1": [{"key2": [4, 3, 2, 1]}, {"key3": [9, 8, 7, 6]}]}
- sorted_dict = {"key1": [{"key2": [1, 2, 3, 4]}, {"key3": [6, 7, 8, 9]}]}
- assert sort_json_policy_dict(nested_dict) == sorted_dict
- assert sort_json_policy_dict(sorted_dict) == sorted_dict
-
-
-def test_list_of_list_sort():
- nested_dict = {"key1": [[4, 3, 2, 1], [9, 8, 7, 6]]}
- sorted_dict = {"key1": [[1, 2, 3, 4], [6, 7, 8, 9]]}
- assert sort_json_policy_dict(nested_dict) == sorted_dict
- assert sort_json_policy_dict(sorted_dict) == sorted_dict
diff --git a/ansible_collections/amazon/aws/tests/unit/module_utils/test_elbv2.py b/ansible_collections/amazon/aws/tests/unit/module_utils/test_elbv2.py
index d7293f0ce..0d2f3c153 100644
--- a/ansible_collections/amazon/aws/tests/unit/module_utils/test_elbv2.py
+++ b/ansible_collections/amazon/aws/tests/unit/module_utils/test_elbv2.py
@@ -6,6 +6,8 @@
from unittest.mock import MagicMock
+import pytest
+
from ansible_collections.amazon.aws.plugins.module_utils import elbv2
one_action = [
@@ -159,3 +161,138 @@ class TestElBV2Utils:
actual_elb_attributes = self.elbv2obj.get_elb_attributes()
# Assert we got the expected result
assert actual_elb_attributes == expected_elb_attributes
+
+
+class TestELBListeners:
+ DEFAULT_PORT = 80
+ DEFAULT_PROTOCOL = "TCP"
+
+ def createListener(self, **kwargs):
+ result = {"Port": self.DEFAULT_PORT, "Protocol": self.DEFAULT_PROTOCOL}
+ if kwargs.get("port"):
+ result["Port"] = kwargs.get("port")
+ if kwargs.get("protocol"):
+ result["Protocol"] = kwargs.get("protocol")
+ if kwargs.get("certificate_arn") and kwargs.get("protocol") in ("TLS", "HTTPS"):
+ result["Certificates"] = [{"CertificateArn": kwargs.get("certificate_arn")}]
+ if kwargs.get("sslPolicy") and kwargs.get("protocol") in ("TLS", "HTTPS"):
+ result["SslPolicy"] = kwargs.get("sslPolicy")
+ if kwargs.get("alpnPolicy") and kwargs.get("protocol") == "TLS":
+ result["AlpnPolicy"] = kwargs.get("alpnPolicy")
+ return result
+
+ @pytest.mark.parametrize("current_protocol", ["TCP", "TLS", "UDP"])
+ @pytest.mark.parametrize(
+ "current_alpn,new_alpn",
+ [
+ (None, "None"),
+ (None, "HTTP1Only"),
+ ("HTTP1Only", "HTTP2Only"),
+ ("HTTP1Only", "HTTP1Only"),
+ ],
+ )
+ def test__compare_listener_alpn_policy(self, current_protocol, current_alpn, new_alpn):
+ current_listener = self.createListener(protocol=current_protocol, alpnPolicy=[current_alpn])
+ new_listener = self.createListener(protocol="TLS", alpnPolicy=[new_alpn])
+ result = None
+ if current_protocol != "TLS":
+ result = {"Protocol": "TLS"}
+ if new_alpn and any((current_protocol != "TLS", not current_alpn, current_alpn and current_alpn != new_alpn)):
+ result = result or {}
+ result["AlpnPolicy"] = [new_alpn]
+
+ assert result == elbv2.ELBListeners._compare_listener(current_listener, new_listener)
+
+ @pytest.mark.parametrize(
+ "current_protocol,new_protocol",
+ [
+ ("TCP", "TCP"),
+ ("TLS", "HTTPS"),
+ ("HTTPS", "HTTPS"),
+ ("TLS", "TLS"),
+ ("HTTPS", "TLS"),
+ ("HTTPS", "TCP"),
+ ("TLS", "TCP"),
+ ],
+ )
+ @pytest.mark.parametrize(
+ "current_ssl,new_ssl",
+ [
+ (None, "ELBSecurityPolicy-TLS-1-0-2015-04"),
+ ("ELBSecurityPolicy-TLS13-1-2-Ext2-2021-06", "ELBSecurityPolicy-TLS-1-0-2015-04"),
+ ("ELBSecurityPolicy-TLS-1-0-2015-04", None),
+ ("ELBSecurityPolicy-TLS-1-0-2015-04", "ELBSecurityPolicy-TLS-1-0-2015-04"),
+ ],
+ )
+ def test__compare_listener_sslpolicy(self, current_protocol, new_protocol, current_ssl, new_ssl):
+ current_listener = self.createListener(protocol=current_protocol, sslPolicy=current_ssl)
+
+ new_listener = self.createListener(protocol=new_protocol, sslPolicy=new_ssl)
+
+ expected = None
+ if new_protocol != current_protocol:
+ expected = {"Protocol": new_protocol}
+ if new_protocol in ("HTTPS", "TLS") and new_ssl and new_ssl != current_ssl:
+ expected = expected or {}
+ expected["SslPolicy"] = new_ssl
+ assert expected == elbv2.ELBListeners._compare_listener(current_listener, new_listener)
+
+ @pytest.mark.parametrize(
+ "current_protocol,new_protocol",
+ [
+ ("TCP", "TCP"),
+ ("TLS", "HTTPS"),
+ ("HTTPS", "HTTPS"),
+ ("TLS", "TLS"),
+ ("HTTPS", "TLS"),
+ ("HTTPS", "TCP"),
+ ("TLS", "TCP"),
+ ],
+ )
+ @pytest.mark.parametrize(
+ "current_certificate,new_certificate",
+ [
+ (None, "arn:aws:iam::012345678901:server-certificate/ansible-test-1"),
+ (
+ "arn:aws:iam::012345678901:server-certificate/ansible-test-1",
+ "arn:aws:iam::012345678901:server-certificate/ansible-test-2",
+ ),
+ ("arn:aws:iam::012345678901:server-certificate/ansible-test-1", None),
+ (
+ "arn:aws:iam::012345678901:server-certificate/ansible-test-1",
+ "arn:aws:iam::012345678901:server-certificate/ansible-test-1",
+ ),
+ ],
+ )
+ def test__compare_listener_certificates(self, current_protocol, new_protocol, current_certificate, new_certificate):
+ current_listener = self.createListener(protocol=current_protocol, certificate_arn=current_certificate)
+
+ new_listener = self.createListener(protocol=new_protocol, certificate_arn=new_certificate)
+
+ expected = None
+ if new_protocol != current_protocol:
+ expected = {"Protocol": new_protocol}
+ if new_protocol in ("HTTPS", "TLS") and new_certificate and new_certificate != current_certificate:
+ expected = expected or {}
+ expected["Certificates"] = [{"CertificateArn": new_certificate}]
+ assert expected == elbv2.ELBListeners._compare_listener(current_listener, new_listener)
+
+ @pytest.mark.parametrize(
+ "are_equals",
+ [True, False],
+ )
+ def test__compare_listener_port(self, are_equals):
+ current_listener = self.createListener()
+ new_port = MagicMock() if not are_equals else None
+ new_listener = self.createListener(port=new_port)
+
+ result = elbv2.ELBListeners._compare_listener(current_listener, new_listener)
+ expected = None
+ if not are_equals:
+ expected = {"Port": new_port}
+ assert result == expected
+
+ def test_ensure_listeners_alpn_policy(self):
+ listeners = [{"Port": self.DEFAULT_PORT, "AlpnPolicy": "HTTP2Optional"}]
+ expected = [{"Port": self.DEFAULT_PORT, "AlpnPolicy": ["HTTP2Optional"]}]
+ assert expected == elbv2.ELBListeners._ensure_listeners_alpn_policy(listeners)
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_lambda_event.py b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_lambda_event.py
new file mode 100644
index 000000000..c292329b4
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_lambda_event.py
@@ -0,0 +1,544 @@
+#
+# (c) 2024 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 contextlib import nullcontext as does_not_raise
+from copy import deepcopy
+from unittest.mock import MagicMock
+from unittest.mock import patch
+
+import pytest
+
+from ansible_collections.amazon.aws.plugins.modules.lambda_event import get_qualifier
+from ansible_collections.amazon.aws.plugins.modules.lambda_event import lambda_event_stream
+from ansible_collections.amazon.aws.plugins.modules.lambda_event import set_default_values
+from ansible_collections.amazon.aws.plugins.modules.lambda_event import validate_params
+
+mock_get_qualifier = "ansible_collections.amazon.aws.plugins.modules.lambda_event.get_qualifier"
+mock_camel_dict_to_snake_dict = "ansible_collections.amazon.aws.plugins.modules.lambda_event.camel_dict_to_snake_dict"
+
+
+@pytest.fixture
+def ansible_aws_module():
+ module = MagicMock()
+ module.check_mode = False
+ module.params = {
+ "state": "present",
+ "lambda_function_arn": None,
+ "event_source": "sqs",
+ "source_params": {
+ "source_arn": "arn:aws:sqs:us-east-2:123456789012:ansible-test-sqs",
+ "batch_size": 200,
+ "starting_position": "LATEST",
+ },
+ "alias": None,
+ "version": 0,
+ }
+ module.exit_json = MagicMock()
+ module.exit_json.side_effect = SystemExit(1)
+ module.fail_json_aws = MagicMock()
+ module.fail_json_aws.side_effect = SystemExit(2)
+ module.fail_json = MagicMock()
+ module.fail_json.side_effect = SystemExit(2)
+ module.client = MagicMock()
+ module.client.return_value = MagicMock()
+ module.boolean = MagicMock()
+ module.boolean.side_effect = lambda x: x.lower() in ["true", "1", "t", "y", "yes"]
+ return module
+
+
+@pytest.mark.parametrize(
+ "module_params,expected",
+ [
+ ({"version": 1}, "1"),
+ ({"alias": "ansible-test"}, "ansible-test"),
+ ({"version": 1, "alias": "ansible-test"}, "1"),
+ ({}, None),
+ ],
+)
+def test_get_qualifier(ansible_aws_module, module_params, expected):
+ ansible_aws_module.params.update(module_params)
+ assert get_qualifier(ansible_aws_module) == expected
+
+
+@pytest.mark.parametrize(
+ "function_name,error_msg",
+ [
+ (
+ "invalid+function+name",
+ "Function name invalid+function+name is invalid. Names must contain only alphanumeric characters and hyphens.",
+ ),
+ (
+ "this_invalid_function_name_has_more_than_64_character_limit_this_is_not_allowed_by_the_module",
+ 'Function name "this_invalid_function_name_has_more_than_64_character_limit_this_is_not_allowed_by_the_module" exceeds 64 character limit',
+ ),
+ (
+ "arn:aws:lambda:us-east-2:123456789012:function:ansible-test-ansible-test-ansible-test-sqs-lambda-function:"
+ "ansible-test-ansible-test-ansible-test-sqs-lambda-function-alias",
+ 'ARN "arn:aws:lambda:us-east-2:123456789012:function:ansible-test-ansible-test-ansible-test-sqs-lambda-function:'
+ 'ansible-test-ansible-test-ansible-test-sqs-lambda-function-alias" exceeds 140 character limit',
+ ),
+ ],
+)
+def test_validate_params_function_name_errors(ansible_aws_module, function_name, error_msg):
+ ansible_aws_module.params.update({"lambda_function_arn": function_name})
+ client = MagicMock()
+ client.get_function = MagicMock()
+ client.get_function.return_value = {}
+ with pytest.raises(SystemExit):
+ validate_params(ansible_aws_module, client)
+
+ ansible_aws_module.fail_json.assert_called_once_with(msg=error_msg)
+
+
+@pytest.mark.parametrize(
+ "qualifier",
+ [None, "ansible-test"],
+)
+@patch(mock_get_qualifier)
+def test_validate_params_with_function_arn(m_get_qualifier, ansible_aws_module, qualifier):
+ function_name = "arn:aws:lambda:us-east-2:123456789012:function:sqs_consumer"
+ ansible_aws_module.params.update({"lambda_function_arn": function_name})
+ m_get_qualifier.return_value = qualifier
+
+ client = MagicMock()
+ client.get_function = MagicMock()
+ client.get_function.return_value = {}
+
+ params = deepcopy(ansible_aws_module.params)
+ params["lambda_function_arn"] = f"{function_name}:{qualifier}" if qualifier else function_name
+
+ validate_params(ansible_aws_module, client)
+ assert params == ansible_aws_module.params
+ m_get_qualifier.assert_called_once()
+
+
+@pytest.mark.parametrize(
+ "qualifier",
+ [None, "ansible-test"],
+)
+@patch(mock_get_qualifier)
+def test_validate_params_with_function_name(m_get_qualifier, ansible_aws_module, qualifier):
+ function_arn = "arn:aws:lambda:us-east-2:123456789012:function:sqs_consumer"
+ function_name = "sqs_consumer"
+ ansible_aws_module.params.update({"lambda_function_arn": function_name})
+ m_get_qualifier.return_value = qualifier
+
+ client = MagicMock()
+ client.get_function = MagicMock()
+ client.get_function.return_value = {
+ "Configuration": {"FunctionArn": function_arn},
+ }
+
+ params = deepcopy(ansible_aws_module.params)
+ params["lambda_function_arn"] = function_arn
+
+ validate_params(ansible_aws_module, client)
+
+ assert params == ansible_aws_module.params
+ m_get_qualifier.assert_called_once()
+ api_params = {"FunctionName": function_name}
+ if qualifier:
+ api_params.update({"Qualifier": qualifier})
+ client.get_function.assert_called_once_with(**api_params)
+
+
+EventSourceMappings = [
+ {
+ "BatchSize": 10,
+ "EventSourceArn": "arn:aws:sqs:us-east-2:123456789012:sqs_consumer",
+ "FunctionArn": "arn:aws:lambda:us-east-2:123456789012:function:sqs_consumer",
+ "LastModified": "2024-02-08T15:24:57.014000+01:00",
+ "MaximumBatchingWindowInSeconds": 0,
+ "State": "Enabled",
+ "StateTransitionReason": "USER_INITIATED",
+ "UUID": "3ab96d4c-b0c4-4885-87d0-f58cb9c0a4cc",
+ }
+]
+
+
+@pytest.mark.parametrize(
+ "check_mode",
+ [True, False],
+)
+@pytest.mark.parametrize(
+ "existing_event_source",
+ [True, False],
+)
+@patch(mock_camel_dict_to_snake_dict)
+def test_lambda_event_stream_with_state_absent(
+ mock_camel_dict_to_snake_dict, ansible_aws_module, check_mode, existing_event_source
+):
+ function_name = "sqs_consumer"
+ ansible_aws_module.params.update({"lambda_function_arn": function_name, "state": "absent"})
+ ansible_aws_module.check_mode = check_mode
+
+ client = MagicMock()
+ client.list_event_source_mappings = MagicMock()
+
+ client.list_event_source_mappings.return_value = {
+ "EventSourceMappings": EventSourceMappings if existing_event_source else []
+ }
+ client.delete_event_source_mapping = MagicMock()
+ event_source_deleted = {"msg": "event source successfully deleted."}
+ client.delete_event_source_mapping.return_value = event_source_deleted
+ mock_camel_dict_to_snake_dict.side_effect = lambda x: x
+
+ events = []
+ changed = False
+ result = lambda_event_stream(ansible_aws_module, client)
+ changed = existing_event_source
+ if existing_event_source:
+ events = EventSourceMappings
+ if not check_mode:
+ events = event_source_deleted
+ client.delete_event_source_mapping.assert_called_once_with(UUID=EventSourceMappings[0]["UUID"])
+ else:
+ client.delete_event_source_mapping.assert_not_called()
+ assert dict(changed=changed, events=events) == result
+
+
+def test_lambda_event_stream_create_event_missing_starting_position(ansible_aws_module):
+ ansible_aws_module.params = {
+ "state": "present",
+ "lambda_function_arn": "sqs_consumer",
+ "event_source": "stream",
+ "source_params": {
+ "source_arn": "arn:aws:sqs:us-east-2:123456789012:ansible-test-sqs",
+ "maximum_batching_window_in_seconds": 1,
+ "batch_size": 200,
+ },
+ "alias": None,
+ "version": 0,
+ }
+
+ client = MagicMock()
+ client.list_event_source_mappings = MagicMock()
+ client.list_event_source_mappings.return_value = {"EventSourceMappings": []}
+
+ error_message = "Source parameter 'starting_position' is required for stream event notification."
+ with pytest.raises(SystemExit):
+ lambda_event_stream(ansible_aws_module, client)
+ ansible_aws_module.fail_json.assert_called_once_with(msg=error_message)
+
+
+@pytest.mark.parametrize(
+ "check_mode",
+ [True, False],
+)
+@pytest.mark.parametrize(
+ "module_params,api_params",
+ [
+ (
+ {
+ "state": "present",
+ "lambda_function_arn": "sqs_consumer",
+ "event_source": "stream",
+ "source_params": {
+ "source_arn": "arn:aws:sqs:us-east-2:123456789012:ansible-test-sqs",
+ "maximum_batching_window_in_seconds": 1,
+ "batch_size": 250,
+ "starting_position": "END",
+ "function_response_types": ["ReportBatchItemFailures"],
+ },
+ "alias": None,
+ "version": 0,
+ },
+ {
+ "FunctionName": "sqs_consumer",
+ "EventSourceArn": "arn:aws:sqs:us-east-2:123456789012:ansible-test-sqs",
+ "StartingPosition": "END",
+ "Enabled": True,
+ "MaximumBatchingWindowInSeconds": 1,
+ "BatchSize": 250,
+ "FunctionResponseTypes": ["ReportBatchItemFailures"],
+ },
+ ),
+ (
+ {
+ "state": "present",
+ "lambda_function_arn": "sqs_consumer",
+ "event_source": "stream",
+ "source_params": {
+ "source_arn": "arn:aws:sqs:us-east-2:123456789012:ansible-test-sqs",
+ "maximum_batching_window_in_seconds": 1,
+ "batch_size": 250,
+ "starting_position": "END",
+ "function_response_types": ["ReportBatchItemFailures"],
+ "enabled": "no",
+ },
+ "alias": None,
+ "version": 0,
+ },
+ {
+ "FunctionName": "sqs_consumer",
+ "EventSourceArn": "arn:aws:sqs:us-east-2:123456789012:ansible-test-sqs",
+ "StartingPosition": "END",
+ "Enabled": False,
+ "MaximumBatchingWindowInSeconds": 1,
+ "BatchSize": 250,
+ "FunctionResponseTypes": ["ReportBatchItemFailures"],
+ },
+ ),
+ (
+ {
+ "state": "present",
+ "lambda_function_arn": "sqs_consumer",
+ "event_source": "sqs",
+ "source_params": {
+ "source_arn": "arn:aws:sqs:us-east-2:123456789012:ansible-test-sqs",
+ "maximum_batching_window_in_seconds": 1,
+ "batch_size": 101,
+ },
+ "alias": None,
+ "version": 0,
+ },
+ {
+ "FunctionName": "sqs_consumer",
+ "EventSourceArn": "arn:aws:sqs:us-east-2:123456789012:ansible-test-sqs",
+ "Enabled": True,
+ "MaximumBatchingWindowInSeconds": 1,
+ "BatchSize": 101,
+ },
+ ),
+ ],
+)
+@patch(mock_camel_dict_to_snake_dict)
+def test_lambda_event_stream_create_event(
+ mock_camel_dict_to_snake_dict, ansible_aws_module, check_mode, module_params, api_params
+):
+ ansible_aws_module.params = module_params
+ ansible_aws_module.check_mode = check_mode
+
+ client = MagicMock()
+ client.list_event_source_mappings = MagicMock()
+ client.list_event_source_mappings.return_value = {"EventSourceMappings": []}
+
+ client.create_event_source_mapping = MagicMock()
+ event_source_created = {"msg": "event source successfully created."}
+ client.create_event_source_mapping.return_value = event_source_created
+ mock_camel_dict_to_snake_dict.side_effect = lambda x: x
+
+ result = lambda_event_stream(ansible_aws_module, client)
+
+ events = []
+
+ if not check_mode:
+ events = event_source_created
+ client.create_event_source_mapping.assert_called_once_with(**api_params)
+ else:
+ client.create_event_source_mapping.assert_not_called()
+
+ assert dict(changed=True, events=events) == result
+
+
+@pytest.mark.parametrize(
+ "check_mode",
+ [True, False],
+)
+@pytest.mark.parametrize(
+ "module_source_params,current_mapping,api_params",
+ [
+ (
+ {"batch_size": 100, "enabled": "false"},
+ {"BatchSize": 120, "State": "Enabled"},
+ {"BatchSize": 100, "Enabled": False},
+ ),
+ (
+ {"batch_size": 100, "enabled": "true"},
+ {"BatchSize": 100, "State": "Enabled"},
+ {},
+ ),
+ ],
+)
+@patch(mock_camel_dict_to_snake_dict)
+def test_lambda_event_stream_update_event(
+ mock_camel_dict_to_snake_dict, ansible_aws_module, check_mode, module_source_params, current_mapping, api_params
+):
+ function_name = "ansible-test-update-event-function"
+ ansible_aws_module.params.update({"lambda_function_arn": function_name})
+ ansible_aws_module.params["source_params"].update(module_source_params)
+ ansible_aws_module.check_mode = check_mode
+
+ client = MagicMock()
+ client.list_event_source_mappings = MagicMock()
+ existing_event_source = deepcopy(EventSourceMappings)
+ existing_event_source[0].update(current_mapping)
+ client.list_event_source_mappings.return_value = {"EventSourceMappings": existing_event_source}
+
+ client.update_event_source_mapping = MagicMock()
+ event_source_updated = {"msg": "event source successfully updated."}
+ client.update_event_source_mapping.return_value = event_source_updated
+ mock_camel_dict_to_snake_dict.side_effect = lambda x: x
+
+ result = lambda_event_stream(ansible_aws_module, client)
+ if not api_params:
+ assert dict(changed=False, events=existing_event_source) == result
+ client.update_event_source_mapping.assert_not_called()
+ elif check_mode:
+ assert dict(changed=True, events=existing_event_source) == result
+ client.update_event_source_mapping.assert_not_called()
+ else:
+ api_params.update({"FunctionName": function_name, "UUID": existing_event_source[0]["UUID"]})
+ assert dict(changed=True, events=event_source_updated) == result
+ client.update_event_source_mapping.assert_called_once_with(**api_params)
+
+
+@pytest.mark.parametrize(
+ "params, expected, exception, message, source_type",
+ [
+ (
+ {
+ "source_arn": "arn:aws:sqs:us-east-1:123456789012:ansible-test-28277052.fifo",
+ "enabled": True,
+ "batch_size": 100,
+ "starting_position": None,
+ "function_response_types": None,
+ "maximum_batching_window_in_seconds": None,
+ },
+ None,
+ pytest.raises(SystemExit),
+ "For FIFO queues the maximum batch_size is 10.",
+ "sqs",
+ ),
+ (
+ {
+ "source_arn": "arn:aws:sqs:us-east-1:123456789012:ansible-test-28277052.fifo",
+ "enabled": True,
+ "batch_size": 10,
+ "starting_position": None,
+ "function_response_types": None,
+ "maximum_batching_window_in_seconds": 1,
+ },
+ None,
+ pytest.raises(SystemExit),
+ "maximum_batching_window_in_seconds is not supported by Amazon SQS FIFO event sources.",
+ "sqs",
+ ),
+ (
+ {
+ "source_arn": "arn:aws:sqs:us-east-1:123456789012:ansible-test-28277052.fifo",
+ "enabled": True,
+ "batch_size": 10,
+ "starting_position": None,
+ "function_response_types": None,
+ "maximum_batching_window_in_seconds": None,
+ },
+ {
+ "source_arn": "arn:aws:sqs:us-east-1:123456789012:ansible-test-28277052.fifo",
+ "enabled": True,
+ "batch_size": 10,
+ "starting_position": None,
+ "function_response_types": None,
+ "maximum_batching_window_in_seconds": None,
+ },
+ does_not_raise(),
+ None,
+ "sqs",
+ ),
+ (
+ {
+ "source_arn": "arn:aws:sqs:us-east-1:123456789012:ansible-test-28277052",
+ "enabled": True,
+ "batch_size": 11000,
+ "starting_position": None,
+ "function_response_types": None,
+ "maximum_batching_window_in_seconds": None,
+ },
+ None,
+ pytest.raises(SystemExit),
+ "For standard queue batch_size must be lower than 10000.",
+ "sqs",
+ ),
+ (
+ {
+ "source_arn": "arn:aws:sqs:us-east-1:123456789012:ansible-test-28277052",
+ "enabled": True,
+ "batch_size": 100,
+ "starting_position": None,
+ "function_response_types": None,
+ "maximum_batching_window_in_seconds": None,
+ },
+ {
+ "source_arn": "arn:aws:sqs:us-east-1:123456789012:ansible-test-28277052",
+ "enabled": True,
+ "batch_size": 100,
+ "starting_position": None,
+ "function_response_types": None,
+ "maximum_batching_window_in_seconds": 1,
+ },
+ does_not_raise(),
+ None,
+ "sqs",
+ ),
+ (
+ {
+ "source_arn": "arn:aws:sqs:us-east-1:123456789012:ansible-test-28277052",
+ "enabled": True,
+ "starting_position": None,
+ "function_response_types": None,
+ "maximum_batching_window_in_seconds": None,
+ },
+ {
+ "source_arn": "arn:aws:sqs:us-east-1:123456789012:ansible-test-28277052",
+ "enabled": True,
+ "batch_size": 100,
+ "starting_position": None,
+ "function_response_types": None,
+ "maximum_batching_window_in_seconds": 1,
+ },
+ does_not_raise(),
+ None,
+ "stream",
+ ),
+ (
+ {
+ "source_arn": "arn:aws:sqs:us-east-1:123456789012:ansible-test-28277052",
+ "enabled": True,
+ "starting_position": None,
+ "function_response_types": None,
+ },
+ {
+ "source_arn": "arn:aws:sqs:us-east-1:123456789012:ansible-test-28277052",
+ "enabled": True,
+ "batch_size": 10,
+ "starting_position": None,
+ "function_response_types": None,
+ },
+ does_not_raise(),
+ None,
+ "sqs",
+ ),
+ (
+ {
+ "source_arn": "arn:aws:sqs:us-east-1:123456789012:ansible-test-28277052",
+ "enabled": True,
+ "batch_size": 10,
+ "starting_position": None,
+ "function_response_types": None,
+ "maximum_batching_window_in_seconds": None,
+ },
+ None,
+ pytest.raises(SystemExit),
+ "batch_size for streams must be between 100 and 10000",
+ "stream",
+ ),
+ ],
+)
+def test__set_default_values(params, expected, exception, message, source_type):
+ result = None
+ module = MagicMock()
+ module.check_mode = False
+ module.params = {
+ "event_source": source_type,
+ "source_params": params,
+ }
+ module.fail_json = MagicMock()
+ module.fail_json.side_effect = SystemExit(message)
+ with exception as e:
+ result = set_default_values(module, params)
+ assert message is None or message in str(e)
+ if expected is not None:
+ assert result == expected