diff --git a/tests/integration/actions_commit_status_test.go b/tests/integration/actions_commit_status_test.go index 4d33989c5d..7be40eb0fa 100644 --- a/tests/integration/actions_commit_status_test.go +++ b/tests/integration/actions_commit_status_test.go @@ -34,7 +34,7 @@ func TestActionsAutomerge(t *testing.T) { assert.False(t, pr.HasMerged, "PR should not be merged") assert.Equal(t, issues_model.PullRequestStatusMergeable, pr.Status, "PR should be mergeable") - scheduled, err := automerge.ScheduleAutoMerge(ctx, user, pr, repo_model.MergeStyleMerge, "Dummy", true) + scheduled, err := automerge.ScheduleAutoMerge(ctx, user, pr, repo_model.MergeStyleMerge, "Dummy", false) require.NoError(t, err, "PR should be scheduled for automerge") assert.True(t, scheduled, "PR should be scheduled for automerge") diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index 28f84eea80..786d7d4155 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -845,160 +845,99 @@ func TestPullMergeBranchProtect(t *testing.T) { }) } -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") +func testPullAutoMergeAfterCommitStatusSucceed(t *testing.T, forkName string, approval bool, deleteBranch bool) { + // create a pull request + session := loginUser(t, "user1") + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + testRepoFork(t, session, "user2", "repo1", "user1", forkName) + defer func() { + testDeleteRepository(t, session, "user1", forkName) + }() + if deleteBranch { + testEditFileToNewBranch(t, session, "user1", forkName, "master", "new_branch_1", "README.md", "Hello, World (Edited)\n") + testPullCreate(t, session, "user1", forkName, false, "master", "new_branch_1", "Indexer notifier test pull") + } else { + testEditFile(t, session, "user1", forkName, "master", "README.md", "Hello, World (Edited)\n") + testPullCreate(t, session, "user1", forkName, 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", true) - require.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", true) - require.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) - require.NoError(t, err) - sha, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName()) - require.NoError(t, err) - masterCommitID, err := baseGitRepo.GetBranchCommitID("master") - require.NoError(t, err) - - branches, _, err := baseGitRepo.GetBranchNames(0, 100) - require.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", - }) - require.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}) + baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"}) + forkedRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: forkName}) + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ + BaseRepoID: baseRepo.ID, + BaseBranch: "master", + HeadRepoID: forkedRepo.ID, + HeadBranch: "master", }) -} -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") + // add protected branch for commit status + csrf := GetCSRF(t, session, "/user2/repo1/settings/branches") - 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", - }) + // Change master branch to protected + reqParams := map[string]string{ + "_csrf": csrf, + "rule_name": "master", + "enable_push": "true", + "enable_status_check": "true", + "status_check_contexts": "gitea/actions", + } + if approval { + reqParams["required_approvals"] = "1" + } - // 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) + req := NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/edit", reqParams) + 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", true) + if deleteBranch { + // check if new branch exists + forkedGitRepo, err := gitrepo.OpenRepository(db.DefaultContext, forkedRepo) require.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", true) - require.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) + newBranch, err := forkedGitRepo.GetBranch("new_branch_1") require.NoError(t, err) - sha, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName()) - require.NoError(t, err) - masterCommitID, err := baseGitRepo.GetBranchCommitID("master") - require.NoError(t, err) - baseGitRepo.Close() - defer func() { - testResetRepo(t, baseRepo.RepoPath(), "master", masterCommitID) - }() + assert.NotNil(t, newBranch) + } - err = commitstatus_service.CreateCommitStatus(db.DefaultContext, baseRepo, user1, sha, &git_model.CommitStatus{ - State: api.CommitStatusSuccess, - TargetURL: "https://gitea.com", - Context: "gitea/actions", - }) - require.NoError(t, err) + // first time insert automerge record, return true + scheduled, err := automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test", false) + require.NoError(t, err) + assert.True(t, scheduled) - time.Sleep(2 * time.Second) + // 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", false) + require.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) + require.NoError(t, err) + sha, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName()) + require.NoError(t, err) + masterCommitID, err := baseGitRepo.GetBranchCommitID("master") + require.NoError(t, err) + + branches, _, err := baseGitRepo.GetBranchNames(0, 100) + require.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", + }) + require.NoError(t, err) + + time.Sleep(2 * time.Second) + + if approval { // reload pr again pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID}) assert.False(t, pr.HasMerged) @@ -1012,13 +951,62 @@ func TestPullAutoMergeAfterCommitStatusSucceedAndApproval(t *testing.T) { 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) + // 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}) + unittest.AssertNotExistsBean(t, &pull_model.AutoMerge{PullID: pr.ID}) + + if deleteBranch { + // check if new branch got removed + forkedGitRepo, err := gitrepo.OpenRepository(db.DefaultContext, forkedRepo) + require.NoError(t, err) + _, err = forkedGitRepo.GetBranch("new_branch_1") + require.Error(t, err) + assert.True(t, git.IsErrBranchNotExist(err)) + } +} + +func TestPullAutoMergeAfterCommitStatusSucceed(t *testing.T) { + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { + for _, testCase := range []struct { + name string + forkName string + approval bool + deleteBranch bool + }{ + { + name: "TestPullAutoMergeAfterCommitStatusSucceed", + forkName: "repo1-1", + approval: false, + deleteBranch: false, + }, + { + name: "TestPullAutoMergeAfterCommitStatusSucceedWithBranchDeletion", + forkName: "repo1-2", + approval: false, + deleteBranch: true, + }, + { + name: "TestPullAutoMergeAfterCommitStatusSucceedAndApproval", + forkName: "repo2-1", + approval: true, + deleteBranch: false, + }, + { + name: "TestPullAutoMergeAfterCommitStatusSucceedAndApprovalWithBranchDeletion", + forkName: "repo2-2", + approval: true, + deleteBranch: true, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + testPullAutoMergeAfterCommitStatusSucceed(t, testCase.forkName, testCase.approval, testCase.deleteBranch) + }) + } }) } @@ -1093,12 +1081,12 @@ func TestPullAutoMergeAfterCommitStatusSucceedAndApprovalForAgitFlow(t *testing. user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) // first time insert automerge record, return true - scheduled, err := automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test", true) + scheduled, err := automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test", false) require.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", true) + scheduled, err = automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test", false) require.Error(t, err) assert.False(t, scheduled) @@ -1150,3 +1138,146 @@ func TestPullAutoMergeAfterCommitStatusSucceedAndApprovalForAgitFlow(t *testing. unittest.AssertNotExistsBean(t, &pull_model.AutoMerge{PullID: pr.ID}) }) } + +func TestPullAutoMergeWithBranchDeletionAfterCommitStatusSucceedAndApprovalForAgitFlow(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + // create a pull request + baseAPITestContext := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) + + dstPath := t.TempDir() + + u.Path = baseAPITestContext.GitPath() + u.User = url.UserPassword("user2", userPassword) + + t.Run("Clone", doGitClone(dstPath, u)) + + err := os.WriteFile(path.Join(dstPath, "test_file"), []byte("## test content"), 0o666) + require.NoError(t, err) + + err = git.AddChanges(dstPath, true) + require.NoError(t, err) + + err = git.CommitChanges(dstPath, git.CommitChangesOptions{ + Committer: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Author: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Message: "Testing commit 1", + }) + require.NoError(t, err) + + stderrBuf := &bytes.Buffer{} + + err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/master", "-o"). + AddDynamicArguments(`topic=test/head2`). + AddArguments("-o"). + AddDynamicArguments(`title="create a test pull request with agit"`). + AddArguments("-o"). + AddDynamicArguments(`description="This PR is a test pull request which created with agit"`). + Run(&git.RunOpts{Dir: dstPath, Stderr: stderrBuf}) + require.NoError(t, err) + + assert.Contains(t, stderrBuf.String(), setting.AppURL+"user2/repo1/pulls/6") + + baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"}) + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ + Flow: issues_model.PullRequestFlowAGit, + BaseRepoID: baseRepo.ID, + BaseBranch: "master", + HeadRepoID: baseRepo.ID, + HeadBranch: "user2/test/head2", + }) + + session := loginUser(t, "user1") + // 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) + + // check if new branch exists + baseGitRepo, err := gitrepo.OpenRepository(db.DefaultContext, baseRepo) + require.NoError(t, err) + newBranch, err := baseGitRepo.GetBranch("user2/test/head2") + require.NoError(t, err) + assert.NotNil(t, newBranch) + + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + // first time insert automerge record, return true + scheduled, err := automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test", true) + require.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", true) + require.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) + require.NoError(t, err) + sha, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName()) + require.NoError(t, err) + masterCommitID, err := baseGitRepo.GetBranchCommitID("master") + require.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", + }) + require.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, "user1") + 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}) + + // check if new branch got removed + baseGitRepo, err = gitrepo.OpenRepository(db.DefaultContext, baseRepo) + require.NoError(t, err) + _, err = baseGitRepo.GetBranch("user2/test/head2") + require.Error(t, err) + assert.True(t, git.IsErrBranchNotExist(err)) + }) +}