import json from unittest.mock import Mock, patch, MagicMock from pr_review.github_client import GitHubClient, REVIEW_MARKER class TestIsDraftPR: def test_returns_true_for_draft(self): with patch('pr_review.github_client.GhApi'): client = GitHubClient('token', 'owner', 'repo') client.api.pulls.get.return_value = Mock(draft=True) assert client.is_draft_pr(42) is True def test_returns_false_for_non_draft(self): with patch('pr_review.github_client.GhApi'): client = GitHubClient('token', 'owner', 'repo') client.api.pulls.get.return_value = Mock(draft=False) assert client.is_draft_pr(42) is False class TestCreateComment: def test_prepends_review_marker(self): with patch('pr_review.github_client.GhApi'): client = GitHubClient('token', 'owner', 'repo') client.create_comment(10, 'Review body') client.api.issues.create_comment.assert_called_once_with( issue_number=10, body=f'{REVIEW_MARKER}\nReview body' ) class TestMinimizeComment: def test_calls_graphql_api(self): with patch('pr_review.github_client.GhApi'): client = GitHubClient('mytoken', 'owner', 'repo') mock_response = MagicMock() mock_response.read.return_value = json.dumps( { 'data': { 'minimizeComment': {'minimizedComment': {'isMinimized': True}} } } ).encode() mock_response.__enter__ = lambda s: s mock_response.__exit__ = Mock(return_value=False) with patch( 'urllib.request.urlopen', return_value=mock_response ) as mock_open: client.minimize_comment('NODE_ID_123') call_args = mock_open.call_args[0][0] assert call_args.get_header('Authorization') == 'Bearer mytoken' sent_payload = json.loads(call_args.data) assert sent_payload['variables']['id'] == 'NODE_ID_123' assert 'minimizeComment' in sent_payload['query'] def test_prints_warning_on_graphql_error(self, capsys): with patch('pr_review.github_client.GhApi'): client = GitHubClient('token', 'owner', 'repo') mock_response = MagicMock() mock_response.read.return_value = json.dumps( {'errors': [{'message': 'Not Found'}]} ).encode() mock_response.__enter__ = lambda s: s mock_response.__exit__ = Mock(return_value=False) with patch('urllib.request.urlopen', return_value=mock_response): client.minimize_comment('BAD_NODE') captured = capsys.readouterr() assert 'Warning' in captured.out assert 'BAD_NODE' in captured.out class TestCollapsePreviousReviews: def test_minimizes_only_marked_comments(self): with patch('pr_review.github_client.GhApi'): client = GitHubClient('token', 'owner', 'repo') client.api.issues.list_comments.return_value = [ {'body': f'{REVIEW_MARKER}\nOld review', 'node_id': 'NID_1'}, {'body': 'Some other comment', 'node_id': 'NID_2'}, {'body': f'{REVIEW_MARKER}\nAnother old review', 'node_id': 'NID_3'}, ] with patch.object(client, 'minimize_comment') as mock_minimize: client.collapse_previous_reviews(10) assert mock_minimize.call_count == 2 mock_minimize.assert_any_call('NID_1') mock_minimize.assert_any_call('NID_3') def test_no_marked_comments_does_nothing(self): with patch('pr_review.github_client.GhApi'): client = GitHubClient('token', 'owner', 'repo') client.api.issues.list_comments.return_value = [ {'body': 'Regular comment', 'node_id': 'NID_1'}, ] with patch.object(client, 'minimize_comment') as mock_minimize: client.collapse_previous_reviews(10) mock_minimize.assert_not_called()