diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 383a9be..e2fd9fa 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.3.17 +current_version = 0.4.0 commit = True tag = True diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..cd3bfb3 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,3 @@ +[settings] +multi_line_output=3 +include_trailing_comma=True diff --git a/setup.py b/setup.py index b389dd2..04694c3 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="synotools", - version="0.3.17", + version="0.4.0", author="Ed Garabito", author_email="eduardo@gottabegarabi.com", description="A Python API wrapper and toolset to interact with Synology NAS devices.", diff --git a/synotools/commands/download.py b/synotools/commands/download.py index 9701f20..1c90731 100644 --- a/synotools/commands/download.py +++ b/synotools/commands/download.py @@ -1,14 +1,20 @@ -import sys +import argparse from fabric import Config, Connection +from synotools.commands.vpn_connect import check_and_connect from synotools.common.logging import get_logger from synotools.models.config import DelugeConfig, SynoConfig logger = get_logger(__name__) -def download_torrent_with_deluge(torrent_url): +def download_torrent_with_deluge(torrent_url, skip_vpn=False): + if skip_vpn: + logger.info("Skipping VPN checks...") + else: + check_and_connect() + logger.debug("Creating config files...") syno_config = SynoConfig() deluge_config = DelugeConfig() @@ -38,9 +44,20 @@ def download_torrent_with_deluge(torrent_url): if __name__ == "__main__": torrent_url = None - try: - torrent_url = sys.argv[1] - except IndexError: - pass + parser = argparse.ArgumentParser(description="Download torrent with Deluge.") + parser.add_argument( + "torrent_url", type=str, help="the torrent url or magnet to download" + ) + parser.add_argument( + "--no-vpn", + dest="no_vpn", + action="store_true", + help="skip vpn check and/or vpn connection (if disabled)", + required=False, + ) + + args = parser.parse_args() + torrent_url = args.torrent_url + no_vpn = args.no_vpn - download_torrent_with_deluge(torrent_url) + download_torrent_with_deluge(torrent_url, no_vpn) diff --git a/synotools/commands/vpn_connect.py b/synotools/commands/vpn_connect.py index b7b125f..48ba1c4 100644 --- a/synotools/commands/vpn_connect.py +++ b/synotools/commands/vpn_connect.py @@ -20,7 +20,9 @@ def check_and_connect(): vpn_connected = is_vpn_enabled(connection) if not vpn_connected: - connect_vpn(connection) + vpn_connected = connect_vpn(connection) + + return vpn_connected def is_vpn_enabled(connection): @@ -28,8 +30,8 @@ def is_vpn_enabled(connection): command = ".scripts/vpn-check-connection.sh" vpn_check_result = connection.sudo(command, warn=True) logger.info(vpn_check_result) - except Exception as e: - logger.error(f"An error occurred: {e}") + except Exception: + logger.exception(f"An error occurred.") raise return ( @@ -44,7 +46,7 @@ def connect_vpn(connection): try: command = f".scripts/vpn-connect.sh {params}" vpn_connect_result = connection.sudo(command, warn=True) - logger.info(vpn_connect_result) + return vpn_connect_result.ok except Exception as e: logger.error(f"An error occurred: {e}") raise diff --git a/tests/unit/command/download/test_download_torrent_with_deluge.py b/tests/unit/command/download/test_download_torrent_with_deluge.py index e647ae5..7abdeee 100644 --- a/tests/unit/command/download/test_download_torrent_with_deluge.py +++ b/tests/unit/command/download/test_download_torrent_with_deluge.py @@ -1,12 +1,15 @@ from unittest.mock import patch from synotools.commands.download import download_torrent_with_deluge -from tests.unit.fixtures import (create_deluge_config_mock, - create_syno_config_mock) +from tests.unit.fixtures import ( + create_deluge_config_mock, + create_syno_config_mock, +) TORRENT_URL = "https://www.archlinux.org/releng/releases/2019.06.01/torrent/" +@patch("synotools.commands.download.check_and_connect") @patch("synotools.commands.download.Connection") @patch( "synotools.commands.download.DelugeConfig", return_value=create_deluge_config_mock() @@ -17,6 +20,7 @@ def test_gets_synology_config_details(syno_config_mock, deluge_config_mock, *_): syno_config_mock.assert_called_once() +@patch("synotools.commands.download.check_and_connect") @patch("synotools.commands.download.Connection") @patch("synotools.commands.download.SynoConfig", return_value=create_syno_config_mock()) @patch( @@ -27,6 +31,7 @@ def test_gets_deluge_config_details(deluge_config_mock, *_): deluge_config_mock.assert_called_once() +@patch("synotools.commands.download.check_and_connect") @patch("synotools.commands.download.get_logger") @patch( "synotools.commands.download.DelugeConfig", return_value=create_deluge_config_mock() @@ -44,6 +49,7 @@ def test_creates_fabric_connection_with_correct_sudo_config( ) +@patch("synotools.commands.download.check_and_connect") @patch("synotools.commands.download.logger") @patch( "synotools.commands.download.DelugeConfig", return_value=create_deluge_config_mock() @@ -63,6 +69,7 @@ def test_creates_fabric_connection_with_correct_credentials( connection_mock.return_value.sudo.assert_called_once_with(expected_command) +@patch("synotools.commands.download.check_and_connect") @patch( "synotools.commands.download.DelugeConfig", return_value=create_deluge_config_mock() ) @@ -80,3 +87,41 @@ def test_logs_message_when_exception_occurs(connection_mock, logger_mock, *_): logger_mock.error.assert_called_once_with( f"An error occurred: Uh oh, something happened!" ) + + +@patch( + "synotools.commands.download.DelugeConfig", return_value=create_deluge_config_mock() +) +@patch("synotools.commands.download.SynoConfig", return_value=create_syno_config_mock()) +@patch("synotools.commands.download.Config") +@patch("synotools.commands.download.check_and_connect") +@patch("synotools.commands.download.logger") +@patch("synotools.commands.download.Connection") +def test_skips_vpn_checks_when_flag_is_passed( + connection_mock, logger_mock, check_and_connect_mock, *_ +): + + # Act + download_torrent_with_deluge(TORRENT_URL, True) + + # Assert + assert not check_and_connect_mock.called + + +@patch( + "synotools.commands.download.DelugeConfig", return_value=create_deluge_config_mock() +) +@patch("synotools.commands.download.SynoConfig", return_value=create_syno_config_mock()) +@patch("synotools.commands.download.Config") +@patch("synotools.commands.download.check_and_connect") +@patch("synotools.commands.download.logger") +@patch("synotools.commands.download.Connection") +def test_checks_and_connects_vpn_by_default( + connection_mock, logger_mock, check_and_connect_mock, *_ +): + + # Act + download_torrent_with_deluge(TORRENT_URL) + + # Assert + assert check_and_connect_mock.called diff --git a/tests/unit/command/vpn_connect/test_check_and_connect.py b/tests/unit/command/vpn_connect/test_check_and_connect.py index 1057294..be92467 100644 --- a/tests/unit/command/vpn_connect/test_check_and_connect.py +++ b/tests/unit/command/vpn_connect/test_check_and_connect.py @@ -94,3 +94,48 @@ def test_connects_if_check_retrieves_it_is_not_connected( check_and_connect() connect_vpn_mock.assert_called_once_with(connection_mock.return_value) + + +@patch("synotools.commands.vpn_connect.logger") +@patch( + "synotools.commands.vpn_connect.SynoConfig", return_value=create_syno_config_mock() +) +@patch("synotools.commands.vpn_connect.Config") +@patch("synotools.commands.vpn_connect.connect_vpn") +@patch("synotools.commands.vpn_connect.is_vpn_enabled") +@patch("synotools.commands.vpn_connect.Connection") +def test_returns_true_when_vpn_is_already_connected( + connection_mock, is_vpn_enabled_mock, connect_vpn_mock, *_ +): + # Arrange + is_vpn_enabled_mock.return_value = True + + # Act + actual = check_and_connect() + + # Assert + assert actual is True + assert not connect_vpn_mock.called + + +@patch("synotools.commands.vpn_connect.logger") +@patch( + "synotools.commands.vpn_connect.SynoConfig", return_value=create_syno_config_mock() +) +@patch("synotools.commands.vpn_connect.Config") +@patch("synotools.commands.vpn_connect.connect_vpn") +@patch("synotools.commands.vpn_connect.is_vpn_enabled") +@patch("synotools.commands.vpn_connect.Connection") +def test_returns_true_when_vpn_is_attempted_to_connect_and_succeeds( + connection_mock, is_vpn_enabled_mock, connect_vpn_mock, *_ +): + # Arrange + is_vpn_enabled_mock.return_value = False + connect_vpn_mock.return_value = True + + # Act + actual = check_and_connect() + + # Assert + assert actual is True + connect_vpn_mock.assert_called_once_with(connection_mock.return_value) diff --git a/tests/unit/command/vpn_connect/test_connect_vpn.py b/tests/unit/command/vpn_connect/test_connect_vpn.py index f517887..d848b6b 100644 --- a/tests/unit/command/vpn_connect/test_connect_vpn.py +++ b/tests/unit/command/vpn_connect/test_connect_vpn.py @@ -31,3 +31,35 @@ def test_logs_error_ans_raises_exception_when_fabric_process_fails(logger_mock, connect_vpn(connection_mock) logger_mock.error.assert_called_once_with("An error occurred: Hah! Didn't work.") + + +@patch( + "synotools.commands.vpn_connect.VpnConfig", return_value=create_vpn_config_fake() +) +@patch("synotools.commands.vpn_connect.logger") +def test_returns_true_when_remote_script_succeeds(logger_mock, *_): + # Arrange + connection_mock = Mock() + connection_mock.sudo.return_value = Mock(ok=True) + + # Act + actual = connect_vpn(connection_mock) + + # Assert + assert actual is True + + +@patch( + "synotools.commands.vpn_connect.VpnConfig", return_value=create_vpn_config_fake() +) +@patch("synotools.commands.vpn_connect.logger") +def test_returns_false_when_remote_script_fails(logger_mock, *_): + # Arrange + connection_mock = Mock() + connection_mock.sudo.return_value = Mock(ok=False) + + # Act + actual = connect_vpn(connection_mock) + + # Assert + assert actual is False