12 Commits

Author SHA256 Message Date
44bd6c23e5 . 2026-01-22 18:24:23 +01:00
17bfc36801 finish script 2026-01-22 18:21:19 +01:00
73d8c3e97f add parser 2026-01-22 18:12:20 +01:00
64e5b51488 check query prameters 2026-01-22 15:49:17 +01:00
1fe6bd98d4 Merge branch 'gitpkgs' of src.opensuse.org:git-workflow/autogits into gitpkgs 2026-01-22 14:22:14 +01:00
e859ed1b54 proper url parsing 2026-01-22 14:20:14 +01:00
1ad2e1738c more url parsing 2026-01-22 14:11:53 +01:00
a6d5d4c79d add ugly url parsing 2026-01-22 14:11:53 +01:00
f5d6f50b32 wip: git package updates 2026-01-22 14:11:53 +01:00
87633e7508 more url parsing 2026-01-21 13:57:53 +01:00
56ce07514a add ugly url parsing 2026-01-21 12:58:48 +01:00
1d95b4cf0f wip: git package updates 2026-01-21 12:49:19 +01:00
36 changed files with 794 additions and 4064 deletions

View File

@@ -23,6 +23,7 @@ jobs:
- run: git checkout FETCH_HEAD - run: git checkout FETCH_HEAD
- run: go generate -C common - run: go generate -C common
- run: go generate -C workflow-pr - run: go generate -C workflow-pr
- run: go generate -C workflow-pr/interfaces
- run: git add -N .; git diff - run: git add -N .; git diff
- run: | - run: |
status=$(git status --short) status=$(git status --short)

View File

@@ -12,6 +12,7 @@ jobs:
- run: git checkout FETCH_HEAD - run: git checkout FETCH_HEAD
- run: go generate -C common - run: go generate -C common
- run: go generate -C workflow-pr - run: go generate -C workflow-pr
- run: go generate -C workflow-pr/interfaces
- run: | - run: |
host=${{ gitea.server_url }} host=${{ gitea.server_url }}
host=${host#https://} host=${host#https://}

View File

@@ -66,7 +66,6 @@ const (
Label_StagingAuto = "staging/Auto" Label_StagingAuto = "staging/Auto"
Label_ReviewPending = "review/Pending" Label_ReviewPending = "review/Pending"
Label_ReviewDone = "review/Done" Label_ReviewDone = "review/Done"
Label_NewRepository = "new/New Repository"
) )
func LabelKey(tag_value string) string { func LabelKey(tag_value string) string {
@@ -93,7 +92,6 @@ type AutogitConfig struct {
NoProjectGitPR bool // do not automatically create project git PRs, just assign reviewers and assume somethign else creates the ProjectGit PR NoProjectGitPR bool // do not automatically create project git PRs, just assign reviewers and assume somethign else creates the ProjectGit PR
ManualMergeOnly bool // only merge with "Merge OK" comment by Project Maintainers and/or Package Maintainers and/or reviewers ManualMergeOnly bool // only merge with "Merge OK" comment by Project Maintainers and/or Package Maintainers and/or reviewers
ManualMergeProject bool // require merge of ProjectGit PRs with "Merge OK" by ProjectMaintainers and/or reviewers ManualMergeProject bool // require merge of ProjectGit PRs with "Merge OK" by ProjectMaintainers and/or reviewers
ReviewRequired bool // always require a maintainer review, even if maintainer submits it. Only ignored if no other package or project reviewers
} }
type AutogitConfigs []*AutogitConfig type AutogitConfigs []*AutogitConfig
@@ -295,9 +293,9 @@ func (config *AutogitConfig) GetRemoteBranch() string {
} }
func (config *AutogitConfig) Label(label string) string { func (config *AutogitConfig) Label(label string) string {
if t, found := config.Labels[LabelKey(label)]; found { if t, found := config.Labels[LabelKey(label)]; found {
return t return t
} }
return label return label
} }

View File

@@ -42,7 +42,6 @@ type GitSubmoduleLister interface {
type GitDirectoryLister interface { type GitDirectoryLister interface {
GitDirectoryList(gitPath, commitId string) (dirlist map[string]string, err error) GitDirectoryList(gitPath, commitId string) (dirlist map[string]string, err error)
GitDirectoryContentList(gitPath, commitId string) (dirlist map[string]string, err error)
} }
type GitStatusLister interface { type GitStatusLister interface {
@@ -273,11 +272,7 @@ func (e *GitHandlerImpl) GitClone(repo, branch, remoteUrl string) (string, error
LogDebug("branch", branch) LogDebug("branch", branch)
} }
*/ */
args := []string{"fetch", "--prune", remoteName} args := []string{"fetch", "--prune", remoteName, branch}
if len(branch) > 0 {
args = append(args, branch)
}
if strings.TrimSpace(e.GitExecWithOutputOrPanic(repo, "rev-parse", "--is-shallow-repository")) == "true" { if strings.TrimSpace(e.GitExecWithOutputOrPanic(repo, "rev-parse", "--is-shallow-repository")) == "true" {
args = slices.Insert(args, 1, "--unshallow") args = slices.Insert(args, 1, "--unshallow")
} }
@@ -792,7 +787,7 @@ func (e *GitHandlerImpl) GitCatFile(cwd, commitId, filename string) (data []byte
return return
} }
// return (directory) -> (hash) map for all submodules // return (filename) -> (hash) map for all submodules
func (e *GitHandlerImpl) GitDirectoryList(gitPath, commitId string) (directoryList map[string]string, err error) { func (e *GitHandlerImpl) GitDirectoryList(gitPath, commitId string) (directoryList map[string]string, err error) {
var done sync.Mutex var done sync.Mutex
directoryList = make(map[string]string) directoryList = make(map[string]string)
@@ -866,82 +861,6 @@ func (e *GitHandlerImpl) GitDirectoryList(gitPath, commitId string) (directoryLi
return directoryList, err return directoryList, err
} }
// return (directory) -> (hash) map for all submodules
func (e *GitHandlerImpl) GitDirectoryContentList(gitPath, commitId string) (directoryList map[string]string, err error) {
var done sync.Mutex
directoryList = make(map[string]string)
done.Lock()
data_in, data_out := ChanIO{make(chan byte)}, ChanIO{make(chan byte)}
LogDebug("Getting directory content for:", commitId)
go func() {
defer done.Unlock()
defer close(data_out.ch)
data_out.Write([]byte(commitId))
data_out.ch <- '\x00'
var c GitCommit
c, err = parseGitCommit(data_in.ch)
if err != nil {
err = fmt.Errorf("Error parsing git commit. Err: %w", err)
return
}
trees := make(map[string]string)
trees[""] = c.Tree
for len(trees) > 0 {
for p, tree := range trees {
delete(trees, p)
data_out.Write([]byte(tree))
data_out.ch <- '\x00'
var tree GitTree
tree, err = parseGitTree(data_in.ch)
if err != nil {
err = fmt.Errorf("Error parsing git tree: %w", err)
return
}
for _, te := range tree.items {
if te.isBlob() || te.isSubmodule() {
directoryList[p+te.name] = te.hash
} else if te.isTree() {
trees[p+te.name] = te.hash
}
}
}
}
}()
cmd := exec.Command("/usr/bin/git", "cat-file", "--batch", "-Z")
cmd.Env = []string{
"GIT_CEILING_DIRECTORIES=" + e.GitPath,
"GIT_LFS_SKIP_SMUDGE=1",
"GIT_CONFIG_GLOBAL=/dev/null",
}
cmd.Dir = filepath.Join(e.GitPath, gitPath)
cmd.Stdout = &data_in
cmd.Stdin = &data_out
cmd.Stderr = writeFunc(func(data []byte) (int, error) {
LogError(string(data))
return len(data), nil
})
LogDebug("command run:", cmd.Args)
if e := cmd.Run(); e != nil {
LogError(e)
close(data_in.ch)
close(data_out.ch)
return directoryList, e
}
done.Lock()
return directoryList, err
}
// return (filename) -> (hash) map for all submodules // return (filename) -> (hash) map for all submodules
func (e *GitHandlerImpl) GitSubmoduleList(gitPath, commitId string) (submoduleList map[string]string, err error) { func (e *GitHandlerImpl) GitSubmoduleList(gitPath, commitId string) (submoduleList map[string]string, err error) {
var done sync.Mutex var done sync.Mutex

View File

@@ -24,7 +24,6 @@ import (
"os" "os"
"os/exec" "os/exec"
"path" "path"
"path/filepath"
"slices" "slices"
"strings" "strings"
"testing" "testing"
@@ -94,56 +93,6 @@ func TestGitClone(t *testing.T) {
} }
} }
func TestGitCloneCommitID(t *testing.T) {
execPath, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
d := t.TempDir()
if err := os.Chdir(d); err != nil {
t.Fatal(err)
}
defer os.Chdir(execPath)
cmd := exec.Command(path.Join(execPath, "test_repo_setup.sh"))
if out, err := cmd.CombinedOutput(); err != nil {
t.Log(string(out))
t.Fatal(err)
}
gh, err := AllocateGitWorkTree(d, "Test", "test@example.com")
if err != nil {
t.Fatal(err)
}
g, err := gh.CreateGitHandler("org")
if err != nil {
t.Fatal(err)
}
// Get a commit ID from pkgA
remoteUrl := "file://" + d + "/pkgA"
out, err := exec.Command("git", "-C", path.Join(d, "pkgA"), "rev-parse", "main").Output()
if err != nil {
t.Fatal(err)
}
commitID := strings.TrimSpace(string(out))
repo := "pkgAcloneCommitID"
if _, err := g.GitClone(repo, commitID, remoteUrl); err != nil {
t.Skip("TODO: Add GitClone CommitID support")
t.Fatalf("GitClone failed with commit ID: %v", err)
}
// Verify we are at the right commit
head, err := g.GitBranchHead(repo, commitID)
if err != nil {
t.Fatalf("GitBranchHead failed: %v", err)
}
if head != commitID {
t.Errorf("Expected head %s, got %s", commitID, head)
}
}
func TestGitMsgParsing(t *testing.T) { func TestGitMsgParsing(t *testing.T) {
t.Run("tree message with size 56", func(t *testing.T) { t.Run("tree message with size 56", func(t *testing.T) {
const hdr = "f40888ea4515fe2e8eea617a16f5f50a45f652d894de3ad181d58de3aafb8f98 tree 56\x00" const hdr = "f40888ea4515fe2e8eea617a16f5f50a45f652d894de3ad181d58de3aafb8f98 tree 56\x00"
@@ -647,66 +596,3 @@ func TestGitStatusParse(t *testing.T) {
}) })
} }
} }
func TestGitDirectoryListRepro(t *testing.T) {
d := t.TempDir()
// Setup a mock environment for GitHandlerImpl
gh, err := AllocateGitWorkTree(d, "Test", "test@example.com")
if err != nil {
t.Fatal(err)
}
org := "repo-org"
repoName := "test-repo"
repoPath := filepath.Join(d, org, repoName)
err = os.MkdirAll(repoPath, 0755)
if err != nil {
t.Fatal(err)
}
runGit := func(args ...string) {
cmd := exec.Command("git", args...)
cmd.Dir = repoPath
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("git %v failed: %v\n%s", args, err, out)
}
}
runGit("init", "-b", "main", "--object-format=sha256")
runGit("config", "user.email", "test@example.com")
runGit("config", "user.name", "test")
// Create a directory and a file
err = os.Mkdir(filepath.Join(repoPath, "subdir"), 0755)
if err != nil {
t.Fatal(err)
}
err = os.WriteFile(filepath.Join(repoPath, "subdir", "file.txt"), []byte("hello"), 0644)
if err != nil {
t.Fatal(err)
}
runGit("add", "subdir/file.txt")
runGit("commit", "-m", "add subdir")
// Now create the handler
g, err := gh.CreateGitHandler(org)
if err != nil {
t.Fatal(err)
}
// Call GitDirectoryList
dirs, err := g.GitDirectoryList(repoName, "HEAD")
if err != nil {
t.Fatal(err)
}
t.Logf("Directories found: %v", dirs)
if len(dirs) == 0 {
t.Error("No directories found, but 'subdir' should be there")
}
if _, ok := dirs["subdir"]; !ok {
t.Errorf("Expected 'subdir' in directory list, got %v", dirs)
}
}

View File

@@ -75,12 +75,7 @@ type GiteaLabelSettter interface {
SetLabels(org, repo string, idx int64, labels []string) ([]*models.Label, error) SetLabels(org, repo string, idx int64, labels []string) ([]*models.Label, error)
} }
type GiteaIssueFetcher interface {
GetIssue(org, repo string, idx int64) (*models.Issue, error)
}
type GiteaTimelineFetcher interface { type GiteaTimelineFetcher interface {
ResetTimelineCache(org, repo string, idx int64)
GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error) GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error)
} }
@@ -152,7 +147,6 @@ type GiteaReviewRequester interface {
type GiteaReviewUnrequester interface { type GiteaReviewUnrequester interface {
UnrequestReview(org, repo string, id int64, reviwers ...string) error UnrequestReview(org, repo string, id int64, reviwers ...string) error
UpdateIssue(org, repo string, idx int64, options *models.EditIssueOption) (*models.Issue, error)
} }
type GiteaReviewer interface { type GiteaReviewer interface {
@@ -206,7 +200,6 @@ type Gitea interface {
GiteaSetRepoOptions GiteaSetRepoOptions
GiteaLabelGetter GiteaLabelGetter
GiteaLabelSettter GiteaLabelSettter
GiteaIssueFetcher
GetNotifications(Type string, since *time.Time) ([]*models.NotificationThread, error) GetNotifications(Type string, since *time.Time) ([]*models.NotificationThread, error)
GetDoneNotifications(Type string, page int64) ([]*models.NotificationThread, error) GetDoneNotifications(Type string, page int64) ([]*models.NotificationThread, error)
@@ -515,26 +508,6 @@ func (gitea *GiteaTransport) SetLabels(owner, repo string, idx int64, labels []s
return ret.Payload, nil return ret.Payload, nil
} }
func (gitea *GiteaTransport) GetIssue(owner, repo string, idx int64) (*models.Issue, error) {
ret, err := gitea.client.Issue.IssueGetIssue(
issue.NewIssueGetIssueParams().WithOwner(owner).WithRepo(repo).WithIndex(idx),
gitea.transport.DefaultAuthentication)
if err != nil {
return nil, err
}
return ret.Payload, nil
}
func (gitea *GiteaTransport) UpdateIssue(owner, repo string, idx int64, options *models.EditIssueOption) (*models.Issue, error) {
ret, err := gitea.client.Issue.IssueEditIssue(
issue.NewIssueEditIssueParams().WithOwner(owner).WithRepo(repo).WithIndex(idx).WithBody(options),
gitea.transport.DefaultAuthentication)
if err != nil {
return nil, err
}
return ret.Payload, nil
}
const ( const (
GiteaNotificationType_Pull = "Pull" GiteaNotificationType_Pull = "Pull"
) )
@@ -742,7 +715,6 @@ func (gitea *GiteaTransport) CreatePullRequestIfNotExist(repo *models.Repository
) )
if err != nil { if err != nil {
LogError("owner:", repo.Owner.UserName, " repo:", repo.Name, " body:", prOptions)
return nil, fmt.Errorf("Cannot create pull request. %w", err), true return nil, fmt.Errorf("Cannot create pull request. %w", err), true
} }
@@ -841,17 +813,6 @@ type TimelineCacheData struct {
var giteaTimelineCache map[string]TimelineCacheData = make(map[string]TimelineCacheData) var giteaTimelineCache map[string]TimelineCacheData = make(map[string]TimelineCacheData)
var giteaTimelineCacheMutex sync.RWMutex var giteaTimelineCacheMutex sync.RWMutex
func (gitea *GiteaTransport) ResetTimelineCache(org, repo string, idx int64) {
giteaTimelineCacheMutex.Lock()
defer giteaTimelineCacheMutex.Unlock()
prID := fmt.Sprintf("%s/%s!%d", org, repo, idx)
Cache, IsCached := giteaTimelineCache[prID]
if IsCached {
Cache.lastCheck = Cache.lastCheck.Add(-time.Hour)
}
}
// returns timeline in reverse chronological create order // returns timeline in reverse chronological create order
func (gitea *GiteaTransport) GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error) { func (gitea *GiteaTransport) GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error) {
page := int64(1) page := int64(1)

View File

@@ -25,7 +25,6 @@ const ProjectFileKey = "_project"
type MaintainershipMap struct { type MaintainershipMap struct {
Data map[string][]string Data map[string][]string
IsDir bool IsDir bool
Config *AutogitConfig
FetchPackage func(string) ([]byte, error) FetchPackage func(string) ([]byte, error)
} }
@@ -40,9 +39,7 @@ func parseMaintainershipData(data []byte) (*MaintainershipMap, error) {
return maintainers, nil return maintainers, nil
} }
func FetchProjectMaintainershipData(gitea GiteaMaintainershipReader, config *AutogitConfig) (*MaintainershipMap, error) { func FetchProjectMaintainershipData(gitea GiteaMaintainershipReader, org, prjGit, branch string) (*MaintainershipMap, error) {
org, prjGit, branch := config.GetPrjGit()
data, _, err := gitea.FetchMaintainershipDirFile(org, prjGit, branch, ProjectFileKey) data, _, err := gitea.FetchMaintainershipDirFile(org, prjGit, branch, ProjectFileKey)
dir := true dir := true
if err != nil || data == nil { if err != nil || data == nil {
@@ -64,7 +61,6 @@ func FetchProjectMaintainershipData(gitea GiteaMaintainershipReader, config *Aut
m, err := parseMaintainershipData(data) m, err := parseMaintainershipData(data)
if m != nil { if m != nil {
m.Config = config
m.IsDir = dir m.IsDir = dir
m.FetchPackage = func(pkg string) ([]byte, error) { m.FetchPackage = func(pkg string) ([]byte, error) {
data, _, err := gitea.FetchMaintainershipDirFile(org, prjGit, branch, pkg) data, _, err := gitea.FetchMaintainershipDirFile(org, prjGit, branch, pkg)
@@ -153,10 +149,7 @@ func (data *MaintainershipMap) IsApproved(pkg string, reviews []*models.PullRevi
} }
LogDebug("Looking for review by:", reviewers) LogDebug("Looking for review by:", reviewers)
slices.Sort(reviewers) if slices.Contains(reviewers, submitter) {
reviewers = slices.Compact(reviewers)
SubmitterIdxInReviewers := slices.Index(reviewers, submitter)
if SubmitterIdxInReviewers > -1 && (!data.Config.ReviewRequired || len(reviewers) == 1) {
LogDebug("Submitter is maintainer. Approving.") LogDebug("Submitter is maintainer. Approving.")
return true return true
} }

View File

@@ -13,10 +13,10 @@ import (
) )
func TestMaintainership(t *testing.T) { func TestMaintainership(t *testing.T) {
config := &common.AutogitConfig{ config := common.AutogitConfig{
Branch: "bar", Branch: "bar",
Organization: "foo", Organization: "foo",
GitProjectName: common.DefaultGitPrj + "#bar", GitProjectName: common.DefaultGitPrj,
} }
packageTests := []struct { packageTests := []struct {
@@ -141,7 +141,7 @@ func TestMaintainership(t *testing.T) {
notFoundError := repository.NewRepoGetContentsNotFound() notFoundError := repository.NewRepoGetContentsNotFound()
for _, test := range packageTests { for _, test := range packageTests {
runTests := func(t *testing.T, mi common.GiteaMaintainershipReader) { runTests := func(t *testing.T, mi common.GiteaMaintainershipReader) {
maintainers, err := common.FetchProjectMaintainershipData(mi, config) maintainers, err := common.FetchProjectMaintainershipData(mi, config.Organization, config.GitProjectName, config.Branch)
if err != nil && !test.otherError { if err != nil && !test.otherError {
if test.maintainersFileErr == nil { if test.maintainersFileErr == nil {
t.Fatal("Unexpected error recieved", err) t.Fatal("Unexpected error recieved", err)
@@ -253,43 +253,3 @@ func TestMaintainershipFileWrite(t *testing.T) {
}) })
} }
} }
func TestReviewRequired(t *testing.T) {
tests := []struct {
name string
maintainers []string
config *common.AutogitConfig
is_approved bool
}{
{
name: "ReviewRequired=false",
maintainers: []string{"maintainer1", "maintainer2"},
config: &common.AutogitConfig{ReviewRequired: false},
is_approved: true,
},
{
name: "ReviewRequired=true",
maintainers: []string{"maintainer1", "maintainer2"},
config: &common.AutogitConfig{ReviewRequired: true},
is_approved: false,
},
{
name: "ReviewRequired=true",
maintainers: []string{"maintainer1"},
config: &common.AutogitConfig{ReviewRequired: true},
is_approved: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
m := &common.MaintainershipMap{
Data: map[string][]string{"": test.maintainers},
}
m.Config = test.config
if approved := m.IsApproved("", nil, "maintainer1", nil); approved != test.is_approved {
t.Error("Expected m.IsApproved()->", test.is_approved, "but didn't get it")
}
})
}
}

View File

@@ -142,45 +142,6 @@ func (m *MockGitDirectoryLister) EXPECT() *MockGitDirectoryListerMockRecorder {
return m.recorder return m.recorder
} }
// GitDirectoryContentList mocks base method.
func (m *MockGitDirectoryLister) GitDirectoryContentList(gitPath, commitId string) (map[string]string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GitDirectoryContentList", gitPath, commitId)
ret0, _ := ret[0].(map[string]string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GitDirectoryContentList indicates an expected call of GitDirectoryContentList.
func (mr *MockGitDirectoryListerMockRecorder) GitDirectoryContentList(gitPath, commitId any) *MockGitDirectoryListerGitDirectoryContentListCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitDirectoryContentList", reflect.TypeOf((*MockGitDirectoryLister)(nil).GitDirectoryContentList), gitPath, commitId)
return &MockGitDirectoryListerGitDirectoryContentListCall{Call: call}
}
// MockGitDirectoryListerGitDirectoryContentListCall wrap *gomock.Call
type MockGitDirectoryListerGitDirectoryContentListCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGitDirectoryListerGitDirectoryContentListCall) Return(dirlist map[string]string, err error) *MockGitDirectoryListerGitDirectoryContentListCall {
c.Call = c.Call.Return(dirlist, err)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGitDirectoryListerGitDirectoryContentListCall) Do(f func(string, string) (map[string]string, error)) *MockGitDirectoryListerGitDirectoryContentListCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGitDirectoryListerGitDirectoryContentListCall) DoAndReturn(f func(string, string) (map[string]string, error)) *MockGitDirectoryListerGitDirectoryContentListCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// GitDirectoryList mocks base method. // GitDirectoryList mocks base method.
func (m *MockGitDirectoryLister) GitDirectoryList(gitPath, commitId string) (map[string]string, error) { func (m *MockGitDirectoryLister) GitDirectoryList(gitPath, commitId string) (map[string]string, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
@@ -602,45 +563,6 @@ func (c *MockGitGitDiffCall) DoAndReturn(f func(string, string, string) (string,
return c return c
} }
// GitDirectoryContentList mocks base method.
func (m *MockGit) GitDirectoryContentList(gitPath, commitId string) (map[string]string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GitDirectoryContentList", gitPath, commitId)
ret0, _ := ret[0].(map[string]string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GitDirectoryContentList indicates an expected call of GitDirectoryContentList.
func (mr *MockGitMockRecorder) GitDirectoryContentList(gitPath, commitId any) *MockGitGitDirectoryContentListCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitDirectoryContentList", reflect.TypeOf((*MockGit)(nil).GitDirectoryContentList), gitPath, commitId)
return &MockGitGitDirectoryContentListCall{Call: call}
}
// MockGitGitDirectoryContentListCall wrap *gomock.Call
type MockGitGitDirectoryContentListCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGitGitDirectoryContentListCall) Return(dirlist map[string]string, err error) *MockGitGitDirectoryContentListCall {
c.Call = c.Call.Return(dirlist, err)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGitGitDirectoryContentListCall) Do(f func(string, string) (map[string]string, error)) *MockGitGitDirectoryContentListCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGitGitDirectoryContentListCall) DoAndReturn(f func(string, string) (map[string]string, error)) *MockGitGitDirectoryContentListCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// GitDirectoryList mocks base method. // GitDirectoryList mocks base method.
func (m *MockGit) GitDirectoryList(gitPath, commitId string) (map[string]string, error) { func (m *MockGit) GitDirectoryList(gitPath, commitId string) (map[string]string, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()

View File

@@ -144,69 +144,6 @@ func (c *MockGiteaLabelSettterSetLabelsCall) DoAndReturn(f func(string, string,
return c return c
} }
// MockGiteaIssueFetcher is a mock of GiteaIssueFetcher interface.
type MockGiteaIssueFetcher struct {
ctrl *gomock.Controller
recorder *MockGiteaIssueFetcherMockRecorder
isgomock struct{}
}
// MockGiteaIssueFetcherMockRecorder is the mock recorder for MockGiteaIssueFetcher.
type MockGiteaIssueFetcherMockRecorder struct {
mock *MockGiteaIssueFetcher
}
// NewMockGiteaIssueFetcher creates a new mock instance.
func NewMockGiteaIssueFetcher(ctrl *gomock.Controller) *MockGiteaIssueFetcher {
mock := &MockGiteaIssueFetcher{ctrl: ctrl}
mock.recorder = &MockGiteaIssueFetcherMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockGiteaIssueFetcher) EXPECT() *MockGiteaIssueFetcherMockRecorder {
return m.recorder
}
// GetIssue mocks base method.
func (m *MockGiteaIssueFetcher) GetIssue(org, repo string, idx int64) (*models.Issue, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetIssue", org, repo, idx)
ret0, _ := ret[0].(*models.Issue)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetIssue indicates an expected call of GetIssue.
func (mr *MockGiteaIssueFetcherMockRecorder) GetIssue(org, repo, idx any) *MockGiteaIssueFetcherGetIssueCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssue", reflect.TypeOf((*MockGiteaIssueFetcher)(nil).GetIssue), org, repo, idx)
return &MockGiteaIssueFetcherGetIssueCall{Call: call}
}
// MockGiteaIssueFetcherGetIssueCall wrap *gomock.Call
type MockGiteaIssueFetcherGetIssueCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaIssueFetcherGetIssueCall) Return(arg0 *models.Issue, arg1 error) *MockGiteaIssueFetcherGetIssueCall {
c.Call = c.Call.Return(arg0, arg1)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaIssueFetcherGetIssueCall) Do(f func(string, string, int64) (*models.Issue, error)) *MockGiteaIssueFetcherGetIssueCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaIssueFetcherGetIssueCall) DoAndReturn(f func(string, string, int64) (*models.Issue, error)) *MockGiteaIssueFetcherGetIssueCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// MockGiteaTimelineFetcher is a mock of GiteaTimelineFetcher interface. // MockGiteaTimelineFetcher is a mock of GiteaTimelineFetcher interface.
type MockGiteaTimelineFetcher struct { type MockGiteaTimelineFetcher struct {
ctrl *gomock.Controller ctrl *gomock.Controller
@@ -270,42 +207,6 @@ func (c *MockGiteaTimelineFetcherGetTimelineCall) DoAndReturn(f func(string, str
return c return c
} }
// ResetTimelineCache mocks base method.
func (m *MockGiteaTimelineFetcher) ResetTimelineCache(org, repo string, idx int64) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "ResetTimelineCache", org, repo, idx)
}
// ResetTimelineCache indicates an expected call of ResetTimelineCache.
func (mr *MockGiteaTimelineFetcherMockRecorder) ResetTimelineCache(org, repo, idx any) *MockGiteaTimelineFetcherResetTimelineCacheCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetTimelineCache", reflect.TypeOf((*MockGiteaTimelineFetcher)(nil).ResetTimelineCache), org, repo, idx)
return &MockGiteaTimelineFetcherResetTimelineCacheCall{Call: call}
}
// MockGiteaTimelineFetcherResetTimelineCacheCall wrap *gomock.Call
type MockGiteaTimelineFetcherResetTimelineCacheCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaTimelineFetcherResetTimelineCacheCall) Return() *MockGiteaTimelineFetcherResetTimelineCacheCall {
c.Call = c.Call.Return()
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaTimelineFetcherResetTimelineCacheCall) Do(f func(string, string, int64)) *MockGiteaTimelineFetcherResetTimelineCacheCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaTimelineFetcherResetTimelineCacheCall) DoAndReturn(f func(string, string, int64)) *MockGiteaTimelineFetcherResetTimelineCacheCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// MockGiteaComment is a mock of GiteaComment interface. // MockGiteaComment is a mock of GiteaComment interface.
type MockGiteaComment struct { type MockGiteaComment struct {
ctrl *gomock.Controller ctrl *gomock.Controller
@@ -802,42 +703,6 @@ func (c *MockGiteaPRTimelineReviewFetcherGetTimelineCall) DoAndReturn(f func(str
return c return c
} }
// ResetTimelineCache mocks base method.
func (m *MockGiteaPRTimelineReviewFetcher) ResetTimelineCache(org, repo string, idx int64) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "ResetTimelineCache", org, repo, idx)
}
// ResetTimelineCache indicates an expected call of ResetTimelineCache.
func (mr *MockGiteaPRTimelineReviewFetcherMockRecorder) ResetTimelineCache(org, repo, idx any) *MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetTimelineCache", reflect.TypeOf((*MockGiteaPRTimelineReviewFetcher)(nil).ResetTimelineCache), org, repo, idx)
return &MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall{Call: call}
}
// MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall wrap *gomock.Call
type MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall) Return() *MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall {
c.Call = c.Call.Return()
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall) Do(f func(string, string, int64)) *MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall) DoAndReturn(f func(string, string, int64)) *MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// MockGiteaCommitFetcher is a mock of GiteaCommitFetcher interface. // MockGiteaCommitFetcher is a mock of GiteaCommitFetcher interface.
type MockGiteaCommitFetcher struct { type MockGiteaCommitFetcher struct {
ctrl *gomock.Controller ctrl *gomock.Controller
@@ -1129,42 +994,6 @@ func (c *MockGiteaReviewTimelineFetcherGetTimelineCall) DoAndReturn(f func(strin
return c return c
} }
// ResetTimelineCache mocks base method.
func (m *MockGiteaReviewTimelineFetcher) ResetTimelineCache(org, repo string, idx int64) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "ResetTimelineCache", org, repo, idx)
}
// ResetTimelineCache indicates an expected call of ResetTimelineCache.
func (mr *MockGiteaReviewTimelineFetcherMockRecorder) ResetTimelineCache(org, repo, idx any) *MockGiteaReviewTimelineFetcherResetTimelineCacheCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetTimelineCache", reflect.TypeOf((*MockGiteaReviewTimelineFetcher)(nil).ResetTimelineCache), org, repo, idx)
return &MockGiteaReviewTimelineFetcherResetTimelineCacheCall{Call: call}
}
// MockGiteaReviewTimelineFetcherResetTimelineCacheCall wrap *gomock.Call
type MockGiteaReviewTimelineFetcherResetTimelineCacheCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaReviewTimelineFetcherResetTimelineCacheCall) Return() *MockGiteaReviewTimelineFetcherResetTimelineCacheCall {
c.Call = c.Call.Return()
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaReviewTimelineFetcherResetTimelineCacheCall) Do(f func(string, string, int64)) *MockGiteaReviewTimelineFetcherResetTimelineCacheCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaReviewTimelineFetcherResetTimelineCacheCall) DoAndReturn(f func(string, string, int64)) *MockGiteaReviewTimelineFetcherResetTimelineCacheCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// MockGiteaPRChecker is a mock of GiteaPRChecker interface. // MockGiteaPRChecker is a mock of GiteaPRChecker interface.
type MockGiteaPRChecker struct { type MockGiteaPRChecker struct {
ctrl *gomock.Controller ctrl *gomock.Controller
@@ -1386,42 +1215,6 @@ func (c *MockGiteaPRCheckerGetTimelineCall) DoAndReturn(f func(string, string, i
return c return c
} }
// ResetTimelineCache mocks base method.
func (m *MockGiteaPRChecker) ResetTimelineCache(org, repo string, idx int64) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "ResetTimelineCache", org, repo, idx)
}
// ResetTimelineCache indicates an expected call of ResetTimelineCache.
func (mr *MockGiteaPRCheckerMockRecorder) ResetTimelineCache(org, repo, idx any) *MockGiteaPRCheckerResetTimelineCacheCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetTimelineCache", reflect.TypeOf((*MockGiteaPRChecker)(nil).ResetTimelineCache), org, repo, idx)
return &MockGiteaPRCheckerResetTimelineCacheCall{Call: call}
}
// MockGiteaPRCheckerResetTimelineCacheCall wrap *gomock.Call
type MockGiteaPRCheckerResetTimelineCacheCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaPRCheckerResetTimelineCacheCall) Return() *MockGiteaPRCheckerResetTimelineCacheCall {
c.Call = c.Call.Return()
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaPRCheckerResetTimelineCacheCall) Do(f func(string, string, int64)) *MockGiteaPRCheckerResetTimelineCacheCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaPRCheckerResetTimelineCacheCall) DoAndReturn(f func(string, string, int64)) *MockGiteaPRCheckerResetTimelineCacheCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// MockGiteaReviewFetcherAndRequesterAndUnrequester is a mock of GiteaReviewFetcherAndRequesterAndUnrequester interface. // MockGiteaReviewFetcherAndRequesterAndUnrequester is a mock of GiteaReviewFetcherAndRequesterAndUnrequester interface.
type MockGiteaReviewFetcherAndRequesterAndUnrequester struct { type MockGiteaReviewFetcherAndRequesterAndUnrequester struct {
ctrl *gomock.Controller ctrl *gomock.Controller
@@ -1607,42 +1400,6 @@ func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterRequestReviewsCall) DoA
return c return c
} }
// ResetTimelineCache mocks base method.
func (m *MockGiteaReviewFetcherAndRequesterAndUnrequester) ResetTimelineCache(org, repo string, idx int64) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "ResetTimelineCache", org, repo, idx)
}
// ResetTimelineCache indicates an expected call of ResetTimelineCache.
func (mr *MockGiteaReviewFetcherAndRequesterAndUnrequesterMockRecorder) ResetTimelineCache(org, repo, idx any) *MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetTimelineCache", reflect.TypeOf((*MockGiteaReviewFetcherAndRequesterAndUnrequester)(nil).ResetTimelineCache), org, repo, idx)
return &MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall{Call: call}
}
// MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall wrap *gomock.Call
type MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall) Return() *MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall {
c.Call = c.Call.Return()
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall) Do(f func(string, string, int64)) *MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall) DoAndReturn(f func(string, string, int64)) *MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// UnrequestReview mocks base method. // UnrequestReview mocks base method.
func (m *MockGiteaReviewFetcherAndRequesterAndUnrequester) UnrequestReview(org, repo string, id int64, reviwers ...string) error { func (m *MockGiteaReviewFetcherAndRequesterAndUnrequester) UnrequestReview(org, repo string, id int64, reviwers ...string) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
@@ -1686,45 +1443,6 @@ func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterUnrequestReviewCall) Do
return c return c
} }
// UpdateIssue mocks base method.
func (m *MockGiteaReviewFetcherAndRequesterAndUnrequester) UpdateIssue(org, repo string, idx int64, options *models.EditIssueOption) (*models.Issue, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateIssue", org, repo, idx, options)
ret0, _ := ret[0].(*models.Issue)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UpdateIssue indicates an expected call of UpdateIssue.
func (mr *MockGiteaReviewFetcherAndRequesterAndUnrequesterMockRecorder) UpdateIssue(org, repo, idx, options any) *MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateIssue", reflect.TypeOf((*MockGiteaReviewFetcherAndRequesterAndUnrequester)(nil).UpdateIssue), org, repo, idx, options)
return &MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall{Call: call}
}
// MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall wrap *gomock.Call
type MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall) Return(arg0 *models.Issue, arg1 error) *MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall {
c.Call = c.Call.Return(arg0, arg1)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall) Do(f func(string, string, int64, *models.EditIssueOption) (*models.Issue, error)) *MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall) DoAndReturn(f func(string, string, int64, *models.EditIssueOption) (*models.Issue, error)) *MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// MockGiteaUnreviewTimelineFetcher is a mock of GiteaUnreviewTimelineFetcher interface. // MockGiteaUnreviewTimelineFetcher is a mock of GiteaUnreviewTimelineFetcher interface.
type MockGiteaUnreviewTimelineFetcher struct { type MockGiteaUnreviewTimelineFetcher struct {
ctrl *gomock.Controller ctrl *gomock.Controller
@@ -1788,42 +1506,6 @@ func (c *MockGiteaUnreviewTimelineFetcherGetTimelineCall) DoAndReturn(f func(str
return c return c
} }
// ResetTimelineCache mocks base method.
func (m *MockGiteaUnreviewTimelineFetcher) ResetTimelineCache(org, repo string, idx int64) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "ResetTimelineCache", org, repo, idx)
}
// ResetTimelineCache indicates an expected call of ResetTimelineCache.
func (mr *MockGiteaUnreviewTimelineFetcherMockRecorder) ResetTimelineCache(org, repo, idx any) *MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetTimelineCache", reflect.TypeOf((*MockGiteaUnreviewTimelineFetcher)(nil).ResetTimelineCache), org, repo, idx)
return &MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall{Call: call}
}
// MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall wrap *gomock.Call
type MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall) Return() *MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall {
c.Call = c.Call.Return()
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall) Do(f func(string, string, int64)) *MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall) DoAndReturn(f func(string, string, int64)) *MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// UnrequestReview mocks base method. // UnrequestReview mocks base method.
func (m *MockGiteaUnreviewTimelineFetcher) UnrequestReview(org, repo string, id int64, reviwers ...string) error { func (m *MockGiteaUnreviewTimelineFetcher) UnrequestReview(org, repo string, id int64, reviwers ...string) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
@@ -1867,45 +1549,6 @@ func (c *MockGiteaUnreviewTimelineFetcherUnrequestReviewCall) DoAndReturn(f func
return c return c
} }
// UpdateIssue mocks base method.
func (m *MockGiteaUnreviewTimelineFetcher) UpdateIssue(org, repo string, idx int64, options *models.EditIssueOption) (*models.Issue, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateIssue", org, repo, idx, options)
ret0, _ := ret[0].(*models.Issue)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UpdateIssue indicates an expected call of UpdateIssue.
func (mr *MockGiteaUnreviewTimelineFetcherMockRecorder) UpdateIssue(org, repo, idx, options any) *MockGiteaUnreviewTimelineFetcherUpdateIssueCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateIssue", reflect.TypeOf((*MockGiteaUnreviewTimelineFetcher)(nil).UpdateIssue), org, repo, idx, options)
return &MockGiteaUnreviewTimelineFetcherUpdateIssueCall{Call: call}
}
// MockGiteaUnreviewTimelineFetcherUpdateIssueCall wrap *gomock.Call
type MockGiteaUnreviewTimelineFetcherUpdateIssueCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaUnreviewTimelineFetcherUpdateIssueCall) Return(arg0 *models.Issue, arg1 error) *MockGiteaUnreviewTimelineFetcherUpdateIssueCall {
c.Call = c.Call.Return(arg0, arg1)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaUnreviewTimelineFetcherUpdateIssueCall) Do(f func(string, string, int64, *models.EditIssueOption) (*models.Issue, error)) *MockGiteaUnreviewTimelineFetcherUpdateIssueCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaUnreviewTimelineFetcherUpdateIssueCall) DoAndReturn(f func(string, string, int64, *models.EditIssueOption) (*models.Issue, error)) *MockGiteaUnreviewTimelineFetcherUpdateIssueCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// MockGiteaReviewRequester is a mock of GiteaReviewRequester interface. // MockGiteaReviewRequester is a mock of GiteaReviewRequester interface.
type MockGiteaReviewRequester struct { type MockGiteaReviewRequester struct {
ctrl *gomock.Controller ctrl *gomock.Controller
@@ -2041,45 +1684,6 @@ func (c *MockGiteaReviewUnrequesterUnrequestReviewCall) DoAndReturn(f func(strin
return c return c
} }
// UpdateIssue mocks base method.
func (m *MockGiteaReviewUnrequester) UpdateIssue(org, repo string, idx int64, options *models.EditIssueOption) (*models.Issue, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateIssue", org, repo, idx, options)
ret0, _ := ret[0].(*models.Issue)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UpdateIssue indicates an expected call of UpdateIssue.
func (mr *MockGiteaReviewUnrequesterMockRecorder) UpdateIssue(org, repo, idx, options any) *MockGiteaReviewUnrequesterUpdateIssueCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateIssue", reflect.TypeOf((*MockGiteaReviewUnrequester)(nil).UpdateIssue), org, repo, idx, options)
return &MockGiteaReviewUnrequesterUpdateIssueCall{Call: call}
}
// MockGiteaReviewUnrequesterUpdateIssueCall wrap *gomock.Call
type MockGiteaReviewUnrequesterUpdateIssueCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaReviewUnrequesterUpdateIssueCall) Return(arg0 *models.Issue, arg1 error) *MockGiteaReviewUnrequesterUpdateIssueCall {
c.Call = c.Call.Return(arg0, arg1)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaReviewUnrequesterUpdateIssueCall) Do(f func(string, string, int64, *models.EditIssueOption) (*models.Issue, error)) *MockGiteaReviewUnrequesterUpdateIssueCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaReviewUnrequesterUpdateIssueCall) DoAndReturn(f func(string, string, int64, *models.EditIssueOption) (*models.Issue, error)) *MockGiteaReviewUnrequesterUpdateIssueCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// MockGiteaReviewer is a mock of GiteaReviewer interface. // MockGiteaReviewer is a mock of GiteaReviewer interface.
type MockGiteaReviewer struct { type MockGiteaReviewer struct {
ctrl *gomock.Controller ctrl *gomock.Controller
@@ -2874,45 +2478,6 @@ func (c *MockGiteaGetDoneNotificationsCall) DoAndReturn(f func(string, int64) ([
return c return c
} }
// GetIssue mocks base method.
func (m *MockGitea) GetIssue(org, repo string, idx int64) (*models.Issue, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetIssue", org, repo, idx)
ret0, _ := ret[0].(*models.Issue)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetIssue indicates an expected call of GetIssue.
func (mr *MockGiteaMockRecorder) GetIssue(org, repo, idx any) *MockGiteaGetIssueCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssue", reflect.TypeOf((*MockGitea)(nil).GetIssue), org, repo, idx)
return &MockGiteaGetIssueCall{Call: call}
}
// MockGiteaGetIssueCall wrap *gomock.Call
type MockGiteaGetIssueCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaGetIssueCall) Return(arg0 *models.Issue, arg1 error) *MockGiteaGetIssueCall {
c.Call = c.Call.Return(arg0, arg1)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaGetIssueCall) Do(f func(string, string, int64) (*models.Issue, error)) *MockGiteaGetIssueCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaGetIssueCall) DoAndReturn(f func(string, string, int64) (*models.Issue, error)) *MockGiteaGetIssueCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// GetIssueComments mocks base method. // GetIssueComments mocks base method.
func (m *MockGitea) GetIssueComments(org, project string, issueNo int64) ([]*models.Comment, error) { func (m *MockGitea) GetIssueComments(org, project string, issueNo int64) ([]*models.Comment, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
@@ -3543,42 +3108,6 @@ func (c *MockGiteaRequestReviewsCall) DoAndReturn(f func(*models.PullRequest, ..
return c return c
} }
// ResetTimelineCache mocks base method.
func (m *MockGitea) ResetTimelineCache(org, repo string, idx int64) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "ResetTimelineCache", org, repo, idx)
}
// ResetTimelineCache indicates an expected call of ResetTimelineCache.
func (mr *MockGiteaMockRecorder) ResetTimelineCache(org, repo, idx any) *MockGiteaResetTimelineCacheCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetTimelineCache", reflect.TypeOf((*MockGitea)(nil).ResetTimelineCache), org, repo, idx)
return &MockGiteaResetTimelineCacheCall{Call: call}
}
// MockGiteaResetTimelineCacheCall wrap *gomock.Call
type MockGiteaResetTimelineCacheCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaResetTimelineCacheCall) Return() *MockGiteaResetTimelineCacheCall {
c.Call = c.Call.Return()
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaResetTimelineCacheCall) Do(f func(string, string, int64)) *MockGiteaResetTimelineCacheCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaResetTimelineCacheCall) DoAndReturn(f func(string, string, int64)) *MockGiteaResetTimelineCacheCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// SetCommitStatus mocks base method. // SetCommitStatus mocks base method.
func (m *MockGitea) SetCommitStatus(org, repo, hash string, status *models.CommitStatus) (*models.CommitStatus, error) { func (m *MockGitea) SetCommitStatus(org, repo, hash string, status *models.CommitStatus) (*models.CommitStatus, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
@@ -3777,45 +3306,6 @@ func (c *MockGiteaUnrequestReviewCall) DoAndReturn(f func(string, string, int64,
return c return c
} }
// UpdateIssue mocks base method.
func (m *MockGitea) UpdateIssue(org, repo string, idx int64, options *models.EditIssueOption) (*models.Issue, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateIssue", org, repo, idx, options)
ret0, _ := ret[0].(*models.Issue)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UpdateIssue indicates an expected call of UpdateIssue.
func (mr *MockGiteaMockRecorder) UpdateIssue(org, repo, idx, options any) *MockGiteaUpdateIssueCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateIssue", reflect.TypeOf((*MockGitea)(nil).UpdateIssue), org, repo, idx, options)
return &MockGiteaUpdateIssueCall{Call: call}
}
// MockGiteaUpdateIssueCall wrap *gomock.Call
type MockGiteaUpdateIssueCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaUpdateIssueCall) Return(arg0 *models.Issue, arg1 error) *MockGiteaUpdateIssueCall {
c.Call = c.Call.Return(arg0, arg1)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaUpdateIssueCall) Do(f func(string, string, int64, *models.EditIssueOption) (*models.Issue, error)) *MockGiteaUpdateIssueCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaUpdateIssueCall) DoAndReturn(f func(string, string, int64, *models.EditIssueOption) (*models.Issue, error)) *MockGiteaUpdateIssueCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// UpdatePullRequest mocks base method. // UpdatePullRequest mocks base method.
func (m *MockGitea) UpdatePullRequest(org, repo string, num int64, options *models.EditPullRequestOption) (*models.PullRequest, error) { func (m *MockGitea) UpdatePullRequest(org, repo string, num int64, options *models.EditPullRequestOption) (*models.PullRequest, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()

View File

@@ -6,9 +6,7 @@ import (
"fmt" "fmt"
"os" "os"
"path" "path"
"regexp"
"slices" "slices"
"strconv"
"strings" "strings"
"src.opensuse.org/autogits/common/gitea-generated/client/repository" "src.opensuse.org/autogits/common/gitea-generated/client/repository"
@@ -162,8 +160,6 @@ func FetchPRSet(user string, gitea GiteaPRTimelineReviewFetcher, org, repo strin
var pr *models.PullRequest var pr *models.PullRequest
var err error var err error
gitea.ResetTimelineCache(org, repo, num)
prjGitOrg, prjGitRepo, _ := config.GetPrjGit() prjGitOrg, prjGitRepo, _ := config.GetPrjGit()
if prjGitOrg == org && prjGitRepo == repo { if prjGitOrg == org && prjGitRepo == repo {
if pr, err = gitea.GetPullRequest(org, repo, num); err != nil { if pr, err = gitea.GetPullRequest(org, repo, num); err != nil {
@@ -188,7 +184,6 @@ func FetchPRSet(user string, gitea GiteaPRTimelineReviewFetcher, org, repo strin
for _, pr := range prs { for _, pr := range prs {
org, repo, idx := pr.PRComponents() org, repo, idx := pr.PRComponents()
gitea.ResetTimelineCache(org, repo, idx)
reviews, err := FetchGiteaReviews(gitea, org, repo, idx) reviews, err := FetchGiteaReviews(gitea, org, repo, idx)
if err != nil { if err != nil {
LogError("Error fetching reviews for", PRtoString(pr.PR), ":", err) LogError("Error fetching reviews for", PRtoString(pr.PR), ":", err)
@@ -321,10 +316,7 @@ func (rs *PRSet) FindMissingAndExtraReviewers(maintainers MaintainershipData, id
// only need project maintainer reviews if: // only need project maintainer reviews if:
// * not created by a bot and has other PRs, or // * not created by a bot and has other PRs, or
// * not created by maintainer // * not created by maintainer
noReviewPRCreators := []string{} noReviewPRCreators := prjMaintainers
if !rs.Config.ReviewRequired {
noReviewPRCreators = prjMaintainers
}
if len(rs.PRs) > 1 { if len(rs.PRs) > 1 {
noReviewPRCreators = append(noReviewPRCreators, rs.BotUser) noReviewPRCreators = append(noReviewPRCreators, rs.BotUser)
} }
@@ -347,10 +339,7 @@ func (rs *PRSet) FindMissingAndExtraReviewers(maintainers MaintainershipData, id
pkg := pr.PR.Base.Repo.Name pkg := pr.PR.Base.Repo.Name
pkgMaintainers := maintainers.ListPackageMaintainers(pkg, nil) pkgMaintainers := maintainers.ListPackageMaintainers(pkg, nil)
Maintainers := slices.Concat(prjMaintainers, pkgMaintainers) Maintainers := slices.Concat(prjMaintainers, pkgMaintainers)
noReviewPkgPRCreators := []string{} noReviewPkgPRCreators := pkgMaintainers
if !rs.Config.ReviewRequired {
noReviewPkgPRCreators = pkgMaintainers
}
LogDebug("packakge maintainers:", Maintainers) LogDebug("packakge maintainers:", Maintainers)
@@ -661,8 +650,6 @@ func (rs *PRSet) Merge(gitea GiteaReviewUnrequester, git Git) error {
} }
// FF all non-prj git and unrequest reviews. // FF all non-prj git and unrequest reviews.
newRepoIssues := make(map[int64]string) // issue index -> org/repo
for _, prinfo := range rs.PRs { for _, prinfo := range rs.PRs {
// remove pending review requests // remove pending review requests
repo := prinfo.PR.Base.Repo repo := prinfo.PR.Base.Repo
@@ -684,15 +671,6 @@ func (rs *PRSet) Merge(gitea GiteaReviewUnrequester, git Git) error {
if rs.IsPrjGitPR(prinfo.PR) { if rs.IsPrjGitPR(prinfo.PR) {
continue continue
} }
isNewRepo := false
for _, l := range prinfo.PR.Labels {
if l.Name == Label_NewRepository {
isNewRepo = true
break
}
}
br := rs.Config.Branch br := rs.Config.Branch
if len(br) == 0 { if len(br) == 0 {
// if branch is unspecified, take it from the PR as it // if branch is unspecified, take it from the PR as it
@@ -701,30 +679,11 @@ func (rs *PRSet) Merge(gitea GiteaReviewUnrequester, git Git) error {
} else if br != prinfo.PR.Base.Name { } else if br != prinfo.PR.Base.Name {
panic(prinfo.PR.Base.Name + " is expected to match " + br) panic(prinfo.PR.Base.Name + " is expected to match " + br)
} }
if isNewRepo {
// Extract issue reference from body: "See issue #XYZ"
rx := regexp.MustCompile(`See issue #(\d+)`)
if matches := rx.FindStringSubmatch(prinfo.PR.Body); len(matches) > 1 {
if issueIdx, err := strconv.ParseInt(matches[1], 10, 64); err == nil {
// We need to know which project git this issue belongs to.
// Since the PR set is linked to a ProjectGit, we can use its org/repo.
prjGitOrg, prjGitRepo, _ := rs.Config.GetPrjGit()
newRepoIssues[issueIdx] = prjGitOrg + "/" + prjGitRepo
}
}
}
prinfo.RemoteName, err = git.GitClone(repo.Name, br, repo.SSHURL) prinfo.RemoteName, err = git.GitClone(repo.Name, br, repo.SSHURL)
PanicOnError(err) PanicOnError(err)
git.GitExecOrPanic(repo.Name, "fetch", prinfo.RemoteName, head.Sha) git.GitExecOrPanic(repo.Name, "fetch", prinfo.RemoteName, head.Sha)
git.GitExecOrPanic(repo.Name, "merge", "--ff", head.Sha)
if isNewRepo {
LogInfo("Force-pushing new repository branch", br, "to", head.Sha)
// we don't merge, we just set the branch to this commit
} else {
git.GitExecOrPanic(repo.Name, "merge", "--ff", head.Sha)
}
} }
// push changes // push changes
@@ -739,37 +698,12 @@ func (rs *PRSet) Merge(gitea GiteaReviewUnrequester, git Git) error {
} }
repo := prinfo.PR.Base.Repo repo := prinfo.PR.Base.Repo
isNewRepo := false
for _, l := range prinfo.PR.Labels {
if l.Name == Label_NewRepository {
isNewRepo = true
break
}
}
if !IsDryRun { if !IsDryRun {
if isNewRepo { git.GitExecOrPanic(repo.Name, "push", prinfo.RemoteName)
git.GitExecOrPanic(repo.Name, "push", "-f", prinfo.RemoteName, prinfo.PR.Head.Sha+":"+prinfo.PR.Base.Name)
} else {
git.GitExecOrPanic(repo.Name, "push", prinfo.RemoteName)
}
} else { } else {
LogInfo("*** WOULD push", repo.Name, "to", prinfo.RemoteName) LogInfo("*** WOULD push", repo.Name, "to", prinfo.RemoteName)
} }
} }
// Close referencing issues
if !IsDryRun {
for issueIdx, prjPath := range newRepoIssues {
parts := strings.Split(prjPath, "/")
if len(parts) == 2 {
LogInfo("Closing issue", prjPath+"#"+strconv.FormatInt(issueIdx, 10))
gitea.UpdateIssue(parts[0], parts[1], issueIdx, &models.EditIssueOption{
State: "closed",
})
}
}
}
return nil return nil
} }

View File

@@ -1,123 +0,0 @@
package common_test
import (
"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 TestFetchPRSet_Linkage(t *testing.T) {
config := &common.AutogitConfig{
Organization: "target-org",
GitProjectName: "test-org/prjgit#main",
}
// 1. Mock a package PR
pkgPR := &models.PullRequest{
Index: 101,
State: "open",
Base: &models.PRBranchInfo{
Ref: "main",
Repo: &models.Repository{
Name: "pkg1",
Owner: &models.User{UserName: "target-org"},
},
},
Head: &models.PRBranchInfo{Sha: "pkg-sha"},
}
// 2. Mock a ProjectGit PR that references the package PR
prjGitPR := &models.PullRequest{
Index: 500,
State: "open",
Base: &models.PRBranchInfo{
Ref: "main",
Name: "main",
Repo: &models.Repository{
Name: "prjgit",
Owner: &models.User{UserName: "test-org"},
},
},
Body: "Forwarded PRs: pkg1\n\nPR: target-org/pkg1!101",
}
t.Run("Fetch from ProjectGit PR", func(t *testing.T) {
ctl := gomock.NewController(t)
defer ctl.Finish()
mockGitea := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl)
mockGitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
// Expect fetch of prjGitPR
mockGitea.EXPECT().GetPullRequest("test-org", "prjgit", int64(500)).Return(prjGitPR, nil)
// Expect fetch of pkgPR because it's linked in body
mockGitea.EXPECT().GetPullRequest("target-org", "pkg1", int64(101)).Return(pkgPR, nil)
// Expect review fetching (part of FetchPRSet)
mockGitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
mockGitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
prset, err := common.FetchPRSet("bot", mockGitea, "test-org", "prjgit", 500, config)
if err != nil {
t.Fatalf("FetchPRSet failed: %v", err)
}
if len(prset.PRs) != 2 {
t.Errorf("Expected 2 PRs in set, got %d", len(prset.PRs))
}
if !prset.IsConsistent() {
t.Error("PR set should be consistent")
}
})
t.Run("Fetch from Package PR via Timeline", func(t *testing.T) {
ctl := gomock.NewController(t)
defer ctl.Finish()
mockGitea := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl)
mockGitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
// 1. FetchPRSet for pkgPR will call LastPrjGitRefOnTimeline
mockGitea.EXPECT().GetTimeline("target-org", "pkg1", int64(101)).Return([]*models.TimelineComment{
{
Type: common.TimelineCommentType_PullRequestRef,
RefIssue: &models.Issue{
Index: 500,
Body: "PR: target-org/pkg1!101",
Repository: &models.RepositoryMeta{
Owner: "test-org",
Name: "prjgit",
},
User: &models.User{UserName: "bot"},
},
},
}, nil)
// 2. It will then fetch the prjGitPR found in timeline (twice in LastPrjGitRefOnTimeline)
mockGitea.EXPECT().GetPullRequest("test-org", "prjgit", int64(500)).Return(prjGitPR, nil).Times(2)
// 3. Then it will recursively fetch linked PRs from prjGitPR body in readPRData
mockGitea.EXPECT().GetPullRequest("target-org", "pkg1", int64(101)).Return(pkgPR, nil)
// Review fetching for all PRs in the set
mockGitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
mockGitea.EXPECT().GetTimeline("test-org", "prjgit", int64(500)).Return([]*models.TimelineComment{}, nil).AnyTimes()
mockGitea.EXPECT().GetTimeline("target-org", "pkg1", int64(101)).Return([]*models.TimelineComment{}, nil).AnyTimes()
prset, err := common.FetchPRSet("bot", mockGitea, "target-org", "pkg1", 101, config)
if err != nil {
t.Fatalf("FetchPRSet failed: %v", err)
}
if len(prset.PRs) != 2 {
t.Errorf("Expected 2 PRs in set, got %d", len(prset.PRs))
}
prjPRInfo, err := prset.GetPrjGitPR()
if err != nil || prjPRInfo.PR.Index != 500 {
t.Errorf("Expected ProjectGit PR 500 to be found, got %v", prjPRInfo)
}
})
}

View File

@@ -1,94 +0,0 @@
package common_test
import (
"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 TestPRSet_Merge_Special(t *testing.T) {
ctl := gomock.NewController(t)
defer ctl.Finish()
mockGitea := mock_common.NewMockGiteaReviewUnrequester(ctl)
mockGit := mock_common.NewMockGit(ctl)
config := &common.AutogitConfig{
Organization: "target-org",
GitProjectName: "test-org/prjgit#main",
Branch: "main",
}
// 1. Regular ProjectGit PR
prjGitPR := &models.PullRequest{
Index: 500,
Base: &models.PRBranchInfo{
Ref: "main",
Name: "main",
Repo: &models.Repository{Name: "prjgit", Owner: &models.User{UserName: "test-org"}, SSHURL: "prj-ssh-url"},
Sha: "base-sha",
},
Head: &models.PRBranchInfo{Sha: "prj-head-sha"},
}
// 2. "new/New Repository" Package PR
newPkgPR := &models.PullRequest{
Index: 101,
Base: &models.PRBranchInfo{
Ref: "main",
Name: "main",
Repo: &models.Repository{Name: "new-pkg", Owner: &models.User{UserName: "target-org"}, SSHURL: "pkg-ssh-url"},
},
Head: &models.PRBranchInfo{Sha: "pkg-head-sha"},
Labels: []*models.Label{
{Name: "new/New Repository"},
},
Body: "See issue #123",
}
prset := &common.PRSet{
Config: config,
PRs: []*common.PRInfo{
{PR: prjGitPR},
{PR: newPkgPR},
},
}
common.IsDryRun = false
// Mock expectations for Merge
// Clone and fetch for PrjGit
mockGit.EXPECT().GitClone("_ObsPrj", "main", "prj-ssh-url").Return("origin", nil)
mockGit.EXPECT().GitExecOrPanic("_ObsPrj", "fetch", "origin", "prj-head-sha")
// mockGit.EXPECT().GitExecWithOutputOrPanic("_ObsPrj", "merge-base", "HEAD", "base-sha", "prj-head-sha").Return("base-sha")
mockGit.EXPECT().GitExec("_ObsPrj", "merge", "--no-ff", "-m", gomock.Any(), "prj-head-sha").Return(nil)
// Unrequest reviews
mockGitea.EXPECT().UnrequestReview("test-org", "prjgit", int64(500), gomock.Any()).Return(nil)
mockGitea.EXPECT().UnrequestReview("target-org", "new-pkg", int64(101), gomock.Any()).Return(nil)
// Clone and fetch for new-pkg
mockGit.EXPECT().GitClone("new-pkg", "main", "pkg-ssh-url").Return("origin", nil)
mockGit.EXPECT().GitExecOrPanic("new-pkg", "fetch", "origin", "pkg-head-sha")
// Pushing changes
mockGit.EXPECT().GitExecOrPanic("_ObsPrj", "push", "origin")
// Special push for new repo: git push -f origin pkg-head-sha:main
mockGit.EXPECT().GitExecOrPanic("new-pkg", "push", "-f", "origin", "pkg-head-sha:main")
// Closing issue
mockGitea.EXPECT().UpdateIssue("test-org", "prjgit", int64(123), gomock.Any()).DoAndReturn(func(org, repo string, idx int64, opt *models.EditIssueOption) (*models.Issue, error) {
if opt.State != "closed" {
t.Errorf("Expected issue state to be closed, got %s", opt.State)
}
return nil, nil
})
err := prset.Merge(mockGitea, mockGit)
if err != nil {
t.Fatalf("Merge failed: %v", err)
}
}

View File

@@ -48,6 +48,8 @@ func reviewsToTimeline(reviews []*models.PullReview) []*models.TimelineComment {
} }
func TestPR(t *testing.T) { func TestPR(t *testing.T) {
return
baseConfig := common.AutogitConfig{ baseConfig := common.AutogitConfig{
Reviewers: []string{"+super1", "*super2", "m1", "-m2"}, Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
Branch: "branch", Branch: "branch",
@@ -78,21 +80,21 @@ func TestPR(t *testing.T) {
{ {
name: "Error fetching PullRequest", name: "Error fetching PullRequest",
data: []prdata{ data: []prdata{
{pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "open"}, pr_err: errors.New("Missing PR")}, {pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "opened"}, pr_err: errors.New("Missing PR")},
}, },
prjGitPRIndex: -1, prjGitPRIndex: -1,
}, },
{ {
name: "Error fetching PullRequest in PrjGit", name: "Error fetching PullRequest in PrjGit",
data: []prdata{ data: []prdata{
{pr: &models.PullRequest{Body: "PR: foo/barPrj#22", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "open"}, pr_err: errors.New("missing PR")}, {pr: &models.PullRequest{Body: "PR: foo/barPrj#22", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "opened"}, pr_err: errors.New("missing PR")},
{pr: &models.PullRequest{Body: "", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, State: "open"}}, {pr: &models.PullRequest{Body: "", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, State: "opened"}},
}, },
}, },
{ {
name: "Error fetching prjgit", name: "Error fetching prjgit",
data: []prdata{ data: []prdata{
{pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "open"}}, {pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "opened"}},
}, },
resLen: 1, resLen: 1,
prjGitPRIndex: -1, prjGitPRIndex: -1,
@@ -100,20 +102,8 @@ func TestPR(t *testing.T) {
{ {
name: "Review set is consistent", name: "Review set is consistent",
data: []prdata{ data: []prdata{
{ {pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "opened"}},
pr: &models.PullRequest{Body: "PR: foo/barPrj#22", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "open"}, {pr: &models.PullRequest{Body: "PR: test/repo#42", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, State: "opened"}},
timeline: []*models.TimelineComment{
{
Type: common.TimelineCommentType_PullRequestRef,
RefIssue: &models.Issue{
Index: 22,
Repository: &models.RepositoryMeta{Name: "barPrj", Owner: "foo"},
User: &models.User{UserName: "test"},
Body: "PR: test/repo#42",
},
},
}},
{pr: &models.PullRequest{Body: "PR: test/repo#42", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, State: "open"}},
}, },
resLen: 2, resLen: 2,
prjGitPRIndex: 1, prjGitPRIndex: 1,
@@ -123,21 +113,8 @@ func TestPR(t *testing.T) {
{ {
name: "Review set is consistent: 1pkg", name: "Review set is consistent: 1pkg",
data: []prdata{ data: []prdata{
{ {pr: &models.PullRequest{Body: "PR: foo/barPrj#22", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "opened"}},
pr: &models.PullRequest{Body: "PR: foo/barPrj#22", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "open"}, {pr: &models.PullRequest{Body: "PR: test/repo#42", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, State: "opened"}},
timeline: []*models.TimelineComment{
{
Type: common.TimelineCommentType_PullRequestRef,
RefIssue: &models.Issue{
Index: 22,
Repository: &models.RepositoryMeta{Name: "barPrj", Owner: "foo"},
User: &models.User{UserName: "test"},
Body: "PR: test/repo#42",
},
},
},
},
{pr: &models.PullRequest{Body: "PR: test/repo#42", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, State: "open"}},
}, },
resLen: 2, resLen: 2,
prjGitPRIndex: 1, prjGitPRIndex: 1,
@@ -146,22 +123,9 @@ func TestPR(t *testing.T) {
{ {
name: "Review set is consistent: 2pkg", name: "Review set is consistent: 2pkg",
data: []prdata{ data: []prdata{
{ {pr: &models.PullRequest{Body: "some desc", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "opened"}},
pr: &models.PullRequest{Body: "some desc\nPR: foo/barPrj#22", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "open"}, {pr: &models.PullRequest{Body: "PR: test/repo#42\nPR: test/repo2#41", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, State: "opened"}},
timeline: []*models.TimelineComment{ {pr: &models.PullRequest{Body: "some other desc\nPR: foo/fer#33", Index: 41, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo2", Owner: &models.User{UserName: "test"}}}, State: "opened"}},
{
Type: common.TimelineCommentType_PullRequestRef,
RefIssue: &models.Issue{
Index: 22,
Repository: &models.RepositoryMeta{Name: "barPrj", Owner: "foo"},
User: &models.User{UserName: "test"},
Body: "PR: test/repo#42\nPR: test/repo2#41",
},
},
},
},
{pr: &models.PullRequest{Body: "PR: test/repo#42\nPR: test/repo2#41", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, State: "open"}},
{pr: &models.PullRequest{Body: "some other desc\nPR: foo/barPrj#22", Index: 41, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo2", Owner: &models.User{UserName: "test"}}}, State: "open"}},
}, },
resLen: 3, resLen: 3,
prjGitPRIndex: 1, prjGitPRIndex: 1,
@@ -171,7 +135,7 @@ func TestPR(t *testing.T) {
name: "Review set of prjgit PR is consistent", name: "Review set of prjgit PR is consistent",
data: []prdata{ data: []prdata{
{ {
pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -190,23 +154,10 @@ func TestPR(t *testing.T) {
{ {
name: "Review set is consistent: 2pkg", name: "Review set is consistent: 2pkg",
data: []prdata{ data: []prdata{
{ {pr: &models.PullRequest{Body: "PR: foo/barPrj#222", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "opened"}},
pr: &models.PullRequest{Body: "PR: foo/barPrj#222", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "open"}, {pr: &models.PullRequest{Body: "PR: test/repo2#41", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, State: "opened"}},
timeline: []*models.TimelineComment{ {pr: &models.PullRequest{Body: "PR: test/repo#42\nPR: test/repo2#41", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, State: "opened"}},
{ {pr: &models.PullRequest{Body: "PR: foo/barPrj#20", Index: 41, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo2", Owner: &models.User{UserName: "test"}}}, State: "opened"}},
Type: common.TimelineCommentType_PullRequestRef,
RefIssue: &models.Issue{
Index: 22,
Repository: &models.RepositoryMeta{Name: "barPrj", Owner: "foo"},
User: &models.User{UserName: "test"},
Body: "PR: test/repo#42\nPR: test/repo2#41",
},
},
},
},
{pr: &models.PullRequest{Body: "PR: test/repo2#41", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, State: "open"}},
{pr: &models.PullRequest{Body: "PR: test/repo#42\nPR: test/repo2#41", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, State: "open"}},
{pr: &models.PullRequest{Body: "PR: foo/barPrj#20", Index: 41, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo2", Owner: &models.User{UserName: "test"}}}, State: "open"}},
}, },
resLen: 3, resLen: 3,
prjGitPRIndex: 2, prjGitPRIndex: 2,
@@ -216,7 +167,7 @@ func TestPR(t *testing.T) {
name: "WIP PR is not approved", name: "WIP PR is not approved",
data: []prdata{ data: []prdata{
{ {
pr: &models.PullRequest{Body: "", Title: "WIP: some title", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "", Title: "WIP: some title", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -233,9 +184,10 @@ func TestPR(t *testing.T) {
}, },
}, },
{ {
name: "Manual review is missing", data: []prdata{ name: "Manual review is missing",
data: []prdata{
{ {
pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -243,7 +195,7 @@ func TestPR(t *testing.T) {
}, },
}, },
{ {
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -260,15 +212,16 @@ func TestPR(t *testing.T) {
Reviewers: []string{"+super1", "*super2", "m1", "-m2"}, Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
Branch: "branch", Branch: "branch",
Organization: "foo", Organization: "foo",
GitProjectName: "barPrj#master", GitProjectName: "barPrj",
ManualMergeOnly: true, ManualMergeOnly: true,
}) })
}}, },
},
{ {
name: "Manual review is done, via PrjGit", name: "Manual review is done, via PrjGit",
data: []prdata{ data: []prdata{
{ {
pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "merge ok", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved}, {Body: "merge ok", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -276,7 +229,7 @@ func TestPR(t *testing.T) {
}, },
}, },
{ {
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -293,7 +246,7 @@ func TestPR(t *testing.T) {
Reviewers: []string{"+super1", "*super2", "m1", "-m2"}, Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
Branch: "branch", Branch: "branch",
Organization: "foo", Organization: "foo",
GitProjectName: "barPrj#master", GitProjectName: "barPrj",
ManualMergeOnly: true, ManualMergeOnly: true,
}) })
}, },
@@ -302,7 +255,7 @@ func TestPR(t *testing.T) {
name: "Manual review is done, via PrjGit", name: "Manual review is done, via PrjGit",
data: []prdata{ data: []prdata{
{ {
pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "merge ok", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved}, {Body: "merge ok", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -310,7 +263,7 @@ func TestPR(t *testing.T) {
}, },
}, },
{ {
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -327,7 +280,7 @@ func TestPR(t *testing.T) {
Reviewers: []string{"+super1", "*super2", "m1", "-m2"}, Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
Branch: "branch", Branch: "branch",
Organization: "foo", Organization: "foo",
GitProjectName: "barPrj#master", GitProjectName: "barPrj",
ManualMergeOnly: true, ManualMergeOnly: true,
ManualMergeProject: true, ManualMergeProject: true,
}) })
@@ -337,7 +290,7 @@ func TestPR(t *testing.T) {
name: "Manual review is not done, via PrjGit", name: "Manual review is not done, via PrjGit",
data: []prdata{ data: []prdata{
{ {
pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "merge ok", User: &models.User{UserName: "notm2"}, State: common.ReviewStateApproved}, {Body: "merge ok", User: &models.User{UserName: "notm2"}, State: common.ReviewStateApproved},
{Body: "merge not ok", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved}, {Body: "merge not ok", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
@@ -346,7 +299,7 @@ func TestPR(t *testing.T) {
}, },
}, },
{ {
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -363,7 +316,7 @@ func TestPR(t *testing.T) {
Reviewers: []string{"+super1", "*super2", "m1", "-m2"}, Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
Branch: "branch", Branch: "branch",
Organization: "foo", Organization: "foo",
GitProjectName: "barPrj#master", GitProjectName: "barPrj",
ManualMergeOnly: true, ManualMergeOnly: true,
ManualMergeProject: true, ManualMergeProject: true,
}) })
@@ -373,7 +326,7 @@ func TestPR(t *testing.T) {
name: "Manual review is done via PackageGit", name: "Manual review is done via PackageGit",
data: []prdata{ data: []prdata{
{ {
pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -381,7 +334,7 @@ func TestPR(t *testing.T) {
}, },
}, },
{ {
pr: &models.PullRequest{Body: "PR: foo/repo#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "Merge ok", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "Merge ok", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -398,7 +351,7 @@ func TestPR(t *testing.T) {
Reviewers: []string{"+super1", "*super2", "m1", "-m2"}, Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
Branch: "branch", Branch: "branch",
Organization: "foo", Organization: "foo",
GitProjectName: "barPrj#master", GitProjectName: "barPrj",
ManualMergeOnly: true, ManualMergeOnly: true,
}) })
}, },
@@ -407,7 +360,7 @@ func TestPR(t *testing.T) {
name: "Manual review done via PkgGits", name: "Manual review done via PkgGits",
data: []prdata{ data: []prdata{
{ {
pr: &models.PullRequest{Body: "PR: foo/repo#20\nPR: foo/repo#21", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/repo#20\nPR: foo/repo#21", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -415,7 +368,7 @@ func TestPR(t *testing.T) {
}, },
}, },
{ {
pr: &models.PullRequest{Body: "PR: foo/repo#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "Merge OK!", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "Merge OK!", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -423,7 +376,7 @@ func TestPR(t *testing.T) {
}, },
}, },
{ {
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 21, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 21, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "merge ok", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "merge ok", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -440,7 +393,7 @@ func TestPR(t *testing.T) {
Reviewers: []string{"+super1", "*super2", "m1", "-m2"}, Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
Branch: "branch", Branch: "branch",
Organization: "foo", Organization: "foo",
GitProjectName: "barPrj#master", GitProjectName: "barPrj",
ManualMergeOnly: true, ManualMergeOnly: true,
}) })
}, },
@@ -449,7 +402,7 @@ func TestPR(t *testing.T) {
name: "Manual review done via PkgGits not allowed", name: "Manual review done via PkgGits not allowed",
data: []prdata{ data: []prdata{
{ {
pr: &models.PullRequest{Body: "PR: foo/repo#20\nPR: foo/repo#21", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/repo#20\nPR: foo/repo#21", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -457,7 +410,7 @@ func TestPR(t *testing.T) {
}, },
}, },
{ {
pr: &models.PullRequest{Body: "PR: foo/repo#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "Merge OK!", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "Merge OK!", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -465,7 +418,7 @@ func TestPR(t *testing.T) {
}, },
}, },
{ {
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 21, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 21, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "merge ok", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "merge ok", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -482,7 +435,7 @@ func TestPR(t *testing.T) {
Reviewers: []string{"+super1", "*super2", "m1", "-m2"}, Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
Branch: "branch", Branch: "branch",
Organization: "foo", Organization: "foo",
GitProjectName: "barPrj#master", GitProjectName: "barPrj",
ManualMergeOnly: true, ManualMergeOnly: true,
ManualMergeProject: true, ManualMergeProject: true,
}) })
@@ -492,7 +445,7 @@ func TestPR(t *testing.T) {
name: "Manual review is is missing on one PR", name: "Manual review is is missing on one PR",
data: []prdata{ data: []prdata{
{ {
pr: &models.PullRequest{Body: "PR: foo/repo#20\nPR: foo/repo#21", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/repo#20\nPR: foo/repo#21", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -500,7 +453,7 @@ func TestPR(t *testing.T) {
}, },
}, },
{ {
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -508,7 +461,7 @@ func TestPR(t *testing.T) {
}, },
}, },
{ {
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 21, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 21, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -525,7 +478,7 @@ func TestPR(t *testing.T) {
Reviewers: []string{"+super1", "*super2", "m1", "-m2"}, Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
Branch: "branch", Branch: "branch",
Organization: "foo", Organization: "foo",
GitProjectName: "barPrj#master", GitProjectName: "barPrj",
ManualMergeOnly: true, ManualMergeOnly: true,
}) })
}, },
@@ -534,7 +487,7 @@ func TestPR(t *testing.T) {
name: "PR is approved with negative optional review", name: "PR is approved with negative optional review",
data: []prdata{ data: []prdata{
{ {
pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"}, pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
reviews: []*models.PullReview{ reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -552,7 +505,7 @@ func TestPR(t *testing.T) {
Reviewers: []string{"+super1", "*super2", "m1", "-m2", "~*bot"}, Reviewers: []string{"+super1", "*super2", "m1", "-m2", "~*bot"},
Branch: "branch", Branch: "branch",
Organization: "foo", Organization: "foo",
GitProjectName: "barPrj#master", GitProjectName: "barPrj",
} }
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &config) return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &config)
}, },
@@ -563,35 +516,30 @@ func TestPR(t *testing.T) {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
ctl := gomock.NewController(t) ctl := gomock.NewController(t)
pr_mock := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl) pr_mock := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl)
pr_mock.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
review_mock := mock_common.NewMockGiteaPRChecker(ctl) review_mock := mock_common.NewMockGiteaPRChecker(ctl)
// reviewer_mock := mock_common.NewMockGiteaReviewRequester(ctl) // reviewer_mock := mock_common.NewMockGiteaReviewRequester(ctl)
prjGitOrg, prjGitRepo, _ := baseConfig.GetPrjGit()
if test.reviewSetFetcher == nil { // if we are fetching the prjgit directly, the these mocks are not called if test.reviewSetFetcher == nil { // if we are fetching the prjgit directly, the these mocks are not called
if test.prjGitPRIndex >= 0 { if test.prjGitPRIndex >= 0 {
pr_mock.EXPECT().GetPullRequest(prjGitOrg, prjGitRepo, int64(test.data[test.prjGitPRIndex].pr.Index)). pr_mock.EXPECT().GetPullRequest(baseConfig.Organization, baseConfig.GitProjectName, test.prjGitPRIndex).
Return(test.data[test.prjGitPRIndex].pr, test.data[test.prjGitPRIndex].pr_err).AnyTimes() Return(test.data[test.prjGitPRIndex].pr, test.data[test.prjGitPRIndex].pr_err)
} else if test.prjGitPRIndex < 0 { } else if test.prjGitPRIndex < 0 {
// no prjgit PR // no prjgit PR
pr_mock.EXPECT().GetPullRequest(prjGitOrg, prjGitRepo, gomock.Any()). pr_mock.EXPECT().GetPullRequest(baseConfig.Organization, baseConfig.GitProjectName, gomock.Any()).
Return(nil, nil).AnyTimes() Return(nil, nil)
} }
} }
var test_err error
for _, data := range test.data { for _, data := range test.data {
pr_mock.EXPECT().GetPullRequest(data.pr.Base.Repo.Owner.UserName, data.pr.Base.Repo.Name, data.pr.Index).Return(data.pr, data.pr_err).AnyTimes() pr_mock.EXPECT().GetPullRequest(data.pr.Base.Repo.Owner.UserName, data.pr.Base.Repo.Name, data.pr.Index).Return(data.pr, data.pr_err).AnyTimes()
if data.pr_err != nil { if data.pr_err != nil {
// test_err is not used and was causing a build error. test_err = data.pr_err
// data.pr_err is directly used in the previous EXPECT call.
} }
review_mock.EXPECT().GetPullRequestReviews(data.pr.Base.Repo.Owner.UserName, data.pr.Base.Repo.Name, data.pr.Index).Return(data.reviews, data.review_error).AnyTimes()
if data.timeline == nil { if data.timeline == nil {
data.timeline = reviewsToTimeline(data.reviews) data.timeline = reviewsToTimeline(data.reviews)
} }
pr_mock.EXPECT().GetTimeline(data.pr.Base.Repo.Owner.UserName, data.pr.Base.Repo.Name, data.pr.Index).Return(data.timeline, nil).AnyTimes()
pr_mock.EXPECT().GetPullRequestReviews(data.pr.Base.Repo.Owner.UserName, data.pr.Base.Repo.Name, data.pr.Index).Return(data.reviews, data.review_error).AnyTimes()
review_mock.EXPECT().GetPullRequestReviews(data.pr.Base.Repo.Owner.UserName, data.pr.Base.Repo.Name, data.pr.Index).Return(data.reviews, data.review_error).AnyTimes()
review_mock.EXPECT().GetTimeline(data.pr.Base.Repo.Owner.UserName, data.pr.Base.Repo.Name, data.pr.Index).Return(data.timeline, nil).AnyTimes() review_mock.EXPECT().GetTimeline(data.pr.Base.Repo.Owner.UserName, data.pr.Base.Repo.Name, data.pr.Index).Return(data.timeline, nil).AnyTimes()
} }
@@ -604,12 +552,27 @@ func TestPR(t *testing.T) {
res, err = common.FetchPRSet("test", pr_mock, "test", "repo", 42, &baseConfig) res, err = common.FetchPRSet("test", pr_mock, "test", "repo", 42, &baseConfig)
} }
if res == nil { if err == nil {
if test_err != nil {
t.Fatal("Expected", test_err, "but got", err)
}
} else {
if res != nil {
t.Fatal("error but got ReviewSet?")
}
if test.api_error != "" {
if err.Error() != test.api_error {
t.Fatal("expected", test.api_error, "but got", err)
}
} else if test_err != err {
t.Fatal("expected", test_err, "but got", err)
}
return return
} }
if len(res.PRs) != test.resLen { if test.resLen != len(res.PRs) {
t.Errorf("Test Case '%s': expected result len %d but got %d", test.name, test.resLen, len(res.PRs)) t.Error("expected result len", test.resLen, "but got", len(res.PRs))
} }
PrjGitPR, err := res.GetPrjGitPR() PrjGitPR, err := res.GetPrjGitPR()
@@ -620,9 +583,6 @@ func TestPR(t *testing.T) {
} }
pr_found := false pr_found := false
if test.prjGitPRIndex >= 0 { if test.prjGitPRIndex >= 0 {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
for i := range test.data { for i := range test.data {
if PrjGitPR.PR == test.data[i].pr && i == test.prjGitPRIndex { if PrjGitPR.PR == test.data[i].pr && i == test.prjGitPRIndex {
t.Log("found at index", i) t.Log("found at index", i)
@@ -873,6 +833,7 @@ func TestFindMissingAndExtraReviewers(t *testing.T) {
[]string{"autogits_obs_staging_bot", "user1"}, []string{"autogits_obs_staging_bot", "user1"},
}, },
}, },
{ {
name: "Add reviewer if also maintainer where review by maintainer is not needed", name: "Add reviewer if also maintainer where review by maintainer is not needed",
prset: &common.PRSet{ prset: &common.PRSet{
@@ -1134,67 +1095,8 @@ func TestFindMissingAndExtraReviewers(t *testing.T) {
expected_missing_reviewers: [][]string{{"pkgm2", "prj2"}}, expected_missing_reviewers: [][]string{{"pkgm2", "prj2"}},
expected_extra_reviewers: [][]string{{}, {"prj1"}}, expected_extra_reviewers: [][]string{{}, {"prj1"}},
}, },
{
name: "Package maintainer submitter, AlwaysRequireReview=false",
prset: &common.PRSet{
PRs: []*common.PRInfo{
{
PR: &models.PullRequest{
User: &models.User{UserName: "pkgmaintainer"},
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}},
},
Reviews: &common.PRReviews{},
},
},
Config: &common.AutogitConfig{
GitProjectName: "prg/repo#main",
Organization: "org",
Branch: "main",
Reviewers: []string{},
ReviewRequired: false,
},
},
maintainers: &common.MaintainershipMap{
Data: map[string][]string{
"pkg": {"pkgmaintainer", "pkgm1"},
},
},
noAutoStaging: true,
expected_missing_reviewers: [][]string{
{},
},
},
{
name: "Package maintainer submitter, AlwaysRequireReview=true",
prset: &common.PRSet{
PRs: []*common.PRInfo{
{
PR: &models.PullRequest{
User: &models.User{UserName: "pkgmaintainer"},
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}},
},
Reviews: &common.PRReviews{},
},
},
Config: &common.AutogitConfig{
GitProjectName: "prg/repo#main",
Organization: "org",
Branch: "main",
Reviewers: []string{},
ReviewRequired: true,
},
},
maintainers: &common.MaintainershipMap{
Data: map[string][]string{
"pkg": {"pkgmaintainer", "pkgm1"},
},
},
noAutoStaging: true,
expected_missing_reviewers: [][]string{
{"pkgm1"},
},
},
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
test.prset.HasAutoStaging = !test.noAutoStaging test.prset.HasAutoStaging = !test.noAutoStaging
@@ -1224,6 +1126,7 @@ func TestFindMissingAndExtraReviewers(t *testing.T) {
} }
func TestPRMerge(t *testing.T) { func TestPRMerge(t *testing.T) {
t.Skip("FAIL: No PrjGit PR found, missing calls")
repoDir := t.TempDir() repoDir := t.TempDir()
cwd, _ := os.Getwd() cwd, _ := os.Getwd()
@@ -1233,11 +1136,7 @@ func TestPRMerge(t *testing.T) {
t.Fatal(string(out)) t.Fatal(string(out))
} }
t.Skip("No tests of PRMerge yet")
return
common.ExtraGitParams = []string{ common.ExtraGitParams = []string{
"TZ=UTC",
"GIT_CONFIG_COUNT=1", "GIT_CONFIG_COUNT=1",
"GIT_CONFIG_KEY_0=protocol.file.allow", "GIT_CONFIG_KEY_0=protocol.file.allow",
"GIT_CONFIG_VALUE_0=always", "GIT_CONFIG_VALUE_0=always",
@@ -1252,7 +1151,7 @@ func TestPRMerge(t *testing.T) {
config := &common.AutogitConfig{ config := &common.AutogitConfig{
Organization: "org", Organization: "org",
GitProjectName: "org/prj#main", GitProjectName: "org/prj#master",
} }
tests := []struct { tests := []struct {
@@ -1264,10 +1163,8 @@ func TestPRMerge(t *testing.T) {
name: "Merge base not merged in main", name: "Merge base not merged in main",
pr: &models.PullRequest{ pr: &models.PullRequest{
Index: 1,
Base: &models.PRBranchInfo{ Base: &models.PRBranchInfo{
Name: "main", Sha: "e8b0de43d757c96a9d2c7101f4bff404e322f53a1fa4041fb85d646110c38ad4", // "base_add_b1"
Sha: "96515c092626c716a4613ba4f68a8d1cc4894317658342c450e656390f524ec3", // "base_add_b1"
Repo: &models.Repository{ Repo: &models.Repository{
Name: "prj", Name: "prj",
Owner: &models.User{ Owner: &models.User{
@@ -1277,7 +1174,7 @@ func TestPRMerge(t *testing.T) {
}, },
}, },
Head: &models.PRBranchInfo{ Head: &models.PRBranchInfo{
Sha: "4119fc725dc11cdf11f982d5bb0a8ba2a138f1180c4323862a18b8e08def5603", // "base_add_b2" Sha: "88584433de1c917c1d773f62b82381848d882491940b5e9b427a540aa9057d9a", // "base_add_b2"
}, },
}, },
mergeError: "Aborting merge", mergeError: "Aborting merge",
@@ -1286,10 +1183,8 @@ func TestPRMerge(t *testing.T) {
name: "Merge conflict in modules, auto-resolved", name: "Merge conflict in modules, auto-resolved",
pr: &models.PullRequest{ pr: &models.PullRequest{
Index: 1,
Base: &models.PRBranchInfo{ Base: &models.PRBranchInfo{
Name: "main", Sha: "4fbd1026b2d7462ebe9229a49100c11f1ad6555520a21ba515122d8bc41328a8",
Sha: "85f59f7aa732b742e58b48356cd46cb1ab1b5c4349eb5c0eda324e2dbea8f9e7",
Repo: &models.Repository{ Repo: &models.Repository{
Name: "prj", Name: "prj",
Owner: &models.User{ Owner: &models.User{
@@ -1299,7 +1194,7 @@ func TestPRMerge(t *testing.T) {
}, },
}, },
Head: &models.PRBranchInfo{ Head: &models.PRBranchInfo{
Sha: "4119fc725dc11cdf11f982d5bb0a8ba2a138f1180c4323862a18b8e08def5603", // "base_add_b2" Sha: "88584433de1c917c1d773f62b82381848d882491940b5e9b427a540aa9057d9a", // "base_add_b2"
}, },
}, },
}, },
@@ -1309,16 +1204,13 @@ func TestPRMerge(t *testing.T) {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
ctl := gomock.NewController(t) ctl := gomock.NewController(t)
mock := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl) mock := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl)
mock.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
reviewUnrequestMock := mock_common.NewMockGiteaReviewUnrequester(ctl) reviewUnrequestMock := mock_common.NewMockGiteaReviewUnrequester(ctl)
reviewUnrequestMock.EXPECT().UnrequestReview(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() reviewUnrequestMock.EXPECT().UnrequestReview(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
testDir := t.TempDir() testDir := t.TempDir()
t.Log("dir:", testDir) t.Log("dir:", testDir)
mock.EXPECT().GetPullRequest("org", "prj", int64(1)).Return(test.pr, nil) mock.EXPECT().GetPullRequest("org", "prj", int64(1)).Return(test.pr, nil)
mock.EXPECT().GetTimeline("org", "prj", int64(1)).Return(nil, nil).AnyTimes()
mock.EXPECT().GetPullRequestReviews("org", "prj", int64(1)).Return(nil, nil).AnyTimes()
set, err := common.FetchPRSet("test", mock, "org", "prj", 1, config) set, err := common.FetchPRSet("test", mock, "org", "prj", 1, config)
if err != nil { if err != nil {
@@ -1339,11 +1231,11 @@ func TestPRMerge(t *testing.T) {
} }
func TestPRChanges(t *testing.T) { func TestPRChanges(t *testing.T) {
t.Skip("FAIL: unexpected calls, missing calls")
tests := []struct { tests := []struct {
name string name string
PRs []*models.PullRequest PRs []*models.PullRequest
PrjPRs *models.PullRequest PrjPRs *models.PullRequest
Timeline []*models.TimelineComment
}{ }{
{ {
name: "Pkg PR is closed", name: "Pkg PR is closed",
@@ -1355,22 +1247,10 @@ func TestPRChanges(t *testing.T) {
}, },
}, },
PrjPRs: &models.PullRequest{ PrjPRs: &models.PullRequest{
Index: 42,
Title: "some PR", Title: "some PR",
Base: &models.PRBranchInfo{Name: "branch", Repo: &models.Repository{Name: "prjgit", Owner: &models.User{UserName: "org"}}}, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "prjgit", Owner: &models.User{UserName: "org"}}},
Body: "PR: org/repo#42", Body: "PR: org/repo#42",
State: "open", State: "opened",
},
Timeline: []*models.TimelineComment{
{
Type: common.TimelineCommentType_PullRequestRef,
RefIssue: &models.Issue{
Index: 42,
Repository: &models.RepositoryMeta{Name: "prjgit", Owner: "org"},
User: &models.User{UserName: "user"},
Body: "PR: org/repo#42",
},
},
}, },
}, },
} }
@@ -1383,14 +1263,9 @@ func TestPRChanges(t *testing.T) {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
ctl := gomock.NewController(t) ctl := gomock.NewController(t)
mock_fetcher := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl) mock_fetcher := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl)
mock_fetcher.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() mock_fetcher.EXPECT().GetPullRequest("org", "prjgit", int64(42)).Return(test.PrjPRs, nil)
mock_fetcher.EXPECT().GetPullRequest("org", "prjgit", int64(42)).Return(test.PrjPRs, nil).AnyTimes()
mock_fetcher.EXPECT().GetTimeline("org", "prjgit", int64(42)).Return(nil, nil).AnyTimes()
mock_fetcher.EXPECT().GetPullRequestReviews("org", "prjgit", int64(42)).Return(nil, nil).AnyTimes()
for _, pr := range test.PRs { for _, pr := range test.PRs {
mock_fetcher.EXPECT().GetPullRequest(pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Index).Return(pr, nil) mock_fetcher.EXPECT().GetPullRequest(pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Index).Return(pr, nil)
mock_fetcher.EXPECT().GetTimeline(pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Index).Return(test.Timeline, nil).AnyTimes()
mock_fetcher.EXPECT().GetPullRequestReviews(pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Index).Return(nil, nil).AnyTimes()
} }
PRs, err := common.FetchPRSet("user", mock_fetcher, "org", "repo", 42, &config) PRs, err := common.FetchPRSet("user", mock_fetcher, "org", "repo", 42, &config)

View File

@@ -2,7 +2,6 @@
set -x set -x
export TZ=UTC
export GIT_CONFIG_COUNT=2 export GIT_CONFIG_COUNT=2
export GIT_CONFIG_KEY_0=protocol.file.allow export GIT_CONFIG_KEY_0=protocol.file.allow

View File

@@ -0,0 +1,187 @@
#!/usr/bin/perl
use strict;
use warnings;
use IPC::Open2;
use URI;
sub FindFactoryCommit {
my ($package) = @_;
# Execute osc cat and capture output
my $osc_cmd = "osc cat openSUSE:Factory $package $package.changes";
open( my $osc_fh, "$osc_cmd |" ) or die "Failed to run osc: $!";
my $data = do { local $/; <$osc_fh> };
close($osc_fh);
# Calculate size
my $size = length($data);
# Create blob header
my $blob = "blob $size\0$data";
# Open a pipe to openssl to compute the hash
my ( $reader, $writer );
my $pid = open2( $reader, $writer, "openssl sha256" );
# Send blob data
print $writer $blob;
close $writer;
# Read the hash result and extract it
my $hash_line = <$reader>;
waitpid( $pid, 0 );
my ($hash) = $hash_line =~ /([a-fA-F0-9]{64})/;
# Run git search command with the hash
print("looking for hash: $hash\n");
my @hashes;
my $git_cmd =
"git -C $package rev-list --all pool/HEAD | while read commit; do git -C $package ls-tree \"\$commit\" | grep -q '^100644 blob $hash' && echo \"\$commit\"; done";
open( my $git_fh, "$git_cmd |" ) or die "Failed to run git search: $!";
while ( my $commit = <$git_fh> ) {
chomp $commit;
print "Found commit $commit\n";
push( @hashes, $commit );
}
close($git_fh);
return @hashes;
}
sub FactoryMd5 {
my ($package) = @_;
my $out = "";
if (system("osc ls openSUSE:Factory $package | grep -q build.specials.obscpio") == 0) {
system("mkdir _extract") == 0 || die "_extract exists or can't make it. Aborting.";
chdir("_extract") || die;
system("osc cat openSUSE:Factory $package build.specials.obscpio | cpio -dium 2> /dev/null") == 0 || die;
system("rm .* 2> /dev/null");
open( my $fh, "find -type f -exec /usr/bin/basename {} \\; | xargs md5sum | awk '{print \$1 FS \$2}' | grep -v d41d8cd98f00b204e9800998ecf8427e |") or die;
while ( my $l = <$fh>) {
$out = $out.$l;
}
close($fh);
chdir("..") && system("rm -rf _extract") == 0 || die;
}
open( my $fh, "osc ls -v openSUSE:Factory $package | awk '{print \$1 FS \$7}' | grep -v -F '_scmsync.obsinfo\nbuild.specials.obscpio' |") or die;
while (my $l = <$fh>) {
$out = $out.$l;
}
close($fh);
return $out;
}
# Read project from first argument
sub Usage {
die "Usage: $0 <OBS Project> <package> <repo>";
}
my $project = shift or Usage();
my $pkg = shift;
my $repo = shift;
if (not defined($repo)) {
Usage();
}
my $meta_url = `osc meta pkg $project $pkg | grep scmsync | sed -e 's,\\s*</\\?scmsync>\\s*,,g'`;
chomp($meta_url);
if ($meta_url ne $repo) {
die "meta not equal to repo for $pkg: $meta_url != $repo";
}
my $u = URI->new($meta_url);
die "Only src.opensuse.org is supported" unless $u->scheme =~ /^https?$/ && $u->host eq 'src.opensuse.org';
my (undef, $org, $repo_path) = split('/', $u->path);
my $branch = $u->fragment;
die "Only src.opensuse.org is supported" unless $org;
if ($org eq "pool") {
print "Already a pool package. We are done.\n";
exit(0);
}
my %params = $u->query_form;
delete $params{trackingbranch};
die "Unsupported query parameters: " . join(', ', keys %params) if keys %params;
my @packages = ($pkg) if defined $pkg;
if ( ! -e $org ) {
mkdir($org);
}
chdir($org);
my $super_user = $ENV{SUPER};
if (defined($super_user)) {
$super_user = "-G $super_user";
} else {
$super_user = "";
}
for my $package ( sort(@packages) ) {
print " ----- PROCESSING $package\n";
my $url = "https://src.opensuse.org/$org/$repo_path.git";
my $push_url = "gitea\@src.opensuse.org:pool/$package.git";
if ( not -e $package ) {
print("cloning...\n");
system("git clone --origin pool $url $package") == 0
or die "Can't clone $org/$repo_path";
}
else {
print("adding remote...\n");
system("git -C $package remote rm pool > /dev/null");
system("git -C $package remote add pool $url") == 0
or die "Can't add pool for $package";
}
system("git -C $package remote set-url pool --push $push_url") == 0
or die "Can't add push remote for $package";
print("fetching remote...\n");
system("git -C $package fetch pool") == 0 or die "Can't fetch pool for $package";
my @commits = FindFactoryCommit($package);
my $Md5Hashes = FactoryMd5($package);
my $c;
my $match = 0;
for my $commit (@commits) {
if ( length($commit) != 64 ) {
print("Failed to find factory commit. Aborting.");
exit(1);
}
if (
system("git -C $package lfs fetch pool $commit") == 0
and system("git -C $package checkout -B factory $commit") == 0
and system("git -C $package lfs checkout") == 0
and chdir($package)) {
open(my $fh, "|-", "md5sum -c --quiet") or die $!;
print $fh $Md5Hashes;
close $fh;
if ($? >> 8 != 0) {
chdir("..") || die;
next;
}
open($fh, "|-", "awk '{print \$2}' | sort | bash -c \"diff <(ls -1 | sort) -\"") or die $!;
print $fh $Md5Hashes;
close $fh;
my $ec = $? >> 8;
chdir("..") || die;
if ($ec == 0) {
$c = $commit;
$match = 1;
last;
}
}
}
if ( !$match ) {
die "Match not found. Aborting.";
}
system ("git -C $package push -f pool factory");
print "$package: $c\n";
}

View File

@@ -1,3 +1,4 @@
SystemsManagement
Java:packages Java:packages
Kernel:firmware Kernel:firmware
Kernel:kdump Kernel:kdump

View File

@@ -0,0 +1,4 @@
#!/usr/bin/bash
osc api '/search/package?match=scmsync' | ../xml_package_parse | ../find_factory_commit_in_gitpkg.pl

View File

@@ -0,0 +1,83 @@
#!/usr/bin/perl
use strict;
use warnings;
use XML::Parser;
my $parser = XML::Parser->new(Handlers => {
Start => \&handle_start,
End => \&handle_end,
Char => \&handle_char,
});
my $current_element = '';
my $current_package_attrs = {};
my $scmsync_content = '';
my %devel_pkgs;
open(my $dfh, "curl -s https://src.opensuse.org/openSUSE/Factory/raw/branch/main/pkgs/_meta/devel_packages |") or die $!;
while(<$dfh>) {
chomp;
$devel_pkgs{$_} = 1;
}
close($dfh);
my $xml_content = do { local $/; <STDIN> };
$parser->parse($xml_content);
sub handle_start {
my ($expat, $element, %attrs) = @_;
$current_element = $element;
if ($element eq 'package') {
$current_package_attrs = \%attrs;
}
if ($element eq 'scmsync') {
$scmsync_content = '';
}
}
sub handle_char {
my ($expat, $string) = @_;
if ($current_element eq 'scmsync') {
$scmsync_content .= $string;
}
}
sub handle_end {
my ($expat, $element) = @_;
if ($element eq 'scmsync') {
my $project = $current_package_attrs->{project};
my $name = $current_package_attrs->{name};
my $scmsync = $scmsync_content;
# Use checks
$project = '' unless defined $project;
$name = '' unless defined $name;
$scmsync = '' unless defined $scmsync;
# Trim
$project =~ s/^\s+|\s+$//g;
$name =~ s/^\s+|\s+$//g;
$scmsync =~ s/^\s+|\s+$//g;
my $has_error = 0;
foreach my $val ($project, $name, $scmsync) {
if ($val =~ /\s/) {
print STDERR "Error: Value '$val' contains whitespace.\n";
$has_error = 1;
}
}
unless ($has_error) {
if ($devel_pkgs{"$name $project"}) {
print "$name $project $scmsync\n";
}
}
}
# Reset current element if we are closing it
if ($current_element eq $element) {
$current_element = '';
}
}

View File

@@ -52,7 +52,7 @@ Config file
| *GitProjectName* | Repository and branch where the ProjectGit lives. | no | string | **Format**: `org/project_repo#branch` | By default assumes `_ObsPrj` with default branch in the *Organization* | | *GitProjectName* | Repository and branch where the ProjectGit lives. | no | string | **Format**: `org/project_repo#branch` | By default assumes `_ObsPrj` with default branch in the *Organization* |
| *ManualMergeOnly* | Merges are permitted only upon receiving a "merge ok" comment from designated maintainers in the PkgGit PR. | no | bool | true, false | false | | *ManualMergeOnly* | Merges are permitted only upon receiving a "merge ok" comment from designated maintainers in the PkgGit PR. | no | bool | true, false | false |
| *ManualMergeProject* | Merges are permitted only upon receiving a "merge ok" comment in the ProjectGit PR from project maintainers. | no | bool | true, false | false | | *ManualMergeProject* | Merges are permitted only upon receiving a "merge ok" comment in the ProjectGit PR from project maintainers. | no | bool | true, false | false |
| *ReviewRequired* | If submitter is a maintainer, require review from another maintainer if available. | no | bool | true, false | false | | *ReviewRequired* | (NOT IMPLEMENTED) If submitter is a maintainer, require review from another maintainer if available. | no | bool | true, false | false |
| *NoProjectGitPR* | Do not create PrjGit PR, but still perform other tasks. | no | bool | true, false | false | | *NoProjectGitPR* | Do not create PrjGit PR, but still perform other tasks. | no | bool | true, false | false |
| *Reviewers* | PrjGit reviewers. Additional review requests are triggered for associated PkgGit PRs. PrjGit PR is merged only when all reviews are complete. | no | array of strings | | `[]` | | *Reviewers* | PrjGit reviewers. Additional review requests are triggered for associated PkgGit PRs. PrjGit PR is merged only when all reviews are complete. | no | array of strings | | `[]` |
| *ReviewGroups* | If a group is specified in Reviewers, its members are listed here. | no | array of objects | | `[]` | | *ReviewGroups* | If a group is specified in Reviewers, its members are listed here. | no | array of objects | | `[]` |

View File

@@ -0,0 +1,18 @@
package interfaces
import "src.opensuse.org/autogits/common"
//go:generate mockgen -source=state_checker.go -destination=../mock/state_checker.go -typed -package mock_main
type StateChecker interface {
VerifyProjectState(configs *common.AutogitConfig) ([]*PRToProcess, error)
CheckRepos()
ConsistencyCheckProcess() error
}
type PRToProcess struct {
Org, Repo, Branch string
}

View File

@@ -1,314 +0,0 @@
package main
import (
"fmt"
"slices"
"strings"
"src.opensuse.org/autogits/common"
"src.opensuse.org/autogits/common/gitea-generated/models"
)
func FindSourceRepository(org, repo string) (*models.Repository, error) {
srcRepo, err := Gitea.GetRepository(org, repo)
if err != nil {
return nil, err
}
if srcRepo == nil {
return nil, fmt.Errorf("Source repository not found: %s/%s", org, repo)
}
if srcRepo.Parent == nil {
return nil, fmt.Errorf("Source has no parents: %s/%s", org, repo)
}
return srcRepo, nil
}
func createEmptyBranch(git common.Git, PackageName, Branch string) {
git.GitExecOrPanic(PackageName, "checkout", "--detach")
git.GitExec(PackageName, "branch", "-D", Branch)
git.GitExecOrPanic(PackageName, "checkout", "-f", "--orphan", Branch)
git.GitExecOrPanic(PackageName, "rm", "-rf", ".")
git.GitExecOrPanic(PackageName, "commit", "--allow-empty", "-m", "Initial empty branch")
}
type TimelineInterface interface {
FindPullRequestReferences(org, repo string, idx int64, creator []string) []*models.TimelineComment
}
type Timeline []*models.TimelineComment
func (timeline *Timeline) FindIssuePullRequestRererences(org, repo string, idx int64, creator []string) []*models.TimelineComment {
ret := make([]*models.TimelineComment, 0, 1)
for _, t := range *timeline {
if t.Type == common.TimelineCommentType_PullRequestRef &&
t.RefIssue != nil &&
t.RefIssue.Repository.Owner == org &&
t.RefIssue.Repository.Name == repo &&
(idx == 0 || t.RefIssue.Index == idx) &&
(len(creator) == 0 || slices.Contains(creator, t.User.UserName)) {
ret = append(ret, t)
}
}
return ret
}
type IssueProcessorInterface interface {
IsAddIssue() bool
IsRmIssue() bool
GetTargetBranch() string
}
type IssueProcessor struct {
issue *models.Issue
IssueTimeline Timeline
TargetBranch string
}
func (i *IssueProcessor) GetTargetBranch() string {
const BranchPrefix = "refs/heads/"
branch := i.issue.Ref
if branch, found := strings.CutPrefix(branch, BranchPrefix); found {
return branch
} else {
common.LogDebug("Invalid branch specified:", branch, ". Using default.")
branch = ""
}
return branch
}
func ProcessIssue(issue *models.Issue, configs common.AutogitConfigs) error {
i := &IssueProcessor{issue: issue}
return i.ProcessIssue(configs)
}
func (i *IssueProcessor) IsAddIssue() bool {
if i == nil || i.issue == nil {
return false
}
title := i.issue.Title
return len(title) > 5 && strings.EqualFold(title[0:5], "[ADD]")
}
func (i *IssueProcessor) IsRmIssue() bool {
if i == nil || i.issue == nil {
return false
}
title := i.issue.Title
return len(title) > 4 && strings.EqualFold(title[0:4], "[RM]")
}
func (i *IssueProcessor) ProcessAddIssue(config *common.AutogitConfig) error {
issue := i.issue
org := issue.Repository.Owner
repo := issue.Repository.Name
// idx := issue.Index
// we need "New Package" label and "Approval Required" label, unless already approved
// either via Label "Approved" or via review comment.
NewIssues := common.FindNewReposInIssueBody(issue.Body)
if NewIssues == nil {
common.LogDebug("No new repos found in issue body")
return nil
}
git, err := GitHandler.CreateGitHandler(config.Organization)
if err != nil {
return err
}
defer git.Close()
for _, nr := range NewIssues.Repos {
common.LogDebug(" - Processing new repository src:", nr.Organization+"/"+nr.PackageName+"#"+nr.Branch)
targetRepo, err := Gitea.GetRepository(config.Organization, nr.PackageName)
if err != nil {
return err
}
if targetRepo == nil {
common.LogInfo(" - Repository", config.Organization+"/"+nr.PackageName, "does not exist. Labeling issue.")
if !common.IsDryRun && issue.State == "open" {
Gitea.SetLabels(org, repo, issue.Index, []string{config.Label(common.Label_NewRepository)})
}
common.LogDebug(" # Done for now with this repo")
continue
}
// check if we already have created a PR here
// TODO, we need to filter by project config permissions of target project, not just assume bot here.
users := []string{CurrentUser.UserName}
prs := i.IssueTimeline.FindIssuePullRequestRererences(config.Organization, nr.PackageName, 0, users)
for _, t := range prs {
pr, err := Gitea.GetPullRequest(config.Organization, nr.PackageName, t.RefIssue.Index)
if err != nil {
common.LogError("Failed to fetch PR", common.PRtoString(pr), ":", err)
}
if issue.State == "open" {
// PR already created, we just need to update it now
common.LogInfo("Update PR ", common.PRtoString(pr), "only... Nothing to do now")
return nil
}
// so, issue is closed .... close associated package PR
_, err = Gitea.UpdateIssue(config.Organization, nr.PackageName, t.RefIssue.Index, &models.EditIssueOption{State: "closed"})
if err != nil {
common.LogError("Failed to close associated PR", common.PRtoString(pr), ":", err)
}
// remove branch if it's a new repository.
return err
}
srcRepo, err := FindSourceRepository(nr.Organization, nr.Repository)
if err != nil {
continue
}
if len(nr.Branch) == 0 {
nr.Branch = srcRepo.DefaultBranch
}
srcRemoteName, err := git.GitClone(nr.PackageName, nr.Branch, srcRepo.SSHURL)
if err != nil {
return err
}
remoteName, err := git.GitClone(nr.PackageName, nr.Branch, targetRepo.SSHURL)
if err != nil {
return err
}
// Check that fork/parent repository relationship exists
if srcRepo.Parent.Name != targetRepo.Name || srcRepo.Parent.Owner.UserName != targetRepo.Owner.UserName {
common.LogError("Source repository is not fork of the Target repository. Fork of:", srcRepo.Parent.Owner.UserName+"/"+srcRepo.Parent.Name)
continue
}
srcBranch := nr.Branch
if srcBranch == "" {
srcBranch = srcRepo.DefaultBranch
}
// We are ready to setup a pending PR.
// 1. empty target branch with empty commit, this will be discarded no merge
// 2. create PR from source to target
// a) if source is not branch, create a source branch in target repo that contains the relevant commit
SourceCommitList := common.SplitLines(git.GitExecWithOutputOrPanic(nr.PackageName, "rev-list", "--first-parent", srcRemoteName+"/"+nr.Branch))
CommitLength := len(SourceCommitList)
SourceCommitId := SourceCommitList[CommitLength-1]
if CommitLength > 20 {
SourceCommitId = SourceCommitList[20]
}
if CommitLength < 2 {
// only 1 commit, then we need empty branch on target
if dl, err := git.GitDirectoryContentList(nr.PackageName, nr.Branch); err == nil && len(dl) > 0 {
createEmptyBranch(git, nr.PackageName, nr.Branch)
}
} else {
git.GitExecOrPanic(nr.PackageName, "checkout", "-B", nr.Branch, SourceCommitId)
}
if !common.IsDryRun {
git.GitExecOrPanic(nr.PackageName, "push", "-f", remoteName, nr.Branch)
}
head := nr.Organization + ":" + srcBranch
isBranch := false
// Hash can be branch name! Check if it's a branch or tag on the remote
out, err := git.GitExecWithOutput(nr.PackageName, "ls-remote", "--heads", srcRepo.SSHURL, srcBranch)
if err == nil && strings.Contains(out, "refs/heads/"+srcBranch) {
isBranch = true
}
if !isBranch {
tempBranch := fmt.Sprintf("new_package_%d_%s", issue.Index, nr.PackageName)
// Re-clone or use existing if branch check was done above
remoteName, err := git.GitClone(nr.PackageName, srcBranch, targetRepo.SSHURL)
if err != nil {
return err
}
git.GitExecOrPanic(nr.PackageName, "remote", "add", "source", srcRepo.SSHURL)
git.GitExecOrPanic(nr.PackageName, "fetch", "source", srcBranch)
git.GitExecOrPanic(nr.PackageName, "checkout", "-B", tempBranch, "FETCH_HEAD")
if !common.IsDryRun {
git.GitExecOrPanic(nr.PackageName, "push", "-f", remoteName, tempBranch)
}
head = tempBranch
}
title := fmt.Sprintf("Add package %s", nr.PackageName)
prjGitOrg, prjGitRepo, _ := config.GetPrjGit()
body := fmt.Sprintf("See issue %s/%s#%d", prjGitOrg, prjGitRepo, issue.Index)
br := i.TargetBranch
if len(br) == 0 {
br = targetRepo.DefaultBranch
}
pr, err, isNew := Gitea.CreatePullRequestIfNotExist(targetRepo, head, br, title, body)
if err != nil {
common.LogError(targetRepo.Name, head, i.TargetBranch, title, body)
return err
}
if !isNew && pr.Body != body {
Gitea.UpdatePullRequest(pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Index, &models.EditPullRequestOption{
AllowMaintainerEdit: true,
Body: body,
})
}
if isNew {
if _, err := Gitea.SetLabels(config.Organization, nr.PackageName, pr.Index, []string{config.Label(common.Label_NewRepository)}); err != nil {
common.LogError("Failed to set label:", common.Label_NewRepository, err)
}
}
}
return nil
}
func (i *IssueProcessor) ProcessIssue(configs common.AutogitConfigs) error {
issue := i.issue
org := issue.Repository.Owner
repo := issue.Repository.Name
idx := issue.Index
// out, _ := json.MarshalIndent(issue, "", " ")
// common.LogDebug(string(out))
var err error
i.IssueTimeline, err = Gitea.GetTimeline(org, repo, idx)
if err != nil {
common.LogError(" timeline fetch failed:", err)
return err
}
i.TargetBranch = i.GetTargetBranch()
config := configs.GetPrjGitConfig(org, repo, i.TargetBranch)
if config == nil {
return fmt.Errorf("Cannot find config for %s/%s#%s", org, repo, i.TargetBranch)
}
common.LogDebug("issue processing:", common.IssueToString(issue), "@", i.TargetBranch)
if i.IsAddIssue() {
i.ProcessAddIssue(config)
} else if i.IsRmIssue() {
// to remove a package, no approval is required. This should happen via
// project git PR reviews
} else {
common.LogError("Non-standard issue created. Ignoring", common.IssueToString(issue))
return nil
}
return nil
}

View File

@@ -1,521 +0,0 @@
package main
import (
"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"
"testing"
)
func TestProcessIssue_Add(t *testing.T) {
ctl := gomock.NewController(t)
defer ctl.Finish()
gitea := mock_common.NewMockGitea(ctl)
Gitea = gitea
common.IsDryRun = false
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Not(int64(999))).Return([]*models.TimelineComment{}, nil).AnyTimes()
CurrentUser = &models.User{UserName: "bot-user"}
config := &common.AutogitConfig{
Organization: "target-org",
GitProjectName: "test-org/test-prj#main",
}
configs := []*common.AutogitConfig{config}
issue := &models.Issue{
Title: "[ADD] pkg1",
Body: "src-org/pkg1#master",
Index: 123,
Repository: &models.RepositoryMeta{
Owner: "test-org",
Name: "test-prj",
},
Ref: "refs/heads/main",
State: "open",
}
expectedBody := "See issue test-org/test-prj#123"
t.Run("Repository does not exist - labels issue", func(t *testing.T) {
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
GitHandler = mockGitGen
mockGit := mock_common.NewMockGit(ctl)
mockGitGen.EXPECT().CreateGitHandler("target-org").Return(mockGit, nil)
mockGit.EXPECT().Close().Return(nil)
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(nil, nil)
gitea.EXPECT().SetLabels("test-org", "test-prj", int64(123), []string{"new/New Repository"}).Return(nil, nil)
err := ProcessIssue(issue, configs)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
})
t.Run("Source is SHA - creates temp branch in target", func(t *testing.T) {
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
GitHandler = mockGitGen
mockGit := mock_common.NewMockGit(ctl)
mockGitGen.EXPECT().CreateGitHandler("target-org").Return(mockGit, nil)
mockGit.EXPECT().Close().Return(nil)
targetRepo := &models.Repository{
Name: "pkg1",
SSHURL: "target-ssh-url",
Owner: &models.User{UserName: "target-org"},
}
srcRepo := &models.Repository{
Name: "pkg1",
SSHURL: "src-ssh-url",
DefaultBranch: "master",
Owner: &models.User{UserName: "src-org"},
Parent: &models.Repository{
Name: "pkg1",
Owner: &models.User{UserName: "target-org"},
},
}
sha := "abcdef0123456789abcdef0123456789abcdef01"
issueSHA := &models.Issue{
Title: "[ADD] pkg1",
Body: "src-org/pkg1#" + sha,
Index: 123,
Repository: &models.RepositoryMeta{Owner: "test-org", Name: "test-prj"},
Ref: "refs/heads/main",
State: "open",
}
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
mockGit.EXPECT().GitClone("pkg1", sha, "src-ssh-url").Return("src-remote", nil)
mockGit.EXPECT().GitClone("pkg1", sha, "target-ssh-url").Return("origin", nil)
// Source commit list and reset logic
mockGit.EXPECT().GitExecWithOutputOrPanic("pkg1", "rev-list", "--first-parent", "src-remote/"+sha).Return(sha + "\n" + "parent-sha")
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "-B", sha, "parent-sha")
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", sha)
mockGit.EXPECT().GitExecWithOutput("pkg1", "ls-remote", "--heads", "src-ssh-url", sha).Return("", nil)
// SHA source logic (creates temp branch)
tempBranch := "new_package_123_pkg1"
mockGit.EXPECT().GitClone("pkg1", sha, "target-ssh-url").Return("origin", nil)
mockGit.EXPECT().GitExecOrPanic("pkg1", "remote", "add", "source", "src-ssh-url")
mockGit.EXPECT().GitExecOrPanic("pkg1", "fetch", "source", sha)
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "-B", tempBranch, "FETCH_HEAD")
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", tempBranch)
// PR creation using temp branch
pr := &models.PullRequest{
Index: 456,
Body: expectedBody,
Base: &models.PRBranchInfo{
Repo: targetRepo,
},
}
gitea.EXPECT().CreatePullRequestIfNotExist(targetRepo, tempBranch, "main", gomock.Any(), gomock.Any()).Return(pr, nil, true)
gitea.EXPECT().SetLabels("target-org", "pkg1", int64(456), []string{"new/New Repository"}).Return(nil, nil)
err := ProcessIssue(issueSHA, configs)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
})
t.Run("Repository exists - continue processing and create PR", func(t *testing.T) {
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
GitHandler = mockGitGen
mockGit := mock_common.NewMockGit(ctl)
mockGitGen.EXPECT().CreateGitHandler("target-org").Return(mockGit, nil)
mockGit.EXPECT().Close().Return(nil)
targetRepo := &models.Repository{
Name: "pkg1",
SSHURL: "target-ssh-url",
Owner: &models.User{UserName: "target-org"},
}
srcRepo := &models.Repository{
Name: "pkg1",
SSHURL: "src-ssh-url",
DefaultBranch: "master",
Owner: &models.User{UserName: "src-org"},
Parent: &models.Repository{
Name: "pkg1",
Owner: &models.User{UserName: "target-org"},
},
}
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
mockGit.EXPECT().GitClone("pkg1", "master", "src-ssh-url").Return("src-remote", nil)
mockGit.EXPECT().GitClone("pkg1", "master", "target-ssh-url").Return("origin", nil)
// Commit list logic
mockGit.EXPECT().GitExecWithOutputOrPanic("pkg1", "rev-list", "--first-parent", "src-remote/master").Return("sha1\nsha2")
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "-B", "master", "sha2")
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", "master")
// Check if source is a branch via ls-remote
mockGit.EXPECT().GitExecWithOutput("pkg1", "ls-remote", "--heads", "src-ssh-url", "master").Return("sha1 refs/heads/master", nil)
// PR creation
pr := &models.PullRequest{
Index: 456,
Body: expectedBody,
Base: &models.PRBranchInfo{
Repo: targetRepo,
},
}
gitea.EXPECT().CreatePullRequestIfNotExist(targetRepo, "src-org:master", "main", gomock.Any(), gomock.Any()).Return(pr, nil, true)
gitea.EXPECT().SetLabels("target-org", "pkg1", int64(456), []string{"new/New Repository"}).Return(nil, nil)
err := ProcessIssue(issue, configs)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
})
t.Run("Source repository is not fork of target repository - aborts", func(t *testing.T) {
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
GitHandler = mockGitGen
mockGit := mock_common.NewMockGit(ctl)
mockGitGen.EXPECT().CreateGitHandler("target-org").Return(mockGit, nil)
mockGit.EXPECT().Close().Return(nil)
targetRepo := &models.Repository{
Name: "pkg1",
SSHURL: "target-ssh-url",
Owner: &models.User{UserName: "target-org"},
}
srcRepo := &models.Repository{
Name: "pkg1",
Owner: &models.User{UserName: "src-org"},
SSHURL: "src-ssh-url",
Parent: &models.Repository{
Name: "other-repo",
Owner: &models.User{UserName: "other-org"},
},
}
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
mockGit.EXPECT().GitClone("pkg1", "master", "src-ssh-url").Return("src-remote", nil)
mockGit.EXPECT().GitClone("pkg1", "master", "target-ssh-url").Return("origin", nil)
err := ProcessIssue(issue, configs)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
})
t.Run("Source repository is fork of target repository - proceeds", func(t *testing.T) {
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
GitHandler = mockGitGen
mockGit := mock_common.NewMockGit(ctl)
mockGitGen.EXPECT().CreateGitHandler("target-org").Return(mockGit, nil)
mockGit.EXPECT().Close().Return(nil)
targetRepo := &models.Repository{
Name: "pkg1",
Owner: &models.User{UserName: "target-org"},
SSHURL: "target-ssh-url",
}
srcRepo := &models.Repository{
Name: "pkg1",
Owner: &models.User{UserName: "src-org"},
SSHURL: "src-ssh-url",
Parent: &models.Repository{
Name: "pkg1",
Owner: &models.User{UserName: "target-org"},
},
DefaultBranch: "master",
}
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
mockGit.EXPECT().GitClone("pkg1", "master", "src-ssh-url").Return("src-remote", nil)
mockGit.EXPECT().GitClone("pkg1", "master", "target-ssh-url").Return("origin", nil)
mockGit.EXPECT().GitExecWithOutputOrPanic("pkg1", "rev-list", "--first-parent", "src-remote/master").Return("sha1\nsha2")
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "-B", "master", "sha2")
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", "master")
mockGit.EXPECT().GitExecWithOutput("pkg1", "ls-remote", "--heads", "src-ssh-url", "master").Return("sha1 refs/heads/master", nil)
pr := &models.PullRequest{
Index: 456,
Body: expectedBody,
Base: &models.PRBranchInfo{
Repo: targetRepo,
},
}
gitea.EXPECT().CreatePullRequestIfNotExist(targetRepo, "src-org:master", "main", gomock.Any(), gomock.Any()).Return(pr, nil, false)
err := ProcessIssue(issue, configs)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
})
t.Run("Source repository has no parent (not a fork) - aborts", func(t *testing.T) {
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
GitHandler = mockGitGen
mockGit := mock_common.NewMockGit(ctl)
mockGitGen.EXPECT().CreateGitHandler("target-org").Return(mockGit, nil)
mockGit.EXPECT().Close().Return(nil)
targetRepo := &models.Repository{
Name: "pkg1",
Owner: &models.User{UserName: "target-org"},
SSHURL: "target-ssh-url",
}
srcRepo := &models.Repository{
Name: "pkg1",
Owner: &models.User{UserName: "src-org"},
SSHURL: "src-ssh-url",
Parent: nil,
DefaultBranch: "master",
}
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
err := ProcessIssue(issue, configs)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
})
t.Run("Target branch missing - creates orphan branch", func(t *testing.T) {
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
GitHandler = mockGitGen
mockGit := mock_common.NewMockGit(ctl)
mockGitGen.EXPECT().CreateGitHandler("target-org").Return(mockGit, nil)
mockGit.EXPECT().Close().Return(nil)
targetRepo := &models.Repository{
Name: "pkg1",
SSHURL: "target-ssh-url",
Owner: &models.User{UserName: "target-org"},
}
srcRepo := &models.Repository{
Name: "pkg1",
SSHURL: "src-ssh-url",
DefaultBranch: "master",
Owner: &models.User{UserName: "src-org"},
Parent: &models.Repository{
Name: "pkg1",
Owner: &models.User{UserName: "target-org"},
},
}
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
mockGit.EXPECT().GitClone("pkg1", "master", "src-ssh-url").Return("src-remote", nil)
mockGit.EXPECT().GitClone("pkg1", "master", "target-ssh-url").Return("origin", nil)
// Branch check - rev-list works but says only 1 commit
mockGit.EXPECT().GitExecWithOutputOrPanic("pkg1", "rev-list", "--first-parent", "src-remote/master").Return("sha1")
// Orphan branch creation via createEmptyBranch
mockGit.EXPECT().GitDirectoryContentList("pkg1", "master").Return(map[string]string{"file": "sha"}, nil)
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "--detach")
mockGit.EXPECT().GitExec("pkg1", "branch", "-D", "master")
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "-f", "--orphan", "master")
mockGit.EXPECT().GitExecOrPanic("pkg1", "rm", "-rf", ".")
mockGit.EXPECT().GitExecOrPanic("pkg1", "commit", "--allow-empty", "-m", "Initial empty branch")
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", "master")
mockGit.EXPECT().GitExecWithOutput("pkg1", "ls-remote", "--heads", "src-ssh-url", "master").Return("sha1 refs/heads/master", nil)
// PR creation
pr := &models.PullRequest{
Index: 456,
Body: expectedBody,
Base: &models.PRBranchInfo{
Repo: targetRepo,
},
}
gitea.EXPECT().CreatePullRequestIfNotExist(targetRepo, "src-org:master", "main", gomock.Any(), gomock.Any()).Return(pr, nil, true)
gitea.EXPECT().SetLabels("target-org", "pkg1", int64(456), []string{"new/New Repository"}).Return(nil, nil)
err := ProcessIssue(issue, configs)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
})
t.Run("Config not found", func(t *testing.T) {
issueNoConfig := &models.Issue{
Title: "[ADD] pkg1",
Body: "src-org/pkg1#master",
Index: 123,
Repository: &models.RepositoryMeta{
Owner: "other-org",
Name: "other-prj",
},
Ref: "refs/heads/main",
State: "open",
}
err := ProcessIssue(issueNoConfig, configs)
if err == nil || err.Error() != "Cannot find config for other-org/other-prj#main" {
t.Errorf("Expected config not found error, got %v", err)
}
})
t.Run("No repos in body", func(t *testing.T) {
err := ProcessIssue(&models.Issue{
Title: "[ADD] pkg1",
Body: "nothing here",
Ref: "refs/heads/main",
Repository: &models.RepositoryMeta{
Owner: "test-org",
Name: "test-prj",
},
State: "open",
}, configs)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
})
t.Run("Source SHA update - updates existing temp branch", func(t *testing.T) {
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
GitHandler = mockGitGen
mockGit := mock_common.NewMockGit(ctl)
mockGitGen.EXPECT().CreateGitHandler("target-org").Return(mockGit, nil).Times(2)
mockGit.EXPECT().Close().Return(nil).Times(2)
targetRepo := &models.Repository{
Name: "pkg1",
SSHURL: "target-ssh-url",
Owner: &models.User{UserName: "target-org"},
}
srcRepo := &models.Repository{
Name: "pkg1",
SSHURL: "src-ssh-url",
DefaultBranch: "master",
Owner: &models.User{UserName: "src-org"},
Parent: &models.Repository{
Name: "pkg1",
Owner: &models.User{UserName: "target-org"},
},
}
sha1 := "abcdef0123456789abcdef0123456789abcdef01"
issue1 := &models.Issue{
Title: "[ADD] pkg1",
Body: "src-org/pkg1#" + sha1,
Index: 123,
Repository: &models.RepositoryMeta{Owner: "test-org", Name: "test-prj"},
Ref: "refs/heads/main",
State: "open",
}
// First call expectations
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
mockGit.EXPECT().GitClone("pkg1", sha1, "src-ssh-url").Return("src-remote", nil)
mockGit.EXPECT().GitClone("pkg1", sha1, "target-ssh-url").Return("origin", nil)
mockGit.EXPECT().GitExecWithOutputOrPanic("pkg1", "rev-list", "--first-parent", "src-remote/"+sha1).Return(sha1 + "\n" + "parent")
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "-B", sha1, "parent")
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", sha1)
mockGit.EXPECT().GitExecWithOutput("pkg1", "ls-remote", "--heads", "src-ssh-url", sha1).Return("", nil)
tempBranch := "new_package_123_pkg1"
mockGit.EXPECT().GitClone("pkg1", sha1, "target-ssh-url").Return("origin", nil)
mockGit.EXPECT().GitExecOrPanic("pkg1", "remote", "add", "source", "src-ssh-url")
mockGit.EXPECT().GitExecOrPanic("pkg1", "fetch", "source", sha1)
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "-B", tempBranch, "FETCH_HEAD")
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", tempBranch)
pr := &models.PullRequest{
Index: 456,
Body: expectedBody,
Base: &models.PRBranchInfo{
Repo: targetRepo,
},
}
gitea.EXPECT().CreatePullRequestIfNotExist(targetRepo, tempBranch, "main", gomock.Any(), gomock.Any()).Return(pr, nil, true)
gitea.EXPECT().SetLabels("target-org", "pkg1", int64(456), []string{"new/New Repository"}).Return(nil, nil)
err := ProcessIssue(issue1, configs)
if err != nil {
t.Errorf("First call failed: %v", err)
}
// Second call with different SHA
sha2 := "0123456789abcdef0123456789abcdef01234567"
issue2 := &models.Issue{
Title: "[ADD] pkg1",
Body: "src-org/pkg1#" + sha2,
Index: 123,
Repository: &models.RepositoryMeta{Owner: "test-org", Name: "test-prj"},
Ref: "refs/heads/main",
State: "open",
}
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
mockGit.EXPECT().GitClone("pkg1", sha2, "src-ssh-url").Return("src-remote", nil)
mockGit.EXPECT().GitClone("pkg1", sha2, "target-ssh-url").Return("origin", nil)
mockGit.EXPECT().GitExecWithOutputOrPanic("pkg1", "rev-list", "--first-parent", "src-remote/"+sha2).Return(sha2 + "\n" + "parent")
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "-B", sha2, "parent")
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", sha2)
mockGit.EXPECT().GitExecWithOutput("pkg1", "ls-remote", "--heads", "src-ssh-url", sha2).Return("", nil)
mockGit.EXPECT().GitClone("pkg1", sha2, "target-ssh-url").Return("origin", nil)
mockGit.EXPECT().GitExecOrPanic("pkg1", "remote", "add", "source", "src-ssh-url")
mockGit.EXPECT().GitExecOrPanic("pkg1", "fetch", "source", sha2)
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "-B", tempBranch, "FETCH_HEAD")
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", tempBranch)
// CreatePullRequestIfNotExist should be called with same tempBranch, return existing PR
gitea.EXPECT().CreatePullRequestIfNotExist(targetRepo, tempBranch, "main", gomock.Any(), gomock.Any()).Return(pr, nil, false)
err = ProcessIssue(issue2, configs)
if err != nil {
t.Errorf("Second call failed: %v", err)
}
})
t.Run("PR already exists and issue is open - does nothing", func(t *testing.T) {
issue999 := &models.Issue{
Title: "[ADD] pkg1",
Body: "src-org/pkg1#master",
Index: 999,
Repository: &models.RepositoryMeta{
Owner: "test-org",
Name: "test-prj",
},
Ref: "refs/heads/main",
State: "open",
}
timeline := []*models.TimelineComment{
{
Type: common.TimelineCommentType_PullRequestRef,
RefIssue: &models.Issue{
Index: 456,
Repository: &models.RepositoryMeta{
Owner: "target-org",
Name: "pkg1",
},
},
User: &models.User{UserName: "bot-user"},
},
}
// We need to override the default GetTimeline mock
gitea.EXPECT().GetTimeline("test-org", "test-prj", int64(999)).Return(timeline, nil)
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
GitHandler = mockGitGen
mockGit := mock_common.NewMockGit(ctl)
mockGitGen.EXPECT().CreateGitHandler("target-org").Return(mockGit, nil)
mockGit.EXPECT().Close().Return(nil)
targetRepo := &models.Repository{
Name: "pkg1",
Owner: &models.User{UserName: "target-org"},
}
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
pr := &models.PullRequest{
Index: 456,
Base: &models.PRBranchInfo{
Repo: targetRepo,
},
}
gitea.EXPECT().GetPullRequest("target-org", "pkg1", int64(456)).Return(pr, nil)
err := ProcessIssue(issue999, configs)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
})
t.Run("PR already exists and issue is closed - closes PR", func(t *testing.T) {
closedIssue := &models.Issue{
Title: "[ADD] pkg1",
Body: "src-org/pkg1#master",
Index: 999,
Repository: &models.RepositoryMeta{
Owner: "test-org",
Name: "test-prj",
},
Ref: "refs/heads/main",
State: "closed",
}
timeline := []*models.TimelineComment{
{
Type: common.TimelineCommentType_PullRequestRef,
RefIssue: &models.Issue{
Index: 456,
Repository: &models.RepositoryMeta{
Owner: "target-org",
Name: "pkg1",
},
},
User: &models.User{UserName: "bot-user"},
},
}
gitea.EXPECT().GetTimeline("test-org", "test-prj", int64(999)).Return(timeline, nil)
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
GitHandler = mockGitGen
mockGit := mock_common.NewMockGit(ctl)
mockGitGen.EXPECT().CreateGitHandler("target-org").Return(mockGit, nil)
mockGit.EXPECT().Close().Return(nil)
targetRepo := &models.Repository{
Name: "pkg1",
Owner: &models.User{UserName: "target-org"},
}
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
pr := &models.PullRequest{
Index: 456,
Base: &models.PRBranchInfo{
Repo: targetRepo,
},
}
gitea.EXPECT().GetPullRequest("target-org", "pkg1", int64(456)).Return(pr, nil)
gitea.EXPECT().UpdateIssue("target-org", "pkg1", int64(456), gomock.Any()).Return(nil, nil)
err := ProcessIssue(closedIssue, configs)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
})
}

View File

@@ -146,22 +146,15 @@ func main() {
num, err := strconv.ParseInt(data[3], 10, 64) num, err := strconv.ParseInt(data[3], 10, 64)
common.LogInfo("Processing:", org, "/", repo, "#", num) common.LogInfo("Processing:", org, "/", repo, "#", num)
common.PanicOnError(err) common.PanicOnError(err)
if pr, err := Gitea.GetPullRequest(org, repo, num); err == nil && pr != nil { pr, err := Gitea.GetPullRequest(org, repo, num)
if err = ProcesPullRequest(pr, configs); err != nil { if err != nil {
common.LogError("PR processor returned error", err) common.LogError("Cannot fetch PR", err)
}
} else if issue, err := Gitea.GetIssue(org, repo, num); err == nil && issue != nil {
processor := &IssueProcessor{
issue: issue,
}
if err = processor.ProcessIssue(configs); err != nil {
common.LogError("issue processor returned error:", err)
}
} else {
common.LogError("Cannot fetch PR or Issue", err)
return return
} }
if err = ProcesPullRequest(pr, configs); err != nil {
common.LogError("processor returned error", err)
}
} }
return return

View File

@@ -2,8 +2,10 @@ package main
import ( import (
"bytes" "bytes"
"fmt"
"log" "log"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
"testing" "testing"
@@ -20,6 +22,83 @@ func TestProjectBranchName(t *testing.T) {
} }
} }
const LocalCMD = "---"
func gitExecs(t *testing.T, git *common.GitHandlerImpl, cmds [][]string) {
for _, cmd := range cmds {
if cmd[0] == LocalCMD {
command := exec.Command(cmd[2], cmd[3:]...)
command.Dir = filepath.Join(git.GitPath, cmd[1])
command.Stdin = nil
command.Env = append([]string{"GIT_CONFIG_COUNT=1", "GIT_CONFIG_KEY_1=protocol.file.allow", "GIT_CONFIG_VALUE_1=always"}, common.ExtraGitParams...)
_, err := command.CombinedOutput()
if err != nil {
t.Errorf(" *** error: %v\n", err)
}
} else {
git.GitExecOrPanic(cmd[0], cmd[1:]...)
}
}
}
func commandsForPackages(dir, prefix string, startN, endN int) [][]string {
commands := make([][]string, (endN-startN+2)*6)
if dir == "" {
dir = "."
}
cmdIdx := 0
for idx := startN; idx <= endN; idx++ {
pkgDir := fmt.Sprintf("%s%d", prefix, idx)
commands[cmdIdx+0] = []string{"", "init", "-q", "--object-format", "sha256", "-b", "testing", pkgDir}
commands[cmdIdx+1] = []string{LocalCMD, pkgDir, "/usr/bin/touch", "testFile"}
commands[cmdIdx+2] = []string{pkgDir, "add", "testFile"}
commands[cmdIdx+3] = []string{pkgDir, "commit", "-m", "added testFile"}
commands[cmdIdx+4] = []string{pkgDir, "config", "receive.denyCurrentBranch", "ignore"}
commands[cmdIdx+5] = []string{"prj", "submodule", "add", filepath.Join("..", pkgDir), filepath.Join(dir, pkgDir)}
cmdIdx += 6
}
// add all the submodules to the prj
commands[cmdIdx+0] = []string{"prj", "commit", "-a", "-m", "adding subpackages"}
return commands
}
func setupGitForTests(t *testing.T, git *common.GitHandlerImpl) {
common.ExtraGitParams = []string{
"GIT_CONFIG_COUNT=1",
"GIT_CONFIG_KEY_0=protocol.file.allow",
"GIT_CONFIG_VALUE_0=always",
"GIT_AUTHOR_NAME=testname",
"GIT_AUTHOR_EMAIL=test@suse.com",
"GIT_AUTHOR_DATE='2005-04-07T22:13:13'",
"GIT_COMMITTER_NAME=testname",
"GIT_COMMITTER_EMAIL=test@suse.com",
"GIT_COMMITTER_DATE='2005-04-07T22:13:13'",
}
gitExecs(t, git, [][]string{
{"", "init", "-q", "--object-format", "sha256", "-b", "testing", "prj"},
{"", "init", "-q", "--object-format", "sha256", "-b", "testing", "foo"},
{LocalCMD, "foo", "/usr/bin/touch", "file1"},
{"foo", "add", "file1"},
{"foo", "commit", "-m", "first commit"},
{"prj", "config", "receive.denyCurrentBranch", "ignore"},
{"prj", "submodule", "init"},
{"prj", "submodule", "add", "../foo", "testRepo"},
{"prj", "add", ".gitmodules", "testRepo"},
{"prj", "commit", "-m", "First instance"},
{"prj", "submodule", "deinit", "testRepo"},
{LocalCMD, "foo", "/usr/bin/touch", "file2"},
{"foo", "add", "file2"},
{"foo", "commit", "-m", "added file2"},
})
}
func TestUpdatePrBranch(t *testing.T) { func TestUpdatePrBranch(t *testing.T) {
var buf bytes.Buffer var buf bytes.Buffer
origLogger := log.Writer() origLogger := log.Writer()
@@ -46,7 +125,7 @@ func TestUpdatePrBranch(t *testing.T) {
req.Pull_Request.Base.Sha = strings.TrimSpace(revs[1]) req.Pull_Request.Base.Sha = strings.TrimSpace(revs[1])
req.Pull_Request.Head.Sha = strings.TrimSpace(revs[0]) req.Pull_Request.Head.Sha = strings.TrimSpace(revs[0])
updateSubmoduleInPR("testRepo", revs[0], git) updateSubmoduleInPR("mainRepo", revs[0], git)
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", "created commit")) common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", "created commit"))
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "push", "origin", "+HEAD:+testing")) common.PanicOnError(git.GitExec(common.DefaultGitPrj, "push", "origin", "+HEAD:+testing"))
git.GitExecOrPanic("prj", "reset", "--hard", "testing") git.GitExecOrPanic("prj", "reset", "--hard", "testing")

View File

@@ -0,0 +1,10 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: pr_processor.go
//
// Generated by this command:
//
// mockgen -source=pr_processor.go -destination=mock/pr_processor.go -typed
//
// Package mock_main is a generated GoMock package.
package mock_main

View File

@@ -3,18 +3,18 @@
// //
// Generated by this command: // Generated by this command:
// //
// mockgen -source=state_checker.go -destination=mock_state_checker.go -typed -package main // mockgen -source=state_checker.go -destination=../mock/state_checker.go -typed -package mock_main
// //
// Package main is a generated GoMock package. // Package mock_main is a generated GoMock package.
package main package mock_main
import ( import (
reflect "reflect" reflect "reflect"
gomock "go.uber.org/mock/gomock" gomock "go.uber.org/mock/gomock"
common "src.opensuse.org/autogits/common" common "src.opensuse.org/autogits/common"
models "src.opensuse.org/autogits/common/gitea-generated/models" interfaces "src.opensuse.org/autogits/workflow-pr/interfaces"
) )
// MockStateChecker is a mock of StateChecker interface. // MockStateChecker is a mock of StateChecker interface.
@@ -42,9 +42,11 @@ func (m *MockStateChecker) EXPECT() *MockStateCheckerMockRecorder {
} }
// CheckRepos mocks base method. // CheckRepos mocks base method.
func (m *MockStateChecker) CheckRepos() { func (m *MockStateChecker) CheckRepos() error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "CheckRepos") ret := m.ctrl.Call(m, "CheckRepos")
ret0, _ := ret[0].(error)
return ret0
} }
// CheckRepos indicates an expected call of CheckRepos. // CheckRepos indicates an expected call of CheckRepos.
@@ -60,19 +62,19 @@ type MockStateCheckerCheckReposCall struct {
} }
// Return rewrite *gomock.Call.Return // Return rewrite *gomock.Call.Return
func (c *MockStateCheckerCheckReposCall) Return() *MockStateCheckerCheckReposCall { func (c *MockStateCheckerCheckReposCall) Return(arg0 error) *MockStateCheckerCheckReposCall {
c.Call = c.Call.Return() c.Call = c.Call.Return(arg0)
return c return c
} }
// Do rewrite *gomock.Call.Do // Do rewrite *gomock.Call.Do
func (c *MockStateCheckerCheckReposCall) Do(f func()) *MockStateCheckerCheckReposCall { func (c *MockStateCheckerCheckReposCall) Do(f func() error) *MockStateCheckerCheckReposCall {
c.Call = c.Call.Do(f) c.Call = c.Call.Do(f)
return c return c
} }
// DoAndReturn rewrite *gomock.Call.DoAndReturn // DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockStateCheckerCheckReposCall) DoAndReturn(f func()) *MockStateCheckerCheckReposCall { func (c *MockStateCheckerCheckReposCall) DoAndReturn(f func() error) *MockStateCheckerCheckReposCall {
c.Call = c.Call.DoAndReturn(f) c.Call = c.Call.DoAndReturn(f)
return c return c
} }
@@ -116,10 +118,10 @@ func (c *MockStateCheckerConsistencyCheckProcessCall) DoAndReturn(f func() error
} }
// VerifyProjectState mocks base method. // VerifyProjectState mocks base method.
func (m *MockStateChecker) VerifyProjectState(configs *common.AutogitConfig) ([]*PRToProcess, error) { func (m *MockStateChecker) VerifyProjectState(configs *common.AutogitConfig) ([]*interfaces.PRToProcess, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "VerifyProjectState", configs) ret := m.ctrl.Call(m, "VerifyProjectState", configs)
ret0, _ := ret[0].([]*PRToProcess) ret0, _ := ret[0].([]*interfaces.PRToProcess)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
@@ -137,81 +139,19 @@ type MockStateCheckerVerifyProjectStateCall struct {
} }
// Return rewrite *gomock.Call.Return // Return rewrite *gomock.Call.Return
func (c *MockStateCheckerVerifyProjectStateCall) Return(arg0 []*PRToProcess, arg1 error) *MockStateCheckerVerifyProjectStateCall { func (c *MockStateCheckerVerifyProjectStateCall) Return(arg0 []*interfaces.PRToProcess, arg1 error) *MockStateCheckerVerifyProjectStateCall {
c.Call = c.Call.Return(arg0, arg1) c.Call = c.Call.Return(arg0, arg1)
return c return c
} }
// Do rewrite *gomock.Call.Do // Do rewrite *gomock.Call.Do
func (c *MockStateCheckerVerifyProjectStateCall) Do(f func(*common.AutogitConfig) ([]*PRToProcess, error)) *MockStateCheckerVerifyProjectStateCall { func (c *MockStateCheckerVerifyProjectStateCall) Do(f func(*common.AutogitConfig) ([]*interfaces.PRToProcess, error)) *MockStateCheckerVerifyProjectStateCall {
c.Call = c.Call.Do(f) c.Call = c.Call.Do(f)
return c return c
} }
// DoAndReturn rewrite *gomock.Call.DoAndReturn // DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockStateCheckerVerifyProjectStateCall) DoAndReturn(f func(*common.AutogitConfig) ([]*PRToProcess, error)) *MockStateCheckerVerifyProjectStateCall { func (c *MockStateCheckerVerifyProjectStateCall) DoAndReturn(f func(*common.AutogitConfig) ([]*interfaces.PRToProcess, error)) *MockStateCheckerVerifyProjectStateCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// MockPullRequestProcessor is a mock of PullRequestProcessor interface.
type MockPullRequestProcessor struct {
ctrl *gomock.Controller
recorder *MockPullRequestProcessorMockRecorder
isgomock struct{}
}
// MockPullRequestProcessorMockRecorder is the mock recorder for MockPullRequestProcessor.
type MockPullRequestProcessorMockRecorder struct {
mock *MockPullRequestProcessor
}
// NewMockPullRequestProcessor creates a new mock instance.
func NewMockPullRequestProcessor(ctrl *gomock.Controller) *MockPullRequestProcessor {
mock := &MockPullRequestProcessor{ctrl: ctrl}
mock.recorder = &MockPullRequestProcessorMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockPullRequestProcessor) EXPECT() *MockPullRequestProcessorMockRecorder {
return m.recorder
}
// Process mocks base method.
func (m *MockPullRequestProcessor) Process(req *models.PullRequest) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Process", req)
ret0, _ := ret[0].(error)
return ret0
}
// Process indicates an expected call of Process.
func (mr *MockPullRequestProcessorMockRecorder) Process(req any) *MockPullRequestProcessorProcessCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Process", reflect.TypeOf((*MockPullRequestProcessor)(nil).Process), req)
return &MockPullRequestProcessorProcessCall{Call: call}
}
// MockPullRequestProcessorProcessCall wrap *gomock.Call
type MockPullRequestProcessorProcessCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockPullRequestProcessorProcessCall) Return(arg0 error) *MockPullRequestProcessorProcessCall {
c.Call = c.Call.Return(arg0)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockPullRequestProcessorProcessCall) Do(f func(*models.PullRequest) error) *MockPullRequestProcessorProcessCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockPullRequestProcessorProcessCall) DoAndReturn(f func(*models.PullRequest) error) *MockPullRequestProcessorProcessCall {
c.Call = c.Call.DoAndReturn(f) c.Call = c.Call.DoAndReturn(f)
return c return c
} }

View File

@@ -1,5 +1,7 @@
package main package main
//go:generate mockgen -source=pr_processor.go -destination=mock/pr_processor.go -typed
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
@@ -190,14 +192,7 @@ func (pr *PRProcessor) SetSubmodulesToMatchPRSet(prset *common.PRSet) error {
} }
if !submodule_found { if !submodule_found {
common.LogInfo("Adding new submodule", repo, "to PrjGit") common.LogError("Failed to find expected repo:", repo)
ref := fmt.Sprintf(common.PrPattern, org, repo, idx)
commitMsg := fmt.Sprintln("Add package", repo, "\n\nThis commit was autocreated by", GitAuthor, "\n\nreferencing PRs:\n", ref)
git.GitExecOrPanic(common.DefaultGitPrj, "submodule", "add", "-b", pr.PR.Base.Name, pr.PR.Base.Repo.SSHURL, repo)
updateSubmoduleInPR(repo, prHead, git)
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", commitMsg))
} }
} }
return nil return nil
@@ -505,7 +500,7 @@ func (pr *PRProcessor) Process(req *models.PullRequest) error {
// make sure that prjgit is consistent and only submodules that are to be *updated* // make sure that prjgit is consistent and only submodules that are to be *updated*
// reset anything that changed that is not part of the prset // reset anything that changed that is not part of the prset
// package removals/additions are *not* counted here // package removals/additions are *not* counted here
org, repo, branch := config.GetPrjGit()
// TODO: this is broken... // TODO: this is broken...
if pr, err := prset.GetPrjGitPR(); err == nil && false { if pr, err := prset.GetPrjGitPR(); err == nil && false {
common.LogDebug("Submodule parse begin") common.LogDebug("Submodule parse begin")
@@ -554,7 +549,7 @@ func (pr *PRProcessor) Process(req *models.PullRequest) error {
} else { } else {
common.LogInfo("* No prjgit") common.LogInfo("* No prjgit")
} }
maintainers, err := common.FetchProjectMaintainershipData(Gitea, config) maintainers, err := common.FetchProjectMaintainershipData(Gitea, org, repo, branch)
if err != nil { if err != nil {
return err return err
} }
@@ -605,7 +600,7 @@ func (pr *PRProcessor) Process(req *models.PullRequest) error {
common.LogError("merge error:", err) common.LogError("merge error:", err)
} }
} else { } else {
err = prset.AssignReviewers(Gitea, maintainers) prset.AssignReviewers(Gitea, maintainers)
} }
return err return err
} }
@@ -615,14 +610,6 @@ type RequestProcessor struct {
recursive int recursive int
} }
func (w *RequestProcessor) Process(pr *models.PullRequest) error {
configs, ok := w.configuredRepos[pr.Base.Repo.Owner.UserName]
if !ok {
return fmt.Errorf("no config found for org %s", pr.Base.Repo.Owner.UserName)
}
return ProcesPullRequest(pr, configs)
}
func ProcesPullRequest(pr *models.PullRequest, configs []*common.AutogitConfig) error { func ProcesPullRequest(pr *models.PullRequest, configs []*common.AutogitConfig) error {
if len(configs) < 1 { if len(configs) < 1 {
// ignoring pull request against unconfigured project (could be just regular sources?) // ignoring pull request against unconfigured project (could be just regular sources?)
@@ -667,21 +654,6 @@ func (w *RequestProcessor) ProcessFunc(request *common.Request) (err error) {
common.LogError("Cannot find PR for issue:", req.Repository.Owner.Username, req.Repository.Name, int64(req.Issue.Number)) common.LogError("Cannot find PR for issue:", req.Repository.Owner.Username, req.Repository.Name, int64(req.Issue.Number))
return err return err
} }
} else if req, ok := request.Data.(*common.IssueWebhookEvent); ok {
issue, err := Gitea.GetIssue(req.Repository.Owner.Username, req.Repository.Name, int64(req.Issue.Number))
if err != nil {
common.LogError("Cannot find issue for issue event:", req.Repository.Owner.Username, req.Repository.Name, int64(req.Issue.Number))
return err
}
configs, ok := w.configuredRepos[req.Repository.Owner.Username]
if !ok {
common.LogError("*** Cannot find config for org:", req.Repository.Owner.Username)
return nil
}
processor := &IssueProcessor{
issue: issue,
}
return processor.ProcessIssue(configs)
} else { } else {
common.LogError("*** Invalid data format for PR processing.") common.LogError("*** Invalid data format for PR processing.")
return fmt.Errorf("*** Invalid data format for PR processing.") return fmt.Errorf("*** Invalid data format for PR processing.")

View File

@@ -6,6 +6,7 @@ import (
"go.uber.org/mock/gomock" "go.uber.org/mock/gomock"
"src.opensuse.org/autogits/common" "src.opensuse.org/autogits/common"
"src.opensuse.org/autogits/common/gitea-generated/client/repository"
"src.opensuse.org/autogits/common/gitea-generated/models" "src.opensuse.org/autogits/common/gitea-generated/models"
mock_common "src.opensuse.org/autogits/common/mock" mock_common "src.opensuse.org/autogits/common/mock"
) )
@@ -16,7 +17,7 @@ func TestOpenPR(t *testing.T) {
Reviewers: []string{"reviewer1", "reviewer2"}, Reviewers: []string{"reviewer1", "reviewer2"},
Branch: "branch", Branch: "branch",
Organization: "test", Organization: "test",
GitProjectName: "prj#testing", GitProjectName: "prj",
}, },
} }
@@ -25,7 +26,6 @@ func TestOpenPR(t *testing.T) {
Number: 1, Number: 1,
Pull_Request: &common.PullRequest{ Pull_Request: &common.PullRequest{
Id: 1, Id: 1,
Number: 1,
Base: common.Head{ Base: common.Head{
Ref: "branch", Ref: "branch",
Sha: "testing", Sha: "testing",
@@ -53,56 +53,6 @@ func TestOpenPR(t *testing.T) {
}, },
} }
modelPR := &models.PullRequest{
ID: 1,
Index: 1,
State: "open",
User: &models.User{UserName: "testuser"},
RequestedReviewers: []*models.User{},
Base: &models.PRBranchInfo{
Ref: "branch",
Sha: "testing",
Repo: &models.Repository{
Name: "testRepo",
Owner: &models.User{
UserName: "test",
},
},
},
Head: &models.PRBranchInfo{
Ref: "branch",
Sha: "testing",
Repo: &models.Repository{
Name: "testRepo",
Owner: &models.User{
UserName: "test",
},
},
},
}
mockCreatePR := &models.PullRequest{
ID: 2,
Index: 2,
Body: "Forwarded PRs: testRepo\n\nPR: test/testRepo!1",
User: &models.User{UserName: "testuser"},
RequestedReviewers: []*models.User{},
Base: &models.PRBranchInfo{
Name: "testing",
Repo: &models.Repository{
Name: "prjcopy",
Owner: &models.User{UserName: "test"},
},
},
Head: &models.PRBranchInfo{
Sha: "head",
},
}
CurrentUser = &models.User{
UserName: "testuser",
}
git := &common.GitHandlerImpl{ git := &common.GitHandlerImpl{
GitCommiter: "tester", GitCommiter: "tester",
GitEmail: "test@suse.com", GitEmail: "test@suse.com",
@@ -110,47 +60,14 @@ func TestOpenPR(t *testing.T) {
t.Run("PR git opened request against PrjGit == no action", func(t *testing.T) { t.Run("PR git opened request against PrjGit == no action", func(t *testing.T) {
ctl := gomock.NewController(t) ctl := gomock.NewController(t)
gitea := mock_common.NewMockGitea(ctl) Gitea = mock_common.NewMockGitea(ctl)
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
Gitea = gitea
git.GitPath = t.TempDir() git.GitPath = t.TempDir()
pr.config.GitProjectName = "testRepo#testing" pr.config.GitProjectName = "testRepo"
event.Repository.Name = "testRepo" event.Repository.Name = "testRepo"
mockGit := mock_common.NewMockGit(ctl)
pr.git = mockGit
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl) if err := pr.Process(event); err != nil {
GitHandler = mockGitGen
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil).AnyTimes()
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head", nil).AnyTimes()
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{"testRepo": "testing"}, nil).AnyTimes()
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
mockGit.EXPECT().GitCatFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
mockGit.EXPECT().Close().Return(nil).AnyTimes()
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
gitea.EXPECT().SetLabels(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Label{}, nil).AnyTimes()
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, 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"},
Name: "prjcopy",
SSHURL: "git@src.opensuse.org:test/prj.git",
}, nil).AnyTimes()
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes()
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockCreatePR, nil, true).AnyTimes()
gitea.EXPECT().RequestReviews(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().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
if err := pr.Process(modelPR); err != nil {
t.Error("Error PrjGit opened request. Should be no error.", err) t.Error("Error PrjGit opened request. Should be no error.", err)
} }
}) })
@@ -158,52 +75,43 @@ func TestOpenPR(t *testing.T) {
t.Run("Open PrjGit PR", func(t *testing.T) { t.Run("Open PrjGit PR", func(t *testing.T) {
ctl := gomock.NewController(t) ctl := gomock.NewController(t)
gitea := mock_common.NewMockGitea(ctl) gitea := mock_common.NewMockGitea(ctl)
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
Gitea = gitea Gitea = gitea
event.Repository.Name = "testRepo" event.Repository.Name = "testRepo"
pr.config.GitProjectName = "prjcopy#testing" pr.config.GitProjectName = "prjcopy"
git.GitPath = t.TempDir() git.GitPath = t.TempDir()
setupGitForTests(t, git) setupGitForTests(t, git)
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes() prjgit := &models.Repository{
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockCreatePR, nil, true).AnyTimes() SSHURL: "./prj",
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes() DefaultBranch: "testing",
gitea.EXPECT().RequestReviews(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() }
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes() giteaPR := &models.PullRequest{
Base: &models.PRBranchInfo{
Repo: &models.Repository{
Owner: &models.User{
UserName: "test",
},
Name: "testRepo",
},
},
User: &models.User{
UserName: "test",
},
}
// gitea.EXPECT().GetAssociatedPrjGitPR("test", "prjcopy", "test", "testRepo", int64(1)).Return(nil, nil)
gitea.EXPECT().CreateRepositoryIfNotExist(git, "test", "prjcopy").Return(prjgit, nil)
gitea.EXPECT().CreatePullRequestIfNotExist(prjgit, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(giteaPR, nil, true)
gitea.EXPECT().GetPullRequest("test", "testRepo", int64(1)).Return(giteaPR, nil)
gitea.EXPECT().RequestReviews(giteaPR, "reviewer1", "reviewer2").Return(nil, nil)
gitea.EXPECT().GetPullRequestReviews("test", "testRepo", int64(0)).Return([]*models.PullReview{}, nil)
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes() gitea.EXPECT().FetchMaintainershipDirFile("test", "prjcopy", "branch", "_project").Return(nil, "", repository.NewRepoGetRawFileNotFound())
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes() gitea.EXPECT().FetchMaintainershipFile("test", "prjcopy", "branch").Return(nil, "", repository.NewRepoGetRawFileNotFound())
mockGit := mock_common.NewMockGit(ctl) err := pr.Process(event)
pr.git = mockGit
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
GitHandler = mockGitGen
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil).AnyTimes()
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head", nil).AnyTimes()
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{"testRepo": "testing"}, nil).AnyTimes()
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
mockGit.EXPECT().GitCatFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
mockGit.EXPECT().Close().Return(nil).AnyTimes()
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
gitea.EXPECT().SetLabels(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Label{}, nil).AnyTimes()
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, 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"},
Name: "prjcopy",
SSHURL: "git@src.opensuse.org:test/prj.git",
}, nil).AnyTimes()
err := pr.Process(modelPR)
if err != nil { if err != nil {
t.Error("error:", err) t.Error("error:", err)
} }
@@ -212,61 +120,30 @@ func TestOpenPR(t *testing.T) {
t.Run("Cannot create prjgit repository", func(t *testing.T) { t.Run("Cannot create prjgit repository", func(t *testing.T) {
ctl := gomock.NewController(t) ctl := gomock.NewController(t)
gitea := mock_common.NewMockGitea(ctl) gitea := mock_common.NewMockGitea(ctl)
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
Gitea = gitea Gitea = gitea
event.Repository.Name = "testRepo" event.Repository.Name = "testRepo"
pr.config.GitProjectName = "prjcopy#testing" pr.config.GitProjectName = "prjcopy"
git.GitPath = t.TempDir() git.GitPath = t.TempDir()
setupGitForTests(t, git) setupGitForTests(t, git)
failedErr := errors.New("Returned error here") failedErr := errors.New("Returned error here")
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, failedErr).AnyTimes() gitea.EXPECT().CreateRepositoryIfNotExist(git, "test", "prjcopy").Return(nil, failedErr)
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockCreatePR, nil, true).AnyTimes()
mockGit := mock_common.NewMockGit(ctl) err := pr.Process(event)
pr.git = mockGit if err != failedErr {
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
GitHandler = mockGitGen
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil).AnyTimes()
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head", nil).AnyTimes()
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{"testRepo": "testing"}, nil).AnyTimes()
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
mockGit.EXPECT().GitCatFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
gitea.EXPECT().RequestReviews(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
mockGit.EXPECT().Close().Return(nil).AnyTimes()
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
gitea.EXPECT().SetLabels(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Label{}, nil).AnyTimes()
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, 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"},
Name: "prjcopy",
SSHURL: "git@src.opensuse.org:test/prj.git",
}, nil).AnyTimes()
err := pr.Process(modelPR)
if err != nil {
t.Error("error:", err) t.Error("error:", err)
} }
}) })
t.Run("Cannot create PR", func(t *testing.T) { t.Run("Cannot create PR", func(t *testing.T) {
ctl := gomock.NewController(t) ctl := gomock.NewController(t)
gitea := mock_common.NewMockGitea(ctl) gitea := mock_common.NewMockGitea(ctl)
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
Gitea = gitea Gitea = gitea
event.Repository.Name = "testRepo" event.Repository.Name = "testRepo"
pr.config.GitProjectName = "prjcopy#testing" pr.config.GitProjectName = "prjcopy"
git.GitPath = t.TempDir() git.GitPath = t.TempDir()
setupGitForTests(t, git) setupGitForTests(t, git)
@@ -275,37 +152,10 @@ func TestOpenPR(t *testing.T) {
DefaultBranch: "testing", DefaultBranch: "testing",
} }
failedErr := errors.New("Returned error here") failedErr := errors.New("Returned error here")
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(prjgit, nil).AnyTimes() gitea.EXPECT().CreateRepositoryIfNotExist(git, "test", "prjcopy").Return(prjgit, nil)
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, failedErr, false) gitea.EXPECT().CreatePullRequestIfNotExist(prjgit, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, failedErr, false)
mockGit := mock_common.NewMockGit(ctl) err := pr.Process(event)
pr.git = mockGit
gitea.EXPECT().RequestReviews(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
GitHandler = mockGitGen
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil).AnyTimes()
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head", nil).AnyTimes()
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{"testRepo": "testing"}, nil).AnyTimes()
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
mockGit.EXPECT().GitCatFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
mockGit.EXPECT().Close().Return(nil).AnyTimes()
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
gitea.EXPECT().SetLabels(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Label{}, nil).AnyTimes()
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, 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"},
Name: "prjcopy",
SSHURL: "git@src.opensuse.org:test/prj.git",
}, nil).AnyTimes()
err := pr.Process(modelPR)
if err != failedErr { if err != failedErr {
t.Error("error:", err) t.Error("error:", err)
} }
@@ -313,54 +163,44 @@ func TestOpenPR(t *testing.T) {
t.Run("Open PrjGit PR", func(t *testing.T) { t.Run("Open PrjGit PR", func(t *testing.T) {
ctl := gomock.NewController(t) ctl := gomock.NewController(t)
gitea := mock_common.NewMockGitea(ctl) gitea := mock_common.NewMockGitea(ctl)
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
Gitea = gitea Gitea = gitea
event.Repository.Name = "testRepo" event.Repository.Name = "testRepo"
pr.config.GitProjectName = "prjcopy#testing" pr.config.GitProjectName = "prjcopy"
git.GitPath = t.TempDir() git.GitPath = t.TempDir()
setupGitForTests(t, git) setupGitForTests(t, git)
prjgit := &models.Repository{
Name: "SomeRepo",
Owner: &models.User{
UserName: "org",
},
SSHURL: "./prj",
DefaultBranch: "testing",
}
giteaPR := &models.PullRequest{
Base: &models.PRBranchInfo{
Repo: prjgit,
},
Index: 13,
User: &models.User{
UserName: "test",
},
}
failedErr := errors.New("Returned error here") failedErr := errors.New("Returned error here")
// gitea.EXPECT().GetAssociatedPrjGitPR("test", "prjcopy", "test", "testRepo", int64(1)).Return(nil, nil)
gitea.EXPECT().CreateRepositoryIfNotExist(git, "test", "prjcopy").Return(prjgit, nil)
gitea.EXPECT().GetPullRequest("test", "testRepo", int64(1)).Return(giteaPR, nil)
gitea.EXPECT().GetPullRequestReviews("org", "SomeRepo", int64(13)).Return([]*models.PullReview{}, nil)
gitea.EXPECT().CreatePullRequestIfNotExist(prjgit, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(giteaPR, nil, true)
gitea.EXPECT().RequestReviews(giteaPR, "reviewer1", "reviewer2").Return(nil, failedErr)
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes() gitea.EXPECT().FetchMaintainershipDirFile("test", "prjcopy", "branch", "_project").Return(nil, "", repository.NewRepoGetRawFileNotFound())
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes() gitea.EXPECT().FetchMaintainershipFile("test", "prjcopy", "branch").Return(nil, "", repository.NewRepoGetRawFileNotFound())
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockCreatePR, nil, true).AnyTimes()
gitea.EXPECT().RequestReviews(gomock.Any(), gomock.Any()).Return(nil, failedErr).AnyTimes()
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes() err := pr.Process(event)
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes() if errors.Unwrap(err) != failedErr {
mockGit := mock_common.NewMockGit(ctl)
pr.git = mockGit
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
GitHandler = mockGitGen
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil).AnyTimes()
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head", nil).AnyTimes()
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{"testRepo": "testing"}, nil).AnyTimes()
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
mockGit.EXPECT().GitCatFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
mockGit.EXPECT().Close().Return(nil).AnyTimes()
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
gitea.EXPECT().SetLabels(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Label{}, nil).AnyTimes()
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, 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"},
Name: "prjcopy",
SSHURL: "git@src.opensuse.org:test/prj.git",
}, nil).AnyTimes()
err := pr.Process(modelPR)
if err != nil {
t.Error("error:", err) t.Error("error:", err)
} }
}) })

View File

@@ -1,7 +1,12 @@
package main package main
/*
import ( import (
"bytes"
"errors" "errors"
"log"
"os"
"path"
"strings"
"testing" "testing"
"go.uber.org/mock/gomock" "go.uber.org/mock/gomock"
@@ -11,147 +16,217 @@ import (
) )
func TestSyncPR(t *testing.T) { func TestSyncPR(t *testing.T) {
CurrentUser = &models.User{UserName: "testuser"} pr := PRProcessor{
config := &common.AutogitConfig{ config: &common.AutogitConfig{
Reviewers: []string{"reviewer1", "reviewer2"}, Reviewers: []string{"reviewer1", "reviewer2"},
Branch: "testing", Branch: "testing",
Organization: "test-org", Organization: "test",
GitProjectName: "test-prj#testing", GitProjectName: "prj",
},
} }
git := &common.GitHandlerImpl{ event := &common.PullRequestWebhookEvent{
GitCommiter: "tester", Action: "syncronized",
GitEmail: "test@suse.com", Number: 42,
GitPath: t.TempDir(), Pull_Request: &common.PullRequest{
} Number: 42,
Base: common.Head{
processor := &PRProcessor{ Ref: "branch",
config: config, Sha: "8a6a69a4232cabda04a4d9563030aa888ff5482f75aa4c6519da32a951a072e2",
git: git, Repo: &common.Repository{
Name: "testRepo",
Owner: &common.Organization{
Username: pr.config.Organization,
},
Default_Branch: "main1",
},
},
Head: common.Head{
Ref: "branch",
Sha: "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985",
Repo: &common.Repository{
Name: "testRepo",
Default_Branch: "main1",
},
},
},
Repository: &common.Repository{
Owner: &common.Organization{
Username: pr.config.Organization,
},
},
} }
modelPR := &models.PullRequest{ modelPR := &models.PullRequest{
Index: 42, Index: 42,
Body: "PR: test-org/test-prj#24", Body: "PR: test/prj#24",
Base: &models.PRBranchInfo{ Base: &models.PRBranchInfo{
Ref: "main", Ref: "branch",
Sha: "8a6a69a4232cabda04a4d9563030aa888ff5482f75aa4c6519da32a951a072e2",
Repo: &models.Repository{ Repo: &models.Repository{
Name: "test-repo", Name: "testRepo",
Owner: &models.User{UserName: "test-org"}, Owner: &models.User{
DefaultBranch: "main", UserName: "test",
},
DefaultBranch: "main1",
}, },
}, },
Head: &models.PRBranchInfo{ Head: &models.PRBranchInfo{
Ref: "branch", Ref: "branch",
Sha: "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985", Sha: "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985",
Repo: &models.Repository{ Repo: &models.Repository{
Name: "test-repo", Name: "testRepo",
Owner: &models.User{UserName: "test-org"}, Owner: &models.User{
DefaultBranch: "main", UserName: "test",
},
DefaultBranch: "main1",
}, },
}, },
} }
PrjGitPR := &models.PullRequest{ PrjGitPR := &models.PullRequest{
Title: "some pull request", Title: "some pull request",
Body: "PR: test-org/test-repo#42", Body: "PR: test/testRepo#42",
Index: 24, Index: 24,
Base: &models.PRBranchInfo{
Ref: "testing",
Repo: &models.Repository{
Name: "test-prj",
Owner: &models.User{UserName: "test-org"},
SSHURL: "url",
},
},
Head: &models.PRBranchInfo{ Head: &models.PRBranchInfo{
Name: "PR_test-repo#42", Name: "testing",
Sha: "db8adab91edb476b9762097d10c6379aa71efd6b60933a1c0e355ddacf419a95", Sha: "db8adab91edb476b9762097d10c6379aa71efd6b60933a1c0e355ddacf419a95",
Repo: &models.Repository{ Repo: &models.Repository{
SSHURL: "url", SSHURL: "./prj",
}, },
}, },
} }
t.Run("PR_sync_request_against_PrjGit_==_no_action", func(t *testing.T) { git := &common.GitHandlerImpl{
GitCommiter: "tester",
GitEmail: "test@suse.com",
}
t.Run("PR sync request against PrjGit == no action", func(t *testing.T) {
ctl := gomock.NewController(t) ctl := gomock.NewController(t)
defer ctl.Finish() Gitea = mock_common.NewMockGitea(ctl)
gitea := mock_common.NewMockGitea(ctl)
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
Gitea = gitea
// Common expectations for FetchPRSet and downstream checks git.GitPath = t.TempDir()
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()
prjGitRepoPR := &models.PullRequest{ pr.config.GitProjectName = "testRepo"
Index: 100, event.Repository.Name = "testRepo"
Base: &models.PRBranchInfo{
Ref: "testing", if err := pr.Process(event); err != nil {
Repo: &models.Repository{ t.Error("Error PrjGit sync request. Should be no error.", err)
Name: "test-prj",
Owner: &models.User{UserName: "test-org"},
},
},
Head: &models.PRBranchInfo{
Ref: "branch",
},
} }
})
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(prjGitRepoPR, nil).AnyTimes() t.Run("Missing submodule in prjgit", func(t *testing.T) {
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes() ctl := gomock.NewController(t)
mock := mock_common.NewMockGitea(ctl)
if err := processor.Process(prjGitRepoPR); err != nil { pr.gitea = mock
t.Errorf("Expected nil error for PrjGit sync request, got %v", err) git.GitPath = t.TempDir()
pr.config.GitProjectName = "prjGit"
event.Repository.Name = "testRepo"
setupGitForTests(t, git)
oldSha := PrjGitPR.Head.Sha
defer func() { PrjGitPR.Head.Sha = oldSha }()
PrjGitPR.Head.Sha = "ab8adab91edb476b9762097d10c6379aa71efd6b60933a1c0e355ddacf419a95"
mock.EXPECT().GetPullRequest(pr.config.Organization, "testRepo", event.Pull_Request.Number).Return(modelPR, nil)
mock.EXPECT().GetPullRequest(pr.config.Organization, "prj", int64(24)).Return(PrjGitPR, nil)
err := pr.Process(event)
if err == nil || err.Error() != "Cannot fetch submodule commit id in prjgit for 'testRepo'" {
t.Error("Invalid error received.", err)
} }
}) })
t.Run("Missing PrjGit PR for the sync", func(t *testing.T) { t.Run("Missing PrjGit PR for the sync", func(t *testing.T) {
ctl := gomock.NewController(t) ctl := gomock.NewController(t)
defer ctl.Finish() mock := mock_common.NewMockGitea(ctl)
gitea := mock_common.NewMockGitea(ctl)
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
Gitea = gitea
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes() pr.gitea = mock
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes() git.GitPath = t.TempDir()
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()
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes() pr.config.GitProjectName = "prjGit"
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("not found")).AnyTimes() event.Repository.Name = "tester"
err := processor.Process(modelPR) setupGitForTests(t, git)
// It should fail because it can't find the project PR linked in body
if err == nil { expectedErr := errors.New("Missing PR should throw error")
t.Errorf("Expected error for missing project PR, got nil") mock.EXPECT().GetPullRequest(config.Organization, "tester", event.Pull_Request.Number).Return(modelPR, expectedErr)
err := pr.Process(event, git, config)
if err == nil || errors.Unwrap(err) != expectedErr {
t.Error("Invalid error received.", err)
} }
}) })
t.Run("PR sync", func(t *testing.T) { t.Run("PR sync", func(t *testing.T) {
var b bytes.Buffer
w := log.Writer()
log.SetOutput(&b)
defer log.SetOutput(w)
ctl := gomock.NewController(t) ctl := gomock.NewController(t)
defer ctl.Finish() mock := mock_common.NewMockGitea(ctl)
gitea := mock_common.NewMockGitea(ctl)
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() Gitea = mock
Gitea = gitea git.GitPath = t.TempDir()
pr.config.GitProjectName = "prjGit"
event.Repository.Name = "testRepo"
setupGitForTests(t, git) setupGitForTests(t, git)
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(PrjGitPR, nil).AnyTimes() // mock.EXPECT().GetAssociatedPrjGitPR(event).Return(PrjGitPR, nil)
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes() mock.EXPECT().GetPullRequest(pr.config.Organization, "testRepo", event.Pull_Request.Number).Return(modelPR, nil)
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes() mock.EXPECT().GetPullRequest(pr.config.Organization, "prj", int64(24)).Return(PrjGitPR, nil)
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()
// For UpdatePrjGitPR err := pr.Process(event)
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
err := processor.Process(modelPR)
if err != nil { if err != nil {
t.Errorf("Unexpected error: %v", err) t.Error("Invalid error received.", err)
t.Error(b.String())
}
// check that we actually created the branch in the prjgit
id, ok := git.GitSubmoduleCommitId("prj", "testRepo", "c097b9d1d69892d0ef2afa66d4e8abf0a1612c6f95d271a6e15d6aff1ad2854c")
if id != "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985" || !ok {
t.Error("Failed creating PR")
t.Error(b.String())
}
// does nothing on next sync of already synced data -- PR is updated
os.RemoveAll(path.Join(git.GitPath, common.DefaultGitPrj))
mock.EXPECT().GetPullRequest(pr.config.Organization, "testRepo", event.Pull_Request.Number).Return(modelPR, nil)
mock.EXPECT().GetPullRequest(pr.config.Organization, "prj", int64(24)).Return(PrjGitPR, nil)
err = pr.Process(event)
if err != nil {
t.Error("Invalid error received.", err)
t.Error(b.String())
}
// check that we actually created the branch in the prjgit
id, ok = git.GitSubmoduleCommitId("prj", "testRepo", "c097b9d1d69892d0ef2afa66d4e8abf0a1612c6f95d271a6e15d6aff1ad2854c")
if id != "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985" || !ok {
t.Error("Failed creating PR")
t.Error(b.String())
}
if id, err := git.GitBranchHead("prj", "PR_testRepo#42"); id != "c097b9d1d69892d0ef2afa66d4e8abf0a1612c6f95d271a6e15d6aff1ad2854c" || err != nil {
t.Error("no branch?", err)
t.Error(b.String())
}
if !strings.Contains(b.String(), "commitID already match - nothing to do") {
// os.CopyFS("/tmp/test", os.DirFS(git.GitPath))
t.Log(b.String())
} }
}) })
} }
*/

View File

@@ -1,915 +0,0 @@
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.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
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.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
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.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
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.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
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.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
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)
}
})
}

View File

@@ -10,6 +10,7 @@ import (
"src.opensuse.org/autogits/common" "src.opensuse.org/autogits/common"
"src.opensuse.org/autogits/common/gitea-generated/models" "src.opensuse.org/autogits/common/gitea-generated/models"
"src.opensuse.org/autogits/workflow-pr/interfaces"
) )
type DefaultStateChecker struct { type DefaultStateChecker struct {
@@ -17,11 +18,11 @@ type DefaultStateChecker struct {
checkOnStart bool checkOnStart bool
checkInterval time.Duration checkInterval time.Duration
processor PullRequestProcessor processor *RequestProcessor
i StateChecker i interfaces.StateChecker
} }
func CreateDefaultStateChecker(checkOnStart bool, processor PullRequestProcessor, gitea common.Gitea, interval time.Duration) *DefaultStateChecker { func CreateDefaultStateChecker(checkOnStart bool, processor *RequestProcessor, gitea common.Gitea, interval time.Duration) *DefaultStateChecker {
var s = &DefaultStateChecker{ var s = &DefaultStateChecker{
checkInterval: interval, checkInterval: interval,
checkOnStart: checkOnStart, checkOnStart: checkOnStart,
@@ -53,7 +54,7 @@ func (s *DefaultStateChecker) ProcessPR(pr *models.PullRequest, config *common.A
return ProcesPullRequest(pr, common.AutogitConfigs{config}) return ProcesPullRequest(pr, common.AutogitConfigs{config})
} }
func PrjGitSubmoduleCheck(config *common.AutogitConfig, git common.Git, repo string, submodules map[string]string) (prsToProcess []*PRToProcess, err error) { func PrjGitSubmoduleCheck(config *common.AutogitConfig, git common.Git, repo string, submodules map[string]string) (prsToProcess []*interfaces.PRToProcess, err error) {
nextSubmodule: nextSubmodule:
for sub, commitID := range submodules { for sub, commitID := range submodules {
common.LogDebug(" + checking", sub, commitID) common.LogDebug(" + checking", sub, commitID)
@@ -73,7 +74,7 @@ nextSubmodule:
branch = repo.DefaultBranch branch = repo.DefaultBranch
} }
prsToProcess = append(prsToProcess, &PRToProcess{ prsToProcess = append(prsToProcess, &interfaces.PRToProcess{
Org: config.Organization, Org: config.Organization,
Repo: submoduleName, Repo: submoduleName,
Branch: branch, Branch: branch,
@@ -116,7 +117,7 @@ nextSubmodule:
return prsToProcess, nil return prsToProcess, nil
} }
func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) ([]*PRToProcess, error) { func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) ([]*interfaces.PRToProcess, error) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
common.LogError("panic caught") common.LogError("panic caught")
@@ -127,7 +128,7 @@ func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) (
} }
}() }()
prsToProcess := []*PRToProcess{} prsToProcess := []*interfaces.PRToProcess{}
prjGitOrg, prjGitRepo, prjGitBranch := config.GetPrjGit() prjGitOrg, prjGitRepo, prjGitBranch := config.GetPrjGit()
common.LogInfo(" checking", prjGitOrg+"/"+prjGitRepo+"#"+prjGitBranch) common.LogInfo(" checking", prjGitOrg+"/"+prjGitRepo+"#"+prjGitBranch)
@@ -147,7 +148,7 @@ func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) (
_, err = git.GitClone(prjGitRepo, prjGitBranch, repo.SSHURL) _, err = git.GitClone(prjGitRepo, prjGitBranch, repo.SSHURL)
common.PanicOnError(err) common.PanicOnError(err)
prsToProcess = append(prsToProcess, &PRToProcess{ prsToProcess = append(prsToProcess, &interfaces.PRToProcess{
Org: prjGitOrg, Org: prjGitOrg,
Repo: prjGitRepo, Repo: prjGitRepo,
Branch: prjGitBranch, Branch: prjGitBranch,
@@ -155,8 +156,7 @@ func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) (
submodules, err := git.GitSubmoduleList(prjGitRepo, "HEAD") submodules, err := git.GitSubmoduleList(prjGitRepo, "HEAD")
// forward any package-gits referred by the project git, but don't go back // forward any package-gits referred by the project git, but don't go back
subPrs, err := PrjGitSubmoduleCheck(config, git, prjGitRepo, submodules) return PrjGitSubmoduleCheck(config, git, prjGitRepo, submodules)
return append(prsToProcess, subPrs...), err
} }
func (s *DefaultStateChecker) CheckRepos() { func (s *DefaultStateChecker) CheckRepos() {
@@ -170,8 +170,7 @@ func (s *DefaultStateChecker) CheckRepos() {
} }
}() }()
processor := s.processor.(*RequestProcessor) for org, configs := range s.processor.configuredRepos {
for org, configs := range processor.configuredRepos {
for _, config := range configs { for _, config := range configs {
if s.checkInterval > 0 { if s.checkInterval > 0 {
sleepInterval := (s.checkInterval - s.checkInterval/2) + time.Duration(rand.Int63n(int64(s.checkInterval))) sleepInterval := (s.checkInterval - s.checkInterval/2) + time.Duration(rand.Int63n(int64(s.checkInterval)))

View File

@@ -1,338 +0,0 @@
package main
import (
"errors"
"strings"
"testing"
"time"
"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 TestPrjGitSubmoduleCheck(t *testing.T) {
ctl := gomock.NewController(t)
defer ctl.Finish()
gitea := mock_common.NewMockGitea(ctl)
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
mockGit := mock_common.NewMockGit(ctl)
Gitea = gitea
config := &common.AutogitConfig{
Organization: "test-org",
Branch: "main",
}
t.Run("Submodule up to date", func(t *testing.T) {
submodules := map[string]string{
"pkg-a": "sha-1",
}
gitea.EXPECT().GetRecentCommits("test-org", "pkg-a", "main", int64(10)).Return([]*models.Commit{
{SHA: "sha-1"},
}, nil)
prs, err := PrjGitSubmoduleCheck(config, mockGit, "prj-repo", submodules)
if err != nil {
t.Fatalf("PrjGitSubmoduleCheck failed: %v", err)
}
if len(prs) != 1 || prs[0].Repo != "pkg-a" {
t.Errorf("Expected 1 PR to process for pkg-a, got %v", prs)
}
})
t.Run("Submodule behind branch", func(t *testing.T) {
submodules := map[string]string{
"pkg-a": "sha-old",
}
// sha-old is the second commit, meaning it's behind the head (sha-new)
gitea.EXPECT().GetRecentCommits("test-org", "pkg-a", "main", int64(10)).Return([]*models.Commit{
{SHA: "sha-new"},
{SHA: "sha-old"},
}, nil)
prs, err := PrjGitSubmoduleCheck(config, mockGit, "prj-repo", submodules)
if err != nil {
t.Fatalf("PrjGitSubmoduleCheck failed: %v", err)
}
if len(prs) != 1 || prs[0].Repo != "pkg-a" {
t.Errorf("Expected 1 PR to process for pkg-a, got %v", prs)
}
})
t.Run("Submodule with new commits - advance branch", func(t *testing.T) {
submodules := map[string]string{
"pkg-a": "sha-very-new",
}
// sha-very-new is NOT in recent commits
gitea.EXPECT().GetRecentCommits("test-org", "pkg-a", "main", int64(10)).Return([]*models.Commit{
{SHA: "sha-new"},
{SHA: "sha-old"},
}, nil)
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
mockGit.EXPECT().GitExecWithOutputOrPanic(gomock.Any(), "rev-list", gomock.Any(), gomock.Any()).Return("commit-1\n").AnyTimes()
mockGit.EXPECT().GitExecWithOutputOrPanic(gomock.Any(), "remote", gomock.Any(), gomock.Any(), gomock.Any()).Return("https://src.opensuse.org/test-org/pkg-a.git\n").AnyTimes()
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any()).Return().AnyTimes()
prs, err := PrjGitSubmoduleCheck(config, mockGit, "prj-repo", submodules)
if err != nil {
t.Fatalf("PrjGitSubmoduleCheck failed: %v", err)
}
if len(prs) != 1 || prs[0].Repo != "pkg-a" {
t.Errorf("Expected 1 PR to process for pkg-a, got %v", prs)
}
})
}
func TestPrjGitSubmoduleCheck_Failures(t *testing.T) {
ctl := gomock.NewController(t)
defer ctl.Finish()
gitea := mock_common.NewMockGitea(ctl)
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
mockGit := mock_common.NewMockGit(ctl)
Gitea = gitea
config := &common.AutogitConfig{
Organization: "test-org",
Branch: "main",
}
t.Run("GetRecentCommits failure", func(t *testing.T) {
submodules := map[string]string{"pkg-a": "sha-1"}
gitea.EXPECT().GetRecentCommits("test-org", "pkg-a", "main", int64(10)).Return(nil, errors.New("gitea error"))
_, err := PrjGitSubmoduleCheck(config, mockGit, "prj-repo", submodules)
if err == nil || !strings.Contains(err.Error(), "Error fetching recent commits") {
t.Errorf("Expected gitea error, got %v", err)
}
})
t.Run("SSH translation failure", func(t *testing.T) {
submodules := map[string]string{"pkg-a": "sha-new"}
gitea.EXPECT().GetRecentCommits("test-org", "pkg-a", "main", int64(10)).Return([]*models.Commit{{SHA: "sha-old"}}, nil)
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any()).Return().AnyTimes()
mockGit.EXPECT().GitExecWithOutputOrPanic(gomock.Any(), "rev-list", gomock.Any(), gomock.Any()).Return("commit-1\n").AnyTimes()
// Return invalid URL that cannot be translated to SSH
mockGit.EXPECT().GitExecWithOutputOrPanic(gomock.Any(), "remote", gomock.Any(), gomock.Any(), gomock.Any()).Return("not-a-url").AnyTimes()
_, err := PrjGitSubmoduleCheck(config, mockGit, "prj-repo", submodules)
if err == nil || !strings.Contains(err.Error(), "Cannot traslate HTTPS git URL to SSH_URL") {
t.Errorf("Expected SSH translation error, got %v", err)
}
})
}
func TestPullRequestToEventState(t *testing.T) {
tests := []struct {
state models.StateType
expected string
}{
{"open", "opened"},
{"closed", "closed"},
{"merged", "merged"},
}
for _, tt := range tests {
if got := pullRequestToEventState(tt.state); got != tt.expected {
t.Errorf("pullRequestToEventState(%v) = %v; want %v", tt.state, got, tt.expected)
}
}
}
func TestDefaultStateChecker_ProcessPR(t *testing.T) {
ctl := gomock.NewController(t)
defer ctl.Finish()
gitea := mock_common.NewMockGitea(ctl)
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
Gitea = gitea
mockGit := mock_common.NewMockGit(ctl)
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
GitHandler = mockGitGen
config := &common.AutogitConfig{
Organization: "test-org",
GitProjectName: "test-prj#main",
}
checker := CreateDefaultStateChecker(false, nil, gitea, time.Duration(0))
pr := &models.PullRequest{
Index: 1,
Base: &models.PRBranchInfo{
Ref: "main",
Repo: &models.Repository{
Name: "test-repo",
DefaultBranch: "main",
Owner: &models.User{UserName: "test-org"},
},
},
}
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil)
mockGit.EXPECT().GetPath().Return("/tmp").AnyTimes()
mockGit.EXPECT().Close().Return(nil)
// Expectations for ProcesPullRequest
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(pr, nil).AnyTimes()
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 := checker.ProcessPR(pr, config)
if err != nil {
t.Errorf("ProcessPR failed: %v", err)
}
}
func TestDefaultStateChecker_VerifyProjectState(t *testing.T) {
ctl := gomock.NewController(t)
defer ctl.Finish()
gitea := mock_common.NewMockGitea(ctl)
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
Gitea = gitea
mockGit := mock_common.NewMockGit(ctl)
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
GitHandler = mockGitGen
config := &common.AutogitConfig{
Organization: "test-org",
GitProjectName: "test-prj#main",
}
checker := CreateDefaultStateChecker(false, nil, gitea, 0)
t.Run("VerifyProjectState success", func(t *testing.T) {
mockGitGen.EXPECT().CreateGitHandler("test-org").Return(mockGit, nil)
mockGit.EXPECT().GetPath().Return("/tmp").AnyTimes()
mockGit.EXPECT().Close().Return(nil)
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), "test-org", "test-prj").Return(&models.Repository{SSHURL: "url"}, nil)
mockGit.EXPECT().GitClone("test-prj", "main", "url").Return("origin", nil)
mockGit.EXPECT().GitSubmoduleList("test-prj", "HEAD").Return(map[string]string{"pkg-a": "sha-1"}, nil)
// PrjGitSubmoduleCheck call inside
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{DefaultBranch: "main"}, nil).AnyTimes()
// Return commits where sha-1 is NOT present
gitea.EXPECT().GetRecentCommits("test-org", "pkg-a", "main", int64(10)).Return([]*models.Commit{
{SHA: "sha-new"},
}, nil).AnyTimes()
// rev-list returns empty string, so no new commits on branch relative to submodule commitID
mockGit.EXPECT().GitExecWithOutputOrPanic(gomock.Any(), "rev-list", gomock.Any(), "sha-1").Return("").AnyTimes()
// And ensure submodule update is called
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "submodule", "update", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes()
prs, err := checker.VerifyProjectState(config)
if err != nil {
t.Errorf("VerifyProjectState failed: %v", err)
}
// Expect project git + pkg-a
if len(prs) != 2 {
t.Errorf("Expected 2 PRs to process, got %d", len(prs))
}
})
t.Run("VerifyProjectState failure - CreateRepository", func(t *testing.T) {
mockGitGen.EXPECT().CreateGitHandler("test-org").Return(mockGit, nil)
mockGit.EXPECT().GetPath().Return("/tmp").AnyTimes()
mockGit.EXPECT().Close().Return(nil)
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), "test-org", "test-prj").Return(nil, errors.New("gitea error"))
_, err := checker.VerifyProjectState(config)
if err == nil || !strings.Contains(err.Error(), "Error fetching or creating") {
t.Errorf("Expected gitea error, got %v", err)
}
})
}
func TestDefaultStateChecker_CheckRepos(t *testing.T) {
ctl := gomock.NewController(t)
defer ctl.Finish()
gitea := mock_common.NewMockGitea(ctl)
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
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},
},
}
checker := CreateDefaultStateChecker(false, nil, gitea, 0)
checker.processor = reqProc
t.Run("CheckRepos success with PRs", func(t *testing.T) {
// Mock VerifyProjectState results
// TODO: fix below
// Since we can't easily mock the internal call s.i.VerifyProjectState because s.i is the checker itself
// and VerifyProjectState is not a separate interface method in repo_check.go (it is, but used internally).
// Actually DefaultStateChecker implements i (StateChecker interface).
mockGitGen.EXPECT().CreateGitHandler("test-org").Return(mockGit, nil).AnyTimes()
mockGit.EXPECT().GetPath().Return("/tmp").AnyTimes()
mockGit.EXPECT().Close().Return(nil).AnyTimes()
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "url"}, nil).AnyTimes()
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{}, nil).AnyTimes()
// GetRecentPullRequests for the project git
gitea.EXPECT().GetRecentPullRequests("test-org", "test-prj", "main").Return([]*models.PullRequest{
{Index: 1, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "test-prj", Owner: &models.User{UserName: "test-org"}}}},
}, nil).AnyTimes()
// ProcessPR calls for the found PR
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.PullRequest{
Index: 1,
Base: &models.PRBranchInfo{
Ref: "main",
Repo: &models.Repository{Name: "test-prj", Owner: &models.User{UserName: "test-org"}},
},
}, nil).AnyTimes()
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()
checker.CheckRepos()
})
t.Run("CheckRepos failure - GetRecentPullRequests", func(t *testing.T) {
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "url"}, nil).AnyTimes()
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{}, nil).AnyTimes()
gitea.EXPECT().GetRecentPullRequests("test-org", "test-prj", "main").Return(nil, errors.New("gitea error")).AnyTimes()
checker.CheckRepos()
// Should log error and continue (no panic)
})
}

View File

@@ -10,6 +10,7 @@ import (
"src.opensuse.org/autogits/common" "src.opensuse.org/autogits/common"
"src.opensuse.org/autogits/common/gitea-generated/models" "src.opensuse.org/autogits/common/gitea-generated/models"
mock_common "src.opensuse.org/autogits/common/mock" mock_common "src.opensuse.org/autogits/common/mock"
mock_main "src.opensuse.org/autogits/workflow-pr/mock"
) )
func TestRepoCheck(t *testing.T) { func TestRepoCheck(t *testing.T) {
@@ -21,15 +22,16 @@ func TestRepoCheck(t *testing.T) {
t.Run("Consistency Check On Start", func(t *testing.T) { t.Run("Consistency Check On Start", func(t *testing.T) {
c := CreateDefaultStateChecker(true, nil, nil, 100) c := CreateDefaultStateChecker(true, nil, nil, 100)
ctl := gomock.NewController(t) ctl := gomock.NewController(t)
state := NewMockStateChecker(ctl) state := mock_main.NewMockStateChecker(ctl)
c.i = state c.i = state
state.EXPECT().CheckRepos().Do(func() { state.EXPECT().CheckRepos().Do(func() error {
// only checkOnStart has checkInterval = 0 // only checkOnStart has checkInterval = 0
if c.checkInterval != 0 { if c.checkInterval != 0 {
t.Fail() t.Fail()
} }
c.exitCheckLoop = true c.exitCheckLoop = true
return nil
}) })
c.ConsistencyCheckProcess() c.ConsistencyCheckProcess()
@@ -41,11 +43,11 @@ func TestRepoCheck(t *testing.T) {
t.Run("No consistency Check On Start", func(t *testing.T) { t.Run("No consistency Check On Start", func(t *testing.T) {
c := CreateDefaultStateChecker(true, nil, nil, 100) c := CreateDefaultStateChecker(true, nil, nil, 100)
ctl := gomock.NewController(t) ctl := gomock.NewController(t)
state := NewMockStateChecker(ctl) state := mock_main.NewMockStateChecker(ctl)
c.i = state c.i = state
nCalls := 10 nCalls := 10
state.EXPECT().CheckRepos().Do(func() { state.EXPECT().CheckRepos().Do(func() error {
// only checkOnStart has checkInterval = 0 // only checkOnStart has checkInterval = 0
if c.checkInterval != 100 { if c.checkInterval != 100 {
t.Fail() t.Fail()
@@ -55,6 +57,7 @@ func TestRepoCheck(t *testing.T) {
if nCalls == 0 { if nCalls == 0 {
c.exitCheckLoop = true c.exitCheckLoop = true
} }
return nil
}).Times(nCalls) }).Times(nCalls)
c.checkOnStart = false c.checkOnStart = false
@@ -63,9 +66,8 @@ func TestRepoCheck(t *testing.T) {
t.Run("CheckRepos() calls CheckProjectState() for each project", func(t *testing.T) { t.Run("CheckRepos() calls CheckProjectState() for each project", func(t *testing.T) {
ctl := gomock.NewController(t) ctl := gomock.NewController(t)
state := NewMockStateChecker(ctl) state := mock_main.NewMockStateChecker(ctl)
gitea := mock_common.NewMockGitea(ctl) gitea := mock_common.NewMockGitea(ctl)
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
config1 := &common.AutogitConfig{ config1 := &common.AutogitConfig{
GitProjectName: "git_repo1", GitProjectName: "git_repo1",
@@ -95,14 +97,15 @@ func TestRepoCheck(t *testing.T) {
state.EXPECT().VerifyProjectState(configs.configuredRepos["repo2_org"][0]) state.EXPECT().VerifyProjectState(configs.configuredRepos["repo2_org"][0])
state.EXPECT().VerifyProjectState(configs.configuredRepos["repo3_org"][0]) state.EXPECT().VerifyProjectState(configs.configuredRepos["repo3_org"][0])
c.CheckRepos() if err := c.CheckRepos(); err != nil {
t.Error(err)
}
}) })
t.Run("CheckRepos errors", func(t *testing.T) { t.Run("CheckRepos errors", func(t *testing.T) {
ctl := gomock.NewController(t) ctl := gomock.NewController(t)
state := NewMockStateChecker(ctl) state := mock_main.NewMockStateChecker(ctl)
gitea := mock_common.NewMockGitea(ctl) gitea := mock_common.NewMockGitea(ctl)
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
config1 := &common.AutogitConfig{ config1 := &common.AutogitConfig{
GitProjectName: "git_repo1", GitProjectName: "git_repo1",
@@ -122,7 +125,11 @@ func TestRepoCheck(t *testing.T) {
err := errors.New("test error") err := errors.New("test error")
state.EXPECT().VerifyProjectState(configs.configuredRepos["repo1_org"][0]).Return(nil, err) state.EXPECT().VerifyProjectState(configs.configuredRepos["repo1_org"][0]).Return(nil, err)
c.CheckRepos() r := c.CheckRepos()
if !errors.Is(r, err) {
t.Error(err)
}
}) })
} }
@@ -147,7 +154,6 @@ func TestVerifyProjectState(t *testing.T) {
t.Run("Project state with no PRs", func(t *testing.T) { t.Run("Project state with no PRs", func(t *testing.T) {
ctl := gomock.NewController(t) ctl := gomock.NewController(t)
gitea := mock_common.NewMockGitea(ctl) gitea := mock_common.NewMockGitea(ctl)
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
git := &common.GitHandlerImpl{ git := &common.GitHandlerImpl{
GitCommiter: "TestCommiter", GitCommiter: "TestCommiter",
@@ -171,11 +177,11 @@ func TestVerifyProjectState(t *testing.T) {
}, },
} }
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{ gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), config1.GitProjectName).Return(&models.Repository{
SSHURL: "./prj", SSHURL: "./prj",
}, nil).AnyTimes() }, nil)
gitea.EXPECT().GetRecentPullRequests(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullRequest{}, nil).AnyTimes() gitea.EXPECT().GetRecentPullRequests(org, "testRepo", "testing")
gitea.EXPECT().GetRecentCommits(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Commit{}, nil).AnyTimes() gitea.EXPECT().GetRecentCommits(org, "testRepo", "testing", gomock.Any())
c := CreateDefaultStateChecker(false, configs, gitea, 0) c := CreateDefaultStateChecker(false, configs, gitea, 0)
/* /*
@@ -193,7 +199,7 @@ func TestVerifyProjectState(t *testing.T) {
t.Run("Project state with 1 PRs that doesn't trigger updates", func(t *testing.T) { t.Run("Project state with 1 PRs that doesn't trigger updates", func(t *testing.T) {
ctl := gomock.NewController(t) ctl := gomock.NewController(t)
gitea := mock_common.NewMockGitea(ctl) gitea := mock_common.NewMockGitea(ctl)
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() process := mock_main.NewMockPullRequestProcessor(ctl)
git := &common.GitHandlerImpl{ git := &common.GitHandlerImpl{
GitCommiter: "TestCommiter", GitCommiter: "TestCommiter",
@@ -217,11 +223,11 @@ func TestVerifyProjectState(t *testing.T) {
}, },
} }
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{ gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), config1.GitProjectName).Return(&models.Repository{
SSHURL: "./prj", SSHURL: "./prj",
}, nil).AnyTimes() }, nil)
gitea.EXPECT().GetRecentPullRequests(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullRequest{ gitea.EXPECT().GetRecentPullRequests(org, "testRepo", "testing").Return([]*models.PullRequest{
&models.PullRequest{ &models.PullRequest{
ID: 1234, ID: 1234,
URL: "url here", URL: "url here",
@@ -253,16 +259,16 @@ func TestVerifyProjectState(t *testing.T) {
}, },
}, },
}, },
}, nil).AnyTimes() }, nil)
gitea.EXPECT().GetRecentCommits(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Commit{}, nil).AnyTimes() gitea.EXPECT().GetRecentCommits(org, "testRepo", "testing", gomock.Any())
c := CreateDefaultStateChecker(false, configs, gitea, 0) c := CreateDefaultStateChecker(false, configs, gitea, 0)
/* /*
c.git = &testGit{ c.git = &testGit{
git: git, git: git,
}*/ }*/
// process.EXPECT().Process(gomock.Any()) process.EXPECT().Process(gomock.Any(), gomock.Any(), gomock.Any())
// c.processor.Opened = process // c.processor.Opened = process
_, err := c.VerifyProjectState(configs.configuredRepos[org][0]) _, err := c.VerifyProjectState(configs.configuredRepos[org][0])

View File

@@ -1,23 +0,0 @@
package main
import (
"src.opensuse.org/autogits/common"
"src.opensuse.org/autogits/common/gitea-generated/models"
)
//go:generate mockgen -source=state_checker.go -destination=mock_state_checker.go -typed -package main
type StateChecker interface {
VerifyProjectState(configs *common.AutogitConfig) ([]*PRToProcess, error)
CheckRepos()
ConsistencyCheckProcess() error
}
type PullRequestProcessor interface {
Process(req *models.PullRequest) error
}
type PRToProcess struct {
Org, Repo, Branch string
}

View File

@@ -1,88 +0,0 @@
package main
import (
"fmt"
"os/exec"
"path/filepath"
"testing"
"src.opensuse.org/autogits/common"
)
const LocalCMD = "---"
func gitExecs(t *testing.T, git *common.GitHandlerImpl, cmds [][]string) {
for _, cmd := range cmds {
if cmd[0] == LocalCMD {
command := exec.Command(cmd[2], cmd[3:]...)
command.Dir = filepath.Join(git.GitPath, cmd[1])
command.Stdin = nil
command.Env = append([]string{"GIT_CONFIG_COUNT=1", "GIT_CONFIG_KEY_1=protocol.file.allow", "GIT_CONFIG_VALUE_1=always"}, common.ExtraGitParams...)
_, err := command.CombinedOutput()
if err != nil {
t.Errorf(" *** error: %v\n", err)
}
} else {
git.GitExecOrPanic(cmd[0], cmd[1:]...)
}
}
}
func commandsForPackages(dir, prefix string, startN, endN int) [][]string {
commands := make([][]string, (endN-startN+2)*6)
if dir == "" {
dir = "."
}
cmdIdx := 0
for idx := startN; idx <= endN; idx++ {
pkgDir := fmt.Sprintf("%s%d", prefix, idx)
commands[cmdIdx+0] = []string{"", "init", "-q", "--object-format", "sha256", "-b", "testing", pkgDir}
commands[cmdIdx+1] = []string{LocalCMD, pkgDir, "/usr/bin/touch", "testFile"}
commands[cmdIdx+2] = []string{pkgDir, "add", "testFile"}
commands[cmdIdx+3] = []string{pkgDir, "commit", "-m", "added testFile"}
commands[cmdIdx+4] = []string{pkgDir, "config", "receive.denyCurrentBranch", "ignore"}
commands[cmdIdx+5] = []string{"prj", "submodule", "add", filepath.Join("..", pkgDir), filepath.Join(dir, pkgDir)}
cmdIdx += 6
}
// add all the submodules to the prj
commands[cmdIdx+0] = []string{"prj", "commit", "-a", "-m", "adding subpackages"}
return commands
}
func setupGitForTests(t *testing.T, git *common.GitHandlerImpl) {
common.ExtraGitParams = []string{
"TZ=UTC",
"GIT_CONFIG_COUNT=1",
"GIT_CONFIG_KEY_0=protocol.file.allow",
"GIT_CONFIG_VALUE_0=always",
"GIT_AUTHOR_NAME=testname",
"GIT_AUTHOR_EMAIL=test@suse.com",
"GIT_AUTHOR_DATE='2005-04-07T22:13:13'",
"GIT_COMMITTER_NAME=testname",
"GIT_COMMITTER_EMAIL=test@suse.com",
"GIT_COMMITTER_DATE='2005-04-07T22:13:13'",
}
gitExecs(t, git, [][]string{
{"", "init", "-q", "--object-format", "sha256", "-b", "testing", "prj"},
{"", "init", "-q", "--object-format", "sha256", "-b", "testing", "foo"},
{LocalCMD, "foo", "/usr/bin/touch", "file1"},
{"foo", "add", "file1"},
{"foo", "commit", "-m", "first commit"},
{"prj", "config", "receive.denyCurrentBranch", "ignore"},
{"prj", "submodule", "init"},
{"prj", "submodule", "add", "../foo", "testRepo"},
{"prj", "add", ".gitmodules", "testRepo"},
{"prj", "commit", "-m", "First instance"},
{"prj", "submodule", "deinit", "testRepo"},
{LocalCMD, "foo", "/usr/bin/touch", "file2"},
{"foo", "add", "file2"},
{"foo", "commit", "-m", "added file2"},
})
}