Files
2026-04-10 16:23:36 -07:00

132 lines
3.8 KiB
Python

"""Shared fixtures for tools tests."""
from __future__ import annotations
import logging
import os
from collections.abc import Callable
from pathlib import Path
import pytest
from fastmcp import FastMCP
from aden_tools.credentials import CREDENTIAL_SPECS, CredentialStoreAdapter
from aden_tools.credentials.store_adapter import _reset_default_adapter_cache
logger = logging.getLogger(__name__)
@pytest.fixture(autouse=True)
def _clear_default_adapter_cache():
"""Drop the memoized default adapter so each test starts clean.
`CredentialStoreAdapter.default()` is process-memoized to avoid redundant
Aden syncs at startup; in tests we need a fresh build per test so env-var
and patch state from one test does not leak into the next.
"""
_reset_default_adapter_cache()
yield
_reset_default_adapter_cache()
@pytest.fixture
def mcp() -> FastMCP:
"""Create a fresh FastMCP instance for testing."""
return FastMCP("test-server")
@pytest.fixture
def mock_credentials() -> CredentialStoreAdapter:
"""Create a CredentialStoreAdapter with mock test credentials."""
return CredentialStoreAdapter.for_testing(
{
"anthropic": "test-anthropic-api-key",
"brave_search": "test-brave-api-key",
# Add other mock credentials as needed
}
)
@pytest.fixture
def sample_text_file(tmp_path: Path) -> Path:
"""Create a simple text file for testing."""
txt_file = tmp_path / "test.txt"
txt_file.write_text("Hello, World!\nLine 2\nLine 3")
return txt_file
@pytest.fixture
def sample_csv(tmp_path: Path) -> Path:
"""Create a simple CSV file for testing."""
csv_file = tmp_path / "test.csv"
csv_file.write_text("name,age,city\nAlice,30,NYC\nBob,25,LA\nCharlie,35,Chicago\n")
return csv_file
@pytest.fixture
def sample_json(tmp_path: Path) -> Path:
"""Create a simple JSON file for testing."""
json_file = tmp_path / "test.json"
json_file.write_text('{"users": [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]}')
return json_file
@pytest.fixture
def large_text_file(tmp_path: Path) -> Path:
"""Create a large text file for size limit testing."""
large_file = tmp_path / "large.txt"
large_file.write_text("x" * 20_000_000) # 20MB
return large_file
@pytest.fixture(scope="session")
def live_credential_resolver() -> Callable[[str], str | None]:
"""Resolve live credentials for integration tests.
Tries two sources in order:
1. Environment variable (spec.env_var)
2. CredentialStoreAdapter.default() (encrypted store + env fallback)
Returns a callable: resolver(credential_name) -> str | None.
Credential values are never logged or exposed in test output.
"""
_adapter: CredentialStoreAdapter | None = None
_adapter_init_failed = False
def _get_adapter() -> CredentialStoreAdapter | None:
nonlocal _adapter, _adapter_init_failed
if _adapter is not None:
return _adapter
if _adapter_init_failed:
return None
try:
_adapter = CredentialStoreAdapter.default()
except Exception as exc:
logger.debug("Could not initialize CredentialStoreAdapter: %s", exc)
_adapter_init_failed = True
return _adapter
def resolve(credential_name: str) -> str | None:
spec = CREDENTIAL_SPECS.get(credential_name)
if spec is None:
return None
# 1. Try env var directly
value = os.environ.get(spec.env_var)
if value:
return value
# 2. Try the adapter (encrypted store + fallback)
adapter = _get_adapter()
if adapter is not None:
try:
value = adapter.get(credential_name)
if value:
return value
except Exception:
pass
return None
return resolve