Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions gittensor/cli/issue_commands/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,12 @@ def fetch_open_issue_pull_requests(
repository_full_name: str,
issue_number: int,
as_json: bool,
) -> list:
"""Fetch open PR submissions for a GitHub issue."""
) -> list | None:
"""Fetch open PR submissions for a GitHub issue.

Returns:
PR list on success (possibly empty), or ``None`` when GraphQL lookup failed.
"""
token = os.environ.get('GITTENSOR_MINER_PAT') or ''
if not token and not as_json:
print_warning('No GitHub token found; set GITTENSOR_MINER_PAT to fetch GitHub issue submissions')
Expand All @@ -270,7 +274,8 @@ def fetch_open_issue_pull_requests(
token=token or None,
open_only=True,
)
# Intentionally return GitHub tool output as-is (no CLI schema mapping yet).
if prs is None:
return None
return prs
except Exception as e:
raise click.ClickException(f'Failed to fetch PR submissions from GitHub: {e}')
Expand Down
7 changes: 7 additions & 0 deletions gittensor/cli/issue_commands/submissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ def issues_submissions(
except click.ClickException as e:
handle_exception(as_json, str(e), click_error_type(e))

if pull_requests is None:
handle_exception(
as_json,
f'Failed to fetch PR submissions from GitHub for {repo_name}#{issue_number} (GraphQL lookup failed).',
'read_failed',
)

if as_json:
submissions = [
{
Expand Down
15 changes: 11 additions & 4 deletions gittensor/utils/github_api_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,14 +329,21 @@ def find_prs_for_issue(
issue_number: int,
open_only: bool = True,
token: Optional[str] = None,
) -> List[PRInfo]:
"""Find PRs that reference an issue via GraphQL cross-reference data."""
) -> Optional[List[PRInfo]]:
"""Find PRs that reference an issue via GraphQL cross-reference data.

Returns:
List of PR info dicts when lookup succeeds (possibly empty).
``None`` when GraphQL lookup fails and callers should not treat the
result as "zero submissions".
Empty list when no token is available (lookup was not attempted).
"""
if token:
try:
prs = _search_issue_referencing_prs_graphql(repo, issue_number, token, open_only=open_only)
return prs or []
return _search_issue_referencing_prs_graphql(repo, issue_number, token, open_only=open_only)
except Exception as exc:
bt.logging.debug(f'GraphQL PR fetch failed for {repo}#{issue_number}: {exc}')
return None

return []

Expand Down
21 changes: 21 additions & 0 deletions tests/cli/test_issue_submission.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,27 @@ def test_submissions_json_contract_read_failure_returns_structured_error(cli_roo
assert 'not found on-chain' not in payload['error']['message']


def test_submissions_json_github_lookup_failure_returns_read_failed(cli_root, runner, sample_issue):
with (
patch('gittensor.cli.issue_commands.submissions.get_contract_address', return_value='0xabc'),
patch('gittensor.cli.issue_commands.submissions.resolve_network', return_value=('ws://x', 'test')),
patch('gittensor.cli.issue_commands.submissions.fetch_issue_from_contract', return_value=sample_issue),
patch('gittensor.cli.issue_commands.submissions.fetch_open_issue_pull_requests', return_value=None),
):
result = runner.invoke(
cli_root,
['issues', 'submissions', '--id', '42', '--json'],
catch_exceptions=False,
)

assert result.exit_code != 0
payload = json.loads(result.stdout)
assert payload['success'] is False
assert payload['error']['type'] == 'read_failed'
assert 'GraphQL lookup failed' in payload['error']['message']
assert 'submission_count' not in payload


def test_submissions_help_via_issue_alias_routes_to_command_help(cli_root, runner):
result = runner.invoke(
cli_root,
Expand Down
14 changes: 12 additions & 2 deletions tests/utils/test_github_api_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,22 @@ def test_find_prs_returns_empty_when_graphql_empty(mock_graphql):


@patch('gittensor.utils.github_api_tools._search_issue_referencing_prs_graphql')
def test_find_prs_returns_empty_when_graphql_errors(mock_graphql):
def test_find_prs_returns_none_when_graphql_errors(mock_graphql):
mock_graphql.side_effect = RuntimeError('boom')

result = find_prs_for_issue('owner/repo', 12, open_only=True, token='fake_token')

assert result == []
assert result is None
mock_graphql.assert_called_once_with('owner/repo', 12, 'fake_token', open_only=True)


@patch('gittensor.utils.github_api_tools._search_issue_referencing_prs_graphql')
def test_find_prs_returns_none_when_graphql_lookup_fails(mock_graphql):
mock_graphql.return_value = None

result = find_prs_for_issue('owner/repo', 12, open_only=True, token='fake_token')

assert result is None
mock_graphql.assert_called_once_with('owner/repo', 12, 'fake_token', open_only=True)


Expand Down