Files
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

183 lines
5.8 KiB
Python

"""Unit tests for cookie management (no network required)."""
import time
import pytest
from xhs_cli.cookies import (
NOTE_CONTEXT_TTL_SECONDS,
cache_note_context,
clear_cookies,
cookies_to_string,
get_cached_note_context,
get_cached_xsec_token,
get_cookies,
get_index_cache_path,
get_note_by_index,
get_token_cache_path,
load_saved_cookies,
load_token_cache,
save_cookies,
save_note_index,
)
@pytest.fixture
def tmp_config_dir(tmp_path, monkeypatch):
"""Override config dir to use temp directory."""
monkeypatch.setattr("xhs_cli.cookies.get_config_dir", lambda: tmp_path)
monkeypatch.setattr("xhs_cli.cookies.get_cookie_path", lambda: tmp_path / "cookies.json")
monkeypatch.setattr("xhs_cli.cookies._TOKEN_CACHE_MEMORY", None)
monkeypatch.setattr("xhs_cli.cookies._TOKEN_CACHE_PATH", None)
return tmp_path
class TestSaveCookies:
def test_save_and_load(self, tmp_config_dir):
cookies = {"a1": "test_value", "web_session": "sess123"}
save_cookies(cookies)
loaded = load_saved_cookies()
assert loaded is not None
assert loaded["a1"] == "test_value"
assert loaded["web_session"] == "sess123"
def test_file_permissions(self, tmp_config_dir):
cookies = {"a1": "test"}
save_cookies(cookies)
cookie_file = tmp_config_dir / "cookies.json"
stat = cookie_file.stat()
assert stat.st_mode & 0o777 == 0o600
class TestLoadSavedCookies:
def test_no_file(self, tmp_config_dir):
assert load_saved_cookies() is None
def test_invalid_json(self, tmp_config_dir):
(tmp_config_dir / "cookies.json").write_text("not json")
assert load_saved_cookies() is None
def test_missing_a1(self, tmp_config_dir):
(tmp_config_dir / "cookies.json").write_text('{"web_session": "x"}')
assert load_saved_cookies() is None
class TestClearCookies:
def test_clear_existing(self, tmp_config_dir):
save_cookies({"a1": "test"})
clear_cookies()
assert load_saved_cookies() is None
def test_clear_nonexistent(self, tmp_config_dir):
# Should not raise
clear_cookies()
class TestCookiesToString:
def test_format(self):
result = cookies_to_string({"a1": "v1", "web_session": "v2"})
assert "a1=v1" in result
assert "web_session=v2" in result
assert "; " in result
class TestGetCookies:
def test_prefers_saved_cookies_by_default(self, monkeypatch):
monkeypatch.setattr("xhs_cli.cookies.load_saved_cookies", lambda: {"a1": "saved"})
monkeypatch.setattr(
"xhs_cli.cookies.extract_browser_cookies",
lambda source: ("chrome", {"a1": "fresh"}),
)
browser, cookies = get_cookies("chrome")
assert browser == "saved"
assert cookies == {"a1": "saved"}
def test_force_refresh_bypasses_saved_cookies(self, monkeypatch):
monkeypatch.setattr("xhs_cli.cookies.load_saved_cookies", lambda: {"a1": "saved"})
monkeypatch.setattr(
"xhs_cli.cookies.extract_browser_cookies",
lambda source: ("chrome", {"a1": "fresh"}),
)
saved = []
monkeypatch.setattr("xhs_cli.cookies.save_cookies", lambda cookies: saved.append(cookies))
browser, cookies = get_cookies("chrome", force_refresh=True)
assert browser == "chrome"
assert cookies == {"a1": "fresh"}
assert saved == [{"a1": "fresh"}]
class TestNoteContextCache:
def test_cache_persists_token_and_source(self, tmp_config_dir):
cache_note_context("note-1", "token-1", "pc_search", context="search")
assert get_cached_xsec_token("note-1") == "token-1"
context = get_cached_note_context("note-1")
assert context["token"] == "token-1"
assert context["source"] == "pc_search"
assert context["context"] == "search"
def test_load_token_cache_keeps_legacy_entries_compatible(self, tmp_config_dir):
get_token_cache_path().write_text('{"note-1":"legacy-token"}')
cache = load_token_cache()
assert cache["note-1"]["token"] == "legacy-token"
assert cache["note-1"]["source"] == ""
def test_expired_note_context_is_not_returned(self, tmp_config_dir):
stale_ts = time.time() - NOTE_CONTEXT_TTL_SECONDS - 10
get_token_cache_path().write_text(
f'{{"note-1":{{"token":"stale-token","source":"pc_search","ts":{stale_ts}}}}}'
)
assert get_cached_note_context("note-1") == {}
class TestNoteIndexCache:
def test_save_and_resolve_index_with_source(self, tmp_config_dir):
save_note_index([
{
"note_id": "note-1",
"xsec_token": "token-1",
"xsec_source": "pc_search",
}
])
assert get_note_by_index(1) == {
"note_id": "note-1",
"xsec_token": "token-1",
"xsec_source": "pc_search",
}
def test_save_empty_index_clears_previous_entries(self, tmp_config_dir):
save_note_index([
{
"note_id": "note-1",
"xsec_token": "token-1",
"xsec_source": "pc_search",
}
])
save_note_index([])
assert get_note_by_index(1) is None
assert get_index_cache_path().read_text() == "[]"
def test_index_file_permissions(self, tmp_config_dir):
save_note_index([{"note_id": "note-1", "xsec_token": "", "xsec_source": ""}])
stat = get_index_cache_path().stat()
assert stat.st_mode & 0o777 == 0o600
def test_index_normalizes_missing_optional_fields(self, tmp_config_dir):
get_index_cache_path().write_text('[{"note_id":"note-1"}]')
assert get_note_by_index(1) == {
"note_id": "note-1",
"xsec_token": "",
"xsec_source": "",
}