mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-11 09:28:27 -04:00
168 lines
5.8 KiB
Python
168 lines
5.8 KiB
Python
|
|
import os
|
||
|
|
from unittest.mock import MagicMock, Mock, patch
|
||
|
|
|
||
|
|
import botocore.exceptions
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from app.s3_client import (
|
||
|
|
AWS_CLIENT_CONFIG,
|
||
|
|
get_s3_contents,
|
||
|
|
get_s3_metadata,
|
||
|
|
get_s3_object,
|
||
|
|
set_s3_metadata,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class TestS3ClientCoverage:
|
||
|
|
"""Coverage tests for s3_client/__init__.py"""
|
||
|
|
|
||
|
|
def test_aws_client_config(self):
|
||
|
|
"""Test AWS_CLIENT_CONFIG is properly configured"""
|
||
|
|
assert AWS_CLIENT_CONFIG.s3 == {"addressing_style": "virtual"}
|
||
|
|
assert AWS_CLIENT_CONFIG.use_fips_endpoint is True
|
||
|
|
|
||
|
|
@patch("app.s3_client.Session")
|
||
|
|
@patch.dict(os.environ, {"NOTIFY_ENVIRONMENT": "production"})
|
||
|
|
def test_get_s3_object_production(self, mock_session):
|
||
|
|
"""Test get_s3_object in production environment"""
|
||
|
|
mock_resource = Mock()
|
||
|
|
mock_obj = Mock()
|
||
|
|
mock_session.return_value.resource.return_value = mock_resource
|
||
|
|
mock_resource.Object.return_value = mock_obj
|
||
|
|
|
||
|
|
result = get_s3_object(
|
||
|
|
"test-bucket", "test-file.txt", "access-key", "secret-key", "us-east-1"
|
||
|
|
)
|
||
|
|
|
||
|
|
mock_session.assert_called_once_with(
|
||
|
|
aws_access_key_id="access-key",
|
||
|
|
aws_secret_access_key="secret-key",
|
||
|
|
region_name="us-east-1",
|
||
|
|
)
|
||
|
|
mock_session.return_value.resource.assert_called_once_with(
|
||
|
|
"s3", config=AWS_CLIENT_CONFIG
|
||
|
|
)
|
||
|
|
mock_resource.Object.assert_called_once_with("test-bucket", "test-file.txt")
|
||
|
|
assert result == mock_obj
|
||
|
|
|
||
|
|
@patch("app.s3_client.Session")
|
||
|
|
@patch.dict(os.environ, {"NOTIFY_ENVIRONMENT": "test"})
|
||
|
|
def test_get_s3_object_test_env_with_mock(self, mock_session):
|
||
|
|
"""Test get_s3_object in test environment with proper mocking"""
|
||
|
|
mock_resource = Mock()
|
||
|
|
mock_obj = Mock()
|
||
|
|
mock_bucket = MagicMock()
|
||
|
|
mock_bucket.creation_date = MagicMock()
|
||
|
|
|
||
|
|
mock_session.return_value.resource.return_value = mock_resource
|
||
|
|
mock_resource.Object.return_value = mock_obj
|
||
|
|
mock_resource.Bucket.return_value = mock_bucket
|
||
|
|
|
||
|
|
result = get_s3_object(
|
||
|
|
"test-bucket", "test-file.txt", "access-key", "secret-key", "us-east-1"
|
||
|
|
)
|
||
|
|
|
||
|
|
assert result == mock_obj
|
||
|
|
|
||
|
|
@patch("app.s3_client.Session")
|
||
|
|
@patch.dict(os.environ, {"NOTIFY_ENVIRONMENT": "test"})
|
||
|
|
def test_get_s3_object_test_env_without_mock(self, mock_session):
|
||
|
|
"""Test get_s3_object in test environment without proper mocking"""
|
||
|
|
mock_resource = Mock()
|
||
|
|
mock_obj = Mock()
|
||
|
|
mock_bucket = Mock()
|
||
|
|
mock_bucket.creation_date = "2024-01-01" # Not a MagicMock
|
||
|
|
|
||
|
|
mock_session.return_value.resource.return_value = mock_resource
|
||
|
|
mock_resource.Object.return_value = mock_obj
|
||
|
|
mock_resource.Bucket.return_value = mock_bucket
|
||
|
|
|
||
|
|
with pytest.raises(Exception, match="Test is not mocked") as exc_info:
|
||
|
|
get_s3_object(
|
||
|
|
"test-bucket", "test-file.txt", "access-key", "secret-key", "us-east-1"
|
||
|
|
)
|
||
|
|
|
||
|
|
assert "Test is not mocked" in str(exc_info.value)
|
||
|
|
|
||
|
|
def test_get_s3_metadata_success(self):
|
||
|
|
"""Test get_s3_metadata success case"""
|
||
|
|
mock_obj = Mock()
|
||
|
|
mock_obj.get.return_value = {"Metadata": {"key1": "value1", "key2": "value2"}}
|
||
|
|
|
||
|
|
result = get_s3_metadata(mock_obj)
|
||
|
|
|
||
|
|
assert result == {"key1": "value1", "key2": "value2"}
|
||
|
|
mock_obj.get.assert_called_once()
|
||
|
|
|
||
|
|
@patch("app.s3_client.current_app")
|
||
|
|
def test_get_s3_metadata_client_error(self, mock_app):
|
||
|
|
"""Test get_s3_metadata with ClientError"""
|
||
|
|
mock_logger = Mock()
|
||
|
|
mock_app.logger = mock_logger
|
||
|
|
|
||
|
|
mock_obj = Mock()
|
||
|
|
mock_obj.bucket_name = "test-bucket"
|
||
|
|
mock_obj.key = "test-key"
|
||
|
|
mock_obj.get.side_effect = botocore.exceptions.ClientError(
|
||
|
|
{"Error": {"Code": "NoSuchKey"}}, "GetObject"
|
||
|
|
)
|
||
|
|
|
||
|
|
with pytest.raises(botocore.exceptions.ClientError):
|
||
|
|
get_s3_metadata(mock_obj)
|
||
|
|
|
||
|
|
mock_logger.error.assert_called_once_with(
|
||
|
|
"Unable to download s3 file test-bucket/test-key"
|
||
|
|
)
|
||
|
|
|
||
|
|
def test_set_s3_metadata(self):
|
||
|
|
"""Test set_s3_metadata"""
|
||
|
|
mock_obj = Mock()
|
||
|
|
mock_obj.bucket_name = "test-bucket"
|
||
|
|
mock_obj.key = "test-key"
|
||
|
|
mock_obj.copy_from.return_value = {"CopyObjectResult": {"ETag": "abc123"}}
|
||
|
|
|
||
|
|
result = set_s3_metadata(
|
||
|
|
mock_obj, metadata1="value1", metadata2=123, metadata3=True
|
||
|
|
)
|
||
|
|
|
||
|
|
mock_obj.copy_from.assert_called_once_with(
|
||
|
|
CopySource="test-bucket/test-key",
|
||
|
|
ServerSideEncryption="AES256",
|
||
|
|
Metadata={"metadata1": "value1", "metadata2": "123", "metadata3": "True"},
|
||
|
|
MetadataDirective="REPLACE",
|
||
|
|
)
|
||
|
|
assert result == {"CopyObjectResult": {"ETag": "abc123"}}
|
||
|
|
|
||
|
|
def test_get_s3_contents_success(self):
|
||
|
|
"""Test get_s3_contents success case"""
|
||
|
|
mock_obj = Mock()
|
||
|
|
mock_body = Mock()
|
||
|
|
mock_body.read.return_value = b"Hello, World!"
|
||
|
|
mock_obj.get.return_value = {"Body": mock_body}
|
||
|
|
|
||
|
|
result = get_s3_contents(mock_obj)
|
||
|
|
|
||
|
|
assert result == "Hello, World!"
|
||
|
|
mock_obj.get.assert_called_once()
|
||
|
|
mock_body.read.assert_called_once()
|
||
|
|
|
||
|
|
@patch("app.s3_client.current_app")
|
||
|
|
def test_get_s3_contents_client_error(self, mock_app):
|
||
|
|
"""Test get_s3_contents with ClientError"""
|
||
|
|
mock_logger = Mock()
|
||
|
|
mock_app.logger = mock_logger
|
||
|
|
|
||
|
|
mock_obj = Mock()
|
||
|
|
mock_obj.bucket_name = "test-bucket"
|
||
|
|
mock_obj.key = "test-key"
|
||
|
|
mock_obj.get.side_effect = botocore.exceptions.ClientError(
|
||
|
|
{"Error": {"Code": "AccessDenied"}}, "GetObject"
|
||
|
|
)
|
||
|
|
|
||
|
|
with pytest.raises(botocore.exceptions.ClientError):
|
||
|
|
get_s3_contents(mock_obj)
|
||
|
|
|
||
|
|
mock_logger.error.assert_called_once_with(
|
||
|
|
"Unable to download s3 file test-bucket/test-key"
|
||
|
|
)
|