pryscraper/tests/test_secrets_backend_integration.py
cryptorugmunch 14a59708ee feat(pry): wire secrets_backend.get_secret() into PrySettings for sensitive fields
PrySettings (settings.py) was reading sensitive values (api_key, *_api_key,
webhook_secret) only from PRY_* env vars. The existing secrets_backend
(used by x402.py and auth.py for jwt_secret) supports gopass as the
default backend, so secrets never needed to live in .env.

This commit:
- Adds PrySettings._apply_secrets_backend() in model_post_init
- For each sensitive field with empty current value, calls
  secrets_backend.get_secret(name) and uses the result
- Priority order: secrets_backend.get_secret > env var > field default
- This means `gopass insert -m pry/api_key` now sets the API key
  without touching .env or environment

Tests added (tests/test_secrets_backend_integration.py):
- test_settings_pulls_from_secrets_backend: confirms gopass fills empty fields
- test_x402_constants_come_from_secrets_backend: confirms x402.py honors gopass
- test_settings_does_not_override_set_env: env beats gopass when both set

Tests updated (tests/conftest.py):
- Session-scope fixture sets PRY_SECRET_BACKEND=env to prevent test runs from
  pulling real gopass secrets (which would cause the auth middleware to
  reject unauthenticated test requests)
- Per-test fixture zeros settings.settings.api_key for in-process tests

Audit item 12. Tests: 623 passed, 1 skipped (pre-existing /ready failure
unrelated to this change).
2026-07-06 11:01:08 +02:00

86 lines
3.1 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.
"""Verify x402 and settings use secrets_backend.get_secret() for sensitive values."""
import os
from unittest.mock import patch
def test_settings_pulls_from_secrets_backend() -> None:
"""PrySettings should populate sensitive fields from secrets_backend.get_secret
when env vars are not set."""
from pydantic import PydanticUserError
# Force a known value from secrets_backend
fake_secret = "fake-secret-from-gopass"
def fake_get_secret(name: str, default: str = "") -> str:
if name == "openrouter_api_key":
return fake_secret
return default
# Clear env so the test is deterministic
env = {k: v for k, v in os.environ.items() if not k.startswith("PRY_")}
env["PRY_OPENROUTER_API_KEY"] = "" # explicitly empty
with patch.dict(os.environ, env, clear=True):
with patch("secrets_backend.get_secret", side_effect=fake_get_secret):
# Re-import fresh
import importlib
from settings import PrySettings
s = PrySettings()
# Sensitive field was empty in env, secrets_backend provided it
assert s.openrouter_api_key == fake_secret, (
f"Expected {fake_secret!r}, got {s.openrouter_api_key!r}"
)
def test_x402_constants_come_from_secrets_backend() -> None:
"""x402.py X402_WALLET and X402_FACILITATOR_URL should be settable
via secrets_backend.get_secret()."""
fake_wallet = "0xABCDEF1234567890"
fake_facilitator = "https://my-facilitator.example.com"
def fake_get_secret(name: str, default: str = "") -> str:
if name == "x402_wallet":
return fake_wallet
if name == "x402_facilitator":
return fake_facilitator
return default
env = {k: v for k, v in os.environ.items() if not k.startswith("PRY_X402_")}
with patch.dict(os.environ, env, clear=True):
with patch("secrets_backend.get_secret", side_effect=fake_get_secret):
# Force re-import of x402 constants
import importlib
import x402
importlib.reload(x402)
assert x402.X402_WALLET == fake_wallet
assert x402.X402_FACILITATOR_URL == fake_facilitator
def test_settings_does_not_override_set_env() -> None:
"""If the env var is already set, secrets_backend must NOT override it."""
fake_secret = "from-gopass"
from_env = "from-env"
def fake_get_secret(name: str, default: str = "") -> str:
if name == "openrouter_api_key":
return fake_secret
return default
env = {k: v for k, v in os.environ.items() if k != "PRY_OPENROUTER_API_KEY"}
env["PRY_OPENROUTER_API_KEY"] = from_env
with patch.dict(os.environ, env, clear=True):
with patch("secrets_backend.get_secret", side_effect=fake_get_secret):
from settings import PrySettings
s = PrySettings()
# Env wins over secrets_backend
assert s.openrouter_api_key == from_env