|
1 | 1 | import boto3 |
2 | 2 | import pytest |
| 3 | +from botocore.exceptions import ClientError |
3 | 4 | from django.conf import settings |
4 | 5 | from moto import mock_s3 # type: ignore[import-untyped] |
5 | 6 | from pytest_lazyfixture import lazy_fixture # type: ignore[import-untyped] |
@@ -160,3 +161,65 @@ def test_stream_access_logs(mocker: MockerFixture, aws_credentials: None) -> Non |
160 | 161 |
|
161 | 162 | # And, bucket is now empty |
162 | 163 | assert "Contents" not in s3_client.list_objects(Bucket=bucket_name) |
| 164 | + |
| 165 | + |
| 166 | +def test_stream_access_logs_handles_deleted_files( |
| 167 | + mocker: MockerFixture, aws_credentials: None |
| 168 | +) -> None: |
| 169 | + # Given - Mock bucket with objects where first raises NoSuchKey |
| 170 | + mock_obj_1 = mocker.MagicMock() |
| 171 | + mock_obj_1.key = "file1" |
| 172 | + mock_obj_1.get.side_effect = ClientError( |
| 173 | + {"Error": {"Code": "NoSuchKey"}}, "GetObject" |
| 174 | + ) |
| 175 | + |
| 176 | + mock_obj_2 = mocker.MagicMock() |
| 177 | + mock_obj_2.key = "file2" |
| 178 | + mock_obj_2.get.return_value = {"Body": mocker.MagicMock(read=lambda: b"data2")} |
| 179 | + |
| 180 | + mock_bucket = mocker.MagicMock() |
| 181 | + mock_bucket.objects.all.return_value = [mock_obj_1, mock_obj_2] |
| 182 | + |
| 183 | + mocker.patch( |
| 184 | + "sse.sse_service.boto3.resource" |
| 185 | + ).return_value.Bucket.return_value = mock_bucket |
| 186 | + mocker.patch( |
| 187 | + "sse.sse_service.gnupg.GPG" |
| 188 | + ).return_value.decrypt.return_value = mocker.MagicMock( |
| 189 | + data=b"2023-11-27T06:42:47+0000,test_key" |
| 190 | + ) |
| 191 | + mocked_logger = mocker.patch("sse.sse_service.logger") |
| 192 | + |
| 193 | + # When |
| 194 | + logs = list(stream_access_logs()) |
| 195 | + |
| 196 | + # Then - Should skip first file with warning and process second |
| 197 | + assert len(logs) == 1 |
| 198 | + mocked_logger.warning.assert_called_once_with( |
| 199 | + "Log file %s has already been deleted, skipping", "file1" |
| 200 | + ) |
| 201 | + |
| 202 | + |
| 203 | +def test_stream_access_logs_reraises_non_nosuchkey_errors( |
| 204 | + mocker: MockerFixture, aws_credentials: None |
| 205 | +) -> None: |
| 206 | + # Given - Mock bucket with object that raises AccessDenied error |
| 207 | + mock_obj = mocker.MagicMock() |
| 208 | + mock_obj.key = "file1" |
| 209 | + mock_obj.get.side_effect = ClientError( |
| 210 | + {"Error": {"Code": "AccessDenied"}}, "GetObject" |
| 211 | + ) |
| 212 | + |
| 213 | + mock_bucket = mocker.MagicMock() |
| 214 | + mock_bucket.objects.all.return_value = [mock_obj] |
| 215 | + |
| 216 | + mocker.patch( |
| 217 | + "sse.sse_service.boto3.resource" |
| 218 | + ).return_value.Bucket.return_value = mock_bucket |
| 219 | + mocker.patch("sse.sse_service.gnupg.GPG") |
| 220 | + |
| 221 | + # When/Then - Should re-raise the ClientError |
| 222 | + with pytest.raises(ClientError) as exc_info: |
| 223 | + list(stream_access_logs()) |
| 224 | + |
| 225 | + assert exc_info.value.response["Error"]["Code"] == "AccessDenied" |
0 commit comments