This commit is contained in:
@@ -4,22 +4,22 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
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/gitcmd"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
"code.gitea.io/gitea/tests"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unittest"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/git/gitcmd"
|
||||
"gitea.dev/modules/gitrepo"
|
||||
"gitea.dev/modules/test"
|
||||
"gitea.dev/modules/util"
|
||||
"gitea.dev/routers/common"
|
||||
repo_service "gitea.dev/services/repository"
|
||||
"gitea.dev/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -36,9 +36,17 @@ func TestCompareTag(t *testing.T) {
|
||||
// A dropdown for both base and head.
|
||||
assert.Lenf(t, selection.Nodes, 2, "The template has changed")
|
||||
|
||||
req = NewRequest(t, "GET", "/user2/repo1/compare/v1.1...HEAD")
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.True(t, test.IsNormalPageCompleted(resp.Body.String()))
|
||||
|
||||
req = NewRequest(t, "GET", "/user2/repo1/compare/v1.1...NotExisting").SetHeader("Accept", "text/html")
|
||||
resp = session.MakeRequest(t, req, http.StatusNotFound)
|
||||
assert.True(t, test.IsNormalPageCompleted(resp.Body.String()))
|
||||
|
||||
req = NewRequest(t, "GET", "/user2/repo1/compare/invalid").SetHeader("Accept", "text/html")
|
||||
resp = session.MakeRequest(t, req, http.StatusNotFound)
|
||||
assert.True(t, test.IsNormalPageCompleted(resp.Body.String()), "expect 404 page not 500")
|
||||
assert.True(t, test.IsNormalPageCompleted(resp.Body.String()))
|
||||
}
|
||||
|
||||
// Compare with inferred default branch (master)
|
||||
@@ -130,53 +138,71 @@ func TestCompareBranches(t *testing.T) {
|
||||
inspectCompare(t, htmlDoc, diffCount, diffChanges)
|
||||
}
|
||||
|
||||
func createUnrelatedBranch(t *testing.T, repo *repo_model.Repository, user *user_model.User, branchName string) {
|
||||
t.Helper()
|
||||
func TestCompareWithRefSuffix(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
repoPath := repo_model.RepoPath(user.Name, repo.Name)
|
||||
require.NoError(t, gitcmd.NewCommand("read-tree", "--empty").WithDir(repoPath).Run(t.Context()))
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
stdout, _, err := gitcmd.NewCommand("hash-object", "-w", "--stdin").
|
||||
WithDir(repoPath).
|
||||
WithStdinBytes([]byte("Unrelated File")).
|
||||
RunStdString(t.Context())
|
||||
// remove-files-b^ resolves to the tip's parent, so the test.txt added by the tip is excluded
|
||||
req := NewRequest(t, "GET", "/user2/repo20/compare/add-csv...remove-files-b^")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
inspectCompare(t, htmlDoc, 2, []string{"link_hi", "test.csv"})
|
||||
|
||||
// a suffix resolves to a commit rather than a branch, so the page offers no pull request to create
|
||||
assert.Equal(t, 0, htmlDoc.doc.Find(".pullrequest-form").Length())
|
||||
|
||||
// the same suffix on the direct ".." comparison resolves to the same commit
|
||||
req = NewRequest(t, "GET", "/user2/repo20/compare/add-csv..remove-files-b^")
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc = NewHTMLParser(t, resp.Body)
|
||||
inspectCompare(t, htmlDoc, 2, []string{"link_hi", "test.csv"})
|
||||
|
||||
// a ~N suffix on the base side resolves and renders the compare page, but also
|
||||
// resolves to a commit rather than a branch, so no pull request form is offered
|
||||
req = NewRequest(t, "GET", "/user2/repo20/compare/add-csv~1...remove-files-b")
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.True(t, test.IsNormalPageCompleted(resp.Body.String()))
|
||||
htmlDoc = NewHTMLParser(t, resp.Body)
|
||||
assert.Equal(t, 0, htmlDoc.doc.Find(".pullrequest-form").Length())
|
||||
|
||||
// the web handler folds an unsupported (^{...}) and an unresolvable (~50) suffix alike into 404
|
||||
for _, basehead := range []string{
|
||||
"add-csv...remove-files-b~50",
|
||||
"add-csv...remove-files-b^{/Add}",
|
||||
"add-csv^{/Add}...remove-files-b",
|
||||
} {
|
||||
req = NewRequest(t, "GET", "/user2/repo20/compare/"+basehead).SetHeader("Accept", "text/html")
|
||||
resp = session.MakeRequest(t, req, http.StatusNotFound)
|
||||
assert.True(t, test.IsNormalPageCompleted(resp.Body.String()))
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveRefWithSuffixContract(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 31})
|
||||
gitRepo, err := gitrepo.OpenRepository(t.Context(), repo)
|
||||
require.NoError(t, err)
|
||||
blobSHA := strings.TrimSpace(stdout)
|
||||
defer gitRepo.Close()
|
||||
|
||||
_, _, err = gitcmd.NewCommand("update-index", "--add", "--replace", "--cacheinfo").
|
||||
AddDynamicArguments("100644", blobSHA, "unrelated.txt").
|
||||
WithDir(repoPath).
|
||||
RunStdString(t.Context())
|
||||
// a nil error guarantees a usable RefName
|
||||
ref, err := common.ResolveRefWithSuffix(gitRepo, "add-csv", "^")
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, ref)
|
||||
// a ref resolved with a suffix must be a commit SHA, not a branch ref
|
||||
// (branch refs would break "New Pull Request" logic)
|
||||
assert.False(t, ref.IsBranch(), "ref with suffix must not resolve to a branch")
|
||||
|
||||
stdout, _, err = gitcmd.NewCommand("write-tree").WithDir(repoPath).RunStdString(t.Context())
|
||||
require.NoError(t, err)
|
||||
treeSHA := strings.TrimSpace(stdout)
|
||||
|
||||
commitTimeStr := time.Now().Format(time.RFC3339)
|
||||
doerSig := user.NewGitSig()
|
||||
env := append(os.Environ(),
|
||||
"GIT_AUTHOR_NAME="+doerSig.Name,
|
||||
"GIT_AUTHOR_EMAIL="+doerSig.Email,
|
||||
"GIT_AUTHOR_DATE="+commitTimeStr,
|
||||
"GIT_COMMITTER_NAME="+doerSig.Name,
|
||||
"GIT_COMMITTER_EMAIL="+doerSig.Email,
|
||||
"GIT_COMMITTER_DATE="+commitTimeStr,
|
||||
)
|
||||
|
||||
messageBytes := bytes.NewBufferString("Unrelated\n")
|
||||
stdout, _, err = gitcmd.NewCommand("commit-tree").AddDynamicArguments(treeSHA).
|
||||
WithEnv(env).
|
||||
WithDir(repoPath).
|
||||
WithStdinBytes(messageBytes.Bytes()).
|
||||
RunStdString(t.Context())
|
||||
require.NoError(t, err)
|
||||
commitSHA := strings.TrimSpace(stdout)
|
||||
|
||||
_, _, err = gitcmd.NewCommand("branch").AddDynamicArguments(branchName, commitSHA).
|
||||
WithDir(repoPath).
|
||||
RunStdString(t.Context())
|
||||
require.NoError(t, err)
|
||||
// a missing ref and an unresolvable suffix both report not-found instead of an empty RefName
|
||||
for _, tc := range []struct{ oriRef, suffix string }{
|
||||
{"does-not-exist", ""},
|
||||
{"add-csv", "~50"},
|
||||
} {
|
||||
ref, err := common.ResolveRefWithSuffix(gitRepo, tc.oriRef, tc.suffix)
|
||||
assert.ErrorIs(t, err, util.ErrNotExist, "ref %q suffix %q", tc.oriRef, tc.suffix)
|
||||
assert.Empty(t, ref, "ref %q suffix %q", tc.oriRef, tc.suffix)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompareBranchesNoCommonMergeBase(t *testing.T) {
|
||||
@@ -184,7 +210,18 @@ func TestCompareBranchesNoCommonMergeBase(t *testing.T) {
|
||||
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"})
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user2.ID, Name: "repo1"})
|
||||
createUnrelatedBranch(t, repo1, user2, "unrelated-history")
|
||||
|
||||
repoPath := repo_model.RepoPath(user2.Name, repo1.Name)
|
||||
_, _, runErr := gitcmd.NewCommand("fast-import").WithDir(repoPath).WithStdinBytes([]byte(strings.TrimSpace(`
|
||||
commit refs/heads/unrelated-history
|
||||
committer User <user@example.com> 1714310400 +0000
|
||||
data 13
|
||||
Second commit
|
||||
M 100644 inline file2.txt
|
||||
data 12
|
||||
Hello from 2
|
||||
`))).RunStdString(t.Context())
|
||||
require.NoError(t, runErr)
|
||||
|
||||
session := loginUser(t, "user2")
|
||||
req := NewRequest(t, "GET", "/user2/repo1/compare/master...unrelated-history")
|
||||
@@ -200,6 +237,53 @@ func TestCompareBranchesNoCommonMergeBase(t *testing.T) {
|
||||
assert.Equal(t, 0, htmlDoc.doc.Find(".pullrequest-form").Length())
|
||||
}
|
||||
|
||||
func TestCompareDownloadDiffOrPatch(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
t.Run("BranchToBranchDiff", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
req := NewRequest(t, "GET", "/user2/repo20/compare/add-csv...remove-files-b.diff")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, "text/plain; charset=utf-8", resp.Header().Get("Content-Type"))
|
||||
assert.Contains(t, resp.Body.String(), "diff --git ")
|
||||
})
|
||||
|
||||
t.Run("BranchToBranchPatch", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
req := NewRequest(t, "GET", "/user2/repo20/compare/add-csv...remove-files-b.patch")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, "text/plain; charset=utf-8", resp.Header().Get("Content-Type"))
|
||||
assert.True(t, strings.HasPrefix(resp.Body.String(), "From "), "patch output should start with a format-patch header")
|
||||
})
|
||||
|
||||
t.Run("SingleRefImplicitBase", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
req := NewRequest(t, "GET", "/user2/repo20/compare/add-csv.diff")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Contains(t, resp.Body.String(), "diff --git ")
|
||||
})
|
||||
|
||||
t.Run("InvalidBaseRef", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
req := NewRequest(t, "GET", "/user2/repo20/compare/does-not-exist...remove-files-b.diff")
|
||||
session.MakeRequest(t, req, http.StatusNotFound)
|
||||
})
|
||||
|
||||
t.Run("PrivateRepoAnonymous", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
// repo16 is private; an unauthenticated request must not leak its existence.
|
||||
req := NewRequest(t, "GET", "/user2/repo16/compare/master...good-sign.diff")
|
||||
MakeRequest(t, req, http.StatusNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCompareCodeExpand(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
@@ -223,12 +307,12 @@ func TestCompareCodeExpand(t *testing.T) {
|
||||
req := NewRequest(t, "GET", "/user1/test_blob_excerpt/compare/main...user2/test_blob_excerpt-fork:forked-branch")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
els := htmlDoc.Find(`button.code-expander-button[hx-get]`)
|
||||
els := htmlDoc.Find(`button.code-expander-button[data-fetch-url]`)
|
||||
|
||||
// all the links in the comparison should be to the forked repo&branch
|
||||
assert.NotZero(t, els.Length())
|
||||
for i := 0; i < els.Length(); i++ {
|
||||
link := els.Eq(i).AttrOr("hx-get", "")
|
||||
link := els.Eq(i).AttrOr("data-fetch-url", "")
|
||||
assert.True(t, strings.HasPrefix(link, "/user2/test_blob_excerpt-fork/blob_excerpt/"))
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user