From 67ec23e08ae863167f96f87dcb553518dfadd375 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 29 Jul 2026 18:38:21 +0200 Subject: [PATCH] Isolate GitHub pull-request response parsing The fetch currently combines transport, JSON decoding, and model conversion, which makes response changes difficult to verify without exercising the network. Put the deterministic work behind a small parser so later payload changes can be covered with raw GraphQL fixtures. --- pkg/commands/git_commands/github.go | 7 ++- pkg/commands/git_commands/github_test.go | 77 ++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/pkg/commands/git_commands/github.go b/pkg/commands/git_commands/github.go index b74815301..06f9ab550 100644 --- a/pkg/commands/git_commands/github.go +++ b/pkg/commands/git_commands/github.go @@ -231,9 +231,12 @@ func (self *GitHubCommands) fetchRecentPRsAux(endpoint string, repoOwner string, return nil, err } + return parsePullRequestsResponse(respBytes) +} + +func parsePullRequestsResponse(respBytes []byte) ([]*models.GithubPullRequest, error) { var result Response - err = json.Unmarshal(respBytes, &result) - if err != nil { + if err := json.Unmarshal(respBytes, &result); err != nil { return nil, err } diff --git a/pkg/commands/git_commands/github_test.go b/pkg/commands/git_commands/github_test.go index b332ba12a..664da857d 100644 --- a/pkg/commands/git_commands/github_test.go +++ b/pkg/commands/git_commands/github_test.go @@ -76,6 +76,83 @@ func TestGraphQLEndpoint(t *testing.T) { } } +func TestParsePullRequestsResponse(t *testing.T) { + t.Run("flattens aliases and normalizes drafts", func(t *testing.T) { + response := []byte(`{ + "data": { + "repository": { + "a1": { + "edges": [ + { + "node": { + "title": "Add feature", + "headRefName": "feature", + "number": 42, + "url": "https://github.com/jesseduffield/lazygit/pull/42", + "headRepositoryOwner": {"login": "contributor"}, + "state": "OPEN", + "isDraft": false + } + } + ] + }, + "a2": { + "edges": [ + { + "node": { + "title": "Draft feature", + "headRefName": "draft-feature", + "number": 43, + "url": "https://github.com/jesseduffield/lazygit/pull/43", + "headRepositoryOwner": {"login": "contributor"}, + "state": "OPEN", + "isDraft": true + } + } + ] + } + } + } +}`) + + prs, err := parsePullRequestsResponse(response) + + assert.NoError(t, err) + assert.ElementsMatch(t, []*models.GithubPullRequest{ + { + HeadRefName: "feature", + Number: 42, + Title: "Add feature", + State: "OPEN", + Url: "https://github.com/jesseduffield/lazygit/pull/42", + HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "contributor"}, + }, + { + HeadRefName: "draft-feature", + Number: 43, + Title: "Draft feature", + State: "DRAFT", + Url: "https://github.com/jesseduffield/lazygit/pull/43", + HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "contributor"}, + }, + }, prs) + }) + + t.Run("returns an empty slice for an empty result", func(t *testing.T) { + prs, err := parsePullRequestsResponse([]byte(`{"data":{"repository":{}}}`)) + + assert.NoError(t, err) + assert.Empty(t, prs) + }) + + t.Run("rejects malformed JSON", func(t *testing.T) { + prs, err := parsePullRequestsResponse([]byte(`{"data":`)) + + assert.Error(t, err) + assert.Nil(t, prs) + }) +} + func TestGenerateGithubPullRequestMap(t *testing.T) { cases := []struct { name string