467 lines
15 KiB
Go
467 lines
15 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"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"
|
|
mock_main "src.opensuse.org/workflow-pr/mock"
|
|
)
|
|
|
|
func TestPR(t *testing.T) {
|
|
baseConfig := common.AutogitConfig{
|
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
|
Branch: "branch",
|
|
Organization: "foo",
|
|
GitProjectName: "barPrj",
|
|
}
|
|
|
|
type prdata struct {
|
|
pr *models.PullRequest
|
|
pr_err error
|
|
reviews []*models.PullReview
|
|
review_error error
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
data []prdata
|
|
api_error string
|
|
|
|
resLen int
|
|
reviewed bool
|
|
consistentSet bool
|
|
prjGitPRIndex int
|
|
|
|
reviewSetFetcher func(*mock_common.MockGiteaPRFetcher) (*PRSet, error)
|
|
}{
|
|
{
|
|
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"}}}}, 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"}}}}, 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: "Error fetching prjgit",
|
|
data: []prdata{
|
|
{pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}}},
|
|
},
|
|
resLen: 1,
|
|
prjGitPRIndex: -1,
|
|
},
|
|
{
|
|
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"}}}}},
|
|
{pr: &models.PullRequest{Body: "PR: test/repo#42", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}}},
|
|
},
|
|
resLen: 2,
|
|
prjGitPRIndex: 1,
|
|
consistentSet: true,
|
|
},
|
|
|
|
{
|
|
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"}}}}},
|
|
{pr: &models.PullRequest{Body: "PR: test/repo#42", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}}},
|
|
},
|
|
resLen: 2,
|
|
prjGitPRIndex: 1,
|
|
consistentSet: true,
|
|
},
|
|
{
|
|
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"}}}}},
|
|
{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"}}}}},
|
|
{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"}}}}},
|
|
},
|
|
resLen: 3,
|
|
prjGitPRIndex: 1,
|
|
consistentSet: true,
|
|
},
|
|
{
|
|
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"}}}},
|
|
reviews: []*models.PullReview{
|
|
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
|
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
|
{Body: "LGTM", User: &models.User{UserName: common.Bot_BuildReview}, State: common.ReviewStateApproved},
|
|
},
|
|
},
|
|
},
|
|
resLen: 1,
|
|
prjGitPRIndex: 0,
|
|
consistentSet: true,
|
|
reviewed: true,
|
|
reviewSetFetcher: func(mock *mock_common.MockGiteaPRFetcher) (*PRSet, error) {
|
|
return FetchPRSet(mock, "foo", "barPrj", 42, &baseConfig)
|
|
},
|
|
},
|
|
{
|
|
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"}}}}},
|
|
{pr: &models.PullRequest{Body: "PR: test/repo2#41", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}}},
|
|
{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"}}}}},
|
|
{pr: &models.PullRequest{Body: "PR: foo/barPrj#20", Index: 41, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo2", Owner: &models.User{UserName: "test"}}}}},
|
|
},
|
|
resLen: 3,
|
|
prjGitPRIndex: 2,
|
|
consistentSet: true,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
ctl := gomock.NewController(t)
|
|
pr_mock := mock_common.NewMockGiteaPRFetcher(ctl)
|
|
review_mock := mock_common.NewMockGiteaPRChecker(ctl)
|
|
// reviewer_mock := mock_common.NewMockGiteaReviewRequester(ctl)
|
|
|
|
if test.reviewSetFetcher == nil { // if we are fetching the prjgit directly, the these mocks are not called
|
|
if test.prjGitPRIndex >= 0 {
|
|
pr_mock.EXPECT().GetAssociatedPrjGitPR(baseConfig.Organization, baseConfig.GitProjectName, test.data[0].pr.Base.Repo.Owner.UserName, test.data[0].pr.Base.Repo.Name, test.data[0].pr.Index).
|
|
Return(test.data[test.prjGitPRIndex].pr, test.data[test.prjGitPRIndex].pr_err)
|
|
} else if test.prjGitPRIndex < 0 {
|
|
// no prjgit PR
|
|
pr_mock.EXPECT().GetAssociatedPrjGitPR(baseConfig.Organization, baseConfig.GitProjectName, test.data[0].pr.Base.Repo.Owner.UserName, test.data[0].pr.Base.Repo.Name, test.data[0].pr.Index).
|
|
Return(nil, nil)
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
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()
|
|
}
|
|
|
|
var res *PRSet
|
|
var err error
|
|
|
|
if test.reviewSetFetcher != nil {
|
|
res, err = test.reviewSetFetcher(pr_mock)
|
|
} else {
|
|
res, err = FetchPRSet(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)
|
|
}
|
|
return
|
|
}
|
|
|
|
if test.resLen != len(res.prs) {
|
|
t.Error("expected result len", test.resLen, "but got", len(res.prs))
|
|
}
|
|
|
|
PrjGitPR, err := res.GetPrjGitPR()
|
|
if test.prjGitPRIndex < 0 {
|
|
if err == nil {
|
|
t.Error("expected error, but nothing")
|
|
}
|
|
}
|
|
pr_found := false
|
|
if test.prjGitPRIndex >= 0 {
|
|
for i := range test.data {
|
|
if PrjGitPR == test.data[i].pr && i == test.prjGitPRIndex {
|
|
t.Log("found at index", i)
|
|
pr_found = true
|
|
}
|
|
}
|
|
if !pr_found {
|
|
t.Error("Cannot find expected PrjGit location in PR set", PrjGitPR)
|
|
}
|
|
} else {
|
|
if PrjGitPR != nil {
|
|
t.Log("Expected prjgit not found, but found?", PrjGitPR)
|
|
}
|
|
}
|
|
|
|
if isConsistent := res.IsConsistent(); isConsistent != test.consistentSet {
|
|
t.Error("IsConsistent() returned unexpected:", isConsistent)
|
|
}
|
|
/*
|
|
if err := res.AssignReviewers(reviewer_mock); err != nil {
|
|
t.Error("expected no errors assigning reviewers:", err)
|
|
}
|
|
*/
|
|
if isReviewed := res.IsReviewed(review_mock); isReviewed != test.reviewed {
|
|
t.Error("expected reviewed to be NOT", isReviewed)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPRAssignReviewers(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
name string
|
|
config common.AutogitConfig
|
|
reviewers []struct {
|
|
org, repo string
|
|
num int64
|
|
reviewer string
|
|
}
|
|
|
|
pkgReviews []*models.PullReview
|
|
prjReviews []*models.PullReview
|
|
|
|
expectedReviewerCall [2][]string
|
|
}{
|
|
{
|
|
name: "No reviewers",
|
|
config: common.AutogitConfig{
|
|
GitProjectName: "repo",
|
|
Organization: "org",
|
|
Branch: "main",
|
|
Reviewers: []string{},
|
|
},
|
|
expectedReviewerCall: [2][]string{{"autogits_obs_staging_bot"}},
|
|
},
|
|
{
|
|
name: "One project reviewer only",
|
|
config: common.AutogitConfig{
|
|
GitProjectName: "repo",
|
|
Organization: "org",
|
|
Branch: "main",
|
|
Reviewers: []string{"-user1"},
|
|
},
|
|
expectedReviewerCall: [2][]string{{"user1", "autogits_obs_staging_bot"}},
|
|
},
|
|
{
|
|
name: "One project reviewer and one pkg reviewer only",
|
|
config: common.AutogitConfig{
|
|
GitProjectName: "repo",
|
|
Organization: "org",
|
|
Branch: "main",
|
|
Reviewers: []string{"-user1", "user2"},
|
|
},
|
|
expectedReviewerCall: [2][]string{{"user1", "autogits_obs_staging_bot"}, {"user2"}},
|
|
},
|
|
{
|
|
name: "No need to get reviews of submitter",
|
|
config: common.AutogitConfig{
|
|
GitProjectName: "repo",
|
|
Organization: "org",
|
|
Branch: "main",
|
|
Reviewers: []string{"-user1", "submitter"},
|
|
},
|
|
expectedReviewerCall: [2][]string{{"user1", "autogits_obs_staging_bot"}, {}},
|
|
},
|
|
{
|
|
name: "Reviews are done",
|
|
config: common.AutogitConfig{
|
|
GitProjectName: "repo",
|
|
Organization: "org",
|
|
Branch: "main",
|
|
Reviewers: []string{"-user1", "user2"},
|
|
},
|
|
pkgReviews: []*models.PullReview{{State: common.ReviewStateApproved, User: &models.User{UserName: "user2"}}},
|
|
prjReviews: []*models.PullReview{
|
|
{
|
|
State: common.ReviewStateRequestChanges,
|
|
User: &models.User{UserName: "user1"},
|
|
},
|
|
{
|
|
State: common.ReviewStateRequestReview,
|
|
User: &models.User{UserName: "autogits_obs_staging_bot"},
|
|
},
|
|
},
|
|
expectedReviewerCall: [2][]string{{}, {}},
|
|
},
|
|
{
|
|
name: "Stale review is not done, re-request it",
|
|
config: common.AutogitConfig{
|
|
GitProjectName: "repo",
|
|
Organization: "org",
|
|
Branch: "main",
|
|
Reviewers: []string{"-user1", "user2"},
|
|
},
|
|
pkgReviews: []*models.PullReview{{State: common.ReviewStateApproved, User: &models.User{UserName: "user2"}}},
|
|
prjReviews: []*models.PullReview{
|
|
{
|
|
State: common.ReviewStateRequestChanges,
|
|
User: &models.User{UserName: "user1"},
|
|
Stale: true,
|
|
},
|
|
{
|
|
State: common.ReviewStateRequestReview,
|
|
Stale: true,
|
|
User: &models.User{UserName: "autogits_obs_staging_bot"},
|
|
},
|
|
},
|
|
expectedReviewerCall: [2][]string{{"user1", "autogits_obs_staging_bot"}, {}},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
ctl := gomock.NewController(t)
|
|
pr_mock := mock_common.NewMockGiteaPRFetcher(ctl)
|
|
review_mock := mock_common.NewMockGiteaReviewFetcherAndRequester(ctl)
|
|
maintainership_mock := mock_main.NewMockMaintainershipData(ctl)
|
|
|
|
pr_mock.EXPECT().GetPullRequest("other", "pkgrepo", int64(1)).Return(&models.PullRequest{
|
|
Body: "Some description is here",
|
|
User: &models.User{UserName: "submitter"},
|
|
RequestedReviewers: []*models.User{},
|
|
Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "pkgrepo", Owner: &models.User{UserName: "other"}}},
|
|
Head: &models.PRBranchInfo{},
|
|
Index: 1,
|
|
}, nil)
|
|
review_mock.EXPECT().GetPullRequestReviews("other", "pkgrepo", int64(1)).Return(test.pkgReviews, nil)
|
|
pr_mock.EXPECT().GetAssociatedPrjGitPR("org", "repo", "other", "pkgrepo", int64(1)).Return(&models.PullRequest{
|
|
Body: fmt.Sprintf(common.PrPattern, "other", "pkgrepo", 1),
|
|
User: &models.User{UserName: "bot1"},
|
|
RequestedReviewers: []*models.User{{UserName: "main_reviewer"}},
|
|
Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "org"}}},
|
|
Head: &models.PRBranchInfo{},
|
|
Index: 42,
|
|
}, nil)
|
|
review_mock.EXPECT().GetPullRequestReviews("org", "repo", int64(42)).Return(test.prjReviews, nil)
|
|
|
|
prs, _ := FetchPRSet(pr_mock, "other", "pkgrepo", int64(1), &test.config)
|
|
if len(prs.prs) != 2 {
|
|
t.Fatal("PRs not fetched")
|
|
}
|
|
for _, pr := range prs.prs {
|
|
r := test.expectedReviewerCall[0]
|
|
if !prs.IsPrjGitPR(pr.pr) {
|
|
r = test.expectedReviewerCall[1]
|
|
}
|
|
if len(r) > 0 {
|
|
review_mock.EXPECT().RequestReviews(pr.pr, r).Return(nil, nil)
|
|
}
|
|
}
|
|
prs.AssignReviewers(review_mock, maintainership_mock)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPRMerge(t *testing.T) {
|
|
cwd, _ := os.Getwd()
|
|
cmd := exec.Command("/usr/bin/bash", path.Join(cwd, "test_repo_setup.sh"))
|
|
cmd.Dir = t.TempDir()
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
t.Fatal(string(out))
|
|
}
|
|
|
|
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'",
|
|
}
|
|
|
|
config := &common.AutogitConfig{
|
|
Organization: "org",
|
|
GitProjectName: "prj",
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
pr *models.PullRequest
|
|
mergeError string
|
|
}{
|
|
{
|
|
name: "Merge base not merged in main",
|
|
|
|
pr: &models.PullRequest{
|
|
Base: &models.PRBranchInfo{
|
|
Sha: "e8b0de43d757c96a9d2c7101f4bff404e322f53a1fa4041fb85d646110c38ad4", // "base_add_b1"
|
|
Repo: &models.Repository{
|
|
Name: "prj",
|
|
Owner: &models.User{
|
|
UserName: "org",
|
|
},
|
|
SSHURL: path.Join(cmd.Dir, "prjgit"),
|
|
},
|
|
},
|
|
Head: &models.PRBranchInfo{
|
|
Sha: "88584433de1c917c1d773f62b82381848d882491940b5e9b427a540aa9057d9a", // "base_add_b2"
|
|
},
|
|
},
|
|
mergeError: "Aborting merge",
|
|
},
|
|
{
|
|
name: "Merge conflict in modules",
|
|
|
|
pr: &models.PullRequest{
|
|
Base: &models.PRBranchInfo{
|
|
Sha: "4fbd1026b2d7462ebe9229a49100c11f1ad6555520a21ba515122d8bc41328a8",
|
|
Repo: &models.Repository{
|
|
Name: "prj",
|
|
Owner: &models.User{
|
|
UserName: "org",
|
|
},
|
|
SSHURL: path.Join(cmd.Dir, "prjgit"),
|
|
},
|
|
},
|
|
Head: &models.PRBranchInfo{
|
|
Sha: "88584433de1c917c1d773f62b82381848d882491940b5e9b427a540aa9057d9a", // "base_add_b2"
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
ctl := gomock.NewController(t)
|
|
mock := mock_common.NewMockGiteaPRFetcher(ctl)
|
|
|
|
mock.EXPECT().GetPullRequest("org", "prj", int64(1)).Return(test.pr, nil)
|
|
|
|
set, err := FetchPRSet(mock, "org", "prj", 1, config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err = set.Merge(); err != nil && (test.mergeError == "" || (len(test.mergeError) > 0 && !strings.Contains(err.Error(), test.mergeError))) {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
}
|
|
}
|