feat(search): add ANONYMOUS_SEARCH to skip the login for a service

A service that sets the flag gets no cookie load, no authenticate() call and no cookie save on a search, on the CLI and in the REST search handler. /api/services reports it as anonymous_search.
This commit is contained in:
imSp4rky
2026-10-10 13:21:14 -06:00
parent 0cded6427f
commit 0a7c76561c
12 changed files with 121 additions and 13 deletions
+58
View File
@@ -0,0 +1,58 @@
"""The search command skips the login for a service that sets ANONYMOUS_SEARCH."""
from types import SimpleNamespace
import pytest
from unshackle.commands import search as search_command
from unshackle.core.search_result import SearchResult
class _SearchService:
ANONYMOUS_SEARCH = False
def __init__(self):
self.calls = []
self.session = SimpleNamespace(cookies=object())
def authenticate(self, cookies, credential):
self.calls.append("authenticate")
def search(self):
yield SearchResult(id_="abc", title="A Title")
@pytest.fixture
def login_calls(monkeypatch, tmp_path):
calls = []
def record(name, value):
def call(*args):
calls.append(name)
return value
return call
dl = search_command.dl
monkeypatch.setattr(dl, "get_cookie_jar", record("get_cookie_jar", None))
monkeypatch.setattr(dl, "get_credentials", record("get_credentials", None))
monkeypatch.setattr(dl, "get_cookie_path", record("get_cookie_path", tmp_path / "cookies.txt"))
monkeypatch.setattr(dl, "save_cookies", record("save_cookies", None))
return calls
def test_search_authenticates_and_saves_cookies_by_default(login_calls):
service = _SearchService()
search_command.result(service)
assert service.calls == ["authenticate"]
assert login_calls == ["get_cookie_jar", "get_credentials", "get_cookie_path", "save_cookies"]
def test_anonymous_search_touches_no_login_material_and_no_cookie_file(login_calls):
service = _SearchService()
service.ANONYMOUS_SEARCH = True
search_command.result(service)
assert service.calls == []
assert login_calls == []
+21
View File
@@ -144,6 +144,27 @@ async def test_server_account_uses_its_own_cache_and_returns_none(seen, tmp_path
assert not (tmp_path / "_requests").exists()
@pytest.mark.asyncio
async def test_anonymous_search_skips_the_login_and_keeps_the_account_cache(seen, tmp_path, monkeypatch):
def search(self):
seen["search_cache_dir"] = self.cache.get("tokens").path.parent
return iter(())
def no_login_material(*args):
raise AssertionError("an anonymous search must not read login material")
handlers.Services.load("EXAMPLE").ANONYMOUS_SEARCH = True
monkeypatch.setattr(_CacheService, "search", search)
monkeypatch.setattr(handlers, "resolve_server_account", lambda *args: "acct")
monkeypatch.setattr(handlers, "server_login_material", no_login_material)
response = await handlers.search_handler({"service": "EXAMPLE", "query": "q", **CREDENTIALS})
assert json.loads(response.body)["results"] == []
assert "seeded" not in seen
assert seen["parent_params"]["cookies_supplied"] is False
assert seen["search_cache_dir"] == tmp_path / "_accounts" / "EXAMPLE" / "acct"
@pytest.mark.asyncio
async def test_full_mode_keeps_the_server_cache(seen, tmp_path, monkeypatch):
monkeypatch.setattr(stats, "mode", "full")