"""Tests for the backup_feature_flags function.""" import json from unittest.mock import MagicMock import pytest from src.backup import backup_feature_flags def json_equivalent(expected_dict): """ Compare a JSON string to an expected dictionary. args: expected_dict (dict): The expected dictionary to compare against. Returns: _JsonCompare: A comparison object that checks if the JSON data matches the expected dictionary. """ class _JsonCompare: def __eq__(self, other): try: return json.loads(other) == expected_dict except Exception: return False return _JsonCompare() @pytest.fixture def mock_splitio_client(): """Mock SplitIOClient for testing.""" client = MagicMock() client.get_all_FF_names.return_value = ['flag_1', 'flag_2', 'flag_3'] client.get_environments.return_value = [{'id': 'env_1', 'name': 'prod'}, {'id': 'env_2', 'name': 'qa'}] client.get_feature_flag_metadata.side_effect = [ {'name': 'flag_1', 'type': 'boolean'}, {'name': 'flag_2', 'type': 'boolean'}, None, ] client.get_feature_flag_definition.side_effect = [ {'flag': 'flag_1', 'enabled': True}, {'flag': 'flag_1', 'enabled': False}, {'flag': 'flag_2', 'enabled': False}, None ] return client @pytest.fixture def mock_s3_client(): """Mock S3 client for testing.""" return MagicMock() def test_backup_feature_flags_uploads_metadata_and_definition(mock_splitio_client, mock_s3_client, monkeypatch): """Test that backup_feature_flags uploads metadata and definitions correctly.""" monkeypatch.setattr('src.backup.S3_KMS_KEY_ID', 'test-kms-key-id') backup_feature_flags( splitio_client=mock_splitio_client, s3_client=mock_s3_client, s3_bucket='test-bucket' ) # Verify put_object called for metadata of flag_1 mock_s3_client.put_object.assert_any_call( Bucket='test-bucket', Key='flag_1/metadata.json', Body=json_equivalent({ 'name': 'flag_1', 'type': 'boolean' }), ServerSideEncryption='aws:kms', SSEKMSKeyId='test-kms-key-id', ) # Verify put_object called for metadata of flag_2 mock_s3_client.put_object.assert_any_call( Bucket='test-bucket', Key='flag_2/metadata.json', Body=json_equivalent({ 'name': 'flag_2', 'type': 'boolean' }), ServerSideEncryption='aws:kms', SSEKMSKeyId='test-kms-key-id', ) # Verify put_object called for definition of flag_1 mock_s3_client.put_object.assert_any_call( Bucket='test-bucket', Key='flag_1/definitions/prod.json', Body=json_equivalent({ 'flag': 'flag_1', 'enabled': True }), ServerSideEncryption='aws:kms', SSEKMSKeyId='test-kms-key-id', ) mock_s3_client.put_object.assert_any_call( Bucket='test-bucket', Key='flag_1/definitions/qa.json', Body=json_equivalent({ 'flag': 'flag_1', 'enabled': False }), ServerSideEncryption='aws:kms', SSEKMSKeyId='test-kms-key-id', ) # Verify put_object called for definition of flag_2 mock_s3_client.put_object.assert_any_call( Bucket='test-bucket', Key='flag_2/definitions/prod.json', Body=json_equivalent({ 'flag': 'flag_2', 'enabled': False }), ServerSideEncryption='aws:kms', SSEKMSKeyId='test-kms-key-id', ) # Ensure metadata for flag_2 was skipped (None) assert mock_s3_client.put_object.call_count == 5 def test_backup_feature_flags_handles_no_flags(mock_s3_client): """Test that backup_feature_flags handles the case with no feature flags.""" client = MagicMock() client.get_all_FF_names.return_value = [] backup_feature_flags( splitio_client=client, s3_client=mock_s3_client, s3_bucket='test-bucket' ) # Nothing should be uploaded mock_s3_client.put_object.assert_not_called()