diff --git a/api/queries_pr_review.go b/api/queries_pr_review.go index f6ef2a74e..3d0132cbd 100644 --- a/api/queries_pr_review.go +++ b/api/queries_pr_review.go @@ -413,6 +413,9 @@ func SuggestedReviewerActors(client *Client, repo ghrepo.Interface, prID string, type responseData struct { Node struct { PullRequest struct { + Author struct { + Login string + } SuggestedActors struct { Nodes []struct { IsAuthor bool @@ -472,7 +475,11 @@ func SuggestedReviewerActors(client *Client, repo ghrepo.Interface, prID string, // Build candidates using cascading quota logic: // Each source has a base quota of 5, plus any unfilled quota from previous sources. // This ensures we show up to 15 total candidates, filling gaps when earlier sources have fewer. + // Pre-seed seen with the PR author since you cannot review your own PR. seen := make(map[string]bool) + if authorLogin := result.Node.PullRequest.Author.Login; authorLogin != "" { + seen[authorLogin] = true + } var candidates []ReviewerCandidate const baseQuota = 5 diff --git a/api/queries_pr_test.go b/api/queries_pr_test.go index 7b7727f3e..8fff120bb 100644 --- a/api/queries_pr_test.go +++ b/api/queries_pr_test.go @@ -160,7 +160,7 @@ func mockReviewerResponse(suggestions, collabs, teams, totalCollabs, totalTeams return fmt.Sprintf(`{ "data": { - "node": {"suggestedReviewerActors": {"nodes": [%s]}}, + "node": {"author": {"login": "testauthor"}, "suggestedReviewerActors": {"nodes": [%s]}}, "repository": { "collaborators": {"nodes": [%s]}, "collaboratorsTotalCount": {"totalCount": %d} @@ -235,7 +235,7 @@ func TestSuggestedReviewerActors(t *testing.T) { httpmock.GraphQL(`query SuggestedReviewerActors\b`), httpmock.StringResponse(`{ "data": { - "node": {"suggestedReviewerActors": {"nodes": [ + "node": {"author": {"login": "testauthor"}, "suggestedReviewerActors": {"nodes": [ {"isAuthor": true, "reviewer": {"__typename": "User", "login": "author", "name": "Author"}}, {"isAuthor": false, "reviewer": {"__typename": "User", "login": "s1", "name": "S1"}}, {"isAuthor": false, "reviewer": {"__typename": "User", "login": "s2", "name": "S2"}} @@ -255,6 +255,34 @@ func TestSuggestedReviewerActors(t *testing.T) { expectedLogins: []string{"s1", "s2", "c1", "OWNER/team1"}, expectedMore: 8, }, + { + name: "author excluded from collaborators", + httpStubs: func(reg *httpmock.Registry) { + reg.Register( + httpmock.GraphQL(`query SuggestedReviewerActors\b`), + httpmock.StringResponse(`{ + "data": { + "node": {"author": {"login": "theauthor"}, "suggestedReviewerActors": {"nodes": [ + {"isAuthor": false, "reviewer": {"__typename": "User", "login": "s1", "name": "S1"}} + ]}}, + "repository": { + "collaborators": {"nodes": [ + {"login": "theauthor", "name": "The Author"}, + {"login": "c1", "name": "C1"} + ]}, + "collaboratorsTotalCount": {"totalCount": 5} + }, + "organization": { + "teams": {"nodes": []}, + "teamsTotalCount": {"totalCount": 0} + } + } + }`)) + }, + expectedCount: 2, + expectedLogins: []string{"s1", "c1"}, + expectedMore: 5, + }, { name: "deduplication across sources", httpStubs: func(reg *httpmock.Registry) { @@ -263,7 +291,7 @@ func TestSuggestedReviewerActors(t *testing.T) { httpmock.GraphQL(`query SuggestedReviewerActors\b`), httpmock.StringResponse(`{ "data": { - "node": {"suggestedReviewerActors": {"nodes": [ + "node": {"author": {"login": "testauthor"}, "suggestedReviewerActors": {"nodes": [ {"isAuthor": false, "reviewer": {"__typename": "User", "login": "shareduser", "name": "Shared"}} ]}}, "repository": { @@ -291,7 +319,7 @@ func TestSuggestedReviewerActors(t *testing.T) { httpmock.GraphQL(`query SuggestedReviewerActors\b`), httpmock.StringResponse(`{ "data": { - "node": {"suggestedReviewerActors": {"nodes": [ + "node": {"author": {"login": "testauthor"}, "suggestedReviewerActors": {"nodes": [ {"isAuthor": false, "reviewer": {"__typename": "User", "login": "s1", "name": "S1"}} ]}}, "repository": { @@ -314,7 +342,7 @@ func TestSuggestedReviewerActors(t *testing.T) { httpmock.GraphQL(`query SuggestedReviewerActors\b`), httpmock.StringResponse(`{ "data": { - "node": {"suggestedReviewerActors": {"nodes": [ + "node": {"author": {"login": "testauthor"}, "suggestedReviewerActors": {"nodes": [ {"isAuthor": false, "reviewer": {"__typename": "Bot", "login": "copilot-pull-request-reviewer"}}, {"isAuthor": false, "reviewer": {"__typename": "User", "login": "s1", "name": "S1"}} ]}},