Skip to content
Merged
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
2 changes: 1 addition & 1 deletion pkg/github/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,7 @@ func getWorkflowRun(ctx context.Context, client *github.Client, owner, repo stri
return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to get workflow run", resp, err), nil, nil
}
defer func() { _ = resp.Body.Close() }()
r, err := json.Marshal(workflowRun)
r, err := json.Marshal(convertToMinimalWorkflowRun(workflowRun))
if err != nil {
return nil, nil, fmt.Errorf("failed to marshal workflow run: %w", err)
}
Expand Down
23 changes: 14 additions & 9 deletions pkg/github/actions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,14 +307,9 @@ func Test_ActionsGet_GetWorkflowRun(t *testing.T) {
toolDef := ActionsGet(translations.NullTranslationHelper)

t.Run("successful workflow run get", func(t *testing.T) {
run := actionsTestWorkflowRun()
mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
GetReposActionsRunsByOwnerByRepoByRunID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
run := &github.WorkflowRun{
ID: github.Ptr(int64(12345)),
Name: github.Ptr("CI"),
Status: github.Ptr("completed"),
Conclusion: github.Ptr("success"),
}
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(run)
}),
Expand All @@ -338,11 +333,21 @@ func Test_ActionsGet_GetWorkflowRun(t *testing.T) {
require.False(t, result.IsError)

textContent := getTextResult(t, result)
var response github.WorkflowRun
var response MinimalWorkflowRun
err = json.Unmarshal([]byte(textContent.Text), &response)
require.NoError(t, err)
assert.NotNil(t, response.ID)
assert.Equal(t, int64(12345), *response.ID)

expected := convertToMinimalWorkflowRun(run)
assert.Equal(t, expected, response)

var payload map[string]any
require.NoError(t, json.Unmarshal([]byte(textContent.Text), &payload))
assert.Equal(t, marshalActionsObject(t, expected), payload)
assert.NotContains(t, payload, "node_id")
assert.NotContains(t, payload, "repository")
assert.NotContains(t, payload, "head_repository")
assert.NotContains(t, payload, "url")
assert.NotContains(t, payload, "jobs_url")
})
}

Expand Down
54 changes: 54 additions & 0 deletions pkg/github/minimal_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,24 @@ type MinimalPRBranchRepo struct {
Description string `json:"description,omitempty"`
}

// MinimalRepoStatus is the trimmed output type for an individual commit status.
type MinimalRepoStatus struct {
State string `json:"state"`
Context string `json:"context"`
Description string `json:"description,omitempty"`
TargetURL string `json:"target_url,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}

// MinimalCombinedStatus is the trimmed output type for a combined commit status.
type MinimalCombinedStatus struct {
State string `json:"state"`
SHA string `json:"sha"`
TotalCount int `json:"total_count"`
Statuses []MinimalRepoStatus `json:"statuses"`
}

type MinimalProjectStatusUpdate struct {
ID string `json:"id"`
Body string `json:"body,omitempty"`
Expand Down Expand Up @@ -1057,6 +1075,42 @@ func convertToMinimalPRBranch(branch *github.PullRequestBranch) *MinimalPRBranch
return b
}

func convertToMinimalCombinedStatus(status *github.CombinedStatus) MinimalCombinedStatus {
minimalStatus := MinimalCombinedStatus{
Statuses: make([]MinimalRepoStatus, 0),
}
if status == nil {
return minimalStatus
}

minimalStatus.State = status.GetState()
minimalStatus.SHA = status.GetSHA()
minimalStatus.TotalCount = status.GetTotalCount()
minimalStatus.Statuses = make([]MinimalRepoStatus, 0, len(status.GetStatuses()))
for _, repoStatus := range status.GetStatuses() {
if repoStatus != nil {
minimalStatus.Statuses = append(minimalStatus.Statuses, convertToMinimalRepoStatus(repoStatus))
}
}

return minimalStatus
}

func convertToMinimalRepoStatus(status *github.RepoStatus) MinimalRepoStatus {
if status == nil {
return MinimalRepoStatus{}
}

return MinimalRepoStatus{
State: status.GetState(),
Context: status.GetContext(),
Description: status.GetDescription(),
TargetURL: status.GetTargetURL(),
CreatedAt: formatMinimalTimestamp(status.CreatedAt),
UpdatedAt: formatMinimalTimestamp(status.UpdatedAt),
}
}

func convertToMinimalProject(fullProject *github.ProjectV2) *MinimalProject {
if fullProject == nil {
return nil
Expand Down
20 changes: 12 additions & 8 deletions pkg/github/pullrequests.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ func GetPullRequestStatus(ctx context.Context, client *github.Client, owner, rep
return ghErrors.NewGitHubAPIStatusErrorResponse(ctx, "failed to get combined status", resp, body), nil
}

r, err := json.Marshal(status)
r, err := json.Marshal(convertToMinimalCombinedStatus(status))
if err != nil {
return nil, fmt.Errorf("failed to marshal response: %w", err)
}
Expand Down Expand Up @@ -1281,10 +1281,9 @@ func AddReplyToPullRequestComment(t translations.TranslationHelperFunc) inventor
}
}

var comment *github.PullRequestComment
var commentResponse *MinimalResponse
if hasBody {
var resp *github.Response
comment, resp, err = client.PullRequests.CreateCommentInReplyTo(ctx, owner, repo, pullNumber, body, commentID)
comment, resp, err := client.PullRequests.CreateCommentInReplyTo(ctx, owner, repo, pullNumber, body, commentID)
if err != nil {
return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to add reply to pull request comment", resp, err), nil, nil
}
Expand All @@ -1297,19 +1296,24 @@ func AddReplyToPullRequestComment(t translations.TranslationHelperFunc) inventor
}
return ghErrors.NewGitHubAPIStatusErrorResponse(ctx, "failed to add reply to pull request comment", resp, bodyBytes), nil, nil
}

commentResponse = &MinimalResponse{
ID: fmt.Sprintf("%d", comment.GetID()),
URL: comment.GetHTMLURL(),
}
}

var result any
switch {
case hasBody && hasReaction:
result = map[string]any{
"comment": comment,
"reaction": reactionResponse,
result = map[string]MinimalResponse{
"comment": *commentResponse,
"reaction": *reactionResponse,
}
case hasReaction:
result = reactionResponse
default:
result = comment
result = commentResponse
}

r, err := json.Marshal(result)
Expand Down
145 changes: 117 additions & 28 deletions pkg/github/pullrequests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1575,42 +1575,58 @@ func Test_GetPullRequestStatus(t *testing.T) {
},
}

// Setup mock status for success case
statusCreatedAt := &github.Timestamp{Time: time.Date(2026, time.August, 11, 9, 30, 0, 0, time.UTC)}
statusUpdatedAt := &github.Timestamp{Time: time.Date(2026, time.August, 11, 9, 35, 0, 0, time.UTC)}
mockStatus := &github.CombinedStatus{
Name: github.Ptr("abcd1234"),
State: github.Ptr("success"),
TotalCount: github.Ptr(3),
SHA: github.Ptr("abcd1234"),
TotalCount: github.Ptr(2),
CommitURL: github.Ptr("https://api.github.com/repos/owner/repo/commits/abcd1234"),
RepositoryURL: github.Ptr(
"https://api.github.com/repos/owner/repo",
),
Statuses: []*github.RepoStatus{
{
ID: github.Ptr(int64(101)),
NodeID: github.Ptr("SC_kwDOStatus101"),
URL: github.Ptr("https://api.github.com/repos/owner/repo/statuses/abcd1234"),
State: github.Ptr("success"),
Context: github.Ptr("continuous-integration/travis-ci"),
Description: github.Ptr("Build succeeded"),
TargetURL: github.Ptr("https://travis-ci.org/owner/repo/builds/123"),
AvatarURL: github.Ptr("https://avatars.githubusercontent.com/in/123"),
Creator: &github.User{
Login: github.Ptr("ci-bot"),
},
CreatedAt: statusCreatedAt,
UpdatedAt: statusUpdatedAt,
},
{
State: github.Ptr("success"),
Context: github.Ptr("codecov/patch"),
Description: github.Ptr("Coverage increased"),
TargetURL: github.Ptr("https://codecov.io/gh/owner/repo/pull/42"),
},
{
State: github.Ptr("success"),
Context: github.Ptr("lint/golangci-lint"),
Description: github.Ptr("No issues found"),
TargetURL: github.Ptr("https://golangci.com/r/owner/repo/pull/42"),
},
},
}
emptyStatus := &github.CombinedStatus{
State: github.Ptr("pending"),
SHA: github.Ptr("abcd1234"),
TotalCount: github.Ptr(0),
Statuses: []*github.RepoStatus{nil},
}

tests := []struct {
name string
mockedClient *http.Client
requestArgs map[string]any
expectError bool
expectedStatus *github.CombinedStatus
expectedStatus *MinimalCombinedStatus
expectedErrMsg string
}{
{
name: "successful status fetch",
name: "successful status fetch with multiple statuses",
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
GetReposPullsByOwnerByRepoByPullNumber: mockResponse(t, http.StatusOK, mockPR),
GetReposCommitsStatusByOwnerByRepoByRef: mockResponse(t, http.StatusOK, mockStatus),
Expand All @@ -1621,8 +1637,46 @@ func Test_GetPullRequestStatus(t *testing.T) {
"repo": "repo",
"pullNumber": float64(42),
},
expectError: false,
expectedStatus: mockStatus,
expectedStatus: &MinimalCombinedStatus{
State: "success",
SHA: "abcd1234",
TotalCount: 2,
Statuses: []MinimalRepoStatus{
{
State: "success",
Context: "continuous-integration/travis-ci",
Description: "Build succeeded",
TargetURL: "https://travis-ci.org/owner/repo/builds/123",
CreatedAt: "2026-08-11T09:30:00Z",
UpdatedAt: "2026-08-11T09:35:00Z",
},
{
State: "success",
Context: "codecov/patch",
Description: "Coverage increased",
TargetURL: "https://codecov.io/gh/owner/repo/pull/42",
},
},
},
},
{
name: "successful status fetch with no statuses",
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
GetReposPullsByOwnerByRepoByPullNumber: mockResponse(t, http.StatusOK, mockPR),
GetReposCommitsStatusByOwnerByRepoByRef: mockResponse(t, http.StatusOK, emptyStatus),
}),
requestArgs: map[string]any{
"method": "get_status",
"owner": "owner",
"repo": "repo",
"pullNumber": float64(42),
},
expectedStatus: &MinimalCombinedStatus{
State: "pending",
SHA: "abcd1234",
TotalCount: 0,
Statuses: []MinimalRepoStatus{},
},
},
{
name: "PR fetch fails",
Expand Down Expand Up @@ -1691,20 +1745,33 @@ func Test_GetPullRequestStatus(t *testing.T) {
require.NoError(t, err)
require.False(t, result.IsError)

// Parse the result and get the text content if no error
textContent := getTextResult(t, result)

// Unmarshal and verify the result
var returnedStatus github.CombinedStatus
var returnedStatus MinimalCombinedStatus
err = json.Unmarshal([]byte(textContent.Text), &returnedStatus)
require.NoError(t, err)
assert.Equal(t, *tc.expectedStatus.State, *returnedStatus.State)
assert.Equal(t, *tc.expectedStatus.TotalCount, *returnedStatus.TotalCount)
assert.Len(t, returnedStatus.Statuses, len(tc.expectedStatus.Statuses))
for i, status := range returnedStatus.Statuses {
assert.Equal(t, *tc.expectedStatus.Statuses[i].State, *status.State)
assert.Equal(t, *tc.expectedStatus.Statuses[i].Context, *status.Context)
assert.Equal(t, *tc.expectedStatus.Statuses[i].Description, *status.Description)
assert.Equal(t, *tc.expectedStatus, returnedStatus)

expectedJSON, err := json.Marshal(tc.expectedStatus)
require.NoError(t, err)
assert.JSONEq(t, string(expectedJSON), textContent.Text)

var payload map[string]any
require.NoError(t, json.Unmarshal([]byte(textContent.Text), &payload))
assert.NotContains(t, payload, "name")
assert.NotContains(t, payload, "commit_url")
assert.NotContains(t, payload, "repository_url")

statuses, ok := payload["statuses"].([]any)
require.True(t, ok)
for _, status := range statuses {
statusPayload, ok := status.(map[string]any)
require.True(t, ok)
assert.NotContains(t, statusPayload, "id")
assert.NotContains(t, statusPayload, "node_id")
assert.NotContains(t, statusPayload, "url")
assert.NotContains(t, statusPayload, "avatar_url")
assert.NotContains(t, statusPayload, "creator")
}
})
}
Expand Down Expand Up @@ -4145,6 +4212,13 @@ func TestAddReplyToPullRequestComment(t *testing.T) {
}
replyCreatedAfterReactionFailure := &atomic.Bool{}

assertMinimalResponse := func(t *testing.T, response map[string]any, expectedID, expectedURL string) {
t.Helper()
assert.Len(t, response, 2)
assert.Equal(t, expectedID, response["id"])
assert.Equal(t, expectedURL, response["url"])
}

tests := []struct {
name string
mockedClient *http.Client
Expand Down Expand Up @@ -4354,14 +4428,29 @@ func TestAddReplyToPullRequestComment(t *testing.T) {
return
}

// Parse the result and verify it's not an error
require.False(t, result.IsError)
textContent := getTextResult(t, result)
if _, ok := tc.requestArgs["body"]; ok {
assert.Contains(t, textContent.Text, "This is a reply to the comment")
}
if _, ok := tc.requestArgs["reaction"]; ok {
assert.Contains(t, textContent.Text, "789")

var response map[string]any
require.NoError(t, json.Unmarshal([]byte(textContent.Text), &response))

_, hasBody := tc.requestArgs["body"]
_, hasReaction := tc.requestArgs["reaction"]
reactionURL := client.BaseURL() + "repos/owner/repo/pulls/comments/123/reactions/789"

switch {
case hasBody && hasReaction:
assert.Len(t, response, 2)
commentResponse, ok := response["comment"].(map[string]any)
require.True(t, ok)
assertMinimalResponse(t, commentResponse, "456", "https://github.com/owner/repo/pull/42#discussion_r456")
reactionResponse, ok := response["reaction"].(map[string]any)
require.True(t, ok)
assertMinimalResponse(t, reactionResponse, "789", reactionURL)
case hasBody:
assertMinimalResponse(t, response, "456", "https://github.com/owner/repo/pull/42#discussion_r456")
default:
assertMinimalResponse(t, response, "789", reactionURL)
}
})
}
Expand Down
Loading