1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2026-05-04 21:27:54 +02:00
Files
Bradly Chang 6eda4c0aab Fix case-insensitive remote URL matching for GitHub PRs
Normalizes the repository owner to lowercase during the PR
mapping.
This ensures that PR icons and integration features work correctly even
when the local git remote URL casing differs from the official
repository casing on GitHub.
2026-04-10 01:36:10 +08:00

364 lines
10 KiB
Go

package git_commands
import (
"testing"
"github.com/jesseduffield/lazygit/pkg/commands/hosting_service"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/stretchr/testify/assert"
)
func TestGetRepoInfoFromURL(t *testing.T) {
cases := []struct {
name string
url string
expected hosting_service.RepoInformation
}{
{
name: "SSH URL",
url: "git@github.com:jesseduffield/lazygit.git",
expected: hosting_service.RepoInformation{
Owner: "jesseduffield",
Repository: "lazygit",
},
},
{
name: "HTTPS URL",
url: "https://github.com/jesseduffield/lazygit.git",
expected: hosting_service.RepoInformation{
Owner: "jesseduffield",
Repository: "lazygit",
},
},
{
name: "HTTPS URL without .git",
url: "https://github.com/jesseduffield/lazygit",
expected: hosting_service.RepoInformation{
Owner: "jesseduffield",
Repository: "lazygit",
},
},
{
name: "SSH URL with org nesting",
url: "git@github.com:my-org/sub-group/lazygit.git",
expected: hosting_service.RepoInformation{
Owner: "my-org/sub-group",
Repository: "lazygit",
},
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
result, err := hosting_service.GetRepoInfoFromURL(c.url)
assert.NoError(t, err)
assert.Equal(t, c.expected, result)
})
}
}
func TestGenerateGithubPullRequestMap(t *testing.T) {
cases := []struct {
name string
prs []*models.GithubPullRequest
branches []*models.Branch
remotes []*models.Remote
expected map[string]*models.GithubPullRequest
}{
{
name: "empty inputs",
prs: []*models.GithubPullRequest{},
branches: []*models.Branch{},
remotes: []*models.Remote{},
expected: map[string]*models.GithubPullRequest{},
},
{
name: "matches PR to branch tracking origin",
prs: []*models.GithubPullRequest{
{
HeadRefName: "feature-branch",
Number: 42,
Title: "Add feature",
State: "OPEN",
Url: "https://github.com/jesseduffield/lazygit/pull/42",
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
},
},
branches: []*models.Branch{
{
Name: "feature-branch",
UpstreamRemote: "origin",
UpstreamBranch: "feature-branch",
},
},
remotes: []*models.Remote{
{
Name: "origin",
Urls: []string{"git@github.com:jesseduffield/lazygit.git"},
},
},
expected: map[string]*models.GithubPullRequest{
"feature-branch": {
HeadRefName: "feature-branch",
Number: 42,
Title: "Add feature",
State: "OPEN",
Url: "https://github.com/jesseduffield/lazygit/pull/42",
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
},
},
},
{
name: "does not match branch without upstream",
prs: []*models.GithubPullRequest{
{
HeadRefName: "feature-branch",
Number: 42,
Title: "Add feature",
State: "OPEN",
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
},
},
branches: []*models.Branch{
{
Name: "feature-branch",
// no upstream set
},
},
remotes: []*models.Remote{
{
Name: "origin",
Urls: []string{"git@github.com:jesseduffield/lazygit.git"},
},
},
expected: map[string]*models.GithubPullRequest{},
},
{
name: "matches fork PR to branch tracking fork remote",
prs: []*models.GithubPullRequest{
{
HeadRefName: "fix-bug",
Number: 99,
Title: "Fix bug",
State: "OPEN",
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "contributor"},
},
},
branches: []*models.Branch{
{
Name: "fix-bug",
UpstreamRemote: "contributor",
UpstreamBranch: "fix-bug",
},
},
remotes: []*models.Remote{
{
Name: "origin",
Urls: []string{"git@github.com:jesseduffield/lazygit.git"},
},
{
Name: "contributor",
Urls: []string{"git@github.com:contributor/lazygit.git"},
},
},
expected: map[string]*models.GithubPullRequest{
"fix-bug": {
HeadRefName: "fix-bug",
Number: 99,
Title: "Fix bug",
State: "OPEN",
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "contributor"},
},
},
},
{
name: "does not match when owner differs",
prs: []*models.GithubPullRequest{
{
HeadRefName: "feature-branch",
Number: 42,
Title: "Add feature",
State: "OPEN",
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "someone-else"},
},
},
branches: []*models.Branch{
{
Name: "feature-branch",
UpstreamRemote: "origin",
UpstreamBranch: "feature-branch",
},
},
remotes: []*models.Remote{
{
Name: "origin",
Urls: []string{"git@github.com:jesseduffield/lazygit.git"},
},
},
expected: map[string]*models.GithubPullRequest{},
},
{
name: "matches when UpstreamRemote is a full URL",
prs: []*models.GithubPullRequest{
{
HeadRefName: "my-branch",
Number: 55,
Title: "Full URL upstream",
State: "OPEN",
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "contributor"},
},
},
branches: []*models.Branch{
{
Name: "my-branch",
UpstreamRemote: "git@github.com:contributor/lazygit.git",
UpstreamBranch: "my-branch",
},
},
remotes: []*models.Remote{
{
Name: "origin",
Urls: []string{"git@github.com:jesseduffield/lazygit.git"},
},
},
expected: map[string]*models.GithubPullRequest{
"my-branch": {
HeadRefName: "my-branch",
Number: 55,
Title: "Full URL upstream",
State: "OPEN",
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "contributor"},
},
},
},
{
name: "uses first PR when branch name is reused (API returns newest first)",
prs: []*models.GithubPullRequest{
// API returns newest first (CREATED_AT DESC)
{
HeadRefName: "update-sponsors",
Number: 50,
Title: "Newest PR",
State: "CLOSED",
Url: "https://github.com/jesseduffield/lazygit/pull/50",
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
},
{
HeadRefName: "update-sponsors",
Number: 30,
Title: "Middle PR",
State: "OPEN",
Url: "https://github.com/jesseduffield/lazygit/pull/30",
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
},
{
HeadRefName: "update-sponsors",
Number: 10,
Title: "Oldest PR",
State: "CLOSED",
Url: "https://github.com/jesseduffield/lazygit/pull/10",
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
},
},
branches: []*models.Branch{
{
Name: "update-sponsors",
UpstreamRemote: "origin",
UpstreamBranch: "update-sponsors",
},
},
remotes: []*models.Remote{
{
Name: "origin",
Urls: []string{"git@github.com:jesseduffield/lazygit.git"},
},
},
expected: map[string]*models.GithubPullRequest{
"update-sponsors": {
HeadRefName: "update-sponsors",
Number: 50,
Title: "Newest PR",
State: "CLOSED",
Url: "https://github.com/jesseduffield/lazygit/pull/50",
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
},
},
},
{
name: "matches with HTTPS remote URL",
prs: []*models.GithubPullRequest{
{
HeadRefName: "my-pr",
Number: 10,
Title: "My PR",
State: "MERGED",
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
},
},
branches: []*models.Branch{
{
Name: "my-pr",
UpstreamRemote: "origin",
UpstreamBranch: "my-pr",
},
},
remotes: []*models.Remote{
{
Name: "origin",
Urls: []string{"https://github.com/jesseduffield/lazygit.git"},
},
},
expected: map[string]*models.GithubPullRequest{
"my-pr": {
HeadRefName: "my-pr",
Number: 10,
Title: "My PR",
State: "MERGED",
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
},
},
},
{
name: "matches when owner casing differs",
prs: []*models.GithubPullRequest{
{
HeadRefName: "fix-case-insensitive",
Number: 42,
Title: "Fix case insensitive",
State: "OPEN",
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "Jesseduffield"}, // Uppercase J
},
},
branches: []*models.Branch{
{
Name: "fix-case-insensitive",
UpstreamRemote: "origin",
UpstreamBranch: "fix-case-insensitive",
},
},
remotes: []*models.Remote{
{
Name: "origin",
Urls: []string{"git@github.com:jesseduffield/lazygit.git"}, // Lowercase j
},
},
expected: map[string]*models.GithubPullRequest{
"fix-case-insensitive": {
HeadRefName: "fix-case-insensitive",
Number: 42,
Title: "Fix case insensitive",
State: "OPEN",
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "Jesseduffield"},
},
},
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
result := GenerateGithubPullRequestMap(c.prs, c.branches, c.remotes)
assert.Equal(t, c.expected, result)
})
}
}