mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-20 00:19:00 +00:00
Integration test processes now get their own work directory, config file and random ports, so several runs can execute in parallel on one machine. The slowest tests also lose fixed sleeps and needlessly expensive setup, with their assertions kept. - Batch PostgreSQL sequence resets, the main per-test fixture cost there - Cheap hashing params in hash and OAuth2 fixture tests, full-cost vectors stay - Single fetch for negative Actions runner assertions instead of 1s polls - Queue flushes and backdated timestamps instead of sleeps - In-memory SQLite test journal, tmpfs for PostgreSQL in CI - E2E instance writes SSH keys into its work directory instead of `~/.ssh/authorized_keys`
265 lines
9.2 KiB
Go
265 lines
9.2 KiB
Go
// Copyright 2024 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package integration
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"testing"
|
|
|
|
auth_model "gitea.dev/models/auth"
|
|
"gitea.dev/models/db"
|
|
git_model "gitea.dev/models/git"
|
|
repo_model "gitea.dev/models/repo"
|
|
"gitea.dev/models/unittest"
|
|
user_model "gitea.dev/models/user"
|
|
"gitea.dev/modules/git"
|
|
"gitea.dev/modules/git/gitcmd"
|
|
"gitea.dev/modules/setting"
|
|
"gitea.dev/modules/test"
|
|
repo_service "gitea.dev/services/repository"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestGitPush(t *testing.T) {
|
|
onGiteaRun(t, testGitPush)
|
|
}
|
|
|
|
func testGitPush(t *testing.T, u *url.URL) {
|
|
t.Run("Push branches at once", func(t *testing.T) {
|
|
runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
|
|
for i := range 4 {
|
|
branchName := fmt.Sprintf("branch-%d", i)
|
|
pushed = append(pushed, branchName)
|
|
doGitCreateBranch(gitPath, branchName)(t)
|
|
}
|
|
pushed = append(pushed, "master")
|
|
doGitPushTestRepository(gitPath, "origin", "--all")(t)
|
|
return pushed, deleted
|
|
})
|
|
})
|
|
|
|
t.Run("Push branches exists", func(t *testing.T) {
|
|
runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
|
|
for i := range 4 {
|
|
branchName := fmt.Sprintf("branch-%d", i)
|
|
if i < 2 {
|
|
pushed = append(pushed, branchName)
|
|
}
|
|
doGitCreateBranch(gitPath, branchName)(t)
|
|
}
|
|
pushed = append(pushed, "master")
|
|
args := append([]string{"origin"}, pushed...)
|
|
doGitPushTestRepository(gitPath, args...)(t)
|
|
|
|
pushed = pushed[:0]
|
|
for i := range 2 {
|
|
branchName := fmt.Sprintf("branch-%d", i)
|
|
pushed = append(pushed, branchName)
|
|
doGitCheckoutWriteFileCommit(localGitAddCommitOptions{
|
|
LocalRepoPath: gitPath,
|
|
CheckoutBranch: branchName,
|
|
TreeFilePath: fmt.Sprintf("file-%s.txt", branchName),
|
|
TreeFileContent: "file " + branchName,
|
|
})(t)
|
|
}
|
|
|
|
for i := 2; i < 4; i++ {
|
|
pushed = append(pushed, fmt.Sprintf("branch-%d", i))
|
|
}
|
|
pushed = append(pushed, "master")
|
|
|
|
// push all, so that master is not changed
|
|
doGitPushTestRepository(gitPath, "origin", "--all")(t)
|
|
|
|
return pushed, deleted
|
|
})
|
|
})
|
|
|
|
t.Run("Push branches one by one", func(t *testing.T) {
|
|
runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
|
|
for i := range 4 {
|
|
branchName := fmt.Sprintf("branch-%d", i)
|
|
doGitCreateBranch(gitPath, branchName)(t)
|
|
doGitPushTestRepository(gitPath, "origin", branchName)(t)
|
|
pushed = append(pushed, branchName)
|
|
}
|
|
return pushed, deleted
|
|
})
|
|
})
|
|
|
|
t.Run("Push branch with options", func(t *testing.T) {
|
|
runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
|
|
branchName := "branch-with-options"
|
|
doGitCreateBranch(gitPath, branchName)(t)
|
|
doGitPushTestRepository(gitPath, "origin", branchName, "-o", "repo.private=true", "-o", "repo.template=true")(t)
|
|
pushed = append(pushed, branchName)
|
|
|
|
return pushed, deleted
|
|
})
|
|
})
|
|
|
|
t.Run("Delete branches", func(t *testing.T) {
|
|
runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
|
|
doGitPushTestRepository(gitPath, "origin", "master")(t) // make sure master is the default branch instead of a branch we are going to delete
|
|
pushed = append(pushed, "master")
|
|
|
|
for i := range 4 {
|
|
branchName := fmt.Sprintf("branch-%d", i)
|
|
pushed = append(pushed, branchName)
|
|
doGitCreateBranch(gitPath, branchName)(t)
|
|
}
|
|
doGitPushTestRepository(gitPath, "origin", "--all")(t)
|
|
|
|
for i := range 2 {
|
|
branchName := fmt.Sprintf("branch-%d", i)
|
|
doGitPushTestRepository(gitPath, "origin", "--delete", branchName)(t)
|
|
deleted = append(deleted, branchName)
|
|
}
|
|
return pushed, deleted
|
|
})
|
|
})
|
|
|
|
t.Run("Push to deleted branch", func(t *testing.T) {
|
|
runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
|
|
doGitPushTestRepository(gitPath, "origin", "master")(t) // make sure master is the default branch instead of a branch we are going to delete
|
|
pushed = append(pushed, "master")
|
|
|
|
doGitCreateBranch(gitPath, "branch-1")(t)
|
|
doGitPushTestRepository(gitPath, "origin", "branch-1")(t)
|
|
pushed = append(pushed, "branch-1")
|
|
|
|
// delete and restore
|
|
doGitPushTestRepository(gitPath, "origin", "--delete", "branch-1")(t)
|
|
doGitPushTestRepository(gitPath, "origin", "branch-1")(t)
|
|
|
|
return pushed, deleted
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestGitPushVisibilityOption(t *testing.T) {
|
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
|
repo, err := repo_service.CreateRepository(t.Context(), user, user, repo_service.CreateRepoOptions{
|
|
Name: "repo-visibility-option",
|
|
AutoInit: false,
|
|
DefaultBranch: "master",
|
|
IsPrivate: false,
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, repo)
|
|
|
|
gitPath := t.TempDir()
|
|
doGitInitTestRepository(gitPath)(t)
|
|
|
|
oldPath, oldUser := u.Path, u.User
|
|
defer func() { u.Path, u.User = oldPath, oldUser }()
|
|
u.Path = repo.FullName() + ".git"
|
|
u.User = url.UserPassword(user.LowerName, userPassword)
|
|
doGitAddRemote(gitPath, "origin", u)(t)
|
|
|
|
// The first push into an empty repository is a "push-to-create", so the
|
|
// repo.private push option is honored to set the initial visibility.
|
|
doGitPushTestRepository(gitPath, "origin", "master", "-o", "repo.private=true")(t)
|
|
repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo.ID})
|
|
assert.True(t, repo.IsPrivate, "repo.private option should apply on push-to-create")
|
|
|
|
// The repository is now populated; a later push must NOT silently flip
|
|
// visibility, otherwise a repo admin could change it bypassing the audit
|
|
// trail, webhooks, and notifications a proper settings change would fire.
|
|
doGitCreateBranch(gitPath, "branch2")(t)
|
|
doGitPushTestRepository(gitPath, "origin", "branch2", "-o", "repo.private=false")(t)
|
|
repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo.ID})
|
|
assert.True(t, repo.IsPrivate, "repo.private option must be ignored on an existing repository")
|
|
|
|
defer test.MockVariableValue(&setting.Repository.ForcePrivate, true)()
|
|
forcedRepo, err := repo_service.CreateRepository(t.Context(), user, user, repo_service.CreateRepoOptions{Name: "repo-visibility-forced", DefaultBranch: "master", IsPrivate: true})
|
|
require.NoError(t, err)
|
|
u.Path = forcedRepo.FullName() + ".git"
|
|
doGitAddRemote(gitPath, "forced", u)(t)
|
|
doGitPushTestRepository(gitPath, "forced", "master", "-o", "repo.private=false")(t)
|
|
assert.True(t, unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: forcedRepo.ID}).IsPrivate)
|
|
})
|
|
}
|
|
|
|
func runTestGitPush(t *testing.T, u *url.URL, gitOperation func(t *testing.T, gitPath string) (pushed, deleted []string)) {
|
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
|
repo, err := repo_service.CreateRepository(t.Context(), user, user, repo_service.CreateRepoOptions{
|
|
Name: "repo-to-push",
|
|
Description: "test git push",
|
|
AutoInit: false,
|
|
DefaultBranch: "main",
|
|
IsPrivate: false,
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, repo)
|
|
|
|
gitPath := t.TempDir()
|
|
|
|
doGitInitTestRepository(gitPath)(t)
|
|
|
|
oldPath := u.Path
|
|
oldUser := u.User
|
|
defer func() {
|
|
u.Path = oldPath
|
|
u.User = oldUser
|
|
}()
|
|
u.Path = repo.FullName() + ".git"
|
|
u.User = url.UserPassword(user.LowerName, userPassword)
|
|
|
|
doGitAddRemote(gitPath, "origin", u)(t)
|
|
|
|
gitRepo, err := git.OpenRepositoryLocal(t.Context(), gitPath)
|
|
require.NoError(t, err)
|
|
defer gitRepo.Close()
|
|
|
|
pushedBranches, deletedBranches := gitOperation(t, gitPath)
|
|
|
|
dbBranches := make([]*git_model.Branch, 0)
|
|
require.NoError(t, db.GetEngine(t.Context()).Where("repo_id=?", repo.ID).Find(&dbBranches))
|
|
assert.Lenf(t, dbBranches, len(pushedBranches), "mismatched number of branches in db")
|
|
dbBranchesMap := make(map[string]*git_model.Branch, len(dbBranches))
|
|
for _, branch := range dbBranches {
|
|
dbBranchesMap[branch.Name] = branch
|
|
}
|
|
|
|
deletedBranchesMap := make(map[string]bool, len(deletedBranches))
|
|
for _, branchName := range deletedBranches {
|
|
deletedBranchesMap[branchName] = true
|
|
}
|
|
|
|
for _, branchName := range pushedBranches {
|
|
branch, ok := dbBranchesMap[branchName]
|
|
deleted := deletedBranchesMap[branchName]
|
|
assert.True(t, ok, "branch %s not found in database", branchName)
|
|
assert.Equal(t, deleted, branch.IsDeleted, "IsDeleted of %s is %v, but it's expected to be %v", branchName, branch.IsDeleted, deleted)
|
|
commitID, err := gitRepo.GetBranchCommitID(t.Context(), branchName)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, commitID, branch.CommitID)
|
|
}
|
|
|
|
require.NoError(t, repo_service.DeleteRepositoryDirectly(t.Context(), repo.ID))
|
|
}
|
|
|
|
func TestPushPullRefs(t *testing.T) {
|
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
|
baseAPITestContext := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
|
|
|
|
u.Path = baseAPITestContext.GitPath()
|
|
u.User = url.UserPassword("user2", userPassword)
|
|
|
|
dstPath := t.TempDir()
|
|
doGitClone(dstPath, u)(t)
|
|
|
|
cmd := gitcmd.NewCommand("push", "--delete", "origin", "refs/pull/2/head")
|
|
stdout, stderr, err := cmd.WithDir(dstPath).RunStdString(t.Context())
|
|
assert.Error(t, err)
|
|
assert.Empty(t, stdout)
|
|
assert.NotContains(t, stderr, "[deleted]", "stderr: %s", stderr)
|
|
})
|
|
}
|