// Copyright 2017 The Gitea Authors. All rights reserved.// SPDX-License-Identifier: MIT package integration import ( "bytes" "crypto/rand" "encoding/hex" "fmt" "io" "net/http" "net/url" "os" "os/exec" "path" "path/filepath" "strconv" "strings" "testing" "time" auth_model "forgejo.org/models/auth" "forgejo.org/models/db" git_model "forgejo.org/models/git" issues_model "forgejo.org/models/issues" "forgejo.org/models/perm" repo_model "forgejo.org/models/repo" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" "forgejo.org/modules/git" "forgejo.org/modules/gitrepo" "forgejo.org/modules/lfs" "forgejo.org/modules/setting" api "forgejo.org/modules/structs" gitea_context "forgejo.org/services/context" files_service "forgejo.org/services/repository/files" "forgejo.org/tests" "github.com/kballard/go-shellquote" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require") const ( littleSize = 1024 // 1KiB bigSize = 32 * 1024 * 1024 // 32MiB) func TestGit(t *testing.T) { onGiteaRun(t, testGit)} func testGit(t *testing.T, u *url.URL) { username := "user2" baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) u.Path = baseAPITestContext.GitPath() forkedUserCtx := NewAPITestContext(t, "user4", "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) t.Run("HTTP", func(t *testing.T) { ensureAnonymousClone(t, u) forEachObjectFormat(t, func(t *testing.T, objectFormat git.ObjectFormat) { defer tests.PrintCurrentTest(t)() httpContext := baseAPITestContext httpContext.Reponame = "repo-tmp-17-" + objectFormat.Name() forkedUserCtx.Reponame = httpContext.Reponame dstPath := t.TempDir() t.Run("CreateRepoInDifferentUser", doAPICreateRepository(forkedUserCtx, false, objectFormat)) t.Run("AddUserAsCollaborator", doAPIAddCollaborator(forkedUserCtx, httpContext.Username, perm.AccessModeRead)) t.Run("ForkFromDifferentUser", doAPIForkRepository(httpContext, forkedUserCtx.Username)) u.Path = httpContext.GitPath() u.User = url.UserPassword(username, userPassword) t.Run("Clone", doGitClone(dstPath, u)) dstPath2 := t.TempDir() t.Run("Partial Clone", doPartialGitClone(dstPath2, 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("CreateAgitFlowPull", doCreateAgitFlowPull(dstPath, &httpContext, "test/head")) t.Run("InternalReferences", doInternalReferences(&httpContext, dstPath)) t.Run("BranchProtect", doBranchProtect(&httpContext, dstPath)) t.Run("AutoMerge", doAutoPRMerge(&httpContext, dstPath)) t.Run("CreatePRAndSetManuallyMerged", doCreatePRAndSetManuallyMerged(httpContext, httpContext, dstPath, "master", "test-manually-merge")) t.Run("MergeFork", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("CreatePRAndMerge", doMergeFork(httpContext, forkedUserCtx, "master", httpContext.Username+":master")) rawTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS) mediaTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS) }) t.Run("PushCreate", doPushCreate(httpContext, u, objectFormat)) }) }) t.Run("SSH", func(t *testing.T) { forEachObjectFormat(t, func(t *testing.T, objectFormat git.ObjectFormat) { defer tests.PrintCurrentTest(t)() sshContext := baseAPITestContext sshContext.Reponame = "repo-tmp-18-" + objectFormat.Name() keyname := "my-testing-key" forkedUserCtx.Reponame = sshContext.Reponame t.Run("CreateRepoInDifferentUser", doAPICreateRepository(forkedUserCtx, false, objectFormat)) t.Run("AddUserAsCollaborator", doAPIAddCollaborator(forkedUserCtx, sshContext.Username, perm.AccessModeRead)) t.Run("ForkFromDifferentUser", doAPIForkRepository(sshContext, forkedUserCtx.Username)) // Setup key the user ssh key withKeyFile(t, keyname, func(keyFile string) { var publicKeyID int64 t.Run("CreateUserKey", doAPICreateUserKey(sshContext, "test-key-"+objectFormat.Name(), keyFile, func(t *testing.T, pk api.PublicKey) { publicKeyID = pk.ID })) // Setup remote link // TODO: get url from api sshURL := createSSHUrl(sshContext.GitPath(), u) // Setup clone folder dstPath := t.TempDir() 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("CreateAgitFlowPull", doCreateAgitFlowPull(dstPath, &sshContext, "test/head2")) t.Run("InternalReferences", doInternalReferences(&sshContext, dstPath)) t.Run("BranchProtect", doBranchProtect(&sshContext, dstPath)) t.Run("MergeFork", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("CreatePRAndMerge", doMergeFork(sshContext, forkedUserCtx, "master", sshContext.Username+":master")) rawTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS) mediaTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS) }) t.Run("PushCreate", doPushCreate(sshContext, sshURL, objectFormat)) t.Run("LFS no access", doLFSNoAccess(sshContext, publicKeyID, objectFormat)) }) }) })} func ensureAnonymousClone(t *testing.T, u *url.URL) { dstLocalPath := t.TempDir() t.Run("CloneAnonymous", doGitClone(dstLocalPath, u))} func standardCommitAndPushTest(t *testing.T, dstPath string) (little, big string) { t.Run("Standard", func(t *testing.T) { defer tests.PrintCurrentTest(t)() little, big = commitAndPushTest(t, dstPath, "data-file-") }) return little, big} func lfsCommitAndPushTest(t *testing.T, dstPath string) (littleLFS, bigLFS string) { t.Run("LFS", func(t *testing.T) { defer tests.PrintCurrentTest(t)() prefix := "lfs-data-file-" err := git.NewCommand(git.DefaultContext, "lfs").AddArguments("install").Run(&git.RunOpts{Dir: dstPath}) require.NoError(t, err) _, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("track").AddDynamicArguments(prefix + "*").RunStdString(&git.RunOpts{Dir: dstPath}) require.NoError(t, err) err = git.AddChanges(dstPath, false, ".gitattributes") require.NoError(t, err) err = git.CommitChangesWithArgs(dstPath, git.AllowLFSFiltersArgs(), git.CommitChangesOptions{ Committer: &git.Signature{ Email: "user2@example.com", Name: "User Two", When: time.Now(), }, Author: &git.Signature{ Email: "user2@example.com", Name: "User Two", When: time.Now(), }, Message: fmt.Sprintf("Testing commit @ %v", time.Now()), }) require.NoError(t, err) littleLFS, bigLFS = commitAndPushTest(t, dstPath, prefix) t.Run("Locks", func(t *testing.T) { defer tests.PrintCurrentTest(t)() lockTest(t, dstPath) }) }) return littleLFS, bigLFS} func commitAndPushTest(t *testing.T, dstPath, prefix string) (little, big string) { t.Run("PushCommit", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("Little", func(t *testing.T) { defer tests.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 } defer tests.PrintCurrentTest(t)() big = doCommitAndPush(t, bigSize, dstPath, prefix) }) }) return little, big} func rawTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS string) { t.Run("Raw", func(t *testing.T) { defer tests.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.MakeRequestNilResponseRecorder(t, req, http.StatusOK) assert.Equal(t, littleSize, resp.Length) if setting.LFS.StartServer { 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.LessOrEqual(t, resp.Body.Len(), 1024) if resp.Body.Len() != littleSize && resp.Body.Len() <= 1024 { assert.Contains(t, resp.Body.String(), lfs.MetaFileIdentifier) } } if !testing.Short() { req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", big)) resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK) assert.Equal(t, bigSize, resp.Length) if setting.LFS.StartServer { 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()) if resp.Body.Len() != bigSize && resp.Body.Len() <= 1024 { assert.Contains(t, resp.Body.String(), lfs.MetaFileIdentifier) } } } })} func mediaTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS string) { t.Run("Media", func(t *testing.T) { defer tests.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) if setting.LFS.StartServer { 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(git.DefaultContext, "lfs").AddArguments("locks").RunStdString(&git.RunOpts{Dir: repoPath}) require.NoError(t, err) _, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("lock").AddDynamicArguments(filename).RunStdString(&git.RunOpts{Dir: repoPath}) require.NoError(t, err) _, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("locks").RunStdString(&git.RunOpts{Dir: repoPath}) require.NoError(t, err) _, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("unlock").AddDynamicArguments(filename).RunStdString(&git.RunOpts{Dir: repoPath}) require.NoError(t, err)} func doCommitAndPush(t *testing.T, size int64, repoPath, prefix string) string { name := generateCommitWithNewData(t, size, repoPath, "user2@example.com", "User Two", prefix) _, _, err := git.NewCommand(git.DefaultContext, "push", "origin", "master").RunStdString(&git.RunOpts{Dir: repoPath}) // Push require.NoError(t, err) return name} func generateCommitWithNewData(t *testing.T, size int64, repoPath, email, fullName, prefix string) string { t.Helper() tmpFile, err := os.CreateTemp(repoPath, prefix) require.NoError(t, err) defer tmpFile.Close() _, err = io.CopyN(tmpFile, rand.Reader, size) require.NoError(t, err) // Commit // Now here we should explicitly allow lfs filters to run globalArgs := git.AllowLFSFiltersArgs() require.NoError(t, git.AddChangesWithArgs(repoPath, globalArgs, false, filepath.Base(tmpFile.Name()))) require.NoError(t, git.CommitChangesWithArgs(repoPath, globalArgs, 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())} func doBranchProtect(baseCtx *APITestContext, dstPath string) func(t *testing.T) { return func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("CreateBranchProtected", doGitCreateBranch(dstPath, "protected")) t.Run("PushProtectedBranch", doGitPushTestRepository(dstPath, "origin", "protected")) ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame, auth_model.AccessTokenScopeWriteRepository) t.Run("PushToNewProtectedBranch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("CreateBranchProtected", doGitCreateBranch(dstPath, "before-create-1")) t.Run("ProtectProtectedBranch", doProtectBranch(ctx, "before-create-1", parameterProtectBranch{ "enable_push": "all", "apply_to_admins": "on", })) t.Run("PushProtectedBranch", doGitPushTestRepository(dstPath, "origin", "before-create-1")) t.Run("GenerateCommit", func(t *testing.T) { generateCommitWithNewData(t, littleSize, dstPath, "user2@example.com", "User Two", "protected-file-data-") }) t.Run("ProtectProtectedBranch", doProtectBranch(ctx, "before-create-2", parameterProtectBranch{ "enable_push": "all", "protected_file_patterns": "protected-file-data-*", "apply_to_admins": "on", })) doGitPushTestRepositoryFail(dstPath, "origin", "HEAD:before-create-2")(t) }) t.Run("FailToPushToProtectedBranch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("ProtectProtectedBranch", doProtectBranch(ctx, "protected")) t.Run("Create modified-protected-branch", doGitCheckoutBranch(dstPath, "-b", "modified-protected-branch", "protected")) t.Run("GenerateCommit", func(t *testing.T) { generateCommitWithNewData(t, littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-") }) doGitPushTestRepositoryFail(dstPath, "origin", "modified-protected-branch:protected")(t) }) t.Run("PushToUnprotectedBranch", doGitPushTestRepository(dstPath, "origin", "modified-protected-branch:unprotected")) t.Run("FailToPushProtectedFilesToProtectedBranch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("Create modified-protected-file-protected-branch", doGitCheckoutBranch(dstPath, "-b", "modified-protected-file-protected-branch", "protected")) t.Run("GenerateCommit", func(t *testing.T) { generateCommitWithNewData(t, littleSize, dstPath, "user2@example.com", "User Two", "protected-file-") }) t.Run("ProtectedFilePathsApplyToAdmins", doProtectBranch(ctx, "protected")) doGitPushTestRepositoryFail(dstPath, "origin", "modified-protected-file-protected-branch:protected")(t) doGitCheckoutBranch(dstPath, "protected")(t) doGitPull(dstPath, "origin", "protected")(t) }) t.Run("PushUnprotectedFilesToProtectedBranch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("Create modified-unprotected-file-protected-branch", doGitCheckoutBranch(dstPath, "-b", "modified-unprotected-file-protected-branch", "protected")) t.Run("UnprotectedFilePaths", doProtectBranch(ctx, "protected", parameterProtectBranch{ "unprotected_file_patterns": "unprotected-file-*", })) t.Run("GenerateCommit", func(t *testing.T) { generateCommitWithNewData(t, littleSize, dstPath, "user2@example.com", "User Two", "unprotected-file-") }) doGitPushTestRepository(dstPath, "origin", "modified-unprotected-file-protected-branch:protected")(t) doGitCheckoutBranch(dstPath, "protected")(t) doGitPull(dstPath, "origin", "protected")(t) }) user, err := user_model.GetUserByName(db.DefaultContext, baseCtx.Username) require.NoError(t, err) t.Run("WhitelistUsers", doProtectBranch(ctx, "protected", parameterProtectBranch{ "enable_push": "whitelist", "enable_whitelist": "on", "whitelist_users": strconv.FormatInt(user.ID, 10), })) t.Run("WhitelistedUserFailToForcePushToProtectedBranch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("Create toforce", doGitCheckoutBranch(dstPath, "-b", "toforce", "master")) t.Run("GenerateCommit", func(t *testing.T) { generateCommitWithNewData(t, littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-") }) doGitPushTestRepositoryFail(dstPath, "-f", "origin", "toforce:protected")(t) }) t.Run("WhitelistedUserPushToProtectedBranch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("Create topush", doGitCheckoutBranch(dstPath, "-b", "topush", "protected")) t.Run("GenerateCommit", func(t *testing.T) { generateCommitWithNewData(t, littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-") }) doGitPushTestRepository(dstPath, "origin", "topush:protected")(t) }) }} type parameterProtectBranch map[string]string func doProtectBranch(ctx APITestContext, branch string, addParameter ...parameterProtectBranch) func(t *testing.T) { // We are going to just use the owner to set the protection. return func(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: ctx.Reponame, OwnerName: ctx.Username}) rule := &git_model.ProtectedBranch{RuleName: branch, RepoID: repo.ID} unittest.LoadBeanIfExists(rule) csrf := GetCSRF(t, ctx.Session, fmt.Sprintf("/%s/%s/settings/branches", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame))) parameter := parameterProtectBranch{ "_csrf": csrf, "rule_id": strconv.FormatInt(rule.ID, 10), "rule_name": branch, } if len(addParameter) > 0 { for k, v := range addParameter[0] { parameter[k] = v } } // Change branch to protected req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/branches/edit", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)), parameter) ctx.Session.MakeRequest(t, req, http.StatusSeeOther) // Check if master branch has been locked successfully flashCookie := ctx.Session.GetCookie(gitea_context.CookieNameFlash) assert.NotNil(t, flashCookie) assert.Equal(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2522"+url.QueryEscape(branch)+"%2522%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value) }} func doMergeFork(ctx, baseCtx APITestContext, baseBranch, headBranch string) func(t *testing.T) { return func(t *testing.T) { defer tests.PrintCurrentTest(t)() var pr api.PullRequest var err error // Create a test pull request t.Run("CreatePullRequest", func(t *testing.T) { pr, err = doAPICreatePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, baseBranch, headBranch)(t) require.NoError(t, err) }) // Ensure the PR page works. // For the base repository owner, the PR is not editable (maintainer edits are not enabled): t.Run("EnsureCanSeePull", doEnsureCanSeePull(baseCtx, pr, false)) // For the head repository owner, the PR is editable: headSession := loginUser(t, "user2") headToken := getTokenForLoggedInUser(t, headSession, auth_model.AccessTokenScopeReadRepository, auth_model.AccessTokenScopeReadUser) headCtx := APITestContext{ Session: headSession, Token: headToken, Username: baseCtx.Username, Reponame: baseCtx.Reponame, } t.Run("EnsureCanSeePull", doEnsureCanSeePull(headCtx, pr, true))Lines 1–500 of 1158. Load more
// Copyright 2017 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package integration import ( "bytes" "crypto/rand" "encoding/hex" "fmt" "io" "net/http" "net/url" "os" "os/exec" "path" "path/filepath" "strconv" "strings" "testing" "time" auth_model "forgejo.org/models/auth" "forgejo.org/models/db" git_model "forgejo.org/models/git" issues_model "forgejo.org/models/issues" "forgejo.org/models/perm" repo_model "forgejo.org/models/repo" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" "forgejo.org/modules/git" "forgejo.org/modules/gitrepo" "forgejo.org/modules/lfs" "forgejo.org/modules/setting" api "forgejo.org/modules/structs" gitea_context "forgejo.org/services/context" files_service "forgejo.org/services/repository/files" "forgejo.org/tests" "github.com/kballard/go-shellquote" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) const ( littleSize = 1024 // 1KiB bigSize = 32 * 1024 * 1024 // 32MiB ) func TestGit(t *testing.T) { onGiteaRun(t, testGit) } func testGit(t *testing.T, u *url.URL) { username := "user2" baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) u.Path = baseAPITestContext.GitPath() forkedUserCtx := NewAPITestContext(t, "user4", "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) t.Run("HTTP", func(t *testing.T) { ensureAnonymousClone(t, u) forEachObjectFormat(t, func(t *testing.T, objectFormat git.ObjectFormat) { defer tests.PrintCurrentTest(t)() httpContext := baseAPITestContext httpContext.Reponame = "repo-tmp-17-" + objectFormat.Name() forkedUserCtx.Reponame = httpContext.Reponame dstPath := t.TempDir() t.Run("CreateRepoInDifferentUser", doAPICreateRepository(forkedUserCtx, false, objectFormat)) t.Run("AddUserAsCollaborator", doAPIAddCollaborator(forkedUserCtx, httpContext.Username, perm.AccessModeRead)) t.Run("ForkFromDifferentUser", doAPIForkRepository(httpContext, forkedUserCtx.Username)) u.Path = httpContext.GitPath() u.User = url.UserPassword(username, userPassword) t.Run("Clone", doGitClone(dstPath, u)) dstPath2 := t.TempDir() t.Run("Partial Clone", doPartialGitClone(dstPath2, 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("CreateAgitFlowPull", doCreateAgitFlowPull(dstPath, &httpContext, "test/head")) t.Run("InternalReferences", doInternalReferences(&httpContext, dstPath)) t.Run("BranchProtect", doBranchProtect(&httpContext, dstPath)) t.Run("AutoMerge", doAutoPRMerge(&httpContext, dstPath)) t.Run("CreatePRAndSetManuallyMerged", doCreatePRAndSetManuallyMerged(httpContext, httpContext, dstPath, "master", "test-manually-merge")) t.Run("MergeFork", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("CreatePRAndMerge", doMergeFork(httpContext, forkedUserCtx, "master", httpContext.Username+":master")) rawTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS) mediaTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS) }) t.Run("PushCreate", doPushCreate(httpContext, u, objectFormat)) }) }) t.Run("SSH", func(t *testing.T) { forEachObjectFormat(t, func(t *testing.T, objectFormat git.ObjectFormat) { defer tests.PrintCurrentTest(t)() sshContext := baseAPITestContext sshContext.Reponame = "repo-tmp-18-" + objectFormat.Name() keyname := "my-testing-key" forkedUserCtx.Reponame = sshContext.Reponame t.Run("CreateRepoInDifferentUser", doAPICreateRepository(forkedUserCtx, false, objectFormat)) t.Run("AddUserAsCollaborator", doAPIAddCollaborator(forkedUserCtx, sshContext.Username, perm.AccessModeRead)) t.Run("ForkFromDifferentUser", doAPIForkRepository(sshContext, forkedUserCtx.Username)) // Setup key the user ssh key withKeyFile(t, keyname, func(keyFile string) { var publicKeyID int64 t.Run("CreateUserKey", doAPICreateUserKey(sshContext, "test-key-"+objectFormat.Name(), keyFile, func(t *testing.T, pk api.PublicKey) { publicKeyID = pk.ID })) // Setup remote link // TODO: get url from api sshURL := createSSHUrl(sshContext.GitPath(), u) // Setup clone folder dstPath := t.TempDir() 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("CreateAgitFlowPull", doCreateAgitFlowPull(dstPath, &sshContext, "test/head2")) t.Run("InternalReferences", doInternalReferences(&sshContext, dstPath)) t.Run("BranchProtect", doBranchProtect(&sshContext, dstPath)) t.Run("MergeFork", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("CreatePRAndMerge", doMergeFork(sshContext, forkedUserCtx, "master", sshContext.Username+":master")) rawTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS) mediaTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS) }) t.Run("PushCreate", doPushCreate(sshContext, sshURL, objectFormat)) t.Run("LFS no access", doLFSNoAccess(sshContext, publicKeyID, objectFormat)) }) }) }) } func ensureAnonymousClone(t *testing.T, u *url.URL) { dstLocalPath := t.TempDir() t.Run("CloneAnonymous", doGitClone(dstLocalPath, u)) } func standardCommitAndPushTest(t *testing.T, dstPath string) (little, big string) { t.Run("Standard", func(t *testing.T) { defer tests.PrintCurrentTest(t)() little, big = commitAndPushTest(t, dstPath, "data-file-") }) return little, big } func lfsCommitAndPushTest(t *testing.T, dstPath string) (littleLFS, bigLFS string) { t.Run("LFS", func(t *testing.T) { defer tests.PrintCurrentTest(t)() prefix := "lfs-data-file-" err := git.NewCommand(git.DefaultContext, "lfs").AddArguments("install").Run(&git.RunOpts{Dir: dstPath}) require.NoError(t, err) _, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("track").AddDynamicArguments(prefix + "*").RunStdString(&git.RunOpts{Dir: dstPath}) require.NoError(t, err) err = git.AddChanges(dstPath, false, ".gitattributes") require.NoError(t, err) err = git.CommitChangesWithArgs(dstPath, git.AllowLFSFiltersArgs(), git.CommitChangesOptions{ Committer: &git.Signature{ Email: "user2@example.com", Name: "User Two", When: time.Now(), }, Author: &git.Signature{ Email: "user2@example.com", Name: "User Two", When: time.Now(), }, Message: fmt.Sprintf("Testing commit @ %v", time.Now()), }) require.NoError(t, err) littleLFS, bigLFS = commitAndPushTest(t, dstPath, prefix) t.Run("Locks", func(t *testing.T) { defer tests.PrintCurrentTest(t)() lockTest(t, dstPath) }) }) return littleLFS, bigLFS } func commitAndPushTest(t *testing.T, dstPath, prefix string) (little, big string) { t.Run("PushCommit", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("Little", func(t *testing.T) { defer tests.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 } defer tests.PrintCurrentTest(t)() big = doCommitAndPush(t, bigSize, dstPath, prefix) }) }) return little, big } func rawTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS string) { t.Run("Raw", func(t *testing.T) { defer tests.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.MakeRequestNilResponseRecorder(t, req, http.StatusOK) assert.Equal(t, littleSize, resp.Length) if setting.LFS.StartServer { 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.LessOrEqual(t, resp.Body.Len(), 1024) if resp.Body.Len() != littleSize && resp.Body.Len() <= 1024 { assert.Contains(t, resp.Body.String(), lfs.MetaFileIdentifier) } } if !testing.Short() { req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", big)) resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK) assert.Equal(t, bigSize, resp.Length) if setting.LFS.StartServer { 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()) if resp.Body.Len() != bigSize && resp.Body.Len() <= 1024 { assert.Contains(t, resp.Body.String(), lfs.MetaFileIdentifier) } } } }) } func mediaTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS string) { t.Run("Media", func(t *testing.T) { defer tests.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) if setting.LFS.StartServer { 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(git.DefaultContext, "lfs").AddArguments("locks").RunStdString(&git.RunOpts{Dir: repoPath}) require.NoError(t, err) _, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("lock").AddDynamicArguments(filename).RunStdString(&git.RunOpts{Dir: repoPath}) require.NoError(t, err) _, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("locks").RunStdString(&git.RunOpts{Dir: repoPath}) require.NoError(t, err) _, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("unlock").AddDynamicArguments(filename).RunStdString(&git.RunOpts{Dir: repoPath}) require.NoError(t, err) } func doCommitAndPush(t *testing.T, size int64, repoPath, prefix string) string { name := generateCommitWithNewData(t, size, repoPath, "user2@example.com", "User Two", prefix) _, _, err := git.NewCommand(git.DefaultContext, "push", "origin", "master").RunStdString(&git.RunOpts{Dir: repoPath}) // Push require.NoError(t, err) return name } func generateCommitWithNewData(t *testing.T, size int64, repoPath, email, fullName, prefix string) string { t.Helper() tmpFile, err := os.CreateTemp(repoPath, prefix) require.NoError(t, err) defer tmpFile.Close() _, err = io.CopyN(tmpFile, rand.Reader, size) require.NoError(t, err) // Commit // Now here we should explicitly allow lfs filters to run globalArgs := git.AllowLFSFiltersArgs() require.NoError(t, git.AddChangesWithArgs(repoPath, globalArgs, false, filepath.Base(tmpFile.Name()))) require.NoError(t, git.CommitChangesWithArgs(repoPath, globalArgs, 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()) } func doBranchProtect(baseCtx *APITestContext, dstPath string) func(t *testing.T) { return func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("CreateBranchProtected", doGitCreateBranch(dstPath, "protected")) t.Run("PushProtectedBranch", doGitPushTestRepository(dstPath, "origin", "protected")) ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame, auth_model.AccessTokenScopeWriteRepository) t.Run("PushToNewProtectedBranch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("CreateBranchProtected", doGitCreateBranch(dstPath, "before-create-1")) t.Run("ProtectProtectedBranch", doProtectBranch(ctx, "before-create-1", parameterProtectBranch{ "enable_push": "all", "apply_to_admins": "on", })) t.Run("PushProtectedBranch", doGitPushTestRepository(dstPath, "origin", "before-create-1")) t.Run("GenerateCommit", func(t *testing.T) { generateCommitWithNewData(t, littleSize, dstPath, "user2@example.com", "User Two", "protected-file-data-") }) t.Run("ProtectProtectedBranch", doProtectBranch(ctx, "before-create-2", parameterProtectBranch{ "enable_push": "all", "protected_file_patterns": "protected-file-data-*", "apply_to_admins": "on", })) doGitPushTestRepositoryFail(dstPath, "origin", "HEAD:before-create-2")(t) }) t.Run("FailToPushToProtectedBranch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("ProtectProtectedBranch", doProtectBranch(ctx, "protected")) t.Run("Create modified-protected-branch", doGitCheckoutBranch(dstPath, "-b", "modified-protected-branch", "protected")) t.Run("GenerateCommit", func(t *testing.T) { generateCommitWithNewData(t, littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-") }) doGitPushTestRepositoryFail(dstPath, "origin", "modified-protected-branch:protected")(t) }) t.Run("PushToUnprotectedBranch", doGitPushTestRepository(dstPath, "origin", "modified-protected-branch:unprotected")) t.Run("FailToPushProtectedFilesToProtectedBranch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("Create modified-protected-file-protected-branch", doGitCheckoutBranch(dstPath, "-b", "modified-protected-file-protected-branch", "protected")) t.Run("GenerateCommit", func(t *testing.T) { generateCommitWithNewData(t, littleSize, dstPath, "user2@example.com", "User Two", "protected-file-") }) t.Run("ProtectedFilePathsApplyToAdmins", doProtectBranch(ctx, "protected")) doGitPushTestRepositoryFail(dstPath, "origin", "modified-protected-file-protected-branch:protected")(t) doGitCheckoutBranch(dstPath, "protected")(t) doGitPull(dstPath, "origin", "protected")(t) }) t.Run("PushUnprotectedFilesToProtectedBranch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("Create modified-unprotected-file-protected-branch", doGitCheckoutBranch(dstPath, "-b", "modified-unprotected-file-protected-branch", "protected")) t.Run("UnprotectedFilePaths", doProtectBranch(ctx, "protected", parameterProtectBranch{ "unprotected_file_patterns": "unprotected-file-*", })) t.Run("GenerateCommit", func(t *testing.T) { generateCommitWithNewData(t, littleSize, dstPath, "user2@example.com", "User Two", "unprotected-file-") }) doGitPushTestRepository(dstPath, "origin", "modified-unprotected-file-protected-branch:protected")(t) doGitCheckoutBranch(dstPath, "protected")(t) doGitPull(dstPath, "origin", "protected")(t) }) user, err := user_model.GetUserByName(db.DefaultContext, baseCtx.Username) require.NoError(t, err) t.Run("WhitelistUsers", doProtectBranch(ctx, "protected", parameterProtectBranch{ "enable_push": "whitelist", "enable_whitelist": "on", "whitelist_users": strconv.FormatInt(user.ID, 10), })) t.Run("WhitelistedUserFailToForcePushToProtectedBranch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("Create toforce", doGitCheckoutBranch(dstPath, "-b", "toforce", "master")) t.Run("GenerateCommit", func(t *testing.T) { generateCommitWithNewData(t, littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-") }) doGitPushTestRepositoryFail(dstPath, "-f", "origin", "toforce:protected")(t) }) t.Run("WhitelistedUserPushToProtectedBranch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("Create topush", doGitCheckoutBranch(dstPath, "-b", "topush", "protected")) t.Run("GenerateCommit", func(t *testing.T) { generateCommitWithNewData(t, littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-") }) doGitPushTestRepository(dstPath, "origin", "topush:protected")(t) }) } } type parameterProtectBranch map[string]string func doProtectBranch(ctx APITestContext, branch string, addParameter ...parameterProtectBranch) func(t *testing.T) { // We are going to just use the owner to set the protection. return func(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: ctx.Reponame, OwnerName: ctx.Username}) rule := &git_model.ProtectedBranch{RuleName: branch, RepoID: repo.ID} unittest.LoadBeanIfExists(rule) csrf := GetCSRF(t, ctx.Session, fmt.Sprintf("/%s/%s/settings/branches", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame))) parameter := parameterProtectBranch{ "_csrf": csrf, "rule_id": strconv.FormatInt(rule.ID, 10), "rule_name": branch, } if len(addParameter) > 0 { for k, v := range addParameter[0] { parameter[k] = v } } // Change branch to protected req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/branches/edit", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)), parameter) ctx.Session.MakeRequest(t, req, http.StatusSeeOther) // Check if master branch has been locked successfully flashCookie := ctx.Session.GetCookie(gitea_context.CookieNameFlash) assert.NotNil(t, flashCookie) assert.Equal(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2522"+url.QueryEscape(branch)+"%2522%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value) } } func doMergeFork(ctx, baseCtx APITestContext, baseBranch, headBranch string) func(t *testing.T) { return func(t *testing.T) { defer tests.PrintCurrentTest(t)() var pr api.PullRequest var err error // Create a test pull request t.Run("CreatePullRequest", func(t *testing.T) { pr, err = doAPICreatePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, baseBranch, headBranch)(t) require.NoError(t, err) }) // Ensure the PR page works. // For the base repository owner, the PR is not editable (maintainer edits are not enabled): t.Run("EnsureCanSeePull", doEnsureCanSeePull(baseCtx, pr, false)) // For the head repository owner, the PR is editable: headSession := loginUser(t, "user2") headToken := getTokenForLoggedInUser(t, headSession, auth_model.AccessTokenScopeReadRepository, auth_model.AccessTokenScopeReadUser) headCtx := APITestContext{ Session: headSession, Token: headToken, Username: baseCtx.Username, Reponame: baseCtx.Reponame, } t.Run("EnsureCanSeePull", doEnsureCanSeePull(headCtx, pr, true))