""" Shared fixtures for strategy testing. These fixtures provide mocked LLM and search components that allow testing strategies without making real API calls. """ import json from unittest.mock import Mock import pytest # ============== Strategy-specific Mock LLM ============== def create_mock_llm_response(content: str) -> Mock: """Create a mock LLM response object.""" response = Mock() response.content = content return response @pytest.fixture def strategy_mock_llm(): """ Create a mock LLM that returns structured responses suitable for strategy testing. The mock handles different types of prompts by pattern matching and returning appropriate responses for question generation, analysis, and synthesis. """ mock = Mock() def invoke_side_effect(prompt, *args, **kwargs): # Convert prompt to string if it's a list of messages if isinstance(prompt, list): prompt_text = " ".join( msg.content if hasattr(msg, "content") else str(msg) for msg in prompt ) else: prompt_text = str(prompt) prompt_lower = prompt_text.lower() # Topic organization - expects simple responses like "0", "1", "-", "d" # Check this first as these prompts may also contain other keywords if ( "source to categorize" in prompt_lower or "existing topics" in prompt_lower ): # If no topics exist yet, return "-" to create a new topic if "no topics yet" in prompt_lower: return create_mock_llm_response("-") # Otherwise, add to topic 0 return create_mock_llm_response("0") # Question generation if "question" in prompt_lower and ( "generate" in prompt_lower or "research" in prompt_lower ): return create_mock_llm_response( "1. What are the key aspects of this topic?\n" "2. How has this evolved over time?\n" "3. What are the main challenges?" ) # Analysis/synthesis if ( "analyze" in prompt_lower or "synthesize" in prompt_lower or "summarize" in prompt_lower ): return create_mock_llm_response( "Based on the available information, the key findings are:\n" "1. Topic has multiple dimensions\n" "2. Research shows various perspectives\n" "3. Further investigation may be needed" ) # Constraint extraction if "constraint" in prompt_lower or "extract" in prompt_lower: return create_mock_llm_response( json.dumps( { "constraints": [ {"type": "temporal", "value": "recent"}, {"type": "geographic", "value": "global"}, ] } ) ) # Candidate generation if "candidate" in prompt_lower or "entity" in prompt_lower: return create_mock_llm_response( json.dumps( { "candidates": [ "Candidate A", "Candidate B", "Candidate C", ] } ) ) # Confidence/evaluation if ( "confidence" in prompt_lower or "evaluate" in prompt_lower or "score" in prompt_lower ): return create_mock_llm_response( json.dumps( { "confidence": 0.75, "evaluation": "Moderate confidence based on available evidence", } ) ) # Classification if "classify" in prompt_lower or "type" in prompt_lower: return create_mock_llm_response( json.dumps({"type": "research", "complexity": "moderate"}) ) # Default response return create_mock_llm_response( "This is a mocked LLM response for testing purposes. " "The topic requires further research and analysis." ) mock.invoke = Mock(side_effect=invoke_side_effect) # Also support __call__ for some LLM implementations mock.__call__ = mock.invoke # Add common attributes that LLMs might have mock.model_name = "mock-model" mock.temperature = 0.7 return mock @pytest.fixture def strategy_mock_search(): """ Create a mock search engine that returns realistic search results. """ mock = Mock() search_results = [ { "title": "Research Article on Topic", "link": "https://example.com/article1", "snippet": "This article discusses various aspects of the topic including methodology and findings.", "full_content": "Full content of the research article discussing the topic in detail.", }, { "title": "Wikipedia: Topic Overview", "link": "https://en.wikipedia.org/wiki/Topic", "snippet": "Topic is a subject of research with multiple dimensions and applications.", "full_content": "Wikipedia article providing comprehensive overview of the topic.", }, { "title": "Academic Paper on Related Research", "link": "https://arxiv.org/abs/1234.5678", "snippet": "This paper presents new findings related to the topic and its implications.", "full_content": "Academic paper with detailed analysis and results.", }, ] mock.run = Mock(return_value=search_results) mock.include_full_content = True return mock @pytest.fixture def strategy_settings_snapshot(): """ Create a standard settings snapshot for strategy testing. """ return { # Search settings "search.iterations": {"value": 2, "type": "int"}, "search.questions_per_iteration": {"value": 3, "type": "int"}, "search.questions": {"value": 3, "type": "int"}, "search.final_max_results": {"value": 100, "type": "int"}, "search.cross_engine_max_results": {"value": 100, "type": "int"}, "search.cross_engine_use_reddit": {"value": False, "type": "bool"}, "search.cross_engine_min_date": {"value": None, "type": "str"}, # LLM settings "llm.provider": {"value": "mock", "type": "str"}, "llm.model": {"value": "mock-model", "type": "str"}, # Search tool settings "search.tool": {"value": "mock", "type": "str"}, # App settings "app.max_user_query_length": {"value": 300, "type": "int"}, # General settings "general.knowledge_accumulation_context_limit": { "value": 5000, "type": "int", }, "general.max_knowledge_items": {"value": 100, "type": "int"}, # Focused iteration settings "focused_iteration.adaptive_questions": {"value": 0, "type": "int"}, "focused_iteration.knowledge_summary_limit": { "value": 10, "type": "int", }, "focused_iteration.snippet_truncate": {"value": 200, "type": "int"}, "focused_iteration.question_generator": { "value": "browsecomp", "type": "str", }, "focused_iteration.prompt_knowledge_truncate": { "value": 1500, "type": "int", }, "focused_iteration.previous_searches_limit": { "value": 10, "type": "int", }, } # ============== Strategy Names for Parametrized Tests ============== # All strategy names supported by the factory FACTORY_STRATEGY_NAMES = [ "source-based", "focused-iteration", "focused-iteration-standard", "news", "topic-organization", "langgraph-agent", ] # Strategy classes that can be imported directly STRATEGY_IMPORTS = [ ("source_based_strategy", "SourceBasedSearchStrategy"), ("focused_iteration_strategy", "FocusedIterationStrategy"), ("news_strategy", "NewsAggregationStrategy"), ("topic_organization_strategy", "TopicOrganizationStrategy"), ("langgraph_agent_strategy", "LangGraphAgentStrategy"), ]