mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-11-13 12:35:05 +00:00
perf: lazy-load services and proxy catalogues, faster namespace strip
load_xml strips namespaces without QName; services import on first use and proxy providers fetch their catalogue on first query; the font scan is skipped when no subtitle names a font and is cached per process. Serve CPU -40% and track requests -28% on 20 MB manifests; CLI startup 2.7 s -> 1.2 s; --list 27-46% faster across six services tested.
This commit is contained in:
@@ -4,6 +4,7 @@ by a bare query and reported by a query that names it."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
@@ -15,17 +16,14 @@ from unshackle.core.proxies.resolve import Unavailable, find_provider, load_prox
|
||||
pytestmark = pytest.mark.unit
|
||||
|
||||
CREDENTIALS = {"username": "a" * 24, "password": "b" * 24}
|
||||
LOGIN_CREDENTIALS = {"username": "someone@example.com", "password": "hunter2"}
|
||||
OTHERS = ("basic", "expressvpn", "protonvpn", "windscribevpn", "gluetun", "hola", "controld")
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def nordvpn_down_surfshark_up(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def refuse() -> list[dict]:
|
||||
raise requests.ConnectionError("no route to api.nordvpn.com")
|
||||
|
||||
def nordvpn_misconfigured_surfshark_up(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
clusters = [{"countryCode": "US", "connectionName": "us-dal.prod.surfshark.com", "location": "Dallas"}]
|
||||
monkeypatch.setattr(config, "proxy_providers", {"nordvpn": CREDENTIALS, "surfsharkvpn": CREDENTIALS})
|
||||
monkeypatch.setattr(NordVPN, "get_countries", staticmethod(refuse))
|
||||
monkeypatch.setattr(config, "proxy_providers", {"nordvpn": LOGIN_CREDENTIALS, "surfsharkvpn": CREDENTIALS})
|
||||
monkeypatch.setattr(SurfsharkVPN, "get_countries", staticmethod(lambda: clusters))
|
||||
|
||||
|
||||
@@ -49,7 +47,7 @@ def test_query_naming_another_provider_still_works() -> None:
|
||||
|
||||
@pytest.mark.parametrize("name", ["nordvpn", "NordVPN"])
|
||||
def test_query_naming_the_failed_provider_reports_why(name: str) -> None:
|
||||
with pytest.raises(ValueError, match="NordVPN could not load: no route to api.nordvpn.com"):
|
||||
with pytest.raises(ValueError, match="NordVPN could not load: NordVPN needs its Service Credentials"):
|
||||
resolve_proxy(f"{name}:us", load_proxy_providers(OTHERS))
|
||||
|
||||
|
||||
@@ -59,10 +57,29 @@ def test_query_naming_an_unknown_provider_lists_the_loaded_ones() -> None:
|
||||
|
||||
|
||||
def test_raise_errors_raises_the_load_failure() -> None:
|
||||
with pytest.raises(requests.ConnectionError):
|
||||
with pytest.raises(ValueError, match="Service Credentials"):
|
||||
load_proxy_providers(OTHERS, raise_errors=True)
|
||||
|
||||
|
||||
def test_catalogue_is_fetched_on_first_use_and_a_failed_fetch_fails_only_its_query(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
calls = []
|
||||
|
||||
def refuse() -> list[dict]:
|
||||
calls.append(1)
|
||||
raise requests.ConnectionError("no route to api.nordvpn.com")
|
||||
|
||||
monkeypatch.setattr(config, "proxy_providers", {"nordvpn": CREDENTIALS, "surfsharkvpn": CREDENTIALS})
|
||||
monkeypatch.setattr(NordVPN, "get_countries", staticmethod(refuse))
|
||||
nordvpn, surfshark = load_proxy_providers(OTHERS)
|
||||
assert isinstance(nordvpn, NordVPN) and not calls
|
||||
|
||||
assert resolve_proxy("us", [nordvpn, surfshark]).endswith("@us-dal.prod.surfshark.com:443")
|
||||
with pytest.raises(ValueError, match="nordvpn failed for us: no route to api.nordvpn.com"):
|
||||
resolve_proxy("nordvpn:us", [nordvpn, surfshark])
|
||||
|
||||
|
||||
def test_bare_query_order_follows_config_key_order_with_controld_last(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
from unshackle.core.proxies import ControlD
|
||||
|
||||
@@ -88,3 +105,71 @@ def test_rest_path_loads_windscribe_but_not_gluetun(monkeypatch: pytest.MonkeyPa
|
||||
assert any(isinstance(p, WindscribeVPN) for p in providers)
|
||||
assert not any(isinstance(p, Gluetun) or getattr(p, "name", None) == "Gluetun" for p in providers)
|
||||
assert resolve_proxy("windscribevpn:us", providers).endswith("@us-central-1.example:443")
|
||||
|
||||
|
||||
def build_controld() -> Any:
|
||||
from unshackle.core.proxies import ControlD
|
||||
|
||||
return ControlD(token="t")
|
||||
|
||||
|
||||
def build_hola(monkeypatch: pytest.MonkeyPatch) -> Any:
|
||||
from unshackle.core import binaries
|
||||
from unshackle.core.proxies import Hola
|
||||
|
||||
monkeypatch.setattr(binaries, "HolaProxy", "hola-proxy")
|
||||
return Hola()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("name", "fetch", "attr"),
|
||||
[
|
||||
("NordVPN", "get_countries", "countries"),
|
||||
("WindscribeVPN", "get_countries", "countries"),
|
||||
("SurfsharkVPN", "get_countries", "countries"),
|
||||
("ControlD", "get_locations", "catalogue"),
|
||||
("Hola", "get_countries", "countries"),
|
||||
],
|
||||
)
|
||||
def test_construction_makes_no_request_and_the_first_read_fetches_once(
|
||||
name: str, fetch: str, attr: str, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
from unshackle.core import proxies
|
||||
|
||||
cls = getattr(proxies, name)
|
||||
calls: list[int] = []
|
||||
|
||||
def counted(*args: Any) -> list[dict]:
|
||||
calls.append(1)
|
||||
return [{"country": "US"}]
|
||||
|
||||
is_static = isinstance(vars(cls)[fetch], staticmethod)
|
||||
monkeypatch.setattr(cls, fetch, staticmethod(counted) if is_static else counted)
|
||||
if name == "ControlD":
|
||||
provider = build_controld()
|
||||
elif name == "Hola":
|
||||
provider = build_hola(monkeypatch)
|
||||
else:
|
||||
provider = cls(**CREDENTIALS)
|
||||
assert not calls
|
||||
|
||||
assert getattr(provider, attr) == [{"country": "US"}]
|
||||
getattr(provider, attr)
|
||||
assert calls == [1]
|
||||
|
||||
|
||||
def test_health_probe_reads_each_proxy_catalogue(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
from unshackle.core.api.handlers import run_health_checks
|
||||
|
||||
calls: list[int] = []
|
||||
|
||||
def countries() -> list[dict]:
|
||||
calls.append(1)
|
||||
return [{"serverCount": 3}]
|
||||
|
||||
monkeypatch.setattr(config, "key_vaults", [])
|
||||
monkeypatch.setattr(config, "proxy_providers", {"nordvpn": CREDENTIALS})
|
||||
monkeypatch.setattr(NordVPN, "get_countries", staticmethod(countries))
|
||||
detail = {c["id"]: c for c in run_health_checks()}["proxies"]["detail"]
|
||||
assert calls == [1]
|
||||
assert "NordVPN (1 Country (3 Servers))" in detail
|
||||
|
||||
Reference in New Issue
Block a user