From 7ab0f28e80545fbf70268fa459608ebf1cb8ba5f Mon Sep 17 00:00:00 2001 From: mikedlr Date: Wed, 1 Mar 2017 16:08:02 +0000 Subject: [PATCH] [cloud] Fix AWS Lambda module tests (#21950) * Revert "Disable broken tests." This reverts commit 0d455037a2acd4f42ddbc862bfa3a15ed5a1b6f1. * lambda module - tests - fix year in copyright * lambda module - tests - mod arguments in each test and eliminate pytest dependency * lambda module - tests - broaden mocking to protect calls to get_aws_connection_info * lambda module - tests - refactoring and cleanup --- .../units/modules/cloud/amazon/test_lambda.py | 192 +++++++----------- 1 file changed, 78 insertions(+), 114 deletions(-) diff --git a/test/units/modules/cloud/amazon/test_lambda.py b/test/units/modules/cloud/amazon/test_lambda.py index 3d30ddac5e..ec75fbaea6 100644 --- a/test/units/modules/cloud/amazon/test_lambda.py +++ b/test/units/modules/cloud/amazon/test_lambda.py @@ -1,5 +1,5 @@ # -# (c) 2016 Michael De La Rue +# (c) 2017 Michael De La Rue # # This file is part of Ansible # @@ -17,11 +17,9 @@ # along with Ansible. If not, see . # Make coding more python3-ish - from __future__ import (absolute_import, division, print_function) from nose.plugins.skip import SkipTest -import pytest import json import copy from ansible.module_utils._text import to_bytes @@ -33,16 +31,13 @@ if not HAS_BOTO3: # lambda is a keyword so we have to hack this. _temp = __import__("ansible.modules.cloud.amazon.lambda") - lda = getattr(_temp.modules.cloud.amazon,"lambda") -exit_return_dict={} - def set_module_args(args): args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) basic._ANSIBLE_ARGS = to_bytes(args) -base_start_function_config_in_aws={ +base_lambda_config={ 'FunctionName' : 'lambda_name', 'Role' : 'arn:aws:iam::987654321012:role/lambda_basic_execution', 'Handler' : 'lambda_python.my_handler', @@ -53,12 +48,12 @@ base_start_function_config_in_aws={ 'CodeSha256' : 'AqMZ+xptM7aC9VXu+5jyp1sqO+Nj4WFMNzQxtPMP2n8=', } -one_change_start_function_config_in_aws=copy.copy(base_start_function_config_in_aws) -one_change_start_function_config_in_aws['Timeout']=4 -two_change_start_function_config_in_aws=copy.copy(one_change_start_function_config_in_aws) -two_change_start_function_config_in_aws['Role']='arn:aws:iam::987654321012:role/lambda_advanced_execution' -code_change_start_function_config_in_aws=copy.copy(base_start_function_config_in_aws) -code_change_start_function_config_in_aws['CodeSha256']='P+Zy8U4T4RiiHWElhL10VBKj9jw4rSJ5bm/TiW+4Rts=' +one_change_lambda_config=copy.copy(base_lambda_config) +one_change_lambda_config['Timeout']=4 +two_change_lambda_config=copy.copy(one_change_lambda_config) +two_change_lambda_config['Role']='arn:aws:iam::987654321012:role/lambda_advanced_execution' +code_change_lambda_config=copy.copy(base_lambda_config) +code_change_lambda_config['CodeSha256']='P+Zy8U4T4RiiHWElhL10VBKj9jw4rSJ5bm/TiW+4Rts=' base_module_args={ "region": "us-west-1", @@ -73,139 +68,116 @@ base_module_args={ } -#TODO: def test_handle_different_types_in_config_params(monkeypatch): - - -def test_update_lambda_if_code_changed(monkeypatch): - - fake_lambda_connection = MagicMock() - fake_lambda_connection.get_function.configure_mock( +def make_mock_connection(config): + """return a mock of ansible's boto3_conn ready to return a mock AWS API client""" + lambda_client_double = MagicMock() + lambda_client_double.get_function.configure_mock( return_value={ - 'Configuration' : code_change_start_function_config_in_aws + 'Configuration' : config } ) - fake_lambda_connection.update_function_configuration.configure_mock( + lambda_client_double.update_function_configuration.configure_mock( return_value={ 'Version' : 1 } ) - fake_boto3_conn=Mock(return_value=fake_lambda_connection) + fake_boto3_conn=Mock(return_value=lambda_client_double) + return (fake_boto3_conn, lambda_client_double) + + +class AnsibleFailJson(Exception): + pass + + +def fail_json_double(*args, **kwargs): + """works like fail_json but returns module results inside exception instead of stdout""" + kwargs['failed'] = True + raise AnsibleFailJson(kwargs) + + +#TODO: def test_handle_different_types_in_config_params(): + + +def test_update_lambda_if_code_changed(): set_module_args(base_module_args) - @patch("ansible.modules.cloud.amazon.lambda.boto3_conn", fake_boto3_conn) - def call_module(): - with pytest.raises(SystemExit): - lda.main() + (boto3_conn_double, lambda_client_double)=make_mock_connection(code_change_lambda_config) - call_module() + with patch.object(lda, 'boto3_conn', boto3_conn_double): + try: + lda.main() + except SystemExit: + pass # guard against calling other than for a lambda connection (e.g. IAM) - assert(len(fake_boto3_conn.mock_calls) == 1), "multiple boto connections used unexpectedly" - assert(len(fake_lambda_connection.update_function_configuration.mock_calls) == 0), \ + assert(len(boto3_conn_double.mock_calls) == 1), "multiple boto connections used unexpectedly" + assert(len(lambda_client_double.update_function_configuration.mock_calls) == 0), \ "unexpectedly updatede lambda configuration when only code changed" - assert(len(fake_lambda_connection.update_function_configuration.mock_calls) < 2), \ + assert(len(lambda_client_double.update_function_configuration.mock_calls) < 2), \ "lambda function update called multiple times when only one time should be needed" - assert(len(fake_lambda_connection.update_function_code.mock_calls) > 1), \ + assert(len(lambda_client_double.update_function_code.mock_calls) > 1), \ "failed to update lambda function when code changed" # 3 because after uploading we call into the return from mock to try to find what function version # was returned so the MagicMock actually sees two calls for one update. - assert(len(fake_lambda_connection.update_function_code.mock_calls) < 3), \ + assert(len(lambda_client_double.update_function_code.mock_calls) < 3), \ "lambda function code update called multiple times when only one time should be needed" -def test_update_lambda_if_config_changed(monkeypatch): - - fake_lambda_connection = MagicMock() - fake_lambda_connection.get_function.configure_mock( - return_value={ - 'Configuration' : two_change_start_function_config_in_aws - } - ) - fake_lambda_connection.update_function_configuration.configure_mock( - return_value={ - 'Version' : 1 - } - ) - fake_boto3_conn=Mock(return_value=fake_lambda_connection) +def test_update_lambda_if_config_changed(): set_module_args(base_module_args) - @patch("ansible.modules.cloud.amazon.lambda.boto3_conn", fake_boto3_conn) - def call_module(): - with pytest.raises(SystemExit): - lda.main() + (boto3_conn_double,lambda_client_double)=make_mock_connection(two_change_lambda_config) - call_module() + with patch.object(lda, 'boto3_conn', boto3_conn_double): + try: + lda.main() + except SystemExit: + pass # guard against calling other than for a lambda connection (e.g. IAM) - assert(len(fake_boto3_conn.mock_calls) == 1), "multiple boto connections used unexpectedly" - assert(len(fake_lambda_connection.update_function_configuration.mock_calls) > 0), \ + assert(len(boto3_conn_double.mock_calls) == 1), "multiple boto connections used unexpectedly" + assert(len(lambda_client_double.update_function_configuration.mock_calls) > 0), \ "failed to update lambda function when configuration changed" - assert(len(fake_lambda_connection.update_function_configuration.mock_calls) < 2), \ + assert(len(lambda_client_double.update_function_configuration.mock_calls) < 2), \ "lambda function update called multiple times when only one time should be needed" - assert(len(fake_lambda_connection.update_function_code.mock_calls) == 0), \ + assert(len(lambda_client_double.update_function_code.mock_calls) == 0), \ "updated lambda code when no change should have happened" +def test_update_lambda_if_only_one_config_item_changed(): -@pytest.mark.skip(reason='test broken, fails when run in isolation') -def test_update_lambda_if_only_one_config_item_changed(monkeypatch): + set_module_args(base_module_args) + (boto3_conn_double,lambda_client_double)=make_mock_connection(one_change_lambda_config) - fake_lambda_connection = MagicMock() - fake_lambda_connection.get_function.configure_mock( - return_value={ - 'Configuration' : one_change_start_function_config_in_aws - } - ) - fake_lambda_connection.update_function_configuration.configure_mock( - return_value={ - 'Version' : 1 - } - ) - fake_boto3_conn=Mock(return_value=fake_lambda_connection) - - @patch("ansible.modules.cloud.amazon.lambda.boto3_conn", fake_boto3_conn) - def call_module(): - with pytest.raises(SystemExit): + with patch.object(lda, 'boto3_conn', boto3_conn_double): + try: lda.main() - - call_module() + except SystemExit: + pass # guard against calling other than for a lambda connection (e.g. IAM) - assert(len(fake_boto3_conn.mock_calls) == 1), "multiple boto connections used unexpectedly" - assert(len(fake_lambda_connection.update_function_configuration.mock_calls) > 0), \ + assert(len(boto3_conn_double.mock_calls) == 1), "multiple boto connections used unexpectedly" + assert(len(lambda_client_double.update_function_configuration.mock_calls) > 0), \ "failed to update lambda function when configuration changed" - assert(len(fake_lambda_connection.update_function_configuration.mock_calls) < 2), \ + assert(len(lambda_client_double.update_function_configuration.mock_calls) < 2), \ "lambda function update called multiple times when only one time should be needed" - assert(len(fake_lambda_connection.update_function_code.mock_calls) == 0), \ + assert(len(lambda_client_double.update_function_code.mock_calls) == 0), \ "updated lambda code when no change should have happened" +def test_dont_update_lambda_if_nothing_changed(): -@pytest.mark.skip(reason='test broken, fails when run in isolation') -def test_dont_update_lambda_if_nothing_changed(monkeypatch): + set_module_args(base_module_args) + (boto3_conn_double,lambda_client_double)=make_mock_connection(base_lambda_config) - fake_lambda_connection = MagicMock() - fake_lambda_connection.get_function.configure_mock( - return_value={ - 'Configuration' : base_start_function_config_in_aws - } - ) - fake_lambda_connection.update_function_configuration.configure_mock( - return_value={ - 'Version' : 1 - } - ) - fake_boto3_conn=Mock(return_value=fake_lambda_connection) - - @patch("ansible.modules.cloud.amazon.lambda.boto3_conn", fake_boto3_conn) - def call_module(): - with pytest.raises(SystemExit): + with patch.object(lda, 'boto3_conn', boto3_conn_double): + try: lda.main() - - call_module() + except SystemExit: + pass # guard against calling other than for a lambda connection (e.g. IAM) - assert(len(fake_boto3_conn.mock_calls) == 1), "multiple boto connections used unexpectedly" - assert(len(fake_lambda_connection.update_function_configuration.mock_calls) == 0), \ + assert(len(boto3_conn_double.mock_calls) == 1), "multiple boto connections used unexpectedly" + assert(len(lambda_client_double.update_function_configuration.mock_calls) == 0), \ "updated lambda function when no configuration changed" - assert(len(fake_lambda_connection.update_function_code.mock_calls) == 0 ), \ + assert(len(lambda_client_double.update_function_code.mock_calls) == 0 ), \ "updated lambda code when no change should have happened" def test_warn_region_not_specified(): @@ -220,20 +192,12 @@ def test_warn_region_not_specified(): "role": 'arn:aws:iam::987654321012:role/lambda_basic_execution', "handler": 'lambda_python.my_handler'}) - class AnsibleFailJson(Exception): - pass + get_aws_connection_info_double=Mock(return_value=(None,None,None)) - def fail_json(*args, **kwargs): - kwargs['failed'] = True - raise AnsibleFailJson(kwargs) - - def call_module(): - with patch.object(basic.AnsibleModule, 'fail_json', fail_json): + with patch.object(lda, 'get_aws_connection_info', get_aws_connection_info_double): + with patch.object(basic.AnsibleModule, 'fail_json', fail_json_double): try: lda.main() except AnsibleFailJson as e: result = e.args[0] assert("region must be specified" in result['msg']) - - call_module() -