Fix automerge will not work because of some events haven't been triggered (#30780)

Replace #25741
Close #24445
Close #30658
Close #20646
~Depends on #30805~

Since #25741 has been rewritten totally, to make the contribution
easier, I will continue the work in this PR. Thanks @6543

---------

Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
(cherry picked from commit c6cf96d31d80ab79d370a6192fd761b4443daec2)

Conflicts:
	tests/integration/editor_test.go
	trivial context conflict because of 75ce1e2ac1 [GITEA] Allow user to select email for file operations in Web UI
	tests/integration/pull_merge_test.go
	trivial context conflicts in imports because more tests were added in Forgejo
This commit is contained in:
Lunny Xiao 2024-05-21 23:23:22 +08:00 committed by Earl Warren
parent 0e0ab349fb
commit 7d7ea45465
No known key found for this signature in database
GPG key ID: 0579CB2928A78A00
7 changed files with 347 additions and 67 deletions

View file

@ -30,28 +30,32 @@ import (
func TestCreateFile(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user2")
// Request editor page
req := NewRequest(t, "GET", "/user2/repo1/_new/master/")
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
lastCommit := doc.GetInputValueByName("last_commit")
assert.NotEmpty(t, lastCommit)
// Save new file to master branch
req = NewRequestWithValues(t, "POST", "/user2/repo1/_new/master/", map[string]string{
"_csrf": doc.GetCSRF(),
"last_commit": lastCommit,
"tree_path": "test.txt",
"content": "Content",
"commit_choice": "direct",
"commit_mail_id": "3",
})
session.MakeRequest(t, req, http.StatusSeeOther)
testCreateFile(t, session, "user2", "repo1", "master", "test.txt", "Content")
})
}
func testCreateFile(t *testing.T, session *TestSession, user, repo, branch, filePath, content string) *httptest.ResponseRecorder {
// Request editor page
newURL := fmt.Sprintf("/%s/%s/_new/%s/", user, repo, branch)
req := NewRequest(t, "GET", newURL)
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
lastCommit := doc.GetInputValueByName("last_commit")
assert.NotEmpty(t, lastCommit)
// Save new file to master branch
req = NewRequestWithValues(t, "POST", newURL, map[string]string{
"_csrf": doc.GetCSRF(),
"last_commit": lastCommit,
"tree_path": filePath,
"content": content,
"commit_choice": "direct",
"commit_mail_id": "3",
})
return session.MakeRequest(t, req, http.StatusSeeOther)
}
func TestCreateFileOnProtectedBranch(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user2")

View file

@ -12,6 +12,8 @@ import (
"net/url"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"testing"
"time"
@ -19,7 +21,9 @@ import (
"code.gitea.io/gitea/models"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
pull_model "code.gitea.io/gitea/models/pull"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@ -32,7 +36,9 @@ import (
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/services/automerge"
"code.gitea.io/gitea/services/pull"
commitstatus_service "code.gitea.io/gitea/services/repository/commitstatus"
files_service "code.gitea.io/gitea/services/repository/files"
webhook_service "code.gitea.io/gitea/services/webhook"
@ -661,3 +667,195 @@ func TestPullMergeIndexerNotifier(t *testing.T) {
}
})
}
func testResetRepo(t *testing.T, repoPath, branch, commitID string) {
f, err := os.OpenFile(filepath.Join(repoPath, "refs", "heads", branch), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)
assert.NoError(t, err)
_, err = f.WriteString(commitID + "\n")
assert.NoError(t, err)
f.Close()
repo, err := git.OpenRepository(context.Background(), repoPath)
assert.NoError(t, err)
defer repo.Close()
id, err := repo.GetBranchCommitID(branch)
assert.NoError(t, err)
assert.EqualValues(t, commitID, id)
}
func TestPullAutoMergeAfterCommitStatusSucceed(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// create a pull request
session := loginUser(t, "user1")
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
forkedName := "repo1-1"
testRepoFork(t, session, "user2", "repo1", "user1", forkedName)
defer func() {
testDeleteRepository(t, session, "user1", forkedName)
}()
testEditFile(t, session, "user1", forkedName, "master", "README.md", "Hello, World (Edited)\n")
testPullCreate(t, session, "user1", forkedName, false, "master", "master", "Indexer notifier test pull")
baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"})
forkedRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: forkedName})
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{
BaseRepoID: baseRepo.ID,
BaseBranch: "master",
HeadRepoID: forkedRepo.ID,
HeadBranch: "master",
})
// add protected branch for commit status
csrf := GetCSRF(t, session, "/user2/repo1/settings/branches")
// Change master branch to protected
req := NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/edit", map[string]string{
"_csrf": csrf,
"rule_name": "master",
"enable_push": "true",
"enable_status_check": "true",
"status_check_contexts": "gitea/actions",
})
session.MakeRequest(t, req, http.StatusSeeOther)
// first time insert automerge record, return true
scheduled, err := automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test")
assert.NoError(t, err)
assert.True(t, scheduled)
// second time insert automerge record, return false because it does exist
scheduled, err = automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test")
assert.Error(t, err)
assert.False(t, scheduled)
// reload pr again
pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID})
assert.False(t, pr.HasMerged)
assert.Empty(t, pr.MergedCommitID)
// update commit status to success, then it should be merged automatically
baseGitRepo, err := gitrepo.OpenRepository(db.DefaultContext, baseRepo)
assert.NoError(t, err)
sha, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
assert.NoError(t, err)
masterCommitID, err := baseGitRepo.GetBranchCommitID("master")
assert.NoError(t, err)
branches, _, err := baseGitRepo.GetBranchNames(0, 100)
assert.NoError(t, err)
assert.ElementsMatch(t, []string{"sub-home-md-img-check", "home-md-img-check", "pr-to-update", "branch2", "DefaultBranch", "develop", "feature/1", "master"}, branches)
baseGitRepo.Close()
defer func() {
testResetRepo(t, baseRepo.RepoPath(), "master", masterCommitID)
}()
err = commitstatus_service.CreateCommitStatus(db.DefaultContext, baseRepo, user1, sha, &git_model.CommitStatus{
State: api.CommitStatusSuccess,
TargetURL: "https://gitea.com",
Context: "gitea/actions",
})
assert.NoError(t, err)
time.Sleep(2 * time.Second)
// realod pr again
pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID})
assert.True(t, pr.HasMerged)
assert.NotEmpty(t, pr.MergedCommitID)
unittest.AssertNotExistsBean(t, &pull_model.AutoMerge{PullID: pr.ID})
})
}
func TestPullAutoMergeAfterCommitStatusSucceedAndApproval(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// create a pull request
session := loginUser(t, "user1")
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
forkedName := "repo1-2"
testRepoFork(t, session, "user2", "repo1", "user1", forkedName)
defer func() {
testDeleteRepository(t, session, "user1", forkedName)
}()
testEditFile(t, session, "user1", forkedName, "master", "README.md", "Hello, World (Edited)\n")
testPullCreate(t, session, "user1", forkedName, false, "master", "master", "Indexer notifier test pull")
baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"})
forkedRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: forkedName})
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{
BaseRepoID: baseRepo.ID,
BaseBranch: "master",
HeadRepoID: forkedRepo.ID,
HeadBranch: "master",
})
// add protected branch for commit status
csrf := GetCSRF(t, session, "/user2/repo1/settings/branches")
// Change master branch to protected
req := NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/edit", map[string]string{
"_csrf": csrf,
"rule_name": "master",
"enable_push": "true",
"enable_status_check": "true",
"status_check_contexts": "gitea/actions",
"required_approvals": "1",
})
session.MakeRequest(t, req, http.StatusSeeOther)
// first time insert automerge record, return true
scheduled, err := automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test")
assert.NoError(t, err)
assert.True(t, scheduled)
// second time insert automerge record, return false because it does exist
scheduled, err = automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test")
assert.Error(t, err)
assert.False(t, scheduled)
// reload pr again
pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID})
assert.False(t, pr.HasMerged)
assert.Empty(t, pr.MergedCommitID)
// update commit status to success, then it should be merged automatically
baseGitRepo, err := gitrepo.OpenRepository(db.DefaultContext, baseRepo)
assert.NoError(t, err)
sha, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
assert.NoError(t, err)
masterCommitID, err := baseGitRepo.GetBranchCommitID("master")
assert.NoError(t, err)
baseGitRepo.Close()
defer func() {
testResetRepo(t, baseRepo.RepoPath(), "master", masterCommitID)
}()
err = commitstatus_service.CreateCommitStatus(db.DefaultContext, baseRepo, user1, sha, &git_model.CommitStatus{
State: api.CommitStatusSuccess,
TargetURL: "https://gitea.com",
Context: "gitea/actions",
})
assert.NoError(t, err)
time.Sleep(2 * time.Second)
// reload pr again
pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID})
assert.False(t, pr.HasMerged)
assert.Empty(t, pr.MergedCommitID)
// approve the PR from non-author
approveSession := loginUser(t, "user2")
req = NewRequest(t, "GET", fmt.Sprintf("/user2/repo1/pulls/%d", pr.Index))
resp := approveSession.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
testSubmitReview(t, approveSession, htmlDoc.GetCSRF(), "user2", "repo1", strconv.Itoa(int(pr.Index)), sha, "approve", http.StatusOK)
time.Sleep(2 * time.Second)
// realod pr again
pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID})
assert.True(t, pr.HasMerged)
assert.NotEmpty(t, pr.MergedCommitID)
unittest.AssertNotExistsBean(t, &pull_model.AutoMerge{PullID: pr.ID})
})
}

View file

@ -425,10 +425,10 @@ func TestPullView_GivenApproveOrRejectReviewOnClosedPR(t *testing.T) {
htmlDoc := NewHTMLParser(t, resp.Body)
// Submit an approve review on the PR.
testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "approve", http.StatusUnprocessableEntity)
testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "", "approve", http.StatusUnprocessableEntity)
// Submit a reject review on the PR.
testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "reject", http.StatusUnprocessableEntity)
testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "", "reject", http.StatusUnprocessableEntity)
})
t.Run("Submit approve/reject review on closed PR", func(t *testing.T) {
@ -445,18 +445,18 @@ func TestPullView_GivenApproveOrRejectReviewOnClosedPR(t *testing.T) {
htmlDoc := NewHTMLParser(t, resp.Body)
// Submit an approve review on the PR.
testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "approve", http.StatusUnprocessableEntity)
testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "", "approve", http.StatusUnprocessableEntity)
// Submit a reject review on the PR.
testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "reject", http.StatusUnprocessableEntity)
testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "", "reject", http.StatusUnprocessableEntity)
})
})
}
func testSubmitReview(t *testing.T, session *TestSession, csrf, owner, repo, pullNumber, reviewType string, expectedSubmitStatus int) *httptest.ResponseRecorder {
func testSubmitReview(t *testing.T, session *TestSession, csrf, owner, repo, pullNumber, commitID, reviewType string, expectedSubmitStatus int) *httptest.ResponseRecorder {
options := map[string]string{
"_csrf": csrf,
"commit_id": "",
"commit_id": commitID,
"content": "test",
"type": reviewType,
}