- Merge all configuration into settings.py with PRY_* env var prefix. - Add legacy env aliases (PROXY_URL, TOR_ENABLED, MAX_RETRIES, etc.) for migration. - Load and persist runtime overrides from JSON config file (default /app/config.json). - Update scraper.py, deps.py, routers/config.py to use unified settings. - Add resolved_proxy_chain and resolved_proxy_url properties. - Replace tests/test_mconfig.py with tests/test_settings.py (9 tests). - Update .env.example with new PRY_* options. - All 500 tests pass; ruff clean.
93 lines
3.3 KiB
Python
93 lines
3.3 KiB
Python
# SPDX-License-Identifier: MIT
|
|
# Copyright (c) 2026 Rug Munch Media LLC
|
|
#
|
|
# Part of Pry — https://git.rugmunch.io/RugMunchMedia/pryscraper
|
|
# Licensed under MIT. See LICENSE.
|
|
"""Tests for unified Pry settings."""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from settings import _NESTED_KEY_MAP, PrySettings
|
|
|
|
|
|
@pytest.fixture
|
|
def isolated_settings(tmp_path: Path) -> PrySettings:
|
|
"""Return a fresh settings instance with a temp config file."""
|
|
config_file = tmp_path / "config.json"
|
|
return PrySettings(config_file=config_file)
|
|
|
|
|
|
def test_settings_get_default(isolated_settings: PrySettings) -> None:
|
|
assert isolated_settings.max_retries == 3
|
|
assert isolated_settings.rate_limit_rpm == 120
|
|
|
|
|
|
def test_settings_proxy_resolution(isolated_settings: PrySettings) -> None:
|
|
assert isolated_settings.resolved_proxy_url is None
|
|
isolated_settings.proxy_enabled = True
|
|
isolated_settings.proxy_url = "http://proxy.example:8080"
|
|
assert isolated_settings.resolved_proxy_url == "http://proxy.example:8080"
|
|
|
|
|
|
def test_settings_tor_chain(isolated_settings: PrySettings) -> None:
|
|
isolated_settings.tor_enabled = True
|
|
isolated_settings.proxy_enabled = True
|
|
isolated_settings.proxy_url = "http://proxy.example:8080"
|
|
chain = isolated_settings.resolved_proxy_chain
|
|
assert len(chain) == 2
|
|
assert chain[0].startswith("socks5://")
|
|
|
|
|
|
def test_settings_to_api_dict_excludes_private(isolated_settings: PrySettings) -> None:
|
|
data = isolated_settings.to_api_dict()
|
|
assert "_proxy_chain" not in data
|
|
assert "proxy" in data
|
|
assert isinstance(data["proxy"]["enabled"], bool)
|
|
|
|
|
|
def test_settings_update_and_persist(isolated_settings: PrySettings) -> None:
|
|
result = isolated_settings.update({"retry": {"max_attempts": 5}})
|
|
assert result["status"] == "ok"
|
|
assert isolated_settings.max_retries == 5
|
|
assert isolated_settings.config_file.exists()
|
|
saved = json.loads(isolated_settings.config_file.read_text(encoding="utf-8"))
|
|
assert saved["retry"]["max_attempts"] == 5
|
|
|
|
|
|
def test_settings_load_config_file(tmp_path: Path) -> None:
|
|
config_file = tmp_path / "config.json"
|
|
config_file.write_text(
|
|
json.dumps({"retry": {"max_attempts": 7}, "tor": {"enabled": True}}),
|
|
encoding="utf-8",
|
|
)
|
|
s = PrySettings(config_file=config_file)
|
|
assert s.max_retries == 7
|
|
assert s.tor_enabled is True
|
|
|
|
|
|
def test_settings_legacy_env_vars(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
|
monkeypatch.setenv("PROXY_URL", "http://legacy-proxy:8080")
|
|
monkeypatch.setenv("TOR_ENABLED", "true")
|
|
monkeypatch.setenv("MAX_RETRIES", "9")
|
|
config_file = tmp_path / "config.json"
|
|
s = PrySettings(config_file=config_file)
|
|
assert s.proxy_url == "http://legacy-proxy:8080"
|
|
assert s.tor_enabled is True
|
|
assert s.max_retries == 9
|
|
|
|
|
|
def test_settings_enable_tor(isolated_settings: PrySettings) -> None:
|
|
result = isolated_settings.enable_tor()
|
|
assert result["status"] == "ok"
|
|
assert isolated_settings.tor_enabled is True
|
|
assert isolated_settings.proxy_enabled is True
|
|
assert isolated_settings.proxy_url == "socks5://tor:9050"
|
|
|
|
|
|
def test_nested_key_map_complete() -> None:
|
|
"""Ensure every API-facing nested key maps to a settings attribute."""
|
|
for attr in _NESTED_KEY_MAP.values():
|
|
assert attr in PrySettings.model_fields, f"PrySettings missing {attr}"
|