This commit is contained in:
@@ -16,29 +16,33 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
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"
|
||||
"code.gitea.io/gitea/models/webhook"
|
||||
"code.gitea.io/gitea/modules/commitstatus"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/queue"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/automerge"
|
||||
"code.gitea.io/gitea/services/automergequeue"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
commitstatus_service "code.gitea.io/gitea/services/repository/commitstatus"
|
||||
auth_model "gitea.dev/models/auth"
|
||||
"gitea.dev/models/db"
|
||||
git_model "gitea.dev/models/git"
|
||||
issues_model "gitea.dev/models/issues"
|
||||
"gitea.dev/models/perm"
|
||||
pull_model "gitea.dev/models/pull"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unittest"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/models/webhook"
|
||||
"gitea.dev/modules/commitstatus"
|
||||
"gitea.dev/modules/git"
|
||||
"gitea.dev/modules/git/gitcmd"
|
||||
"gitea.dev/modules/gitrepo"
|
||||
"gitea.dev/modules/json"
|
||||
"gitea.dev/modules/queue"
|
||||
"gitea.dev/modules/setting"
|
||||
api "gitea.dev/modules/structs"
|
||||
"gitea.dev/modules/test"
|
||||
"gitea.dev/modules/translation"
|
||||
"gitea.dev/modules/util"
|
||||
"gitea.dev/services/automerge"
|
||||
"gitea.dev/services/automergequeue"
|
||||
"gitea.dev/services/forms"
|
||||
pull_service "gitea.dev/services/pull"
|
||||
repo_service "gitea.dev/services/repository"
|
||||
commitstatus_service "gitea.dev/services/repository/commitstatus"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -78,7 +82,7 @@ func testPullMerge(t *testing.T, session *TestSession, user, repo, pullNum strin
|
||||
}
|
||||
|
||||
func testPullCleanUp(t *testing.T, session *TestSession, user, repo, pullnum string) *httptest.ResponseRecorder {
|
||||
req := NewRequest(t, "GET", path.Join(user, repo, "pulls", pullnum))
|
||||
req := NewRequest(t, "GET", "/"+path.Join(user, repo, "pulls", pullnum))
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
// Click the little button to create a pull
|
||||
@@ -91,12 +95,25 @@ func testPullCleanUp(t *testing.T, session *TestSession, user, repo, pullnum str
|
||||
return resp
|
||||
}
|
||||
|
||||
func preparePullMergeWebhook(t *testing.T, repoID int64) {
|
||||
require.NoError(t, db.TruncateBeans(t.Context(), &webhook.Webhook{}, &webhook.HookTask{}))
|
||||
require.NoError(t, db.Insert(t.Context(), &webhook.Webhook{
|
||||
RepoID: repoID,
|
||||
URL: "http://localhost/gitea-test-webhook-pull-merge",
|
||||
ContentType: webhook.ContentTypeJSON,
|
||||
Events: `{"push_only":true,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":false}}`,
|
||||
IsActive: true,
|
||||
}))
|
||||
}
|
||||
|
||||
func assertPullMergeWebhookTask(t *testing.T, repoID int64) {
|
||||
hook := unittest.AssertExistsAndLoadBean(t, &webhook.Webhook{RepoID: repoID})
|
||||
unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{HookID: hook.ID})
|
||||
}
|
||||
|
||||
func TestPullMerge(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
hookTasks, err := webhook.HookTasks(t.Context(), 1, 1) // Retrieve previous hook number
|
||||
assert.NoError(t, err)
|
||||
hookTasksLenBefore := len(hookTasks)
|
||||
|
||||
preparePullMergeWebhook(t, 1)
|
||||
session := loginUser(t, "user1") // FIXME: don't use admin user for testing
|
||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
|
||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
||||
@@ -122,18 +139,13 @@ func TestPullMerge(t *testing.T) {
|
||||
assert.Equal(t, 4, repo.NumPulls)
|
||||
assert.Equal(t, 3, repo.NumOpenPulls)
|
||||
|
||||
hookTasks, err = webhook.HookTasks(t.Context(), 1, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, hookTasks, hookTasksLenBefore+1)
|
||||
assertPullMergeWebhookTask(t, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPullRebase(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
hookTasks, err := webhook.HookTasks(t.Context(), 1, 1) // Retrieve previous hook number
|
||||
assert.NoError(t, err)
|
||||
hookTasksLenBefore := len(hookTasks)
|
||||
|
||||
preparePullMergeWebhook(t, 1)
|
||||
session := loginUser(t, "user1") // FIXME: don't use admin user for testing
|
||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
|
||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
||||
@@ -159,18 +171,13 @@ func TestPullRebase(t *testing.T) {
|
||||
assert.Equal(t, 4, repo.NumPulls)
|
||||
assert.Equal(t, 3, repo.NumOpenPulls)
|
||||
|
||||
hookTasks, err = webhook.HookTasks(t.Context(), 1, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, hookTasks, hookTasksLenBefore+1)
|
||||
assertPullMergeWebhookTask(t, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPullRebaseMerge(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
hookTasks, err := webhook.HookTasks(t.Context(), 1, 1) // Retrieve previous hook number
|
||||
assert.NoError(t, err)
|
||||
hookTasksLenBefore := len(hookTasks)
|
||||
|
||||
preparePullMergeWebhook(t, 1)
|
||||
session := loginUser(t, "user1") // FIXME: don't use admin user for testing
|
||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
|
||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
||||
@@ -196,18 +203,13 @@ func TestPullRebaseMerge(t *testing.T) {
|
||||
assert.Equal(t, 4, repo.NumPulls)
|
||||
assert.Equal(t, 3, repo.NumOpenPulls)
|
||||
|
||||
hookTasks, err = webhook.HookTasks(t.Context(), 1, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, hookTasks, hookTasksLenBefore+1)
|
||||
assertPullMergeWebhookTask(t, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPullSquash(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
hookTasks, err := webhook.HookTasks(t.Context(), 1, 1) // Retrieve previous hook number
|
||||
assert.NoError(t, err)
|
||||
hookTasksLenBefore := len(hookTasks)
|
||||
|
||||
preparePullMergeWebhook(t, 1)
|
||||
session := loginUser(t, "user1") // FIXME: don't use admin user for testing
|
||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
|
||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
||||
@@ -222,18 +224,13 @@ func TestPullSquash(t *testing.T) {
|
||||
DeleteBranch: false,
|
||||
})
|
||||
|
||||
hookTasks, err = webhook.HookTasks(t.Context(), 1, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, hookTasks, hookTasksLenBefore+1)
|
||||
assertPullMergeWebhookTask(t, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPullSquashWithHeadCommitID(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
hookTasks, err := webhook.HookTasks(t.Context(), 1, 1) // Retrieve previous hook number
|
||||
assert.NoError(t, err)
|
||||
hookTasksLenBefore := len(hookTasks)
|
||||
|
||||
preparePullMergeWebhook(t, 1)
|
||||
session := loginUser(t, "user1") // FIXME: don't use admin user for testing
|
||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
|
||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
||||
@@ -266,9 +263,7 @@ func TestPullSquashWithHeadCommitID(t *testing.T) {
|
||||
assert.Equal(t, 4, repo.NumPulls)
|
||||
assert.Equal(t, 3, repo.NumOpenPulls)
|
||||
|
||||
hookTasks, err = webhook.HookTasks(t.Context(), 1, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, hookTasks, hookTasksLenBefore+1)
|
||||
assertPullMergeWebhookTask(t, 1)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -302,24 +297,19 @@ func TestPullCleanUpAfterMerge(t *testing.T) {
|
||||
|
||||
// Check PR branch deletion
|
||||
resp = testPullCleanUp(t, session, elem[1], elem[2], elem[4])
|
||||
respJSON := struct {
|
||||
Redirect string
|
||||
}{}
|
||||
DecodeJSON(t, resp, &respJSON)
|
||||
respJSON := test.ParseJSONRedirect(resp.Body.Bytes())
|
||||
require.NotEmpty(t, respJSON.Redirect, "Redirected URL is not found")
|
||||
|
||||
assert.NotEmpty(t, respJSON.Redirect, "Redirected URL is not found")
|
||||
|
||||
elem = strings.Split(respJSON.Redirect, "/")
|
||||
elem = strings.Split(*respJSON.Redirect, "/")
|
||||
assert.Equal(t, "pulls", elem[3])
|
||||
|
||||
// Check branch deletion result
|
||||
req := NewRequest(t, "GET", respJSON.Redirect)
|
||||
req := NewRequest(t, "GET", *respJSON.Redirect)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
resultMsg := htmlDoc.doc.Find(".ui.message>p").Text()
|
||||
|
||||
assert.Equal(t, "Branch \"user1/repo1:feature/test\" has been deleted.", resultMsg)
|
||||
resultMsg := strings.TrimSpace(htmlDoc.doc.Find(".ui.message.flash-message").Text())
|
||||
assert.Equal(t, `Branch "user1/repo1:feature/test" has been deleted.`, resultMsg)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -334,11 +324,8 @@ func TestCantMergeWorkInProgress(t *testing.T) {
|
||||
req := NewRequest(t, "GET", test.RedirectURL(resp))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
text := strings.TrimSpace(htmlDoc.doc.Find(".merge-section > .item").Last().Text())
|
||||
assert.NotEmpty(t, text, "Can't find WIP text")
|
||||
|
||||
assert.Contains(t, text, translation.NewLocale("en-US").TrString("repo.pulls.cannot_merge_work_in_progress"), "Unable to find WIP text")
|
||||
assert.Contains(t, text, "[wip]", "Unable to find WIP text")
|
||||
wipToggleButtonCount := htmlDoc.Find(`.merge-section > .item button[data-global-init="initPullRequestWipToggle"]`).Length()
|
||||
assert.Equal(t, 1, wipToggleButtonCount)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -374,11 +361,11 @@ func TestCantMergeConflict(t *testing.T) {
|
||||
BaseBranch: "base",
|
||||
})
|
||||
|
||||
err := pull_service.Merge(t.Context(), pr, user1, repo_model.MergeStyleMerge, "", "CONFLICT", false)
|
||||
err := pull_service.Merge(pr, user1, repo_model.MergeStyleMerge, "", "CONFLICT", false)
|
||||
assert.Error(t, err, "Merge should return an error due to conflict")
|
||||
assert.True(t, pull_service.IsErrMergeConflicts(err), "Merge error is not a conflict error")
|
||||
|
||||
err = pull_service.Merge(t.Context(), pr, user1, repo_model.MergeStyleRebase, "", "CONFLICT", false)
|
||||
err = pull_service.Merge(pr, user1, repo_model.MergeStyleRebase, "", "CONFLICT", false)
|
||||
assert.Error(t, err, "Merge should return an error due to conflict")
|
||||
assert.True(t, pull_service.IsErrRebaseConflicts(err), "Merge error is not a conflict error")
|
||||
})
|
||||
@@ -470,7 +457,7 @@ func TestCantMergeUnrelated(t *testing.T) {
|
||||
BaseBranch: "base",
|
||||
})
|
||||
|
||||
err = pull_service.Merge(t.Context(), pr, user1, repo_model.MergeStyleMerge, "", "UNRELATED", false)
|
||||
err = pull_service.Merge(pr, user1, repo_model.MergeStyleMerge, "", "UNRELATED", false)
|
||||
assert.Error(t, err, "Merge should return an error due to unrelated")
|
||||
assert.True(t, pull_service.IsErrMergeUnrelatedHistories(err), "Merge error is not a unrelated histories error")
|
||||
})
|
||||
@@ -506,11 +493,97 @@ func TestFastForwardOnlyMerge(t *testing.T) {
|
||||
BaseBranch: "master",
|
||||
})
|
||||
|
||||
err := pull_service.Merge(t.Context(), pr, user1, repo_model.MergeStyleFastForwardOnly, "", "FAST-FORWARD-ONLY", false)
|
||||
err := pull_service.Merge(pr, user1, repo_model.MergeStyleFastForwardOnly, "", "FAST-FORWARD-ONLY", false)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFastForwardOnlyMergeWithRequiredSignedCommits(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
session := loginUser(t, "user1")
|
||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
|
||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "update", "README.md", "Hello, signed\n")
|
||||
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
|
||||
createPRReq := NewRequestWithJSON(t, http.MethodPost, "/api/v1/repos/user1/repo1/pulls", &api.CreatePullRequestOption{
|
||||
Head: "update",
|
||||
Base: "master",
|
||||
Title: "ff-only merge under require-signed-commits",
|
||||
}).AddTokenAuth(token)
|
||||
session.MakeRequest(t, createPRReq, http.StatusCreated)
|
||||
|
||||
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user1"})
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user1.ID, Name: "repo1"})
|
||||
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{
|
||||
HeadRepoID: repo1.ID,
|
||||
BaseRepoID: repo1.ID,
|
||||
HeadBranch: "update",
|
||||
BaseBranch: "master",
|
||||
})
|
||||
|
||||
// Enable require-signed-commits on master.
|
||||
require.NoError(t, git_model.UpdateProtectBranch(t.Context(), repo1, &git_model.ProtectedBranch{
|
||||
RepoID: repo1.ID,
|
||||
RuleName: "master",
|
||||
RequireSignedCommits: true,
|
||||
}, git_model.WhitelistOptions{}))
|
||||
|
||||
prIndex := strconv.FormatInt(pr.Index, 10)
|
||||
mergeURL := "/user1/repo1/pulls/" + prIndex + "/merge"
|
||||
|
||||
notVerifiedMsg := translation.NewLocale("en-US").TrString("repo.pulls.require_signed_head_commits_unverified")
|
||||
apiNotVerifiedMsg := pull_service.ErrHeadCommitsNotAllVerified.Error()
|
||||
// Matches the unexported "wont sign: %s" format and nokey signingMode in
|
||||
// services/asymkey/sign.go; the test config uses SIGNING_KEY = none.
|
||||
const wontSignMsg = "wont sign: nokey"
|
||||
|
||||
for _, style := range []repo_model.MergeStyle{repo_model.MergeStyleFastForwardOnly, repo_model.MergeStyleMerge} {
|
||||
t.Run(string(style)+"/head-commits-unverified", func(t *testing.T) {
|
||||
mergeReq := NewRequestWithValues(t, http.MethodPost, mergeURL, map[string]string{"do": string(style)})
|
||||
resp := session.MakeRequest(t, mergeReq, http.StatusBadRequest)
|
||||
assert.Equal(t, notVerifiedMsg, test.ParseJSONError(resp.Body.Bytes()).ErrorMessage)
|
||||
})
|
||||
}
|
||||
|
||||
for _, style := range []repo_model.MergeStyle{repo_model.MergeStyleRebase, repo_model.MergeStyleRebaseMerge, repo_model.MergeStyleSquash} {
|
||||
t.Run(string(style)+"/wont-sign", func(t *testing.T) {
|
||||
mergeReq := NewRequestWithValues(t, http.MethodPost, mergeURL, map[string]string{"do": string(style)})
|
||||
resp := session.MakeRequest(t, mergeReq, http.StatusBadRequest)
|
||||
assert.Equal(t, wontSignMsg, test.ParseJSONError(resp.Body.Bytes()).ErrorMessage)
|
||||
})
|
||||
}
|
||||
|
||||
// Admin force-merge must not bypass the unverified-head-commits check, since
|
||||
// the pre-receive hook would reject the push regardless.
|
||||
t.Run("fast-forward-only/admin-force-merge-does-not-bypass", func(t *testing.T) {
|
||||
mergeReq := NewRequestWithValues(t, http.MethodPost, mergeURL, map[string]string{
|
||||
"do": string(repo_model.MergeStyleFastForwardOnly),
|
||||
"force_merge": "true",
|
||||
})
|
||||
resp := session.MakeRequest(t, mergeReq, http.StatusBadRequest)
|
||||
assert.Equal(t, notVerifiedMsg, test.ParseJSONError(resp.Body.Bytes()).ErrorMessage)
|
||||
})
|
||||
|
||||
t.Run("api/fast-forward-only/head-commits-unverified", func(t *testing.T) {
|
||||
apiReq := NewRequestWithJSON(t, http.MethodPost,
|
||||
fmt.Sprintf("/api/v1/repos/user1/repo1/pulls/%s/merge", prIndex),
|
||||
&forms.MergePullRequestForm{Do: string(repo_model.MergeStyleFastForwardOnly)},
|
||||
).AddTokenAuth(token)
|
||||
resp := session.MakeRequest(t, apiReq, http.StatusMethodNotAllowed)
|
||||
apiBody := DecodeJSON(t, resp, &api.APIError{})
|
||||
assert.Equal(t, apiNotVerifiedMsg, apiBody.Message)
|
||||
})
|
||||
|
||||
pb, err := git_model.GetFirstMatchProtectedBranchRule(t.Context(), repo1.ID, "master")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, pb)
|
||||
pb.RequireSignedCommits = false
|
||||
require.NoError(t, git_model.UpdateProtectBranch(t.Context(), repo1, pb, git_model.WhitelistOptions{}))
|
||||
|
||||
require.NoError(t, pull_service.Merge(pr, user1, repo_model.MergeStyleFastForwardOnly, "", "FAST-FORWARD-ONLY", false))
|
||||
})
|
||||
}
|
||||
|
||||
func TestCantFastForwardOnlyMergeDiverging(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
session := loginUser(t, "user1") // FIXME: don't use admin user for testing
|
||||
@@ -542,7 +615,7 @@ func TestCantFastForwardOnlyMergeDiverging(t *testing.T) {
|
||||
BaseBranch: "master",
|
||||
})
|
||||
|
||||
err := pull_service.Merge(t.Context(), pr, user1, repo_model.MergeStyleFastForwardOnly, "", "DIVERGING", false)
|
||||
err := pull_service.Merge(pr, user1, repo_model.MergeStyleFastForwardOnly, "", "DIVERGING", false)
|
||||
assert.Error(t, err, "Merge should return an error due to being for a diverging branch")
|
||||
assert.True(t, pull_service.IsErrMergeDivergingFastForwardOnly(err), "Merge error is not a diverging fast-forward-only error")
|
||||
})
|
||||
@@ -681,8 +754,7 @@ func TestPullMergeIndexerNotifier(t *testing.T) {
|
||||
|
||||
// search issues
|
||||
searchIssuesResp := session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
|
||||
var apiIssuesBefore []*api.Issue
|
||||
DecodeJSON(t, searchIssuesResp, &apiIssuesBefore)
|
||||
apiIssuesBefore := DecodeJSON(t, searchIssuesResp, []*api.Issue{})
|
||||
assert.Empty(t, apiIssuesBefore)
|
||||
|
||||
// merge the pull request
|
||||
@@ -704,11 +776,9 @@ func TestPullMergeIndexerNotifier(t *testing.T) {
|
||||
|
||||
// search issues again
|
||||
searchIssuesResp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
|
||||
var apiIssuesAfter []*api.Issue
|
||||
DecodeJSON(t, searchIssuesResp, &apiIssuesAfter)
|
||||
if assert.Len(t, apiIssuesAfter, 1) {
|
||||
assert.Equal(t, issue.ID, apiIssuesAfter[0].ID)
|
||||
}
|
||||
apiIssuesAfter := DecodeJSON(t, searchIssuesResp, []*api.Issue{})
|
||||
require.Len(t, apiIssuesAfter, 1)
|
||||
assert.Equal(t, issue.ID, apiIssuesAfter[0].ID)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1024,6 +1094,71 @@ func TestPullNonMergeForAdminWithBranchProtection(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestPullForceMergeForBypassAllowlistUser(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
ownerSession := loginUser(t, "user2")
|
||||
ownerCtx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeWriteRepository)
|
||||
|
||||
bypassUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user4"})
|
||||
doAPIAddCollaborator(ownerCtx, bypassUser.Name, perm.AccessModeWrite)(t)
|
||||
|
||||
bypassSession := loginUser(t, bypassUser.Name)
|
||||
forkedName := "repo1-bypass-allowlist"
|
||||
testRepoFork(t, bypassSession, "user2", "repo1", bypassUser.Name, forkedName, "")
|
||||
defer testDeleteRepository(t, bypassSession, bypassUser.Name, forkedName)
|
||||
|
||||
testEditFile(t, bypassSession, bypassUser.Name, forkedName, "master", "README.md", "Hello, World (Bypass Allowlist)\n")
|
||||
resp := testPullCreate(t, bypassSession, bypassUser.Name, forkedName, false, "master", "master", "Bypass allowlist merge test pull")
|
||||
pullURL := test.RedirectURL(resp)
|
||||
elem := strings.Split(pullURL, "/")
|
||||
assert.Equal(t, "pulls", elem[3])
|
||||
|
||||
prIndex, err := strconv.ParseInt(elem[4], 10, 64)
|
||||
assert.NoError(t, err)
|
||||
|
||||
pbCreateReq := NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/edit", map[string]string{
|
||||
"rule_name": "master",
|
||||
"enable_push": "all",
|
||||
"enable_status_check": "true",
|
||||
"status_check_contexts": "gitea/actions",
|
||||
"block_admin_merge_override": "true",
|
||||
"enable_bypass_allowlist": "on",
|
||||
"bypass_allowlist_users": strconv.FormatInt(bypassUser.ID, 10),
|
||||
})
|
||||
ownerSession.MakeRequest(t, pbCreateReq, http.StatusSeeOther)
|
||||
defer testAPIDeleteBranchProtection(t, "master", http.StatusNoContent)
|
||||
|
||||
token := getTokenForLoggedInUser(t, bypassSession, auth_model.AccessTokenScopeWriteRepository)
|
||||
|
||||
resp = bypassSession.MakeRequest(t, NewRequest(t, "GET", pullURL), http.StatusOK)
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
assert.Contains(t, htmlDoc.doc.Find(".merge-section").Text(), "You are allowed to bypass branch protection rules for this merge.")
|
||||
mergeFormProps, exists := htmlDoc.doc.Find("#pull-request-merge-form").Attr("data-merge-form-props")
|
||||
require.True(t, exists)
|
||||
var mergeForm map[string]any
|
||||
require.NoError(t, json.Unmarshal([]byte(mergeFormProps), &mergeForm))
|
||||
assert.Equal(t, true, mergeForm["canMergeNow"])
|
||||
assert.Equal(t, false, mergeForm["allOverridableChecksOk"])
|
||||
|
||||
mergeReq := func(forceMerge bool) *RequestWrapper {
|
||||
return NewRequestWithValues(t, "POST", fmt.Sprintf("/api/v1/repos/user2/repo1/pulls/%d/merge", prIndex), map[string]string{
|
||||
"head_commit_id": "",
|
||||
"merge_when_checks_succeed": "false",
|
||||
"force_merge": strconv.FormatBool(forceMerge),
|
||||
"do": "rebase",
|
||||
}).AddTokenAuth(token)
|
||||
}
|
||||
|
||||
bypassSession.MakeRequest(t, mergeReq(false), http.StatusMethodNotAllowed)
|
||||
bypassSession.MakeRequest(t, mergeReq(true), http.StatusOK)
|
||||
|
||||
baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"})
|
||||
pr, err := issues_model.GetPullRequestByIndex(t.Context(), baseRepo.ID, prIndex)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, pr.HasMerged)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPullSquashMergeEmpty(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
session := loginUser(t, "user1") // FIXME: don't use admin user for testing
|
||||
@@ -1137,7 +1272,7 @@ Commit description.
|
||||
commitMessage: `loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong message`,
|
||||
},
|
||||
},
|
||||
expectedMessage: `* looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo...`,
|
||||
expectedMessage: "* looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo...\n\n",
|
||||
},
|
||||
{
|
||||
name: "Test Co-authored-by",
|
||||
@@ -1186,7 +1321,8 @@ Co-authored-by: user4 <user4@example.com>
|
||||
pullIndex, err := strconv.ParseInt(elems[4], 10, 64)
|
||||
assert.NoError(t, err)
|
||||
pullRequest := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, Index: pullIndex})
|
||||
squashMergeCommitMessage := pull_service.GetSquashMergeCommitMessages(t.Context(), pullRequest)
|
||||
squashMergeCommitMessage, err := pull_service.GetSquashMergeCommitMessages(t.Context(), pullRequest)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.expectedMessage, squashMergeCommitMessage)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user