mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-08-22 04:28:06 +02:00
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
519 lines
16 KiB
Go
519 lines
16 KiB
Go
// Copyright 2017 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package integration
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"path"
|
|
"testing"
|
|
|
|
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/modules/git"
|
|
"code.gitea.io/gitea/modules/json"
|
|
"code.gitea.io/gitea/modules/translation"
|
|
gitea_context "code.gitea.io/gitea/services/context"
|
|
"code.gitea.io/gitea/tests"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestCreateFile(t *testing.T) {
|
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
|
session := loginUser(t, "user2")
|
|
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")
|
|
|
|
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",
|
|
})
|
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
|
// Check if master branch has been locked successfully
|
|
flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
|
|
assert.NotNil(t, flashCookie)
|
|
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2522master%2522%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
|
|
|
|
// 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",
|
|
})
|
|
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
|
// Check body for error message
|
|
assert.Contains(t, resp.Body.String(), "Cannot commit to protected branch "master".")
|
|
|
|
// remove the protected branch
|
|
csrf = GetCSRF(t, session, "/user2/repo1/settings/branches")
|
|
|
|
// Change master branch to protected
|
|
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/1/delete", map[string]string{
|
|
"_csrf": csrf,
|
|
})
|
|
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
res := make(map[string]string)
|
|
assert.NoError(t, json.NewDecoder(resp.Body).Decode(&res))
|
|
assert.EqualValues(t, "/user2/repo1/settings/branches", res["redirect"])
|
|
|
|
// Check if master branch has been locked successfully
|
|
flashCookie = session.GetCookie(gitea_context.CookieNameFlash)
|
|
assert.NotNil(t, flashCookie)
|
|
assert.EqualValues(t, "error%3DRemoving%2Bbranch%2Bprotection%2Brule%2B%25221%2522%2Bfailed.", flashCookie.Value)
|
|
})
|
|
}
|
|
|
|
func testEditFile(t *testing.T, session *TestSession, user, repo, branch, filePath, newContent string) *httptest.ResponseRecorder {
|
|
// Get to the 'edit this file' page
|
|
req := NewRequest(t, "GET", path.Join(user, repo, "_edit", branch, filePath))
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
lastCommit := htmlDoc.GetInputValueByName("last_commit")
|
|
assert.NotEmpty(t, lastCommit)
|
|
|
|
// Submit the edits
|
|
req = NewRequestWithValues(t, "POST", path.Join(user, repo, "_edit", branch, filePath),
|
|
map[string]string{
|
|
"_csrf": htmlDoc.GetCSRF(),
|
|
"last_commit": lastCommit,
|
|
"tree_path": filePath,
|
|
"content": newContent,
|
|
"commit_choice": "direct",
|
|
"commit_mail_id": "-1",
|
|
},
|
|
)
|
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
|
|
|
// Verify the change
|
|
req = NewRequest(t, "GET", path.Join(user, repo, "raw/branch", branch, filePath))
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
|
assert.EqualValues(t, newContent, resp.Body.String())
|
|
|
|
return resp
|
|
}
|
|
|
|
func testEditFileToNewBranch(t *testing.T, session *TestSession, user, repo, branch, targetBranch, filePath, newContent string) *httptest.ResponseRecorder {
|
|
// Get to the 'edit this file' page
|
|
req := NewRequest(t, "GET", path.Join(user, repo, "_edit", branch, filePath))
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
lastCommit := htmlDoc.GetInputValueByName("last_commit")
|
|
assert.NotEmpty(t, lastCommit)
|
|
|
|
// Submit the edits
|
|
req = NewRequestWithValues(t, "POST", path.Join(user, repo, "_edit", branch, filePath),
|
|
map[string]string{
|
|
"_csrf": htmlDoc.GetCSRF(),
|
|
"last_commit": lastCommit,
|
|
"tree_path": filePath,
|
|
"content": newContent,
|
|
"commit_choice": "commit-to-new-branch",
|
|
"new_branch_name": targetBranch,
|
|
"commit_mail_id": "-1",
|
|
},
|
|
)
|
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
|
|
|
// Verify the change
|
|
req = NewRequest(t, "GET", path.Join(user, repo, "raw/branch", targetBranch, filePath))
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
|
assert.EqualValues(t, newContent, resp.Body.String())
|
|
|
|
return resp
|
|
}
|
|
|
|
func TestEditFile(t *testing.T) {
|
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
|
session := loginUser(t, "user2")
|
|
testEditFile(t, session, "user2", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
|
})
|
|
}
|
|
|
|
func TestEditFileToNewBranch(t *testing.T) {
|
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
|
session := loginUser(t, "user2")
|
|
testEditFileToNewBranch(t, session, "user2", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n")
|
|
})
|
|
}
|
|
|
|
func TestEditorAddTranslation(t *testing.T) {
|
|
defer tests.PrepareTestEnv(t)()
|
|
|
|
session := loginUser(t, "user2")
|
|
req := NewRequest(t, "GET", "/user2/repo1/_new/master")
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
|
|
placeholder, ok := htmlDoc.Find("input[name='commit_summary']").Attr("placeholder")
|
|
assert.True(t, ok)
|
|
assert.EqualValues(t, `Add "<filename>"`, placeholder)
|
|
}
|
|
|
|
func TestCommitMail(t *testing.T) {
|
|
onGiteaRun(t, func(t *testing.T, _ *url.URL) {
|
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
|
// Require that the user has KeepEmailPrivate enabled, because it needs
|
|
// to be tested that even with this setting enabled, it will use the
|
|
// provided mail and not revert to the placeholder one.
|
|
assert.True(t, user.KeepEmailPrivate)
|
|
|
|
inactivatedMail := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 35, UID: user.ID})
|
|
assert.False(t, inactivatedMail.IsActivated)
|
|
|
|
otherEmail := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 1, IsActivated: true})
|
|
assert.NotEqualValues(t, otherEmail.UID, user.ID)
|
|
|
|
primaryEmail := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 3, UID: user.ID, IsActivated: true})
|
|
|
|
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
|
gitRepo, _ := git.OpenRepository(git.DefaultContext, repo1.RepoPath())
|
|
defer gitRepo.Close()
|
|
|
|
session := loginUser(t, user.Name)
|
|
|
|
lastCommitAndCSRF := func(t *testing.T, link string, skipLastCommit bool) (string, string) {
|
|
t.Helper()
|
|
|
|
req := NewRequest(t, "GET", link)
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
lastCommit := htmlDoc.GetInputValueByName("last_commit")
|
|
if !skipLastCommit {
|
|
assert.NotEmpty(t, lastCommit)
|
|
}
|
|
|
|
return lastCommit, htmlDoc.GetCSRF()
|
|
}
|
|
|
|
type caseOpts struct {
|
|
link string
|
|
fileName string
|
|
base map[string]string
|
|
skipLastCommit bool
|
|
}
|
|
|
|
// Base2 should have different content, so we can test two 'correct' operations
|
|
// without the second becoming a noop because no content was changed. If needed,
|
|
// link2 can point to a new file that's used with base2.
|
|
assertCase := func(t *testing.T, case1, case2 caseOpts) {
|
|
t.Helper()
|
|
|
|
t.Run("Not activated", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
lastCommit, csrf := lastCommitAndCSRF(t, case1.link, case1.skipLastCommit)
|
|
baseCopy := case1.base
|
|
baseCopy["_csrf"] = csrf
|
|
baseCopy["last_commit"] = lastCommit
|
|
baseCopy["commit_mail_id"] = fmt.Sprintf("%d", inactivatedMail.ID)
|
|
|
|
req := NewRequestWithValues(t, "POST", case1.link, baseCopy)
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
assert.Contains(t,
|
|
htmlDoc.doc.Find(".ui.negative.message").Text(),
|
|
translation.NewLocale("en-US").Tr("repo.editor.invalid_commit_mail"),
|
|
)
|
|
})
|
|
|
|
t.Run("Not belong to user", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
lastCommit, csrf := lastCommitAndCSRF(t, case1.link, case1.skipLastCommit)
|
|
baseCopy := case1.base
|
|
baseCopy["_csrf"] = csrf
|
|
baseCopy["last_commit"] = lastCommit
|
|
baseCopy["commit_mail_id"] = fmt.Sprintf("%d", otherEmail.ID)
|
|
|
|
req := NewRequestWithValues(t, "POST", case1.link, baseCopy)
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
assert.Contains(t,
|
|
htmlDoc.doc.Find(".ui.negative.message").Text(),
|
|
translation.NewLocale("en-US").Tr("repo.editor.invalid_commit_mail"),
|
|
)
|
|
})
|
|
|
|
t.Run("Placeholder mail", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
lastCommit, csrf := lastCommitAndCSRF(t, case1.link, case1.skipLastCommit)
|
|
baseCopy := case1.base
|
|
baseCopy["_csrf"] = csrf
|
|
baseCopy["last_commit"] = lastCommit
|
|
baseCopy["commit_mail_id"] = "-1"
|
|
|
|
req := NewRequestWithValues(t, "POST", case1.link, baseCopy)
|
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
|
if !case2.skipLastCommit {
|
|
newlastCommit, _ := lastCommitAndCSRF(t, case1.link, false)
|
|
assert.NotEqualValues(t, newlastCommit, lastCommit)
|
|
}
|
|
|
|
commit, err := gitRepo.GetCommitByPath(case1.fileName)
|
|
assert.NoError(t, err)
|
|
|
|
assert.EqualValues(t, "user2", commit.Author.Name)
|
|
assert.EqualValues(t, "user2@noreply.example.org", commit.Author.Email)
|
|
assert.EqualValues(t, "user2", commit.Committer.Name)
|
|
assert.EqualValues(t, "user2@noreply.example.org", commit.Committer.Email)
|
|
})
|
|
|
|
t.Run("Normal", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
lastCommit, csrf := lastCommitAndCSRF(t, case2.link, case2.skipLastCommit)
|
|
baseCopy := case2.base
|
|
baseCopy["_csrf"] = csrf
|
|
baseCopy["last_commit"] = lastCommit
|
|
baseCopy["commit_mail_id"] = fmt.Sprintf("%d", primaryEmail.ID)
|
|
|
|
req := NewRequestWithValues(t, "POST", case2.link, baseCopy)
|
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
|
if !case2.skipLastCommit {
|
|
newlastCommit, _ := lastCommitAndCSRF(t, case2.link, false)
|
|
assert.NotEqualValues(t, newlastCommit, lastCommit)
|
|
}
|
|
|
|
commit, err := gitRepo.GetCommitByPath(case2.fileName)
|
|
assert.NoError(t, err)
|
|
|
|
assert.EqualValues(t, "user2", commit.Author.Name)
|
|
assert.EqualValues(t, primaryEmail.Email, commit.Author.Email)
|
|
assert.EqualValues(t, "user2", commit.Committer.Name)
|
|
assert.EqualValues(t, primaryEmail.Email, commit.Committer.Email)
|
|
})
|
|
}
|
|
|
|
t.Run("New", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
assertCase(t, caseOpts{
|
|
fileName: "new_file",
|
|
link: "user2/repo1/_new/master",
|
|
base: map[string]string{
|
|
"tree_path": "new_file",
|
|
"content": "new_content",
|
|
"commit_choice": "direct",
|
|
},
|
|
}, caseOpts{
|
|
fileName: "new_file_2",
|
|
link: "user2/repo1/_new/master",
|
|
base: map[string]string{
|
|
"tree_path": "new_file_2",
|
|
"content": "new_content",
|
|
"commit_choice": "direct",
|
|
},
|
|
},
|
|
)
|
|
})
|
|
|
|
t.Run("Edit", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
assertCase(t, caseOpts{
|
|
fileName: "README.md",
|
|
link: "user2/repo1/_edit/master/README.md",
|
|
base: map[string]string{
|
|
"tree_path": "README.md",
|
|
"content": "Edit content",
|
|
"commit_choice": "direct",
|
|
},
|
|
}, caseOpts{
|
|
fileName: "README.md",
|
|
link: "user2/repo1/_edit/master/README.md",
|
|
base: map[string]string{
|
|
"tree_path": "README.md",
|
|
"content": "Other content",
|
|
"commit_choice": "direct",
|
|
},
|
|
},
|
|
)
|
|
})
|
|
|
|
t.Run("Delete", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
assertCase(t, caseOpts{
|
|
fileName: "new_file",
|
|
link: "user2/repo1/_delete/master/new_file",
|
|
base: map[string]string{
|
|
"commit_choice": "direct",
|
|
},
|
|
}, caseOpts{
|
|
fileName: "new_file_2",
|
|
link: "user2/repo1/_delete/master/new_file_2",
|
|
base: map[string]string{
|
|
"commit_choice": "direct",
|
|
},
|
|
},
|
|
)
|
|
})
|
|
|
|
t.Run("Upload", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
// Upload two separate times, so we have two different 'uploads' that can
|
|
// be used independently of each other.
|
|
uploadFile := func(t *testing.T, name, content string) string {
|
|
t.Helper()
|
|
|
|
body := &bytes.Buffer{}
|
|
mpForm := multipart.NewWriter(body)
|
|
err := mpForm.WriteField("_csrf", GetCSRF(t, session, "/user2/repo1/_upload/master"))
|
|
require.NoError(t, err)
|
|
|
|
file, err := mpForm.CreateFormFile("file", name)
|
|
require.NoError(t, err)
|
|
|
|
io.Copy(file, bytes.NewBufferString(content))
|
|
require.NoError(t, mpForm.Close())
|
|
|
|
req := NewRequestWithBody(t, "POST", "/user2/repo1/upload-file", body)
|
|
req.Header.Add("Content-Type", mpForm.FormDataContentType())
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
respMap := map[string]string{}
|
|
DecodeJSON(t, resp, &respMap)
|
|
return respMap["uuid"]
|
|
}
|
|
|
|
file1UUID := uploadFile(t, "upload_file_1", "Uploaded a file!")
|
|
file2UUID := uploadFile(t, "upload_file_2", "Uploaded another file!")
|
|
|
|
assertCase(t, caseOpts{
|
|
fileName: "upload_file_1",
|
|
link: "user2/repo1/_upload/master",
|
|
skipLastCommit: true,
|
|
base: map[string]string{
|
|
"commit_choice": "direct",
|
|
"files": file1UUID,
|
|
},
|
|
}, caseOpts{
|
|
fileName: "upload_file_2",
|
|
link: "user2/repo1/_upload/master",
|
|
skipLastCommit: true,
|
|
base: map[string]string{
|
|
"commit_choice": "direct",
|
|
"files": file2UUID,
|
|
},
|
|
},
|
|
)
|
|
})
|
|
|
|
t.Run("Apply patch", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
assertCase(t, caseOpts{
|
|
fileName: "diff-file-1.txt",
|
|
link: "user2/repo1/_diffpatch/master",
|
|
base: map[string]string{
|
|
"tree_path": "patch",
|
|
"commit_choice": "direct",
|
|
"content": `diff --git a/diff-file-1.txt b/diff-file-1.txt
|
|
new file mode 100644
|
|
index 0000000000..50fcd26d6c
|
|
--- /dev/null
|
|
+++ b/diff-file-1.txt
|
|
@@ -0,0 +1 @@
|
|
+File 1
|
|
`,
|
|
},
|
|
}, caseOpts{
|
|
fileName: "diff-file-2.txt",
|
|
link: "user2/repo1/_diffpatch/master",
|
|
base: map[string]string{
|
|
"tree_path": "patch",
|
|
"commit_choice": "direct",
|
|
"content": `diff --git a/diff-file-2.txt b/diff-file-2.txt
|
|
new file mode 100644
|
|
index 0000000000..4475433e27
|
|
--- /dev/null
|
|
+++ b/diff-file-2.txt
|
|
@@ -0,0 +1 @@
|
|
+File 2
|
|
`,
|
|
},
|
|
})
|
|
})
|
|
|
|
t.Run("Cherry pick", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
commitID1, err := gitRepo.GetCommitByPath("diff-file-1.txt")
|
|
assert.NoError(t, err)
|
|
commitID2, err := gitRepo.GetCommitByPath("diff-file-2.txt")
|
|
assert.NoError(t, err)
|
|
|
|
assertCase(t, caseOpts{
|
|
fileName: "diff-file-1.txt",
|
|
link: "user2/repo1/_cherrypick/" + commitID1.ID.String() + "/master",
|
|
base: map[string]string{
|
|
"commit_choice": "direct",
|
|
"revert": "true",
|
|
},
|
|
}, caseOpts{
|
|
fileName: "diff-file-2.txt",
|
|
link: "user2/repo1/_cherrypick/" + commitID2.ID.String() + "/master",
|
|
base: map[string]string{
|
|
"commit_choice": "direct",
|
|
"revert": "true",
|
|
},
|
|
})
|
|
})
|
|
})
|
|
}
|