From 6881b7ecf59406fa87974a02b97d96668425e886 Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Sun, 1 Jun 2025 07:17:13 -0700 Subject: [PATCH] match filter unit test --- src/ytdl_sub/plugins/date_range.py | 10 +- .../subscriptions/subscription_download.py | 16 ++- .../validators/string_select_validator.py | 2 +- tests/conftest.py | 25 ++++ tests/integration/conftest.py | 1 - tests/integration/plugins/test_date_range.py | 34 ------ .../integration/plugins/test_file_convert.py | 1 - tests/unit/plugins/__init__.py | 0 tests/unit/plugins/test_date_range.py | 108 ++++++++++++++++++ 9 files changed, 148 insertions(+), 49 deletions(-) delete mode 100644 tests/integration/plugins/test_date_range.py create mode 100644 tests/unit/plugins/__init__.py create mode 100644 tests/unit/plugins/test_date_range.py diff --git a/src/ytdl_sub/plugins/date_range.py b/src/ytdl_sub/plugins/date_range.py index 6909a87f..34370a1a 100644 --- a/src/ytdl_sub/plugins/date_range.py +++ b/src/ytdl_sub/plugins/date_range.py @@ -1,5 +1,6 @@ -from typing import List, Set +from typing import List from typing import Optional +from typing import Set from typing import Tuple from ytdl_sub.config.plugin.plugin import Plugin @@ -35,9 +36,11 @@ class DateRangeOptions(ToggleableOptionsDictValidator): date_range: before: "now" after: "today-2weeks" + breaks: True + type: "upload_date" """ - _optional_keys = {"enable", "before", "after", "breaks"} + _optional_keys = {"enable", "before", "after", "breaks", "type"} def __init__(self, name, value): super().__init__(name, value) @@ -81,7 +84,8 @@ class DateRangeOptions(ToggleableOptionsDictValidator): """ :expected type: Optional[OverridesFormatter] :description: - Which type of date to use. Must be either ``upload_date`` or ``release_date`` + Which type of date to use. Must be either ``upload_date`` or ``release_date``. + Defaults to ``upload_date``. """ return self._type diff --git a/src/ytdl_sub/subscriptions/subscription_download.py b/src/ytdl_sub/subscriptions/subscription_download.py index b3fb8bfa..2fd9c89e 100644 --- a/src/ytdl_sub/subscriptions/subscription_download.py +++ b/src/ytdl_sub/subscriptions/subscription_download.py @@ -349,12 +349,16 @@ class SubscriptionDownload(BaseSubscription, ABC): return self.download_archive.get_file_handler_transaction_log() - def get_ytdl_options(self, plugins: Optional[List[Plugin]], dry_run: bool) -> SubscriptionYTDLOptions: + def get_ytdl_options( + self, plugins: Optional[List[Plugin]], dry_run: bool + ) -> SubscriptionYTDLOptions: """ Parameters ---------- plugins Optional. If not provided, will reinitialize them + dry_run + Whether its dry run or not Returns ------- @@ -392,10 +396,7 @@ class SubscriptionDownload(BaseSubscription, ABC): logging.info("Skipping %s", self.name) return FileHandlerTransactionLog() - subscription_ytdl_options = self.get_ytdl_options( - plugins=plugins, - dry_run=dry_run - ) + subscription_ytdl_options = self.get_ytdl_options(plugins=plugins, dry_run=dry_run) downloader = MultiUrlDownloader( options=self.downloader_options, @@ -440,10 +441,7 @@ class SubscriptionDownload(BaseSubscription, ABC): self.download_archive.reinitialize(dry_run=dry_run) plugins = self._initialize_plugins() - subscription_ytdl_options = self.get_ytdl_options( - plugins=plugins, - dry_run=dry_run - ) + subscription_ytdl_options = self.get_ytdl_options(plugins=plugins, dry_run=dry_run) # Re-add the original downloader class' plugins plugins.extend( diff --git a/src/ytdl_sub/validators/string_select_validator.py b/src/ytdl_sub/validators/string_select_validator.py index 8586bec7..9f28fcd4 100644 --- a/src/ytdl_sub/validators/string_select_validator.py +++ b/src/ytdl_sub/validators/string_select_validator.py @@ -28,7 +28,7 @@ class OverridesStringSelectValidator(OverridesStringFormatterValidator): def post_process(self, resolved: str) -> str: if resolved not in self._select_values: raise self._validation_exception( - f"Must be one of the following values: {', '.join(self._select_values)}" + f"Must be one of the following values: {', '.join(sorted(self._select_values))}" ) return resolved diff --git a/tests/conftest.py b/tests/conftest.py index 12ba38a9..aabbba8d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,6 +12,7 @@ from typing import Callable from typing import Dict from typing import List from typing import Optional +from typing import Tuple from unittest.mock import patch import pytest @@ -272,3 +273,27 @@ def mock_run_from_cli(args: str) -> List[Subscription]: args_list = ["ytdl-sub"] + shlex.split(args) with patch.object(sys, "argv", args_list): return main() + + +def get_match_filters( + subscription: Subscription, dry_run: bool, download_filters: bool +) -> Tuple[List[str], List[str]]: + """ + Util function to get match filters from a subscription. + + Returns + ------- + match_filters, breaking_match_filters + """ + options = subscription.get_ytdl_options(plugins=None, dry_run=dry_run) + options_dict = ( + options.download_builder().to_dict() + if download_filters + else options.metadata_builder().to_dict() + ) + if "match_filter" not in options_dict: + return [], [] + + match_filter_str = repr(options_dict["match_filter"]) + out = eval(match_filter_str.split("(", maxsplit=1)[-1].split(")")[0]) + return out diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 621ad68f..fbd26c4c 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -1,6 +1,5 @@ import contextlib import os -import shutil from pathlib import Path from typing import Callable from typing import Dict diff --git a/tests/integration/plugins/test_date_range.py b/tests/integration/plugins/test_date_range.py deleted file mode 100644 index 8d50aa82..00000000 --- a/tests/integration/plugins/test_date_range.py +++ /dev/null @@ -1,34 +0,0 @@ -import re - -import pytest - -from ytdl_sub.subscriptions.subscription import Subscription -from ytdl_sub.utils.exceptions import ValidationException - - -@pytest.fixture -def single_song_video_dict(output_directory): - return { - "download": "https://your.name.here", - "output_options": {"output_directory": output_directory, "file_name": "will_error.mp4"}, - # test multi-tags compile - "music_tags": {"genres": ["multi_tag_1", "multi_tag_2"]}, - } - - -class TestDateRange: - def test_date_range( - self, - config, - single_song_video_dict, - output_directory, - subscription_name, - mock_download_collection_entries, - ): - ytdl_options = Subscription.from_dict( - config=config, - preset_name=subscription_name, - preset_dict=single_song_video_dict, - ).get_ytdl_options(plugins=None, dry_run=False) - - assert ytdl_options.download_builder().to_dict() is False diff --git a/tests/integration/plugins/test_file_convert.py b/tests/integration/plugins/test_file_convert.py index 740cea5b..06980b22 100644 --- a/tests/integration/plugins/test_file_convert.py +++ b/tests/integration/plugins/test_file_convert.py @@ -4,7 +4,6 @@ import pytest from expected_download import assert_expected_downloads from expected_transaction_log import assert_transaction_log_matches -from ytdl_sub.entries.entry import ytdl_sub_chapters_from_comments from ytdl_sub.subscriptions.subscription import Subscription diff --git a/tests/unit/plugins/__init__.py b/tests/unit/plugins/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/plugins/test_date_range.py b/tests/unit/plugins/test_date_range.py new file mode 100644 index 00000000..a024d8e2 --- /dev/null +++ b/tests/unit/plugins/test_date_range.py @@ -0,0 +1,108 @@ +import re +from typing import Any +from typing import Dict + +import pytest +from conftest import get_match_filters + +from ytdl_sub.config.config_file import ConfigFile +from ytdl_sub.subscriptions.subscription import Subscription +from ytdl_sub.utils.exceptions import ValidationException + + +@pytest.fixture +def preset_dict(output_directory) -> Dict[str, Any]: + return { + "download": "https://your.name.here", + "output_options": {"output_directory": output_directory, "file_name": "will_error.mp4"}, + } + + +class TestDateRange: + @pytest.mark.parametrize("date_range_type", ["upload_date", "release_date"]) + def test_date_range_type( + self, + default_config: ConfigFile, + preset_dict: Dict[str, Any], + output_directory: str, + date_range_type: str, + ): + preset_dict["date_range"] = { + "before": "20250530", + "after": "20250510", + "type": date_range_type, + } + sub = Subscription.from_dict( + config=default_config, + preset_name="test_date_range", + preset_dict=preset_dict, + ) + + metadata_filter, metadata_breaking_filter = get_match_filters( + subscription=sub, dry_run=False, download_filters=False + ) + assert metadata_filter == [ + f"!is_live & !is_upcoming & !post_live & {date_range_type} < 20250530" + ] + assert metadata_breaking_filter == [f"{date_range_type} >= 20250510"] + + download_filter, download_breaking_filter = get_match_filters( + subscription=sub, dry_run=False, download_filters=True + ) + assert not download_filter + assert not download_breaking_filter + + def test_date_range_breaks_false( + self, + default_config: ConfigFile, + preset_dict: Dict[str, Any], + output_directory: str, + ): + preset_dict["date_range"] = { + "before": "20250530", + "after": "20250510", + "breaks": False, + } + sub = Subscription.from_dict( + config=default_config, + preset_name="test_date_range", + preset_dict=preset_dict, + ) + + metadata_filter, metadata_breaking_filter = get_match_filters( + subscription=sub, dry_run=False, download_filters=False + ) + assert metadata_filter == [ + f"!is_live & !is_upcoming & !post_live & upload_date < 20250530 & upload_date >= 20250510" + ] + assert not metadata_breaking_filter + + download_filter, download_breaking_filter = get_match_filters( + subscription=sub, dry_run=False, download_filters=True + ) + assert not download_filter + assert not download_breaking_filter + + def test_date_range_invalid_type( + self, + default_config: ConfigFile, + preset_dict: Dict[str, Any], + output_directory: str, + ): + preset_dict["date_range"] = { + "before": "20250530", + "after": "20250510", + "type": "no", + } + + error_msg = ( + "Validation error in test_date_range.date_range.type: " + "Must be one of the following values: release_date, upload_date" + ) + + with pytest.raises(ValidationException, match=re.escape(error_msg)): + Subscription.from_dict( + config=default_config, + preset_name="test_date_range", + preset_dict=preset_dict, + ).download(dry_run=False)