from __future__ import annotations import base64 import json from datetime import date, datetime import pytest from m3u8downloaderd.models import DATE_TEMPLATE, DEFAULT_PROXY, MAX_ACTIVE_DOWNLOADS, ValidationError, decode_download_items, ensure_within_root, resolve_default_title, validate_max_active_downloads, validate_proxy def _payload() -> list[dict[str, object]]: return [ { "code": 17, "title": "Episode 1", "href": "/episode/1", "image_src": "https://cdn.example.test/cover.jpg", "m3u8_url": "https://cdn.example.test/video.m3u8", "m3u8_referer": "https://example.test/watch/1", "ignored": True, } ] def test_download_items_accept_json_and_url_safe_base64() -> None: value = json.dumps(_payload()) encoded = base64.urlsafe_b64encode(value.encode()).decode().rstrip("=") for input_value in (value, encoded): items = decode_download_items(input_value) assert len(items) == 1 assert items[0].title == "Episode 1" assert items[0].code == 17 def test_download_items_reject_non_http_sources() -> None: payload = _payload() payload[0]["m3u8_url"] = "file:///etc/passwd" with pytest.raises(ValidationError, match="HTTP"): decode_download_items(json.dumps(payload)) def test_template_proxy_and_root_rules(tmp_path) -> None: assert resolve_default_title(DATE_TEMPLATE, datetime(2026, 9, 26, 14, 30, 5)) == "2026-09-26_14-30-05" assert resolve_default_title("YYYY-MM-DD", date(2026, 9, 26)) == "2026-09-26" assert resolve_default_title("weekly") == "weekly" assert validate_proxy("") == "" assert validate_proxy("http://127.0.0.1:7890") == "http://127.0.0.1:7890" assert validate_proxy(DEFAULT_PROXY) == DEFAULT_PROXY assert validate_max_active_downloads("2") == 2 assert validate_max_active_downloads(MAX_ACTIVE_DOWNLOADS) == MAX_ACTIVE_DOWNLOADS with pytest.raises(ValidationError, match="Authenticated"): validate_proxy("http://name:secret@127.0.0.1:7890") with pytest.raises(ValidationError, match="between"): validate_max_active_downloads(MAX_ACTIVE_DOWNLOADS + 1) child = tmp_path / "child" child.mkdir() assert ensure_within_root(child, tmp_path) == child.resolve() with pytest.raises(ValidationError, match="inside"): ensure_within_root(tmp_path.parent, tmp_path)