- Add a test case specifically verifying that `Gitea.SetLabels` is called with `staging/Auto` when a *new* project PR is created for submodules. - Verify `PrjGitDescription` and `SetSubmodulesToMatchPRSet` behave correctly when a single `PRSet` contains 5+ different package repositories.
911 lines
27 KiB
Go
911 lines
27 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"go.uber.org/mock/gomock"
|
|
"src.opensuse.org/autogits/common"
|
|
"src.opensuse.org/autogits/common/gitea-generated/models"
|
|
mock_common "src.opensuse.org/autogits/common/mock"
|
|
)
|
|
|
|
func TestPrjGitDescription(t *testing.T) {
|
|
config := &common.AutogitConfig{
|
|
Organization: "test-org",
|
|
GitProjectName: "test-prj#main",
|
|
}
|
|
|
|
prset := &common.PRSet{
|
|
Config: config,
|
|
PRs: []*common.PRInfo{
|
|
{
|
|
PR: &models.PullRequest{
|
|
State: "open",
|
|
Index: 1,
|
|
Base: &models.PRBranchInfo{
|
|
Ref: "main",
|
|
Repo: &models.Repository{
|
|
Name: "pkg-a",
|
|
Owner: &models.User{UserName: "test-org"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
PR: &models.PullRequest{
|
|
State: "open",
|
|
Index: 2,
|
|
Base: &models.PRBranchInfo{
|
|
Ref: "main",
|
|
Repo: &models.Repository{
|
|
Name: "pkg-b",
|
|
Owner: &models.User{UserName: "test-org"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
GitAuthor = "Bot"
|
|
title, desc := PrjGitDescription(prset)
|
|
|
|
expectedTitle := "Forwarded PRs: pkg-a, pkg-b"
|
|
if title != expectedTitle {
|
|
t.Errorf("Expected title %q, got %q", expectedTitle, title)
|
|
}
|
|
|
|
if !strings.Contains(desc, "PR: test-org/pkg-a!1") || !strings.Contains(desc, "PR: test-org/pkg-b!2") {
|
|
t.Errorf("Description missing PR references: %s", desc)
|
|
}
|
|
}
|
|
|
|
func TestAllocatePRProcessor(t *testing.T) {
|
|
ctl := gomock.NewController(t)
|
|
defer ctl.Finish()
|
|
|
|
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
GitHandler = mockGitGen
|
|
mockGit := mock_common.NewMockGit(ctl)
|
|
|
|
configs := common.AutogitConfigs{
|
|
{
|
|
Organization: "test-org",
|
|
Branch: "main",
|
|
GitProjectName: "test-prj#main",
|
|
},
|
|
}
|
|
|
|
req := &models.PullRequest{
|
|
Index: 1,
|
|
Base: &models.PRBranchInfo{
|
|
Ref: "main",
|
|
Repo: &models.Repository{
|
|
Name: "test-repo",
|
|
Owner: &models.User{UserName: "test-org"},
|
|
},
|
|
},
|
|
}
|
|
|
|
mockGitGen.EXPECT().CreateGitHandler("test-org").Return(mockGit, nil)
|
|
mockGit.EXPECT().GetPath().Return("/tmp/test")
|
|
|
|
processor, err := AllocatePRProcessor(req, configs)
|
|
if err != nil {
|
|
t.Fatalf("AllocatePRProcessor failed: %v", err)
|
|
}
|
|
|
|
if processor.config.Organization != "test-org" {
|
|
t.Errorf("Expected organization test-org, got %s", processor.config.Organization)
|
|
}
|
|
}
|
|
|
|
func TestAllocatePRProcessor_Failures(t *testing.T) {
|
|
ctl := gomock.NewController(t)
|
|
defer ctl.Finish()
|
|
|
|
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
GitHandler = mockGitGen
|
|
|
|
configs := common.AutogitConfigs{} // Empty configs
|
|
|
|
req := &models.PullRequest{
|
|
Index: 1,
|
|
Base: &models.PRBranchInfo{
|
|
Ref: "main",
|
|
Repo: &models.Repository{
|
|
Name: "test-repo",
|
|
Owner: &models.User{UserName: "test-org"},
|
|
},
|
|
},
|
|
}
|
|
|
|
t.Run("Missing config", func(t *testing.T) {
|
|
processor, err := AllocatePRProcessor(req, configs)
|
|
if err == nil || err.Error() != "Cannot find config for PR" {
|
|
t.Errorf("Expected 'Cannot find config for PR' error, got %v", err)
|
|
}
|
|
if processor != nil {
|
|
t.Error("Expected nil processor")
|
|
}
|
|
})
|
|
|
|
t.Run("GitHandler failure", func(t *testing.T) {
|
|
validConfigs := common.AutogitConfigs{
|
|
{
|
|
Organization: "test-org",
|
|
Branch: "main",
|
|
GitProjectName: "test-prj#main",
|
|
},
|
|
}
|
|
mockGitGen.EXPECT().CreateGitHandler("test-org").Return(nil, errors.New("git error"))
|
|
|
|
processor, err := AllocatePRProcessor(req, validConfigs)
|
|
if err == nil || !strings.Contains(err.Error(), "Error allocating GitHandler") {
|
|
t.Errorf("Expected GitHandler error, got %v", err)
|
|
}
|
|
if processor != nil {
|
|
t.Error("Expected nil processor")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestSetSubmodulesToMatchPRSet_Failures(t *testing.T) {
|
|
ctl := gomock.NewController(t)
|
|
defer ctl.Finish()
|
|
|
|
mockGit := mock_common.NewMockGit(ctl)
|
|
config := &common.AutogitConfig{
|
|
Organization: "test-org",
|
|
GitProjectName: "test-prj#main",
|
|
}
|
|
|
|
processor := &PRProcessor{
|
|
config: config,
|
|
git: mockGit,
|
|
}
|
|
|
|
t.Run("GitSubmoduleList failure", func(t *testing.T) {
|
|
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), "HEAD").Return(nil, errors.New("list error"))
|
|
err := processor.SetSubmodulesToMatchPRSet(&common.PRSet{})
|
|
if err == nil || err.Error() != "list error" {
|
|
t.Errorf("Expected 'list error', got %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestSetSubmodulesToMatchPRSet(t *testing.T) {
|
|
ctl := gomock.NewController(t)
|
|
defer ctl.Finish()
|
|
|
|
mockGit := mock_common.NewMockGit(ctl)
|
|
config := &common.AutogitConfig{
|
|
Organization: "test-org",
|
|
GitProjectName: "test-prj#main",
|
|
}
|
|
|
|
processor := &PRProcessor{
|
|
config: config,
|
|
git: mockGit,
|
|
}
|
|
|
|
prset := &common.PRSet{
|
|
Config: config,
|
|
PRs: []*common.PRInfo{
|
|
{
|
|
PR: &models.PullRequest{
|
|
State: "open",
|
|
Index: 1,
|
|
Base: &models.PRBranchInfo{
|
|
Ref: "main",
|
|
Repo: &models.Repository{
|
|
Name: "pkg-a",
|
|
Owner: &models.User{UserName: "test-org"},
|
|
},
|
|
},
|
|
Head: &models.PRBranchInfo{
|
|
Sha: "new-sha",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), "HEAD").Return(map[string]string{"pkg-a": "old-sha"}, nil)
|
|
// Expect submodule update and commit
|
|
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
mockGit.EXPECT().GitStatus(gomock.Any()).Return([]common.GitStatusData{}, nil).AnyTimes()
|
|
|
|
err := processor.SetSubmodulesToMatchPRSet(prset)
|
|
if err != nil {
|
|
t.Errorf("SetSubmodulesToMatchPRSet failed: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRebaseAndSkipSubmoduleCommits(t *testing.T) {
|
|
ctl := gomock.NewController(t)
|
|
defer ctl.Finish()
|
|
|
|
mockGit := mock_common.NewMockGit(ctl)
|
|
config := &common.AutogitConfig{
|
|
Organization: "test-org",
|
|
GitProjectName: "test-prj#main",
|
|
}
|
|
|
|
processor := &PRProcessor{
|
|
config: config,
|
|
git: mockGit,
|
|
}
|
|
|
|
prset := &common.PRSet{
|
|
Config: config,
|
|
PRs: []*common.PRInfo{
|
|
{
|
|
RemoteName: "origin",
|
|
PR: &models.PullRequest{
|
|
Base: &models.PRBranchInfo{
|
|
Name: "main",
|
|
Repo: &models.Repository{
|
|
Name: "test-prj",
|
|
Owner: &models.User{UserName: "test-org"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
t.Run("Clean rebase", func(t *testing.T) {
|
|
mockGit.EXPECT().GitExec(common.DefaultGitPrj, "rebase", "origin/main").Return(nil)
|
|
err := processor.RebaseAndSkipSubmoduleCommits(prset, "main")
|
|
if err != nil {
|
|
t.Errorf("Expected nil error, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("Rebase with submodule conflict - skip", func(t *testing.T) {
|
|
// First rebase fails
|
|
mockGit.EXPECT().GitExec(common.DefaultGitPrj, "rebase", "origin/main").Return(errors.New("conflict"))
|
|
// Status shows submodule change
|
|
mockGit.EXPECT().GitStatus(common.DefaultGitPrj).Return([]common.GitStatusData{
|
|
{SubmoduleChanges: "S..."},
|
|
}, nil)
|
|
// Skip called
|
|
mockGit.EXPECT().GitExec(common.DefaultGitPrj, "rebase", "--skip").Return(nil)
|
|
|
|
err := processor.RebaseAndSkipSubmoduleCommits(prset, "main")
|
|
if err != nil {
|
|
t.Errorf("Expected nil error, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("Rebase with real conflict - abort", func(t *testing.T) {
|
|
mockGit.EXPECT().GitExec(common.DefaultGitPrj, "rebase", "origin/main").Return(errors.New("conflict"))
|
|
// Status shows real change
|
|
mockGit.EXPECT().GitStatus(common.DefaultGitPrj).Return([]common.GitStatusData{
|
|
{SubmoduleChanges: "N..."},
|
|
}, nil)
|
|
// Abort called
|
|
mockGit.EXPECT().GitExecOrPanic(common.DefaultGitPrj, "rebase", "--abort").Return()
|
|
|
|
err := processor.RebaseAndSkipSubmoduleCommits(prset, "main")
|
|
if err == nil || !strings.Contains(err.Error(), "Unexpected conflict in rebase") {
|
|
t.Errorf("Expected 'Unexpected conflict' error, got %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestUpdatePrjGitPR(t *testing.T) {
|
|
ctl := gomock.NewController(t)
|
|
defer ctl.Finish()
|
|
|
|
mockGit := mock_common.NewMockGit(ctl)
|
|
gitea := mock_common.NewMockGitea(ctl)
|
|
Gitea = gitea
|
|
CurrentUser = &models.User{UserName: "bot"}
|
|
|
|
config := &common.AutogitConfig{
|
|
Organization: "test-org",
|
|
GitProjectName: "test-prj#main",
|
|
}
|
|
|
|
processor := &PRProcessor{
|
|
config: config,
|
|
git: mockGit,
|
|
}
|
|
|
|
t.Run("Only project git in PR", func(t *testing.T) {
|
|
prset := &common.PRSet{
|
|
Config: config,
|
|
PRs: []*common.PRInfo{
|
|
{
|
|
RemoteName: "origin",
|
|
PR: &models.PullRequest{
|
|
Base: &models.PRBranchInfo{
|
|
Name: "main",
|
|
Repo: &models.Repository{
|
|
Name: "test-prj",
|
|
Owner: &models.User{UserName: "test-org"},
|
|
},
|
|
},
|
|
Head: &models.PRBranchInfo{
|
|
Sha: "sha1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockGit.EXPECT().GitExecOrPanic(common.DefaultGitPrj, "fetch", "origin", "sha1")
|
|
err := processor.UpdatePrjGitPR(prset)
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("PR on another remote", func(t *testing.T) {
|
|
prset := &common.PRSet{
|
|
Config: config,
|
|
PRs: []*common.PRInfo{
|
|
{
|
|
RemoteName: "origin",
|
|
PR: &models.PullRequest{
|
|
Base: &models.PRBranchInfo{
|
|
Name: "main",
|
|
RepoID: 1,
|
|
Repo: &models.Repository{
|
|
Name: "test-prj",
|
|
Owner: &models.User{UserName: "test-org"},
|
|
SSHURL: "url",
|
|
},
|
|
},
|
|
Head: &models.PRBranchInfo{
|
|
Name: "feature",
|
|
RepoID: 2, // Different RepoID
|
|
Sha: "sha1",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
PR: &models.PullRequest{
|
|
Base: &models.PRBranchInfo{
|
|
Name: "other",
|
|
Repo: &models.Repository{
|
|
Name: "other-pkg",
|
|
Owner: &models.User{UserName: "test-org"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("remote2", nil)
|
|
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", "remote2", "sha1")
|
|
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "checkout", "sha1")
|
|
|
|
err := processor.UpdatePrjGitPR(prset)
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("Standard update with rebase and force push", func(t *testing.T) {
|
|
prset := &common.PRSet{
|
|
Config: config,
|
|
BotUser: "bot",
|
|
PRs: []*common.PRInfo{
|
|
{
|
|
RemoteName: "origin",
|
|
PR: &models.PullRequest{
|
|
User: &models.User{UserName: "bot"},
|
|
Mergeable: false, // Triggers rebase
|
|
Base: &models.PRBranchInfo{
|
|
Name: "main",
|
|
RepoID: 1,
|
|
Repo: &models.Repository{
|
|
Name: "test-prj",
|
|
Owner: &models.User{UserName: "test-org"},
|
|
SSHURL: "url",
|
|
},
|
|
},
|
|
Head: &models.PRBranchInfo{
|
|
Name: "PR_branch",
|
|
RepoID: 1,
|
|
Sha: "old-head",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
PR: &models.PullRequest{
|
|
State: "open",
|
|
Base: &models.PRBranchInfo{
|
|
Repo: &models.Repository{
|
|
Name: "pkg-a",
|
|
Owner: &models.User{UserName: "test-org"},
|
|
},
|
|
},
|
|
Head: &models.PRBranchInfo{Sha: "pkg-sha"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
|
|
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", gomock.Any(), gomock.Any())
|
|
// Rebase expectations
|
|
mockGit.EXPECT().GitExec(gomock.Any(), "rebase", gomock.Any()).Return(nil)
|
|
|
|
// First call returns old-head, second returns new-head to trigger push
|
|
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("old-head", nil).Times(1)
|
|
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("new-head", nil).Times(1)
|
|
|
|
// SetSubmodulesToMatchPRSet expectations
|
|
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), "HEAD").Return(map[string]string{"pkg-a": "old-pkg-sha"}, nil)
|
|
// Catch all GitExec calls with any number of arguments up to 5
|
|
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
|
|
mockGit.EXPECT().GitStatus(gomock.Any()).Return(nil, nil).AnyTimes()
|
|
|
|
// UpdatePullRequest expectation
|
|
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
|
|
err := processor.UpdatePrjGitPR(prset)
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("isPrTitleSame logic", func(t *testing.T) {
|
|
longTitle := strings.Repeat("a", 251) + "..."
|
|
prset := &common.PRSet{
|
|
Config: config,
|
|
BotUser: "bot",
|
|
PRs: []*common.PRInfo{
|
|
{
|
|
RemoteName: "origin",
|
|
PR: &models.PullRequest{
|
|
User: &models.User{UserName: "bot"},
|
|
Title: longTitle,
|
|
Base: &models.PRBranchInfo{
|
|
Name: "main",
|
|
RepoID: 1,
|
|
Repo: &models.Repository{
|
|
Name: "test-prj",
|
|
Owner: &models.User{UserName: "test-org"},
|
|
},
|
|
},
|
|
Head: &models.PRBranchInfo{
|
|
Name: "PR_branch",
|
|
RepoID: 1,
|
|
Sha: "head",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
PR: &models.PullRequest{
|
|
State: "open",
|
|
Base: &models.PRBranchInfo{
|
|
Repo: &models.Repository{
|
|
Name: "pkg-a",
|
|
Owner: &models.User{UserName: "test-org"},
|
|
},
|
|
},
|
|
Head: &models.PRBranchInfo{Sha: "pkg-sha"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
|
|
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", gomock.Any(), gomock.Any())
|
|
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head", nil).AnyTimes()
|
|
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), "HEAD").Return(map[string]string{"pkg-a": "pkg-sha"}, nil)
|
|
// mockGit.EXPECT().GitExec(...) not called because no push (headCommit == newHeadCommit)
|
|
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
|
|
err := processor.UpdatePrjGitPR(prset)
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestCreatePRjGitPR_Integration(t *testing.T) {
|
|
ctl := gomock.NewController(t)
|
|
defer ctl.Finish()
|
|
|
|
mockGit := mock_common.NewMockGit(ctl)
|
|
gitea := mock_common.NewMockGitea(ctl)
|
|
Gitea = gitea
|
|
|
|
config := &common.AutogitConfig{
|
|
Organization: "test-org",
|
|
GitProjectName: "test-prj#main",
|
|
}
|
|
|
|
processor := &PRProcessor{
|
|
config: config,
|
|
git: mockGit,
|
|
}
|
|
|
|
prset := &common.PRSet{
|
|
Config: config,
|
|
PRs: []*common.PRInfo{
|
|
{
|
|
PR: &models.PullRequest{
|
|
State: "open",
|
|
Base: &models.PRBranchInfo{
|
|
Repo: &models.Repository{Name: "pkg-a", Owner: &models.User{UserName: "test-org"}},
|
|
},
|
|
Head: &models.PRBranchInfo{Sha: "pkg-sha"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
t.Run("Create new project PR with label", func(t *testing.T) {
|
|
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head-sha", nil).AnyTimes()
|
|
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{}, nil).AnyTimes()
|
|
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
mockGit.EXPECT().GitStatus(gomock.Any()).Return(nil, nil).AnyTimes()
|
|
|
|
prjPR := &models.PullRequest{
|
|
Index: 10,
|
|
Base: &models.PRBranchInfo{
|
|
Name: "main",
|
|
RepoID: 1,
|
|
Repo: &models.Repository{Name: "test-prj", Owner: &models.User{UserName: "test-org"}},
|
|
},
|
|
Head: &models.PRBranchInfo{
|
|
Sha: "prj-head-sha",
|
|
},
|
|
}
|
|
|
|
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{Owner: &models.User{UserName: "test-org"}}, nil).AnyTimes()
|
|
// CreatePullRequestIfNotExist returns isNew=true
|
|
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(prjPR, nil, true).AnyTimes()
|
|
// Expect SetLabels to be called for new PR
|
|
gitea.EXPECT().SetLabels("test-org", gomock.Any(), int64(10), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
|
|
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
|
|
err := processor.CreatePRjGitPR("PR_branch", prset)
|
|
if err != nil {
|
|
t.Errorf("CreatePRjGitPR failed: %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestMultiPackagePRSet(t *testing.T) {
|
|
GitAuthor = "Bot" // Ensure non-empty author
|
|
config := &common.AutogitConfig{
|
|
Organization: "test-org",
|
|
GitProjectName: "test-prj#main",
|
|
}
|
|
|
|
prset := &common.PRSet{
|
|
Config: config,
|
|
}
|
|
|
|
for i := 1; i <= 5; i++ {
|
|
name := fmt.Sprintf("pkg-%d", i)
|
|
prset.PRs = append(prset.PRs, &common.PRInfo{
|
|
PR: &models.PullRequest{
|
|
Index: int64(i),
|
|
State: "open",
|
|
Base: &models.PRBranchInfo{
|
|
Ref: "main",
|
|
Repo: &models.Repository{Name: name, Owner: &models.User{UserName: "test-org"}},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
GitAuthor = "Bot"
|
|
title, desc := PrjGitDescription(prset)
|
|
|
|
// PrjGitDescription generates title like "Forwarded PRs: pkg-1, pkg-2, pkg-3, pkg-4, pkg-5"
|
|
for i := 1; i <= 5; i++ {
|
|
name := fmt.Sprintf("pkg-%d", i)
|
|
if !strings.Contains(title, name) {
|
|
t.Errorf("Title missing package %s: %s", name, title)
|
|
}
|
|
}
|
|
|
|
for i := 1; i <= 5; i++ {
|
|
ref := fmt.Sprintf("PR: test-org/pkg-%d!%d", i, i)
|
|
if !strings.Contains(desc, ref) {
|
|
t.Errorf("Description missing reference %s", ref)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPRProcessor_Process_EdgeCases(t *testing.T) {
|
|
ctl := gomock.NewController(t)
|
|
defer ctl.Finish()
|
|
|
|
mockGit := mock_common.NewMockGit(ctl)
|
|
gitea := mock_common.NewMockGitea(ctl)
|
|
Gitea = gitea
|
|
CurrentUser = &models.User{UserName: "bot"}
|
|
|
|
config := &common.AutogitConfig{
|
|
Organization: "test-org",
|
|
GitProjectName: "test-prj#main",
|
|
}
|
|
|
|
processor := &PRProcessor{
|
|
config: config,
|
|
git: mockGit,
|
|
}
|
|
|
|
t.Run("Merged project PR - update downstream", func(t *testing.T) {
|
|
prjPR := &models.PullRequest{
|
|
State: "closed",
|
|
HasMerged: true,
|
|
Index: 100,
|
|
Base: &models.PRBranchInfo{
|
|
Name: "main",
|
|
Repo: &models.Repository{Name: "test-prj", Owner: &models.User{UserName: "test-org"}, SSHURL: "url"},
|
|
},
|
|
Head: &models.PRBranchInfo{Name: "PR_branch"},
|
|
}
|
|
|
|
pkgPR := &models.PullRequest{
|
|
State: "open",
|
|
Index: 1,
|
|
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg-a", Owner: &models.User{UserName: "test-org"}}},
|
|
Head: &models.PRBranchInfo{Sha: "pkg-sha"},
|
|
}
|
|
|
|
prset := &common.PRSet{
|
|
BotUser: "bot",
|
|
Config: config,
|
|
PRs: []*common.PRInfo{
|
|
{PR: prjPR},
|
|
{PR: pkgPR},
|
|
},
|
|
}
|
|
_ = prset // Suppress unused for now if it's really unused, but it's likely used by common.FetchPRSet internally if we weren't mocking everything
|
|
|
|
// Mock expectations for Process setup
|
|
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(prjPR, nil).AnyTimes()
|
|
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
|
|
// Mock maintainership file calls
|
|
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
// Mock expectations for the merged path
|
|
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
|
|
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{"pkg-a": "old-sha"}, nil).AnyTimes()
|
|
gitea.EXPECT().GetRecentCommits(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Commit{{SHA: "pkg-sha"}}, nil).AnyTimes()
|
|
|
|
// Downstream update expectations
|
|
gitea.EXPECT().AddComment(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
gitea.EXPECT().ManualMergePR(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
err := processor.Process(pkgPR)
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("Superfluous PR - close it", func(t *testing.T) {
|
|
prjPR := &models.PullRequest{
|
|
State: "open",
|
|
Index: 100,
|
|
User: &models.User{UserName: "bot"},
|
|
Body: "Forwarded PRs: \n", // No PRs linked
|
|
Base: &models.PRBranchInfo{
|
|
Name: "main",
|
|
Repo: &models.Repository{Name: "test-prj", Owner: &models.User{UserName: "test-org"}},
|
|
},
|
|
Head: &models.PRBranchInfo{Name: "PR_branch", Sha: "head-sha"},
|
|
MergeBase: "base-sha",
|
|
}
|
|
|
|
prset := &common.PRSet{
|
|
BotUser: "bot",
|
|
Config: config,
|
|
PRs: []*common.PRInfo{
|
|
{PR: prjPR},
|
|
},
|
|
}
|
|
_ = prset
|
|
|
|
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(prjPR, nil).AnyTimes()
|
|
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{Owner: &models.User{UserName: "test-org"}}, nil).AnyTimes()
|
|
|
|
// Mock maintainership file calls
|
|
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
// Standard update calls within Process
|
|
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", gomock.Any(), gomock.Any()).AnyTimes()
|
|
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head-sha", nil).AnyTimes()
|
|
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{}, nil).AnyTimes()
|
|
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
mockGit.EXPECT().GitStatus(gomock.Any()).Return(nil, nil).AnyTimes()
|
|
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
|
|
// Diff check for superfluous
|
|
mockGit.EXPECT().GitDiff(gomock.Any(), gomock.Any(), gomock.Any()).Return("", nil).AnyTimes()
|
|
|
|
// Expectations for closing
|
|
gitea.EXPECT().AddComment(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
err := processor.Process(prjPR)
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestVerifyRepositoryConfiguration(t *testing.T) {
|
|
ctl := gomock.NewController(t)
|
|
defer ctl.Finish()
|
|
|
|
gitea := mock_common.NewMockGitea(ctl)
|
|
Gitea = gitea
|
|
|
|
repo := &models.Repository{
|
|
Name: "test-repo",
|
|
Owner: &models.User{
|
|
UserName: "test-user",
|
|
},
|
|
AutodetectManualMerge: true,
|
|
AllowManualMerge: true,
|
|
}
|
|
|
|
t.Run("Config already correct", func(t *testing.T) {
|
|
err := verifyRepositoryConfiguration(repo)
|
|
if err != nil {
|
|
t.Errorf("Expected nil error, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("Config incorrect - trigger update", func(t *testing.T) {
|
|
repo.AllowManualMerge = false
|
|
gitea.EXPECT().SetRepoOptions("test-user", "test-repo", true).Return(&models.Repository{}, nil)
|
|
|
|
err := verifyRepositoryConfiguration(repo)
|
|
if err != nil {
|
|
t.Errorf("Expected nil error, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("Update failure", func(t *testing.T) {
|
|
repo.AllowManualMerge = false
|
|
expectedErr := errors.New("update failed")
|
|
gitea.EXPECT().SetRepoOptions("test-user", "test-repo", true).Return(nil, expectedErr)
|
|
|
|
err := verifyRepositoryConfiguration(repo)
|
|
if err != expectedErr {
|
|
t.Errorf("Expected %v, got %v", expectedErr, err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestProcessFunc(t *testing.T) {
|
|
ctl := gomock.NewController(t)
|
|
defer ctl.Finish()
|
|
|
|
gitea := mock_common.NewMockGitea(ctl)
|
|
Gitea = gitea
|
|
mockGit := mock_common.NewMockGit(ctl)
|
|
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
GitHandler = mockGitGen
|
|
|
|
config := &common.AutogitConfig{
|
|
Organization: "test-org",
|
|
GitProjectName: "test-prj#main",
|
|
}
|
|
|
|
reqProc := &RequestProcessor{
|
|
configuredRepos: map[string][]*common.AutogitConfig{
|
|
"test-org": {config},
|
|
},
|
|
}
|
|
|
|
modelPR := &models.PullRequest{
|
|
Index: 1,
|
|
Base: &models.PRBranchInfo{
|
|
Ref: "main",
|
|
Repo: &models.Repository{
|
|
Name: "test-repo",
|
|
DefaultBranch: "main",
|
|
Owner: &models.User{UserName: "test-org"},
|
|
},
|
|
},
|
|
}
|
|
|
|
t.Run("PullRequestWebhookEvent", func(t *testing.T) {
|
|
event := &common.PullRequestWebhookEvent{
|
|
Pull_Request: &common.PullRequest{
|
|
Number: 1,
|
|
Base: common.Head{
|
|
Ref: "main",
|
|
Repo: &common.Repository{
|
|
Name: "test-repo",
|
|
Owner: &common.Organization{
|
|
Username: "test-org",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
gitea.EXPECT().GetPullRequest("test-org", "test-repo", int64(1)).Return(modelPR, nil).AnyTimes()
|
|
// AllocatePRProcessor and ProcesPullRequest calls inside
|
|
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil)
|
|
mockGit.EXPECT().GetPath().Return("/tmp").AnyTimes()
|
|
mockGit.EXPECT().Close().Return(nil)
|
|
|
|
// Expect Process calls (mocked via mockGit mostly)
|
|
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
|
|
err := reqProc.ProcessFunc(&common.Request{Data: event})
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("IssueCommentWebhookEvent", func(t *testing.T) {
|
|
event := &common.IssueCommentWebhookEvent{
|
|
Issue: &common.IssueDetail{Number: 1},
|
|
Repository: &common.Repository{
|
|
Name: "test-repo",
|
|
Owner: &common.Organization{Username: "test-org"},
|
|
},
|
|
}
|
|
|
|
gitea.EXPECT().GetPullRequest("test-org", "test-repo", int64(1)).Return(modelPR, nil).AnyTimes()
|
|
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil)
|
|
mockGit.EXPECT().Close().Return(nil)
|
|
|
|
err := reqProc.ProcessFunc(&common.Request{Data: event})
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("Recursion limit", func(t *testing.T) {
|
|
reqProc.recursive = 3
|
|
err := reqProc.ProcessFunc(&common.Request{})
|
|
if err != nil {
|
|
t.Errorf("Expected nil error on recursion limit, got %v", err)
|
|
}
|
|
if reqProc.recursive != 3 {
|
|
t.Errorf("Expected recursive to be 3, got %d", reqProc.recursive)
|
|
}
|
|
reqProc.recursive = 0 // Reset
|
|
})
|
|
|
|
t.Run("Invalid data format", func(t *testing.T) {
|
|
err := reqProc.ProcessFunc(&common.Request{Data: nil})
|
|
if err == nil || !strings.Contains(err.Error(), "Invalid data format") {
|
|
t.Errorf("Expected 'Invalid data format' error, got %v", err)
|
|
}
|
|
})
|
|
}
|