refacot rtests

This commit is contained in:
Jesse Bannon 2023-10-21 11:27:23 -07:00
parent ae3d0966b1
commit f3efee5add
5 changed files with 229 additions and 213 deletions

View file

@ -29,6 +29,10 @@ logger = Logger.get()
_VIEW_EXTRA_ARGS_FORMATTER = "--preset _view --overrides.url {}"
def _log_time() -> str:
return datetime.now().strftime("%Y-%m-%d-%H%M%S")
def _maybe_write_subscription_log_file(
config: ConfigFile,
subscription: Subscription,
@ -49,11 +53,10 @@ def _maybe_write_subscription_log_file(
if success and not config.config_options.persist_logs.keep_successful_logs:
return
log_time = datetime.now().strftime("%Y-%m-%d-%H%M%S")
log_subscription_name = sanitize_filename(subscription.name).lower().replace(" ", "_")
log_success = "success" if success else "error"
log_filename = f"{log_time}.{log_subscription_name}.{log_success}.log"
log_filename = f"{_log_time()}.{log_subscription_name}.{log_success}.log"
persist_log_path = Path(config.config_options.persist_logs.logs_directory) / log_filename
if not success:

View file

@ -0,0 +1,87 @@
import datetime
import os
import shutil
import tempfile
import time
from typing import Callable
from unittest.mock import patch
import mergedeep
import pytest
from ytdl_sub.config.config_file import ConfigFile
from ytdl_sub.subscriptions.subscription import Subscription
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
from ytdl_sub.utils.file_handler import FileMetadata
from ytdl_sub.utils.logger import Logger
@pytest.fixture
def mock_subscription_download_factory():
def _mock_subscription_download_factory(mock_success_output: bool) -> Callable:
def _mock_download(self: Subscription, dry_run: bool) -> FileHandlerTransactionLog:
Logger.get().info(
"name=%s success=%s dry_run=%s", self.name, mock_success_output, dry_run
)
if not mock_success_output:
raise ValueError("error")
(
self._enhanced_download_archive.get_file_handler_transaction_log()
.log_created_file("created_file.txt", FileMetadata())
.log_modified_file("modified_file.txt", FileMetadata())
.log_removed_file("deleted_file.txt")
)
return self._enhanced_download_archive.get_file_handler_transaction_log()
return _mock_download
return _mock_subscription_download_factory
@pytest.fixture
def mock_subscription_download_success(mock_subscription_download_factory: Callable):
with patch.object(
Subscription,
"download",
new=mock_subscription_download_factory(mock_success_output=True),
):
yield
@pytest.fixture
def persist_logs_directory() -> str:
# Delete the temp_dir on creation
with tempfile.TemporaryDirectory() as temp_dir:
pass
yield temp_dir
if os.path.isdir(temp_dir):
shutil.rmtree(temp_dir)
@pytest.fixture
def persist_logs_config_factory(
music_video_config: ConfigFile, persist_logs_directory: str
) -> Callable:
def _persist_logs_config_factory(keep_successful_logs: bool) -> ConfigFile:
return ConfigFile.from_dict(
dict(
mergedeep.merge(
music_video_config.as_dict(),
{
"configuration": {
"persist_logs": {
"logs_directory": persist_logs_directory,
"keep_successful_logs": keep_successful_logs,
},
}
},
)
)
)
return _persist_logs_config_factory

View file

@ -1,113 +1,24 @@
import os.path
import re
import shutil
import sys
import tempfile
import time
from pathlib import Path
from typing import Callable
from typing import List
from typing import Optional
from typing import Tuple
from unittest.mock import MagicMock
from unittest.mock import Mock
from unittest.mock import patch
import mergedeep
import pytest
from conftest import assert_logs
from ytdl_sub.cli.entrypoint import _download_subscriptions_from_yaml_files
from ytdl_sub.cli.entrypoint import main
from ytdl_sub.cli.output_summary import output_summary
from ytdl_sub.cli.output_transaction_log import logger as transaction_logger
from ytdl_sub.config.config_file import ConfigFile
from ytdl_sub.subscriptions.subscription import Subscription
from ytdl_sub.utils.exceptions import ExperimentalFeatureNotEnabled
from ytdl_sub.utils.file_handler import FileHandler
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
from ytdl_sub.utils.file_handler import FileMetadata
from ytdl_sub.utils.logger import Logger
####################################################################################################
# SHARED FIXTURES
@pytest.fixture
def mock_subscription_download_factory():
def _mock_subscription_download_factory(mock_success_output: bool) -> Callable:
def _mock_download(self: Subscription, dry_run: bool) -> FileHandlerTransactionLog:
Logger.get().info(
"name=%s success=%s dry_run=%s", self.name, mock_success_output, dry_run
)
time.sleep(1)
if not mock_success_output:
raise ValueError("error")
(
self._enhanced_download_archive.get_file_handler_transaction_log()
.log_created_file("created_file.txt", FileMetadata())
.log_modified_file("modified_file.txt", FileMetadata())
.log_removed_file("deleted_file.txt")
)
return self._enhanced_download_archive.get_file_handler_transaction_log()
return _mock_download
return _mock_subscription_download_factory
@pytest.fixture
def mock_subscription_download_success(mock_subscription_download_factory: Callable):
with patch.object(
Subscription,
"download",
new=mock_subscription_download_factory(mock_success_output=True),
):
yield
####################################################################################################
# PERSIST LOGS FIXTURES + TESTS
@pytest.fixture
def persist_logs_directory() -> str:
# Delete the temp_dir on creation
with tempfile.TemporaryDirectory() as temp_dir:
pass
yield temp_dir
if os.path.isdir(temp_dir):
shutil.rmtree(temp_dir)
@pytest.fixture
def persist_logs_config_factory(
music_video_config: ConfigFile, persist_logs_directory: str
) -> Callable:
def _persist_logs_config_factory(keep_successful_logs: bool) -> ConfigFile:
return ConfigFile.from_dict(
dict(
mergedeep.merge(
music_video_config.as_dict(),
{
"configuration": {
"persist_logs": {
"logs_directory": persist_logs_directory,
"keep_successful_logs": keep_successful_logs,
},
}
},
)
)
)
return _persist_logs_config_factory
@pytest.mark.parametrize("dry_run", [True, False])
@pytest.mark.parametrize("mock_success_output", [True, False])
@pytest.mark.parametrize("keep_successful_logs", [True, False])
@ -128,7 +39,8 @@ def test_subscription_logs_write_to_file(
Subscription,
"download",
new=mock_subscription_download_factory(mock_success_output=mock_success_output),
):
# mock datetime to be an index to be able to run instantly
), patch("ytdl_sub.cli.entrypoint._log_time", side_effect=[str(idx) for idx in range(10)]):
try:
_download_subscriptions_from_yaml_files(
config=config,
@ -149,7 +61,7 @@ def test_subscription_logs_write_to_file(
elif not mock_success_output:
assert len(log_directory_files) == 2
for log_path in log_directory_files:
assert bool(re.match(r"\d{4}-\d{2}-\d{2}-\d{6}\.john_smith\.error\.log", log_path.name))
assert bool(re.match(r"\d\.john_smith\.error\.log", log_path.name))
with open(log_path, "r", encoding="utf-8") as log_file:
assert log_file.readlines()[-1] == (
f"Please upload the error log file '{str(log_path)}' and make a Github issue "
@ -160,9 +72,7 @@ def test_subscription_logs_write_to_file(
else:
assert len(log_directory_files) == num_subscriptions
for log_file_path in log_directory_files:
assert bool(
re.match(r"\d{4}-\d{2}-\d{2}-\d{6}\.john_smith\.success\.log", log_file_path.name)
)
assert bool(re.match(r"\d\.john_smith\.success\.log", log_file_path.name))
with open(log_file_path, "r", encoding="utf-8") as log_file:
assert (
log_file.readlines()[-1]
@ -170,123 +80,6 @@ def test_subscription_logs_write_to_file(
)
####################################################################################################
# TRANSACTION LOGS FIXTURES + TESTS
@pytest.fixture
def transaction_log_file_path() -> str:
# Delete the temp_file on creation
with tempfile.NamedTemporaryFile() as temp_file:
pass
yield temp_file.name
if os.path.isfile(temp_file.name):
FileHandler.delete(temp_file.name)
@pytest.mark.parametrize("file_transaction_log", [None, "output.log"])
def test_suppress_transaction_log(
mock_subscription_download_success,
music_video_config_path: Path,
music_video_subscription_path: Path,
file_transaction_log: Optional[str],
) -> None:
with patch.object(
sys,
"argv",
[
"ytdl-sub",
"--config",
str(music_video_config_path),
"sub",
str(music_video_subscription_path),
"--suppress-transaction-log",
]
+ (["--transaction-log", file_transaction_log] if file_transaction_log else []),
), patch("ytdl_sub.cli.output_transaction_log.output_transaction_log") as mock_transaction_log:
transaction_logs = main()
assert transaction_logs
assert mock_transaction_log.call_count == 0
def test_transaction_log_to_file(
mock_subscription_download_success,
music_video_config_path: Path,
music_video_subscription_path: Path,
transaction_log_file_path: Path,
) -> None:
with patch.object(
sys,
"argv",
[
"ytdl-sub",
"--config",
str(music_video_config_path),
"sub",
str(music_video_subscription_path),
"--transaction-log",
str(transaction_log_file_path),
],
):
subscriptions = main()
assert subscriptions
with open(transaction_log_file_path, "r", encoding="utf-8") as transaction_log_file:
assert transaction_log_file.readlines()[0] == "Transaction log for john_smith:\n"
def test_transaction_log_to_logger(
mock_subscription_download_success,
music_video_config_path: Path,
music_video_subscription_path: Path,
) -> None:
with patch.object(
sys,
"argv",
[
"ytdl-sub",
"--config",
str(music_video_config_path),
"sub",
str(music_video_subscription_path),
],
), assert_logs(
logger=transaction_logger,
expected_message="Transaction log for john_smith:\n",
log_level="info",
):
transaction_logs = main()
assert transaction_logs
def test_output_summary():
subscription_values: List[Tuple[str, int, int, int, int, Optional[Exception]]] = [
("long_name_but_lil_values", 0, 0, 0, 6, None),
("john_smith", 1, 0, 0, 52, None),
("david_gore", 0, 0, 0, 4, None),
("christopher_snoop", 50, 0, 3, 518, None),
("beyond funk", 0, 0, 0, 176, ValueError("lol")),
]
mock_subscriptions: List[MagicMock] = []
for values in subscription_values:
sub = Mock()
sub.name = values[0]
sub.num_entries_added = values[1]
sub.num_entries_modified = values[2]
sub.num_entries_removed = values[3]
sub.num_entries = values[4]
sub.exception = values[5]
mock_subscriptions.append(sub)
_ = output_summary(subscriptions=mock_subscriptions)
assert True # Test used for manual inspection - too hard to test ansi color codes
def test_update_with_info_json_requires_experimental_flag(
music_video_config_path: Path,
music_video_subscription_path: Path,

View file

@ -0,0 +1,32 @@
from typing import List
from typing import Optional
from typing import Tuple
from unittest.mock import MagicMock
from unittest.mock import Mock
from ytdl_sub.cli.output_summary import output_summary
def test_output_summary_one_error():
subscription_values: List[Tuple[str, int, int, int, int, Optional[Exception]]] = [
("long_name_but_lil_values", 0, 0, 0, 6, None),
("john_smith", 1, 0, 0, 52, None),
("david_gore", 0, 0, 0, 4, None),
("christopher_snoop", 50, 0, 3, 518, None),
("beyond funk", 0, 0, 0, 176, ValueError("lol")),
]
mock_subscriptions: List[MagicMock] = []
for values in subscription_values:
sub = Mock()
sub.name = values[0]
sub.num_entries_added = values[1]
sub.num_entries_modified = values[2]
sub.num_entries_removed = values[3]
sub.num_entries = values[4]
sub.exception = values[5]
mock_subscriptions.append(sub)
_ = output_summary(subscriptions=mock_subscriptions)
assert True # Test used for manual inspection - too hard to test ansi color codes

View file

@ -0,0 +1,101 @@
import os
import sys
import tempfile
from pathlib import Path
from typing import Optional
from unittest.mock import patch
import pytest
from conftest import assert_logs
from ytdl_sub.cli.entrypoint import main
from ytdl_sub.cli.output_transaction_log import logger as transaction_logger
from ytdl_sub.utils.file_handler import FileHandler
@pytest.fixture
def transaction_log_file_path() -> str:
# Delete the temp_file on creation
with tempfile.NamedTemporaryFile() as temp_file:
pass
yield temp_file.name
if os.path.isfile(temp_file.name):
FileHandler.delete(temp_file.name)
@pytest.mark.parametrize("file_transaction_log", [None, "output.log"])
def test_suppress_transaction_log(
mock_subscription_download_success,
music_video_config_path: Path,
music_video_subscription_path: Path,
file_transaction_log: Optional[str],
) -> None:
with patch.object(
sys,
"argv",
[
"ytdl-sub",
"--config",
str(music_video_config_path),
"sub",
str(music_video_subscription_path),
"--suppress-transaction-log",
]
+ (["--transaction-log", file_transaction_log] if file_transaction_log else []),
), patch("ytdl_sub.cli.output_transaction_log.output_transaction_log") as mock_transaction_log:
transaction_logs = main()
assert transaction_logs
assert mock_transaction_log.call_count == 0
def test_transaction_log_to_file(
mock_subscription_download_success,
music_video_config_path: Path,
music_video_subscription_path: Path,
transaction_log_file_path: Path,
) -> None:
with patch.object(
sys,
"argv",
[
"ytdl-sub",
"--config",
str(music_video_config_path),
"sub",
str(music_video_subscription_path),
"--transaction-log",
str(transaction_log_file_path),
],
):
subscriptions = main()
assert subscriptions
with open(transaction_log_file_path, "r", encoding="utf-8") as transaction_log_file:
assert transaction_log_file.readlines()[0] == "Transaction log for john_smith:\n"
def test_transaction_log_to_logger(
mock_subscription_download_success,
music_video_config_path: Path,
music_video_subscription_path: Path,
) -> None:
with patch.object(
sys,
"argv",
[
"ytdl-sub",
"--config",
str(music_video_config_path),
"sub",
str(music_video_subscription_path),
],
), assert_logs(
logger=transaction_logger,
expected_message="Transaction log for john_smith:\n",
log_level="info",
):
transaction_logs = main()
assert transaction_logs