From 5f899316cef88602a0cdb7102278343e87f246c5 Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Wed, 5 Aug 2026 06:03:55 +0200 Subject: [PATCH] Unify aria2 pause and resume state changes --- core/download/aria2_backend.py | 23 +++++++++---- tests/test_downloader_boundaries.py | 52 +++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/core/download/aria2_backend.py b/core/download/aria2_backend.py index 1caa86a..b18651a 100644 --- a/core/download/aria2_backend.py +++ b/core/download/aria2_backend.py @@ -1187,23 +1187,34 @@ def queue_aria2_desired_state( return {"success": True, "message": message} -def pause_download(download_id: str, *, dependencies: Any = None) -> Dict[str, Any]: - """Pause an aria2 download. Built-in Python downloads cannot be paused.""" +def _set_aria2_desired_download_state( + download_id: str, + status: str, + *, + dependencies: Any = None, +) -> Dict[str, Any]: facade = _require_dependencies(dependencies) if download_id in facade.cancelled_downloads: return {"success": False, "error": "Download is being cancelled"} return facade._queue_aria2_desired_state( + download_id, + status, + ) + + +def pause_download(download_id: str, *, dependencies: Any = None) -> Dict[str, Any]: + """Pause an aria2 download. Built-in Python downloads cannot be paused.""" + return _set_aria2_desired_download_state( download_id, "paused", + dependencies=dependencies, ) def resume_download(download_id: str, *, dependencies: Any = None) -> Dict[str, Any]: """Resume a paused aria2 download.""" - facade = _require_dependencies(dependencies) - if download_id in facade.cancelled_downloads: - return {"success": False, "error": "Download is being cancelled"} - return facade._queue_aria2_desired_state( + return _set_aria2_desired_download_state( download_id, "downloading", + dependencies=dependencies, ) diff --git a/tests/test_downloader_boundaries.py b/tests/test_downloader_boundaries.py index 2ffa8f7..dfc24ec 100644 --- a/tests/test_downloader_boundaries.py +++ b/tests/test_downloader_boundaries.py @@ -3,10 +3,14 @@ import tempfile import threading import unittest from types import SimpleNamespace -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock, call, patch from core.download.api import context as downloader -from core.download.aria2_backend import recover_aria2_missing_control_file +from core.download.aria2_backend import ( + pause_download, + recover_aria2_missing_control_file, + resume_download, +) from core.download.orchestrator import find_active_download_for_path from core.path_utils import normalize_metadata_file_path @@ -135,6 +139,50 @@ class DownloaderBoundaryTests(unittest.TestCase): self.assertFalse(downloader._aria2_action_error_is_ok("paused", "permission denied")) self.assertFalse(downloader._aria2_action_error_is_ok("completed", "already paused")) + def test_pause_and_resume_queue_their_expected_aria2_states(self): + facade = SimpleNamespace( + cancelled_downloads=set(), + _queue_aria2_desired_state=MagicMock( + side_effect=lambda download_id, status: { + "success": True, + "download_id": download_id, + "status": status, + } + ), + ) + + self.assertEqual( + {"success": True, "download_id": "pause-1", "status": "paused"}, + pause_download("pause-1", dependencies=facade), + ) + self.assertEqual( + { + "success": True, + "download_id": "resume-1", + "status": "downloading", + }, + resume_download("resume-1", dependencies=facade), + ) + facade._queue_aria2_desired_state.assert_has_calls( + [call("pause-1", "paused"), call("resume-1", "downloading")] + ) + + def test_pause_and_resume_reject_downloads_being_cancelled(self): + facade = SimpleNamespace( + cancelled_downloads={"cancelled-1"}, + _queue_aria2_desired_state=MagicMock(), + ) + + self.assertEqual( + {"success": False, "error": "Download is being cancelled"}, + pause_download("cancelled-1", dependencies=facade), + ) + self.assertEqual( + {"success": False, "error": "Download is being cancelled"}, + resume_download("cancelled-1", dependencies=facade), + ) + facade._queue_aria2_desired_state.assert_not_called() + def test_aria2_missing_control_file_error_is_detected(self): self.assertTrue( downloader._is_aria2_missing_control_file_error(