"""Basic tests for the GitHub API wrapper."""
import json
from unittest.mock import MagicMock, patch
import pytest
from pr_review_mcp.gh_api import GitHubAPI, GitHubAPIError
class TestGitHubAPI:
"""Test GitHub API wrapper."""
@patch("pr_review_mcp.gh_api.subprocess.run")
def test_execute_graphql_success(self, mock_run):
"""Test successful GraphQL query execution."""
# Mock successful response
mock_run.return_value = MagicMock(
stdout=json.dumps({"data": {"test": "result"}}), returncode=0
)
api = GitHubAPI()
result = api.execute_graphql("query { test }", {"var": "value"})
assert result == {"test": "result"}
mock_run.assert_called_once()
@patch("pr_review_mcp.gh_api.subprocess.run")
def test_execute_graphql_error(self, mock_run):
"""Test GraphQL query with errors."""
# Mock error response
mock_run.return_value = MagicMock(
stdout=json.dumps({"data": {}, "errors": [{"message": "Invalid query"}]}), returncode=0
)
api = GitHubAPI()
with pytest.raises(GitHubAPIError, match="GraphQL errors"):
api.execute_graphql("query { invalid }", {})
@patch("pr_review_mcp.gh_api.subprocess.run")
def test_get_pr_id(self, mock_run):
"""Test getting PR ID."""
mock_run.return_value = MagicMock(
stdout=json.dumps({"data": {"repository": {"pullRequest": {"id": "PR_test123"}}}}),
returncode=0,
)
api = GitHubAPI()
pr_id = api.get_pr_id("owner", "repo", 1)
assert pr_id == "PR_test123"
@patch("pr_review_mcp.gh_api.subprocess.run")
def test_list_review_threads(self, mock_run):
"""Test listing review threads."""
mock_run.return_value = MagicMock(
stdout=json.dumps(
{
"data": {
"repository": {
"pullRequest": {
"reviewThreads": {
"nodes": [
{
"id": "PRRT_1",
"isResolved": False,
"path": "test.py",
"line": 10,
"comments": {
"nodes": [
{
"id": "PRRC_1",
"author": {"login": "reviewer"},
"body": "Fix this",
"createdAt": "2025-12-10T00:00:00Z",
}
]
},
}
]
}
}
}
}
}
),
returncode=0,
)
api = GitHubAPI()
threads = api.list_review_threads("owner", "repo", 1)
assert len(threads) == 1
assert threads[0]["id"] == "PRRT_1"
assert threads[0]["isResolved"] is False
@patch("pr_review_mcp.gh_api.subprocess.run")
def test_add_thread_reply(self, mock_run):
"""Test adding a reply to a review thread."""
mock_run.return_value = MagicMock(
stdout=json.dumps(
{
"data": {
"addPullRequestReviewThreadReply": {
"comment": {
"id": "PRRC_new",
"body": "Thanks for the review!",
"createdAt": "2025-12-10T01:00:00Z",
"author": {"login": "contributor"},
}
}
}
}
),
returncode=0,
)
api = GitHubAPI()
comment = api.add_thread_reply("PR_123", "PRRT_1", "Thanks for the review!")
assert comment["id"] == "PRRC_new"
assert comment["body"] == "Thanks for the review!"
assert comment["author"]["login"] == "contributor"
@patch("pr_review_mcp.gh_api.subprocess.run")
def test_resolve_thread(self, mock_run):
"""Test resolving a review thread."""
mock_run.return_value = MagicMock(
stdout=json.dumps(
{"data": {"resolveReviewThread": {"thread": {"id": "PRRT_1", "isResolved": True}}}}
),
returncode=0,
)
api = GitHubAPI()
thread = api.resolve_thread("PRRT_1")
assert thread["id"] == "PRRT_1"
assert thread["isResolved"] is True
@patch("pr_review_mcp.gh_api.subprocess.run")
def test_list_review_threads_filter_unresolved(self, mock_run):
"""Test filtering unresolved threads."""
mock_run.return_value = MagicMock(
stdout=json.dumps(
{
"data": {
"repository": {
"pullRequest": {
"reviewThreads": {
"nodes": [
{
"id": "PRRT_1",
"isResolved": False,
"path": "test.py",
"line": 10,
"comments": {"nodes": []},
},
{
"id": "PRRT_2",
"isResolved": True,
"path": "test.py",
"line": 20,
"comments": {"nodes": []},
},
]
}
}
}
}
}
),
returncode=0,
)
api = GitHubAPI()
# Test with unresolved_only=True (default)
unresolved_threads = api.list_review_threads("owner", "repo", 1, unresolved_only=True)
assert len(unresolved_threads) == 1
assert unresolved_threads[0]["id"] == "PRRT_1"
# Test with unresolved_only=False
all_threads = api.list_review_threads("owner", "repo", 1, unresolved_only=False)
assert len(all_threads) == 2