"""Tests for the GitHub client.""" from unittest.mock import MagicMock, patch from datetime import datetime import pytest from src.github_client import GitHubClient, RateLimitExceededError class TestGitHubClient: """Tests for GitHubClient class.""" @pytest.fixture def mock_ghapi(self): """Create a mock GhApi class.""" with patch("src.github_client.GhApi") as mock: yield mock @pytest.fixture def client(self, mock_ghapi): """Create a GitHub client with mocked API.""" return GitHubClient(token=None, verbose=False) def test_init_without_token(self, client): """Test initialization without token.""" assert client.token is None assert client.verbose is False assert client.timeout == 30 def test_init_with_token(self, mock_ghapi): """Test initialization with token.""" client = GitHubClient(token="test_token", verbose=True) assert client.token == "test_token" assert client.verbose is True def test_search_repositories(self, client, mock_ghapi): """Test repository search.""" mock_api_instance = MagicMock() mock_response = MagicMock() mock_response.get.return_value = {"items": [{"full_name": "test/repo"}]} mock_response.headers = {"X-RateLimit-Remaining": "60"} mock_api_instance.search.repos.return_value = mock_response mock_ghapi.return_value = mock_api_instance result = client.search_repositories( language="python", stars_min=100, per_page=5, ) assert len(result) == 1 mock_api_instance.search.repos.assert_called_once() def test_search_repositories_with_query(self, client, mock_ghapi): """Test repository search with custom query.""" mock_api_instance = MagicMock() mock_response = MagicMock() mock_response.get.return_value = {"items": []} mock_response.headers = {"X-RateLimit-Remaining": "60"} mock_api_instance.search.repos.return_value = mock_response mock_ghapi.return_value = mock_api_instance client.search_repositories( query="machine learning", language="python", stars_min=50, stars_max=500, per_page=10, ) mock_api_instance.search.repos.assert_called_once() call_args = mock_api_instance.search.repos.call_args assert "machine learning" in call_args[1]["q"] assert "language:python" in call_args[1]["q"] def test_get_file_tree(self, client, mock_ghapi): """Test getting file tree.""" mock_api_instance = MagicMock() mock_response = MagicMock() mock_response.truncated = False mock_response.tree = [ {"path": "src/main.py", "type": "blob"}, {"path": "README.md", "type": "blob"}, ] mock_response.headers = {"X-RateLimit-Remaining": "60"} mock_api_instance.git_trees.get.return_value = mock_response mock_ghapi.return_value = mock_api_instance result = client.get_file_tree("owner", "repo") assert len(result) == 2 assert result[0]["path"] == "src/main.py" def test_get_file_content(self, client, mock_ghapi): """Test getting file content.""" import base64 mock_api_instance = MagicMock() mock_response = { "encoding": "base64", "content": base64.b64encode(b"print('hello')").decode(), } mock_api_instance.repos.get_content.return_value = mock_response mock_ghapi.return_value = mock_api_instance result = client.get_file_content("owner", "repo", "main.py") assert result == "print('hello')" def test_get_file_content_not_base64(self, client, mock_ghapi): """Test getting file content when not base64 encoded.""" mock_api_instance = MagicMock() mock_response = {"encoding": "utf-8"} mock_api_instance.repos.get_content.return_value = mock_response mock_ghapi.return_value = mock_api_instance result = client.get_file_content("owner", "repo", "main.py") assert result is None class TestRateLimitExceededError: """Tests for RateLimitExceededError class.""" def test_error_creation(self): """Test creating rate limit error.""" reset_time = datetime.now() error = RateLimitExceededError( reset_time=reset_time, message="Custom message", ) assert error.reset_time == reset_time assert error.message == "Custom message" def test_error_default_message(self): """Test error with default message.""" error = RateLimitExceededError(reset_time=None) assert "rate limit" in error.message.lower()