New package handling -- stacked with previous PR tests #108

Open
adamm wants to merge 26 commits from new-packages into main
29 changed files with 3422 additions and 503 deletions

View File

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

View File

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

View File

@@ -65,6 +65,7 @@ const (
Label_StagingAuto = "staging/Auto"
Label_ReviewPending = "review/Pending"
Label_ReviewDone = "review/Done"
Label_NewRepository = "new/New Repository"
)
func LabelKey(tag_value string) string {

View File

@@ -42,6 +42,7 @@ type GitSubmoduleLister interface {
type GitDirectoryLister interface {
GitDirectoryList(gitPath, commitId string) (dirlist map[string]string, err error)
GitDirectoryContentList(gitPath, commitId string) (dirlist map[string]string, err error)
}
type GitStatusLister interface {
@@ -272,7 +273,11 @@ func (e *GitHandlerImpl) GitClone(repo, branch, remoteUrl string) (string, error
LogDebug("branch", branch)
}
*/
args := []string{"fetch", "--prune", remoteName, branch}
args := []string{"fetch", "--prune", remoteName}
if len(branch) > 0 {
args = append(args, branch)
}
if strings.TrimSpace(e.GitExecWithOutputOrPanic(repo, "rev-parse", "--is-shallow-repository")) == "true" {
args = slices.Insert(args, 1, "--unshallow")
}
@@ -787,7 +792,7 @@ func (e *GitHandlerImpl) GitCatFile(cwd, commitId, filename string) (data []byte
return
}
// return (filename) -> (hash) map for all submodules
// return (directory) -> (hash) map for all submodules
func (e *GitHandlerImpl) GitDirectoryList(gitPath, commitId string) (directoryList map[string]string, err error) {
var done sync.Mutex
directoryList = make(map[string]string)
@@ -861,6 +866,82 @@ func (e *GitHandlerImpl) GitDirectoryList(gitPath, commitId string) (directoryLi
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
func (e *GitHandlerImpl) GitSubmoduleList(gitPath, commitId string) (submoduleList map[string]string, err error) {
var done sync.Mutex

View File

@@ -24,6 +24,7 @@ import (
"os"
"os/exec"
"path"
"path/filepath"
"slices"
"strings"
"testing"
@@ -596,3 +597,66 @@ 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,6 +75,10 @@ type GiteaLabelSettter interface {
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 {
GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error)
}
@@ -147,6 +151,7 @@ type GiteaReviewRequester interface {
type GiteaReviewUnrequester interface {
UnrequestReview(org, repo string, id int64, reviwers ...string) error
UpdateIssue(org, repo string, idx int64, options *models.EditIssueOption) (*models.Issue, error)
}
type GiteaReviewer interface {
@@ -200,6 +205,7 @@ type Gitea interface {
GiteaSetRepoOptions
GiteaLabelGetter
GiteaLabelSettter
GiteaIssueFetcher
GetNotifications(Type string, since *time.Time) ([]*models.NotificationThread, error)
GetDoneNotifications(Type string, page int64) ([]*models.NotificationThread, error)
@@ -508,6 +514,26 @@ func (gitea *GiteaTransport) SetLabels(owner, repo string, idx int64, labels []s
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 (
GiteaNotificationType_Pull = "Pull"
)
@@ -715,6 +741,7 @@ func (gitea *GiteaTransport) CreatePullRequestIfNotExist(repo *models.Repository
)
if err != nil {
LogError("owner:", repo.Owner.UserName, " repo:", repo.Name, " body:", prOptions)
return nil, fmt.Errorf("Cannot create pull request. %w", err), true
}

View File

@@ -142,6 +142,45 @@ func (m *MockGitDirectoryLister) EXPECT() *MockGitDirectoryListerMockRecorder {
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.
func (m *MockGitDirectoryLister) GitDirectoryList(gitPath, commitId string) (map[string]string, error) {
m.ctrl.T.Helper()
@@ -563,6 +602,45 @@ func (c *MockGitGitDiffCall) DoAndReturn(f func(string, string, string) (string,
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.
func (m *MockGit) GitDirectoryList(gitPath, commitId string) (map[string]string, error) {
m.ctrl.T.Helper()

View File

@@ -144,6 +144,69 @@ func (c *MockGiteaLabelSettterSetLabelsCall) DoAndReturn(f func(string, string,
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.
type MockGiteaTimelineFetcher struct {
ctrl *gomock.Controller
@@ -1443,6 +1506,45 @@ func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterUnrequestReviewCall) Do
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.
type MockGiteaUnreviewTimelineFetcher struct {
ctrl *gomock.Controller
@@ -1549,6 +1651,45 @@ func (c *MockGiteaUnreviewTimelineFetcherUnrequestReviewCall) DoAndReturn(f func
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.
type MockGiteaReviewRequester struct {
ctrl *gomock.Controller
@@ -1684,6 +1825,45 @@ func (c *MockGiteaReviewUnrequesterUnrequestReviewCall) DoAndReturn(f func(strin
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.
type MockGiteaReviewer struct {
ctrl *gomock.Controller
@@ -2478,6 +2658,45 @@ func (c *MockGiteaGetDoneNotificationsCall) DoAndReturn(f func(string, int64) ([
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.
func (m *MockGitea) GetIssueComments(org, project string, issueNo int64) ([]*models.Comment, error) {
m.ctrl.T.Helper()
@@ -3306,6 +3525,45 @@ func (c *MockGiteaUnrequestReviewCall) DoAndReturn(f func(string, string, int64,
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.
func (m *MockGitea) UpdatePullRequest(org, repo string, num int64, options *models.EditPullRequestOption) (*models.PullRequest, error) {
m.ctrl.T.Helper()

View File

@@ -6,7 +6,9 @@ import (
"fmt"
"os"
"path"
"regexp"
"slices"
"strconv"
"strings"
"src.opensuse.org/autogits/common/gitea-generated/client/repository"
@@ -650,6 +652,8 @@ func (rs *PRSet) Merge(gitea GiteaReviewUnrequester, git Git) error {
}
// FF all non-prj git and unrequest reviews.
newRepoIssues := make(map[int64]string) // issue index -> org/repo
for _, prinfo := range rs.PRs {
// remove pending review requests
repo := prinfo.PR.Base.Repo
@@ -671,6 +675,15 @@ func (rs *PRSet) Merge(gitea GiteaReviewUnrequester, git Git) error {
if rs.IsPrjGitPR(prinfo.PR) {
continue
}
isNewRepo := false
for _, l := range prinfo.PR.Labels {
if l.Name == Label_NewRepository {
isNewRepo = true
break
}
}
br := rs.Config.Branch
if len(br) == 0 {
// if branch is unspecified, take it from the PR as it
@@ -679,11 +692,30 @@ func (rs *PRSet) Merge(gitea GiteaReviewUnrequester, git Git) error {
} else if br != prinfo.PR.Base.Name {
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)
PanicOnError(err)
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
@@ -698,12 +730,37 @@ func (rs *PRSet) Merge(gitea GiteaReviewUnrequester, git Git) error {
}
repo := prinfo.PR.Base.Repo
isNewRepo := false
for _, l := range prinfo.PR.Labels {
if l.Name == Label_NewRepository {
isNewRepo = true
break
}
}
if !IsDryRun {
git.GitExecOrPanic(repo.Name, "push", prinfo.RemoteName)
if isNewRepo {
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 {
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
}

121
common/pr_linkage_test.go Normal file
View File

@@ -0,0 +1,121 @@
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)
// 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)
// 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

@@ -0,0 +1,94 @@
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,8 +48,6 @@ func reviewsToTimeline(reviews []*models.PullReview) []*models.TimelineComment {
}
func TestPR(t *testing.T) {
return
baseConfig := common.AutogitConfig{
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
Branch: "branch",
@@ -80,21 +78,21 @@ func TestPR(t *testing.T) {
{
name: "Error fetching PullRequest",
data: []prdata{
{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")},
{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")},
},
prjGitPRIndex: -1,
},
{
name: "Error fetching PullRequest in PrjGit",
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_err: errors.New("missing PR")},
{pr: &models.PullRequest{Body: "", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, 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_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"}},
},
},
{
name: "Error fetching prjgit",
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: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "open"}},
},
resLen: 1,
prjGitPRIndex: -1,
@@ -102,8 +100,20 @@ func TestPR(t *testing.T) {
{
name: "Review set is consistent",
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: test/repo#42", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, 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"},
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,
prjGitPRIndex: 1,
@@ -113,8 +123,21 @@ func TestPR(t *testing.T) {
{
name: "Review set is consistent: 1pkg",
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: test/repo#42", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, 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"},
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,
prjGitPRIndex: 1,
@@ -123,9 +146,22 @@ func TestPR(t *testing.T) {
{
name: "Review set is consistent: 2pkg",
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: "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: "some other desc\nPR: foo/fer#33", Index: 41, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo2", 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"},
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\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,
prjGitPRIndex: 1,
@@ -135,7 +171,7 @@ func TestPR(t *testing.T) {
name: "Review set of prjgit PR is consistent",
data: []prdata{
{
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"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -154,10 +190,23 @@ func TestPR(t *testing.T) {
{
name: "Review set is consistent: 2pkg",
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: test/repo2#41", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, State: "opened"}},
{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"}},
{
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"},
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\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,
prjGitPRIndex: 2,
@@ -167,7 +216,7 @@ func TestPR(t *testing.T) {
name: "WIP PR is not approved",
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"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -184,10 +233,9 @@ 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"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -195,7 +243,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: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -212,16 +260,15 @@ func TestPR(t *testing.T) {
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
Branch: "branch",
Organization: "foo",
GitProjectName: "barPrj",
GitProjectName: "barPrj#master",
ManualMergeOnly: true,
})
},
},
}},
{
name: "Manual review is done, via PrjGit",
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"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "merge ok", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -229,7 +276,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: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -246,7 +293,7 @@ func TestPR(t *testing.T) {
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
Branch: "branch",
Organization: "foo",
GitProjectName: "barPrj",
GitProjectName: "barPrj#master",
ManualMergeOnly: true,
})
},
@@ -255,7 +302,7 @@ func TestPR(t *testing.T) {
name: "Manual review is done, via PrjGit",
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"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "merge ok", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -263,7 +310,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: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -280,7 +327,7 @@ func TestPR(t *testing.T) {
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
Branch: "branch",
Organization: "foo",
GitProjectName: "barPrj",
GitProjectName: "barPrj#master",
ManualMergeOnly: true,
ManualMergeProject: true,
})
@@ -290,7 +337,7 @@ func TestPR(t *testing.T) {
name: "Manual review is not done, via PrjGit",
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"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "merge ok", User: &models.User{UserName: "notm2"}, State: common.ReviewStateApproved},
{Body: "merge not ok", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
@@ -299,7 +346,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: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -316,7 +363,7 @@ func TestPR(t *testing.T) {
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
Branch: "branch",
Organization: "foo",
GitProjectName: "barPrj",
GitProjectName: "barPrj#master",
ManualMergeOnly: true,
ManualMergeProject: true,
})
@@ -326,7 +373,7 @@ func TestPR(t *testing.T) {
name: "Manual review is done via PackageGit",
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"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -334,7 +381,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: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "Merge ok", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -351,7 +398,7 @@ func TestPR(t *testing.T) {
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
Branch: "branch",
Organization: "foo",
GitProjectName: "barPrj",
GitProjectName: "barPrj#master",
ManualMergeOnly: true,
})
},
@@ -360,7 +407,7 @@ func TestPR(t *testing.T) {
name: "Manual review done via PkgGits",
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"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -368,7 +415,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: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "Merge OK!", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -376,7 +423,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: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "merge ok", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -393,7 +440,7 @@ func TestPR(t *testing.T) {
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
Branch: "branch",
Organization: "foo",
GitProjectName: "barPrj",
GitProjectName: "barPrj#master",
ManualMergeOnly: true,
})
},
@@ -402,7 +449,7 @@ func TestPR(t *testing.T) {
name: "Manual review done via PkgGits not allowed",
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"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -410,7 +457,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: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "Merge OK!", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -418,7 +465,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: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "merge ok", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -435,7 +482,7 @@ func TestPR(t *testing.T) {
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
Branch: "branch",
Organization: "foo",
GitProjectName: "barPrj",
GitProjectName: "barPrj#master",
ManualMergeOnly: true,
ManualMergeProject: true,
})
@@ -445,7 +492,7 @@ func TestPR(t *testing.T) {
name: "Manual review is is missing on one PR",
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"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -453,7 +500,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: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -461,7 +508,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: "opened"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
@@ -478,7 +525,7 @@ func TestPR(t *testing.T) {
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
Branch: "branch",
Organization: "foo",
GitProjectName: "barPrj",
GitProjectName: "barPrj#master",
ManualMergeOnly: true,
})
},
@@ -487,7 +534,7 @@ func TestPR(t *testing.T) {
name: "PR is approved with negative optional review",
data: []prdata{
{
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"},
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"},
reviews: []*models.PullReview{
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
@@ -505,7 +552,7 @@ func TestPR(t *testing.T) {
Reviewers: []string{"+super1", "*super2", "m1", "-m2", "~*bot"},
Branch: "branch",
Organization: "foo",
GitProjectName: "barPrj",
GitProjectName: "barPrj#master",
}
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &config)
},
@@ -519,27 +566,31 @@ func TestPR(t *testing.T) {
review_mock := mock_common.NewMockGiteaPRChecker(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.prjGitPRIndex >= 0 {
pr_mock.EXPECT().GetPullRequest(baseConfig.Organization, baseConfig.GitProjectName, test.prjGitPRIndex).
Return(test.data[test.prjGitPRIndex].pr, test.data[test.prjGitPRIndex].pr_err)
pr_mock.EXPECT().GetPullRequest(prjGitOrg, prjGitRepo, int64(test.data[test.prjGitPRIndex].pr.Index)).
Return(test.data[test.prjGitPRIndex].pr, test.data[test.prjGitPRIndex].pr_err).AnyTimes()
} else if test.prjGitPRIndex < 0 {
// no prjgit PR
pr_mock.EXPECT().GetPullRequest(baseConfig.Organization, baseConfig.GitProjectName, gomock.Any()).
Return(nil, nil)
pr_mock.EXPECT().GetPullRequest(prjGitOrg, prjGitRepo, gomock.Any()).
Return(nil, nil).AnyTimes()
}
}
var test_err error
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()
if data.pr_err != nil {
test_err = data.pr_err
// test_err is not used and was causing a build error.
// 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 {
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()
}
@@ -552,27 +603,12 @@ func TestPR(t *testing.T) {
res, err = common.FetchPRSet("test", pr_mock, "test", "repo", 42, &baseConfig)
}
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)
}
if res == nil {
return
}
if test.resLen != len(res.PRs) {
t.Error("expected result len", test.resLen, "but got", len(res.PRs))
if len(res.PRs) != test.resLen {
t.Errorf("Test Case '%s': expected result len %d but got %d", test.name, test.resLen, len(res.PRs))
}
PrjGitPR, err := res.GetPrjGitPR()
@@ -583,6 +619,9 @@ func TestPR(t *testing.T) {
}
pr_found := false
if test.prjGitPRIndex >= 0 {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
for i := range test.data {
if PrjGitPR.PR == test.data[i].pr && i == test.prjGitPRIndex {
t.Log("found at index", i)
@@ -1126,7 +1165,6 @@ func TestFindMissingAndExtraReviewers(t *testing.T) {
}
func TestPRMerge(t *testing.T) {
t.Skip("FAIL: No PrjGit PR found, missing calls")
repoDir := t.TempDir()
cwd, _ := os.Getwd()
@@ -1136,7 +1174,11 @@ func TestPRMerge(t *testing.T) {
t.Fatal(string(out))
}
t.Skip("No tests of PRMerge yet")
return
common.ExtraGitParams = []string{
"TZ=UTC",
"GIT_CONFIG_COUNT=1",
"GIT_CONFIG_KEY_0=protocol.file.allow",
"GIT_CONFIG_VALUE_0=always",
@@ -1151,7 +1193,7 @@ func TestPRMerge(t *testing.T) {
config := &common.AutogitConfig{
Organization: "org",
GitProjectName: "org/prj#master",
GitProjectName: "org/prj#main",
}
tests := []struct {
@@ -1163,8 +1205,10 @@ func TestPRMerge(t *testing.T) {
name: "Merge base not merged in main",
pr: &models.PullRequest{
Index: 1,
Base: &models.PRBranchInfo{
Sha: "e8b0de43d757c96a9d2c7101f4bff404e322f53a1fa4041fb85d646110c38ad4", // "base_add_b1"
Name: "main",
Sha: "96515c092626c716a4613ba4f68a8d1cc4894317658342c450e656390f524ec3", // "base_add_b1"
Repo: &models.Repository{
Name: "prj",
Owner: &models.User{
@@ -1174,7 +1218,7 @@ func TestPRMerge(t *testing.T) {
},
},
Head: &models.PRBranchInfo{
Sha: "88584433de1c917c1d773f62b82381848d882491940b5e9b427a540aa9057d9a", // "base_add_b2"
Sha: "4119fc725dc11cdf11f982d5bb0a8ba2a138f1180c4323862a18b8e08def5603", // "base_add_b2"
},
},
mergeError: "Aborting merge",
@@ -1183,8 +1227,10 @@ func TestPRMerge(t *testing.T) {
name: "Merge conflict in modules, auto-resolved",
pr: &models.PullRequest{
Index: 1,
Base: &models.PRBranchInfo{
Sha: "4fbd1026b2d7462ebe9229a49100c11f1ad6555520a21ba515122d8bc41328a8",
Name: "main",
Sha: "85f59f7aa732b742e58b48356cd46cb1ab1b5c4349eb5c0eda324e2dbea8f9e7",
Repo: &models.Repository{
Name: "prj",
Owner: &models.User{
@@ -1194,7 +1240,7 @@ func TestPRMerge(t *testing.T) {
},
},
Head: &models.PRBranchInfo{
Sha: "88584433de1c917c1d773f62b82381848d882491940b5e9b427a540aa9057d9a", // "base_add_b2"
Sha: "4119fc725dc11cdf11f982d5bb0a8ba2a138f1180c4323862a18b8e08def5603", // "base_add_b2"
},
},
},
@@ -1206,11 +1252,13 @@ func TestPRMerge(t *testing.T) {
mock := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl)
reviewUnrequestMock := mock_common.NewMockGiteaReviewUnrequester(ctl)
reviewUnrequestMock.EXPECT().UnrequestReview(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
reviewUnrequestMock.EXPECT().UnrequestReview(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
testDir := t.TempDir()
t.Log("dir:", testDir)
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)
if err != nil {
@@ -1231,11 +1279,11 @@ func TestPRMerge(t *testing.T) {
}
func TestPRChanges(t *testing.T) {
t.Skip("FAIL: unexpected calls, missing calls")
tests := []struct {
name string
PRs []*models.PullRequest
PrjPRs *models.PullRequest
name string
PRs []*models.PullRequest
PrjPRs *models.PullRequest
Timeline []*models.TimelineComment
}{
{
name: "Pkg PR is closed",
@@ -1247,10 +1295,22 @@ func TestPRChanges(t *testing.T) {
},
},
PrjPRs: &models.PullRequest{
Index: 42,
Title: "some PR",
Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "prjgit", Owner: &models.User{UserName: "org"}}},
Base: &models.PRBranchInfo{Name: "branch", Repo: &models.Repository{Name: "prjgit", Owner: &models.User{UserName: "org"}}},
Body: "PR: org/repo#42",
State: "opened",
State: "open",
},
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",
},
},
},
},
}
@@ -1263,9 +1323,13 @@ func TestPRChanges(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
ctl := gomock.NewController(t)
mock_fetcher := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl)
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 {
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)

View File

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

View File

@@ -1,18 +0,0 @@
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

@@ -0,0 +1,189 @@
package main
import (
"fmt"
"strings"
"src.opensuse.org/autogits/common"
"src.opensuse.org/autogits/common/gitea-generated/models"
)
func ProcessIssue(issue *models.Issue, configs []*common.AutogitConfig) error {
title := issue.Title
/*
org := issue.Repository.Owner
repo := issue.Repository.Name
idx := issue.Index
*/
const BranchPrefix = "refs/heads/"
branch := issue.Ref
if strings.HasPrefix(branch, BranchPrefix) {
branch = strings.TrimPrefix(branch, BranchPrefix)
} else {
common.LogDebug("Invalid branch specified:", branch, ". Using default.")
branch = ""
}
// out, _ := json.MarshalIndent(issue, "", " ")
// common.LogDebug(string(out))
common.LogDebug("issue processing:", common.IssueToString(issue), "@", branch)
if len(title) > 5 && strings.EqualFold(title[0:5], "[ADD]") {
// 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
}
org := issue.Repository.Owner
repo := issue.Repository.Name
config := common.AutogitConfigs(configs).GetPrjGitConfig(org, repo, branch)
if config == nil {
return fmt.Errorf("Cannot find config for %s/%s#%s", org, repo, branch)
}
if len(branch) == 0 {
branch = config.Branch
}
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 {
Gitea.SetLabels(org, repo, issue.Index, []string{config.Label(common.Label_NewRepository)})
}
common.LogDebug(" # Done for now with this repo")
continue
}
srcRepo, err := Gitea.GetRepository(nr.Organization, nr.Repository)
if err != nil {
return err
}
if srcRepo == nil {
common.LogError("Source repository not found:", nr.Organization+"/"+nr.Repository)
continue
}
if srcRepo.Parent == nil {
common.LogError("Source has no parents.")
continue
}
if len(nr.Branch) == 0 {
nr.Branch = srcRepo.DefaultBranch
}
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
CreateEmptyBranch := true
if err := git.GitExec(nr.PackageName, "checkout", "-B", nr.Branch, remoteName+"/"+nr.Branch); err == nil {
// verify that our branch is empty
dir, err := git.GitDirectoryContentList(nr.PackageName, "HEAD")
if err != nil {
common.LogError("Failed to list directory of", nr.Branch, "Aborting", err)
return err
}
if len(dir) == 0 {
common.LogDebug("New package branch is already empty.")
CreateEmptyBranch = false
}
}
if CreateEmptyBranch {
git.GitExecOrPanic(nr.PackageName, "checkout", "--detach")
git.GitExec(nr.PackageName, "branch", "-D", nr.Branch)
git.GitExecOrPanic(nr.PackageName, "checkout", "-f", "--orphan", nr.Branch)
git.GitExecOrPanic(nr.PackageName, "rm", "-rf", ".")
git.GitExecOrPanic(nr.PackageName, "commit", "--allow-empty", "-m", "Initial empty branch")
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)
body := fmt.Sprintf("See issue %s/%s#%d", org, repo, issue.Index)
br := branch
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, branch, title, body)
return err
}
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)
}
}
}
} else if len(title) > 4 && strings.EqualFold(title[0:4], "[RM]") {
// 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

@@ -0,0 +1,482 @@
package main
import (
"fmt"
"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 TestProcessIssue_Add(t *testing.T) {
ctl := gomock.NewController(t)
defer ctl.Finish()
gitea := mock_common.NewMockGitea(ctl)
Gitea = gitea
common.IsDryRun = false
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",
}
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().GetPath().Return("/tmp/test").AnyTimes()
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"},
},
}
issueSHA := &models.Issue{
Title: "[ADD] pkg1",
Body: "src-org/pkg1#abcdef0123456789abcdef0123456789abcdef01",
Index: 123,
Repository: &models.RepositoryMeta{Owner: "test-org", Name: "test-prj"},
Ref: "refs/heads/main",
}
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
mockGit.EXPECT().GitClone("pkg1", "abcdef0123456789abcdef0123456789abcdef01", "target-ssh-url").Return("origin", nil)
// New orphan branch logic
mockGit.EXPECT().GitExec("pkg1", "checkout", "-B", "abcdef0123456789abcdef0123456789abcdef01", "origin/abcdef0123456789abcdef0123456789abcdef01").Return(fmt.Errorf("error"))
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "--detach")
mockGit.EXPECT().GitExec("pkg1", "branch", "-D", "abcdef0123456789abcdef0123456789abcdef01")
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "-f", "--orphan", "abcdef0123456789abcdef0123456789abcdef01")
mockGit.EXPECT().GitExecOrPanic("pkg1", "rm", "-rf", ".")
mockGit.EXPECT().GitExecOrPanic("pkg1", "commit", "--allow-empty", "-m", "Initial empty branch")
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", "abcdef0123456789abcdef0123456789abcdef01")
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
mockGit.EXPECT().GitExecWithOutput("pkg1", "ls-remote", "--heads", "src-ssh-url", "abcdef0123456789abcdef0123456789abcdef01").Return("", nil)
// SHA source logic
tempBranch := "new_package_123_pkg1"
mockGit.EXPECT().GitClone("pkg1", "abcdef0123456789abcdef0123456789abcdef01", "target-ssh-url").Return("origin", nil)
mockGit.EXPECT().GitExecOrPanic("pkg1", "remote", "add", "source", "src-ssh-url")
mockGit.EXPECT().GitExecOrPanic("pkg1", "fetch", "source", "abcdef0123456789abcdef0123456789abcdef01")
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "-B", tempBranch, "FETCH_HEAD")
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", tempBranch)
// PR creation using temp branch
gitea.EXPECT().CreatePullRequestIfNotExist(targetRepo, tempBranch, "main", gomock.Any(), gomock.Any()).Return(&models.PullRequest{Index: 456}, nil, false)
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)
// 4. Branch check - exists via local git
mockGit.EXPECT().GitClone("pkg1", "master", "target-ssh-url").Return("origin", nil)
mockGit.EXPECT().GitExec("pkg1", "checkout", "-B", "master", "origin/master").Return(nil)
mockGit.EXPECT().GitDirectoryContentList("pkg1", "HEAD").Return(map[string]string{"some-file": "some-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", gomock.Any())
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", "master")
// 5. Source repo fetch
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
// Check if source is a branch via ls-remote
mockGit.EXPECT().GitExecWithOutput("pkg1", "ls-remote", "--heads", "src-ssh-url", "master").Return("src-sha refs/heads/master", nil)
// PR creation
packagePR := &models.PullRequest{
Index: 456,
Base: &models.PRBranchInfo{
Ref: "main",
Repo: targetRepo,
Name: "main",
},
Head: &models.PRBranchInfo{
Ref: "master",
Repo: srcRepo,
Sha: "src-sha",
},
URL: "http://gitea/pr/456",
}
gitea.EXPECT().CreatePullRequestIfNotExist(targetRepo, "src-org:master", "main", gomock.Any(), gomock.Any()).Return(packagePR, 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"},
Parent: &models.Repository{
Name: "other-repo",
Owner: &models.User{UserName: "other-org"},
},
}
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
mockGit.EXPECT().GitClone("pkg1", "master", "target-ssh-url").Return("origin", 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("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)
mockGit.EXPECT().GitClone("pkg1", "master", "target-ssh-url").Return("origin", nil)
mockGit.EXPECT().GitExec("pkg1", "checkout", "-B", "master", "origin/master").Return(nil)
mockGit.EXPECT().GitDirectoryContentList("pkg1", "HEAD").Return(map[string]string{"some-file": "some-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", gomock.Any())
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", "master")
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
mockGit.EXPECT().GitExecWithOutput("pkg1", "ls-remote", "--heads", "src-ssh-url", "master").Return("src-sha refs/heads/master", nil)
gitea.EXPECT().CreatePullRequestIfNotExist(targetRepo, "src-org:master", "main", gomock.Any(), gomock.Any()).Return(&models.PullRequest{Index: 456}, 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().GetPath().Return("/tmp/test").AnyTimes()
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)
// 4. Branch check - missing
mockGit.EXPECT().GitClone("pkg1", "master", "target-ssh-url").Return("origin", nil)
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(fmt.Errorf("not found"))
// Orphan branch creation
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")
// 5. Source repo fetch
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
mockGit.EXPECT().GitExecWithOutput("pkg1", "ls-remote", "--heads", "src-ssh-url", "master").Return("src-sha refs/heads/master", nil)
// PR creation
gitea.EXPECT().CreatePullRequestIfNotExist(targetRepo, "src-org:master", "main", gomock.Any(), gomock.Any()).Return(&models.PullRequest{Index: 456}, nil, false)
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",
}
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) {
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
GitHandler = mockGitGen
err := ProcessIssue(&models.Issue{
Title: "[ADD] pkg1",
Body: "nothing here",
Ref: "refs/heads/main",
Repository: &models.RepositoryMeta{
Owner: "test-org",
Name: "test-prj",
},
}, 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().GetPath().Return("/tmp/test").AnyTimes()
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",
}
// First call expectations
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
mockGit.EXPECT().GitClone("pkg1", sha1, "target-ssh-url").Return("origin", nil)
mockGit.EXPECT().GitExec("pkg1", "checkout", "-B", sha1, "origin/"+sha1).Return(fmt.Errorf("error"))
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "--detach")
mockGit.EXPECT().GitExec("pkg1", "branch", "-D", sha1)
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "-f", "--orphan", sha1)
mockGit.EXPECT().GitExecOrPanic("pkg1", "rm", "-rf", ".")
mockGit.EXPECT().GitExecOrPanic("pkg1", "commit", "--allow-empty", "-m", "Initial empty branch")
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", sha1)
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
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)
gitea.EXPECT().CreatePullRequestIfNotExist(targetRepo, tempBranch, "main", gomock.Any(), gomock.Any()).Return(&models.PullRequest{Index: 456}, 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",
}
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
mockGit.EXPECT().GitClone("pkg1", sha2, "target-ssh-url").Return("origin", nil)
mockGit.EXPECT().GitExec("pkg1", "checkout", "-B", sha2, "origin/"+sha2).Return(fmt.Errorf("error"))
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "--detach")
mockGit.EXPECT().GitExec("pkg1", "branch", "-D", sha2)
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "-f", "--orphan", sha2)
mockGit.EXPECT().GitExecOrPanic("pkg1", "rm", "-rf", ".")
mockGit.EXPECT().GitExecOrPanic("pkg1", "commit", "--allow-empty", "-m", "Initial empty branch")
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", sha2)
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
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(&models.PullRequest{Index: 456}, nil, false)
err = ProcessIssue(issue2, configs)
if err != nil {
t.Errorf("Second call failed: %v", err)
}
})
}

View File

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

View File

@@ -2,10 +2,8 @@ package main
import (
"bytes"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
@@ -22,83 +20,6 @@ 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) {
var buf bytes.Buffer
origLogger := log.Writer()
@@ -125,7 +46,7 @@ func TestUpdatePrBranch(t *testing.T) {
req.Pull_Request.Base.Sha = strings.TrimSpace(revs[1])
req.Pull_Request.Head.Sha = strings.TrimSpace(revs[0])
updateSubmoduleInPR("mainRepo", revs[0], git)
updateSubmoduleInPR("testRepo", revs[0], git)
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", "created commit"))
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "push", "origin", "+HEAD:+testing"))
git.GitExecOrPanic("prj", "reset", "--hard", "testing")

View File

@@ -1,10 +0,0 @@
// 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:
//
// mockgen -source=state_checker.go -destination=../mock/state_checker.go -typed -package mock_main
// mockgen -source=state_checker.go -destination=mock_state_checker.go -typed -package main
//
// Package mock_main is a generated GoMock package.
package mock_main
// Package main is a generated GoMock package.
package main
import (
reflect "reflect"
gomock "go.uber.org/mock/gomock"
common "src.opensuse.org/autogits/common"
interfaces "src.opensuse.org/autogits/workflow-pr/interfaces"
models "src.opensuse.org/autogits/common/gitea-generated/models"
)
// MockStateChecker is a mock of StateChecker interface.
@@ -42,11 +42,9 @@ func (m *MockStateChecker) EXPECT() *MockStateCheckerMockRecorder {
}
// CheckRepos mocks base method.
func (m *MockStateChecker) CheckRepos() error {
func (m *MockStateChecker) CheckRepos() {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CheckRepos")
ret0, _ := ret[0].(error)
return ret0
m.ctrl.Call(m, "CheckRepos")
}
// CheckRepos indicates an expected call of CheckRepos.
@@ -62,19 +60,19 @@ type MockStateCheckerCheckReposCall struct {
}
// Return rewrite *gomock.Call.Return
func (c *MockStateCheckerCheckReposCall) Return(arg0 error) *MockStateCheckerCheckReposCall {
c.Call = c.Call.Return(arg0)
func (c *MockStateCheckerCheckReposCall) Return() *MockStateCheckerCheckReposCall {
c.Call = c.Call.Return()
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockStateCheckerCheckReposCall) Do(f func() error) *MockStateCheckerCheckReposCall {
func (c *MockStateCheckerCheckReposCall) Do(f func()) *MockStateCheckerCheckReposCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockStateCheckerCheckReposCall) DoAndReturn(f func() error) *MockStateCheckerCheckReposCall {
func (c *MockStateCheckerCheckReposCall) DoAndReturn(f func()) *MockStateCheckerCheckReposCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
@@ -118,10 +116,10 @@ func (c *MockStateCheckerConsistencyCheckProcessCall) DoAndReturn(f func() error
}
// VerifyProjectState mocks base method.
func (m *MockStateChecker) VerifyProjectState(configs *common.AutogitConfig) ([]*interfaces.PRToProcess, error) {
func (m *MockStateChecker) VerifyProjectState(configs *common.AutogitConfig) ([]*PRToProcess, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "VerifyProjectState", configs)
ret0, _ := ret[0].([]*interfaces.PRToProcess)
ret0, _ := ret[0].([]*PRToProcess)
ret1, _ := ret[1].(error)
return ret0, ret1
}
@@ -139,19 +137,81 @@ type MockStateCheckerVerifyProjectStateCall struct {
}
// Return rewrite *gomock.Call.Return
func (c *MockStateCheckerVerifyProjectStateCall) Return(arg0 []*interfaces.PRToProcess, arg1 error) *MockStateCheckerVerifyProjectStateCall {
func (c *MockStateCheckerVerifyProjectStateCall) Return(arg0 []*PRToProcess, arg1 error) *MockStateCheckerVerifyProjectStateCall {
c.Call = c.Call.Return(arg0, arg1)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockStateCheckerVerifyProjectStateCall) Do(f func(*common.AutogitConfig) ([]*interfaces.PRToProcess, error)) *MockStateCheckerVerifyProjectStateCall {
func (c *MockStateCheckerVerifyProjectStateCall) Do(f func(*common.AutogitConfig) ([]*PRToProcess, error)) *MockStateCheckerVerifyProjectStateCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockStateCheckerVerifyProjectStateCall) DoAndReturn(f func(*common.AutogitConfig) ([]*interfaces.PRToProcess, error)) *MockStateCheckerVerifyProjectStateCall {
func (c *MockStateCheckerVerifyProjectStateCall) DoAndReturn(f func(*common.AutogitConfig) ([]*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)
return c
}

View File

@@ -1,7 +1,5 @@
package main
//go:generate mockgen -source=pr_processor.go -destination=mock/pr_processor.go -typed
import (
"encoding/json"
"errors"
@@ -192,7 +190,14 @@ func (pr *PRProcessor) SetSubmodulesToMatchPRSet(prset *common.PRSet) error {
}
if !submodule_found {
common.LogError("Failed to find expected repo:", repo)
common.LogInfo("Adding new submodule", repo, "to PrjGit")
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
@@ -600,7 +605,7 @@ func (pr *PRProcessor) Process(req *models.PullRequest) error {
common.LogError("merge error:", err)
}
} else {
prset.AssignReviewers(Gitea, maintainers)
err = prset.AssignReviewers(Gitea, maintainers)
}
return err
}
@@ -610,6 +615,14 @@ type RequestProcessor struct {
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 {
if len(configs) < 1 {
// ignoring pull request against unconfigured project (could be just regular sources?)
@@ -654,6 +667,18 @@ 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))
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
}
return ProcessIssue(issue, configs)
} else {
common.LogError("*** Invalid data format for PR processing.")
return fmt.Errorf("*** Invalid data format for PR processing.")

View File

@@ -6,7 +6,6 @@ import (
"go.uber.org/mock/gomock"
"src.opensuse.org/autogits/common"
"src.opensuse.org/autogits/common/gitea-generated/client/repository"
"src.opensuse.org/autogits/common/gitea-generated/models"
mock_common "src.opensuse.org/autogits/common/mock"
)
@@ -17,7 +16,7 @@ func TestOpenPR(t *testing.T) {
Reviewers: []string{"reviewer1", "reviewer2"},
Branch: "branch",
Organization: "test",
GitProjectName: "prj",
GitProjectName: "prj#testing",
},
}
@@ -26,6 +25,7 @@ func TestOpenPR(t *testing.T) {
Number: 1,
Pull_Request: &common.PullRequest{
Id: 1,
Number: 1,
Base: common.Head{
Ref: "branch",
Sha: "testing",
@@ -53,6 +53,56 @@ 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{
GitCommiter: "tester",
GitEmail: "test@suse.com",
@@ -60,14 +110,46 @@ func TestOpenPR(t *testing.T) {
t.Run("PR git opened request against PrjGit == no action", func(t *testing.T) {
ctl := gomock.NewController(t)
Gitea = mock_common.NewMockGitea(ctl)
gitea := mock_common.NewMockGitea(ctl)
Gitea = gitea
git.GitPath = t.TempDir()
pr.config.GitProjectName = "testRepo"
pr.config.GitProjectName = "testRepo#testing"
event.Repository.Name = "testRepo"
mockGit := mock_common.NewMockGit(ctl)
pr.git = mockGit
if err := pr.Process(event); err != nil {
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()
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)
}
})
@@ -79,39 +161,47 @@ func TestOpenPR(t *testing.T) {
Gitea = gitea
event.Repository.Name = "testRepo"
pr.config.GitProjectName = "prjcopy"
pr.config.GitProjectName = "prjcopy#testing"
git.GitPath = t.TempDir()
setupGitForTests(t, git)
prjgit := &models.Repository{
SSHURL: "./prj",
DefaultBranch: "testing",
}
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().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().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
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()
gitea.EXPECT().FetchMaintainershipDirFile("test", "prjcopy", "branch", "_project").Return(nil, "", repository.NewRepoGetRawFileNotFound())
gitea.EXPECT().FetchMaintainershipFile("test", "prjcopy", "branch").Return(nil, "", repository.NewRepoGetRawFileNotFound())
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()
err := pr.Process(event)
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)
}
@@ -124,15 +214,44 @@ func TestOpenPR(t *testing.T) {
Gitea = gitea
event.Repository.Name = "testRepo"
pr.config.GitProjectName = "prjcopy"
pr.config.GitProjectName = "prjcopy#testing"
git.GitPath = t.TempDir()
setupGitForTests(t, git)
failedErr := errors.New("Returned error here")
gitea.EXPECT().CreateRepositoryIfNotExist(git, "test", "prjcopy").Return(nil, failedErr)
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, failedErr).AnyTimes()
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockCreatePR, nil, true).AnyTimes()
err := pr.Process(event)
if err != failedErr {
mockGit := mock_common.NewMockGit(ctl)
pr.git = mockGit
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)
}
})
@@ -143,7 +262,7 @@ func TestOpenPR(t *testing.T) {
Gitea = gitea
event.Repository.Name = "testRepo"
pr.config.GitProjectName = "prjcopy"
pr.config.GitProjectName = "prjcopy#testing"
git.GitPath = t.TempDir()
setupGitForTests(t, git)
@@ -152,10 +271,37 @@ func TestOpenPR(t *testing.T) {
DefaultBranch: "testing",
}
failedErr := errors.New("Returned error here")
gitea.EXPECT().CreateRepositoryIfNotExist(git, "test", "prjcopy").Return(prjgit, nil)
gitea.EXPECT().CreatePullRequestIfNotExist(prjgit, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, failedErr, false)
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(prjgit, nil).AnyTimes()
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, failedErr, false)
err := pr.Process(event)
mockGit := mock_common.NewMockGit(ctl)
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 {
t.Error("error:", err)
}
@@ -167,40 +313,49 @@ func TestOpenPR(t *testing.T) {
Gitea = gitea
event.Repository.Name = "testRepo"
pr.config.GitProjectName = "prjcopy"
pr.config.GitProjectName = "prjcopy#testing"
git.GitPath = t.TempDir()
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")
// 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().FetchMaintainershipDirFile("test", "prjcopy", "branch", "_project").Return(nil, "", repository.NewRepoGetRawFileNotFound())
gitea.EXPECT().FetchMaintainershipFile("test", "prjcopy", "branch").Return(nil, "", repository.NewRepoGetRawFileNotFound())
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes()
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
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()
err := pr.Process(event)
if errors.Unwrap(err) != failedErr {
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()
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)
}
})

View File

@@ -1,12 +1,7 @@
package main
/*
import (
"bytes"
"errors"
"log"
"os"
"path"
"strings"
"testing"
"go.uber.org/mock/gomock"
@@ -16,217 +11,144 @@ import (
)
func TestSyncPR(t *testing.T) {
pr := PRProcessor{
config: &common.AutogitConfig{
Reviewers: []string{"reviewer1", "reviewer2"},
Branch: "testing",
Organization: "test",
GitProjectName: "prj",
},
CurrentUser = &models.User{UserName: "testuser"}
config := &common.AutogitConfig{
Reviewers: []string{"reviewer1", "reviewer2"},
Branch: "testing",
Organization: "test-org",
GitProjectName: "test-prj#testing",
}
event := &common.PullRequestWebhookEvent{
Action: "syncronized",
Number: 42,
Pull_Request: &common.PullRequest{
Number: 42,
Base: common.Head{
Ref: "branch",
Sha: "8a6a69a4232cabda04a4d9563030aa888ff5482f75aa4c6519da32a951a072e2",
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,
},
},
git := &common.GitHandlerImpl{
GitCommiter: "tester",
GitEmail: "test@suse.com",
GitPath: t.TempDir(),
}
processor := &PRProcessor{
config: config,
git: git,
}
modelPR := &models.PullRequest{
Index: 42,
Body: "PR: test/prj#24",
Body: "PR: test-org/test-prj#24",
Base: &models.PRBranchInfo{
Ref: "branch",
Sha: "8a6a69a4232cabda04a4d9563030aa888ff5482f75aa4c6519da32a951a072e2",
Ref: "main",
Repo: &models.Repository{
Name: "testRepo",
Owner: &models.User{
UserName: "test",
},
DefaultBranch: "main1",
Name: "test-repo",
Owner: &models.User{UserName: "test-org"},
DefaultBranch: "main",
},
},
Head: &models.PRBranchInfo{
Ref: "branch",
Sha: "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985",
Repo: &models.Repository{
Name: "testRepo",
Owner: &models.User{
UserName: "test",
},
DefaultBranch: "main1",
Name: "test-repo",
Owner: &models.User{UserName: "test-org"},
DefaultBranch: "main",
},
},
}
PrjGitPR := &models.PullRequest{
Title: "some pull request",
Body: "PR: test/testRepo#42",
Body: "PR: test-org/test-repo#42",
Index: 24,
Base: &models.PRBranchInfo{
Ref: "testing",
Repo: &models.Repository{
Name: "test-prj",
Owner: &models.User{UserName: "test-org"},
SSHURL: "url",
},
},
Head: &models.PRBranchInfo{
Name: "testing",
Name: "PR_test-repo#42",
Sha: "db8adab91edb476b9762097d10c6379aa71efd6b60933a1c0e355ddacf419a95",
Repo: &models.Repository{
SSHURL: "./prj",
SSHURL: "url",
},
},
}
git := &common.GitHandlerImpl{
GitCommiter: "tester",
GitEmail: "test@suse.com",
}
t.Run("PR sync request against PrjGit == no action", func(t *testing.T) {
t.Run("PR_sync_request_against_PrjGit_==_no_action", func(t *testing.T) {
ctl := gomock.NewController(t)
Gitea = mock_common.NewMockGitea(ctl)
defer ctl.Finish()
gitea := mock_common.NewMockGitea(ctl)
Gitea = gitea
git.GitPath = t.TempDir()
// Common expectations for FetchPRSet and downstream checks
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()
pr.config.GitProjectName = "testRepo"
event.Repository.Name = "testRepo"
if err := pr.Process(event); err != nil {
t.Error("Error PrjGit sync request. Should be no error.", err)
prjGitRepoPR := &models.PullRequest{
Index: 100,
Base: &models.PRBranchInfo{
Ref: "testing",
Repo: &models.Repository{
Name: "test-prj",
Owner: &models.User{UserName: "test-org"},
},
},
Head: &models.PRBranchInfo{
Ref: "branch",
},
}
})
t.Run("Missing submodule in prjgit", func(t *testing.T) {
ctl := gomock.NewController(t)
mock := mock_common.NewMockGitea(ctl)
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(prjGitRepoPR, nil).AnyTimes()
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
pr.gitea = mock
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)
if err := processor.Process(prjGitRepoPR); err != nil {
t.Errorf("Expected nil error for PrjGit sync request, got %v", err)
}
})
t.Run("Missing PrjGit PR for the sync", func(t *testing.T) {
ctl := gomock.NewController(t)
mock := mock_common.NewMockGitea(ctl)
defer ctl.Finish()
gitea := mock_common.NewMockGitea(ctl)
Gitea = gitea
pr.gitea = mock
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()
pr.config.GitProjectName = "prjGit"
event.Repository.Name = "tester"
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("not found")).AnyTimes()
setupGitForTests(t, git)
expectedErr := errors.New("Missing PR should throw error")
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)
err := processor.Process(modelPR)
// It should fail because it can't find the project PR linked in body
if err == nil {
t.Errorf("Expected error for missing project PR, got nil")
}
})
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)
mock := mock_common.NewMockGitea(ctl)
Gitea = mock
git.GitPath = t.TempDir()
pr.config.GitProjectName = "prjGit"
event.Repository.Name = "testRepo"
defer ctl.Finish()
gitea := mock_common.NewMockGitea(ctl)
Gitea = gitea
setupGitForTests(t, git)
// mock.EXPECT().GetAssociatedPrjGitPR(event).Return(PrjGitPR, nil)
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)
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(PrjGitPR, 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 := pr.Process(event)
// For UpdatePrjGitPR
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
err := processor.Process(modelPR)
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())
}
// 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())
t.Errorf("Unexpected error: %v", err)
}
})
}
*/

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,23 @@
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

@@ -0,0 +1,88 @@
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"},
})
}