test setup

This commit is contained in:
Jesse Bannon 2026-01-19 11:15:08 -08:00
parent bca397cd7b
commit 6ecd04619e
7 changed files with 614 additions and 574 deletions

View file

@ -1,4 +1,5 @@
from typing import Dict from typing import Dict
from typing import List
from ytdl_sub.config.overrides import Overrides from ytdl_sub.config.overrides import Overrides
from ytdl_sub.config.plugin.plugin_mapping import PluginMapping from ytdl_sub.config.plugin.plugin_mapping import PluginMapping
@ -19,6 +20,23 @@ class ResolutionLevel:
RESOLVE = 2 RESOLVE = 2
INTERNAL = 3 INTERNAL = 3
@classmethod
def name_of(cls, resolution_level: int) -> str:
if resolution_level == cls.ORIGINAL:
return "original"
if resolution_level == cls.FILL:
return "fill"
if resolution_level == cls.RESOLVE:
return "resolve"
if resolution_level == cls.INTERNAL:
return "internal"
@classmethod
def all(cls) -> List[int]:
return [cls.ORIGINAL, cls.FILL, cls.RESOLVE, cls.INTERNAL]
raise ValueError("Invalid resolution level")
class VariableValidation: class VariableValidation:
@ -30,25 +48,20 @@ class VariableValidation:
elif self._resolution_level == ResolutionLevel.RESOLVE: elif self._resolution_level == ResolutionLevel.RESOLVE:
# Partial resolve everything, but not including internal variables # Partial resolve everything, but not including internal variables
self.unresolved_variables |= VARIABLES.variable_names(include_sanitized=True) self.unresolved_variables |= VARIABLES.variable_names(include_sanitized=True)
self.script = self.script.resolve_partial( self.script = self.script.resolve_partial(unresolvable=self.unresolved_variables)
unresolvable=self.unresolved_variables
)
elif self._resolution_level == ResolutionLevel.INTERNAL: elif self._resolution_level == ResolutionLevel.INTERNAL:
# Partial resolve everything including internal variables # Partial resolve everything including internal variables
self.script = self.script.resolve_partial( self.script = self.script.resolve_partial(unresolvable=self.unresolved_variables)
unresolvable=self.unresolved_variables
)
else: else:
raise ValueError("Invalid resolution level for validation") raise ValueError("Invalid resolution level for validation")
def __init__( def __init__(
self, self,
overrides: Overrides, overrides: Overrides,
downloader_options: MultiUrlValidator, downloader_options: MultiUrlValidator,
output_options: OutputOptions, output_options: OutputOptions,
plugins: PresetPlugins, plugins: PresetPlugins,
resolution_level: int = ResolutionLevel.FILL resolution_level: int = ResolutionLevel.FILL,
): ):
self.overrides = overrides self.overrides = overrides
self.downloader_options = downloader_options self.downloader_options = downloader_options
@ -77,7 +90,6 @@ class VariableValidation:
self.unresolved_runtime_variables -= added_variables | modified_variables self.unresolved_runtime_variables -= added_variables | modified_variables
def ensure_proper_usage(self) -> Dict: def ensure_proper_usage(self) -> Dict:
""" """
Validate variables resolve as plugins are executed, and return Validate variables resolve as plugins are executed, and return
@ -136,17 +148,18 @@ class VariableValidation:
resolved_subscription["download"].append(url_output) resolved_subscription["download"].append(url_output)
# TODO: make function # TODO: make function
resolved_subscription['overrides'] = {} resolved_subscription["overrides"] = {}
for name in self.overrides.keys: for name in self.overrides.keys:
value = self.script.definition_of(name) value = self.script.definition_of(name)
if name in self.script.function_names: if name in self.script.function_names:
# Keep custom functions as-is # Keep custom functions as-is
resolved_subscription['overrides'][name] = self.overrides.dict_with_format_strings[name] resolved_subscription["overrides"][name] = self.overrides.dict_with_format_strings[
name
]
elif resolved := value.maybe_resolvable: elif resolved := value.maybe_resolvable:
resolved_subscription['overrides'][name] = resolved.native resolved_subscription["overrides"][name] = resolved.native
else: else:
resolved_subscription['overrides'][name] = ScriptUtils.to_native_script(value) resolved_subscription["overrides"][name] = ScriptUtils.to_native_script(value)
assert not self.unresolved_runtime_variables assert not self.unresolved_runtime_variables
return resolved_subscription return resolved_subscription

View file

@ -680,7 +680,7 @@ class Script:
raise RuntimeException(f"Tried to get unresolved variable {variable_name}") raise RuntimeException(f"Tried to get unresolved variable {variable_name}")
def definition_of(self, name: str) -> SyntaxTree: def definition_of(self, name: str) -> SyntaxTree:
if name.startswith('%') and name[1:] in self._functions: if name.startswith("%") and name[1:] in self._functions:
return self._functions[name[1:]] return self._functions[name[1:]]
if name in self._variables: if name in self._variables:
return self._variables[name] return self._variables[name]

View file

@ -263,14 +263,14 @@ class BaseSubscription(ABC):
------- -------
Human-readable, condensed YAML definition of the subscription. Human-readable, condensed YAML definition of the subscription.
""" """
out = ( if resolution_level == ResolutionLevel.ORIGINAL:
VariableValidation( return self._preset_options.yaml
overrides=self.overrides,
downloader_options=self.downloader_options, out = VariableValidation(
output_options=self.output_options, overrides=self.overrides,
plugins=self.plugins, downloader_options=self.downloader_options,
resolution_level=resolution_level, output_options=self.output_options,
) plugins=self.plugins,
.ensure_proper_usage() resolution_level=resolution_level,
) ).ensure_proper_usage()
return dump_yaml(out) return dump_yaml(out)

View file

@ -240,7 +240,7 @@ def _validate_formatter(
unresolved_variables: Set[str], unresolved_variables: Set[str],
unresolved_runtime_variables: Set[str], unresolved_runtime_variables: Set[str],
formatter_validator: Union[StringFormatterValidator, OverridesStringFormatterValidator], formatter_validator: Union[StringFormatterValidator, OverridesStringFormatterValidator],
resolve_partial: bool = True resolve_partial: bool = True,
) -> str: ) -> str:
parsed = formatter_validator.parsed parsed = formatter_validator.parsed
if resolved := parsed.maybe_resolvable: if resolved := parsed.maybe_resolvable:
@ -253,9 +253,11 @@ def _validate_formatter(
if resolve_partial and not is_static_formatter: if resolve_partial and not is_static_formatter:
formatter_hash = get_md5_hash(formatter_validator.format_string) formatter_hash = get_md5_hash(formatter_validator.format_string)
parsed = mock_script.add_parsed( parsed = (
{formatter_hash: formatter_validator.parsed} mock_script.add_parsed({formatter_hash: formatter_validator.parsed})
).resolve_partial(unresolvable=unresolved_variables).definition_of(formatter_hash) .resolve_partial(unresolvable=unresolved_variables)
.definition_of(formatter_hash)
)
# Add lambda functions to custom function names, if it's custom # Add lambda functions to custom function names, if it's custom
for lambda_func in parsed.lambdas: for lambda_func in parsed.lambdas:

View file

@ -5,7 +5,7 @@ from pathlib import Path
from typing import Dict from typing import Dict
DISABLE_YOUTUBE_TESTS: bool = True DISABLE_YOUTUBE_TESTS: bool = True
REGENERATE_FIXTURES: bool = False REGENERATE_FIXTURES: bool = True
RESOURCE_PATH: Path = Path("tests") / "resources" RESOURCE_PATH: Path = Path("tests") / "resources"
_FILE_FIXTURE_PATH: Path = RESOURCE_PATH / "file_fixtures" _FILE_FIXTURE_PATH: Path = RESOURCE_PATH / "file_fixtures"

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,32 @@
from pathlib import Path
import pytest
import yaml
from resources import expected_json
from ytdl_sub.config.config_file import ConfigFile
from ytdl_sub.config.validators.variable_validation import ResolutionLevel
from ytdl_sub.subscriptions.subscription import Subscription
def _ensure_resolved_yaml(sub: Subscription, preset_type: str, resolution_level: int) -> None:
output_yaml = sub.resolved_yaml(resolution_level=resolution_level)
out = yaml.safe_load(output_yaml)
expected_out_filename = (
f"{preset_type}/inspect_sub_{ResolutionLevel.name_of(resolution_level)}.json"
)
assert out == expected_json(out, expected_out_filename)
@pytest.mark.parametrize("resolution_level", ResolutionLevel.all())
def test_resolution_original_tv_show(
resolution_level: int, config_file: ConfigFile, tv_show_subscriptions_path: Path
):
_ensure_resolved_yaml(
sub=Subscription.from_file_path(
config=config_file, subscription_path=tv_show_subscriptions_path
)[0],
preset_type="tv_show",
resolution_level=resolution_level,
)