""" Comprehensive tests for the GitHub search engine. Tests initialization, search functionality, and API configuration. """ import base64 import pytest from unittest.mock import Mock, patch class TestGitHubSearchEngineInit: """Tests for GitHub search engine initialization.""" def test_init_default_parameters(self): """Test initialization with default parameters.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine() assert engine.max_results >= 10 # Default search type is repositories assert engine.search_type == "repositories" assert engine.api_key is None def test_init_with_api_key(self): """Test initialization with GitHub API key.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine(api_key="ghp_test_token_123") assert engine.api_key == "ghp_test_token_123" def test_init_with_search_type(self): """Test initialization with specific search type.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine(search_type="code") assert engine.search_type == "code" def test_init_with_custom_max_results(self): """Test initialization with custom max results.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine(max_results=50) assert engine.max_results >= 50 class TestGitHubSearchExecution: """Tests for GitHub search execution.""" @pytest.fixture def github_engine(self): """Create a GitHub engine instance.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) return GitHubSearchEngine(max_results=10) def test_engine_initialization(self, github_engine): """Test that engine is properly initialized.""" assert github_engine is not None assert github_engine.max_results >= 10 def test_engine_has_api_base(self, github_engine): """Test that engine has API base URL configured.""" assert hasattr(github_engine, "api_base") assert "api.github.com" in github_engine.api_base class TestGitHubAPIConfiguration: """Tests for GitHub API configuration.""" def test_api_key_in_headers(self): """Test that API key is included in headers when provided.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine(api_key="ghp_my_token") assert engine.api_key == "ghp_my_token" # Check that Authorization header is set assert "Authorization" in engine.headers assert "ghp_my_token" in engine.headers["Authorization"] def test_no_api_key_anonymous_access(self): """Test that engine works without API key (anonymous access).""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine() assert engine.api_key is None # Authorization header should not be present without API key assert "Authorization" not in engine.headers class TestGitHubSearchTypes: """Tests for different GitHub search types.""" def test_repository_search_type(self): """Test repository search type configuration.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine(search_type="repositories") assert engine.search_type == "repositories" def test_code_search_type(self): """Test code search type configuration.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine(search_type="code") assert engine.search_type == "code" def test_issues_search_type(self): """Test issues search type configuration.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine(search_type="issues") assert engine.search_type == "issues" def test_users_search_type(self): """Test users search type configuration.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine(search_type="users") assert engine.search_type == "users" class TestGitHubEngineType: """Tests for GitHub engine type identification.""" def test_engine_type_set(self): """Test that engine type is properly set.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine() assert "github" in engine.engine_type.lower() class TestGitHubHeaders: """Tests for GitHub request headers.""" def test_accept_header_set(self): """Test that Accept header is set for API compatibility.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine() assert "Accept" in engine.headers assert "application/vnd.github" in engine.headers["Accept"] def test_user_agent_header_set(self): """Test that User-Agent header is set.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine() assert "User-Agent" in engine.headers class TestGitHubAdvancedInit: """Tests for advanced initialization options.""" def test_init_with_readme_option(self): """Test initialization with include_readme option.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine(include_readme=False) assert engine.include_readme is False def test_init_with_issues_option(self): """Test initialization with include_issues option.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine(include_issues=True) assert engine.include_issues is True def test_init_with_llm(self): """Test initialization with LLM.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) mock_llm = Mock() engine = GitHubSearchEngine(llm=mock_llm) assert engine.llm is mock_llm class TestGitHubRateLimits: """Tests for rate limit handling.""" def test_handle_rate_limits_normal(self): """Test rate limit handling with normal remaining.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine() mock_response = Mock() mock_response.headers = { "X-RateLimit-Remaining": "50", "X-RateLimit-Reset": "0", } # Should not raise or sleep engine._handle_rate_limits(mock_response) def test_handle_rate_limits_low_remaining(self): """Test rate limit handling with low remaining requests.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine() mock_response = Mock() mock_response.headers = { "X-RateLimit-Remaining": "3", "X-RateLimit-Reset": "0", } # Should log warning but not sleep engine._handle_rate_limits(mock_response) class TestGitHubOptimizeQuery: """Tests for query optimization.""" def test_optimize_query_without_llm(self): """Test query optimization returns original without LLM.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine(llm=None) # Patch the config getter to return None with patch( "local_deep_research.web_search_engines.engines.search_engine_github.llm_config.get_llm", return_value=None, ): result = engine._optimize_github_query("python web framework") assert result == "python web framework" def test_optimize_query_with_llm(self): """Test query optimization with LLM.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) mock_llm = Mock() mock_llm.invoke.return_value = Mock( content="python web framework stars:>100" ) engine = GitHubSearchEngine(llm=mock_llm) result = engine._optimize_github_query("python web framework") assert "python" in result.lower() mock_llm.invoke.assert_called_once() def test_optimize_query_handles_error(self): """Test query optimization handles LLM errors gracefully.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) mock_llm = Mock() mock_llm.invoke.side_effect = Exception("LLM error") engine = GitHubSearchEngine(llm=mock_llm) result = engine._optimize_github_query("test query") # Should fall back to original query assert result == "test query" def test_optimize_query_handles_empty_response(self): """Test query optimization handles empty LLM response.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) mock_llm = Mock() mock_llm.invoke.return_value = Mock(content="") engine = GitHubSearchEngine(llm=mock_llm) result = engine._optimize_github_query("test query") # Should fall back to original query assert result == "test query" class TestGitHubSearchGithub: """Tests for _search_github method.""" def test_search_github_success(self): """Test successful GitHub search.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine(llm=None) mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = { "total_count": 2, "items": [ {"id": 1, "full_name": "owner/repo1"}, {"id": 2, "full_name": "owner/repo2"}, ], } mock_response.headers = { "X-RateLimit-Remaining": "50", "X-RateLimit-Reset": "0", } with patch( "local_deep_research.web_search_engines.engines.search_engine_github.llm_config.get_llm", return_value=None, ): with patch( "local_deep_research.web_search_engines.engines.search_engine_github.safe_get", return_value=mock_response, ): result = engine._search_github("python") assert len(result) == 2 assert result[0]["full_name"] == "owner/repo1" def test_search_github_handles_error(self): """Test search handles API errors.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine(llm=None) with patch( "local_deep_research.web_search_engines.engines.search_engine_github.llm_config.get_llm", return_value=None, ): with patch( "local_deep_research.web_search_engines.engines.search_engine_github.safe_get", side_effect=Exception("API error"), ): result = engine._search_github("test query") assert result == [] def test_search_github_handles_non_200_response(self): """Test search handles non-200 responses.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine(llm=None) mock_response = Mock() mock_response.status_code = 403 mock_response.text = "Rate limit exceeded" mock_response.headers = { "X-RateLimit-Remaining": "0", "X-RateLimit-Reset": "0", } with patch( "local_deep_research.web_search_engines.engines.search_engine_github.llm_config.get_llm", return_value=None, ): with patch( "local_deep_research.web_search_engines.engines.search_engine_github.safe_get", return_value=mock_response, ): result = engine._search_github("test") assert result == [] class TestGitHubGetReadmeContent: """Tests for _get_readme_content method.""" def test_get_readme_success(self): """Test successful README retrieval.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine() readme_content = "# Test README\nThis is a test." encoded_content = base64.b64encode(readme_content.encode()).decode() mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = { "content": encoded_content, "encoding": "base64", } mock_response.headers = { "X-RateLimit-Remaining": "50", "X-RateLimit-Reset": "0", } with patch( "local_deep_research.web_search_engines.engines.search_engine_github.safe_get", return_value=mock_response, ): result = engine._get_readme_content("owner/repo") assert "Test README" in result def test_get_readme_not_found(self): """Test README not found.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine() mock_response = Mock() mock_response.status_code = 404 mock_response.headers = { "X-RateLimit-Remaining": "50", "X-RateLimit-Reset": "0", } with patch( "local_deep_research.web_search_engines.engines.search_engine_github.safe_get", return_value=mock_response, ): result = engine._get_readme_content("owner/repo") assert result == "" def test_get_readme_handles_error(self): """Test README retrieval handles errors.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine() with patch( "local_deep_research.web_search_engines.engines.search_engine_github.safe_get", side_effect=Exception("Network error"), ): result = engine._get_readme_content("owner/repo") assert result == "" class TestGitHubGetRecentIssues: """Tests for _get_recent_issues method.""" def test_get_issues_success(self): """Test successful issues retrieval.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine() mock_issues = [ {"number": 1, "title": "Issue 1"}, {"number": 2, "title": "Issue 2"}, ] mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = mock_issues mock_response.headers = { "X-RateLimit-Remaining": "50", "X-RateLimit-Reset": "0", } with patch( "local_deep_research.web_search_engines.engines.search_engine_github.safe_get", return_value=mock_response, ): result = engine._get_recent_issues("owner/repo", limit=5) assert len(result) == 2 assert result[0]["title"] == "Issue 1" def test_get_issues_not_found(self): """Test issues not found.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine() mock_response = Mock() mock_response.status_code = 404 mock_response.headers = { "X-RateLimit-Remaining": "50", "X-RateLimit-Reset": "0", } with patch( "local_deep_research.web_search_engines.engines.search_engine_github.safe_get", return_value=mock_response, ): result = engine._get_recent_issues("owner/repo") assert result == [] class TestGitHubFormatPreviews: """Tests for preview formatting methods.""" def test_format_repository_preview(self): """Test repository preview formatting.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine() repo = { "id": 123, "full_name": "owner/repo", "html_url": "https://github.com/owner/repo", "description": "Test description", "stargazers_count": 100, "forks_count": 10, "language": "Python", "owner": {"login": "owner"}, } preview = engine._format_repository_preview(repo) assert preview["title"] == "owner/repo" assert preview["stars"] == 100 assert preview["language"] == "Python" assert preview["search_type"] == "repository" def test_format_code_preview(self): """Test code preview formatting.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine() code = { "sha": "abc123", "name": "test.py", "path": "src/test.py", "html_url": "https://github.com/owner/repo/blob/main/test.py", "repository": { "full_name": "owner/repo", "html_url": "https://github.com/owner/repo", }, } preview = engine._format_code_preview(code) assert "test.py" in preview["title"] assert preview["path"] == "src/test.py" assert preview["search_type"] == "code" def test_format_issue_preview(self): """Test issue preview formatting.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine() issue = { "number": 42, "title": "Bug report", "html_url": "https://github.com/owner/repo/issues/42", "body": "This is a bug description", "state": "open", "user": {"login": "reporter"}, "comments": 5, } preview = engine._format_issue_preview(issue) assert preview["title"] == "Bug report" assert preview["state"] == "open" assert preview["search_type"] == "issue" def test_format_user_preview(self): """Test user preview formatting.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine() user = { "id": 1, "login": "testuser", "html_url": "https://github.com/testuser", "bio": "Test bio", "followers": 100, "public_repos": 50, } preview = engine._format_user_preview(user) assert preview["title"] == "testuser" assert preview["followers"] == 100 assert preview["search_type"] == "user" class TestGitHubGetPreviews: """Tests for _get_previews method.""" def test_get_previews_repository(self): """Test getting repository previews.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine(search_type="repositories", llm=None) mock_results = [ { "id": 123, "full_name": "owner/repo", "html_url": "https://github.com/owner/repo", "description": "Test repo", "stargazers_count": 100, "forks_count": 10, "owner": {"login": "owner"}, } ] with patch.object(engine, "_search_github", return_value=mock_results): previews = engine._get_previews("python") assert len(previews) == 1 assert previews[0]["search_type"] == "repository" def test_get_previews_code(self): """Test getting code previews.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine(search_type="code", llm=None) mock_results = [ { "sha": "abc123", "name": "test.py", "path": "src/test.py", "html_url": "https://github.com/owner/repo/blob/main/test.py", "repository": {"full_name": "owner/repo"}, } ] with patch.object(engine, "_search_github", return_value=mock_results): previews = engine._get_previews("function") assert len(previews) == 1 assert previews[0]["search_type"] == "code" def test_get_previews_no_results(self): """Test getting previews with no results.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine(llm=None) with patch.object(engine, "_search_github", return_value=[]): previews = engine._get_previews("nonexistent") assert previews == [] def test_get_previews_contribution_query(self): """Test getting previews for contribution-focused queries.""" from local_deep_research.web_search_engines.engines.search_engine_github import ( GitHubSearchEngine, ) engine = GitHubSearchEngine(search_type="issues", llm=None) mock_results = [ { "id": 123, "full_name": "owner/repo", "html_url": "https://github.com/owner/repo", "description": "Good for beginners", "stargazers_count": 500, "forks_count": 50, "owner": {"login": "owner"}, } ] with patch.object(engine, "_search_github", return_value=mock_results): previews = engine._get_previews( "beginner python projects to contribute" ) # Should have adjusted search type assert len(previews) == 1