mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-26 17:34:11 -05:00
baefea311f
On merge we walk the merge history and ensure that all lfs objects pointed to in the history are added to the base repository. This switches from relying on having git-lfs installed on the server, (and in fact .gitattributes being correctly installed.)
376 lines
13 KiB
Go
376 lines
13 KiB
Go
// Copyright 2017 The Gitea Authors. All rights reserved.
|
|
// Use of this source code is governed by a MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package integrations
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"code.gitea.io/gitea/models"
|
|
"code.gitea.io/gitea/modules/git"
|
|
api "code.gitea.io/gitea/modules/structs"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
const (
|
|
littleSize = 1024 //1ko
|
|
bigSize = 128 * 1024 * 1024 //128Mo
|
|
)
|
|
|
|
func TestGit(t *testing.T) {
|
|
onGiteaRun(t, testGit)
|
|
}
|
|
|
|
func testGit(t *testing.T, u *url.URL) {
|
|
username := "user2"
|
|
baseAPITestContext := NewAPITestContext(t, username, "repo1")
|
|
|
|
u.Path = baseAPITestContext.GitPath()
|
|
|
|
forkedUserCtx := NewAPITestContext(t, "user4", "repo1")
|
|
|
|
t.Run("HTTP", func(t *testing.T) {
|
|
PrintCurrentTest(t)
|
|
ensureAnonymousClone(t, u)
|
|
httpContext := baseAPITestContext
|
|
httpContext.Reponame = "repo-tmp-17"
|
|
forkedUserCtx.Reponame = httpContext.Reponame
|
|
|
|
dstPath, err := ioutil.TempDir("", httpContext.Reponame)
|
|
assert.NoError(t, err)
|
|
defer os.RemoveAll(dstPath)
|
|
|
|
t.Run("CreateRepoInDifferentUser", doAPICreateRepository(forkedUserCtx, false))
|
|
t.Run("AddUserAsCollaborator", doAPIAddCollaborator(forkedUserCtx, httpContext.Username, models.AccessModeRead))
|
|
|
|
t.Run("ForkFromDifferentUser", doAPIForkRepository(httpContext, forkedUserCtx.Username))
|
|
|
|
u.Path = httpContext.GitPath()
|
|
u.User = url.UserPassword(username, userPassword)
|
|
|
|
t.Run("Clone", doGitClone(dstPath, u))
|
|
|
|
little, big := standardCommitAndPushTest(t, dstPath)
|
|
littleLFS, bigLFS := lfsCommitAndPushTest(t, dstPath)
|
|
rawTest(t, &httpContext, little, big, littleLFS, bigLFS)
|
|
mediaTest(t, &httpContext, little, big, littleLFS, bigLFS)
|
|
|
|
t.Run("BranchProtectMerge", doBranchProtectPRMerge(&httpContext, dstPath))
|
|
t.Run("MergeFork", func(t *testing.T) {
|
|
t.Run("CreatePRAndMerge", doMergeFork(httpContext, forkedUserCtx, "master", httpContext.Username+":master"))
|
|
t.Run("DeleteRepository", doAPIDeleteRepository(httpContext))
|
|
rawTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS)
|
|
mediaTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS)
|
|
})
|
|
})
|
|
t.Run("SSH", func(t *testing.T) {
|
|
PrintCurrentTest(t)
|
|
sshContext := baseAPITestContext
|
|
sshContext.Reponame = "repo-tmp-18"
|
|
keyname := "my-testing-key"
|
|
forkedUserCtx.Reponame = sshContext.Reponame
|
|
t.Run("CreateRepoInDifferentUser", doAPICreateRepository(forkedUserCtx, false))
|
|
t.Run("AddUserAsCollaborator", doAPIAddCollaborator(forkedUserCtx, sshContext.Username, models.AccessModeRead))
|
|
t.Run("ForkFromDifferentUser", doAPIForkRepository(sshContext, forkedUserCtx.Username))
|
|
|
|
//Setup key the user ssh key
|
|
withKeyFile(t, keyname, func(keyFile string) {
|
|
t.Run("CreateUserKey", doAPICreateUserKey(sshContext, "test-key", keyFile))
|
|
|
|
//Setup remote link
|
|
//TODO: get url from api
|
|
sshURL := createSSHUrl(sshContext.GitPath(), u)
|
|
|
|
//Setup clone folder
|
|
dstPath, err := ioutil.TempDir("", sshContext.Reponame)
|
|
assert.NoError(t, err)
|
|
defer os.RemoveAll(dstPath)
|
|
|
|
t.Run("Clone", doGitClone(dstPath, sshURL))
|
|
|
|
little, big := standardCommitAndPushTest(t, dstPath)
|
|
littleLFS, bigLFS := lfsCommitAndPushTest(t, dstPath)
|
|
rawTest(t, &sshContext, little, big, littleLFS, bigLFS)
|
|
mediaTest(t, &sshContext, little, big, littleLFS, bigLFS)
|
|
|
|
t.Run("BranchProtectMerge", doBranchProtectPRMerge(&sshContext, dstPath))
|
|
t.Run("MergeFork", func(t *testing.T) {
|
|
t.Run("CreatePRAndMerge", doMergeFork(sshContext, forkedUserCtx, "master", sshContext.Username+":master"))
|
|
t.Run("DeleteRepository", doAPIDeleteRepository(sshContext))
|
|
rawTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS)
|
|
mediaTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS)
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
func ensureAnonymousClone(t *testing.T, u *url.URL) {
|
|
dstLocalPath, err := ioutil.TempDir("", "repo1")
|
|
assert.NoError(t, err)
|
|
defer os.RemoveAll(dstLocalPath)
|
|
t.Run("CloneAnonymous", doGitClone(dstLocalPath, u))
|
|
|
|
}
|
|
|
|
func standardCommitAndPushTest(t *testing.T, dstPath string) (little, big string) {
|
|
t.Run("Standard", func(t *testing.T) {
|
|
PrintCurrentTest(t)
|
|
little, big = commitAndPushTest(t, dstPath, "data-file-")
|
|
})
|
|
return
|
|
}
|
|
|
|
func lfsCommitAndPushTest(t *testing.T, dstPath string) (littleLFS, bigLFS string) {
|
|
t.Run("LFS", func(t *testing.T) {
|
|
PrintCurrentTest(t)
|
|
prefix := "lfs-data-file-"
|
|
_, err := git.NewCommand("lfs").AddArguments("install").RunInDir(dstPath)
|
|
assert.NoError(t, err)
|
|
_, err = git.NewCommand("lfs").AddArguments("track", prefix+"*").RunInDir(dstPath)
|
|
assert.NoError(t, err)
|
|
err = git.AddChanges(dstPath, false, ".gitattributes")
|
|
assert.NoError(t, err)
|
|
|
|
littleLFS, bigLFS = commitAndPushTest(t, dstPath, prefix)
|
|
|
|
t.Run("Locks", func(t *testing.T) {
|
|
PrintCurrentTest(t)
|
|
lockTest(t, dstPath)
|
|
})
|
|
})
|
|
return
|
|
}
|
|
|
|
func commitAndPushTest(t *testing.T, dstPath, prefix string) (little, big string) {
|
|
t.Run("PushCommit", func(t *testing.T) {
|
|
PrintCurrentTest(t)
|
|
t.Run("Little", func(t *testing.T) {
|
|
PrintCurrentTest(t)
|
|
little = doCommitAndPush(t, littleSize, dstPath, prefix)
|
|
})
|
|
t.Run("Big", func(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("Skipping test in short mode.")
|
|
return
|
|
}
|
|
PrintCurrentTest(t)
|
|
big = doCommitAndPush(t, bigSize, dstPath, prefix)
|
|
})
|
|
})
|
|
return
|
|
}
|
|
|
|
func rawTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS string) {
|
|
t.Run("Raw", func(t *testing.T) {
|
|
PrintCurrentTest(t)
|
|
username := ctx.Username
|
|
reponame := ctx.Reponame
|
|
|
|
session := loginUser(t, username)
|
|
|
|
// Request raw paths
|
|
req := NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", little))
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
assert.Equal(t, littleSize, resp.Body.Len())
|
|
|
|
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", littleLFS))
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
|
assert.NotEqual(t, littleSize, resp.Body.Len())
|
|
assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
|
|
|
|
if !testing.Short() {
|
|
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", big))
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
|
assert.Equal(t, bigSize, resp.Body.Len())
|
|
|
|
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", bigLFS))
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
|
assert.NotEqual(t, bigSize, resp.Body.Len())
|
|
assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
|
|
}
|
|
})
|
|
}
|
|
|
|
func mediaTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS string) {
|
|
t.Run("Media", func(t *testing.T) {
|
|
PrintCurrentTest(t)
|
|
|
|
username := ctx.Username
|
|
reponame := ctx.Reponame
|
|
|
|
session := loginUser(t, username)
|
|
|
|
// Request media paths
|
|
req := NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", little))
|
|
resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
|
assert.Equal(t, littleSize, resp.Length)
|
|
|
|
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", littleLFS))
|
|
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
|
assert.Equal(t, littleSize, resp.Length)
|
|
|
|
if !testing.Short() {
|
|
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", big))
|
|
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
|
assert.Equal(t, bigSize, resp.Length)
|
|
|
|
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", bigLFS))
|
|
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
|
assert.Equal(t, bigSize, resp.Length)
|
|
}
|
|
})
|
|
}
|
|
|
|
func lockTest(t *testing.T, repoPath string) {
|
|
lockFileTest(t, "README.md", repoPath)
|
|
}
|
|
|
|
func lockFileTest(t *testing.T, filename, repoPath string) {
|
|
_, err := git.NewCommand("lfs").AddArguments("locks").RunInDir(repoPath)
|
|
assert.NoError(t, err)
|
|
_, err = git.NewCommand("lfs").AddArguments("lock", filename).RunInDir(repoPath)
|
|
assert.NoError(t, err)
|
|
_, err = git.NewCommand("lfs").AddArguments("locks").RunInDir(repoPath)
|
|
assert.NoError(t, err)
|
|
_, err = git.NewCommand("lfs").AddArguments("unlock", filename).RunInDir(repoPath)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func doCommitAndPush(t *testing.T, size int, repoPath, prefix string) string {
|
|
name, err := generateCommitWithNewData(size, repoPath, "user2@example.com", "User Two", prefix)
|
|
assert.NoError(t, err)
|
|
_, err = git.NewCommand("push", "origin", "master").RunInDir(repoPath) //Push
|
|
assert.NoError(t, err)
|
|
return name
|
|
}
|
|
|
|
func generateCommitWithNewData(size int, repoPath, email, fullName, prefix string) (string, error) {
|
|
//Generate random file
|
|
data := make([]byte, size)
|
|
_, err := rand.Read(data)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
tmpFile, err := ioutil.TempFile(repoPath, prefix)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer tmpFile.Close()
|
|
_, err = tmpFile.Write(data)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
//Commit
|
|
err = git.AddChanges(repoPath, false, filepath.Base(tmpFile.Name()))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
err = git.CommitChanges(repoPath, git.CommitChangesOptions{
|
|
Committer: &git.Signature{
|
|
Email: email,
|
|
Name: fullName,
|
|
When: time.Now(),
|
|
},
|
|
Author: &git.Signature{
|
|
Email: email,
|
|
Name: fullName,
|
|
When: time.Now(),
|
|
},
|
|
Message: fmt.Sprintf("Testing commit @ %v", time.Now()),
|
|
})
|
|
return filepath.Base(tmpFile.Name()), err
|
|
}
|
|
|
|
func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
PrintCurrentTest(t)
|
|
t.Run("CreateBranchProtected", doGitCreateBranch(dstPath, "protected"))
|
|
t.Run("PushProtectedBranch", doGitPushTestRepository(dstPath, "origin", "protected"))
|
|
|
|
ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame)
|
|
t.Run("ProtectProtectedBranchNoWhitelist", doProtectBranch(ctx, "protected", ""))
|
|
t.Run("GenerateCommit", func(t *testing.T) {
|
|
_, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-")
|
|
assert.NoError(t, err)
|
|
})
|
|
t.Run("FailToPushToProtectedBranch", doGitPushTestRepositoryFail(dstPath, "origin", "protected"))
|
|
t.Run("PushToUnprotectedBranch", doGitPushTestRepository(dstPath, "origin", "protected:unprotected"))
|
|
var pr api.PullRequest
|
|
var err error
|
|
t.Run("CreatePullRequest", func(t *testing.T) {
|
|
pr, err = doAPICreatePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, "protected", "unprotected")(t)
|
|
assert.NoError(t, err)
|
|
})
|
|
t.Run("MergePR", doAPIMergePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr.Index))
|
|
t.Run("PullProtected", doGitPull(dstPath, "origin", "protected"))
|
|
t.Run("ProtectProtectedBranchWhitelist", doProtectBranch(ctx, "protected", baseCtx.Username))
|
|
|
|
t.Run("CheckoutMaster", doGitCheckoutBranch(dstPath, "master"))
|
|
t.Run("CreateBranchForced", doGitCreateBranch(dstPath, "toforce"))
|
|
t.Run("GenerateCommit", func(t *testing.T) {
|
|
_, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-")
|
|
assert.NoError(t, err)
|
|
})
|
|
t.Run("FailToForcePushToProtectedBranch", doGitPushTestRepositoryFail(dstPath, "-f", "origin", "toforce:protected"))
|
|
t.Run("MergeProtectedToToforce", doGitMerge(dstPath, "protected"))
|
|
t.Run("PushToProtectedBranch", doGitPushTestRepository(dstPath, "origin", "toforce:protected"))
|
|
t.Run("CheckoutMasterAgain", doGitCheckoutBranch(dstPath, "master"))
|
|
}
|
|
}
|
|
|
|
func doProtectBranch(ctx APITestContext, branch string, userToWhitelist string) func(t *testing.T) {
|
|
// We are going to just use the owner to set the protection.
|
|
return func(t *testing.T) {
|
|
csrf := GetCSRF(t, ctx.Session, fmt.Sprintf("/%s/%s/settings/branches", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)))
|
|
|
|
if userToWhitelist == "" {
|
|
// Change branch to protected
|
|
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/branches/%s", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame), url.PathEscape(branch)), map[string]string{
|
|
"_csrf": csrf,
|
|
"protected": "on",
|
|
})
|
|
ctx.Session.MakeRequest(t, req, http.StatusFound)
|
|
} else {
|
|
user, err := models.GetUserByName(userToWhitelist)
|
|
assert.NoError(t, err)
|
|
// Change branch to protected
|
|
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/branches/%s", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame), url.PathEscape(branch)), map[string]string{
|
|
"_csrf": csrf,
|
|
"protected": "on",
|
|
"enable_whitelist": "on",
|
|
"whitelist_users": strconv.FormatInt(user.ID, 10),
|
|
})
|
|
ctx.Session.MakeRequest(t, req, http.StatusFound)
|
|
}
|
|
// Check if master branch has been locked successfully
|
|
flashCookie := ctx.Session.GetCookie("macaron_flash")
|
|
assert.NotNil(t, flashCookie)
|
|
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Bbranch%2B%2527"+url.QueryEscape(branch)+"%2527%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
|
|
}
|
|
}
|
|
|
|
func doMergeFork(ctx, baseCtx APITestContext, baseBranch, headBranch string) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
var pr api.PullRequest
|
|
var err error
|
|
t.Run("CreatePullRequest", func(t *testing.T) {
|
|
pr, err = doAPICreatePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, baseBranch, headBranch)(t)
|
|
assert.NoError(t, err)
|
|
})
|
|
t.Run("MergePR", doAPIMergePullRequest(baseCtx, baseCtx.Username, baseCtx.Reponame, pr.Index))
|
|
|
|
}
|
|
}
|