mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-03-02 07:11:14 -05:00
* Removed all govuk css * Updated reference files * Removing govuk js * Fixed casing for modules, removed unused page * Got more reference images * Updated template page * Removed govuk padding util * Updated hint to uswds hint * More govuk cleanup * Commiting backstopjs ref files * Fixed all unit tests that broke due to brittleness around govuk styling * Added new ref images * Final removal of govuk * Officially removed all govuk references * Updated reference file * Updated link to button * UI modernization * Cleanup * removed govuk escaping tests since they are no longer needed * Fix CodeQL security issue in escapeElementName function - Escape backslashes first before other special characters - Prevents potential double-escaping vulnerability - Addresses CodeQL alert about improper string escaping * Found more govuk removal. Fixed unit tests * Add missing pipeline check to pre-commit * updated test * Updated e2e test * More update to e2e test * Fixed another e2e test * Simple PR comments addressed * More updates * Updated backstop ref files * Refactored folder selection for non-admins * Updated redundant line * Updated tests to include correct mocks * Added more ref files * Addressing carlos comments * Addressing Carlo comments, cleanup of window initing * More cleanup and addressing carlo comments * Fixing a11 scan * Fixed a few issues with javascript * Fixed for pr * Fixing e2e tests * Tweaking e2e test * Added more ref files and cleaned up urls.js * Fixed bug with creating new template * Removed brittle test - addressed code ql comment * e2e race condition fix * More e2e test fixes * Updated e2e tests to not wait for text sent * Updated test to not wait for button click response * Made tear down more resilent if staging is down * reverted e2e test to what was working before main merge * Updated backstopRef images * Updated gulp to include job-polling differently
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"
|
|
)
|