Compare commits
15 Commits
group-bot-
...
pr-tests
| Author | SHA256 | Date | |
|---|---|---|---|
| f959684540 | |||
| 18f7ed658a | |||
| c05fa236d1 | |||
| c866303696 | |||
| e806d6ad0d | |||
| abf8aa58fc | |||
| 4f132ec154 | |||
| 86a7fd072e | |||
| 5f5e7d98b5 | |||
| e8738c9585 | |||
| 2f18adaa67 | |||
| b7f5c97de1 | |||
| 09001ce01b | |||
| 37c9cc7a57 | |||
| 362e481a09 |
@@ -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)
|
||||
|
||||
@@ -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://}
|
||||
|
||||
@@ -18,23 +18,20 @@ import (
|
||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
||||
)
|
||||
|
||||
type ReviewBot struct {
|
||||
configs common.AutogitConfigs
|
||||
acceptRx *regexp.Regexp
|
||||
rejectRx *regexp.Regexp
|
||||
groupName string
|
||||
gitea common.Gitea
|
||||
var configs common.AutogitConfigs
|
||||
var acceptRx *regexp.Regexp
|
||||
var rejectRx *regexp.Regexp
|
||||
var groupName string
|
||||
|
||||
func InitRegex(newGroupName string) {
|
||||
groupName = newGroupName
|
||||
acceptRx = regexp.MustCompile("^:\\s*(LGTM|approved?)")
|
||||
rejectRx = regexp.MustCompile("^:\\s*")
|
||||
}
|
||||
|
||||
func (bot *ReviewBot) InitRegex(newGroupName string) {
|
||||
bot.groupName = newGroupName
|
||||
bot.acceptRx = regexp.MustCompile("^:\\s*(LGTM|approved?)")
|
||||
bot.rejectRx = regexp.MustCompile("^:\\s*")
|
||||
}
|
||||
|
||||
func (bot *ReviewBot) ParseReviewLine(reviewText string) (bool, string) {
|
||||
func ParseReviewLine(reviewText string) (bool, string) {
|
||||
line := strings.TrimSpace(reviewText)
|
||||
groupTextName := "@" + bot.groupName
|
||||
groupTextName := "@" + groupName
|
||||
glen := len(groupTextName)
|
||||
if len(line) < glen || line[0:glen] != groupTextName {
|
||||
return false, line
|
||||
@@ -54,20 +51,20 @@ func (bot *ReviewBot) ParseReviewLine(reviewText string) (bool, string) {
|
||||
return false, line
|
||||
}
|
||||
|
||||
func (bot *ReviewBot) ReviewAccepted(reviewText string) bool {
|
||||
func ReviewAccepted(reviewText string) bool {
|
||||
for _, line := range common.SplitStringNoEmpty(reviewText, "\n") {
|
||||
if matched, reviewLine := bot.ParseReviewLine(line); matched {
|
||||
return bot.acceptRx.MatchString(reviewLine)
|
||||
if matched, reviewLine := ParseReviewLine(line); matched {
|
||||
return acceptRx.MatchString(reviewLine)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (bot *ReviewBot) ReviewRejected(reviewText string) bool {
|
||||
func ReviewRejected(reviewText string) bool {
|
||||
for _, line := range common.SplitStringNoEmpty(reviewText, "\n") {
|
||||
if matched, reviewLine := bot.ParseReviewLine(line); matched {
|
||||
if bot.rejectRx.MatchString(reviewLine) {
|
||||
return !bot.acceptRx.MatchString(reviewLine)
|
||||
if matched, reviewLine := ParseReviewLine(line); matched {
|
||||
if rejectRx.MatchString(reviewLine) {
|
||||
return !acceptRx.MatchString(reviewLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,10 +114,10 @@ var commentStrings = []string{
|
||||
"change_time_estimate",
|
||||
}*/
|
||||
|
||||
func (bot *ReviewBot) FindAcceptableReviewInTimeline(user string, timeline []*models.TimelineComment, reviews []*models.PullReview) *models.TimelineComment {
|
||||
func FindAcceptableReviewInTimeline(user string, timeline []*models.TimelineComment, reviews []*models.PullReview) *models.TimelineComment {
|
||||
for _, t := range timeline {
|
||||
if t.Type == common.TimelineCommentType_Comment && t.User.UserName == user && t.Created == t.Updated {
|
||||
if bot.ReviewAccepted(t.Body) || bot.ReviewRejected(t.Body) {
|
||||
if ReviewAccepted(t.Body) || ReviewRejected(t.Body) {
|
||||
return t
|
||||
}
|
||||
}
|
||||
@@ -129,9 +126,9 @@ func (bot *ReviewBot) FindAcceptableReviewInTimeline(user string, timeline []*mo
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bot *ReviewBot) FindOurLastReviewInTimeline(timeline []*models.TimelineComment) *models.TimelineComment {
|
||||
func FindOurLastReviewInTimeline(timeline []*models.TimelineComment) *models.TimelineComment {
|
||||
for _, t := range timeline {
|
||||
if t.Type == common.TimelineCommentType_Review && t.User.UserName == bot.groupName && t.Created == t.Updated {
|
||||
if t.Type == common.TimelineCommentType_Review && t.User.UserName == groupName && t.Created == t.Updated {
|
||||
return t
|
||||
}
|
||||
}
|
||||
@@ -139,13 +136,13 @@ func (bot *ReviewBot) FindOurLastReviewInTimeline(timeline []*models.TimelineCom
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bot *ReviewBot) UnrequestReviews(org, repo string, id int64, users []string) {
|
||||
if err := bot.gitea.UnrequestReview(org, repo, id, users...); err != nil {
|
||||
func UnrequestReviews(gitea common.Gitea, org, repo string, id int64, users []string) {
|
||||
if err := gitea.UnrequestReview(org, repo, id, users...); err != nil {
|
||||
common.LogError("Can't remove reviewrs after a review:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (bot *ReviewBot) ProcessNotifications(notification *models.NotificationThread) {
|
||||
func ProcessNotifications(notification *models.NotificationThread, gitea common.Gitea) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
common.LogInfo("panic cought --- recovered")
|
||||
@@ -172,14 +169,14 @@ func (bot *ReviewBot) ProcessNotifications(notification *models.NotificationThre
|
||||
id, _ := strconv.ParseInt(match[3], 10, 64)
|
||||
|
||||
common.LogInfo("processing:", fmt.Sprintf("%s/%s!%d", org, repo, id))
|
||||
pr, err := bot.gitea.GetPullRequest(org, repo, id)
|
||||
pr, err := gitea.GetPullRequest(org, repo, id)
|
||||
if err != nil {
|
||||
common.LogError(" ** Cannot fetch PR associated with review:", subject.URL, "Error:", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := bot.ProcessPR(pr); err == nil && !common.IsDryRun {
|
||||
if err := bot.gitea.SetNotificationRead(notification.ID); err != nil {
|
||||
if err := ProcessPR(pr); err == nil && !common.IsDryRun {
|
||||
if err := gitea.SetNotificationRead(notification.ID); err != nil {
|
||||
common.LogDebug(" Cannot set notification as read", err)
|
||||
}
|
||||
} else if err != nil && err != ReviewNotFinished {
|
||||
@@ -189,24 +186,24 @@ func (bot *ReviewBot) ProcessNotifications(notification *models.NotificationThre
|
||||
|
||||
var ReviewNotFinished = fmt.Errorf("Review is not finished")
|
||||
|
||||
func (bot *ReviewBot) ProcessPR(pr *models.PullRequest) error {
|
||||
func ProcessPR(pr *models.PullRequest) error {
|
||||
org := pr.Base.Repo.Owner.UserName
|
||||
repo := pr.Base.Repo.Name
|
||||
id := pr.Index
|
||||
|
||||
found := false
|
||||
for _, reviewer := range pr.RequestedReviewers {
|
||||
if reviewer != nil && reviewer.UserName == bot.groupName {
|
||||
if reviewer != nil && reviewer.UserName == groupName {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
common.LogInfo(" review is not requested for", bot.groupName)
|
||||
common.LogInfo(" review is not requested for", groupName)
|
||||
return nil
|
||||
}
|
||||
|
||||
config := bot.configs.GetPrjGitConfig(org, repo, pr.Base.Name)
|
||||
config := configs.GetPrjGitConfig(org, repo, pr.Base.Name)
|
||||
if config == nil {
|
||||
return fmt.Errorf("Cannot find config for: %s", pr.URL)
|
||||
}
|
||||
@@ -216,17 +213,17 @@ func (bot *ReviewBot) ProcessPR(pr *models.PullRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
reviews, err := bot.gitea.GetPullRequestReviews(org, repo, id)
|
||||
reviews, err := gitea.GetPullRequestReviews(org, repo, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to fetch reviews for: %v: %w", pr.URL, err)
|
||||
}
|
||||
|
||||
timeline, err := common.FetchTimelineSinceReviewRequestOrPush(bot.gitea, bot.groupName, pr.Head.Sha, org, repo, id)
|
||||
timeline, err := common.FetchTimelineSinceReviewRequestOrPush(gitea, groupName, pr.Head.Sha, org, repo, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to fetch timeline to review. %w", err)
|
||||
}
|
||||
|
||||
groupConfig, err := config.GetReviewGroup(bot.groupName)
|
||||
groupConfig, err := config.GetReviewGroup(groupName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to fetch review group. %w", err)
|
||||
}
|
||||
@@ -237,30 +234,30 @@ func (bot *ReviewBot) ProcessPR(pr *models.PullRequest) error {
|
||||
// pr.Head.Sha
|
||||
|
||||
for _, reviewer := range requestReviewers {
|
||||
if review := bot.FindAcceptableReviewInTimeline(reviewer, timeline, reviews); review != nil {
|
||||
if bot.ReviewAccepted(review.Body) {
|
||||
if review := FindAcceptableReviewInTimeline(reviewer, timeline, reviews); review != nil {
|
||||
if ReviewAccepted(review.Body) {
|
||||
if !common.IsDryRun {
|
||||
text := reviewer + " approved a review on behalf of " + bot.groupName
|
||||
if review := bot.FindOurLastReviewInTimeline(timeline); review == nil || review.Body != text {
|
||||
_, err := bot.gitea.AddReviewComment(pr, common.ReviewStateApproved, text)
|
||||
text := reviewer + " approved a review on behalf of " + groupName
|
||||
if review := FindOurLastReviewInTimeline(timeline); review == nil || review.Body != text {
|
||||
_, err := gitea.AddReviewComment(pr, common.ReviewStateApproved, text)
|
||||
if err != nil {
|
||||
common.LogError(" -> failed to write approval comment", err)
|
||||
}
|
||||
bot.UnrequestReviews(org, repo, id, requestReviewers)
|
||||
UnrequestReviews(gitea, org, repo, id, requestReviewers)
|
||||
}
|
||||
}
|
||||
common.LogInfo(" -> approved by", reviewer)
|
||||
common.LogInfo(" review at", review.Created)
|
||||
return nil
|
||||
} else if bot.ReviewRejected(review.Body) {
|
||||
} else if ReviewRejected(review.Body) {
|
||||
if !common.IsDryRun {
|
||||
text := reviewer + " requested changes on behalf of " + bot.groupName + ". See " + review.HTMLURL
|
||||
if review := bot.FindOurLastReviewInTimeline(timeline); review == nil || review.Body != text {
|
||||
_, err := bot.gitea.AddReviewComment(pr, common.ReviewStateRequestChanges, text)
|
||||
text := reviewer + " requested changes on behalf of " + groupName + ". See " + review.HTMLURL
|
||||
if review := FindOurLastReviewInTimeline(timeline); review == nil || review.Body != text {
|
||||
_, err := gitea.AddReviewComment(pr, common.ReviewStateRequestChanges, text)
|
||||
if err != nil {
|
||||
common.LogError(" -> failed to write rejecting comment", err)
|
||||
}
|
||||
bot.UnrequestReviews(org, repo, id, requestReviewers)
|
||||
UnrequestReviews(gitea, org, repo, id, requestReviewers)
|
||||
}
|
||||
}
|
||||
common.LogInfo(" -> declined by", reviewer)
|
||||
@@ -274,7 +271,7 @@ func (bot *ReviewBot) ProcessPR(pr *models.PullRequest) error {
|
||||
if !groupConfig.Silent && len(requestReviewers) > 0 {
|
||||
common.LogDebug(" Requesting reviews for:", requestReviewers)
|
||||
if !common.IsDryRun {
|
||||
if _, err := bot.gitea.RequestReviews(pr, requestReviewers...); err != nil {
|
||||
if _, err := gitea.RequestReviews(pr, requestReviewers...); err != nil {
|
||||
common.LogDebug(" -> err:", err)
|
||||
}
|
||||
} else {
|
||||
@@ -287,40 +284,42 @@ func (bot *ReviewBot) ProcessPR(pr *models.PullRequest) error {
|
||||
// add a helpful comment, if not yet added
|
||||
found_help_comment := false
|
||||
for _, t := range timeline {
|
||||
if t.Type == common.TimelineCommentType_Comment && t.User != nil && t.User.UserName == bot.groupName {
|
||||
if t.Type == common.TimelineCommentType_Comment && t.User != nil && t.User.UserName == groupName {
|
||||
found_help_comment = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found_help_comment && !common.IsDryRun {
|
||||
helpComment := fmt.Sprintln("Review by", bot.groupName, "represents a group of reviewers:", strings.Join(requestReviewers, ", "), ".\n\n"+
|
||||
helpComment := fmt.Sprintln("Review by", groupName, "represents a group of reviewers:", strings.Join(requestReviewers, ", "), ".\n\n"+
|
||||
"Do **not** use standard review interface to review on behalf of the group.\n"+
|
||||
"To accept the review on behalf of the group, create the following comment: `@"+bot.groupName+": approve`.\n"+
|
||||
"To request changes on behalf of the group, create the following comment: `@"+bot.groupName+": decline` followed with lines justifying the decision.\n"+
|
||||
"To accept the review on behalf of the group, create the following comment: `@"+groupName+": approve`.\n"+
|
||||
"To request changes on behalf of the group, create the following comment: `@"+groupName+": decline` followed with lines justifying the decision.\n"+
|
||||
"Future edits of the comments are ignored, a new comment is required to change the review state.")
|
||||
if slices.Contains(groupConfig.Reviewers, pr.User.UserName) {
|
||||
helpComment = helpComment + "\n\n" +
|
||||
"Submitter is member of this review group, hence they are excluded from being one of the reviewers here"
|
||||
}
|
||||
bot.gitea.AddComment(pr, helpComment)
|
||||
gitea.AddComment(pr, helpComment)
|
||||
}
|
||||
|
||||
return ReviewNotFinished
|
||||
}
|
||||
|
||||
func (bot *ReviewBot) PeriodReviewCheck() {
|
||||
notifications, err := bot.gitea.GetNotifications(common.GiteaNotificationType_Pull, nil)
|
||||
func PeriodReviewCheck() {
|
||||
notifications, err := gitea.GetNotifications(common.GiteaNotificationType_Pull, nil)
|
||||
if err != nil {
|
||||
common.LogError(" Error fetching unread notifications: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, notification := range notifications {
|
||||
bot.ProcessNotifications(notification)
|
||||
ProcessNotifications(notification, gitea)
|
||||
}
|
||||
}
|
||||
|
||||
var gitea common.Gitea
|
||||
|
||||
func main() {
|
||||
giteaUrl := flag.String("gitea-url", "https://src.opensuse.org", "Gitea instance used for reviews")
|
||||
rabbitMqHost := flag.String("rabbit-url", "amqps://rabbit.opensuse.org", "RabbitMQ instance where Gitea webhook notifications are sent")
|
||||
@@ -356,7 +355,7 @@ func main() {
|
||||
flag.Usage()
|
||||
return
|
||||
}
|
||||
targetGroupName := args[0]
|
||||
groupName = args[0]
|
||||
|
||||
if *configFile == "" {
|
||||
common.LogError("Missing config file")
|
||||
@@ -379,14 +378,14 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
giteaTransport := common.AllocateGiteaTransport(*giteaUrl)
|
||||
configs, err := common.ResolveWorkflowConfigs(giteaTransport, configData)
|
||||
gitea = common.AllocateGiteaTransport(*giteaUrl)
|
||||
configs, err = common.ResolveWorkflowConfigs(gitea, configData)
|
||||
if err != nil {
|
||||
common.LogError("Cannot parse workflow configs:", err)
|
||||
return
|
||||
}
|
||||
|
||||
reviewer, err := giteaTransport.GetCurrentUser()
|
||||
reviewer, err := gitea.GetCurrentUser()
|
||||
if err != nil {
|
||||
common.LogError("Cannot fetch review user:", err)
|
||||
return
|
||||
@@ -396,18 +395,14 @@ func main() {
|
||||
*interval = 1
|
||||
}
|
||||
|
||||
bot := &ReviewBot{
|
||||
gitea: giteaTransport,
|
||||
configs: configs,
|
||||
}
|
||||
bot.InitRegex(targetGroupName)
|
||||
InitRegex(groupName)
|
||||
|
||||
common.LogInfo(" ** processing group reviews for group:", bot.groupName)
|
||||
common.LogInfo(" ** processing group reviews for group:", groupName)
|
||||
common.LogInfo(" ** username in Gitea:", reviewer.UserName)
|
||||
common.LogInfo(" ** polling interval:", *interval, "min")
|
||||
common.LogInfo(" ** connecting to RabbitMQ:", *rabbitMqHost)
|
||||
|
||||
if bot.groupName != reviewer.UserName {
|
||||
if groupName != reviewer.UserName {
|
||||
common.LogError(" ***** Reviewer does not match group name. Aborting. *****")
|
||||
return
|
||||
}
|
||||
@@ -419,13 +414,10 @@ func main() {
|
||||
}
|
||||
|
||||
config_update := ConfigUpdatePush{
|
||||
bot: bot,
|
||||
config_modified: make(chan *common.AutogitConfig),
|
||||
}
|
||||
|
||||
process_issue_pr := IssueCommentProcessor{
|
||||
bot: bot,
|
||||
}
|
||||
process_issue_pr := IssueCommentProcessor{}
|
||||
|
||||
configUpdates := &common.RabbitMQGiteaEventsProcessor{
|
||||
Orgs: []string{},
|
||||
@@ -435,7 +427,7 @@ func main() {
|
||||
},
|
||||
}
|
||||
configUpdates.Connection().RabbitURL = u
|
||||
for _, c := range bot.configs {
|
||||
for _, c := range configs {
|
||||
if org, _, _ := c.GetPrjGit(); !slices.Contains(configUpdates.Orgs, org) {
|
||||
configUpdates.Orgs = append(configUpdates.Orgs, org)
|
||||
}
|
||||
@@ -448,17 +440,17 @@ func main() {
|
||||
select {
|
||||
case configTouched, ok := <-config_update.config_modified:
|
||||
if ok {
|
||||
for idx, c := range bot.configs {
|
||||
for idx, c := range configs {
|
||||
if c == configTouched {
|
||||
org, repo, branch := c.GetPrjGit()
|
||||
prj := fmt.Sprintf("%s/%s#%s", org, repo, branch)
|
||||
common.LogInfo("Detected config update for", prj)
|
||||
|
||||
new_config, err := common.ReadWorkflowConfig(bot.gitea, prj)
|
||||
new_config, err := common.ReadWorkflowConfig(gitea, prj)
|
||||
if err != nil {
|
||||
common.LogError("Failed parsing Project config for", prj, err)
|
||||
} else {
|
||||
bot.configs[idx] = new_config
|
||||
configs[idx] = new_config
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -468,7 +460,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
bot.PeriodReviewCheck()
|
||||
PeriodReviewCheck()
|
||||
time.Sleep(time.Duration(*interval * int64(time.Minute)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,359 +1,6 @@
|
||||
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 TestProcessPR(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
mockGitea := mock_common.NewMockGitea(ctrl)
|
||||
groupName := "testgroup"
|
||||
|
||||
bot := &ReviewBot{
|
||||
gitea: mockGitea,
|
||||
groupName: groupName,
|
||||
}
|
||||
bot.InitRegex(groupName)
|
||||
|
||||
org := "myorg"
|
||||
repo := "myrepo"
|
||||
prIndex := int64(1)
|
||||
headSha := "abcdef123456"
|
||||
|
||||
pr := &models.PullRequest{
|
||||
Index: prIndex,
|
||||
URL: "http://gitea/pr/1",
|
||||
State: "open",
|
||||
Base: &models.PRBranchInfo{
|
||||
Name: "main",
|
||||
Repo: &models.Repository{
|
||||
Name: repo,
|
||||
Owner: &models.User{
|
||||
UserName: org,
|
||||
},
|
||||
},
|
||||
},
|
||||
Head: &models.PRBranchInfo{
|
||||
Sha: headSha,
|
||||
},
|
||||
User: &models.User{
|
||||
UserName: "submitter",
|
||||
},
|
||||
RequestedReviewers: []*models.User{
|
||||
{UserName: groupName},
|
||||
},
|
||||
}
|
||||
|
||||
prjConfig := &common.AutogitConfig{
|
||||
GitProjectName: org + "/" + repo + "#main",
|
||||
ReviewGroups: []*common.ReviewGroup{
|
||||
{
|
||||
Name: groupName,
|
||||
Reviewers: []string{"reviewer1", "reviewer2"},
|
||||
},
|
||||
},
|
||||
}
|
||||
bot.configs = common.AutogitConfigs{prjConfig}
|
||||
|
||||
t.Run("Review not requested for group", func(t *testing.T) {
|
||||
prNoRequest := *pr
|
||||
prNoRequest.RequestedReviewers = nil
|
||||
err := bot.ProcessPR(&prNoRequest)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("PR is closed", func(t *testing.T) {
|
||||
prClosed := *pr
|
||||
prClosed.State = "closed"
|
||||
err := bot.ProcessPR(&prClosed)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Successful Approval", func(t *testing.T) {
|
||||
common.IsDryRun = false
|
||||
mockGitea.EXPECT().GetPullRequestReviews(org, repo, prIndex).Return(nil, nil)
|
||||
// reviewer1 approved in timeline
|
||||
timeline := []*models.TimelineComment{
|
||||
{
|
||||
Type: common.TimelineCommentType_Comment,
|
||||
User: &models.User{UserName: "reviewer1"},
|
||||
Body: "@" + groupName + ": approve",
|
||||
},
|
||||
}
|
||||
|
||||
mockGitea.EXPECT().GetTimeline(org, repo, prIndex).Return(timeline, nil)
|
||||
|
||||
expectedText := "reviewer1 approved a review on behalf of " + groupName
|
||||
mockGitea.EXPECT().AddReviewComment(pr, common.ReviewStateApproved, expectedText).Return(nil, nil)
|
||||
mockGitea.EXPECT().UnrequestReview(org, repo, prIndex, gomock.Any()).Return(nil)
|
||||
|
||||
err := bot.ProcessPR(pr)
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil error, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Dry Run - No actions taken", func(t *testing.T) {
|
||||
common.IsDryRun = true
|
||||
mockGitea.EXPECT().GetPullRequestReviews(org, repo, prIndex).Return(nil, nil)
|
||||
timeline := []*models.TimelineComment{
|
||||
{
|
||||
Type: common.TimelineCommentType_Comment,
|
||||
User: &models.User{UserName: "reviewer1"},
|
||||
Body: "@" + groupName + ": approve",
|
||||
},
|
||||
}
|
||||
mockGitea.EXPECT().GetTimeline(org, repo, prIndex).Return(timeline, nil)
|
||||
|
||||
// No AddReviewComment or UnrequestReview should be called
|
||||
err := bot.ProcessPR(pr)
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil error, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Approval already exists - No new comment", func(t *testing.T) {
|
||||
common.IsDryRun = false
|
||||
mockGitea.EXPECT().GetPullRequestReviews(org, repo, prIndex).Return(nil, nil)
|
||||
|
||||
approvalText := "reviewer1 approved a review on behalf of " + groupName
|
||||
timeline := []*models.TimelineComment{
|
||||
{
|
||||
Type: common.TimelineCommentType_Review,
|
||||
User: &models.User{UserName: groupName},
|
||||
Body: approvalText,
|
||||
},
|
||||
{
|
||||
Type: common.TimelineCommentType_Comment,
|
||||
User: &models.User{UserName: "reviewer1"},
|
||||
Body: "@" + groupName + ": approve",
|
||||
},
|
||||
{
|
||||
Type: common.TimelineCommentType_Comment,
|
||||
User: &models.User{UserName: groupName},
|
||||
Body: "Help comment",
|
||||
},
|
||||
}
|
||||
|
||||
mockGitea.EXPECT().GetTimeline(org, repo, prIndex).Return(timeline, nil)
|
||||
|
||||
// No AddReviewComment, UnrequestReview, or AddComment should be called
|
||||
err := bot.ProcessPR(pr)
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil error, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Rejection already exists - No new comment", func(t *testing.T) {
|
||||
common.IsDryRun = false
|
||||
mockGitea.EXPECT().GetPullRequestReviews(org, repo, prIndex).Return(nil, nil)
|
||||
|
||||
rejectionText := "reviewer1 requested changes on behalf of " + groupName + ". See http://gitea/comment/123"
|
||||
timeline := []*models.TimelineComment{
|
||||
{
|
||||
Type: common.TimelineCommentType_Review,
|
||||
User: &models.User{UserName: groupName},
|
||||
Body: rejectionText,
|
||||
},
|
||||
{
|
||||
Type: common.TimelineCommentType_Comment,
|
||||
User: &models.User{UserName: "reviewer1"},
|
||||
Body: "@" + groupName + ": decline",
|
||||
HTMLURL: "http://gitea/comment/123",
|
||||
},
|
||||
{
|
||||
Type: common.TimelineCommentType_Comment,
|
||||
User: &models.User{UserName: groupName},
|
||||
Body: "Help comment",
|
||||
},
|
||||
}
|
||||
|
||||
mockGitea.EXPECT().GetTimeline(org, repo, prIndex).Return(timeline, nil)
|
||||
|
||||
err := bot.ProcessPR(pr)
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil error, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Pending review - Help comment already exists", func(t *testing.T) {
|
||||
common.IsDryRun = false
|
||||
mockGitea.EXPECT().GetPullRequestReviews(org, repo, prIndex).Return(nil, nil)
|
||||
|
||||
timeline := []*models.TimelineComment{
|
||||
{
|
||||
Type: common.TimelineCommentType_Comment,
|
||||
User: &models.User{UserName: groupName},
|
||||
Body: "Some help comment",
|
||||
},
|
||||
}
|
||||
|
||||
mockGitea.EXPECT().GetTimeline(org, repo, prIndex).Return(timeline, nil)
|
||||
|
||||
// It will try to request reviews
|
||||
mockGitea.EXPECT().RequestReviews(pr, "reviewer1", "reviewer2").Return(nil, nil)
|
||||
|
||||
// AddComment should NOT be called because bot already has a comment in timeline
|
||||
err := bot.ProcessPR(pr)
|
||||
if err != ReviewNotFinished {
|
||||
t.Errorf("Expected ReviewNotFinished error, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Submitter is group member - Excluded from review request", func(t *testing.T) {
|
||||
common.IsDryRun = false
|
||||
prSubmitterMember := *pr
|
||||
prSubmitterMember.User = &models.User{UserName: "reviewer1"}
|
||||
mockGitea.EXPECT().GetPullRequestReviews(org, repo, prIndex).Return(nil, nil)
|
||||
mockGitea.EXPECT().GetTimeline(org, repo, prIndex).Return(nil, nil)
|
||||
mockGitea.EXPECT().RequestReviews(&prSubmitterMember, "reviewer2").Return(nil, nil)
|
||||
mockGitea.EXPECT().AddComment(&prSubmitterMember, gomock.Any()).Return(nil)
|
||||
err := bot.ProcessPR(&prSubmitterMember)
|
||||
if err != ReviewNotFinished {
|
||||
t.Errorf("Expected ReviewNotFinished error, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Successful Rejection", func(t *testing.T) {
|
||||
common.IsDryRun = false
|
||||
mockGitea.EXPECT().GetPullRequestReviews(org, repo, prIndex).Return(nil, nil)
|
||||
timeline := []*models.TimelineComment{
|
||||
{
|
||||
Type: common.TimelineCommentType_Comment,
|
||||
User: &models.User{UserName: "reviewer2"},
|
||||
Body: "@" + groupName + ": decline",
|
||||
HTMLURL: "http://gitea/comment/999",
|
||||
},
|
||||
}
|
||||
mockGitea.EXPECT().GetTimeline(org, repo, prIndex).Return(timeline, nil)
|
||||
expectedText := "reviewer2 requested changes on behalf of " + groupName + ". See http://gitea/comment/999"
|
||||
mockGitea.EXPECT().AddReviewComment(pr, common.ReviewStateRequestChanges, expectedText).Return(nil, nil)
|
||||
mockGitea.EXPECT().UnrequestReview(org, repo, prIndex, gomock.Any()).Return(nil)
|
||||
err := bot.ProcessPR(pr)
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil error, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Config not found", func(t *testing.T) {
|
||||
bot.configs = common.AutogitConfigs{}
|
||||
err := bot.ProcessPR(pr)
|
||||
if err == nil {
|
||||
t.Error("Expected error when config is missing, got nil")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Gitea error in GetPullRequestReviews", func(t *testing.T) {
|
||||
bot.configs = common.AutogitConfigs{prjConfig}
|
||||
mockGitea.EXPECT().GetPullRequestReviews(org, repo, prIndex).Return(nil, fmt.Errorf("gitea error"))
|
||||
err := bot.ProcessPR(pr)
|
||||
if err == nil {
|
||||
t.Error("Expected error from gitea, got nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestProcessNotifications(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
mockGitea := mock_common.NewMockGitea(ctrl)
|
||||
groupName := "testgroup"
|
||||
|
||||
bot := &ReviewBot{
|
||||
gitea: mockGitea,
|
||||
groupName: groupName,
|
||||
}
|
||||
bot.InitRegex(groupName)
|
||||
|
||||
org := "myorg"
|
||||
repo := "myrepo"
|
||||
prIndex := int64(123)
|
||||
notificationID := int64(456)
|
||||
|
||||
notification := &models.NotificationThread{
|
||||
ID: notificationID,
|
||||
Subject: &models.NotificationSubject{
|
||||
URL: fmt.Sprintf("http://gitea/api/v1/repos/%s/%s/pulls/%d", org, repo, prIndex),
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Notification Success", func(t *testing.T) {
|
||||
common.IsDryRun = false
|
||||
pr := &models.PullRequest{
|
||||
Index: prIndex,
|
||||
Base: &models.PRBranchInfo{
|
||||
Name: "main",
|
||||
Repo: &models.Repository{
|
||||
Name: repo,
|
||||
Owner: &models.User{UserName: org},
|
||||
},
|
||||
},
|
||||
|
||||
Head: &models.PRBranchInfo{
|
||||
Sha: "headsha",
|
||||
Repo: &models.Repository{
|
||||
Name: repo,
|
||||
Owner: &models.User{UserName: org},
|
||||
},
|
||||
},
|
||||
|
||||
User: &models.User{UserName: "submitter"},
|
||||
RequestedReviewers: []*models.User{{UserName: groupName}},
|
||||
}
|
||||
|
||||
mockGitea.EXPECT().GetPullRequest(org, repo, prIndex).Return(pr, nil)
|
||||
|
||||
prjConfig := &common.AutogitConfig{
|
||||
GitProjectName: org + "/" + repo + "#main",
|
||||
ReviewGroups: []*common.ReviewGroup{{Name: groupName, Reviewers: []string{"r1"}}},
|
||||
}
|
||||
bot.configs = common.AutogitConfigs{prjConfig}
|
||||
mockGitea.EXPECT().GetPullRequestReviews(org, repo, prIndex).Return(nil, nil)
|
||||
timeline := []*models.TimelineComment{
|
||||
{
|
||||
Type: common.TimelineCommentType_Comment,
|
||||
User: &models.User{UserName: "r1"},
|
||||
Body: "@" + groupName + ": approve",
|
||||
},
|
||||
}
|
||||
mockGitea.EXPECT().GetTimeline(org, repo, prIndex).Return(timeline, nil)
|
||||
expectedText := "r1 approved a review on behalf of " + groupName
|
||||
mockGitea.EXPECT().AddReviewComment(pr, common.ReviewStateApproved, expectedText).Return(nil, nil)
|
||||
mockGitea.EXPECT().UnrequestReview(org, repo, prIndex, gomock.Any()).Return(nil)
|
||||
|
||||
mockGitea.EXPECT().SetNotificationRead(notificationID).Return(nil)
|
||||
|
||||
bot.ProcessNotifications(notification)
|
||||
|
||||
})
|
||||
|
||||
t.Run("Invalid Notification URL", func(t *testing.T) {
|
||||
badNotification := &models.NotificationThread{
|
||||
Subject: &models.NotificationSubject{
|
||||
URL: "http://gitea/invalid/url",
|
||||
},
|
||||
}
|
||||
bot.ProcessNotifications(badNotification)
|
||||
})
|
||||
|
||||
t.Run("Gitea error in GetPullRequest", func(t *testing.T) {
|
||||
mockGitea.EXPECT().GetPullRequest(org, repo, prIndex).Return(nil, fmt.Errorf("gitea error"))
|
||||
bot.ProcessNotifications(notification)
|
||||
})
|
||||
}
|
||||
import "testing"
|
||||
|
||||
func TestReviewApprovalCheck(t *testing.T) {
|
||||
tests := []struct {
|
||||
@@ -413,78 +60,16 @@ func TestReviewApprovalCheck(t *testing.T) {
|
||||
InString: "@group2: disapprove",
|
||||
Rejected: true,
|
||||
},
|
||||
{
|
||||
Name: "Whitespace before colon",
|
||||
GroupName: "group",
|
||||
InString: "@group : LGTM",
|
||||
Approved: true,
|
||||
},
|
||||
{
|
||||
Name: "No whitespace after colon",
|
||||
GroupName: "group",
|
||||
InString: "@group:LGTM",
|
||||
Approved: true,
|
||||
},
|
||||
{
|
||||
Name: "Leading and trailing whitespace on line",
|
||||
GroupName: "group",
|
||||
InString: " @group: LGTM ",
|
||||
Approved: true,
|
||||
},
|
||||
{
|
||||
Name: "Multiline: Approved on second line",
|
||||
GroupName: "group",
|
||||
InString: "Random noise\n@group: approved",
|
||||
Approved: true,
|
||||
},
|
||||
{
|
||||
Name: "Multiline: Multiple group mentions, first wins",
|
||||
GroupName: "group",
|
||||
InString: "@group: decline\n@group: approve",
|
||||
Rejected: true,
|
||||
},
|
||||
{
|
||||
Name: "Multiline: Multiple group mentions, noise in between",
|
||||
GroupName: "group",
|
||||
InString: "noise\n@group: approve\nmore noise",
|
||||
Approved: true,
|
||||
},
|
||||
{
|
||||
Name: "Not at start of line (even with whitespace)",
|
||||
GroupName: "group",
|
||||
InString: "Hello @group: approve",
|
||||
Approved: false,
|
||||
},
|
||||
{
|
||||
Name: "Rejecting with reason",
|
||||
GroupName: "group",
|
||||
InString: "@group: decline because of X, Y and Z",
|
||||
Rejected: true,
|
||||
},
|
||||
{
|
||||
Name: "No colon after group",
|
||||
GroupName: "group",
|
||||
InString: "@group LGTM",
|
||||
Approved: false,
|
||||
Rejected: false,
|
||||
},
|
||||
{
|
||||
Name: "Invalid char after group",
|
||||
GroupName: "group",
|
||||
InString: "@group! LGTM",
|
||||
Approved: false,
|
||||
Rejected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
bot := &ReviewBot{}
|
||||
bot.InitRegex(test.GroupName)
|
||||
InitRegex(test.GroupName)
|
||||
|
||||
if r := bot.ReviewAccepted(test.InString); r != test.Approved {
|
||||
if r := ReviewAccepted(test.InString); r != test.Approved {
|
||||
t.Error("ReviewAccepted() returned", r, "expecting", test.Approved)
|
||||
}
|
||||
if r := bot.ReviewRejected(test.InString); r != test.Rejected {
|
||||
if r := ReviewRejected(test.InString); r != test.Rejected {
|
||||
t.Error("ReviewRejected() returned", r, "expecting", test.Rejected)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -7,9 +7,7 @@ import (
|
||||
"src.opensuse.org/autogits/common"
|
||||
)
|
||||
|
||||
type IssueCommentProcessor struct {
|
||||
bot *ReviewBot
|
||||
}
|
||||
type IssueCommentProcessor struct{}
|
||||
|
||||
func (s *IssueCommentProcessor) ProcessFunc(req *common.Request) error {
|
||||
if req.Type != common.RequestType_IssueComment {
|
||||
@@ -21,15 +19,14 @@ func (s *IssueCommentProcessor) ProcessFunc(req *common.Request) error {
|
||||
repo := data.Repository.Name
|
||||
index := int64(data.Issue.Number)
|
||||
|
||||
pr, err := s.bot.gitea.GetPullRequest(org, repo, index)
|
||||
pr, err := gitea.GetPullRequest(org, repo, index)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to fetch PullRequest from event: %s/%s!%d Error: %w", org, repo, index, err)
|
||||
}
|
||||
return s.bot.ProcessPR(pr)
|
||||
return ProcessPR(pr)
|
||||
}
|
||||
|
||||
type ConfigUpdatePush struct {
|
||||
bot *ReviewBot
|
||||
config_modified chan *common.AutogitConfig
|
||||
}
|
||||
|
||||
@@ -49,7 +46,7 @@ func (s *ConfigUpdatePush) ProcessFunc(req *common.Request) error {
|
||||
}
|
||||
branch := data.Ref[len(branch_ref):]
|
||||
|
||||
c := s.bot.configs.GetPrjGitConfig(org, repo, branch)
|
||||
c := configs.GetPrjGitConfig(org, repo, branch)
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -67,7 +64,7 @@ func (s *ConfigUpdatePush) ProcessFunc(req *common.Request) error {
|
||||
}
|
||||
|
||||
if modified_config {
|
||||
for _, config := range s.bot.configs {
|
||||
for _, config := range configs {
|
||||
if o, r, _ := config.GetPrjGit(); o == org && r == repo {
|
||||
s.config_modified <- config
|
||||
}
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
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 TestIssueCommentProcessor(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
mockGitea := mock_common.NewMockGitea(ctrl)
|
||||
groupName := "testgroup"
|
||||
bot := &ReviewBot{
|
||||
gitea: mockGitea,
|
||||
groupName: groupName,
|
||||
}
|
||||
bot.InitRegex(groupName)
|
||||
|
||||
processor := &IssueCommentProcessor{bot: bot}
|
||||
|
||||
org := "myorg"
|
||||
repo := "myrepo"
|
||||
index := 123
|
||||
|
||||
event := &common.IssueCommentWebhookEvent{
|
||||
Repository: &common.Repository{
|
||||
Name: repo,
|
||||
Owner: &common.Organization{
|
||||
Username: org,
|
||||
},
|
||||
},
|
||||
Issue: &common.IssueDetail{
|
||||
Number: index,
|
||||
},
|
||||
}
|
||||
|
||||
req := &common.Request{
|
||||
Type: common.RequestType_IssueComment,
|
||||
Data: event,
|
||||
}
|
||||
|
||||
t.Run("Successful Processing", func(t *testing.T) {
|
||||
pr := &models.PullRequest{
|
||||
Index: int64(index),
|
||||
Base: &models.PRBranchInfo{
|
||||
Name: "main",
|
||||
Repo: &models.Repository{
|
||||
Name: repo,
|
||||
Owner: &models.User{UserName: org},
|
||||
},
|
||||
},
|
||||
Head: &models.PRBranchInfo{
|
||||
Sha: "headsha",
|
||||
Repo: &models.Repository{
|
||||
Name: repo,
|
||||
Owner: &models.User{UserName: org},
|
||||
},
|
||||
},
|
||||
User: &models.User{UserName: "submitter"},
|
||||
RequestedReviewers: []*models.User{{UserName: groupName}},
|
||||
}
|
||||
|
||||
mockGitea.EXPECT().GetPullRequest(org, repo, int64(index)).Return(pr, nil)
|
||||
|
||||
prjConfig := &common.AutogitConfig{
|
||||
GitProjectName: org + "/" + repo + "#main",
|
||||
ReviewGroups: []*common.ReviewGroup{{Name: groupName, Reviewers: []string{"r1"}}},
|
||||
}
|
||||
bot.configs = common.AutogitConfigs{prjConfig}
|
||||
mockGitea.EXPECT().GetPullRequestReviews(org, repo, int64(index)).Return(nil, nil)
|
||||
mockGitea.EXPECT().GetTimeline(org, repo, int64(index)).Return(nil, nil)
|
||||
mockGitea.EXPECT().RequestReviews(pr, "r1").Return(nil, nil)
|
||||
mockGitea.EXPECT().AddComment(pr, gomock.Any()).Return(nil)
|
||||
|
||||
err := processor.ProcessFunc(req)
|
||||
if err != ReviewNotFinished {
|
||||
t.Errorf("Expected ReviewNotFinished, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Gitea error in GetPullRequest", func(t *testing.T) {
|
||||
mockGitea.EXPECT().GetPullRequest(org, repo, int64(index)).Return(nil, fmt.Errorf("gitea error"))
|
||||
err := processor.ProcessFunc(req)
|
||||
if err == nil {
|
||||
t.Error("Expected error, got nil")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Wrong Request Type", func(t *testing.T) {
|
||||
wrongReq := &common.Request{Type: common.RequestType_Push}
|
||||
err := processor.ProcessFunc(wrongReq)
|
||||
if err == nil {
|
||||
t.Error("Expected error for wrong request type, got nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestConfigUpdatePush(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
groupName := "testgroup"
|
||||
bot := &ReviewBot{
|
||||
groupName: groupName,
|
||||
}
|
||||
bot.InitRegex(groupName)
|
||||
|
||||
configChan := make(chan *common.AutogitConfig, 1)
|
||||
processor := &ConfigUpdatePush{
|
||||
bot: bot,
|
||||
config_modified: configChan,
|
||||
}
|
||||
|
||||
org := "myorg"
|
||||
repo := "myrepo"
|
||||
branch := "main"
|
||||
|
||||
prjConfig := &common.AutogitConfig{
|
||||
GitProjectName: org + "/" + repo + "#" + branch,
|
||||
Organization: org,
|
||||
Branch: branch,
|
||||
}
|
||||
bot.configs = common.AutogitConfigs{prjConfig}
|
||||
|
||||
event := &common.PushWebhookEvent{
|
||||
Ref: "refs/heads/" + branch,
|
||||
Repository: &common.Repository{
|
||||
Name: repo,
|
||||
Owner: &common.Organization{
|
||||
Username: org,
|
||||
},
|
||||
},
|
||||
Commits: []common.Commit{
|
||||
{
|
||||
Modified: []string{common.ProjectConfigFile},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
req := &common.Request{
|
||||
Type: common.RequestType_Push,
|
||||
Data: event,
|
||||
}
|
||||
|
||||
t.Run("Config Modified", func(t *testing.T) {
|
||||
err := processor.ProcessFunc(req)
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil error, got %v", err)
|
||||
}
|
||||
|
||||
select {
|
||||
case modified := <-configChan:
|
||||
if modified != prjConfig {
|
||||
t.Errorf("Expected modified config to be %v, got %v", prjConfig, modified)
|
||||
}
|
||||
default:
|
||||
t.Error("Expected config modification signal, but none received")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("No Config Modified", func(t *testing.T) {
|
||||
noConfigEvent := *event
|
||||
noConfigEvent.Commits = []common.Commit{{Modified: []string{"README.md"}}}
|
||||
noConfigReq := &common.Request{Type: common.RequestType_Push, Data: &noConfigEvent}
|
||||
|
||||
err := processor.ProcessFunc(noConfigReq)
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil error, got %v", err)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-configChan:
|
||||
t.Error("Did not expect config modification signal")
|
||||
default:
|
||||
// Success
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Wrong Branch Ref", func(t *testing.T) {
|
||||
wrongBranchEvent := *event
|
||||
wrongBranchEvent.Ref = "refs/tags/v1.0"
|
||||
wrongBranchReq := &common.Request{Type: common.RequestType_Push, Data: &wrongBranchEvent}
|
||||
|
||||
err := processor.ProcessFunc(wrongBranchReq)
|
||||
if err == nil {
|
||||
t.Error("Expected error for wrong branch ref, got nil")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Config Not Found", func(t *testing.T) {
|
||||
bot.configs = common.AutogitConfigs{}
|
||||
err := processor.ProcessFunc(req)
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil error even if config not found, got %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package main
|
||||
|
||||
//go:generate mockgen -source=pr_processor.go -destination=mock/pr_processor.go -typed
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -600,7 +598,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
|
||||
}
|
||||
@@ -626,6 +624,15 @@ func ProcesPullRequest(pr *models.PullRequest, configs []*common.AutogitConfig)
|
||||
return PRProcessor.Process(pr)
|
||||
}
|
||||
|
||||
func (w *RequestProcessor) Process(pr *models.PullRequest) error {
|
||||
configs, ok := w.configuredRepos[pr.Base.Repo.Owner.UserName]
|
||||
if !ok {
|
||||
common.LogError("*** Cannot find config for org:", pr.Base.Repo.Owner.UserName)
|
||||
return fmt.Errorf("*** Cannot find config for org: %s", pr.Base.Repo.Owner.UserName)
|
||||
}
|
||||
return ProcesPullRequest(pr, configs)
|
||||
}
|
||||
|
||||
func (w *RequestProcessor) ProcessFunc(request *common.Request) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
910
workflow-pr/pr_processor_test.go
Normal file
910
workflow-pr/pr_processor_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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)))
|
||||
|
||||
333
workflow-pr/repo_check_extended_test.go
Normal file
333
workflow-pr/repo_check_extended_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
@@ -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])
|
||||
|
||||
23
workflow-pr/state_checker.go
Normal file
23
workflow-pr/state_checker.go
Normal 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
|
||||
}
|
||||
87
workflow-pr/test_utils_test.go
Normal file
87
workflow-pr/test_utils_test.go
Normal file
@@ -0,0 +1,87 @@
|
||||
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{
|
||||
"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"},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user