From 0a06370a9b33816127beac02ffe2f9bc3c240871 Mon Sep 17 00:00:00 2001 From: Russell Martin Date: Wed, 18 Oct 2023 09:57:30 -0400 Subject: [PATCH] Add "individual torrent" methods to `torrents` namespace (fixes #261) --- src/qbittorrentapi/torrents.py | 175 ++++++++++++++++++++++++++++++ tests/test_app.py | 12 +++ tests/test_auth.py | 12 +++ tests/test_log.py | 11 ++ tests/test_rss.py | 11 ++ tests/test_search.py | 12 +++ tests/test_sync.py | 12 +++ tests/test_torrents.py | 188 ++++++++++++++++++++++++++------- tests/test_transfer.py | 12 +++ tox.ini | 3 +- 10 files changed, 411 insertions(+), 37 deletions(-) diff --git a/src/qbittorrentapi/torrents.py b/src/qbittorrentapi/torrents.py index 6b71caac2..4098839a4 100644 --- a/src/qbittorrentapi/torrents.py +++ b/src/qbittorrentapi/torrents.py @@ -2697,6 +2697,181 @@ def errored( **kwargs, ) + @wraps(TorrentsAPIMixIn.torrents_properties) + def properties( + self, + torrent_hash: str | None = None, + **kwargs: APIKwargsT, + ) -> TorrentPropertiesDictionary: + return self._client.torrents_properties(torrent_hash=torrent_hash, **kwargs) + + @wraps(TorrentsAPIMixIn.torrents_trackers) + def trackers( + self, + torrent_hash: str | None = None, + **kwargs: APIKwargsT, + ) -> TrackersList: + return self._client.torrents_trackers(torrent_hash=torrent_hash, **kwargs) + + @wraps(TorrentsAPIMixIn.torrents_webseeds) + def webseeds( + self, + torrent_hash: str | None = None, + **kwargs: APIKwargsT, + ) -> WebSeedsList: + return self._client.torrents_webseeds(torrent_hash=torrent_hash, **kwargs) + + @wraps(TorrentsAPIMixIn.torrents_files) + def files( + self, + torrent_hash: str | None = None, + **kwargs: APIKwargsT, + ) -> TorrentFilesList: + return self._client.torrents_files(torrent_hash=torrent_hash, **kwargs) + + @wraps(TorrentsAPIMixIn.torrents_piece_states) + def piece_states( + self, + torrent_hash: str | None = None, + **kwargs: APIKwargsT, + ) -> TorrentPieceInfoList: + return self._client.torrents_piece_states(torrent_hash=torrent_hash, **kwargs) + + pieceStates = piece_states + + @wraps(TorrentsAPIMixIn.torrents_piece_hashes) + def piece_hashes( + self, + torrent_hash: str | None = None, + **kwargs: APIKwargsT, + ) -> TorrentPieceInfoList: + return self._client.torrents_piece_hashes(torrent_hash=torrent_hash, **kwargs) + + pieceHashes = piece_hashes + + @wraps(TorrentsAPIMixIn.torrents_add_trackers) + def add_trackers( + self, + torrent_hash: str | None = None, + urls: Iterable[str] | None = None, + **kwargs: APIKwargsT, + ) -> None: + return self._client.torrents_add_trackers( + torrent_hash=torrent_hash, + urls=urls, + **kwargs, + ) + + addTrackers = add_trackers + + @wraps(TorrentsAPIMixIn.torrents_edit_tracker) + def edit_tracker( + self, + torrent_hash: str | None = None, + original_url: str | None = None, + new_url: str | None = None, + **kwargs: APIKwargsT, + ) -> None: + return self._client.torrents_edit_tracker( + torrent_hash=torrent_hash, + original_url=original_url, + new_url=new_url, + **kwargs, + ) + + editTracker = edit_tracker + + @wraps(TorrentsAPIMixIn.torrents_remove_trackers) + def remove_trackers( + self, + torrent_hash: str | None = None, + urls: Iterable[str] | None = None, + **kwargs: APIKwargsT, + ) -> None: + return self._client.torrents_remove_trackers( + torrent_hash=torrent_hash, + urls=urls, + **kwargs, + ) + + removeTrackers = remove_trackers + + @wraps(TorrentsAPIMixIn.torrents_file_priority) + def file_priority( + self, + torrent_hash: str | None = None, + file_ids: int | Iterable[str | int] | None = None, + priority: str | int | None = None, + **kwargs: APIKwargsT, + ) -> None: + return self._client.torrents_file_priority( + torrent_hash=torrent_hash, + file_ids=file_ids, + priority=priority, + **kwargs, + ) + + filePrio = file_priority + + @wraps(TorrentsAPIMixIn.torrents_rename) + def rename( + self, + torrent_hash: str | None = None, + new_torrent_name: str | None = None, + **kwargs: APIKwargsT, + ) -> None: + return self._client.torrents_rename( + torrent_hash=torrent_hash, + new_torrent_name=new_torrent_name, + **kwargs, + ) + + @wraps(TorrentsAPIMixIn.torrents_rename_file) + def rename_file( + self, + torrent_hash: str | None = None, + file_id: str | int | None = None, + new_file_name: str | None = None, + old_path: str | None = None, + new_path: str | None = None, + **kwargs: APIKwargsT, + ) -> None: + return self._client.torrents_rename_file( + torrent_hash=torrent_hash, + file_id=file_id, + new_file_name=new_file_name, + old_path=old_path, + new_path=new_path, + **kwargs, + ) + + renameFile = rename_file + + @wraps(TorrentsAPIMixIn.torrents_rename_folder) + def rename_folder( + self, + torrent_hash: str | None = None, + old_path: str | None = None, + new_path: str | None = None, + **kwargs: APIKwargsT, + ) -> None: + return self._client.torrents_rename_folder( + torrent_hash=torrent_hash, + old_path=old_path, + new_path=new_path, + **kwargs, + ) + + renameFolder = rename_folder + + @wraps(TorrentsAPIMixIn.torrents_export) + def export( + self, + torrent_hash: str | None = None, + **kwargs: APIKwargsT, + ) -> bytes: + return self._client.torrents_export(torrent_hash=torrent_hash, **kwargs) + class TorrentCategories(ClientCache[TorrentsAPIMixIn]): """ diff --git a/tests/test_app.py b/tests/test_app.py index 3414723b2..f612c6f01 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -1,5 +1,8 @@ +import sys + import pytest +from qbittorrentapi import APINames from qbittorrentapi._attrdict import AttrDict from qbittorrentapi.app import NetworkInterface from qbittorrentapi.app import NetworkInterfaceAddressList @@ -7,6 +10,15 @@ from tests.conftest import IS_QBT_DEV +@pytest.mark.skipif(sys.version_info < (3, 9), reason="removeprefix not in 3.8") +def test_methods(client): + namespace = APINames.Application + all_dotted_methods = set(dir(getattr(client, namespace))) + + for meth in [meth for meth in dir(client) if meth.startswith(f"{namespace}_")]: + assert meth.removeprefix(f"{namespace}_") in all_dotted_methods + + def test_version(client, app_version): assert client.app_version() == app_version assert client.app.version == app_version diff --git a/tests/test_auth.py b/tests/test_auth.py index 211c213eb..1182e7272 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -1,9 +1,21 @@ +import sys + import pytest +from qbittorrentapi import APINames from qbittorrentapi import Client from qbittorrentapi.exceptions import APIConnectionError +@pytest.mark.skipif(sys.version_info < (3, 9), reason="removeprefix not in 3.8") +def test_methods(client): + namespace = APINames.Authorization + all_dotted_methods = set(dir(getattr(client, namespace))) + + for meth in [meth for meth in dir(client) if meth.startswith(f"{namespace}_")]: + assert meth.removeprefix(f"{namespace}_") in all_dotted_methods + + def test_is_logged_in(): client = Client( RAISE_NOTIMPLEMENTEDERROR_FOR_UNIMPLEMENTED_API_ENDPOINTS=True, diff --git a/tests/test_log.py b/tests/test_log.py index 5028a3134..bdc9c8087 100644 --- a/tests/test_log.py +++ b/tests/test_log.py @@ -1,3 +1,5 @@ +import sys + import pytest from qbittorrentapi.definitions import APINames @@ -5,6 +7,15 @@ from qbittorrentapi.log import LogPeersList +@pytest.mark.skipif(sys.version_info < (3, 9), reason="removeprefix not in 3.8") +def test_methods(client): + namespace = APINames.Log + all_dotted_methods = set(dir(getattr(client, namespace))) + + for meth in [meth for meth in dir(client) if meth.startswith(f"{namespace}_")]: + assert meth.removeprefix(f"{namespace}_") in all_dotted_methods + + @pytest.mark.parametrize("main_func", ["log_main", "log.main"]) @pytest.mark.parametrize("last_known_id", (None, 0)) def test_log_main_id(client, main_func, last_known_id): diff --git a/tests/test_rss.py b/tests/test_rss.py index 018c966e0..291ff8c09 100644 --- a/tests/test_rss.py +++ b/tests/test_rss.py @@ -1,7 +1,9 @@ +import sys from time import sleep import pytest +from qbittorrentapi import APINames from qbittorrentapi._version_support import v from qbittorrentapi.exceptions import APIError from qbittorrentapi.rss import RSSitemsDictionary @@ -51,6 +53,15 @@ def delete_feed(name): yield "" +@pytest.mark.skipif(sys.version_info < (3, 9), reason="removeprefix not in 3.8") +def test_methods(client): + namespace = APINames.RSS + all_dotted_methods = set(dir(getattr(client, namespace))) + + for meth in [meth for meth in dir(client) if meth.startswith(f"{namespace}_")]: + assert meth.removeprefix(f"{namespace}_") in all_dotted_methods + + @pytest.mark.skipif_before_api_version("2.2.1") @pytest.mark.parametrize( "refresh_item_func", diff --git a/tests/test_search.py b/tests/test_search.py index 8c5b16590..5ee89c6d7 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -1,5 +1,8 @@ +import sys + import pytest +from qbittorrentapi import APINames from qbittorrentapi import NotFound404Error from qbittorrentapi.search import SearchCategoriesList from qbittorrentapi.search import SearchJobDictionary @@ -13,6 +16,15 @@ PLUGIN_URL = "https://raw.githubusercontent.com/khensolomon/leyts/master/yts.py" +@pytest.mark.skipif(sys.version_info < (3, 9), reason="removeprefix not in 3.8") +def test_methods(client): + namespace = APINames.Search + all_dotted_methods = set(dir(getattr(client, namespace))) + + for meth in [meth for meth in dir(client) if meth.startswith(f"{namespace}_")]: + assert meth.removeprefix(f"{namespace}_") in all_dotted_methods + + @pytest.mark.skipif_before_api_version("2.1.1") @pytest.mark.parametrize( "update_func", ["search_update_plugins", "search.update_plugins"] diff --git a/tests/test_sync.py b/tests/test_sync.py index 4cb8b7a50..5e94b98e5 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -1,9 +1,21 @@ +import sys + import pytest +from qbittorrentapi import APINames from qbittorrentapi.sync import SyncMainDataDictionary from qbittorrentapi.sync import SyncTorrentPeersDictionary +@pytest.mark.skipif(sys.version_info < (3, 9), reason="removeprefix not in 3.8") +def test_methods(client): + namespace = APINames.Sync + all_dotted_methods = set(dir(getattr(client, namespace))) + + for meth in [meth for meth in dir(client) if meth.startswith(f"{namespace}_")]: + assert meth.removeprefix(f"{namespace}_") in all_dotted_methods + + @pytest.mark.parametrize("maindata_func", ["sync_maindata", "sync.maindata"]) @pytest.mark.parametrize("rid", [None, 0, 1, 100000]) def test_sync_maindata(client, maindata_func, rid): diff --git a/tests/test_torrents.py b/tests/test_torrents.py index 5b04e1fbc..b0f13b77f 100644 --- a/tests/test_torrents.py +++ b/tests/test_torrents.py @@ -1,10 +1,12 @@ import errno import platform +import sys from time import sleep import pytest import requests +from qbittorrentapi import APINames from qbittorrentapi._version_support import v from qbittorrentapi.exceptions import Conflict409Error from qbittorrentapi.exceptions import Forbidden403Error @@ -46,6 +48,18 @@ def enable_queueing(client): client.app.set_preferences(dict(queueing_enabled=True)) +@pytest.mark.skipif(sys.version_info < (3, 9), reason="removeprefix not in 3.8") +def test_methods(client): + all_dotted_methods = { + meth + for namespace in [APINames.Torrents, "torrent_tags", "torrent_categories"] + for meth in dir(getattr(client, namespace)) + } + + for meth in [meth for meth in dir(client) if meth.startswith("torrents_")]: + assert meth.removeprefix("torrents_") in all_dotted_methods + + # something was wrong with torrents_add on v2.0.0 (the initial version) @pytest.mark.skipif_before_api_version("2.0.1") @pytest.mark.parametrize( @@ -306,72 +320,117 @@ def test_torrents_add_download_path(client, use_download_path, tmp_path): check(lambda: mkpath(torrent.info.download_path), download_path) -def test_properties(client, orig_torrent): - props = client.torrents_properties(torrent_hash=orig_torrent.hash) +@pytest.mark.parametrize( + "properties_func", ["torrents_properties", "torrents.properties"] +) +def test_properties(client, orig_torrent, properties_func): + props = client.func(properties_func)(torrent_hash=orig_torrent.hash) assert isinstance(props, TorrentPropertiesDictionary) -def test_trackers(client, orig_torrent): - trackers = client.torrents_trackers(torrent_hash=orig_torrent.hash) +@pytest.mark.parametrize("trackers_func", ["torrents_trackers", "torrents.trackers"]) +def test_trackers(client, orig_torrent, trackers_func): + trackers = client.func(trackers_func)(torrent_hash=orig_torrent.hash) assert isinstance(trackers, TrackersList) -def test_trackers_slice(client, orig_torrent): - trackers = client.torrents_trackers(torrent_hash=orig_torrent.hash) +@pytest.mark.parametrize("trackers_func", ["torrents_trackers", "torrents.trackers"]) +def test_trackers_slice(client, orig_torrent, trackers_func): + trackers = client.func(trackers_func)(torrent_hash=orig_torrent.hash) assert isinstance(trackers[1:2], TrackersList) -def test_webseeds(client, orig_torrent): - web_seeds = client.torrents_webseeds(torrent_hash=orig_torrent.hash) +@pytest.mark.parametrize("webseeds_func", ["torrents_webseeds", "torrents.webseeds"]) +def test_webseeds(client, orig_torrent, webseeds_func): + web_seeds = client.func(webseeds_func)(torrent_hash=orig_torrent.hash) assert isinstance(web_seeds, WebSeedsList) -def test_webseeds_slice(client, orig_torrent): - web_seeds = client.torrents_webseeds(torrent_hash=orig_torrent.hash) +@pytest.mark.parametrize("webseeds_func", ["torrents_webseeds", "torrents.webseeds"]) +def test_webseeds_slice(client, orig_torrent, webseeds_func): + web_seeds = client.func(webseeds_func)(torrent_hash=orig_torrent.hash) assert isinstance(web_seeds[1:2], WebSeedsList) -def test_files(client, orig_torrent): - files = client.torrents_files(torrent_hash=orig_torrent.hash) +@pytest.mark.parametrize("files_func", ["torrents_files", "torrents.files"]) +def test_files(client, orig_torrent, files_func): + files = client.func(files_func)(torrent_hash=orig_torrent.hash) assert isinstance(files, TorrentFilesList) assert "availability" in files[0] assert all(file["id"] == file["index"] for file in files) -def test_files_slice(client, orig_torrent): - files = client.torrents_files(torrent_hash=orig_torrent.hash) +@pytest.mark.parametrize("files_func", ["torrents_files", "torrents.files"]) +def test_files_slice(client, orig_torrent, files_func): + files = client.func(files_func)(torrent_hash=orig_torrent.hash) assert isinstance(files[1:2], TorrentFilesList) @pytest.mark.parametrize( - "piece_state_func", ["torrents_piece_states", "torrents_pieceStates"] + "piece_state_func", + [ + "torrents_piece_states", + "torrents_pieceStates", + "torrents.piece_states", + "torrents.pieceStates", + ], ) def test_piece_states(client, orig_torrent, piece_state_func): piece_states = client.func(piece_state_func)(torrent_hash=orig_torrent.hash) assert isinstance(piece_states, TorrentPieceInfoList) -def test_piece_states_slice(client, orig_torrent): - piece_states = client.torrents_piece_states(torrent_hash=orig_torrent.hash) +@pytest.mark.parametrize( + "piece_state_func", + [ + "torrents_piece_states", + "torrents_pieceStates", + "torrents.piece_states", + "torrents.pieceStates", + ], +) +def test_piece_states_slice(client, orig_torrent, piece_state_func): + piece_states = client.func(piece_state_func)(torrent_hash=orig_torrent.hash) assert isinstance(piece_states[1:2], TorrentPieceInfoList) @pytest.mark.parametrize( - "piece_hashes_func", ["torrents_piece_hashes", "torrents_pieceHashes"] + "piece_hashes_func", + [ + "torrents_piece_hashes", + "torrents_pieceHashes", + "torrents.piece_hashes", + "torrents.pieceHashes", + ], ) def test_piece_hashes(client, orig_torrent, piece_hashes_func): piece_hashes = client.func(piece_hashes_func)(torrent_hash=orig_torrent.hash) assert isinstance(piece_hashes, TorrentPieceInfoList) -def test_piece_hashes_slice(client, orig_torrent): - piece_hashes = client.torrents_piece_hashes(torrent_hash=orig_torrent.hash) +@pytest.mark.parametrize( + "piece_hashes_func", + [ + "torrents_piece_hashes", + "torrents_pieceHashes", + "torrents.piece_hashes", + "torrents.pieceHashes", + ], +) +def test_piece_hashes_slice(client, orig_torrent, piece_hashes_func): + piece_hashes = client.func(piece_hashes_func)(torrent_hash=orig_torrent.hash) assert isinstance(piece_hashes[1:2], TorrentPieceInfoList) @pytest.mark.parametrize("trackers", ["127.0.0.1", ["127.0.0.2", "127.0.0.3"]]) @pytest.mark.parametrize( - "add_trackers_func", ["torrents_add_trackers", "torrents_addTrackers"] + "add_trackers_func", + [ + "torrents_add_trackers", + "torrents_addTrackers", + "torrents.add_trackers", + "torrents.addTrackers", + ], ) def test_add_trackers(client, trackers, new_torrent, add_trackers_func): client.func(add_trackers_func)(torrent_hash=new_torrent.hash, urls=trackers) @@ -380,7 +439,13 @@ def test_add_trackers(client, trackers, new_torrent, add_trackers_func): @pytest.mark.skipif_before_api_version("2.2.0") @pytest.mark.parametrize( - "edit_trackers_func", ["torrents_edit_tracker", "torrents_editTracker"] + "edit_trackers_func", + [ + "torrents_edit_tracker", + "torrents_editTracker", + "torrents.edit_tracker", + "torrents.editTracker", + ], ) def test_edit_tracker(client, orig_torrent, edit_trackers_func): orig_torrent.add_trackers("127.1.0.1") @@ -395,7 +460,13 @@ def test_edit_tracker(client, orig_torrent, edit_trackers_func): @pytest.mark.skipif_after_api_version("2.2.0") @pytest.mark.parametrize( - "edit_trackers_func", ["torrents_edit_tracker", "torrents_editTracker"] + "edit_trackers_func", + [ + "torrents_edit_tracker", + "torrents_editTracker", + "torrents.edit_tracker", + "torrents.editTracker", + ], ) def test_edit_tracker_not_implemented(client, orig_torrent, edit_trackers_func): with pytest.raises(NotImplementedError): @@ -411,7 +482,13 @@ def test_edit_tracker_not_implemented(client, orig_torrent, edit_trackers_func): ], ) @pytest.mark.parametrize( - "remove_trackers_func", ["torrents_remove_trackers", "torrents_removeTrackers"] + "remove_trackers_func", + [ + "torrents_remove_trackers", + "torrents_removeTrackers", + "torrents.remove_trackers", + "torrents.removeTrackers", + ], ) def test_remove_trackers(client, trackers, orig_torrent, remove_trackers_func): orig_torrent.add_trackers(trackers) @@ -426,7 +503,13 @@ def test_remove_trackers(client, trackers, orig_torrent, remove_trackers_func): @pytest.mark.skipif_after_api_version("2.2.0") @pytest.mark.parametrize( - "remove_trackers_func", ["torrents_remove_trackers", "torrents_removeTrackers"] + "remove_trackers_func", + [ + "torrents_remove_trackers", + "torrents_removeTrackers", + "torrents.remove_trackers", + "torrents.removeTrackers", + ], ) def test_remove_trackers_not_implemented(client, orig_torrent, remove_trackers_func): with pytest.raises(NotImplementedError): @@ -434,7 +517,13 @@ def test_remove_trackers_not_implemented(client, orig_torrent, remove_trackers_f @pytest.mark.parametrize( - "file_prio_func", ["torrents_file_priority", "torrents_filePrio"] + "file_prio_func", + [ + "torrents_file_priority", + "torrents_filePrio", + "torrents.file_priority", + "torrents.filePrio", + ], ) def test_file_priority(client, orig_torrent, file_prio_func): client.func(file_prio_func)(torrent_hash=orig_torrent.hash, file_ids=0, priority=6) @@ -444,15 +533,22 @@ def test_file_priority(client, orig_torrent, file_prio_func): @pytest.mark.parametrize("new_name", ["new name 2", "new_name_2"]) -def test_rename(client, new_torrent, new_name): - client.torrents_rename(torrent_hash=new_torrent.hash, new_torrent_name=new_name) +@pytest.mark.parametrize("rename_func", ["torrents_rename", "torrents.rename"]) +def test_rename(client, new_torrent, new_name, rename_func): + client.func(rename_func)(torrent_hash=new_torrent.hash, new_torrent_name=new_name) check(lambda: new_torrent.info.name.replace("+", " "), new_name) @pytest.mark.skipif_before_api_version("2.4.0") @pytest.mark.parametrize("new_name", ["new name file 2", "new_name_file_2"]) @pytest.mark.parametrize( - "rename_file_func", ["torrents_rename_file", "torrents_renameFile"] + "rename_file_func", + [ + "torrents_rename_file", + "torrents_renameFile", + "torrents.rename_file", + "torrents.renameFile", + ], ) def test_rename_file( client, @@ -487,7 +583,13 @@ def test_rename_file( @pytest.mark.skipif_after_api_version("2.4.0") @pytest.mark.parametrize( - "rename_file_func", ["torrents_rename_file", "torrents_renameFile"] + "rename_file_func", + [ + "torrents_rename_file", + "torrents_renameFile", + "torrents.rename_file", + "torrents.renameFile", + ], ) def test_rename_file_not_implemented( client, @@ -501,7 +603,13 @@ def test_rename_file_not_implemented( @pytest.mark.skipif_before_api_version("2.7") @pytest.mark.parametrize("new_name", ["asdf zxcv", "asdf_zxcv"]) @pytest.mark.parametrize( - "rename_folder_func", ["torrents_rename_folder", "torrents_renameFolder"] + "rename_folder_func", + [ + "torrents_rename_folder", + "torrents_renameFolder", + "torrents.rename_folder", + "torrents.renameFolder", + ], ) def test_rename_folder(client, app_version, new_torrent, new_name, rename_folder_func): if v(app_version) >= v("v4.3.3"): @@ -538,7 +646,13 @@ def test_rename_folder(client, app_version, new_torrent, new_name, rename_folder @pytest.mark.skipif_after_api_version("2.7") @pytest.mark.parametrize( - "rename_folder_func", ["torrents_rename_folder", "torrents_renameFolder"] + "rename_folder_func", + [ + "torrents_rename_folder", + "torrents_renameFolder", + "torrents.rename_folder", + "torrents.renameFolder", + ], ) def test_rename_folder_not_implemented(client, rename_folder_func): with pytest.raises(NotImplementedError): @@ -546,14 +660,16 @@ def test_rename_folder_not_implemented(client, rename_folder_func): @pytest.mark.skipif_before_api_version("2.8.14") -def test_export(client, orig_torrent): - assert isinstance(client.torrents_export(torrent_hash=orig_torrent.hash), bytes) +@pytest.mark.parametrize("export_func", ["torrents_export", "torrents.export"]) +def test_export(client, orig_torrent, export_func): + assert isinstance(client.func(export_func)(torrent_hash=orig_torrent.hash), bytes) @pytest.mark.skipif_after_api_version("2.8.14") -def test_export_not_implemented(client): +@pytest.mark.parametrize("export_func", ["torrents_export", "torrents.export"]) +def test_export_not_implemented(client, export_func): with pytest.raises(NotImplementedError): - client.torrents_export() + client.func(export_func)() @pytest.mark.parametrize("info_func", ["torrents_info", "torrents.info"]) diff --git a/tests/test_transfer.py b/tests/test_transfer.py index 6dcdd3592..97e11406a 100644 --- a/tests/test_transfer.py +++ b/tests/test_transfer.py @@ -1,8 +1,20 @@ +import sys + import pytest +from qbittorrentapi import APINames from qbittorrentapi.transfer import TransferInfoDictionary +@pytest.mark.skipif(sys.version_info < (3, 9), reason="removeprefix not in 3.8") +def test_methods(client): + namespace = APINames.Transfer + all_dotted_methods = set(dir(getattr(client, namespace))) + + for meth in [meth for meth in dir(client) if meth.startswith(f"{namespace}_")]: + assert meth.removeprefix(f"{namespace}_") in all_dotted_methods + + def test_info(client): info = client.transfer_info() assert isinstance(info, TransferInfoDictionary) diff --git a/tox.ini b/tox.ini index 1003f6431..ff784ebf3 100644 --- a/tox.ini +++ b/tox.ini @@ -21,7 +21,8 @@ passenv = CI # needed for test_shutdown() commands_pre = !ci: -docker stop qbt-tox-testing commands = !ci: docker run {env:DOCKER_ARGS} {env:DOCKER_QBT_IMAGE_NAME}:{env:DOCKER_QBT_IMAGE_TAG} - python -Xdev -m coverage run -m pytest {posargs:-vv --color=yes} + !ci: python -Xdev -m pytest {posargs:-vv --color=yes} + ci: python -Xdev -m coverage run -m pytest {posargs:-vv --color=yes} commands_post = !ci: docker stop qbt-tox-testing [docs]