diff --git a/monitorrent/plugin_managers.py b/monitorrent/plugin_managers.py index 4ef2a6ec..7909348e 100644 --- a/monitorrent/plugin_managers.py +++ b/monitorrent/plugin_managers.py @@ -212,6 +212,20 @@ def find_torrent(self, torrent_hash): result = self.default_client.find_torrent(torrent_hash) return result or False + def get_download_status(self, client_name): + client = self.get_client(client_name) + result = client.get_download_status() + return result or False + + def get_download_status_by_id(self, torrent_id): + """ + + :type torrent_id: str + """ + if self.default_client is None: + return False + return self.default_client.get_download_status_by_hash(torrent_id) + def add_torrent(self, torrent, topic_settings): """ :type torrent: str diff --git a/monitorrent/plugins/clients/__init__.py b/monitorrent/plugins/clients/__init__.py index 1460e513..8131417b 100644 --- a/monitorrent/plugins/clients/__init__.py +++ b/monitorrent/plugins/clients/__init__.py @@ -1,6 +1,43 @@ +from enum import Enum + from monitorrent.plugins.trackers import Topic +class TorrentDownloadStatus(Enum): + Queued = 1, + Preparing = 2, + Checking = 3, + Downloading = 5, + Verifying = 6, + Finished = 7, + Seeding = 8, + Paused = 9, + Error = 10, + Unknown = 20 + + +class DownloadStatus(dict): + def __init__(self, downloaded_bytes, total_bytes, download_speed, upload_speed, torrent_status, progress, ratio): + """ + :type downloaded_bytes: int + :type total_bytes: int + :type download_speed: int + :type upload_speed: int + :type torrent_status: TorrentDownloadStatus + :type progress: float + :type ratio: float + """ + + super(DownloadStatus, self).__init__() + self.downloaded_bytes = downloaded_bytes + self.total_bytes = total_bytes + self.download_speed = download_speed + self.upload_speed = upload_speed + self.torrent_status = torrent_status + self.progress = progress + self.ratio = ratio + + class TopicSettings(object): download_dir = None diff --git a/monitorrent/plugins/clients/deluge.py b/monitorrent/plugins/clients/deluge.py index a2224fac..f59697c8 100644 --- a/monitorrent/plugins/clients/deluge.py +++ b/monitorrent/plugins/clients/deluge.py @@ -1,4 +1,3 @@ -import six import base64 import structlog @@ -9,6 +8,8 @@ from monitorrent.plugin_managers import register_plugin from datetime import datetime +from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus + log = structlog.get_logger() @@ -22,6 +23,23 @@ class DelugeCredentials(Base): password = Column(String, nullable=True) +status_mapping = { + "Queued": TorrentDownloadStatus.Queued, + "Downloading": TorrentDownloadStatus.Downloading, + "Seeding": TorrentDownloadStatus.Seeding, + "Paused": TorrentDownloadStatus.Paused, + "Checking": TorrentDownloadStatus.Checking, + "Error": TorrentDownloadStatus.Error, +} + + +def get_status(status, is_paused): + if is_paused: + return TorrentDownloadStatus.Paused + else: + return status_mapping[status] + + class DelugeClientPlugin(object): name = "deluge" form = [{ @@ -139,5 +157,37 @@ def remove_torrent(self, torrent_hash): return client.call("core.remove_torrent", torrent_hash.lower(), False) + def get_download_status(self): + client = self._get_client() + client.connect() + result = client.call("core.get_torrents_status", + {}, []) + statuses = {} + for key, value in result.items(): + statuses[key.decode("utf-8")] = DownloadStatus(value[b'total_done'], value[b'total_size'], + value[b'download_payload_rate'], + value[b'upload_payload_rate'], + get_status(value[b'state'].decode("utf-8"), + value[b'paused']), + value[b'progress'], value[b'ratio']) + return statuses + + def get_download_status_by_hash(self, torrent_hash): + client = self._get_client() + lower_hash = torrent_hash.lower() + if not client: + return False + client.connect() + result = client.call("core.get_torrents_status", + {'hash': lower_hash}, ['total_done', 'total_size', 'download_payload_rate', + 'upload_payload_rate', 'state', 'progress']) + value = result.popitem()[1] + return DownloadStatus(value[b'total_done'], value[b'total_size'], + value[b'download_payload_rate'], + value[b'upload_payload_rate'], + get_status(value[b'state'].decode("utf-8"), + value[b'paused']), + value[b'progress'], value[b'ratio']) + register_plugin('client', 'deluge', DelugeClientPlugin()) diff --git a/monitorrent/plugins/clients/downloader.py b/monitorrent/plugins/clients/downloader.py index a0483dbf..c2ce7fc3 100644 --- a/monitorrent/plugins/clients/downloader.py +++ b/monitorrent/plugins/clients/downloader.py @@ -5,6 +5,7 @@ from sqlalchemy import Column, Integer, String from monitorrent.db import Base, DBSession from monitorrent.plugin_managers import register_plugin +from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus from monitorrent.utils.bittorrent_ex import Torrent import base64 @@ -108,4 +109,10 @@ def remove_torrent(self, torrent_hash): except OSError: return False + def get_download_status(self): + return {"": DownloadStatus(0, 0, 0, 0, TorrentDownloadStatus.Unknown, 0, 0)} + + def get_download_status_by_hash(self, torrent_hash): + return DownloadStatus(0, 0, 0, 0, TorrentDownloadStatus.Unknown, 0, 0) + register_plugin('client', DownloaderPlugin.name, DownloaderPlugin()) diff --git a/monitorrent/plugins/clients/qbittorrent.py b/monitorrent/plugins/clients/qbittorrent.py index e94aed2b..6a006ea8 100644 --- a/monitorrent/plugins/clients/qbittorrent.py +++ b/monitorrent/plugins/clients/qbittorrent.py @@ -17,6 +17,27 @@ from datetime import datetime import dateutil.parser +from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus + +status_mapping = { + "queuedUP": TorrentDownloadStatus.Queued, + "queuedDL": TorrentDownloadStatus.Queued, + "stalledDL": TorrentDownloadStatus.Downloading, + "metaDL": TorrentDownloadStatus.Downloading, + "downloading": TorrentDownloadStatus.Downloading, + "stalledUP": TorrentDownloadStatus.Seeding, + "uploading": TorrentDownloadStatus.Seeding, + "pausedDL": TorrentDownloadStatus.Paused, + "pausedUP": TorrentDownloadStatus.Paused, + "checkingUP": TorrentDownloadStatus.Checking, + "checkingDL": TorrentDownloadStatus.Checking, + "error": TorrentDownloadStatus.Error, +} + + +def get_status(status): + return status_mapping[status] + class QBittorrentCredentials(Base): __tablename__ = "qbittorrent_credentials" @@ -167,5 +188,28 @@ def remove_torrent(self, torrent_hash): r = parameters['session'].post(parameters['target'] + "command/delete", data=payload) return r.status_code == 200 + def get_download_status(self): + parameters = self._get_params() + response = parameters['session'].get(parameters['target'] + "query/torrents/") + response.raise_for_status() + result = response.json() + torrents = {} + for torrent in result: + torrents[torrent['hash']] = DownloadStatus(torrent['progress'] * torrent['size'], torrent['size'], + torrent['dlspeed'], + torrent['upspeed'], get_status(torrent['state']), + torrent['progress'], torrent['ratio']) + return torrents + + def get_download_status_by_hash(self, torrent_hash): + parameters = self._get_params() + torrent_hash = torrent_hash.lower() + response = parameters['session'].get(parameters['target'] + "query/propertiesGeneral/" + torrent_hash) + response.raise_for_status() + result = response.json() + return DownloadStatus(result['total_downloaded'], result['total_size'], result['dl_speed'], + result['up_speed'], TorrentDownloadStatus.Unknown, + result['total_downloaded'] / result['total_size'] * 100, result['share_ratio']) + register_plugin('client', 'qbittorrent', QBittorrentClientPlugin()) diff --git a/monitorrent/plugins/clients/transmission.py b/monitorrent/plugins/clients/transmission.py index 26fcf8a0..fb2fe071 100644 --- a/monitorrent/plugins/clients/transmission.py +++ b/monitorrent/plugins/clients/transmission.py @@ -6,6 +6,20 @@ from monitorrent.plugin_managers import register_plugin import base64 +from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus + +status_mapping = { + "stopped": TorrentDownloadStatus.Paused, + "check pending": TorrentDownloadStatus.Checking, + "checking": TorrentDownloadStatus.Checking, + "downloading": TorrentDownloadStatus.Downloading, + "seeding": TorrentDownloadStatus.Seeding +} + + +def get_status(status): + return status_mapping[status] + class TransmissionCredentials(Base): __tablename__ = "transmission_credentials" @@ -115,4 +129,22 @@ def remove_torrent(self, torrent_hash): client.remove_torrent(torrent_hash.lower(), delete_data=False) return True + def get_download_status(self): + client = self.check_connection() + torrents = client.get_torrents(None, []) + result = {} + for torrent in torrents: + result[torrent.hashString] = DownloadStatus(torrent.downloadedEver, torrent.totalSize, + torrent.rateDownload, + torrent.rateUpload, + get_status(torrent.status), torrent.progress, torrent.ratio) + return result + + def get_download_status_by_hash(self, torrent_hash): + client = self.check_connection() + torrent = client.get_torrent(torrent_hash.lower(), []) + return DownloadStatus(torrent.downloadedEver, torrent.totalSize, torrent.rateDownload, torrent.rateUpload, + get_status(torrent.status), torrent.progress, torrent.ratio) + + register_plugin('client', 'transmission', TransmissionClientPlugin()) diff --git a/monitorrent/plugins/clients/utorrent.py b/monitorrent/plugins/clients/utorrent.py index 9682dd99..421cd4c1 100644 --- a/monitorrent/plugins/clients/utorrent.py +++ b/monitorrent/plugins/clients/utorrent.py @@ -5,15 +5,45 @@ import json +from aenum import IntFlag + import requests from io import BytesIO from sqlalchemy import Column, Integer, String from monitorrent.db import Base, DBSession from monitorrent.plugin_managers import register_plugin +from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus from monitorrent.utils.soup import get_soup +class StatusFlags(IntFlag): + Started = 1, + Checking = 2, + StartAfterCheck = 4, + Checked = 8, + Error = 16, + Paused = 32, + Queued = 64, + Loaded = 128 + + +def get_status(status): + if StatusFlags.Error in status: + return TorrentDownloadStatus.Error + elif StatusFlags.Paused in status: + return TorrentDownloadStatus.Paused + elif StatusFlags.Started in status: + return TorrentDownloadStatus.Downloading + elif StatusFlags.Checking in status: + return TorrentDownloadStatus.Checking + elif StatusFlags.Queued in status: + return TorrentDownloadStatus.Queued + elif StatusFlags.Checked in status: + return TorrentDownloadStatus.Paused + + + class UTorrentCredentials(Base): __tablename__ = "utorrent_credentials" @@ -137,4 +167,30 @@ def remove_torrent(self, torrent_hash): parameters['session'].get(parameters['target'], params=payload) return True + def get_download_status(self): + parameters = self._get_params() + payload = {"list": '1', "token": parameters["token"]} + torrents = parameters['session'].get(parameters['target'], + params=payload) + array = json.loads(torrents.text)['torrents'] + result = {} + for torrent in array: + result[torrent[0]] = DownloadStatus(torrent[5], torrent[3], torrent[9], torrent[8], + get_status(StatusFlags(torrent[1])), + float(torrent[4]) / 10.0, torrent[7]) + return result + + def get_download_status_by_hash(self, torrent_hash): + parameters = self._get_params() + + payload = {"list": '1', "token": parameters["token"]} + torrents = parameters['session'].get(parameters['target'], + params=payload) + array = json.loads(torrents.text)['torrents'] + torrent = next(torrent for torrent in array if torrent[0] == torrent_hash) + if torrent: + return DownloadStatus(torrent[5], torrent[3], torrent[9], torrent[8], get_status(StatusFlags(torrent[1])), + float(torrent[4]) / 10.0, torrent[7]) + + register_plugin('client', 'utorrent', UTorrentClientPlugin()) diff --git a/monitorrent/rest/clients.py b/monitorrent/rest/clients.py index 6d7f2f8a..e7944b1e 100644 --- a/monitorrent/rest/clients.py +++ b/monitorrent/rest/clients.py @@ -1,3 +1,4 @@ +import json from builtins import str from builtins import object import falcon @@ -56,7 +57,21 @@ def on_put(self, req, resp, client): resp.status = falcon.HTTP_NO_CONTENT -# noinspection PyUnusedLocal +class TorrentStatus(object): + def __init__(self, clients_manager): + """ + :type clients_manager: ClientsManager + """ + self.clients_manager = clients_manager + + def on_get(self, req, resp, torrent_hash): + """ + + :type torrent_hash: str + """ + resp.json = self.clients_manager.get_download_status_by_id(torrent_hash).__dict__ # noinspection PyUnusedLocal + + class ClientCheck(object): def __init__(self, clients_manager): """ @@ -125,4 +140,30 @@ def on_put(self, req, resp, client): except KeyError as e: log.error("Client could not be found", client=client, exception=str(e)) raise falcon.HTTPNotFound(title='Client plugin \'{0}\' not found'.format(client), description=str(e)) + except Exception as e: + log.error("An error has occurred", exception=str(e)) + raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) resp.status = falcon.HTTP_NO_CONTENT + + +class ClientStatus(object): + def __init__(self, clients_manager): + """ + :type clients_manager: ClientsManager + """ + self.clients_manager = clients_manager + + def on_get(self, req, resp, client): + try: + result = self.clients_manager.get_download_status(client) + result_dict = {} + for key, value in result.items(): + result_dict[key] = value.__dict__ + resp.json = result_dict + except KeyError as e: + log.error("Client could not be found", client=client, exception=str(e)) + raise falcon.HTTPNotFound(title='Client plugin \'{0}\' not found'.format(client), description=str(e)) + except Exception as e: + log.error("An error has occurred", exception=str(e)) + raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) + resp.status = falcon.HTTP_OK diff --git a/monitorrent/rest/new_version.py b/monitorrent/rest/new_version.py index d9caa712..70702593 100644 --- a/monitorrent/rest/new_version.py +++ b/monitorrent/rest/new_version.py @@ -1,5 +1,10 @@ +import falcon +import structlog + from monitorrent.new_version_checker import NewVersionChecker +log = structlog.get_logger() + # noinspection PyUnusedLocal class NewVersion(object): diff --git a/monitorrent/rest/notifiers.py b/monitorrent/rest/notifiers.py index 59385c87..7dc6e3bd 100644 --- a/monitorrent/rest/notifiers.py +++ b/monitorrent/rest/notifiers.py @@ -5,6 +5,7 @@ from builtins import object import structlog + from monitorrent.plugin_managers import NotifierManager log = structlog.get_logger() diff --git a/requirements.txt b/requirements.txt index afd7b4e6..84de59ab 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,5 +18,6 @@ structlog #python 2/3 compatibility and backports six -enum34>=1.0.4 +enum34 +aenum future diff --git a/server.py b/server.py index 4b52df8a..4990d707 100644 --- a/server.py +++ b/server.py @@ -25,7 +25,8 @@ from monitorrent.rest.login import Login, Logout from monitorrent.rest.topics import TopicCollection, TopicParse, Topic, TopicResetStatus, TopicPauseState from monitorrent.rest.trackers import TrackerCollection, Tracker, TrackerCheck -from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault +from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault, \ + TorrentStatus, ClientStatus from monitorrent.rest.settings_authentication import SettingsAuthentication from monitorrent.rest.settings_password import SettingsPassword from monitorrent.rest.settings_execute import SettingsExecute @@ -91,6 +92,8 @@ def create_app(secret_key, token, tracker_manager, clients_manager, notifier_man app.add_route('/api/clients/{client}', Client(clients_manager)) app.add_route('/api/clients/{client}/check', ClientCheck(clients_manager)) app.add_route('/api/clients/{client}/default', ClientDefault(clients_manager)) + app.add_route('/api/clients/{client}/status', ClientStatus(clients_manager)) + app.add_route('/api/clients/torrent/status/{torrent_hash}', TorrentStatus(clients_manager)) app.add_route('/api/notifiers', NotifierCollection(notifier_manager)) app.add_route('/api/notifiers/{notifier}', Notifier(notifier_manager)) app.add_route('/api/notifiers/{notifier}/check', NotifierCheck(notifier_manager)) diff --git a/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_not_found b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_not_found new file mode 100644 index 00000000..2fc8e205 --- /dev/null +++ b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_not_found @@ -0,0 +1,54 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Basic YWRtaW46cGFzc3dvcmQ=] + Connection: [keep-alive] + User-Agent: [python-requests/2.13.0] + method: GET + uri: http://localhost:8080/gui/token.html + response: + body: {string: '
iCEj-xbAgTOaaeiKOsvzyoHIcwP93kS_MHyHDPsjmjJSDW0tbGVNbd77zFgAAAAA
'} + headers: + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['121'] + Content-Type: [text/html] + Set-Cookie: [GUID=ueUrdYTfO6krxw1B8KYu; path=/] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Basic YWRtaW46cGFzc3dvcmQ=] + Connection: [keep-alive] + Cookie: [GUID=ueUrdYTfO6krxw1B8KYu] + User-Agent: [python-requests/2.13.0] + method: GET + uri: http://localhost:8080/gui/?list=1&token=iCEj-xbAgTOaaeiKOsvzyoHIcwP93kS_MHyHDPsjmjJSDW0tbGVNbd77zFgAAAAA + response: + body: {string: '{"build":43388,"torrents": [ + + + ["3F6D30E6E4C65684C4ADF8BE45F1F8D71CC4E2AE",201,"Sherlock.S04E01.The.Six.Thatchers.1080i.HDTV.Rus.HDCLUB.ts",5619792444,222,1250541568,0,0,6277,3106179,1005,"",0,218,36,62,2373454,1,4369250876,"","","Downloading + 22.2 %","1",1489828545,0,"","C:\\Users\\DSilence\\Downloads",0,"75BD3F66"]], + + "label": [],"torrentc": "1089974307" + + ,"rssfeeds": [] + + ,"rssfilters": [] + + } + + '} + headers: + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['392'] + Content-Type: [text/plain] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_success b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_success new file mode 100644 index 00000000..e1b046a7 --- /dev/null +++ b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_success @@ -0,0 +1,54 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Basic YWRtaW46cGFzc3dvcmQ=] + Connection: [keep-alive] + User-Agent: [python-requests/2.13.0] + method: GET + uri: http://localhost:8080/gui/token.html + response: + body: {string: '
3N-t0vKNCuF_9FhJ4qcCCcumfFp-0Ymx1m1K-X3zF2jEhR8RvBPDCN_7zFgAAAAA
'} + headers: + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['121'] + Content-Type: [text/html] + Set-Cookie: [GUID=5NB1vxis1FNc70BFwRXi; path=/] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Basic YWRtaW46cGFzc3dvcmQ=] + Connection: [keep-alive] + Cookie: [GUID=5NB1vxis1FNc70BFwRXi] + User-Agent: [python-requests/2.13.0] + method: GET + uri: http://localhost:8080/gui/?list=1&token=3N-t0vKNCuF_9FhJ4qcCCcumfFp-0Ymx1m1K-X3zF2jEhR8RvBPDCN_7zFgAAAAA + response: + body: {string: '{"build":43388,"torrents": [ + + + ["3F6D30E6E4C65684C4ADF8BE45F1F8D71CC4E2AE",201,"Sherlock.S04E01.The.Six.Thatchers.1080i.HDTV.Rus.HDCLUB.ts",5619792444,222,1251196928,0,0,6277,3106179,1005,"",0,218,36,62,2373454,1,4368595516,"","","Downloading + 22.2 %","1",1489828545,0,"","C:\\Users\\Test\\Downloads",0,"75BD3F66"]], + + "label": [],"torrentc": "675099747" + + ,"rssfeeds": [] + + ,"rssfilters": [] + + } + + '} + headers: + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['391'] + Content-Type: [text/plain] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_not_found b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_not_found new file mode 100644 index 00000000..dc8b787c --- /dev/null +++ b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_not_found @@ -0,0 +1,40 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Basic YWRtaW46cGFzc3dvcmQ=] + Connection: [keep-alive] + User-Agent: [python-requests/2.13.0] + method: GET + uri: http://localhost:8080/gui/token.html + response: + body: {string: '
iCEj-xbAgTOaaeiKOsvzyoHIcwP93kS_MHyHDPsjmjJSDW0tbGVNbd77zFgAAAAA
'} + headers: + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['121'] + Content-Type: [text/html] + Set-Cookie: [GUID=ueUrdYTfO6krxw1B8KYu; path=/] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Basic YWRtaW46cGFzc3dvcmQ=] + Connection: [keep-alive] + Cookie: [GUID=ueUrdYTfO6krxw1B8KYu] + User-Agent: [python-requests/2.13.0] + method: GET + uri: http://localhost:8080/gui/?list=1&token=iCEj-xbAgTOaaeiKOsvzyoHIcwP93kS_MHyHDPsjmjJSDW0tbGVNbd77zFgAAAAA + response: + body: {string: ''} + headers: + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['392'] + Content-Type: [text/plain] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_success b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_success new file mode 100644 index 00000000..e1b046a7 --- /dev/null +++ b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_success @@ -0,0 +1,54 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Basic YWRtaW46cGFzc3dvcmQ=] + Connection: [keep-alive] + User-Agent: [python-requests/2.13.0] + method: GET + uri: http://localhost:8080/gui/token.html + response: + body: {string: '
3N-t0vKNCuF_9FhJ4qcCCcumfFp-0Ymx1m1K-X3zF2jEhR8RvBPDCN_7zFgAAAAA
'} + headers: + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['121'] + Content-Type: [text/html] + Set-Cookie: [GUID=5NB1vxis1FNc70BFwRXi; path=/] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Basic YWRtaW46cGFzc3dvcmQ=] + Connection: [keep-alive] + Cookie: [GUID=5NB1vxis1FNc70BFwRXi] + User-Agent: [python-requests/2.13.0] + method: GET + uri: http://localhost:8080/gui/?list=1&token=3N-t0vKNCuF_9FhJ4qcCCcumfFp-0Ymx1m1K-X3zF2jEhR8RvBPDCN_7zFgAAAAA + response: + body: {string: '{"build":43388,"torrents": [ + + + ["3F6D30E6E4C65684C4ADF8BE45F1F8D71CC4E2AE",201,"Sherlock.S04E01.The.Six.Thatchers.1080i.HDTV.Rus.HDCLUB.ts",5619792444,222,1251196928,0,0,6277,3106179,1005,"",0,218,36,62,2373454,1,4368595516,"","","Downloading + 22.2 %","1",1489828545,0,"","C:\\Users\\Test\\Downloads",0,"75BD3F66"]], + + "label": [],"torrentc": "675099747" + + ,"rssfeeds": [] + + ,"rssfilters": [] + + } + + '} + headers: + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['391'] + Content-Type: [text/plain] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/plugins/clients/test_deluge_plugin.py b/tests/plugins/clients/test_deluge_plugin.py index e154a7da..d3921e7c 100644 --- a/tests/plugins/clients/test_deluge_plugin.py +++ b/tests/plugins/clients/test_deluge_plugin.py @@ -277,3 +277,115 @@ def test_get_download_dir_exception(self, deluge_client): plugin.get_download_dir() rpc_client.call.assert_called_once_with('core.get_config_value', 'move_completed_path') + + @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient') + def test_should_get_torrent_status_by_hash_successfully(self, deluge_client): + torrent_hash = 'SomeRandomHashMockString' + rpc_client = deluge_client.return_value + rpc_client.connected = True + rpc_client.call.return_value = {str.encode(torrent_hash.lower()): {b'total_done': 30, + b'total_size': 100, + b'download_payload_rate': 50000.0, + b'upload_payload_rate': 0.0, + b'state': b'downloading', + b'progress': 23, + b'ratio': 1, + b'paused': True}} + + plugin = DelugeClientPlugin() + settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} + plugin.set_settings(settings) + + result = plugin.get_download_status_by_hash(torrent_hash) + assert result.download_speed == 50000.0 + assert result.upload_speed == 0.0 + assert result.total_bytes == 100 + assert result.downloaded_bytes == 30 + + @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient') + def test_should_fail_when_no_settings_on_get_torrent_status_by_hash(self, deluge_client): + rpc_client = deluge_client.return_value + rpc_client.connected = True + rpc_client.call.side_effect = Exception + + torrent_hash = 'SomeRandomHashMockString' + + plugin = DelugeClientPlugin() + + assert plugin.get_download_status_by_hash(torrent_hash) is False + + rpc_client.call.assert_not_called() + + @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient') + def test_should_fail_get_torrent_status_by_hash_on_error(self, deluge_client): + rpc_client = deluge_client.return_value + rpc_client.connected = True + rpc_client.call.side_effect = Exception + + torrent_hash = 'SomeRandomHashMockString' + + plugin = DelugeClientPlugin() + settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} + plugin.set_settings(settings) + + with pytest.raises(Exception) as e: + plugin.get_download_status_by_hash(torrent_hash) + + rpc_client.call.assert_called_once_with("core.get_torrents_status", + {'hash': torrent_hash.lower()}, + ['total_done', 'total_size', 'download_payload_rate', + 'upload_payload_rate', 'state', 'progress']) + + @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient') + def test_should_get_torrent_statuses_successfully(self, deluge_client): + torrent_hash = 'SomeRandomHashMockString' + rpc_client = deluge_client.return_value + rpc_client.connected = True + rpc_client.call.return_value = {str.encode(torrent_hash.lower()): {b'total_done': 30, + b'total_size': 100, + b'download_payload_rate': 50000.0, + b'upload_payload_rate': 0.0, + b'state': b'Downloading', + b'progress': 23, + b'ratio': 1, + b'paused': False + }} + + plugin = DelugeClientPlugin() + settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} + plugin.set_settings(settings) + + result = plugin.get_download_status().popitem()[1] + assert result.download_speed == 50000.0 + assert result.upload_speed == 0.0 + assert result.total_bytes == 100 + assert result.downloaded_bytes == 30 + + @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient') + def test_should_fail_when_no_settings_on_get_torrent_statuses(self, deluge_client): + rpc_client = deluge_client.return_value + rpc_client.connected = True + rpc_client.call.side_effect = Exception + + plugin = DelugeClientPlugin() + + with pytest.raises(Exception): + plugin.get_download_status() + + rpc_client.call.assert_not_called() + + @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient') + def test_should_fail_get_torrent_status_on_error(self, deluge_client): + rpc_client = deluge_client.return_value + rpc_client.connected = True + rpc_client.call.side_effect = Exception + + plugin = DelugeClientPlugin() + settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} + plugin.set_settings(settings) + + with pytest.raises(Exception) as e: + plugin.get_download_status() + + rpc_client.call.assert_called_once_with("core.get_torrents_status", + {}, []) diff --git a/tests/plugins/clients/test_downloader.py b/tests/plugins/clients/test_downloader.py index 85624935..7005fce4 100644 --- a/tests/plugins/clients/test_downloader.py +++ b/tests/plugins/clients/test_downloader.py @@ -70,7 +70,7 @@ def test_find_torrent(self): find_torrent = plugin.find_torrent(torrent.info_hash) self.assertNotEqual(False, find_torrent) - date_added = datetime.fromtimestamp(os.path.getctime(downloaded_filepath))\ + date_added = datetime.fromtimestamp(os.path.getctime(downloaded_filepath)) \ .replace(tzinfo=reference.LocalTimezone()).astimezone(utc) expected = {'name': torrent_filename, 'date_added': date_added} self.assertEqual(expected, find_torrent) @@ -197,3 +197,19 @@ def test_remove_torrent_failed(self): remove.side_effect = OSError self.assertFalse(plugin.remove_torrent(torrent.info_hash)) self.assertTrue(os.path.exists(downloaded_filepath)) + + def test_download_status_by_hash(self): + plugin = DownloaderPlugin() + status = plugin.get_download_status_by_hash("torrent") + assert status.upload_speed == 0 + assert status.download_speed == 0 + assert status.total_bytes == 0 + assert status.downloaded_bytes == 0 + + def test_download_status(self): + plugin = DownloaderPlugin() + status = plugin.get_download_status().popitem()[1] + assert status.upload_speed == 0 + assert status.download_speed == 0 + assert status.total_bytes == 0 + assert status.downloaded_bytes == 0 diff --git a/tests/plugins/clients/test_qbittorrent.py b/tests/plugins/clients/test_qbittorrent.py index d2c64fcd..7e622328 100644 --- a/tests/plugins/clients/test_qbittorrent.py +++ b/tests/plugins/clients/test_qbittorrent.py @@ -247,3 +247,151 @@ def test_get_download_dir_error(self, mocker): with pytest.raises(Exception) as e: plugin.get_download_dir() + + @Mocker() + def test_should_get_download_status_by_hash_successfully(self, mocker): + target = "{0}:{1}/".format(self.real_host, self.real_port) + + mocker.post(target + "login", text="Ok.") + + hash_string = 'hash' + + mocker.get(target + "query/propertiesGeneral/" + hash_string, + status_code=200, + text="{\"addition_date\":1487792728,\"comment\":\"\",\"completion_date\":-1,\"created_by\":\"\"," + "\"creation_date\":1487792646,\"dl_limit\":-1,\"dl_speed\":5990252,\"dl_speed_avg\":5851317," + "\"eta\":1025,\"last_seen\":1487792930,\"nb_connections\":16,\"nb_connections_limit\":100," + "\"peers\":2,\"peers_total\":15,\"piece_size\":8388608,\"pieces_have\":136," + "\"pieces_num\":871,\"reannounce\":2849," + "\"save_path\":\"C:\\\\Users\\\\DSilence\\\\Downloads\\\\test_dir\\\\\",\"seeding_time\":0," + "\"seeds\":14,\"seeds_total\":16,\"share_ratio\":0.015455593117895568,\"time_elapsed\":202," + "\"total_downloaded\":1187817372,\"total_downloaded_session\":1187817372," + "\"total_size\":7299496252,\"total_uploaded\":18358422,\"total_uploaded_session\":18358422," + "\"total_wasted\":110,\"up_limit\":-1,\"up_speed\":129319,\"up_speed_avg\":90435}") + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + plugin = QBittorrentClientPlugin() + plugin.set_settings(settings) + + result = plugin.get_download_status_by_hash(hash_string) + assert result.upload_speed == 129319 + assert result.download_speed == 5990252 + assert result.downloaded_bytes == 1187817372 + assert result.total_bytes == 7299496252 + + @Mocker() + def test_should_fail_download_status_by_hash_when_not_logged_in(self, mocker): + target = "{0}:{1}/".format(self.real_host, self.real_port) + + mocker.post(target + "login", text="Fails.") + + hash_string = 'hash' + + mocker.get(target + "query/propertiesGeneral/" + hash_string, + status_code=200, + text="{\"addition_date\":1487792728,\"comment\":\"\",\"completion_date\":-1,\"created_by\":\"\"," + "\"creation_date\":1487792646,\"dl_limit\":-1,\"dl_speed\":5990252,\"dl_speed_avg\":5851317," + "\"eta\":1025,\"last_seen\":1487792930,\"nb_connections\":16,\"nb_connections_limit\":100," + "\"peers\":2,\"peers_total\":15,\"piece_size\":8388608,\"pieces_have\":136," + "\"pieces_num\":871,\"reannounce\":2849," + "\"save_path\":\"C:\\\\Users\\\\sad\\\\tmp\\\\test_dir\\\\\",\"seeding_time\":0," + "\"seeds\":14,\"seeds_total\":16,\"share_ratio\":0.015455593117895568,\"time_elapsed\":202," + "\"total_downloaded\":1187817372,\"total_downloaded_session\":1187817372," + "\"total_size\":7299496252,\"total_uploaded\":18358422,\"total_uploaded_session\":18358422," + "\"total_wasted\":110,\"up_limit\":-1,\"up_speed\":129319,\"up_speed_avg\":90435}") + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + plugin = QBittorrentClientPlugin() + plugin.set_settings(settings) + + with pytest.raises(Exception): + plugin.get_download_status_by_hash(hash_string) + + @Mocker() + def test_should_fail_when_download_status_by_hash_wasnt_retrieved(self, mocker): + target = "{0}:{1}/".format(self.real_host, self.real_port) + + mocker.post(target + "login", text="Ok.") + + error = {'error': 500} + hash_string = 'hash' + mocker.get(target + "query/propertiesGeneral/" + hash_string, status_code=500, text=json.dumps(error)) + + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + + plugin = QBittorrentClientPlugin() + plugin.set_settings(settings) + + with pytest.raises(Exception) as e: + plugin.get_download_status_by_hash(hash_string) + + @Mocker() + def test_should_get_download_status_successfully(self, mocker): + target = "{0}:{1}/".format(self.real_host, self.real_port) + + mocker.post(target + "login", text="Ok.") + + mocker.get(target + "query/torrents/", + status_code = 200, + text='[{"added_on":1489778322,"category":"","completion_on":4294967295,"dlspeed":5242223,' + '"eta":8640000,"f_l_piece_prio":false,"force_start":false,' + '"hash":"1bc6e893df6ee2d5b84aa41bb5cf4e189112a8a7",' + '"name":"en_windows_10_pro_10240_x64_dvd","num_complete":28,"num_incomplete":2,' + '"num_leechs":0,"num_seeds":0,"priority":1,"progress":0.20,"ratio":0,' + '"save_path":"C:\\\\Users\\\\DSilence\\\\Downloads\\\\","seq_dl":false,"size":3992293376,' + '"state":"pausedDL","super_seeding":false,"upspeed":0}]') + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + plugin = QBittorrentClientPlugin() + plugin.set_settings(settings) + + expected_key = '1bc6e893df6ee2d5b84aa41bb5cf4e189112a8a7' + status = plugin.get_download_status() + assert expected_key in status + result = status[expected_key] + assert result.upload_speed == 0 + assert result.download_speed == 5242223 + assert result.downloaded_bytes == 798458675.2 + assert result.total_bytes == 3992293376 + + @Mocker() + def test_should_fail_download_status_when_not_logged_in(self, mocker): + target = "{0}:{1}/".format(self.real_host, self.real_port) + + mocker.post(target + "login", text="Fails.") + + mocker.get(target + "query/torrents/", + status_code=200, + text='[{"added_on":1489778322,"category":"","completion_on":4294967295,"dlspeed":5242223,' + '"eta":8640000,"f_l_piece_prio":false,"force_start":false,' + '"hash":"1bc6e893df6ee2d5b84aa41bb5cf4e189112a8a7",' + '"name":"en_windows_10_pro_10240_x64_dvd","num_complete":28,"num_incomplete":2,' + '"num_leechs":0,"num_seeds":0,"priority":1,"progress":0.20,"ratio":0,' + '"save_path":"C:\\\\Users\\\\DSilence\\\\Downloads\\\\","seq_dl":false,"size":3992293376,' + '"state":"pausedDL","super_seeding":false,"upspeed":0}]') + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + plugin = QBittorrentClientPlugin() + plugin.set_settings(settings) + + with pytest.raises(Exception): + plugin.get_download_status() + + @Mocker() + def test_should_fail_when_download_status_wasnt_retrieved(self, mocker): + target = "{0}:{1}/".format(self.real_host, self.real_port) + + mocker.post(target + "login", text="Ok.") + + error = {'error': 500} + mocker.get(target + "query/torrents/", status_code=500, text=json.dumps(error)) + + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + + plugin = QBittorrentClientPlugin() + plugin.set_settings(settings) + + with pytest.raises(Exception) as e: + plugin.get_download_status() diff --git a/tests/plugins/clients/test_transmission_plugin.py b/tests/plugins/clients/test_transmission_plugin.py index e78e490a..a35233ac 100644 --- a/tests/plugins/clients/test_transmission_plugin.py +++ b/tests/plugins/clients/test_transmission_plugin.py @@ -134,7 +134,8 @@ def test_add_torrent_with_settings(self, transmission_client): torrent = b'!torrent.content' self.assertTrue(plugin.add_torrent(torrent, TopicSettings('/path/to/download/dir'))) - rpc_client.add_torrent.assert_called_once_with(base64.b64encode(torrent).decode('utf-8'), download_dir='/path/to/download/dir') + rpc_client.add_torrent.assert_called_once_with(base64.b64encode(torrent).decode('utf-8'), + download_dir='/path/to/download/dir') @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') def test_add_torrent_without_credentials(self, transmission_client): @@ -233,3 +234,90 @@ def test_get_download_dir_exception(self, transmission_client): rpc_client.get_session.assert_called_once() + @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') + def test_should_get_status_by_hash(self, transmission_client): + rpc_client = transmission_client.return_value + rpc_client.get_torrent.return_value = namedtuple('Torrent', + ('rateDownload', 'totalSize', 'downloadedEver', + 'rateUpload', 'status', 'progress', 'ratio') + )(5000, 10000, 6000, 20, 'downloading', 23, 1) + + plugin = TransmissionClientPlugin() + settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} + plugin.set_settings(settings) + + status = plugin.get_download_status_by_hash('4e2597302ad6b4d7a545c8ec02621ac232316b96') + assert status.downloaded_bytes == 6000 + assert status.download_speed == 5000 + assert status.total_bytes == 10000 + assert status.upload_speed == 20 + + @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') + def test_should_fail_when_no_settings_for_get_status_by_hash(self, transmission_client): + rpc_client = transmission_client.return_value + rpc_client.get_torrent.side_effect = transmissionrpc.TransmissionError + + plugin = TransmissionClientPlugin() + with pytest.raises(Exception): + plugin.get_download_status_by_hash('4e2597302ad6b4d7a545c8ec02621ac232316b96') + rpc_client.get_torrent.assert_not_called() + + @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') + def test_should_fail_get_status_by_hash_when_get_torrent_fails(self, transmission_client): + rpc_client = transmission_client.return_value + rpc_client.get_torrent.side_effect = transmissionrpc.TransmissionError + + plugin = TransmissionClientPlugin() + settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} + plugin.set_settings(settings) + + with pytest.raises(transmissionrpc.TransmissionError) as e: + plugin.get_download_status_by_hash('4e2597302ad6b4d7a545c8ec02621ac232316b96') + + rpc_client.get_torrent.assert_called_once() + + @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') + def test_should_get_status(self, transmission_client): + rpc_client = transmission_client.return_value + torrent_id = '4e2597302ad6b4d7a545c8ec02621ac232316b96' + rpc_client.get_torrents.return_value = [namedtuple('Torrent', + ('hashString', 'rateDownload', 'totalSize', 'downloadedEver', + 'rateUpload', 'status', 'progress', 'ratio') + )(torrent_id, 5000, 10000, 6000, 20, 'downloading', 23, 1)] + + plugin = TransmissionClientPlugin() + settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} + plugin.set_settings(settings) + + statuses = plugin.get_download_status() + assert torrent_id in statuses + status = statuses[torrent_id] + assert status.downloaded_bytes == 6000 + assert status.download_speed == 5000 + assert status.total_bytes == 10000 + assert status.upload_speed == 20 + + @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') + def test_should_fail_when_no_settings_for_get_status(self, transmission_client): + rpc_client = transmission_client.return_value + rpc_client.get_torrents.side_effect = transmissionrpc.TransmissionError + + plugin = TransmissionClientPlugin() + + with pytest.raises(Exception): + plugin.get_download_status() + rpc_client.get_torrent.assert_not_called() + + @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') + def test_should_fail_get_status_when_get_torrent_fails(self, transmission_client): + rpc_client = transmission_client.return_value + rpc_client.get_torrents.side_effect = transmissionrpc.TransmissionError + + plugin = TransmissionClientPlugin() + settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} + plugin.set_settings(settings) + + with pytest.raises(transmissionrpc.TransmissionError) as e: + plugin.get_download_status() + + rpc_client.get_torrents.assert_called_once() diff --git a/tests/plugins/clients/test_utorrent_plugin.py b/tests/plugins/clients/test_utorrent_plugin.py index 588af011..f852ee18 100644 --- a/tests/plugins/clients/test_utorrent_plugin.py +++ b/tests/plugins/clients/test_utorrent_plugin.py @@ -1,9 +1,11 @@ + import pytest from ddt import ddt from mock import patch, Mock, MagicMock from requests import Response -from monitorrent.plugins.clients.utorrent import UTorrentClientPlugin +from monitorrent.plugins.clients import TorrentDownloadStatus +from monitorrent.plugins.clients.utorrent import UTorrentClientPlugin, StatusFlags, get_status from tests import DbTestCase, use_vcr @@ -164,3 +166,81 @@ def test_remove_torrent_success(self, get_mock): torrent = b'torrent' self.assertTrue(plugin.remove_torrent(torrent)) + + @use_vcr + def test_get_download_status_by_hash_success(self): + plugin = UTorrentClientPlugin() + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + plugin.set_settings(settings) + + torrent = "3F6D30E6E4C65684C4ADF8BE45F1F8D71CC4E2AE" + result = plugin.get_download_status_by_hash(torrent) + + assert result.upload_speed == 6277 + assert result.download_speed == 3106179 + assert result.downloaded_bytes == 1251196928 + assert result.total_bytes == 5619792444 + + def test_get_download_status_by_hash_bad_settings(self): + plugin = UTorrentClientPlugin() + torrent = 'torrent' + with pytest.raises(TypeError): + plugin.get_download_status_by_hash(torrent) + + @use_vcr + def test_get_download_status_by_hash_not_found(self): + plugin = UTorrentClientPlugin() + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + plugin.set_settings(settings) + + torrent = "torrent" + with pytest.raises(Exception) as e: + plugin.get_download_status_by_hash(torrent) + + @use_vcr + def test_get_download_status_success(self): + plugin = UTorrentClientPlugin() + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + plugin.set_settings(settings) + + torrent_hash = "3F6D30E6E4C65684C4ADF8BE45F1F8D71CC4E2AE" + result = plugin.get_download_status() + + assert torrent_hash in result + torrent = result[torrent_hash] + assert torrent.upload_speed == 6277 + assert torrent.download_speed == 3106179 + assert torrent.downloaded_bytes == 1251196928 + assert torrent.total_bytes == 5619792444 + + def test_get_download_status_bad_settings(self): + plugin = UTorrentClientPlugin() + with pytest.raises(TypeError): + plugin.get_download_status() + + @use_vcr + def test_get_download_status_not_found(self): + plugin = UTorrentClientPlugin() + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + plugin.set_settings(settings) + + with pytest.raises(Exception) as e: + plugin.get_download_status() + + def test_get_status(self): + status = StatusFlags.Error + assert get_status(status) == TorrentDownloadStatus.Error + status = StatusFlags.Paused + assert get_status(status) == TorrentDownloadStatus.Paused + status = StatusFlags.Checking + assert get_status(status) == TorrentDownloadStatus.Checking + status = StatusFlags.Started + assert get_status(status) == TorrentDownloadStatus.Downloading + status = StatusFlags.Queued + assert get_status(status) == TorrentDownloadStatus.Queued + status = StatusFlags.Checked + assert get_status(status) == TorrentDownloadStatus.Paused diff --git a/tests/rest/test_api_clients.py b/tests/rest/test_api_clients.py index eb82cdc7..d0ea15bc 100644 --- a/tests/rest/test_api_clients.py +++ b/tests/rest/test_api_clients.py @@ -3,8 +3,11 @@ import falcon from mock import MagicMock from ddt import ddt, data + +from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus from tests import RestTestBase -from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault +from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault, TorrentStatus, \ + ClientStatus from monitorrent.plugin_managers import ClientsManager @@ -84,7 +87,7 @@ def test_not_found_settings(self): self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND) self.assertTrue('application/json' in self.srmock.headers_dict['Content-Type']) - def test_get_settings_error(self): + def test_error_get_settings(self): clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()}) clients_manager.get_settings = MagicMock(side_effect=Exception) @@ -118,7 +121,7 @@ def test_not_found_update_settings(self): body=json.dumps({'login': 'login', 'password': 'password'})) self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND) - def test_eroor_update_settings(self): + def test_error_update_settings(self): clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()}) clients_manager.set_settings = MagicMock(side_effect=Exception) @@ -130,6 +133,74 @@ def test_eroor_update_settings(self): self.assertEqual(self.srmock.status, falcon.HTTP_INTERNAL_SERVER_ERROR) +@ddt +class TorrentStatusTest(RestTestBase): + def test_get_download_status_by_hash(self): + clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()}) + clients_manager.get_download_status_by_id = MagicMock(return_value=DownloadStatus(1, 2, 3, 4, + TorrentDownloadStatus.Unknown, + 5, 6)) + + client = TorrentStatus(clients_manager) + client.__no_auth__ = True + self.api.add_route('/api/clients/torrent/status/{torrent_hash}', client) + + body = self.simulate_request('/api/clients/torrent/status/{0}'.format("hash"), decode="utf-8") + self.assertEqual(self.srmock.status, falcon.HTTP_OK) + self.assertTrue('application/json' in self.srmock.headers_dict['Content-Type']) + + result = json.loads(body) + assert result['downloaded_bytes'] == 1 + assert result['total_bytes'] == 2 + assert result['download_speed'] == 3 + assert result['upload_speed'] == 4 + +@ddt +class ClientStatusTest(RestTestBase): + def test_get_download_status(self): + clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()}) + clients_manager.get_download_status = MagicMock(return_value={"torrent": DownloadStatus(1, 2, 3, 4, + TorrentDownloadStatus + .Unknown, 5, 6)}) + + client = ClientStatus(clients_manager) + client.__no_auth__ = True + self.api.add_route('/api/clients/{client}/status', client) + + body = self.simulate_request('/api/clients/{0}/status'.format("client"), decode="utf-8") + self.assertEqual(self.srmock.status, falcon.HTTP_OK) + self.assertTrue('application/json' in self.srmock.headers_dict['Content-Type']) + + result = json.loads(body) + torrent = result["torrent"] + assert torrent['downloaded_bytes'] == 1 + assert torrent['total_bytes'] == 2 + assert torrent['download_speed'] == 3 + assert torrent['upload_speed'] == 4 + + def test_check_client_not_found(self): + clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()}) + clients_manager.get_download_status = MagicMock(side_effect=KeyError) + + client = ClientStatus(clients_manager) + client.__no_auth__ = True + self.api.add_route('/api/clients/{client}/status', client) + + self.simulate_request('/api/clients/{0}/status'.format("client")) + self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND) + + def test_check_client_error(self): + clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()}) + clients_manager.get_download_status = MagicMock(side_effect=Exception) + + client = ClientStatus(clients_manager) + client.__no_auth__ = True + self.api.add_route('/api/clients/{client}/status', client) + + self.simulate_request('/api/clients/{0}/status'.format("client")) + self.assertEqual(self.srmock.status, falcon.HTTP_INTERNAL_SERVER_ERROR) + + @ddt class CheckClientTest(RestTestBase): @data(True, False) @@ -249,3 +320,14 @@ def test_set_default_not_found(self): self.simulate_request('/api/clients/{0}/default'.format('random.org'), method='PUT') self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND) + + def test_set_default_on_error(self): + clients_manager = ClientsManager({'tracker.org': ClientCollectionTest.TestClient()}) + clients_manager.set_default = MagicMock(side_effect=Exception) + + client = ClientDefault(clients_manager) + client.__no_auth__ = True + self.api.add_route('/api/clients/{client}/default', client) + + self.simulate_request('/api/clients/{0}/default'.format('tracker.org'), method='PUT') + self.assertEqual(self.srmock.status, falcon.HTTP_INTERNAL_SERVER_ERROR) \ No newline at end of file diff --git a/tests/rest/test_api_execute.py b/tests/rest/test_api_execute.py index 950a5327..c56d1370 100644 --- a/tests/rest/test_api_execute.py +++ b/tests/rest/test_api_execute.py @@ -99,20 +99,23 @@ def set_result(): self.assertEqual(result, {'is_running': True, 'logs': [{}]}) - def test_execute_logs_failure(self): + def test_wrong_params(self): log_manager = ExecuteLogManager() log_manager.get_current_execute_log_details = Mock(side_effect=Exception) - log_manager.is_running = Mock(return_value=True) - - execute_log_current = ExecuteLogCurrent(log_manager) + log_manager.is_running = Mock(return_value=False) time = TimeMock() with patch("monitorrent.rest.execute.time", time): + + execute_log_current = ExecuteLogCurrent(log_manager) + self.api.add_route(self.test_route, execute_log_current) self.simulate_request(self.test_route, decode='utf-8') + self.assertEqual(self.srmock.status, falcon.HTTP_INTERNAL_SERVER_ERROR) + @ddt class ExecuteCallTest(RestTestBase): def test_execute(self): diff --git a/tests/rest/test_api_notifiers.py b/tests/rest/test_api_notifiers.py index ccdb5009..54a65b13 100644 --- a/tests/rest/test_api_notifiers.py +++ b/tests/rest/test_api_notifiers.py @@ -218,8 +218,7 @@ def test_check_notifier_not_found(self): self.simulate_request('/api/clients/{0}/check'.format('tracker.org')) self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND) - def test_check_notifier_failed(self): - # noinspection PyTypeChecker + def test_check_notifier_exception(self): notifiers_manager = NotifierManager(Mock(), {'test': NotifierCollectionTest.TestNotifier()}) notifiers_manager.send_test_message = Mock(side_effect=Exception) diff --git a/tests/test_plugin_managers_clients_manager.py b/tests/test_plugin_managers_clients_manager.py index b73e6160..0a9d57c5 100644 --- a/tests/test_plugin_managers_clients_manager.py +++ b/tests/test_plugin_managers_clients_manager.py @@ -1,5 +1,7 @@ from ddt import ddt, data from mock import Mock, MagicMock, patch + +from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus from tests import TestCase, DbTestCase from monitorrent.plugin_managers import ClientsManager, DbClientsManager from monitorrent.settings_manager import SettingsManager @@ -173,6 +175,26 @@ def test_remove_torrent_false(self): remove_torrent_mock1.assert_called_once_with(torrent_hash) remove_torrent_mock2.assert_not_called() + def test_get_download_status_by_hash_success(self): + result = DownloadStatus(1, 2, 3, 4, TorrentDownloadStatus.Downloading, 5, 6) + get_download_status_by_hash_1 = MagicMock(return_value=result) + get_download_status_by_hash_2 = MagicMock(return_value=False) + + torrent_hash = 'hash' + self.client1.get_download_status_by_hash = get_download_status_by_hash_1 + self.client2.get_download_status_by_hash = get_download_status_by_hash_2 + actual_result = self.clients_manager.get_download_status_by_id(torrent_hash) + assert actual_result == result + + def test_get_download_status_success(self): + result = {"hash": DownloadStatus(1, 2, 3, 4, TorrentDownloadStatus.Downloading, 5, 6)} + get_download_status = MagicMock(return_value=result) + + self.client1.get_download_status = get_download_status + actual_result = self.clients_manager.get_download_status(self.CLIENT1_NAME) + assert actual_result == result + + def test_get_plugins(self): clients = self.clients_manager.clients get_plugins_mock = Mock(return_value=clients) @@ -196,6 +218,7 @@ def test_empty_clients(self): self.assertFalse(clients_amanger.add_torrent('!torrent', None)) self.assertFalse(clients_amanger.find_torrent('hash')) self.assertFalse(clients_amanger.remove_torrent('hash')) + self.assertFalse(clients_amanger.get_download_status_by_id('hash')) def test_unknow_client(self): with self.assertRaises(KeyError):