Skip to content

Commit 2516e27

Browse files
committed
fixed tests
Signed-off-by: Dhiren-Mhatre <kp064669@gmail.com>
1 parent 970e77d commit 2516e27

File tree

2 files changed

+77
-55
lines changed

2 files changed

+77
-55
lines changed

gen3/file.py

Lines changed: 49 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -177,65 +177,64 @@ def _ensure_dirpath_exists(path: Path) -> Path:
177177

178178
return out_path
179179

180-
def download_single(
181-
self,
182-
guid,
183-
download_path=".",
184-
filename_format="original",
185-
protocol=None,
186-
skip_completed=False,
187-
rename=False,
188-
):
189-
"""Download a single file with enhanced options.
180+
def download_single(self, object_id, path):
181+
"""
182+
Download a single file using its GUID.
190183
191184
Args:
192-
guid (str): File GUID to download
193-
download_path (str): Directory to save file
194-
filename_format (str): Format for filename - 'original', 'guid', or 'combined'
195-
protocol (str, optional): Protocol preference for download
196-
skip_completed (bool): Skip if file already exists
197-
rename (bool): Rename file if conflict exists
185+
object_id (str): The file's unique ID
186+
path (str): Path to store the downloaded file at
198187
199188
Returns:
200-
Dict: Download result with status and details
189+
bool: True if download successful, False otherwise
201190
"""
202-
# Create a single-item manifest to reuse async logic
203-
manifest_data = [{"guid": guid}]
204-
205-
# Use the async download logic with single process
206-
loop = asyncio.new_event_loop()
207-
asyncio.set_event_loop(loop)
208-
209191
try:
210-
result = loop.run_until_complete(
211-
self.async_download_multiple(
212-
manifest_data=manifest_data,
213-
download_path=download_path,
214-
filename_format=filename_format,
215-
protocol=protocol,
216-
max_concurrent_requests=1,
217-
num_processes=1,
218-
queue_size=1,
219-
skip_completed=skip_completed,
220-
rename=rename,
221-
no_progress=True,
222-
)
223-
)
192+
url = self.get_presigned_url(object_id)
193+
except Exception as e:
194+
logging.critical(f"Unable to get a presigned URL for download: {e}")
195+
return False
224196

225-
# Extract the single result
226-
if result["succeeded"]:
227-
return {"status": "downloaded", "filepath": result["succeeded"][0]}
228-
elif result["skipped"]:
229-
return {"status": "skipped", "filepath": result["skipped"][0]}
230-
elif result["failed"]:
231-
return {"status": "failed", "error": result["failed"][0]}
197+
response = requests.get(url["url"], stream=True)
198+
if response.status_code != 200:
199+
logging.error(f"Response code: {response.status_code}")
200+
if response.status_code >= 500:
201+
for _ in range(MAX_RETRIES):
202+
logging.info("Retrying now...")
203+
# NOTE could be updated with exponential backoff
204+
time.sleep(1)
205+
response = requests.get(url["url"], stream=True)
206+
if response.status_code == 200:
207+
break
208+
if response.status_code != 200:
209+
logging.critical("Response status not 200, try again later")
210+
return False
232211
else:
233-
return {"status": "failed", "error": "Unknown error"}
212+
return False
234213

235-
except Exception as e:
236-
return {"status": "failed", "error": f"Download failed: {e}"}
237-
finally:
238-
loop.close()
214+
response.raise_for_status()
215+
216+
total_size_in_bytes = int(response.headers.get("content-length"))
217+
total_downloaded = 0
218+
219+
index = Gen3Index(self._auth_provider)
220+
record = index.get_record(object_id)
221+
222+
filename = record["file_name"]
223+
224+
out_path = Gen3File._ensure_dirpath_exists(Path(path))
225+
226+
with open(os.path.join(out_path, filename), "wb") as f:
227+
for data in response.iter_content(4096):
228+
total_downloaded += len(data)
229+
f.write(data)
230+
231+
if total_size_in_bytes == total_downloaded:
232+
logging.info(f"File {filename} downloaded successfully")
233+
else:
234+
logging.error(f"File {filename} not downloaded successfully")
235+
return False
236+
237+
return True
239238

240239
def upload_file_to_guid(
241240
self, guid, file_name, protocol=None, expires_in=None, bucket=None

tests/download_tests/test_async_download.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from unittest.mock import patch
1+
from unittest.mock import patch, MagicMock, AsyncMock
22
import json
33
import pytest
44
from pathlib import Path
@@ -99,7 +99,6 @@ def test_load_manifest(self, mock_gen3_auth):
9999
data = json.load(f)
100100
assert len(data) == len(manifest_list)
101101

102-
@pytest.mark.skip(reason="download_single uses multiprocessing which is incompatible with mocking in tests")
103102
@patch("gen3.file.requests")
104103
@patch("gen3.index.Gen3Index.get_record")
105104
@pytest.mark.parametrize("download_dir_overwrite", [None, "sub/path"])
@@ -165,7 +164,6 @@ def test_download_single(
165164
if download_dir_overwrite and os.path.exists(download_path):
166165
shutil.rmtree(download_path)
167166

168-
@pytest.mark.skip(reason="download_single uses multiprocessing which is incompatible with mocking in tests")
169167
@patch("gen3.file.requests")
170168
def test_download_single_no_auth(self, mock_get, download_dir, mock_gen3_auth):
171169

@@ -196,7 +194,6 @@ def test_download_single_no_auth(self, mock_get, download_dir, mock_gen3_auth):
196194

197195
assert result == False
198196

199-
@pytest.mark.skip(reason="download_single uses multiprocessing which is incompatible with mocking in tests")
200197
@patch("gen3.file.requests")
201198
def test_download_single_wrong_auth(self, mock_get, download_dir, mock_gen3_auth):
202199

@@ -227,7 +224,6 @@ def test_download_single_wrong_auth(self, mock_get, download_dir, mock_gen3_auth
227224

228225
assert result == False
229226

230-
@pytest.mark.skip(reason="download_single uses multiprocessing which is incompatible with mocking in tests")
231227
@patch("gen3.file.requests")
232228
def test_download_single_bad_id(self, mock_get, download_dir, mock_gen3_auth):
233229

@@ -268,3 +264,30 @@ def test_load_manifest_bad_format(self):
268264

269265
manifest_list = _load_manifest(Path(DIR, "resources/bad_format.json"))
270266
assert manifest_list == None
267+
268+
@pytest.mark.asyncio
269+
async def test_async_download_multiple_empty_manifest(self, mock_gen3_auth):
270+
"""
271+
Test async_download_multiple with an empty manifest.
272+
Verifies it returns empty results without errors.
273+
"""
274+
file_tool = Gen3File(mock_gen3_auth)
275+
result = await file_tool.async_download_multiple(manifest_data=[])
276+
277+
assert result == {"succeeded": [], "failed": [], "skipped": []}
278+
279+
@pytest.mark.asyncio
280+
async def test_async_download_multiple_invalid_guids(self, mock_gen3_auth):
281+
"""
282+
Test async_download_multiple with invalid GUIDs.
283+
Verifies it returns empty results for missing GUIDs.
284+
"""
285+
file_tool = Gen3File(mock_gen3_auth)
286+
287+
# Manifest with missing guid/object_id fields
288+
manifest_data = [{"file_name": "test.txt"}, {}]
289+
290+
result = await file_tool.async_download_multiple(manifest_data=manifest_data)
291+
292+
assert result == {"succeeded": [], "failed": [], "skipped": []}
293+

0 commit comments

Comments
 (0)