Skip to content

Commit 05e7094

Browse files
authored
♻️ refactor image module.
2 parents f85a489 + 6015f90 commit 05e7094

File tree

4 files changed

+376
-61
lines changed

4 files changed

+376
-61
lines changed

backend/apps/image_app.py

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import logging
22
from urllib.parse import unquote
33

4-
import aiohttp
54
from fastapi import APIRouter
65

7-
from consts.const import DATA_PROCESS_SERVICE
6+
from services.image_service import proxy_image_impl
87

98
# Create router
109
router = APIRouter()
@@ -28,23 +27,8 @@ async def proxy_image(url: str):
2827
try:
2928
# URL decode
3029
decoded_url = unquote(url)
31-
32-
# Create session to call the data processing service
33-
async with aiohttp.ClientSession() as session:
34-
# Call the data processing service to load the image
35-
data_process_url = f"{DATA_PROCESS_SERVICE}/tasks/load_image?url={decoded_url}"
36-
37-
async with session.get(data_process_url) as response:
38-
if response.status != 200:
39-
error_text = await response.text()
40-
logger.warning(
41-
f"Failed to fetch image from data process service: {error_text}")
42-
return {"success": False, "error": "Failed to fetch image or image format not supported"}
43-
44-
result = await response.json()
45-
return result
46-
30+
return await proxy_image_impl(decoded_url)
4731
except Exception as e:
4832
logger.error(
4933
f"Error occurred while proxying image: {str(e)}, URL: {url[:50]}...")
50-
return {"success": False, "error": str(e)}
34+
return {"success": False, "error": str(e)}

backend/services/image_service.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import logging
2+
from http import HTTPStatus
3+
4+
import aiohttp
5+
6+
from consts.const import DATA_PROCESS_SERVICE
7+
8+
logger = logging.getLogger("image_service")
9+
10+
async def proxy_image_impl(decoded_url: str):
11+
# Create session to call the data processing service
12+
async with aiohttp.ClientSession() as session:
13+
# Call the data processing service to load the image
14+
data_process_url = f"{DATA_PROCESS_SERVICE}/tasks/load_image?url={decoded_url}"
15+
16+
async with session.get(data_process_url) as response:
17+
if response.status != HTTPStatus.OK:
18+
error_text = await response.text()
19+
logger.error(
20+
f"Failed to fetch image from data process service: {error_text}")
21+
return {"success": False, "error": "Failed to fetch image or image format not supported"}
22+
23+
result = await response.json()
24+
return result

test/backend/app/test_image_app.py

Lines changed: 47 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import json
21
import sys
32
import os
4-
from io import BytesIO
53
import pytest
64
from unittest.mock import patch, MagicMock, AsyncMock
75

8-
# Add the project root directory to sys.path
9-
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..')))
6+
# Dynamically determine the backend path
7+
current_dir = os.path.dirname(os.path.abspath(__file__))
8+
backend_dir = os.path.abspath(os.path.join(current_dir, "../../../backend"))
9+
sys.path.append(backend_dir)
1010

1111
# Mock the consts.const module before importing the image_app module
1212
mock_const = MagicMock()
@@ -36,74 +36,77 @@
3636
"error": "Failed to fetch image or image format not supported"
3737
}
3838

39+
3940
@pytest.mark.asyncio
4041
async def test_proxy_image_success(monkeypatch):
4142
"""Test successful image proxy request"""
4243
# Create mock response
4344
mock_response = AsyncMock()
4445
mock_response.status = 200
4546
mock_response.json = AsyncMock(return_value=success_response)
46-
47+
4748
# Create mock session
4849
mock_session = AsyncMock()
4950
mock_get = AsyncMock()
5051
mock_get.__aenter__.return_value = mock_response
5152
mock_session.get = MagicMock(return_value=mock_get)
52-
53+
5354
# Create mock session factory
5455
mock_client_session = AsyncMock()
5556
mock_client_session.__aenter__.return_value = mock_session
56-
57-
# Patch the ClientSession
58-
with patch('backend.apps.image_app.aiohttp.ClientSession') as mock_session_class:
57+
58+
# Patch the ClientSession in the correct module
59+
with patch('backend.services.image_service.aiohttp.ClientSession') as mock_session_class:
5960
mock_session_class.return_value = mock_client_session
60-
61+
6162
# Test with TestClient
6263
response = client.get(f"/image?url={encoded_test_url}")
63-
64+
6465
# Assertions
6566
assert response.status_code == 200
6667
assert response.json() == success_response
67-
68+
6869
# Verify correct URL was called
6970
mock_session.get.assert_called_once()
7071

72+
7173
@pytest.mark.asyncio
7274
async def test_proxy_image_remote_error(monkeypatch):
7375
"""Test image proxy when remote service returns error"""
7476
# Create mock response
7577
mock_response = AsyncMock()
7678
mock_response.status = 404
7779
mock_response.text = AsyncMock(return_value="Image not found")
78-
80+
7981
# Create mock session
8082
mock_session = AsyncMock()
8183
mock_get = AsyncMock()
8284
mock_get.__aenter__.return_value = mock_response
8385
mock_session.get = MagicMock(return_value=mock_get)
84-
86+
8587
# Create mock session factory
8688
mock_client_session = AsyncMock()
8789
mock_client_session.__aenter__.return_value = mock_session
88-
90+
8991
# Create expected error response
9092
expected_error_response = {
9193
"success": False,
9294
"error": "Failed to fetch image: Image not found"
9395
}
94-
95-
# Patch the ClientSession
96-
with patch('backend.apps.image_app.aiohttp.ClientSession') as mock_session_class:
96+
97+
# Patch the ClientSession in the correct module
98+
with patch('backend.services.image_service.aiohttp.ClientSession') as mock_session_class:
9799
mock_session_class.return_value = mock_client_session
98-
100+
99101
# Test with TestClient
100102
response = client.get(f"/image?url={encoded_test_url}")
101-
103+
102104
# Assertions
103105
assert response.status_code == 200
104106
assert response.json()["success"] is False
105107
assert "Failed to fetch image" in response.json()["error"]
106108

109+
107110
@pytest.mark.asyncio
108111
async def test_proxy_image_exception(monkeypatch):
109112
"""Test image proxy when an exception occurs"""
@@ -112,60 +115,62 @@ async def test_proxy_image_exception(monkeypatch):
112115
mock_get = AsyncMock()
113116
mock_get.__aenter__.side_effect = Exception("Connection error")
114117
mock_session.get = MagicMock(return_value=mock_get)
115-
118+
116119
# Create mock session factory
117120
mock_client_session = AsyncMock()
118121
mock_client_session.__aenter__.return_value = mock_session
119-
120-
# Patch the ClientSession
121-
with patch('backend.apps.image_app.aiohttp.ClientSession') as mock_session_class:
122+
123+
# Patch the ClientSession in the correct module
124+
with patch('backend.services.image_service.aiohttp.ClientSession') as mock_session_class:
122125
mock_session_class.return_value = mock_client_session
123-
126+
124127
# Test with TestClient
125128
response = client.get(f"/image?url={encoded_test_url}")
126-
129+
127130
# Assertions
128131
assert response.status_code == 200
129132
assert response.json()["success"] is False
130133
assert response.json()["error"] == "Connection error"
131134

135+
132136
@pytest.mark.asyncio
133137
async def test_proxy_image_with_special_chars(monkeypatch):
134138
"""Test image proxy with URL containing special characters"""
135139
special_url = "https://example.com/image with spaces.jpg"
136140
encoded_special_url = "https%3A%2F%2Fexample.com%2Fimage%20with%20spaces.jpg"
137-
141+
138142
# Create mock response
139143
mock_response = AsyncMock()
140144
mock_response.status = 200
141145
mock_response.json = AsyncMock(return_value=success_response)
142-
146+
143147
# Create mock session
144148
mock_session = AsyncMock()
145149
mock_get = AsyncMock()
146150
mock_get.__aenter__.return_value = mock_response
147151
mock_session.get = MagicMock(return_value=mock_get)
148-
152+
149153
# Create mock session factory
150154
mock_client_session = AsyncMock()
151155
mock_client_session.__aenter__.return_value = mock_session
152-
153-
# Patch the ClientSession
154-
with patch('backend.apps.image_app.aiohttp.ClientSession') as mock_session_class:
156+
157+
# Patch the ClientSession in the correct module
158+
with patch('backend.services.image_service.aiohttp.ClientSession') as mock_session_class:
155159
mock_session_class.return_value = mock_client_session
156-
160+
157161
# Test with TestClient
158162
response = client.get(f"/image?url={encoded_special_url}")
159-
163+
160164
# Assertions
161165
assert response.status_code == 200
162166
assert response.json() == success_response
163-
167+
164168
# Verify URL was correctly passed
165169
mock_session.get.assert_called_once()
166170
called_args = mock_session.get.call_args[0][0]
167171
assert special_url in called_args or encoded_special_url in called_args
168172

173+
169174
@pytest.mark.asyncio
170175
async def test_proxy_image_logging(monkeypatch):
171176
"""Test error handling when an exception occurs"""
@@ -174,23 +179,23 @@ async def test_proxy_image_logging(monkeypatch):
174179
mock_get = AsyncMock()
175180
mock_get.__aenter__.side_effect = Exception("Logging test error")
176181
mock_session.get = MagicMock(return_value=mock_get)
177-
182+
178183
# Create mock session factory
179184
mock_client_session = AsyncMock()
180185
mock_client_session.__aenter__.return_value = mock_session
181-
182-
# Patch the ClientSession
183-
with patch('backend.apps.image_app.aiohttp.ClientSession') as mock_session_class:
186+
187+
# Patch the ClientSession in the correct module
188+
with patch('backend.services.image_service.aiohttp.ClientSession') as mock_session_class:
184189
mock_session_class.return_value = mock_client_session
185-
190+
186191
# Test with TestClient
187192
response = client.get(f"/image?url={encoded_test_url}")
188-
193+
189194
# Focus on verifying the error handling in the response
190195
assert response.status_code == 200 # API should still return 200 status
191196
response_data = response.json()
192197
assert response_data["success"] is False
193198
assert "Logging test error" in response_data["error"]
194-
199+
195200
# Verify the mock was called with the expected URL
196201
mock_session.get.assert_called_once()

0 commit comments

Comments
 (0)