From 95e9cd2b4d23908415ecce93af7060a98e5678c8 Mon Sep 17 00:00:00 2001 From: Dan Guido Date: Sat, 16 Aug 2025 03:34:28 -0400 Subject: [PATCH] Add unit test for AWS Lightsail boto3 parameter fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Tests that get_aws_connection_info() is called without boto3 parameter - Verifies the module can be imported successfully - Checks source code doesn't contain boto3=True - Regression test specifically for issue #14822 - All 4 test cases pass This ensures the fix remains in place and prevents regression. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- tests/unit/test_lightsail_boto3_fix.py | 157 +++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 tests/unit/test_lightsail_boto3_fix.py diff --git a/tests/unit/test_lightsail_boto3_fix.py b/tests/unit/test_lightsail_boto3_fix.py new file mode 100644 index 00000000..8fc79a34 --- /dev/null +++ b/tests/unit/test_lightsail_boto3_fix.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python +""" +Test for AWS Lightsail boto3 parameter fix. +Verifies that get_aws_connection_info() works without the deprecated boto3 parameter. +Addresses issue #14822. +""" + +import sys +import os +import unittest +from unittest.mock import patch, MagicMock +import importlib.util + +# Add the library directory to the path +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../library')) + + +class TestLightsailBoto3Fix(unittest.TestCase): + """Test that lightsail_region_facts.py works without boto3 parameter.""" + + def setUp(self): + """Set up test fixtures.""" + # Mock the ansible module_utils since we're testing outside of Ansible + self.mock_modules = { + 'ansible.module_utils.basic': MagicMock(), + 'ansible.module_utils.ec2': MagicMock(), + 'ansible.module_utils.aws.core': MagicMock(), + } + + # Apply mocks + self.patches = [] + for module_name, mock_module in self.mock_modules.items(): + patcher = patch.dict('sys.modules', {module_name: mock_module}) + patcher.start() + self.patches.append(patcher) + + def tearDown(self): + """Clean up patches.""" + for patcher in self.patches: + patcher.stop() + + def test_lightsail_region_facts_imports(self): + """Test that lightsail_region_facts can be imported.""" + try: + # Import the module + spec = importlib.util.spec_from_file_location( + "lightsail_region_facts", + os.path.join(os.path.dirname(__file__), '../../library/lightsail_region_facts.py') + ) + module = importlib.util.module_from_spec(spec) + + # This should not raise an error + spec.loader.exec_module(module) + + # Verify the module loaded + self.assertIsNotNone(module) + self.assertTrue(hasattr(module, 'main')) + + except Exception as e: + self.fail(f"Failed to import lightsail_region_facts: {e}") + + def test_get_aws_connection_info_called_without_boto3(self): + """Test that get_aws_connection_info is called without boto3 parameter.""" + # Mock get_aws_connection_info to track calls + mock_get_aws_connection_info = MagicMock( + return_value=('us-west-2', None, {}) + ) + + with patch('ansible.module_utils.ec2.get_aws_connection_info', mock_get_aws_connection_info): + # Import the module + spec = importlib.util.spec_from_file_location( + "lightsail_region_facts", + os.path.join(os.path.dirname(__file__), '../../library/lightsail_region_facts.py') + ) + module = importlib.util.module_from_spec(spec) + + # Mock AnsibleModule + mock_ansible_module = MagicMock() + mock_ansible_module.params = {} + mock_ansible_module.check_mode = False + + with patch('ansible.module_utils.basic.AnsibleModule', return_value=mock_ansible_module): + # Execute the module + try: + spec.loader.exec_module(module) + module.main() + except SystemExit: + # Module calls exit_json or fail_json which raises SystemExit + pass + except Exception: + # We expect some exceptions since we're mocking, but we want to check the call + pass + + # Verify get_aws_connection_info was called + if mock_get_aws_connection_info.called: + # Get the call arguments + call_args = mock_get_aws_connection_info.call_args + + # Ensure boto3=True is NOT in the arguments + if call_args: + # Check positional arguments + if call_args[0]: # args + self.assertTrue(len(call_args[0]) <= 1, + "get_aws_connection_info should be called with at most 1 positional arg (module)") + + # Check keyword arguments + if call_args[1]: # kwargs + self.assertNotIn('boto3', call_args[1], + "get_aws_connection_info should not be called with boto3 parameter") + + def test_no_boto3_parameter_in_source(self): + """Verify that boto3 parameter is not present in the source code.""" + lightsail_path = os.path.join(os.path.dirname(__file__), '../../library/lightsail_region_facts.py') + + with open(lightsail_path, 'r') as f: + content = f.read() + + # Check that boto3=True is not in the file + self.assertNotIn('boto3=True', content, + "boto3=True parameter should not be present in lightsail_region_facts.py") + + # Check that boto3 parameter is not used with get_aws_connection_info + self.assertNotIn('get_aws_connection_info(module, boto3', content, + "get_aws_connection_info should not be called with boto3 parameter") + + def test_regression_issue_14822(self): + """ + Regression test for issue #14822. + Ensures that the deprecated boto3 parameter is not used. + """ + # This test documents the specific issue that was fixed + # The boto3 parameter was deprecated and removed in amazon.aws collection + # that comes with Ansible 11.x + + lightsail_path = os.path.join(os.path.dirname(__file__), '../../library/lightsail_region_facts.py') + + with open(lightsail_path, 'r') as f: + lines = f.readlines() + + # Find the line that calls get_aws_connection_info + for line_num, line in enumerate(lines, 1): + if 'get_aws_connection_info' in line and 'region' in line: + # This should be around line 85 + # Verify it doesn't have boto3=True + self.assertNotIn('boto3', line, + f"Line {line_num} should not contain boto3 parameter") + + # Verify the correct format + self.assertIn('get_aws_connection_info(module)', line, + f"Line {line_num} should call get_aws_connection_info(module) without boto3") + break + else: + self.fail("Could not find get_aws_connection_info call in lightsail_region_facts.py") + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file