package common_test import ( "errors" "os" "os/exec" "path" "slices" "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 TestCockpit(t *testing.T) { common.SetLoggingLevel(common.LogLevelDebug) gitea := common.AllocateGiteaTransport("https://src.opensuse.org") tl, err := gitea.GetTimeline("cockpit", "cockpit", 29) if err != nil { t.Fatal("Fail to timeline", err) } t.Log(tl) r, err := common.FetchGiteaReviews(gitea, []string{}, "cockpit", "cockpit", 29) if err != nil { t.Fatal("Error:", err) } t.Error(r) } */ func reviewsToTimeline(reviews []*models.PullReview) []*models.TimelineComment { timeline := make([]*models.TimelineComment, len(reviews)) for idx, review := range reviews { if review.ID == 0 { review.ID = int64(idx) + 100 } timeline[idx] = &models.TimelineComment{ Type: common.TimelineCommentType_Review, ReviewID: review.ID, } } return timeline } func TestPR(t *testing.T) { return baseConfig := common.AutogitConfig{ Reviewers: []string{"+super1", "*super2", "m1", "-m2"}, Branch: "branch", Organization: "foo", GitProjectName: "foo/barPrj#master", } type prdata struct { pr *models.PullRequest pr_err error reviews []*models.PullReview timeline []*models.TimelineComment review_error error } tests := []struct { name string data []prdata api_error string resLen int reviewed bool consistentSet bool prjGitPRIndex int reviewSetFetcher func(*mock_common.MockGiteaPRTimelineReviewFetcher) (*common.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"}}}, State: "opened"}, 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"}}, }, }, { 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"}}, }, 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"}}}, 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"}}, }, 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"}}}, 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"}}, }, 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"}}}, 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"}}, }, 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"}}}, User: &models.User{UserName: "submitter"}, State: "opened"}, 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.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) { return common.FetchPRSet("test", 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"}}}, 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"}}, }, resLen: 3, prjGitPRIndex: 2, consistentSet: true, }, { 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"}, 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: false, reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) { return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &baseConfig) }, }, { 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"}, 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}, }, }, { pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"}, reviews: []*models.PullReview{ {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, }, }, }, resLen: 2, prjGitPRIndex: 0, consistentSet: true, reviewed: false, reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) { return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{ Reviewers: []string{"+super1", "*super2", "m1", "-m2"}, Branch: "branch", Organization: "foo", GitProjectName: "barPrj", 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"}, reviews: []*models.PullReview{ {Body: "merge ok", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: common.Bot_BuildReview}, State: common.ReviewStateApproved}, }, }, { pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"}, reviews: []*models.PullReview{ {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, }, }, }, resLen: 2, prjGitPRIndex: 0, consistentSet: true, reviewed: true, reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) { return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{ Reviewers: []string{"+super1", "*super2", "m1", "-m2"}, Branch: "branch", Organization: "foo", GitProjectName: "barPrj", 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"}, reviews: []*models.PullReview{ {Body: "merge ok", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: common.Bot_BuildReview}, State: common.ReviewStateApproved}, }, }, { pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"}, reviews: []*models.PullReview{ {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, }, }, }, resLen: 2, prjGitPRIndex: 0, consistentSet: true, reviewed: true, reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) { return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{ Reviewers: []string{"+super1", "*super2", "m1", "-m2"}, Branch: "branch", Organization: "foo", GitProjectName: "barPrj", ManualMergeOnly: true, ManualMergeProject: true, }) }, }, { 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"}, 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}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: common.Bot_BuildReview}, State: common.ReviewStateApproved}, }, }, { pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"}, reviews: []*models.PullReview{ {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, }, }, }, resLen: 2, prjGitPRIndex: 0, consistentSet: true, reviewed: false, reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) { return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{ Reviewers: []string{"+super1", "*super2", "m1", "-m2"}, Branch: "branch", Organization: "foo", GitProjectName: "barPrj", ManualMergeOnly: true, ManualMergeProject: true, }) }, }, { 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"}, 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}, }, }, { pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"}, reviews: []*models.PullReview{ {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "Merge ok", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, }, }, }, resLen: 2, prjGitPRIndex: 0, consistentSet: true, reviewed: true, reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) { return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{ Reviewers: []string{"+super1", "*super2", "m1", "-m2"}, Branch: "branch", Organization: "foo", GitProjectName: "barPrj", ManualMergeOnly: true, }) }, }, { 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"}, 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}, }, }, { pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"}, reviews: []*models.PullReview{ {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "Merge OK!", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, }, }, { pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 21, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"}, reviews: []*models.PullReview{ {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "merge ok", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, }, }, }, resLen: 3, prjGitPRIndex: 0, consistentSet: true, reviewed: true, reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) { return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{ Reviewers: []string{"+super1", "*super2", "m1", "-m2"}, Branch: "branch", Organization: "foo", GitProjectName: "barPrj", ManualMergeOnly: true, }) }, }, { 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"}, 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}, }, }, { pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"}, reviews: []*models.PullReview{ {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "Merge OK!", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, }, }, { pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 21, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"}, reviews: []*models.PullReview{ {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "merge ok", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, }, }, }, resLen: 3, prjGitPRIndex: 0, consistentSet: true, reviewed: false, reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) { return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{ Reviewers: []string{"+super1", "*super2", "m1", "-m2"}, Branch: "branch", Organization: "foo", GitProjectName: "barPrj", ManualMergeOnly: true, ManualMergeProject: true, }) }, }, { 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"}, 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}, }, }, { pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"}, reviews: []*models.PullReview{ {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, }, }, { pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 21, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"}, reviews: []*models.PullReview{ {Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved}, {Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved}, }, }, }, resLen: 3, prjGitPRIndex: 0, consistentSet: true, reviewed: false, reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) { return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{ Reviewers: []string{"+super1", "*super2", "m1", "-m2"}, Branch: "branch", Organization: "foo", GitProjectName: "barPrj", ManualMergeOnly: true, }) }, }, { 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"}, 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}, {Body: "LGTM", User: &models.User{UserName: "bot"}, State: common.ReviewStateRequestChanges}, }, }, }, resLen: 1, prjGitPRIndex: 0, consistentSet: true, reviewed: true, reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) { config := common.AutogitConfig{ Reviewers: []string{"+super1", "*super2", "m1", "-m2", "~*bot"}, Branch: "branch", Organization: "foo", GitProjectName: "barPrj", } return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &config) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctl := gomock.NewController(t) pr_mock := mock_common.NewMockGiteaPRTimelineReviewFetcher(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().GetPullRequest(baseConfig.Organization, baseConfig.GitProjectName, test.prjGitPRIndex). Return(test.data[test.prjGitPRIndex].pr, test.data[test.prjGitPRIndex].pr_err) } else if test.prjGitPRIndex < 0 { // no prjgit PR pr_mock.EXPECT().GetPullRequest(baseConfig.Organization, baseConfig.GitProjectName, gomock.Any()). 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() if data.timeline == nil { data.timeline = reviewsToTimeline(data.reviews) } review_mock.EXPECT().GetTimeline(data.pr.Base.Repo.Owner.UserName, data.pr.Base.Repo.Name, data.pr.Index).Return(data.timeline, nil).AnyTimes() } var res *common.PRSet var err error if test.reviewSetFetcher != nil { res, err = test.reviewSetFetcher(pr_mock) } else { 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) } 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.PR == 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) } */ maintainers := mock_common.NewMockMaintainershipData(ctl) maintainers.EXPECT().ListPackageMaintainers(gomock.Any(), gomock.Any()).Return([]string{}).AnyTimes() maintainers.EXPECT().ListProjectMaintainers(gomock.Any()).Return([]string{}).AnyTimes() maintainers.EXPECT().IsApproved(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes() if isApproved := res.IsApproved(review_mock, maintainers); isApproved != test.reviewed { t.Error("expected reviewed to be NOT", isApproved) } }) } } func TestFindMissingAndExtraReviewers(t *testing.T) { tests := []struct { name string reviewers []struct { org, repo string num int64 reviewer string } prset *common.PRSet maintainers common.MaintainershipData noAutoStaging bool expected_missing_reviewers [][]string expected_extra_reviewers [][]string }{ { name: "No reviewers", prset: &common.PRSet{ PRs: []*common.PRInfo{ { PR: &models.PullRequest{ User: &models.User{UserName: "foo"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}}, }, Reviews: &common.PRReviews{}, }, }, Config: &common.AutogitConfig{ GitProjectName: "prg/repo#main", Organization: "org", Branch: "main", Reviewers: []string{}, }, }, maintainers: &common.MaintainershipMap{Data: map[string][]string{}}, }, { name: "One project reviewer only", prset: &common.PRSet{ PRs: []*common.PRInfo{ { PR: &models.PullRequest{ User: &models.User{UserName: "foo"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}}, }, Reviews: &common.PRReviews{}, }, { PR: &models.PullRequest{ User: &models.User{UserName: "foo"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prg"}}}, }, Reviews: &common.PRReviews{}, }, }, Config: &common.AutogitConfig{ GitProjectName: "prg/repo#main", Organization: "org", Branch: "main", Reviewers: []string{"-user1"}, }, }, maintainers: &common.MaintainershipMap{Data: map[string][]string{}}, expected_missing_reviewers: [][]string{ []string{}, []string{"autogits_obs_staging_bot", "user1"}, }, }, { name: "One project reviewer only and no auto staging", noAutoStaging: true, prset: &common.PRSet{ PRs: []*common.PRInfo{ { PR: &models.PullRequest{ User: &models.User{UserName: "foo"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}}, }, Reviews: &common.PRReviews{}, }, { PR: &models.PullRequest{ User: &models.User{UserName: "foo"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prg"}}}, }, Reviews: &common.PRReviews{}, }, }, Config: &common.AutogitConfig{ GitProjectName: "prg/repo#main", Organization: "org", Branch: "main", Reviewers: []string{"-user1"}, }, }, maintainers: &common.MaintainershipMap{Data: map[string][]string{}}, expected_missing_reviewers: [][]string{ nil, {"user1"}, }, }, { name: "One project reviewer and one pkg reviewer only", prset: &common.PRSet{ PRs: []*common.PRInfo{ { PR: &models.PullRequest{ User: &models.User{UserName: "foo"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}}, }, Reviews: &common.PRReviews{}, }, { PR: &models.PullRequest{ User: &models.User{UserName: "foo"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prg"}}}, }, Reviews: &common.PRReviews{}, }, }, Config: &common.AutogitConfig{ GitProjectName: "prg/repo#main", Organization: "org", Branch: "main", Reviewers: []string{"-user1", "user2"}, }, }, maintainers: &common.MaintainershipMap{Data: map[string][]string{}}, expected_missing_reviewers: [][]string{ []string{"user2"}, []string{"autogits_obs_staging_bot", "user1"}, }, }, { name: "No need to get reviews of submitter reviewer", prset: &common.PRSet{ PRs: []*common.PRInfo{ { PR: &models.PullRequest{ User: &models.User{UserName: "submitter"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}}, }, Reviews: &common.PRReviews{ Reviews: []*models.PullReview{{State: common.ReviewStateRequestReview, User: &models.User{UserName: "m1"}}}, RequestedReviewers: []string{"m1"}, FullTimeline: []*models.TimelineComment{ {User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "m1"}, Type: common.TimelineCommentType_ReviewRequested}, }, }, }, { PR: &models.PullRequest{ User: &models.User{UserName: "foo"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prg"}}}, }, Reviews: &common.PRReviews{}, }, }, Config: &common.AutogitConfig{ GitProjectName: "prg/repo#main", Organization: "org", Branch: "main", Reviewers: []string{"-user1", "submitter"}, }, BotUser: "bot", }, maintainers: &common.MaintainershipMap{Data: map[string][]string{"pkg": []string{"m1", "submitter"}}}, expected_missing_reviewers: [][]string{ nil, {"autogits_obs_staging_bot", "user1"}, }, expected_extra_reviewers: [][]string{ {"m1"}, }, }, { name: "No need to get reviews of submitter maintainer", prset: &common.PRSet{ PRs: []*common.PRInfo{ { PR: &models.PullRequest{ User: &models.User{UserName: "submitter"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}}, }, Reviews: &common.PRReviews{}, }, { PR: &models.PullRequest{ User: &models.User{UserName: "foo"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prg"}}}, }, Reviews: &common.PRReviews{}, }, }, Config: &common.AutogitConfig{ GitProjectName: "prg/repo#main", Organization: "org", Branch: "main", Reviewers: []string{"-user1", "submitter"}, }, }, maintainers: &common.MaintainershipMap{Data: map[string][]string{"pkg": []string{"submitter"}}}, expected_missing_reviewers: [][]string{ []string{}, []string{"autogits_obs_staging_bot", "user1"}, }, }, { name: "Add reviewer if also maintainer where review by maintainer is not needed", prset: &common.PRSet{ PRs: []*common.PRInfo{ { PR: &models.PullRequest{ User: &models.User{UserName: "submitter"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}}, }, Reviews: &common.PRReviews{}, }, { PR: &models.PullRequest{ User: &models.User{UserName: "bot"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prg"}}}, }, Reviews: &common.PRReviews{}, }, }, Config: &common.AutogitConfig{ GitProjectName: "prg/repo#main", Organization: "org", Branch: "main", Reviewers: []string{"-user1", "submitter", "*reviewer"}, }, BotUser: "bot", }, maintainers: &common.MaintainershipMap{Data: map[string][]string{"pkg": []string{"submitter", "reviewer"}, "": []string{"reviewer"}}}, expected_missing_reviewers: [][]string{ []string{"reviewer"}, []string{"autogits_obs_staging_bot", "reviewer", "user1"}, }, }, { name: "Dont remove reviewer if also maintainer", prset: &common.PRSet{ PRs: []*common.PRInfo{ { PR: &models.PullRequest{ User: &models.User{UserName: "submitter"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}}, }, Reviews: &common.PRReviews{ Reviews: []*models.PullReview{{State: common.ReviewStateRequestReview, User: &models.User{UserName: "reviewer"}}}, RequestedReviewers: []string{"reviewer"}, FullTimeline: []*models.TimelineComment{{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "reviewer"}}}, }, }, { PR: &models.PullRequest{ User: &models.User{UserName: "bot"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prg"}}}, }, Reviews: &common.PRReviews{ Reviews: []*models.PullReview{{State: common.ReviewStateRequestReview, User: &models.User{UserName: "reviewer"}}}, RequestedReviewers: []string{"reviewer"}, FullTimeline: []*models.TimelineComment{{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "reviewer"}}}, }, }, }, Config: &common.AutogitConfig{ GitProjectName: "prg/repo#main", Organization: "org", Branch: "main", Reviewers: []string{"-user1", "submitter", "*reviewer"}, }, BotUser: "bot", }, maintainers: &common.MaintainershipMap{Data: map[string][]string{"pkg": []string{"submitter", "reviewer"}, "": []string{"reviewer"}}}, expected_missing_reviewers: [][]string{ []string{}, []string{"autogits_obs_staging_bot", "user1"}, }, }, { name: "Extra project reviewer on the package", prset: &common.PRSet{ PRs: []*common.PRInfo{ { PR: &models.PullRequest{ User: &models.User{UserName: "submitter"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}}, }, Reviews: &common.PRReviews{ Reviews: []*models.PullReview{ {State: common.ReviewStateApproved, User: &models.User{UserName: "user2"}}, {State: common.ReviewStateApproved, User: &models.User{UserName: "pkgmaintainer"}}, {State: common.ReviewStatePending, User: &models.User{UserName: "prjmaintainer"}}, }, RequestedReviewers: []string{"user2", "pkgmaintainer", "prjmaintainer"}, FullTimeline: []*models.TimelineComment{ {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "user2"}}, {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "pkgmaintainer"}}, {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "prjmaintainer"}}, }, }, }, { PR: &models.PullRequest{ User: &models.User{UserName: "bot"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prg"}}}, }, Reviews: &common.PRReviews{ Reviews: []*models.PullReview{ {State: common.ReviewStateRequestChanges, User: &models.User{UserName: "user1"}}, {State: common.ReviewStateRequestReview, User: &models.User{UserName: "autogits_obs_staging_bot"}}, }, RequestedReviewers: []string{"user1", "autogits_obs_staging_bot"}, FullTimeline: []*models.TimelineComment{ {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "user1"}}, {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "autogits_obs_staging_bot"}}, }, }, }, }, Config: &common.AutogitConfig{ GitProjectName: "prg/repo#main", Organization: "org", Branch: "main", Reviewers: []string{"-user1", "submitter"}, }, BotUser: "bot", }, maintainers: &common.MaintainershipMap{Data: map[string][]string{"pkg": []string{"pkgmaintainer"}, "": {"prjmaintainer"}}}, expected_missing_reviewers: [][]string{}, expected_extra_reviewers: [][]string{{"prjmaintainer"}}, }, { name: "Extra project reviewers on the package and project", prset: &common.PRSet{ PRs: []*common.PRInfo{ { PR: &models.PullRequest{ User: &models.User{UserName: "submitter"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}}, }, Reviews: &common.PRReviews{ Reviews: []*models.PullReview{ {State: common.ReviewStateApproved, User: &models.User{UserName: "user2"}}, {State: common.ReviewStateApproved, User: &models.User{UserName: "pkgmaintainer"}}, {State: common.ReviewStatePending, User: &models.User{UserName: "prjmaintainer"}}, {State: common.ReviewStatePending, User: &models.User{UserName: "pkgm1"}}, {State: common.ReviewStatePending, User: &models.User{UserName: "pkgm2"}}, {State: common.ReviewStatePending, User: &models.User{UserName: "prj1"}}, {State: common.ReviewStatePending, User: &models.User{UserName: "prj2"}}, {State: common.ReviewStatePending, User: &models.User{UserName: "someother"}}, }, RequestedReviewers: []string{"user2", "pkgmaintainer", "prjmaintainer", "pkgm1", "pkgm2", "someother", "prj1", "prj2"}, FullTimeline: []*models.TimelineComment{ {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "pkgmaintainer"}}, {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "prjmaintainer"}}, {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "prj1"}}, {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "prj2"}}, {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "pkgm1"}}, {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "pkgm2"}}, }, }, }, { PR: &models.PullRequest{ User: &models.User{UserName: "bot"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prg"}}}, }, Reviews: &common.PRReviews{ Reviews: []*models.PullReview{ {State: common.ReviewStateRequestChanges, User: &models.User{UserName: "user1"}}, {State: common.ReviewStateRequestReview, User: &models.User{UserName: "autogits_obs_staging_bot"}}, {State: common.ReviewStatePending, User: &models.User{UserName: "prj1"}}, {State: common.ReviewStatePending, User: &models.User{UserName: "prj2"}}, }, RequestedReviewers: []string{"user1", "autogits_obs_staging_bot", "prj1", "prj2"}, FullTimeline: []*models.TimelineComment{ {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "autogits_obs_staging_bot"}}, {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "prj1"}}, {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "prj2"}}, }, }, }, }, Config: &common.AutogitConfig{ GitProjectName: "prg/repo#main", Organization: "org", Branch: "main", Reviewers: []string{"-user1", "submitter"}, }, BotUser: "bot", }, maintainers: &common.MaintainershipMap{Data: map[string][]string{"pkg": []string{"pkgmaintainer", "pkgm1", "pkgm2"}, "": {"prjmaintainer", "prj1", "prj2"}}}, expected_missing_reviewers: [][]string{}, expected_extra_reviewers: [][]string{{"pkgm1", "pkgm2", "prj1", "prj2", "prjmaintainer"}, {"prj1", "prj2"}}, }, { name: "No extra project reviewers on the package and project (all pending)", prset: &common.PRSet{ PRs: []*common.PRInfo{ { PR: &models.PullRequest{ User: &models.User{UserName: "submitter"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}}, }, Reviews: &common.PRReviews{ Reviews: []*models.PullReview{ {State: common.ReviewStateApproved, User: &models.User{UserName: "user2"}}, {State: common.ReviewStateRequestReview, User: &models.User{UserName: "pkgmaintainer"}}, {State: common.ReviewStateRequestReview, User: &models.User{UserName: "prjmaintainer"}}, {State: common.ReviewStateRequestReview, User: &models.User{UserName: "pkgm1"}}, {State: common.ReviewStateRequestReview, User: &models.User{UserName: "prj1"}}, {State: common.ReviewStateRequestReview, User: &models.User{UserName: "someother"}}, }, RequestedReviewers: []string{"user2", "pkgmaintainer", "prjmaintainer", "pkgm1", "someother", "prj1"}, FullTimeline: []*models.TimelineComment{ {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "pkgm1"}}, {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "pkgmaintainer"}}, {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "prjmaintainer"}}, {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "prj1"}}, {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "!bot"}, Assignee: &models.User{UserName: "someother"}}, }, }, }, { PR: &models.PullRequest{ User: &models.User{UserName: "bot"}, Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prj"}}}, }, Reviews: &common.PRReviews{ Reviews: []*models.PullReview{ {State: common.ReviewStateRequestChanges, User: &models.User{UserName: "user1"}}, {State: common.ReviewStateRequestReview, User: &models.User{UserName: "autogits_obs_staging_bot"}}, {State: common.ReviewStateRequestReview, User: &models.User{UserName: "prj1"}}, }, RequestedReviewers: []string{"user1", "autogits_obs_staging_bot", "prj1"}, FullTimeline: []*models.TimelineComment{ {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "autogits_obs_staging_bot"}}, {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "prj1"}}, {Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "!bot"}, Assignee: &models.User{UserName: "user1"}}, }, }, }, }, Config: &common.AutogitConfig{ GitProjectName: "prj/repo#main", Organization: "org", Branch: "main", Reviewers: []string{"-user1", "submitter"}, }, BotUser: "bot", }, maintainers: &common.MaintainershipMap{Data: map[string][]string{"pkg": []string{"pkgmaintainer", "pkgm1", "pkgm2"}, "": {"prjmaintainer", "prj1", "prj2"}}}, expected_missing_reviewers: [][]string{{"pkgm2", "prj2"}}, expected_extra_reviewers: [][]string{{}, {"prj1"}}, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { test.prset.HasAutoStaging = !test.noAutoStaging for idx, pr := range test.prset.PRs { missing, extra := test.prset.FindMissingAndExtraReviewers(test.maintainers, idx) // avoid nil dereference below, by adding empty array elements if idx >= len(test.expected_missing_reviewers) { test.expected_missing_reviewers = append(test.expected_missing_reviewers, nil) } if idx >= len(test.expected_extra_reviewers) { test.expected_extra_reviewers = append(test.expected_extra_reviewers, nil) } slices.Sort(test.expected_extra_reviewers[idx]) slices.Sort(test.expected_missing_reviewers[idx]) if slices.Compare(missing, test.expected_missing_reviewers[idx]) != 0 { t.Error("Expected missing reviewers for", common.PRtoString(pr.PR), ":", test.expected_missing_reviewers[idx], "but have:", missing) } if slices.Compare(extra, test.expected_extra_reviewers[idx]) != 0 { t.Error("Expected reviewers to remove for", common.PRtoString(pr.PR), ":", test.expected_extra_reviewers[idx], "but have:", extra) } } }) } } func TestPRMerge(t *testing.T) { t.Skip("FAIL: No PrjGit PR found, missing calls") repoDir := t.TempDir() cwd, _ := os.Getwd() cmd := exec.Command(path.Join(cwd, "test_repo_setup.sh")) cmd.Dir = repoDir 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: "org/prj#master", } 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: "file://" + path.Join(repoDir, "prjgit"), }, }, Head: &models.PRBranchInfo{ Sha: "88584433de1c917c1d773f62b82381848d882491940b5e9b427a540aa9057d9a", // "base_add_b2" }, }, mergeError: "Aborting merge", }, { name: "Merge conflict in modules, auto-resolved", pr: &models.PullRequest{ Base: &models.PRBranchInfo{ Sha: "4fbd1026b2d7462ebe9229a49100c11f1ad6555520a21ba515122d8bc41328a8", Repo: &models.Repository{ Name: "prj", Owner: &models.User{ UserName: "org", }, SSHURL: "file://" + 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.NewMockGiteaPRTimelineReviewFetcher(ctl) reviewUnrequestMock := mock_common.NewMockGiteaReviewUnrequester(ctl) reviewUnrequestMock.EXPECT().UnrequestReview(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) testDir := t.TempDir() t.Log("dir:", testDir) mock.EXPECT().GetPullRequest("org", "prj", int64(1)).Return(test.pr, nil) set, err := common.FetchPRSet("test", mock, "org", "prj", 1, config) if err != nil { t.Fatal(err) } gh, _ := common.AllocateGitWorkTree(testDir, "", "") git, err := gh.CreateGitHandler("org") err = set.Merge(reviewUnrequestMock, git) if err != nil && (test.mergeError == "" || (len(test.mergeError) > 0 && !strings.Contains(err.Error(), test.mergeError))) { os.CopyFS("/tmp/upstream", os.DirFS(repoDir)) os.CopyFS("/tmp/out", os.DirFS(testDir)) t.Fatal(err) } }) } } func TestPRChanges(t *testing.T) { t.Skip("FAIL: unexpected calls, missing calls") tests := []struct { name string PRs []*models.PullRequest PrjPRs *models.PullRequest }{ { name: "Pkg PR is closed", PRs: []*models.PullRequest{ { Base: &models.PRBranchInfo{Repo: &models.Repository{Owner: &models.User{UserName: "org"}, Name: "repo"}}, Index: 42, State: "merged", }, }, PrjPRs: &models.PullRequest{ Title: "some PR", Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "prjgit", Owner: &models.User{UserName: "org"}}}, Body: "PR: org/repo#42", State: "opened", }, }, } config := common.AutogitConfig{ Branch: "main", GitProjectName: "org/prjgit#branch", } for _, test := range tests { 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) for _, pr := range test.PRs { mock_fetcher.EXPECT().GetPullRequest(pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Index).Return(pr, nil) } PRs, err := common.FetchPRSet("user", mock_fetcher, "org", "repo", 42, &config) if err != nil { t.Fatal(err) } if PRs.IsConsistent() { t.Fatal("Inconsistent set!") } }) } }