"""Typed settings accessors and form validation.""" import pytest from youtube_automate import settings as settings_module from youtube_automate.settings import DEFAULTS, Settings, validate, validate_all class TestAccessors: def test_missing_key_returns_the_default(self, settings): assert settings.get_int("retention_days") == 14 assert settings.get_str("sub_langs") == "en.*" assert settings.get_bool("write_subs") is True def test_unknown_key_never_raises(self, settings): assert settings.get_str("no_such_key") == "" assert settings.get_int("no_such_key") == 0 def test_set_then_get(self, settings): settings.set("retention_days", "30") assert settings.get_int("retention_days") == 30 def test_set_overwrites(self, settings): settings.set("retention_days", "30") settings.set("retention_days", "45") assert settings.get_int("retention_days") == 45 def test_corrupt_integer_falls_back_to_the_default(self, settings): settings.set("retention_days", "not a number") assert settings.get_int("retention_days") == 14 @pytest.mark.parametrize("truthy", ["true", "True", "1", "yes", "on"]) def test_bool_truthy_forms(self, settings, truthy): settings.set("write_subs", truthy) assert settings.get_bool("write_subs") is True @pytest.mark.parametrize("falsy", ["false", "False", "0", "no", "off", ""]) def test_bool_falsy_forms(self, settings, falsy): settings.set("write_subs", falsy) assert settings.get_bool("write_subs") is False def test_corrupt_bool_falls_back_to_the_default(self, settings): settings.set("write_subs", "maybe") assert settings.get_bool("write_subs") is True def test_all_editable_covers_every_default(self, settings): values = settings.all_editable() assert set(values) == set(DEFAULTS) def test_secrets_are_not_editable(self): for key in settings_module.SECRET_KEYS: assert key not in settings_module.EDITABLE class TestValidation: @pytest.mark.parametrize( "key, value", [ ("retention_days", "14"), ("backfill_days", "0"), ("max_height", "720"), ("min_duration_seconds", "120"), ("disk_cap_gb", "0"), ("write_subs", "true"), ("sponsorblock_mark", "false"), ("jellyfin_url", "http://127.0.0.1:8096"), ("pot_provider_url", "https://example.com:4416"), ("sub_langs", "en.*"), ], ) def test_accepts_good_values(self, key, value): ok, _ = validate(key, value) assert ok @pytest.mark.parametrize( "key, value", [ ("retention_days", "abc"), ("retention_days", "0"), ("retention_days", "-5"), ("max_height", "10"), ("min_duration_seconds", "-1"), ("write_subs", "maybe"), ("jellyfin_url", "not-a-url"), ("jellyfin_url", "ftp://host/"), ("jellyfin_url", "http://"), ("sub_langs", ""), ], ) def test_rejects_bad_values(self, key, value): ok, message = validate(key, value) assert not ok assert message def test_validate_all_reports_each_bad_field(self): errors = validate_all( {"retention_days": "abc", "max_height": "720", "jellyfin_url": "nope"} ) assert set(errors) == {"retention_days", "jellyfin_url"} def test_validate_all_ignores_unknown_keys(self): assert validate_all({"not_a_setting": "x"}) == {} def test_empty_api_key_is_acceptable(self): ok, _ = validate("jellyfin_api_key", "") assert ok def test_whitespace_is_tolerated(self): ok, _ = validate("retention_days", " 21 ") assert ok