1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110"""Tests for cleanup service."""
from unittest.mock import AsyncMock, patch
import pytest
from botocore.exceptions import ClientError
from src.services.cleanup_service import CleanupService
@pytest.fixture
def mock_s3_client():
"""Mock S3 client for testing."""
client = AsyncMock()
return client
@pytest.fixture
def cleanup_service(mock_s3_client):
"""Cleanup service instance with mocked S3."""
with patch("src.services.cleanup_service.settings") as mock_settings:
mock_settings.s3_temp_bucket = "equalify-pdf-temp"
yield CleanupService(mock_s3_client)
@pytest.mark.asyncio
async def test_cleanup_job_files_success(cleanup_service, mock_s3_client):
"""Test successful file cleanup."""
# Arrange
s3_key = "temp/test-job-123.pdf"
mock_s3_client.delete_object.return_value = {}
# Act
result = await cleanup_service.cleanup_job_files(s3_key)
# Assert
assert result is True
mock_s3_client.delete_object.assert_called_once_with(
Bucket="equalify-pdf-temp",
Key=s3_key
)
@pytest.mark.asyncio
async def test_cleanup_job_files_already_deleted(cleanup_service, mock_s3_client):
"""Test cleanup of non-existent file (idempotent behavior)."""
# Arrange
s3_key = "temp/deleted-job-456.pdf"
# Mock ClientError with NoSuchKey code
error = ClientError(
{"Error": {"Code": "NoSuchKey", "Message": "The specified key does not exist."}},
"DeleteObject"
)
mock_s3_client.delete_object.side_effect = error
# Act
result = await cleanup_service.cleanup_job_files(s3_key)
# Assert
assert result is True # Idempotent success
mock_s3_client.delete_object.assert_called_once()
@pytest.mark.asyncio
async def test_cleanup_job_files_s3_error(cleanup_service, mock_s3_client):
"""Test cleanup with S3 error (logs but doesn't fail)."""
# Arrange
s3_key = "temp/error-job-789.pdf"
mock_s3_client.delete_object.side_effect = ClientError(
{"Error": {"Code": "InternalError", "Message": "S3 error"}},
"DeleteObject"
)
# Act
result = await cleanup_service.cleanup_job_files(s3_key)
# Assert
assert result is False # Error logged, returns False
mock_s3_client.delete_object.assert_called_once()
@pytest.mark.asyncio
async def test_cleanup_job_files_generic_exception(cleanup_service, mock_s3_client):
"""Test cleanup with unexpected exception."""
# Arrange
s3_key = "temp/exception-job-999.pdf"
mock_s3_client.delete_object.side_effect = RuntimeError("Unexpected error")
# Act
result = await cleanup_service.cleanup_job_files(s3_key)
# Assert
assert result is False
mock_s3_client.delete_object.assert_called_once()
@pytest.mark.asyncio
async def test_cleanup_service_uses_correct_bucket(cleanup_service, mock_s3_client):
"""Test that cleanup service uses configured temp bucket."""
# Arrange
s3_key = "temp/bucket-test.pdf"
mock_s3_client.delete_object.return_value = {}
# Act
await cleanup_service.cleanup_job_files(s3_key)
# Assert
call_args = mock_s3_client.delete_object.call_args
assert call_args[1]["Bucket"] == "equalify-pdf-temp"
assert call_args[1]["Key"] == s3_key