From 9646a3bd6557292582c8f676f480f3f21bd4dadf Mon Sep 17 00:00:00 2001 From: jbannon Date: Sun, 17 Jul 2022 05:14:06 +0000 Subject: [PATCH] more fail tests --- src/ytdl_sub/config/preset.py | 4 +- src/ytdl_sub/plugins/plugin.py | 11 +-- src/ytdl_sub/plugins/regex.py | 26 +++++-- src/ytdl_sub/subscriptions/subscription.py | 28 ++++++++ tests/e2e/plugins/test_regex.py | 80 ++++++++++++++++++++++ 5 files changed, 140 insertions(+), 9 deletions(-) diff --git a/src/ytdl_sub/config/preset.py b/src/ytdl_sub/config/preset.py index 278adf4e..b95b589a 100644 --- a/src/ytdl_sub/config/preset.py +++ b/src/ytdl_sub/config/preset.py @@ -145,7 +145,9 @@ class Preset(StrictDictValidator): plugin = PluginMapping.get(plugin=key) plugin_options = self._validate_key(key=key, validator=plugin.plugin_options_type) - plugin_options.validate_with_source_variables(source_variables=self._source_variables) + plugin_options.validate_with_variables( + source_variables=self._source_variables, override_variables=self.overrides.keys + ) plugins.append((plugin, plugin_options)) diff --git a/src/ytdl_sub/plugins/plugin.py b/src/ytdl_sub/plugins/plugin.py index cba5cbb7..6283f758 100644 --- a/src/ytdl_sub/plugins/plugin.py +++ b/src/ytdl_sub/plugins/plugin.py @@ -32,15 +32,18 @@ class PluginOptions(StrictDictValidator): return [] # pylint: disable=unused-argument - def validate_with_source_variables(self, source_variables: List[str]) -> None: + def validate_with_variables( + self, source_variables: List[str], override_variables: List[str] + ) -> None: """ - Performs validation after init using the source variables, in case the plugin - depends on specific source variables. + Optional validation after init with the session's source and override variables. Parameters ---------- source_variables - Source variables to be used when running the plugin + Available source variables when running the plugin + override_variables + Available override variables when running the plugin """ return None diff --git a/src/ytdl_sub/plugins/regex.py b/src/ytdl_sub/plugins/regex.py index 8deec694..42dd1f6b 100644 --- a/src/ytdl_sub/plugins/regex.py +++ b/src/ytdl_sub/plugins/regex.py @@ -45,7 +45,7 @@ class SourceVariableRegex(StrictDictValidator): # If there are capture groups, ensure there are capture group names if len(self._capture_group_names.list) != self._match.num_capture_groups: raise self._validation_exception( - f"Number of capture group names must match number of capture groups, " + f"number of capture group names must match number of capture groups, " f"{len(self._capture_group_names.list)} != {self._match.num_capture_groups}" ) @@ -148,21 +148,39 @@ class RegexOptions(PluginOptions): key="skip_if_match_fails", validator=BoolValidator, default=False ).value - def validate_with_source_variables(self, source_variables: List[str]) -> None: + def validate_with_variables( + self, source_variables: List[str], override_variables: List[str] + ) -> None: """ Ensures each source variable capture group is valid Parameters ---------- source_variables - Variables to check against the provided capture groups + Available source variables when running the plugin + override_variables + Available override variables when running the plugin """ - for key in self.source_variable_capture_dict.keys(): + for key, regex_options in self.source_variable_capture_dict.items(): + # Ensure each variable getting captured is a source variable if key not in source_variables: raise self._validation_exception( f"cannot regex capture '{key}' because it is not a source variable" ) + # Ensure the capture group names are not existing source/override variables + for capture_group_name in regex_options.capture_group_names: + if capture_group_name in source_variables: + raise self._validation_exception( + f"'{capture_group_name}' cannot be used as a capture group name because it " + f"is a source variable" + ) + if capture_group_name in override_variables: + raise self._validation_exception( + f"'{capture_group_name}' cannot be used as a capture group name because it " + f"is an override variable" + ) + @property def source_variable_capture_dict(self) -> Dict[str, SourceVariableRegex]: """ diff --git a/src/ytdl_sub/subscriptions/subscription.py b/src/ytdl_sub/subscriptions/subscription.py index e15e26d8..8dab4fdf 100644 --- a/src/ytdl_sub/subscriptions/subscription.py +++ b/src/ytdl_sub/subscriptions/subscription.py @@ -3,6 +3,7 @@ import copy import os import shutil from pathlib import Path +from typing import Dict from typing import List from typing import Optional from typing import Tuple @@ -318,3 +319,30 @@ class Subscription: preset_options=preset, config_options=config.config_options, ) + + @classmethod + def from_dict(cls, config: ConfigFile, preset_name: str, preset_dict: Dict) -> "Subscription": + """ + Creates a subscription from a preset dict + + Parameters + ---------- + config: + Validated instance of the config + preset_name: + Name of the preset + preset_dict: + The preset config in dict format + + Returns + ------- + Initialized subscription + """ + return cls.from_preset( + preset=Preset.from_dict( + config=config, + preset_name=preset_name, + preset_dict=preset_dict, + ), + config=config, + ) diff --git a/tests/e2e/plugins/test_regex.py b/tests/e2e/plugins/test_regex.py index fc5100cb..d66d0c11 100644 --- a/tests/e2e/plugins/test_regex.py +++ b/tests/e2e/plugins/test_regex.py @@ -1,3 +1,4 @@ +import copy import re import pytest @@ -6,6 +7,7 @@ from e2e.expected_transaction_log import assert_transaction_log_matches from ytdl_sub.config.preset import Preset from ytdl_sub.subscriptions.subscription import Subscription from ytdl_sub.utils.exceptions import RegexNoMatchException +from ytdl_sub.utils.exceptions import ValidationException @pytest.fixture @@ -123,3 +125,81 @@ class TestRegex: ), ): _ = playlist_subscription_no_match_fails.download(dry_run=True) + + def test_regex_fails_capture_group_is_source_variable( + self, regex_subscription_dict, music_video_config + ): + regex_subscription_dict["regex"]["from"]["title"]["capture_group_names"][0] = "uid" + with pytest.raises( + ValidationException, + match=re.escape( + "'uid' cannot be used as a capture group name because it is a source variable" + ), + ): + _ = Subscription.from_dict( + config=music_video_config, + preset_name="test_regex_fails_capture_group_is_source_variable", + preset_dict=regex_subscription_dict, + ) + + def test_regex_fails_capture_group_is_override_variable( + self, regex_subscription_dict, music_video_config + ): + regex_subscription_dict["regex"]["from"]["title"]["capture_group_names"][ + 0 + ] = "in_regex_default" + with pytest.raises( + ValidationException, + match=re.escape( + "'in_regex_default' cannot be used as a capture group name because it is an override variable" + ), + ): + _ = Subscription.from_dict( + config=music_video_config, + preset_name="test_regex_fails_capture_group_is_override_variable", + preset_dict=regex_subscription_dict, + ) + + def test_regex_fails_source_variable_does_not_exist( + self, regex_subscription_dict, music_video_config + ): + regex_subscription_dict["regex"]["from"]["dne"] = copy.deepcopy( + regex_subscription_dict["regex"]["from"]["title"] + ) + with pytest.raises( + ValidationException, + match=re.escape("cannot regex capture 'dne' because it is not a source variable"), + ): + _ = Subscription.from_dict( + config=music_video_config, + preset_name="test_regex_fails_source_variable_does_not_exist", + preset_dict=regex_subscription_dict, + ) + + def test_regex_fails_unequal_defaults(self, regex_subscription_dict, music_video_config): + regex_subscription_dict["regex"]["from"]["title"]["defaults"] = ["1 != 2"] + with pytest.raises( + ValidationException, + match=re.escape("number of defaults must match number of capture groups, 1 != 2"), + ): + _ = Subscription.from_dict( + config=music_video_config, + preset_name="test_regex_fails_unequal_defaults", + preset_dict=regex_subscription_dict, + ) + + def test_regex_fails_unequal_capture_group_names( + self, regex_subscription_dict, music_video_config + ): + regex_subscription_dict["regex"]["from"]["title"]["capture_group_names"].append("unequal") + with pytest.raises( + ValidationException, + match=re.escape( + "number of capture group names must match number of capture groups, 3 != 2" + ), + ): + _ = Subscription.from_dict( + config=music_video_config, + preset_name="test_regex_fails_unequal_capture_group_names", + preset_dict=regex_subscription_dict, + )