"""Test fixtures for Azure AI Search tool tests.""" import warnings from typing import Any, Dict, Generator, List, Protocol, Type, TypeVar, Union from unittest.mock import AsyncMock, MagicMock, patch import pytest from autogen_core import ComponentModel T = TypeVar("T") class AccessTokenProtocol(Protocol): """Protocol matching Azure AccessToken.""" token: str expires_on: int class MockAccessToken: """Mock implementation of AccessToken.""" def __init__(self, token: str, expires_on: int) -> None: self.token = token self.expires_on = expires_on class MockAzureKeyCredential: """Mock implementation of AzureKeyCredential.""" def __init__(self, key: str) -> None: self.key = key class MockTokenCredential: """Mock implementation of TokenCredential for testing.""" def get_token( self, *scopes: str, claims: str | None = None, tenant_id: str | None = None, enable_cae: bool = False, **kwargs: Any, ) -> AccessTokenProtocol: """Mock get_token method that implements TokenCredential protocol.""" return MockAccessToken("mock-token", 12345) try: from azure.core.credentials import AccessToken, AzureKeyCredential, TokenCredential _access_token_type: Type[AccessToken] = AccessToken azure_sdk_available = True except ImportError: AzureKeyCredential = MockAzureKeyCredential # type: ignore TokenCredential = MockTokenCredential # type: ignore _access_token_type = MockAccessToken # type: ignore azure_sdk_available = False CredentialType = Union[AzureKeyCredential, TokenCredential, MockAzureKeyCredential, MockTokenCredential, Any] needs_azure_sdk = pytest.mark.skipif(not azure_sdk_available, reason="Azure SDK not available") warnings.filterwarnings( "ignore", message="Type google.*uses PyType_Spec with a metaclass that has custom tp_new", category=DeprecationWarning, ) @pytest.fixture def mock_vectorized_query() -> Generator[MagicMock, None, None]: """Create a mock VectorizedQuery for testing.""" with patch("azure.search.documents.models.VectorizedQuery") as mock: yield mock @pytest.fixture def test_config() -> ComponentModel: """Return a test configuration for the Azure AI Search tool.""" return ComponentModel( provider="autogen_ext.tools.azure.MockAzureAISearchTool", config={ "name": "TestAzureSearch", "description": "Test Azure AI Search Tool", "endpoint": "https://test-search-service.search.windows.net", "index_name": "test-index", "api_version": "2023-10-01-Preview", "credential": AzureKeyCredential("test-key") if azure_sdk_available else {"api_key": "test-key"}, "query_type": "keyword", "search_fields": ["content", "title"], "select_fields": ["id", "content", "title", "source"], "top": 5, }, ) @pytest.fixture def keyword_config() -> ComponentModel: """Return a keyword search configuration.""" return ComponentModel( provider="autogen_ext.tools.azure.MockAzureAISearchTool", config={ "name": "KeywordSearch", "description": "Keyword search tool", "endpoint": "https://test-search-service.search.windows.net", "index_name": "test-index", "credential": AzureKeyCredential("test-key") if azure_sdk_available else {"api_key": "test-key"}, "query_type": "keyword", "search_fields": ["content", "title"], "select_fields": ["id", "content", "title", "source"], }, ) @pytest.fixture def vector_config() -> ComponentModel: """Create a test configuration for vector search.""" return ComponentModel( provider="autogen_ext.tools.azure.MockAzureAISearchTool", config={ "name": "VectorSearch", "description": "Vector search tool", "endpoint": "https://test-search-service.search.windows.net", "index_name": "test-index", "api_version": "2023-10-01-Preview", "credential": AzureKeyCredential("test-key") if azure_sdk_available else {"api_key": "test-key"}, "query_type": "vector", "vector_fields": ["embedding"], "select_fields": ["id", "content", "title", "source"], "top": 5, }, ) @pytest.fixture def hybrid_config() -> ComponentModel: """Create a test configuration for hybrid search.""" return ComponentModel( provider="autogen_ext.tools.azure.MockAzureAISearchTool", config={ "name": "HybridSearch", "description": "Hybrid search tool", "endpoint": "https://test-search-service.search.windows.net", "index_name": "test-index", "api_version": "2023-10-01-Preview", "credential": AzureKeyCredential("test-key") if azure_sdk_available else {"api_key": "test-key"}, "query_type": "keyword", "search_fields": ["content", "title"], "vector_fields": ["embedding"], "select_fields": ["id", "content", "title", "source"], "top": 5, }, ) @pytest.fixture def mock_search_response() -> List[Dict[str, Any]]: """Create a mock search response.""" return [ { "@search.score": 0.95, "id": "doc1", "content": "This is the first document content", "title": "Document 1", "source": "test-source-1", }, { "@search.score": 0.85, "id": "doc2", "content": "This is the second document content", "title": "Document 2", "source": "test-source-2", }, ] class AsyncIterator: """Async iterator for testing.""" def __init__(self, items: List[Dict[str, Any]]) -> None: self.items = items.copy() def __aiter__(self) -> "AsyncIterator": return self async def __anext__(self) -> Dict[str, Any]: if not self.items: raise StopAsyncIteration return self.items.pop(0) async def get_count(self) -> int: """Return count of items.""" return len(self.items) @pytest.fixture def mock_search_client(mock_search_response: List[Dict[str, Any]]) -> tuple[MagicMock, Any]: """Create a mock search client for testing.""" mock_client = MagicMock() mock_client.__aenter__ = AsyncMock(return_value=mock_client) mock_client.__aexit__ = AsyncMock(return_value=None) search_results = AsyncIterator(mock_search_response) mock_client.search = MagicMock(return_value=search_results) patcher = patch("azure.search.documents.aio.SearchClient", return_value=mock_client) return mock_client, patcher def test_validate_credentials_scenarios() -> None: """Test all validate_credentials scenarios to ensure full code coverage.""" import sys from autogen_ext.tools.azure._config import AzureAISearchConfig module_path = sys.modules[AzureAISearchConfig.__module__].__file__ if module_path is not None: assert "autogen-ext" in module_path data: Any = "not a dict" result: Any = AzureAISearchConfig.validate_credentials(data) # type: ignore assert result == data data_empty: Dict[str, Any] = {} result_empty: Dict[str, Any] = AzureAISearchConfig.validate_credentials(data_empty) # type: ignore assert isinstance(result_empty, dict) data_items: Dict[str, Any] = {"key1": "value1", "key2": "value2"} result_items: Dict[str, Any] = AzureAISearchConfig.validate_credentials(data_items) # type: ignore assert result_items["key1"] == "value1" assert result_items["key2"] == "value2" data_with_api_key: Dict[str, Any] = { "name": "test", "endpoint": "https://test.search.windows.net", "index_name": "test-index", "credential": {"api_key": "test-key"}, } result_with_api_key: Dict[str, Any] = AzureAISearchConfig.validate_credentials(data_with_api_key) # type: ignore cred = result_with_api_key["credential"] # type: ignore assert isinstance(cred, (AzureKeyCredential, MockAzureKeyCredential)) assert hasattr(cred, "key") assert cred.key == "test-key" # type: ignore credential: Any = AzureKeyCredential("test-key") data_with_credential: Dict[str, Any] = { "name": "test", "endpoint": "https://test.search.windows.net", "index_name": "test-index", "credential": credential, } result_with_credential: Dict[str, Any] = AzureAISearchConfig.validate_credentials(data_with_credential) # type: ignore assert result_with_credential["credential"] is credential data_without_api_key: Dict[str, Any] = { "name": "test", "endpoint": "https://test.search.windows.net", "index_name": "test-index", "credential": {"username": "test-user", "password": "test-pass"}, } result_without_api_key: Dict[str, Any] = AzureAISearchConfig.validate_credentials(data_without_api_key) # type: ignore assert result_without_api_key["credential"] == {"username": "test-user", "password": "test-pass"} def test_model_dump_scenarios() -> None: """Test all model_dump scenarios to ensure full code coverage.""" import sys from autogen_ext.tools.azure._config import AzureAISearchConfig module_path = sys.modules[AzureAISearchConfig.__module__].__file__ if module_path is not None: assert "autogen-ext" in module_path config = AzureAISearchConfig( name="test", endpoint="https://endpoint", index_name="index", credential=AzureKeyCredential("key"), # type: ignore ) result = config.model_dump() assert result["credential"] == {"type": "AzureKeyCredential"} if azure_sdk_available: from azure.core.credentials import AccessToken from azure.core.credentials import TokenCredential as RealTokenCredential class TestTokenCredential(RealTokenCredential): def get_token(self, *args: Any, **kwargs: Any) -> AccessToken: """Override of get_token method that returns proper type.""" return AccessToken("test-token", 12345) config = AzureAISearchConfig( name="test", endpoint="https://endpoint", index_name="index", credential=TestTokenCredential() ) result = config.model_dump() assert result["credential"] == {"type": "TokenCredential"} else: pytest.skip("Skipping TokenCredential test - Azure SDK not available")