From 19b505a2778e7c236552c94bc395c07d63639961 Mon Sep 17 00:00:00 2001 From: mkdev11 Date: Mon, 4 May 2026 22:36:57 +0200 Subject: [PATCH] fix issue bounty closure reason gate --- gittensor/utils/github_api_tools.py | 12 ++++++++++ tests/utils/test_github_api_tools.py | 33 ++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/gittensor/utils/github_api_tools.py b/gittensor/utils/github_api_tools.py index f1220afb7..54138658e 100644 --- a/gittensor/utils/github_api_tools.py +++ b/gittensor/utils/github_api_tools.py @@ -1052,6 +1052,18 @@ def check_github_issue_closed(repo: str, issue_number: int, token: str) -> Optio if data.get('state') != 'closed': return {'is_closed': False} + state_reason = data.get('state_reason') + if not isinstance(state_reason, str) or state_reason.strip().lower() != 'completed': + bt.logging.info( + f'Issue closed on GitHub but not completed: {repo}#{issue_number} state_reason={state_reason}' + ) + return { + 'is_closed': True, + 'solver_github_id': None, + 'pr_number': None, + 'solver_lookup_failed': False, + } + bt.logging.debug(f'Finding solver for {repo}#{issue_number}') solver_lookup = find_solver_from_cross_references(repo, issue_number, token) if solver_lookup is None: diff --git a/tests/utils/test_github_api_tools.py b/tests/utils/test_github_api_tools.py index 7f74a0b2f..d516788f1 100644 --- a/tests/utils/test_github_api_tools.py +++ b/tests/utils/test_github_api_tools.py @@ -1004,7 +1004,7 @@ class TestCheckGithubIssueClosed: def test_graphql_failure_sets_solver_lookup_failed(self, mock_logging, mock_get, mock_graphql): issue_response = Mock() issue_response.status_code = 200 - issue_response.json.return_value = {'state': 'closed'} + issue_response.json.return_value = {'state': 'closed', 'state_reason': 'completed'} mock_get.return_value = issue_response mock_graphql.return_value = None @@ -1023,7 +1023,7 @@ def test_graphql_failure_sets_solver_lookup_failed(self, mock_logging, mock_get, def test_closed_issue_with_no_solver_keeps_lookup_failed_false(self, mock_logging, mock_get, mock_graphql): issue_response = Mock() issue_response.status_code = 200 - issue_response.json.return_value = {'state': 'closed'} + issue_response.json.return_value = {'state': 'closed', 'state_reason': 'completed'} mock_get.return_value = issue_response mock_graphql.return_value = _graphql_response([]) @@ -1036,6 +1036,35 @@ def test_closed_issue_with_no_solver_keeps_lookup_failed_false(self, mock_loggin 'solver_lookup_failed': False, } + @pytest.mark.parametrize( + 'issue_payload', + [ + {'state': 'closed', 'state_reason': 'not_planned'}, + {'state': 'closed', 'state_reason': 'duplicate'}, + {'state': 'closed', 'state_reason': 'transferred'}, + {'state': 'closed', 'state_reason': None}, + {'state': 'closed'}, + ], + ) + @patch('gittensor.utils.github_api_tools.execute_graphql_query') + @patch('gittensor.utils.github_api_tools.requests.get') + @patch('gittensor.utils.github_api_tools.bt.logging') + def test_non_completed_closed_issue_skips_solver_lookup(self, mock_logging, mock_get, mock_graphql, issue_payload): + issue_response = Mock() + issue_response.status_code = 200 + issue_response.json.return_value = issue_payload + mock_get.return_value = issue_response + + result = check_github_issue_closed('owner/repo', 12, 'fake_token') + + assert result == { + 'is_closed': True, + 'solver_github_id': None, + 'pr_number': None, + 'solver_lookup_failed': False, + } + mock_graphql.assert_not_called() + # ============================================================================ # load_miners_prs Per-PR Error Resilience Tests