Files
notifications-admin/tests/app/s3_client/test_s3_client_coverage.py
Alex Janousek 6f5750f095 Removed all govuk css (#2814)
* 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
2025-10-06 09:38:54 -04:00

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"
)