Skip to main content
Glama
conftest.py8.45 kB
""" Pytest configuration for PAL MCP Server tests """ import asyncio import importlib import os import sys import tempfile from pathlib import Path import pytest # On macOS, the default pytest temp dir is typically under /var (e.g. /private/var/folders/...). # If /var is considered a dangerous system path, tests must use a safe temp root (like /tmp). if sys.platform == "darwin": os.environ["TMPDIR"] = "/tmp" # tempfile caches the temp dir after first lookup; clear it so pytest fixtures pick up TMPDIR. tempfile.tempdir = None # Ensure the parent directory is in the Python path for imports parent_dir = Path(__file__).resolve().parent.parent if str(parent_dir) not in sys.path: sys.path.insert(0, str(parent_dir)) import utils.env as env_config # noqa: E402 # Ensure tests operate with runtime environment rather than .env overrides during imports env_config.reload_env({"PAL_MCP_FORCE_ENV_OVERRIDE": "false"}) # Set default model to a specific value for tests to avoid auto mode # This prevents all tests from failing due to missing model parameter os.environ["DEFAULT_MODEL"] = "gemini-2.5-flash" # Force reload of config module to pick up the env var import config # noqa: E402 importlib.reload(config) # Note: This creates a test sandbox environment # Tests create their own temporary directories as needed # Configure asyncio for Windows compatibility if sys.platform == "win32": asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # Register providers for all tests from providers.gemini import GeminiModelProvider # noqa: E402 from providers.openai import OpenAIModelProvider # noqa: E402 from providers.registry import ModelProviderRegistry # noqa: E402 from providers.shared import ProviderType # noqa: E402 from providers.xai import XAIModelProvider # noqa: E402 # Register providers at test startup ModelProviderRegistry.register_provider(ProviderType.GOOGLE, GeminiModelProvider) ModelProviderRegistry.register_provider(ProviderType.OPENAI, OpenAIModelProvider) ModelProviderRegistry.register_provider(ProviderType.XAI, XAIModelProvider) # Register CUSTOM provider if CUSTOM_API_URL is available (for integration tests) # But only if we're actually running integration tests, not unit tests if os.getenv("CUSTOM_API_URL") and "test_prompt_regression.py" in os.getenv("PYTEST_CURRENT_TEST", ""): from providers.custom import CustomProvider # noqa: E402 def custom_provider_factory(api_key=None): """Factory function that creates CustomProvider with proper parameters.""" base_url = os.getenv("CUSTOM_API_URL", "") return CustomProvider(api_key=api_key or "", base_url=base_url) ModelProviderRegistry.register_provider(ProviderType.CUSTOM, custom_provider_factory) @pytest.fixture def project_path(tmp_path): """ Provides a temporary directory for tests. This ensures all file operations during tests are isolated. """ # Create a subdirectory for this specific test test_dir = tmp_path / "test_workspace" test_dir.mkdir(parents=True, exist_ok=True) return test_dir def _set_dummy_keys_if_missing(): """Set dummy API keys only when they are completely absent.""" for var in ("GEMINI_API_KEY", "OPENAI_API_KEY", "XAI_API_KEY"): if not os.environ.get(var): os.environ[var] = "dummy-key-for-tests" # Pytest configuration def pytest_configure(config): """Configure pytest with custom markers""" config.addinivalue_line("markers", "asyncio: mark test as async") config.addinivalue_line("markers", "no_mock_provider: disable automatic provider mocking") # Assume we need dummy keys until we learn otherwise config._needs_dummy_keys = True def pytest_collection_modifyitems(session, config, items): """Hook that runs after test collection to check for no_mock_provider markers.""" # Always set dummy keys if real keys are missing # This ensures tests work in CI even with no_mock_provider marker _set_dummy_keys_if_missing() @pytest.fixture(autouse=True) def mock_provider_availability(request, monkeypatch): """ Automatically mock provider availability for all tests to prevent effective auto mode from being triggered when DEFAULT_MODEL is unavailable. This fixture ensures that when tests run with dummy API keys, the tools don't require model selection unless explicitly testing auto mode. """ # Skip this fixture for tests that need real providers if hasattr(request, "node"): marker = request.node.get_closest_marker("no_mock_provider") if marker: return # Ensure providers are registered (in case other tests cleared the registry) from providers.shared import ProviderType registry = ModelProviderRegistry() if ProviderType.GOOGLE not in registry._providers: ModelProviderRegistry.register_provider(ProviderType.GOOGLE, GeminiModelProvider) if ProviderType.OPENAI not in registry._providers: ModelProviderRegistry.register_provider(ProviderType.OPENAI, OpenAIModelProvider) if ProviderType.XAI not in registry._providers: ModelProviderRegistry.register_provider(ProviderType.XAI, XAIModelProvider) # Ensure CUSTOM provider is registered if needed for integration tests if ( os.getenv("CUSTOM_API_URL") and "test_prompt_regression.py" in os.getenv("PYTEST_CURRENT_TEST", "") and ProviderType.CUSTOM not in registry._providers ): from providers.custom import CustomProvider def custom_provider_factory(api_key=None): base_url = os.getenv("CUSTOM_API_URL", "") return CustomProvider(api_key=api_key or "", base_url=base_url) ModelProviderRegistry.register_provider(ProviderType.CUSTOM, custom_provider_factory) # Also mock is_effective_auto_mode for all BaseTool instances to return False # unless we're specifically testing auto mode behavior from tools.shared.base_tool import BaseTool def mock_is_effective_auto_mode(self): # If this is an auto mode test file or specific auto mode test, use the real logic test_file = request.node.fspath.basename if hasattr(request, "node") and hasattr(request.node, "fspath") else "" test_name = request.node.name if hasattr(request, "node") else "" # Allow auto mode for tests in auto mode files or with auto in the name if ( "auto_mode" in test_file.lower() or "auto" in test_name.lower() or "intelligent_fallback" in test_file.lower() or "per_tool_model_defaults" in test_file.lower() ): # Call original method logic from config import DEFAULT_MODEL if DEFAULT_MODEL.lower() == "auto": return True provider = ModelProviderRegistry.get_provider_for_model(DEFAULT_MODEL) return provider is None # For all other tests, return False to disable auto mode return False monkeypatch.setattr(BaseTool, "is_effective_auto_mode", mock_is_effective_auto_mode) @pytest.fixture(autouse=True) def clear_model_restriction_env(monkeypatch): """Ensure per-test isolation from user-defined model restriction env vars.""" restriction_vars = [ "OPENAI_ALLOWED_MODELS", "GOOGLE_ALLOWED_MODELS", "XAI_ALLOWED_MODELS", "OPENROUTER_ALLOWED_MODELS", "DIAL_ALLOWED_MODELS", ] for var in restriction_vars: monkeypatch.delenv(var, raising=False) @pytest.fixture(autouse=True) def disable_force_env_override(monkeypatch): """Default tests to runtime environment visibility unless they explicitly opt in.""" monkeypatch.setenv("PAL_MCP_FORCE_ENV_OVERRIDE", "false") env_config.reload_env({"PAL_MCP_FORCE_ENV_OVERRIDE": "false"}) monkeypatch.setenv("DEFAULT_MODEL", "gemini-2.5-flash") monkeypatch.setenv("MAX_CONVERSATION_TURNS", "50") import importlib import sys import config import utils.conversation_memory as conversation_memory importlib.reload(config) importlib.reload(conversation_memory) test_conversation_module = sys.modules.get("tests.test_conversation_memory") if test_conversation_module is not None: test_conversation_module.MAX_CONVERSATION_TURNS = conversation_memory.MAX_CONVERSATION_TURNS try: yield finally: env_config.reload_env()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/BeehiveInnovations/zen-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server