3803f3df4b
Setup wizard with consent-first cookie extraction (Chrome/Firefox/Safari), yt-dlp auto-install, ScrapeCreators push, quality scoring (5 core sources), status banner redesign, honest Reddit labeling, inline YouTube transcripts, Exa free web search, Reddit public fallback, and post-research quality nudge. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
186 lines
7.5 KiB
Python
186 lines
7.5 KiB
Python
"""Tests for browser cookie extraction integration in env.py."""
|
|
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
import pytest
|
|
|
|
from scripts.lib.env import extract_browser_credentials, COOKIE_DOMAINS
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def _base_config(**overrides):
|
|
"""Return a minimal config dict with common defaults."""
|
|
cfg = {
|
|
"AUTH_TOKEN": None,
|
|
"CT0": None,
|
|
"TRUTHSOCIAL_TOKEN": None,
|
|
"FROM_BROWSER": None,
|
|
"SETUP_COMPLETE": None,
|
|
}
|
|
cfg.update(overrides)
|
|
return cfg
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestExtractBrowserCredentials:
|
|
"""Unit tests for extract_browser_credentials()."""
|
|
|
|
@patch("scripts.lib.cookie_extract.extract_cookies_with_source")
|
|
def test_auto_with_setup_complete_populates_credentials(self, mock_extract):
|
|
"""FROM_BROWSER=auto, SETUP_COMPLETE=true, mock returns valid cookies
|
|
-> config now contains AUTH_TOKEN and CT0."""
|
|
mock_extract.return_value = ({"auth_token": "tok123", "ct0": "ct0val"}, "chrome")
|
|
|
|
config = _base_config(FROM_BROWSER="auto", SETUP_COMPLETE="true")
|
|
result = extract_browser_credentials(config)
|
|
|
|
assert result["AUTH_TOKEN"] == "tok123"
|
|
assert result["CT0"] == "ct0val"
|
|
# Should have been called for x domain
|
|
mock_extract.assert_any_call("auto", ".x.com", ["auth_token", "ct0"])
|
|
|
|
@patch("scripts.lib.cookie_extract.extract_cookies_with_source")
|
|
def test_explicit_auth_token_skips_x_extraction(self, mock_extract):
|
|
"""Config already has AUTH_TOKEN and CT0 from env var
|
|
-> cookie extraction skipped for X (explicit takes priority)."""
|
|
# Return None for any non-X domains that still get checked (e.g. Truth Social)
|
|
mock_extract.return_value = None
|
|
config = _base_config(
|
|
AUTH_TOKEN="explicit_token",
|
|
CT0="explicit_ct0",
|
|
FROM_BROWSER="auto",
|
|
SETUP_COMPLETE="true",
|
|
)
|
|
result = extract_browser_credentials(config)
|
|
|
|
# X cookies should not appear in result (already set)
|
|
assert "AUTH_TOKEN" not in result
|
|
assert "CT0" not in result
|
|
# extract_cookies should NOT have been called for .x.com
|
|
for call in mock_extract.call_args_list:
|
|
assert call[0][1] != ".x.com", "Should not extract cookies for X when credentials already set"
|
|
|
|
@patch("scripts.lib.cookie_extract.extract_cookies_with_source")
|
|
def test_from_browser_off_skips_all(self, mock_extract):
|
|
"""FROM_BROWSER=off -> no cookie extraction attempted."""
|
|
config = _base_config(FROM_BROWSER="off", SETUP_COMPLETE="true")
|
|
result = extract_browser_credentials(config)
|
|
|
|
assert result == {}
|
|
mock_extract.assert_not_called()
|
|
|
|
@patch("scripts.lib.cookie_extract.extract_cookies_with_source")
|
|
def test_no_setup_complete_no_from_browser_defaults_off(self, mock_extract):
|
|
"""FROM_BROWSER not set and SETUP_COMPLETE not set
|
|
-> no extraction (wizard hasn't run)."""
|
|
config = _base_config() # both None
|
|
result = extract_browser_credentials(config)
|
|
|
|
assert result == {}
|
|
mock_extract.assert_not_called()
|
|
|
|
@patch("scripts.lib.cookie_extract.extract_cookies_with_source")
|
|
def test_setup_complete_no_from_browser_defaults_auto(self, mock_extract):
|
|
"""SETUP_COMPLETE is set but FROM_BROWSER is not
|
|
-> defaults to 'auto'."""
|
|
mock_extract.return_value = ({"auth_token": "found", "ct0": "found_ct0"}, "firefox")
|
|
|
|
config = _base_config(SETUP_COMPLETE="true") # FROM_BROWSER=None
|
|
result = extract_browser_credentials(config)
|
|
|
|
assert result["AUTH_TOKEN"] == "found"
|
|
# extract_cookies should have been called with 'auto'
|
|
mock_extract.assert_any_call("auto", ".x.com", ["auth_token", "ct0"])
|
|
|
|
@patch("scripts.lib.cookie_extract.extract_cookies_with_source")
|
|
def test_from_browser_firefox_only(self, mock_extract):
|
|
"""FROM_BROWSER=firefox -> only Firefox extraction attempted."""
|
|
mock_extract.return_value = ({"auth_token": "ff_tok", "ct0": "ff_ct0"}, "firefox")
|
|
|
|
config = _base_config(FROM_BROWSER="firefox", SETUP_COMPLETE="true")
|
|
result = extract_browser_credentials(config)
|
|
|
|
assert result["AUTH_TOKEN"] == "ff_tok"
|
|
# All calls should use 'firefox' as the browser arg
|
|
for call in mock_extract.call_args_list:
|
|
assert call[0][0] == "firefox"
|
|
|
|
@patch("scripts.lib.cookie_extract.extract_cookies_with_source")
|
|
def test_extraction_returns_none_config_unchanged(self, mock_extract):
|
|
"""Cookie extraction returns None for X -> config unchanged for AUTH_TOKEN."""
|
|
mock_extract.return_value = None
|
|
|
|
config = _base_config(FROM_BROWSER="auto", SETUP_COMPLETE="true")
|
|
result = extract_browser_credentials(config)
|
|
|
|
assert "AUTH_TOKEN" not in result
|
|
assert "CT0" not in result
|
|
|
|
@patch("scripts.lib.cookie_extract.extract_cookies_with_source")
|
|
def test_extraction_raises_exception_config_unchanged(self, mock_extract):
|
|
"""Cookie extraction raises exception -> caught, config unchanged."""
|
|
mock_extract.side_effect = RuntimeError("database locked")
|
|
|
|
config = _base_config(FROM_BROWSER="auto", SETUP_COMPLETE="true")
|
|
result = extract_browser_credentials(config)
|
|
|
|
# Should not raise, and no credentials populated
|
|
assert "AUTH_TOKEN" not in result
|
|
assert "CT0" not in result
|
|
|
|
@patch("scripts.lib.cookie_extract.extract_cookies_with_source")
|
|
def test_partial_credentials_only_fills_missing(self, mock_extract):
|
|
"""If AUTH_TOKEN is set but CT0 is not, only CT0 gets filled."""
|
|
mock_extract.return_value = ({"auth_token": "cookie_tok", "ct0": "cookie_ct0"}, "chrome")
|
|
|
|
config = _base_config(
|
|
AUTH_TOKEN="explicit",
|
|
CT0=None,
|
|
FROM_BROWSER="auto",
|
|
SETUP_COMPLETE="true",
|
|
)
|
|
result = extract_browser_credentials(config)
|
|
|
|
# AUTH_TOKEN already set, should not be overridden
|
|
assert "AUTH_TOKEN" not in result
|
|
# CT0 was missing, should be filled
|
|
assert result["CT0"] == "cookie_ct0"
|
|
|
|
|
|
class TestGetConfigCookieIntegration:
|
|
"""Integration test: get_config() calls extract_browser_credentials."""
|
|
|
|
@patch("scripts.lib.cookie_extract.extract_cookies_with_source")
|
|
@patch("scripts.lib.env._find_project_env", return_value=None)
|
|
@patch("scripts.lib.env.load_env_file", return_value={})
|
|
@patch("scripts.lib.env.get_openai_auth")
|
|
def test_get_config_injects_cookies(
|
|
self, mock_openai, mock_load, mock_proj, mock_extract
|
|
):
|
|
"""get_config merges browser cookies into the returned config."""
|
|
from scripts.lib.env import get_config, OpenAIAuth
|
|
|
|
mock_openai.return_value = OpenAIAuth(
|
|
token=None, source="none", status="missing",
|
|
account_id=None, codex_auth_file="/fake",
|
|
)
|
|
mock_extract.return_value = ({"auth_token": "browser_tok", "ct0": "browser_ct0"}, "firefox")
|
|
|
|
import os
|
|
env_patch = {
|
|
"SETUP_COMPLETE": "true",
|
|
"FROM_BROWSER": "auto",
|
|
"LAST30DAYS_CONFIG_DIR": "",
|
|
}
|
|
with patch.dict(os.environ, env_patch, clear=False):
|
|
config = get_config()
|
|
|
|
assert config["AUTH_TOKEN"] == "browser_tok"
|
|
assert config["CT0"] == "browser_ct0"
|