Refactor issue filtering (#5129)

This commit is contained in:
Rohit Malhotra
2024-11-19 12:23:42 -05:00
committed by GitHub
parent de07fcfddc
commit 7f5022c8fe
5 changed files with 256 additions and 228 deletions

View File

@@ -18,7 +18,9 @@ class IssueHandlerInterface(ABC):
issue_type: ClassVar[str]
@abstractmethod
def get_converted_issues(self, comment_id: int | None = None) -> list[GithubIssue]:
def get_converted_issues(
self, issue_numbers: list[int] | None = None, comment_id: int | None = None
) -> list[GithubIssue]:
"""Download issues from GitHub."""
pass
@@ -138,13 +140,29 @@ class IssueHandler(IssueHandlerInterface):
return all_comments if all_comments else None
def get_converted_issues(self, comment_id: int | None = None) -> list[GithubIssue]:
def get_converted_issues(
self, issue_numbers: list[int] | None = None, comment_id: int | None = None
) -> list[GithubIssue]:
"""Download issues from Github.
Returns:
List of Github issues.
"""
if not issue_numbers:
raise ValueError('Unspecified issue number')
all_issues = self._download_issues_from_github()
logger.info(f'Limiting resolving to issues {issue_numbers}.')
all_issues = [
issue
for issue in all_issues
if issue['number'] in issue_numbers and 'pull_request' not in issue
]
if len(issue_numbers) == 1 and not all_issues:
raise ValueError(f'Issue {issue_numbers[0]} not found')
converted_issues = []
for issue in all_issues:
if any([issue.get(key) is None for key in ['number', 'title', 'body']]):
@@ -153,9 +171,6 @@ class IssueHandler(IssueHandlerInterface):
)
continue
if 'pull_request' in issue:
continue
# Get issue thread comments
thread_comments = self._get_issue_comments(
issue['number'], comment_id=comment_id
@@ -486,8 +501,16 @@ class PRHandler(IssueHandler):
return closing_issues
def get_converted_issues(self, comment_id: int | None = None) -> list[GithubIssue]:
def get_converted_issues(
self, issue_numbers: list[int] | None = None, comment_id: int | None = None
) -> list[GithubIssue]:
if not issue_numbers:
raise ValueError('Unspecified issue numbers')
all_issues = self._download_issues_from_github()
logger.info(f'Limiting resolving to issues {issue_numbers}.')
all_issues = [issue for issue in all_issues if issue['number'] in issue_numbers]
converted_issues = []
for issue in all_issues:
# For PRs, body can be None

View File

@@ -83,11 +83,10 @@ async def resolve_issues(
issue_handler = issue_handler_factory(issue_type, owner, repo, token)
# Load dataset
issues: list[GithubIssue] = issue_handler.get_converted_issues()
issues: list[GithubIssue] = issue_handler.get_converted_issues(
issue_numbers=issue_numbers
)
if issue_numbers is not None:
issues = [issue for issue in issues if issue.number in issue_numbers]
logger.info(f'Limiting resolving to issues {issue_numbers}.')
if limit_issues is not None:
issues = issues[:limit_issues]
logger.info(f'Limiting resolving to first {limit_issues} issues.')

View File

@@ -339,13 +339,10 @@ async def resolve_issue(
# Load dataset
issues: list[GithubIssue] = issue_handler.get_converted_issues(
comment_id=comment_id
issue_numbers=[issue_number], comment_id=comment_id
)
# Find the specific issue
issue = next((i for i in issues if i.number == issue_number), None)
if not issue:
raise ValueError(f'Issue {issue_number} not found')
issue = issues[0]
if comment_id is not None:
if (

View File

@@ -1,17 +1,18 @@
from unittest.mock import patch, MagicMock
from openhands.resolver.issue_definitions import IssueHandler, PRHandler
from openhands.resolver.github_issue import GithubIssue, ReviewThread
from openhands.events.action.message import MessageAction
from unittest.mock import MagicMock, patch
from openhands.core.config import LLMConfig
from openhands.events.action.message import MessageAction
from openhands.resolver.github_issue import GithubIssue, ReviewThread
from openhands.resolver.issue_definitions import IssueHandler, PRHandler
def test_get_converted_issues_initializes_review_comments():
# Mock the necessary dependencies
with patch("requests.get") as mock_get:
with patch('requests.get') as mock_get:
# Mock the response for issues
mock_issues_response = MagicMock()
mock_issues_response.json.return_value = [
{"number": 1, "title": "Test Issue", "body": "Test Body"}
{'number': 1, 'title': 'Test Issue', 'body': 'Test Body'}
]
# Mock the response for comments
mock_comments_response = MagicMock()
@@ -26,10 +27,10 @@ def test_get_converted_issues_initializes_review_comments():
] # Need two comment responses because we make two API calls
# Create an instance of IssueHandler
handler = IssueHandler("test-owner", "test-repo", "test-token")
handler = IssueHandler('test-owner', 'test-repo', 'test-token')
# Get converted issues
issues = handler.get_converted_issues()
issues = handler.get_converted_issues(issue_numbers=[1])
# Verify that we got exactly one issue
assert len(issues) == 1
@@ -39,35 +40,35 @@ def test_get_converted_issues_initializes_review_comments():
# Verify other fields are set correctly
assert issues[0].number == 1
assert issues[0].title == "Test Issue"
assert issues[0].body == "Test Body"
assert issues[0].owner == "test-owner"
assert issues[0].repo == "test-repo"
assert issues[0].title == 'Test Issue'
assert issues[0].body == 'Test Body'
assert issues[0].owner == 'test-owner'
assert issues[0].repo == 'test-repo'
def test_pr_handler_guess_success_with_thread_comments():
# Create a PR handler instance
handler = PRHandler("test-owner", "test-repo", "test-token")
handler = PRHandler('test-owner', 'test-repo', 'test-token')
# Create a mock issue with thread comments but no review comments
issue = GithubIssue(
owner="test-owner",
repo="test-repo",
owner='test-owner',
repo='test-repo',
number=1,
title="Test PR",
body="Test Body",
thread_comments=["First comment", "Second comment"],
closing_issues=["Issue description"],
title='Test PR',
body='Test Body',
thread_comments=['First comment', 'Second comment'],
closing_issues=['Issue description'],
review_comments=None,
thread_ids=None,
head_branch="test-branch",
head_branch='test-branch',
)
# Create mock history
history = [MessageAction(content="Fixed the issue by implementing X and Y")]
history = [MessageAction(content='Fixed the issue by implementing X and Y')]
# Create mock LLM config
llm_config = LLMConfig(model="test-model", api_key="test-key")
llm_config = LLMConfig(model='test-model', api_key='test-key')
# Mock the LLM response
mock_response = MagicMock()
@@ -84,7 +85,7 @@ The changes successfully address the feedback."""
]
# Test the guess_success method
with patch("litellm.completion", return_value=mock_response):
with patch('litellm.completion', return_value=mock_response):
success, success_list, explanation = handler.guess_success(
issue, history, llm_config
)
@@ -92,39 +93,39 @@ The changes successfully address the feedback."""
# Verify the results
assert success is True
assert success_list == [True]
assert "successfully address" in explanation
assert 'successfully address' in explanation
def test_pr_handler_get_converted_issues_with_comments():
# Mock the necessary dependencies
with patch("requests.get") as mock_get:
with patch('requests.get') as mock_get:
# Mock the response for PRs
mock_prs_response = MagicMock()
mock_prs_response.json.return_value = [
{
"number": 1,
"title": "Test PR",
"body": "Test Body fixes #1",
"head": {"ref": "test-branch"},
'number': 1,
'title': 'Test PR',
'body': 'Test Body fixes #1',
'head': {'ref': 'test-branch'},
}
]
# Mock the response for PR comments
mock_comments_response = MagicMock()
mock_comments_response.json.return_value = [
{"body": "First comment"},
{"body": "Second comment"},
{'body': 'First comment'},
{'body': 'Second comment'},
]
# Mock the response for PR metadata (GraphQL)
mock_graphql_response = MagicMock()
mock_graphql_response.json.return_value = {
"data": {
"repository": {
"pullRequest": {
"closingIssuesReferences": {"edges": []},
"reviews": {"nodes": []},
"reviewThreads": {"edges": []},
'data': {
'repository': {
'pullRequest': {
'closingIssuesReferences': {'edges': []},
'reviews': {'nodes': []},
'reviewThreads': {'edges': []},
}
}
}
@@ -138,7 +139,7 @@ def test_pr_handler_get_converted_issues_with_comments():
# Mock the response for fetching the external issue referenced in PR body
mock_external_issue_response = MagicMock()
mock_external_issue_response.json.return_value = {
"body": "This is additional context from an externally referenced issue."
'body': 'This is additional context from an externally referenced issue.'
}
mock_get.side_effect = [
@@ -150,56 +151,56 @@ def test_pr_handler_get_converted_issues_with_comments():
]
# Mock the post request for GraphQL
with patch("requests.post") as mock_post:
with patch('requests.post') as mock_post:
mock_post.return_value = mock_graphql_response
# Create an instance of PRHandler
handler = PRHandler("test-owner", "test-repo", "test-token")
handler = PRHandler('test-owner', 'test-repo', 'test-token')
# Get converted issues
prs = handler.get_converted_issues()
prs = handler.get_converted_issues(issue_numbers=[1])
# Verify that we got exactly one PR
assert len(prs) == 1
# Verify that thread_comments are set correctly
assert prs[0].thread_comments == ["First comment", "Second comment"]
assert prs[0].thread_comments == ['First comment', 'Second comment']
# Verify other fields are set correctly
assert prs[0].number == 1
assert prs[0].title == "Test PR"
assert prs[0].body == "Test Body fixes #1"
assert prs[0].owner == "test-owner"
assert prs[0].repo == "test-repo"
assert prs[0].head_branch == "test-branch"
assert prs[0].title == 'Test PR'
assert prs[0].body == 'Test Body fixes #1'
assert prs[0].owner == 'test-owner'
assert prs[0].repo == 'test-repo'
assert prs[0].head_branch == 'test-branch'
assert prs[0].closing_issues == [
"This is additional context from an externally referenced issue."
'This is additional context from an externally referenced issue.'
]
def test_pr_handler_guess_success_only_review_comments():
# Create a PR handler instance
handler = PRHandler("test-owner", "test-repo", "test-token")
handler = PRHandler('test-owner', 'test-repo', 'test-token')
# Create a mock issue with only review comments
issue = GithubIssue(
owner="test-owner",
repo="test-repo",
owner='test-owner',
repo='test-repo',
number=1,
title="Test PR",
body="Test Body",
title='Test PR',
body='Test Body',
thread_comments=None,
closing_issues=["Issue description"],
review_comments=["Please fix the formatting", "Add more tests"],
closing_issues=['Issue description'],
review_comments=['Please fix the formatting', 'Add more tests'],
thread_ids=None,
head_branch="test-branch",
head_branch='test-branch',
)
# Create mock history
history = [MessageAction(content="Fixed the formatting and added more tests")]
history = [MessageAction(content='Fixed the formatting and added more tests')]
# Create mock LLM config
llm_config = LLMConfig(model="test-model", api_key="test-key")
llm_config = LLMConfig(model='test-model', api_key='test-key')
# Mock the LLM response
mock_response = MagicMock()
@@ -216,7 +217,7 @@ The changes successfully address the review comments."""
]
# Test the guess_success method
with patch("litellm.completion", return_value=mock_response):
with patch('litellm.completion', return_value=mock_response):
success, success_list, explanation = handler.guess_success(
issue, history, llm_config
)
@@ -224,32 +225,32 @@ The changes successfully address the review comments."""
# Verify the results
assert success is True
assert success_list == [True]
assert "successfully address" in explanation
assert 'successfully address' in explanation
def test_pr_handler_guess_success_no_comments():
# Create a PR handler instance
handler = PRHandler("test-owner", "test-repo", "test-token")
handler = PRHandler('test-owner', 'test-repo', 'test-token')
# Create a mock issue with no comments
issue = GithubIssue(
owner="test-owner",
repo="test-repo",
owner='test-owner',
repo='test-repo',
number=1,
title="Test PR",
body="Test Body",
title='Test PR',
body='Test Body',
thread_comments=None,
closing_issues=["Issue description"],
closing_issues=['Issue description'],
review_comments=None,
thread_ids=None,
head_branch="test-branch",
head_branch='test-branch',
)
# Create mock history
history = [MessageAction(content="Fixed the issue")]
history = [MessageAction(content='Fixed the issue')]
# Create mock LLM config
llm_config = LLMConfig(model="test-model", api_key="test-key")
llm_config = LLMConfig(model='test-model', api_key='test-key')
# Test that it returns appropriate message when no comments are present
success, success_list, explanation = handler.guess_success(
@@ -257,29 +258,29 @@ def test_pr_handler_guess_success_no_comments():
)
assert success is False
assert success_list is None
assert explanation == "No feedback was found to process"
assert explanation == 'No feedback was found to process'
def test_get_issue_comments_with_specific_comment_id():
# Mock the necessary dependencies
with patch("requests.get") as mock_get:
with patch('requests.get') as mock_get:
# Mock the response for comments
mock_comments_response = MagicMock()
mock_comments_response.json.return_value = [
{"id": 123, "body": "First comment"},
{"id": 456, "body": "Second comment"},
{'id': 123, 'body': 'First comment'},
{'id': 456, 'body': 'Second comment'},
]
mock_get.return_value = mock_comments_response
# Create an instance of IssueHandler
handler = IssueHandler("test-owner", "test-repo", "test-token")
handler = IssueHandler('test-owner', 'test-repo', 'test-token')
# Get comments with a specific comment_id
specific_comment = handler._get_issue_comments(issue_number=1, comment_id=123)
# Verify only the specific comment is returned
assert specific_comment == ["First comment"]
assert specific_comment == ['First comment']
def test_pr_handler_get_converted_issues_with_specific_thread_comment():
@@ -287,50 +288,50 @@ def test_pr_handler_get_converted_issues_with_specific_thread_comment():
specific_comment_id = 123
# Mock GraphQL response for review threads
with patch("requests.get") as mock_get:
with patch('requests.get') as mock_get:
# Mock the response for PRs
mock_prs_response = MagicMock()
mock_prs_response.json.return_value = [
{
"number": 1,
"title": "Test PR",
"body": "Test Body",
"head": {"ref": "test-branch"},
'number': 1,
'title': 'Test PR',
'body': 'Test Body',
'head': {'ref': 'test-branch'},
}
]
# Mock the response for PR comments
mock_comments_response = MagicMock()
mock_comments_response.json.return_value = [
{"body": "First comment", "id": 123},
{"body": "Second comment", "id": 124},
{'body': 'First comment', 'id': 123},
{'body': 'Second comment', 'id': 124},
]
# Mock the response for PR metadata (GraphQL)
mock_graphql_response = MagicMock()
mock_graphql_response.json.return_value = {
"data": {
"repository": {
"pullRequest": {
"closingIssuesReferences": {"edges": []},
"reviews": {"nodes": []},
"reviewThreads": {
"edges": [
'data': {
'repository': {
'pullRequest': {
'closingIssuesReferences': {'edges': []},
'reviews': {'nodes': []},
'reviewThreads': {
'edges': [
{
"node": {
"id": "review-thread-1",
"isResolved": False,
"comments": {
"nodes": [
'node': {
'id': 'review-thread-1',
'isResolved': False,
'comments': {
'nodes': [
{
"fullDatabaseId": 121,
"body": "Specific review comment",
"path": "file1.txt",
'fullDatabaseId': 121,
'body': 'Specific review comment',
'path': 'file1.txt',
},
{
"fullDatabaseId": 456,
"body": "Another review comment",
"path": "file2.txt",
'fullDatabaseId': 456,
'body': 'Another review comment',
'path': 'file2.txt',
},
]
},
@@ -356,30 +357,32 @@ def test_pr_handler_get_converted_issues_with_specific_thread_comment():
]
# Mock the post request for GraphQL
with patch("requests.post") as mock_post:
with patch('requests.post') as mock_post:
mock_post.return_value = mock_graphql_response
# Create an instance of PRHandler
handler = PRHandler("test-owner", "test-repo", "test-token")
handler = PRHandler('test-owner', 'test-repo', 'test-token')
# Get converted issues
prs = handler.get_converted_issues(comment_id=specific_comment_id)
prs = handler.get_converted_issues(
issue_numbers=[1], comment_id=specific_comment_id
)
# Verify that we got exactly one PR
assert len(prs) == 1
# Verify that thread_comments are set correctly
assert prs[0].thread_comments == ["First comment"]
assert prs[0].thread_comments == ['First comment']
assert prs[0].review_comments == []
assert prs[0].review_threads == []
# Verify other fields are set correctly
assert prs[0].number == 1
assert prs[0].title == "Test PR"
assert prs[0].body == "Test Body"
assert prs[0].owner == "test-owner"
assert prs[0].repo == "test-repo"
assert prs[0].head_branch == "test-branch"
assert prs[0].title == 'Test PR'
assert prs[0].body == 'Test Body'
assert prs[0].owner == 'test-owner'
assert prs[0].repo == 'test-repo'
assert prs[0].head_branch == 'test-branch'
def test_pr_handler_get_converted_issues_with_specific_review_thread_comment():
@@ -387,50 +390,50 @@ def test_pr_handler_get_converted_issues_with_specific_review_thread_comment():
specific_comment_id = 123
# Mock GraphQL response for review threads
with patch("requests.get") as mock_get:
with patch('requests.get') as mock_get:
# Mock the response for PRs
mock_prs_response = MagicMock()
mock_prs_response.json.return_value = [
{
"number": 1,
"title": "Test PR",
"body": "Test Body",
"head": {"ref": "test-branch"},
'number': 1,
'title': 'Test PR',
'body': 'Test Body',
'head': {'ref': 'test-branch'},
}
]
# Mock the response for PR comments
mock_comments_response = MagicMock()
mock_comments_response.json.return_value = [
{"body": "First comment", "id": 120},
{"body": "Second comment", "id": 124},
{'body': 'First comment', 'id': 120},
{'body': 'Second comment', 'id': 124},
]
# Mock the response for PR metadata (GraphQL)
mock_graphql_response = MagicMock()
mock_graphql_response.json.return_value = {
"data": {
"repository": {
"pullRequest": {
"closingIssuesReferences": {"edges": []},
"reviews": {"nodes": []},
"reviewThreads": {
"edges": [
'data': {
'repository': {
'pullRequest': {
'closingIssuesReferences': {'edges': []},
'reviews': {'nodes': []},
'reviewThreads': {
'edges': [
{
"node": {
"id": "review-thread-1",
"isResolved": False,
"comments": {
"nodes": [
'node': {
'id': 'review-thread-1',
'isResolved': False,
'comments': {
'nodes': [
{
"fullDatabaseId": specific_comment_id,
"body": "Specific review comment",
"path": "file1.txt",
'fullDatabaseId': specific_comment_id,
'body': 'Specific review comment',
'path': 'file1.txt',
},
{
"fullDatabaseId": 456,
"body": "Another review comment",
"path": "file1.txt",
'fullDatabaseId': 456,
'body': 'Another review comment',
'path': 'file1.txt',
},
]
},
@@ -456,14 +459,16 @@ def test_pr_handler_get_converted_issues_with_specific_review_thread_comment():
]
# Mock the post request for GraphQL
with patch("requests.post") as mock_post:
with patch('requests.post') as mock_post:
mock_post.return_value = mock_graphql_response
# Create an instance of PRHandler
handler = PRHandler("test-owner", "test-repo", "test-token")
handler = PRHandler('test-owner', 'test-repo', 'test-token')
# Get converted issues
prs = handler.get_converted_issues(comment_id=specific_comment_id)
prs = handler.get_converted_issues(
issue_numbers=[1], comment_id=specific_comment_id
)
# Verify that we got exactly one PR
assert len(prs) == 1
@@ -475,17 +480,17 @@ def test_pr_handler_get_converted_issues_with_specific_review_thread_comment():
assert isinstance(prs[0].review_threads[0], ReviewThread)
assert (
prs[0].review_threads[0].comment
== "Specific review comment\n---\nlatest feedback:\nAnother review comment\n"
== 'Specific review comment\n---\nlatest feedback:\nAnother review comment\n'
)
assert prs[0].review_threads[0].files == ["file1.txt"]
assert prs[0].review_threads[0].files == ['file1.txt']
# Verify other fields are set correctly
assert prs[0].number == 1
assert prs[0].title == "Test PR"
assert prs[0].body == "Test Body"
assert prs[0].owner == "test-owner"
assert prs[0].repo == "test-repo"
assert prs[0].head_branch == "test-branch"
assert prs[0].title == 'Test PR'
assert prs[0].body == 'Test Body'
assert prs[0].owner == 'test-owner'
assert prs[0].repo == 'test-repo'
assert prs[0].head_branch == 'test-branch'
def test_pr_handler_get_converted_issues_with_specific_comment_and_issue_refs():
@@ -493,50 +498,50 @@ def test_pr_handler_get_converted_issues_with_specific_comment_and_issue_refs():
specific_comment_id = 123
# Mock GraphQL response for review threads
with patch("requests.get") as mock_get:
with patch('requests.get') as mock_get:
# Mock the response for PRs
mock_prs_response = MagicMock()
mock_prs_response.json.return_value = [
{
"number": 1,
"title": "Test PR fixes #3",
"body": "Test Body",
"head": {"ref": "test-branch"},
'number': 1,
'title': 'Test PR fixes #3',
'body': 'Test Body',
'head': {'ref': 'test-branch'},
}
]
# Mock the response for PR comments
mock_comments_response = MagicMock()
mock_comments_response.json.return_value = [
{"body": "First comment", "id": 120},
{"body": "Second comment", "id": 124},
{'body': 'First comment', 'id': 120},
{'body': 'Second comment', 'id': 124},
]
# Mock the response for PR metadata (GraphQL)
mock_graphql_response = MagicMock()
mock_graphql_response.json.return_value = {
"data": {
"repository": {
"pullRequest": {
"closingIssuesReferences": {"edges": []},
"reviews": {"nodes": []},
"reviewThreads": {
"edges": [
'data': {
'repository': {
'pullRequest': {
'closingIssuesReferences': {'edges': []},
'reviews': {'nodes': []},
'reviewThreads': {
'edges': [
{
"node": {
"id": "review-thread-1",
"isResolved": False,
"comments": {
"nodes": [
'node': {
'id': 'review-thread-1',
'isResolved': False,
'comments': {
'nodes': [
{
"fullDatabaseId": specific_comment_id,
"body": "Specific review comment that references #6",
"path": "file1.txt",
'fullDatabaseId': specific_comment_id,
'body': 'Specific review comment that references #6',
'path': 'file1.txt',
},
{
"fullDatabaseId": 456,
"body": "Another review comment referencing #7",
"path": "file2.txt",
'fullDatabaseId': 456,
'body': 'Another review comment referencing #7',
'path': 'file2.txt',
},
]
},
@@ -557,13 +562,13 @@ def test_pr_handler_get_converted_issues_with_specific_comment_and_issue_refs():
# Mock the response for fetching the external issue referenced in PR body
mock_external_issue_response_in_body = MagicMock()
mock_external_issue_response_in_body.json.return_value = {
"body": "External context #1."
'body': 'External context #1.'
}
# Mock the response for fetching the external issue referenced in review thread
mock_external_issue_response_review_thread = MagicMock()
mock_external_issue_response_review_thread.json.return_value = {
"body": "External context #2."
'body': 'External context #2.'
}
mock_get.side_effect = [
@@ -576,14 +581,16 @@ def test_pr_handler_get_converted_issues_with_specific_comment_and_issue_refs():
]
# Mock the post request for GraphQL
with patch("requests.post") as mock_post:
with patch('requests.post') as mock_post:
mock_post.return_value = mock_graphql_response
# Create an instance of PRHandler
handler = PRHandler("test-owner", "test-repo", "test-token")
handler = PRHandler('test-owner', 'test-repo', 'test-token')
# Get converted issues
prs = handler.get_converted_issues(comment_id=specific_comment_id)
prs = handler.get_converted_issues(
issue_numbers=[1], comment_id=specific_comment_id
)
# Verify that we got exactly one PR
assert len(prs) == 1
@@ -595,52 +602,52 @@ def test_pr_handler_get_converted_issues_with_specific_comment_and_issue_refs():
assert isinstance(prs[0].review_threads[0], ReviewThread)
assert (
prs[0].review_threads[0].comment
== "Specific review comment that references #6\n---\nlatest feedback:\nAnother review comment referencing #7\n"
== 'Specific review comment that references #6\n---\nlatest feedback:\nAnother review comment referencing #7\n'
)
assert prs[0].closing_issues == [
"External context #1.",
"External context #2.",
'External context #1.',
'External context #2.',
] # Only includes references inside comment ID and body PR
# Verify other fields are set correctly
assert prs[0].number == 1
assert prs[0].title == "Test PR fixes #3"
assert prs[0].body == "Test Body"
assert prs[0].owner == "test-owner"
assert prs[0].repo == "test-repo"
assert prs[0].head_branch == "test-branch"
assert prs[0].title == 'Test PR fixes #3'
assert prs[0].body == 'Test Body'
assert prs[0].owner == 'test-owner'
assert prs[0].repo == 'test-repo'
assert prs[0].head_branch == 'test-branch'
def test_pr_handler_get_converted_issues_with_duplicate_issue_refs():
# Mock the necessary dependencies
with patch("requests.get") as mock_get:
with patch('requests.get') as mock_get:
# Mock the response for PRs
mock_prs_response = MagicMock()
mock_prs_response.json.return_value = [
{
"number": 1,
"title": "Test PR",
"body": "Test Body fixes #1",
"head": {"ref": "test-branch"},
'number': 1,
'title': 'Test PR',
'body': 'Test Body fixes #1',
'head': {'ref': 'test-branch'},
}
]
# Mock the response for PR comments
mock_comments_response = MagicMock()
mock_comments_response.json.return_value = [
{"body": "First comment addressing #1"},
{"body": "Second comment addressing #2"},
{'body': 'First comment addressing #1'},
{'body': 'Second comment addressing #2'},
]
# Mock the response for PR metadata (GraphQL)
mock_graphql_response = MagicMock()
mock_graphql_response.json.return_value = {
"data": {
"repository": {
"pullRequest": {
"closingIssuesReferences": {"edges": []},
"reviews": {"nodes": []},
"reviewThreads": {"edges": []},
'data': {
'repository': {
'pullRequest': {
'closingIssuesReferences': {'edges': []},
'reviews': {'nodes': []},
'reviewThreads': {'edges': []},
}
}
}
@@ -654,13 +661,13 @@ def test_pr_handler_get_converted_issues_with_duplicate_issue_refs():
# Mock the response for fetching the external issue referenced in PR body
mock_external_issue_response_in_body = MagicMock()
mock_external_issue_response_in_body.json.return_value = {
"body": "External context #1."
'body': 'External context #1.'
}
# Mock the response for fetching the external issue referenced in review thread
mock_external_issue_response_in_comment = MagicMock()
mock_external_issue_response_in_comment.json.return_value = {
"body": "External context #2."
'body': 'External context #2.'
}
mock_get.side_effect = [
@@ -673,32 +680,32 @@ def test_pr_handler_get_converted_issues_with_duplicate_issue_refs():
]
# Mock the post request for GraphQL
with patch("requests.post") as mock_post:
with patch('requests.post') as mock_post:
mock_post.return_value = mock_graphql_response
# Create an instance of PRHandler
handler = PRHandler("test-owner", "test-repo", "test-token")
handler = PRHandler('test-owner', 'test-repo', 'test-token')
# Get converted issues
prs = handler.get_converted_issues()
prs = handler.get_converted_issues(issue_numbers=[1])
# Verify that we got exactly one PR
assert len(prs) == 1
# Verify that thread_comments are set correctly
assert prs[0].thread_comments == [
"First comment addressing #1",
"Second comment addressing #2",
'First comment addressing #1',
'Second comment addressing #2',
]
# Verify other fields are set correctly
assert prs[0].number == 1
assert prs[0].title == "Test PR"
assert prs[0].body == "Test Body fixes #1"
assert prs[0].owner == "test-owner"
assert prs[0].repo == "test-repo"
assert prs[0].head_branch == "test-branch"
assert prs[0].title == 'Test PR'
assert prs[0].body == 'Test Body fixes #1'
assert prs[0].owner == 'test-owner'
assert prs[0].repo == 'test-repo'
assert prs[0].head_branch == 'test-branch'
assert prs[0].closing_issues == [
"External context #1.",
"External context #2.",
'External context #1.',
'External context #2.',
]

View File

@@ -112,7 +112,7 @@ def test_download_issues_from_github():
return mock_issues_response
with patch('requests.get', side_effect=get_mock_response):
issues = handler.get_converted_issues()
issues = handler.get_converted_issues(issue_numbers=[1, 3])
assert len(issues) == 2
assert handler.issue_type == 'issue'
@@ -225,7 +225,7 @@ def test_download_pr_from_github():
with patch('requests.get', side_effect=get_mock_response):
with patch('requests.post', return_value=mock_graphql_response):
issues = handler.get_converted_issues()
issues = handler.get_converted_issues(issue_numbers=[1, 2, 3])
assert len(issues) == 3
assert handler.issue_type == 'pr'
@@ -811,7 +811,7 @@ def test_download_pr_with_review_comments():
with patch('requests.get', side_effect=get_mock_response):
with patch('requests.post', return_value=mock_graphql_response):
issues = handler.get_converted_issues()
issues = handler.get_converted_issues(issue_numbers=[1])
assert len(issues) == 1
assert handler.issue_type == 'pr'
@@ -867,7 +867,9 @@ def test_download_issue_with_specific_comment():
return mock_issue_response
with patch('requests.get', side_effect=get_mock_response):
issues = handler.get_converted_issues(comment_id=specific_comment_id)
issues = handler.get_converted_issues(
issue_numbers=[1], comment_id=specific_comment_id
)
assert len(issues) == 1
assert issues[0].number == 1