"""Suite-wide test setup.""" from __future__ import annotations import importlib import socket import socketserver from pathlib import Path from typing import Any, Callable, Optional import pytest from unshackle.core import binaries from unshackle.core.config import config from unshackle.core.proxies.proxy import CATALOGUES from unshackle.core.update_checker import UpdateChecker requests_downloader = importlib.import_module("unshackle.core.downloaders.requests") # shutdown() blocks until the serve loop polls again, and the 0.5s default is paid by every # test server at teardown. The default changes for the whole process, servers in core included. socketserver.BaseServer.serve_forever.__defaults__ = (0.01,) @pytest.fixture(autouse=True) def loopback_only(request: pytest.FixtureRequest, monkeypatch: pytest.MonkeyPatch) -> None: """Fail a name lookup for any host but loopback, so a stray request cannot hang a unit test.""" if request.node.get_closest_marker("live") or request.node.get_closest_marker("slow"): return resolve = socket.getaddrinfo def offline(host: Any, *args: Any, **kwargs: Any) -> Any: if host not in (None, "", "localhost", "127.0.0.1", "::1"): raise socket.gaierror(socket.EAI_NONAME, f"unit tests are offline: {host!r} is not loopback") return resolve(host, *args, **kwargs) monkeypatch.setattr(socket, "getaddrinfo", offline) @pytest.fixture(autouse=True) def no_update_check(monkeypatch: pytest.MonkeyPatch) -> None: """Keep the release lookup of the health route off the network.""" monkeypatch.setattr(UpdateChecker, "check_for_updates_sync", lambda *args, **kwargs: None) @pytest.fixture(autouse=True) def fresh_proxy_catalogues() -> None: """Fetch each proxy provider catalogue again, so one test's stub never answers another test.""" CATALOGUES.clear() @pytest.fixture(autouse=True) def default_muxing(monkeypatch: pytest.MonkeyPatch) -> None: """Mux with the default options, whatever the muxing block of the local unshackle.yaml sets.""" monkeypatch.setattr(config, "muxing", {}) @pytest.fixture(autouse=True) def default_codec_names(monkeypatch: pytest.MonkeyPatch) -> None: """Name codecs with the built-in labels, whatever codec_names the local unshackle.yaml sets.""" monkeypatch.setattr(config, "codec_names", {}) @pytest.fixture(autouse=True) def no_speed_limit(monkeypatch: pytest.MonkeyPatch) -> None: """Start with no speed limit, so a limit that an earlier test locked cannot block a later one.""" monkeypatch.setattr(requests_downloader, "speed_limiter", None) monkeypatch.setattr(requests_downloader, "speed_limit_locked", False) @pytest.fixture def mux_args(monkeypatch: pytest.MonkeyPatch) -> Callable[..., list[str]]: """Run Tracks.mux up to mkvmerge and return its arguments; ``ids`` is what the track ID probe reports.""" class Stop(Exception): pass def run(tracks: Any, ids: Optional[dict[str, int]] = None, **kwargs: Any) -> list[str]: captured: list[list[str]] = [] def fake_popen(args: list[str], **_: object) -> None: captured.append(args) raise Stop monkeypatch.setattr("unshackle.core.tracks.tracks.subprocess.Popen", fake_popen) monkeypatch.setattr("unshackle.core.tracks.tracks.file_track_ids", lambda path: ids or {"video": 0}) monkeypatch.setattr(binaries, "MKVToolNix", Path("mkvmerge")) with pytest.raises(Stop): tracks.mux("Title", delete=False, **kwargs) return captured[0] return run