From 6445c7a9fa30ef8875738e028e4a9efb71c114fe Mon Sep 17 00:00:00 2001 From: advancedresearcharray Date: Sat, 6 Jun 2026 20:32:32 +0000 Subject: [PATCH 1/2] fix: add authorAssociation to IssueFragment (GraphQL path) Move authorAssociation onto the Issue GraphQL node (not Actor.author) per review feedback on #2265, populate it in fragmentToMinimalIssue and the legacy GraphQL path, and add author_association to MinimalPullRequest on the REST path (fixes #2250). --- pkg/github/issues.go | 22 +++++++++------- pkg/github/minimal_types.go | 52 ++++++++++++++++++++----------------- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/pkg/github/issues.go b/pkg/github/issues.go index ef9bbc4305..c9d3eb3dcf 100644 --- a/pkg/github/issues.go +++ b/pkg/github/issues.go @@ -480,11 +480,12 @@ func mergeIssueFieldValues(existing, incoming []*github.IssueRequestFieldValue) // IssueFragment represents a fragment of an issue node in the GraphQL API. type IssueFragment struct { - Number githubv4.Int - Title githubv4.String - Body githubv4.String - State githubv4.String - DatabaseID int64 + Number githubv4.Int + Title githubv4.String + Body githubv4.String + State githubv4.String + DatabaseID int64 + AuthorAssociation githubv4.String Author struct { Login githubv4.String @@ -616,11 +617,12 @@ func getIssueQueryType(hasLabels bool, hasSince bool) any { // is removed. type LegacyIssueFragment struct { - Number githubv4.Int - Title githubv4.String - Body githubv4.String - State githubv4.String - DatabaseID int64 + Number githubv4.Int + Title githubv4.String + Body githubv4.String + State githubv4.String + DatabaseID int64 + AuthorAssociation githubv4.String Author struct { Login githubv4.String diff --git a/pkg/github/minimal_types.go b/pkg/github/minimal_types.go index 5200be297f..0be7a85e88 100644 --- a/pkg/github/minimal_types.go +++ b/pkg/github/minimal_types.go @@ -396,6 +396,7 @@ type MinimalPullRequest struct { MergeableState string `json:"mergeable_state,omitempty"` HTMLURL string `json:"html_url"` User *MinimalUser `json:"user,omitempty"` + AuthorAssociation string `json:"author_association,omitempty"` Labels []string `json:"labels,omitempty"` Assignees []string `json:"assignees,omitempty"` RequestedReviewers []string `json:"requested_reviewers,omitempty"` @@ -557,13 +558,14 @@ func convertToMinimalIssue(issue *github.Issue) MinimalIssue { func fragmentToMinimalIssue(fragment IssueFragment) MinimalIssue { m := MinimalIssue{ - Number: int(fragment.Number), - Title: sanitize.Sanitize(string(fragment.Title)), - Body: sanitize.Sanitize(string(fragment.Body)), - State: string(fragment.State), - Comments: int(fragment.Comments.TotalCount), - CreatedAt: fragment.CreatedAt.Format(time.RFC3339), - UpdatedAt: fragment.UpdatedAt.Format(time.RFC3339), + Number: int(fragment.Number), + Title: sanitize.Sanitize(string(fragment.Title)), + Body: sanitize.Sanitize(string(fragment.Body)), + State: string(fragment.State), + Comments: int(fragment.Comments.TotalCount), + CreatedAt: fragment.CreatedAt.Format(time.RFC3339), + UpdatedAt: fragment.UpdatedAt.Format(time.RFC3339), + AuthorAssociation: string(fragment.AuthorAssociation), User: &MinimalUser{ Login: string(fragment.Author.Login), }, @@ -634,13 +636,14 @@ func convertToMinimalIssuesResponse(fragment IssueQueryFragment) MinimalIssuesRe // Legacy* block when the flag is removed. func legacyFragmentToMinimalIssue(fragment LegacyIssueFragment) MinimalIssue { m := MinimalIssue{ - Number: int(fragment.Number), - Title: sanitize.Sanitize(string(fragment.Title)), - Body: sanitize.Sanitize(string(fragment.Body)), - State: string(fragment.State), - Comments: int(fragment.Comments.TotalCount), - CreatedAt: fragment.CreatedAt.Format(time.RFC3339), - UpdatedAt: fragment.UpdatedAt.Format(time.RFC3339), + Number: int(fragment.Number), + Title: sanitize.Sanitize(string(fragment.Title)), + Body: sanitize.Sanitize(string(fragment.Body)), + State: string(fragment.State), + Comments: int(fragment.Comments.TotalCount), + CreatedAt: fragment.CreatedAt.Format(time.RFC3339), + UpdatedAt: fragment.UpdatedAt.Format(time.RFC3339), + AuthorAssociation: string(fragment.AuthorAssociation), User: &MinimalUser{ Login: string(fragment.Author.Login), }, @@ -744,16 +747,17 @@ func convertToMinimalFileContentResponse(resp *github.RepositoryContentResponse) func convertToMinimalPullRequest(pr *github.PullRequest) MinimalPullRequest { m := MinimalPullRequest{ - Number: pr.GetNumber(), - Title: pr.GetTitle(), - Body: pr.GetBody(), - State: pr.GetState(), - Draft: pr.GetDraft(), - Merged: pr.GetMerged(), - MergeableState: pr.GetMergeableState(), - HTMLURL: pr.GetHTMLURL(), - User: convertToMinimalUser(pr.GetUser()), - Additions: pr.GetAdditions(), + Number: pr.GetNumber(), + Title: pr.GetTitle(), + Body: pr.GetBody(), + State: pr.GetState(), + Draft: pr.GetDraft(), + Merged: pr.GetMerged(), + MergeableState: pr.GetMergeableState(), + HTMLURL: pr.GetHTMLURL(), + User: convertToMinimalUser(pr.GetUser()), + AuthorAssociation: pr.GetAuthorAssociation(), + Additions: pr.GetAdditions(), Deletions: pr.GetDeletions(), ChangedFiles: pr.GetChangedFiles(), Commits: pr.GetCommits(), From 9a21254177785bc8ca9366e846599000a44d69c3 Mon Sep 17 00:00:00 2001 From: advancedresearcharray Date: Sat, 6 Jun 2026 20:32:32 +0000 Subject: [PATCH 2/2] test: assert author_association in pull request responses Cover the REST-path MinimalPullRequest field added for #2250 in pull_request_read and list_pull_requests handler tests. --- pkg/github/issues_test.go | 85 ++++++++++++++++++--------------- pkg/github/pullrequests_test.go | 22 +++++---- 2 files changed, 61 insertions(+), 46 deletions(-) diff --git a/pkg/github/issues_test.go b/pkg/github/issues_test.go index 7e47cdb527..03bf4275f5 100644 --- a/pkg/github/issues_test.go +++ b/pkg/github/issues_test.go @@ -1848,14 +1848,15 @@ func Test_ListIssues(t *testing.T) { // Mock issues data mockIssuesAll := []map[string]any{ { - "number": 123, - "title": "First Issue", - "body": "This is the first test issue", - "state": "OPEN", - "databaseId": 1001, - "createdAt": "2023-01-01T00:00:00Z", - "updatedAt": "2023-01-01T00:00:00Z", - "author": map[string]any{"login": "user1"}, + "number": 123, + "title": "First Issue", + "body": "This is the first test issue", + "state": "OPEN", + "databaseId": 1001, + "authorAssociation": "MEMBER", + "createdAt": "2023-01-01T00:00:00Z", + "updatedAt": "2023-01-01T00:00:00Z", + "author": map[string]any{"login": "user1"}, "labels": map[string]any{ "nodes": []map[string]any{ {"name": "bug", "id": "label1", "description": "Bug label"}, @@ -1875,14 +1876,15 @@ func Test_ListIssues(t *testing.T) { }, }, { - "number": 456, - "title": "Second Issue", - "body": "This is the second test issue", - "state": "OPEN", - "databaseId": 1002, - "createdAt": "2023-02-01T00:00:00Z", - "updatedAt": "2023-02-01T00:00:00Z", - "author": map[string]any{"login": "user2"}, + "number": 456, + "title": "Second Issue", + "body": "This is the second test issue", + "state": "OPEN", + "databaseId": 1002, + "authorAssociation": "CONTRIBUTOR", + "createdAt": "2023-02-01T00:00:00Z", + "updatedAt": "2023-02-01T00:00:00Z", + "author": map[string]any{"login": "user2"}, "labels": map[string]any{ "nodes": []map[string]any{ {"name": "enhancement", "id": "label2", "description": "Enhancement label"}, @@ -1916,14 +1918,15 @@ func Test_ListIssues(t *testing.T) { mockIssuesOpen := []map[string]any{mockIssuesAll[0], mockIssuesAll[1]} mockIssuesClosed := []map[string]any{ { - "number": 789, - "title": "Closed Issue", - "body": "This is a closed issue", - "state": "CLOSED", - "databaseId": 1003, - "createdAt": "2023-03-01T00:00:00Z", - "updatedAt": "2023-03-01T00:00:00Z", - "author": map[string]any{"login": "user3"}, + "number": 789, + "title": "Closed Issue", + "body": "This is a closed issue", + "state": "CLOSED", + "databaseId": 1003, + "authorAssociation": "NONE", + "createdAt": "2023-03-01T00:00:00Z", + "updatedAt": "2023-03-01T00:00:00Z", + "author": map[string]any{"login": "user3"}, "labels": map[string]any{ "nodes": []map[string]any{}, }, @@ -2115,8 +2118,8 @@ func Test_ListIssues(t *testing.T) { // Define the actual query strings that match the implementation issueFieldValuesSelection := "issueFieldValues(first: 25){nodes{__typename,... on IssueFieldDateValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value},... on IssueFieldNumberValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},valueNumber: value},... on IssueFieldSingleSelectValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value},... on IssueFieldTextValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value}}}" - qBasicNoLabels := "query($after:String$direction:OrderDirection!$first:Int!$issueFieldValues:[IssueFieldValueFilter!]!$orderBy:IssueOrderField!$owner:String!$repo:String!$states:[IssueState!]!){repository(owner: $owner, name: $repo){issues(first: $first, after: $after, states: $states, orderBy: {field: $orderBy, direction: $direction}, filterBy: {issueFieldValues: $issueFieldValues}){nodes{number,title,body,state,databaseId,author{login},createdAt,updatedAt,labels(first: 100){nodes{name,id,description}},comments{totalCount}," + issueFieldValuesSelection + "},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount},isPrivate}}" - qWithLabels := "query($after:String$direction:OrderDirection!$first:Int!$issueFieldValues:[IssueFieldValueFilter!]!$labels:[String!]!$orderBy:IssueOrderField!$owner:String!$repo:String!$states:[IssueState!]!){repository(owner: $owner, name: $repo){issues(first: $first, after: $after, labels: $labels, states: $states, orderBy: {field: $orderBy, direction: $direction}, filterBy: {issueFieldValues: $issueFieldValues}){nodes{number,title,body,state,databaseId,author{login},createdAt,updatedAt,labels(first: 100){nodes{name,id,description}},comments{totalCount}," + issueFieldValuesSelection + "},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount},isPrivate}}" + qBasicNoLabels := "query($after:String$direction:OrderDirection!$first:Int!$issueFieldValues:[IssueFieldValueFilter!]!$orderBy:IssueOrderField!$owner:String!$repo:String!$states:[IssueState!]!){repository(owner: $owner, name: $repo){issues(first: $first, after: $after, states: $states, orderBy: {field: $orderBy, direction: $direction}, filterBy: {issueFieldValues: $issueFieldValues}){nodes{number,title,body,state,databaseId,authorAssociation,author{login},createdAt,updatedAt,labels(first: 100){nodes{name,id,description}},comments{totalCount}," + issueFieldValuesSelection + "},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount},isPrivate}}" + qWithLabels := "query($after:String$direction:OrderDirection!$first:Int!$issueFieldValues:[IssueFieldValueFilter!]!$labels:[String!]!$orderBy:IssueOrderField!$owner:String!$repo:String!$states:[IssueState!]!){repository(owner: $owner, name: $repo){issues(first: $first, after: $after, labels: $labels, states: $states, orderBy: {field: $orderBy, direction: $direction}, filterBy: {issueFieldValues: $issueFieldValues}){nodes{number,title,body,state,databaseId,authorAssociation,author{login},createdAt,updatedAt,labels(first: 100){nodes{name,id,description}},comments{totalCount}," + issueFieldValuesSelection + "},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount},isPrivate}}" for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { @@ -2193,13 +2196,17 @@ func Test_ListIssues(t *testing.T) { // (including float formatting); #789 has no field values. switch issue.Number { case 123: + assert.Equal(t, "MEMBER", issue.AuthorAssociation) assert.Equal(t, []MinimalFieldValue{{Field: "priority", Value: "P1"}}, issue.FieldValues) case 456: + assert.Equal(t, "CONTRIBUTOR", issue.AuthorAssociation) assert.Equal(t, []MinimalFieldValue{ {Field: "due", Value: "2026-06-01"}, {Field: "estimate", Value: "2.5"}, {Field: "notes", Value: "needs triage"}, }, issue.FieldValues) + case 789: + assert.Equal(t, "NONE", issue.AuthorAssociation) default: assert.Empty(t, issue.FieldValues) } @@ -2299,8 +2306,8 @@ func Test_ListIssues_FieldFilters(t *testing.T) { ) } - qNoLabels := "query($after:String$direction:OrderDirection!$first:Int!$issueFieldValues:[IssueFieldValueFilter!]!$orderBy:IssueOrderField!$owner:String!$repo:String!$states:[IssueState!]!){repository(owner: $owner, name: $repo){issues(first: $first, after: $after, states: $states, orderBy: {field: $orderBy, direction: $direction}, filterBy: {issueFieldValues: $issueFieldValues}){nodes{number,title,body,state,databaseId,author{login},createdAt,updatedAt,labels(first: 100){nodes{name,id,description}},comments{totalCount},issueFieldValues(first: 25){nodes{__typename,... on IssueFieldDateValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value},... on IssueFieldNumberValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},valueNumber: value},... on IssueFieldSingleSelectValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value},... on IssueFieldTextValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value}}}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount},isPrivate}}" - qWithLabels := "query($after:String$direction:OrderDirection!$first:Int!$issueFieldValues:[IssueFieldValueFilter!]!$labels:[String!]!$orderBy:IssueOrderField!$owner:String!$repo:String!$states:[IssueState!]!){repository(owner: $owner, name: $repo){issues(first: $first, after: $after, labels: $labels, states: $states, orderBy: {field: $orderBy, direction: $direction}, filterBy: {issueFieldValues: $issueFieldValues}){nodes{number,title,body,state,databaseId,author{login},createdAt,updatedAt,labels(first: 100){nodes{name,id,description}},comments{totalCount},issueFieldValues(first: 25){nodes{__typename,... on IssueFieldDateValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value},... on IssueFieldNumberValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},valueNumber: value},... on IssueFieldSingleSelectValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value},... on IssueFieldTextValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value}}}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount},isPrivate}}" + qNoLabels := "query($after:String$direction:OrderDirection!$first:Int!$issueFieldValues:[IssueFieldValueFilter!]!$orderBy:IssueOrderField!$owner:String!$repo:String!$states:[IssueState!]!){repository(owner: $owner, name: $repo){issues(first: $first, after: $after, states: $states, orderBy: {field: $orderBy, direction: $direction}, filterBy: {issueFieldValues: $issueFieldValues}){nodes{number,title,body,state,databaseId,authorAssociation,author{login},createdAt,updatedAt,labels(first: 100){nodes{name,id,description}},comments{totalCount},issueFieldValues(first: 25){nodes{__typename,... on IssueFieldDateValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value},... on IssueFieldNumberValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},valueNumber: value},... on IssueFieldSingleSelectValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value},... on IssueFieldTextValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value}}}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount},isPrivate}}" + qWithLabels := "query($after:String$direction:OrderDirection!$first:Int!$issueFieldValues:[IssueFieldValueFilter!]!$labels:[String!]!$orderBy:IssueOrderField!$owner:String!$repo:String!$states:[IssueState!]!){repository(owner: $owner, name: $repo){issues(first: $first, after: $after, labels: $labels, states: $states, orderBy: {field: $orderBy, direction: $direction}, filterBy: {issueFieldValues: $issueFieldValues}){nodes{number,title,body,state,databaseId,authorAssociation,author{login},createdAt,updatedAt,labels(first: 100){nodes{name,id,description}},comments{totalCount},issueFieldValues(first: 25){nodes{__typename,... on IssueFieldDateValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value},... on IssueFieldNumberValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},valueNumber: value},... on IssueFieldSingleSelectValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value},... on IssueFieldTextValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value}}}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount},isPrivate}}" baseVars := func() map[string]any { return map[string]any{ @@ -2661,7 +2668,7 @@ func Test_ListIssues_IFC_InsidersMode(t *testing.T) { }) } - query := "query($after:String$direction:OrderDirection!$first:Int!$issueFieldValues:[IssueFieldValueFilter!]!$orderBy:IssueOrderField!$owner:String!$repo:String!$states:[IssueState!]!){repository(owner: $owner, name: $repo){issues(first: $first, after: $after, states: $states, orderBy: {field: $orderBy, direction: $direction}, filterBy: {issueFieldValues: $issueFieldValues}){nodes{number,title,body,state,databaseId,author{login},createdAt,updatedAt,labels(first: 100){nodes{name,id,description}},comments{totalCount},issueFieldValues(first: 25){nodes{__typename,... on IssueFieldDateValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value},... on IssueFieldNumberValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},valueNumber: value},... on IssueFieldSingleSelectValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value},... on IssueFieldTextValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value}}}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount},isPrivate}}" + query := "query($after:String$direction:OrderDirection!$first:Int!$issueFieldValues:[IssueFieldValueFilter!]!$orderBy:IssueOrderField!$owner:String!$repo:String!$states:[IssueState!]!){repository(owner: $owner, name: $repo){issues(first: $first, after: $after, states: $states, orderBy: {field: $orderBy, direction: $direction}, filterBy: {issueFieldValues: $issueFieldValues}){nodes{number,title,body,state,databaseId,authorAssociation,author{login},createdAt,updatedAt,labels(first: 100){nodes{name,id,description}},comments{totalCount},issueFieldValues(first: 25){nodes{__typename,... on IssueFieldDateValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value},... on IssueFieldNumberValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},valueNumber: value},... on IssueFieldSingleSelectValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value},... on IssueFieldTextValue{field{... on IssueFieldDate{name,fullDatabaseId},... on IssueFieldNumber{name,fullDatabaseId},... on IssueFieldSingleSelect{name,fullDatabaseId},... on IssueFieldText{name,fullDatabaseId}},value}}}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount},isPrivate}}" vars := map[string]any{ "owner": "octocat", @@ -2794,14 +2801,15 @@ func Test_LegacyListIssues_OmitsFieldValuesAndFilters(t *testing.T) { mockIssues := []map[string]any{ { - "number": 7, - "title": "Legacy issue", - "body": "body", - "state": "OPEN", - "databaseId": 7, - "createdAt": "2026-01-01T00:00:00Z", - "updatedAt": "2026-01-01T00:00:00Z", - "author": map[string]any{"login": "octocat"}, + "number": 7, + "title": "Legacy issue", + "body": "body", + "state": "OPEN", + "databaseId": 7, + "authorAssociation": "OWNER", + "createdAt": "2026-01-01T00:00:00Z", + "updatedAt": "2026-01-01T00:00:00Z", + "author": map[string]any{"login": "octocat"}, "labels": map[string]any{"nodes": []map[string]any{}}, "comments": map[string]any{"totalCount": 0}, }, @@ -2815,7 +2823,7 @@ func Test_LegacyListIssues_OmitsFieldValuesAndFilters(t *testing.T) { // The legacy query must NOT reference issueFieldValues (neither in the selection // set nor in filterBy). The matcher's query string therefore omits both. - const legacyQuery = "query($after:String$direction:OrderDirection!$first:Int!$orderBy:IssueOrderField!$owner:String!$repo:String!$states:[IssueState!]!){repository(owner: $owner, name: $repo){issues(first: $first, after: $after, states: $states, orderBy: {field: $orderBy, direction: $direction}){nodes{number,title,body,state,databaseId,author{login},createdAt,updatedAt,labels(first: 100){nodes{name,id,description}},comments{totalCount}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount},isPrivate}}" + const legacyQuery = "query($after:String$direction:OrderDirection!$first:Int!$orderBy:IssueOrderField!$owner:String!$repo:String!$states:[IssueState!]!){repository(owner: $owner, name: $repo){issues(first: $first, after: $after, states: $states, orderBy: {field: $orderBy, direction: $direction}){nodes{number,title,body,state,databaseId,authorAssociation,author{login},createdAt,updatedAt,labels(first: 100){nodes{name,id,description}},comments{totalCount}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount},isPrivate}}" vars := map[string]any{ "owner": "owner", "repo": "repo", @@ -2851,6 +2859,7 @@ func Test_LegacyListIssues_OmitsFieldValuesAndFilters(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(getTextResult(t, result).Text), &resp)) require.Len(t, resp.Issues, 1) assert.Equal(t, 7, resp.Issues[0].Number) + assert.Equal(t, "OWNER", resp.Issues[0].AuthorAssociation) assert.Nil(t, resp.Issues[0].FieldValues, "legacy list_issues must not return field_values") } diff --git a/pkg/github/pullrequests_test.go b/pkg/github/pullrequests_test.go index aff71e4c1a..82034dfe30 100644 --- a/pkg/github/pullrequests_test.go +++ b/pkg/github/pullrequests_test.go @@ -49,6 +49,7 @@ func Test_GetPullRequest(t *testing.T) { User: &github.User{ Login: github.Ptr("testuser"), }, + AuthorAssociation: github.Ptr("MEMBER"), } tests := []struct { @@ -134,6 +135,7 @@ func Test_GetPullRequest(t *testing.T) { assert.Equal(t, tc.expectedPR.GetTitle(), returnedPR.Title) assert.Equal(t, tc.expectedPR.GetState(), returnedPR.State) assert.Equal(t, tc.expectedPR.GetHTMLURL(), returnedPR.HTMLURL) + assert.Equal(t, tc.expectedPR.GetAuthorAssociation(), returnedPR.AuthorAssociation) }) } } @@ -592,16 +594,18 @@ func Test_ListPullRequests(t *testing.T) { // Setup mock PRs for success case mockPRs := []*github.PullRequest{ { - Number: github.Ptr(42), - Title: github.Ptr("First PR"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/42"), + Number: github.Ptr(42), + Title: github.Ptr("First PR"), + State: github.Ptr("open"), + HTMLURL: github.Ptr("https://github.com/owner/repo/pull/42"), + AuthorAssociation: github.Ptr("OWNER"), }, { - Number: github.Ptr(43), - Title: github.Ptr("Second PR"), - State: github.Ptr("closed"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/43"), + Number: github.Ptr(43), + Title: github.Ptr("Second PR"), + State: github.Ptr("closed"), + HTMLURL: github.Ptr("https://github.com/owner/repo/pull/43"), + AuthorAssociation: github.Ptr("CONTRIBUTOR"), }, } @@ -695,9 +699,11 @@ func Test_ListPullRequests(t *testing.T) { assert.Equal(t, *tc.expectedPRs[0].Number, returnedPRs[0].Number) assert.Equal(t, *tc.expectedPRs[0].Title, returnedPRs[0].Title) assert.Equal(t, *tc.expectedPRs[0].State, returnedPRs[0].State) + assert.Equal(t, tc.expectedPRs[0].GetAuthorAssociation(), returnedPRs[0].AuthorAssociation) assert.Equal(t, *tc.expectedPRs[1].Number, returnedPRs[1].Number) assert.Equal(t, *tc.expectedPRs[1].Title, returnedPRs[1].Title) assert.Equal(t, *tc.expectedPRs[1].State, returnedPRs[1].State) + assert.Equal(t, tc.expectedPRs[1].GetAuthorAssociation(), returnedPRs[1].AuthorAssociation) }) } }