mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-11-12 20:15:10 +00:00
The client dials the server's targets through the proxy (http/https CONNECT or SOCKS5, with login), with the same target checks and no direct fallback. tunnel-test takes --proxy too.
118 lines
4.2 KiB
Python
118 lines
4.2 KiB
Python
"""RemoteService.search: the body ``search --remote`` posts, and the results it yields."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from types import SimpleNamespace
|
|
from typing import Any
|
|
|
|
import pytest
|
|
|
|
from unshackle.core import remote_service
|
|
from unshackle.core.remote_service import RemoteService
|
|
|
|
pytestmark = pytest.mark.unit
|
|
|
|
|
|
class FakeClient:
|
|
server_url = "https://server.example"
|
|
|
|
def __init__(self) -> None:
|
|
self.sent: dict[str, Any] = {}
|
|
self.tunnel: Any = None
|
|
|
|
def post(self, endpoint: str, data: dict[str, Any]) -> dict[str, Any]:
|
|
self.sent = {"endpoint": endpoint, **data}
|
|
return {"results": [{"id": "t1", "title": "Show", "label": "2019"}], "cache": {"token": "x"}}
|
|
|
|
|
|
def make_service(params: dict[str, Any], server_accounts: Any = None) -> tuple[RemoteService, FakeClient, list]:
|
|
svc = RemoteService.__new__(RemoteService)
|
|
client = FakeClient()
|
|
saved: list = []
|
|
svc.service_tag = "TEST"
|
|
svc.title_id = "query"
|
|
svc.client = client # type: ignore[assignment]
|
|
svc.ctx = SimpleNamespace(parent=SimpleNamespace(params=params), obj=None) # type: ignore[assignment]
|
|
svc._server_accounts = server_accounts
|
|
svc._anonymous_search = False
|
|
svc.load_cache_files = lambda profile=None: {"token": "old"} # type: ignore[method-assign]
|
|
svc.save_returned_cache = lambda data, profile=None: saved.append(data) # type: ignore[method-assign]
|
|
return svc, client, saved
|
|
|
|
|
|
def test_search_sends_client_login_and_yields_results(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setattr(remote_service, "load_credentials_for_transport", lambda tag, profile: {"username": "u"})
|
|
monkeypatch.setattr(remote_service, "load_cookies_for_transport", lambda tag, profile: None)
|
|
svc, client, saved = make_service({"profile": "p1", "no_proxy": True})
|
|
|
|
results = list(svc.search())
|
|
|
|
assert client.sent == {
|
|
"endpoint": "/api/search",
|
|
"service": "TEST",
|
|
"query": "query",
|
|
"profile": "p1",
|
|
"credentials": {"username": "u"},
|
|
"cache": {"token": "old"},
|
|
"no_proxy": True,
|
|
}
|
|
assert [(r.id, r.title, r.label) for r in results] == [("t1", "Show", "2019")]
|
|
assert saved == [{"token": "x"}]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("server_accounts", "anonymous"), [({"global": True}, False), (None, True)], ids=["server-account", "anonymous"]
|
|
)
|
|
def test_search_sends_no_login(server_accounts: Any, anonymous: bool) -> None:
|
|
svc, client, _ = make_service({}, server_accounts)
|
|
svc._anonymous_search = anonymous
|
|
|
|
list(svc.search())
|
|
|
|
assert client.sent == {"endpoint": "/api/search", "service": "TEST", "query": "query"}
|
|
|
|
|
|
@pytest.mark.parametrize("proxy", [None, "http://127.0.0.1:8080"])
|
|
def test_local_proxy_sends_the_server_a_tunnel(monkeypatch: pytest.MonkeyPatch, proxy: str | None) -> None:
|
|
class FakeTunnel:
|
|
def __init__(self, server_url: str, headers: dict, exit_proxy: str | None = None) -> None:
|
|
self.id = "abc"
|
|
self.exit_proxy = exit_proxy
|
|
|
|
def start(self) -> str:
|
|
return self.id
|
|
|
|
def stop(self) -> None:
|
|
self.stopped = True
|
|
|
|
monkeypatch.setattr(remote_service, "TunnelClient", FakeTunnel)
|
|
svc, client, _ = make_service({"local_proxy": True, "proxy": proxy})
|
|
svc.log = logging.getLogger("test")
|
|
client.session = SimpleNamespace(headers={"X-Secret-Key": "k"}) # type: ignore[attr-defined]
|
|
|
|
list(svc.search())
|
|
|
|
assert client.sent["proxy"] == "tunnel:abc"
|
|
assert client.tunnel.exit_proxy == proxy
|
|
assert client.tunnel.stopped
|
|
|
|
|
|
def test_a_loopback_proxy_points_to_local_proxy(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
import click
|
|
|
|
monkeypatch.setattr(remote_service, "resolve_remote_proxy_arg", lambda arg, providers=None: arg)
|
|
svc, _, _ = make_service({"proxy": "http://127.0.0.1:8888"}, {"global": True})
|
|
|
|
with pytest.raises(click.ClickException, match="without --local-proxy"):
|
|
list(svc.search())
|
|
|
|
|
|
def test_local_proxy_is_refused_for_a_server_account_service() -> None:
|
|
import click
|
|
|
|
svc, _, _ = make_service({"local_proxy": True}, {"global": True})
|
|
|
|
with pytest.raises(click.ClickException, match="its own account"):
|
|
list(svc.search())
|