mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-27 01:22:18 +00:00
* test(e2e): add a HashiCorp Vault secret manager lane key_management_system had no end-to-end coverage: the Rust crates and the Python unit tests all run against mocked managers. This adds a secret_manager suite that drives a proxy configured with hashicorp_vault against a real Vault. The tests seed a fresh secret name per test with the runner's OPENAI_API_KEY and register a deployment pointing at os.environ/<name>. The proxy's env never holds that name, so get_secret's os.environ fallback cannot mask a broken manager, and a bogus value in Vault must come back as the provider's 401. Virtual keys are checked written to and removed from Vault under prefix_for_stored_virtual_keys. The setting is global to the proxy, so the lane has its own config and the secret_manager_vault opt-in marker, and stays out of the per-PR selector. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(e2e): make the secret manager suite backend-agnostic One marker and opt-in (secret_manager / E2E_SECRET_MANAGER=<system>) pick the backend from secret_backends.BACKENDS. The tests reach the manager through a SecretStore protocol, and each backend contributes a secret_store_<system>.py module, a registry entry, and gateway/secret_manager_<system>_ci_config.yml. requires_capability deselects tests a backend cannot support (CyberArk does not delete), and test_secret_backends.py checks every lane config against its backend without a live stack. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(e2e): add a CyberArk Conjur secret manager lane Adds cyberark as the second secret_manager backend: a Conjur store over its REST API (policy-declared variables, raw-text values, policy-patch teardown), its lane config, and a registry entry without deletes_stored_keys, since the proxy's CyberArk delete answers not_supported and Conjur keeps the key. secret_manager/backend.sh up|down <system> boots any backend in Docker and writes proxy.env and tests.env, so every lane runs the same way; the registry test checks the script boots exactly the registered backends. e2e_http gains send_text_external for APIs that speak raw text rather than JSON. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(e2e): give the secret manager suite a client with .proxy and address review The shared resources fixture reads client.proxy, so a bare ProxyClient errored every live test at setup. backend.sh now writes its env under a per-user directory with umask 077, the markerless unit tests are gone per tests/e2e/AGENTS.md, and routine comments are trimmed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
57 lines
1.7 KiB
Python
57 lines
1.7 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
from dataclasses import dataclass
|
|
from typing import Final
|
|
|
|
import pytest
|
|
|
|
from e2e_config import SECRET_MANAGER_OPT_IN_ENV
|
|
from proxy_client import ProxyClient
|
|
from secret_backends import BACKENDS, selected_backend
|
|
from secret_store import SecretBackend, SecretStore
|
|
|
|
REQUIRES_CAPABILITY: Final = "requires_capability"
|
|
|
|
|
|
def pytest_configure(config: pytest.Config) -> None:
|
|
config.addinivalue_line(
|
|
"markers",
|
|
f"{REQUIRES_CAPABILITY}(capability): secret_manager test deselected when the backend "
|
|
f"{SECRET_MANAGER_OPT_IN_ENV} names lacks the capability (secret_store.Capability)",
|
|
)
|
|
|
|
|
|
def _lacks_capability(item: pytest.Item, backend: SecretBackend) -> bool:
|
|
marker: Final = item.get_closest_marker(REQUIRES_CAPABILITY)
|
|
return marker is not None and marker.args[0] not in backend.capabilities
|
|
|
|
|
|
def pytest_collection_modifyitems(config: pytest.Config, items: list[pytest.Item]) -> None:
|
|
backend: Final = BACKENDS.get(os.environ.get(SECRET_MANAGER_OPT_IN_ENV, "").strip())
|
|
if backend is None:
|
|
return
|
|
deselected: Final = [item for item in items if _lacks_capability(item, backend)]
|
|
if deselected:
|
|
config.hook.pytest_deselected(items=deselected)
|
|
items[:] = [item for item in items if not _lacks_capability(item, backend)]
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class SecretManagerClient:
|
|
proxy: ProxyClient
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def client(proxy: ProxyClient) -> SecretManagerClient:
|
|
return SecretManagerClient(proxy)
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def backend() -> SecretBackend:
|
|
return selected_backend()
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def store(backend: SecretBackend) -> SecretStore:
|
|
return backend.from_env()
|