This commit is contained in:
140
tests/test_github_client.py
Normal file
140
tests/test_github_client.py
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
"""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()
|
||||||
Reference in New Issue
Block a user