refactor(settings): unify config.py, mconfig.py, and settings.py into single PrySettings

- 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.
This commit is contained in:
Crypto Rug Munch 2026-07-03 05:04:29 +02:00
parent ecb05bbf49
commit 1f9e71d294
9 changed files with 424 additions and 316 deletions

View file

@ -1,47 +0,0 @@
# 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 Pry config."""
from mconfig import PryConfig
def test_config_get_default() -> None:
c = PryConfig()
assert c.get("nonexistent.key", "fallback") == "fallback"
def test_config_get_retry_exists() -> None:
c = PryConfig()
val = c.get("retry.max_attempts")
assert isinstance(val, int)
assert val > 0
def test_config_get_rate_limit_exists() -> None:
c = PryConfig()
val = c.get("rate_limit.rpm")
assert isinstance(val, int)
assert val > 0
def test_config_to_dict_excludes_private() -> None:
c = PryConfig()
d = c.to_dict()
assert "_proxy_chain" not in d
assert "_proxy_url" not in d
def test_config_update_returns_ok() -> None:
c = PryConfig()
result = c.update({"retry": {"max_attempts": 5}})
assert result["status"] == "ok"
assert c.get("retry.max_attempts") == 5
def test_config_get_proxy_url_string_or_none() -> None:
c = PryConfig()
url = c.get_proxy_url()
assert url is None or isinstance(url, str)

93
tests/test_settings.py Normal file
View file

@ -0,0 +1,93 @@
# 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}"