pryscraper/tests/test_secrets_backend.py
cryptorugmunch 97af94098a
All checks were successful
CI / lint (pull_request) Passed locally on Talos
CI / typecheck (pull_request) Passed locally on Talos
CI / test (pull_request) Passed locally on Talos
CI / Secret scan (gitleaks) (pull_request) Passed locally on Talos
CI / Security audit (bandit) (pull_request) Passed locally on Talos
fix(lint): resolve remaining ruff errors and unblock MCP SSE test
2026-07-02 22:38:10 +02:00

101 lines
3.5 KiB
Python

"""Tests for secrets_backend module."""
# 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.
from __future__ import annotations
import importlib
import os
from unittest.mock import patch
import secrets_backend
from secrets_backend import (
BACKEND_AUTO,
_normalize_name,
backend_info,
get_secret,
set_secret,
)
def test_normalize_name_adds_pry_prefix():
assert _normalize_name("api_key") == "pry/api_key"
assert _normalize_name("pry/api_key") == "pry/api_key"
# Names that already have a slash are treated as paths (not re-prefixed)
assert _normalize_name("PRY/api_key").startswith("pry/")
def test_get_secret_default_when_missing():
# Use a name that definitely doesn't exist anywhere
with patch.dict(os.environ, {"PRY_SECRET_BACKEND": "env"}, clear=False):
value = get_secret("definitely_not_a_real_secret_name_xyz_12345", default="missing")
assert value == "missing"
def test_get_secret_from_env(monkeypatch):
monkeypatch.setenv("PRY_SECRET_BACKEND", "env")
monkeypatch.setenv("PRY_TEST_SECRET", "from-env")
value = get_secret("test_secret")
assert value == "from-env"
def test_get_secret_from_env_uppercase(monkeypatch):
"""Even when the caller passes lowercase, we should find PRY_UPPERCASE."""
monkeypatch.setenv("PRY_SECRET_BACKEND", "env")
monkeypatch.setenv("PRY_FOO", "upper-value")
value = get_secret("foo")
assert value == "upper-value"
def test_get_secret_falls_back_to_env_when_gopass_misses(monkeypatch):
"""When the active backend is gopass and a secret is not in gopass,
we should fall back to the env (so partial migrations work)."""
monkeypatch.setenv("PRY_SECRET_BACKEND", "gopass")
monkeypatch.setenv("PRY_FALLBACK_TEST", "env-fallback")
# gopass lookup will fail (entry doesn't exist), then we check env
with patch("secrets_backend._get_from_gopass", return_value=None):
value = get_secret("fallback_test")
assert value == "env-fallback"
def test_backend_info_returns_expected_keys():
info = backend_info()
assert "requested" in info
assert "active" in info
assert "gopass_available" in info
assert "gopass_path" in info
assert "env_file" in info
def test_get_secret_from_file(tmp_path, monkeypatch):
"""Read a secret from a .env file when backend is file."""
env_file = tmp_path / "test.env"
env_file.write_text("PRY_FROM_FILE=hello\n# comment\nPRY_OTHER=world\n")
monkeypatch.setenv("PRY_SECRET_BACKEND", "file")
monkeypatch.setenv("PRY_ENV_FILE", str(env_file))
# Reimport to pick up the new env file
importlib.reload(secrets_backend)
try:
value = secrets_backend.get_secret("from_file")
assert value == "hello"
finally:
# Restore
monkeypatch.delenv("PRY_ENV_FILE", raising=False)
importlib.reload(secrets_backend)
def test_set_secret_returns_false_when_gopass_unavailable(monkeypatch):
monkeypatch.setattr(secrets_backend, "_GOPASS_AVAILABLE", False)
# Force the global to be re-evaluated
monkeypatch.setattr(secrets_backend, "_gopass_available", lambda: False)
result = set_secret("test_secret", "value")
assert result is False
def test_detect_backend_invalid_falls_back_to_auto(monkeypatch):
monkeypatch.setenv("PRY_SECRET_BACKEND", "not-a-real-backend")
backend = secrets_backend._detect_backend()
assert backend == BACKEND_AUTO