code.gitea.io/gitea@v1.22.3/tests/integration/git_push_test.go (about)

     1  // Copyright 2024 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package integration
     5  
     6  import (
     7  	"fmt"
     8  	"net/url"
     9  	"strings"
    10  	"testing"
    11  
    12  	auth_model "code.gitea.io/gitea/models/auth"
    13  	"code.gitea.io/gitea/models/db"
    14  	git_model "code.gitea.io/gitea/models/git"
    15  	"code.gitea.io/gitea/models/unittest"
    16  	user_model "code.gitea.io/gitea/models/user"
    17  	"code.gitea.io/gitea/modules/git"
    18  	repo_service "code.gitea.io/gitea/services/repository"
    19  
    20  	"github.com/stretchr/testify/assert"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  func TestGitPush(t *testing.T) {
    25  	onGiteaRun(t, testGitPush)
    26  }
    27  
    28  func testGitPush(t *testing.T, u *url.URL) {
    29  	t.Run("Push branches at once", func(t *testing.T) {
    30  		runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
    31  			for i := 0; i < 100; i++ {
    32  				branchName := fmt.Sprintf("branch-%d", i)
    33  				pushed = append(pushed, branchName)
    34  				doGitCreateBranch(gitPath, branchName)(t)
    35  			}
    36  			pushed = append(pushed, "master")
    37  			doGitPushTestRepository(gitPath, "origin", "--all")(t)
    38  			return pushed, deleted
    39  		})
    40  	})
    41  
    42  	t.Run("Push branches exists", func(t *testing.T) {
    43  		runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
    44  			for i := 0; i < 10; i++ {
    45  				branchName := fmt.Sprintf("branch-%d", i)
    46  				if i < 5 {
    47  					pushed = append(pushed, branchName)
    48  				}
    49  				doGitCreateBranch(gitPath, branchName)(t)
    50  			}
    51  			// only push master and the first 5 branches
    52  			pushed = append(pushed, "master")
    53  			args := append([]string{"origin"}, pushed...)
    54  			doGitPushTestRepository(gitPath, args...)(t)
    55  
    56  			pushed = pushed[:0]
    57  			// do some changes for the first 5 branches created above
    58  			for i := 0; i < 5; i++ {
    59  				branchName := fmt.Sprintf("branch-%d", i)
    60  				pushed = append(pushed, branchName)
    61  
    62  				doGitAddSomeCommits(gitPath, branchName)(t)
    63  			}
    64  
    65  			for i := 5; i < 10; i++ {
    66  				pushed = append(pushed, fmt.Sprintf("branch-%d", i))
    67  			}
    68  			pushed = append(pushed, "master")
    69  
    70  			// push all, so that master are not chagned
    71  			doGitPushTestRepository(gitPath, "origin", "--all")(t)
    72  
    73  			return pushed, deleted
    74  		})
    75  	})
    76  
    77  	t.Run("Push branches one by one", func(t *testing.T) {
    78  		runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
    79  			for i := 0; i < 100; i++ {
    80  				branchName := fmt.Sprintf("branch-%d", i)
    81  				doGitCreateBranch(gitPath, branchName)(t)
    82  				doGitPushTestRepository(gitPath, "origin", branchName)(t)
    83  				pushed = append(pushed, branchName)
    84  			}
    85  			return pushed, deleted
    86  		})
    87  	})
    88  
    89  	t.Run("Push branch with options", func(t *testing.T) {
    90  		runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
    91  			branchName := "branch-with-options"
    92  			doGitCreateBranch(gitPath, branchName)(t)
    93  			doGitPushTestRepository(gitPath, "origin", branchName, "-o", "repo.private=true", "-o", "repo.template=true")(t)
    94  			pushed = append(pushed, branchName)
    95  
    96  			return pushed, deleted
    97  		})
    98  	})
    99  
   100  	t.Run("Delete branches", func(t *testing.T) {
   101  		runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
   102  			doGitPushTestRepository(gitPath, "origin", "master")(t) // make sure master is the default branch instead of a branch we are going to delete
   103  			pushed = append(pushed, "master")
   104  
   105  			for i := 0; i < 100; i++ {
   106  				branchName := fmt.Sprintf("branch-%d", i)
   107  				pushed = append(pushed, branchName)
   108  				doGitCreateBranch(gitPath, branchName)(t)
   109  			}
   110  			doGitPushTestRepository(gitPath, "origin", "--all")(t)
   111  
   112  			for i := 0; i < 10; i++ {
   113  				branchName := fmt.Sprintf("branch-%d", i)
   114  				doGitPushTestRepository(gitPath, "origin", "--delete", branchName)(t)
   115  				deleted = append(deleted, branchName)
   116  			}
   117  			return pushed, deleted
   118  		})
   119  	})
   120  
   121  	t.Run("Push to deleted branch", func(t *testing.T) {
   122  		runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
   123  			doGitPushTestRepository(gitPath, "origin", "master")(t) // make sure master is the default branch instead of a branch we are going to delete
   124  			pushed = append(pushed, "master")
   125  
   126  			doGitCreateBranch(gitPath, "branch-1")(t)
   127  			doGitPushTestRepository(gitPath, "origin", "branch-1")(t)
   128  			pushed = append(pushed, "branch-1")
   129  
   130  			// delete and restore
   131  			doGitPushTestRepository(gitPath, "origin", "--delete", "branch-1")(t)
   132  			doGitPushTestRepository(gitPath, "origin", "branch-1")(t)
   133  
   134  			return pushed, deleted
   135  		})
   136  	})
   137  }
   138  
   139  func runTestGitPush(t *testing.T, u *url.URL, gitOperation func(t *testing.T, gitPath string) (pushed, deleted []string)) {
   140  	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
   141  	repo, err := repo_service.CreateRepository(db.DefaultContext, user, user, repo_service.CreateRepoOptions{
   142  		Name:          "repo-to-push",
   143  		Description:   "test git push",
   144  		AutoInit:      false,
   145  		DefaultBranch: "main",
   146  		IsPrivate:     false,
   147  	})
   148  	require.NoError(t, err)
   149  	require.NotEmpty(t, repo)
   150  
   151  	gitPath := t.TempDir()
   152  
   153  	doGitInitTestRepository(gitPath)(t)
   154  
   155  	oldPath := u.Path
   156  	oldUser := u.User
   157  	defer func() {
   158  		u.Path = oldPath
   159  		u.User = oldUser
   160  	}()
   161  	u.Path = repo.FullName() + ".git"
   162  	u.User = url.UserPassword(user.LowerName, userPassword)
   163  
   164  	doGitAddRemote(gitPath, "origin", u)(t)
   165  
   166  	gitRepo, err := git.OpenRepository(git.DefaultContext, gitPath)
   167  	require.NoError(t, err)
   168  	defer gitRepo.Close()
   169  
   170  	pushedBranches, deletedBranches := gitOperation(t, gitPath)
   171  
   172  	dbBranches := make([]*git_model.Branch, 0)
   173  	require.NoError(t, db.GetEngine(db.DefaultContext).Where("repo_id=?", repo.ID).Find(&dbBranches))
   174  	assert.Equalf(t, len(pushedBranches), len(dbBranches), "mismatched number of branches in db")
   175  	dbBranchesMap := make(map[string]*git_model.Branch, len(dbBranches))
   176  	for _, branch := range dbBranches {
   177  		dbBranchesMap[branch.Name] = branch
   178  	}
   179  
   180  	deletedBranchesMap := make(map[string]bool, len(deletedBranches))
   181  	for _, branchName := range deletedBranches {
   182  		deletedBranchesMap[branchName] = true
   183  	}
   184  
   185  	for _, branchName := range pushedBranches {
   186  		branch, ok := dbBranchesMap[branchName]
   187  		deleted := deletedBranchesMap[branchName]
   188  		assert.True(t, ok, "branch %s not found in database", branchName)
   189  		assert.Equal(t, deleted, branch.IsDeleted, "IsDeleted of %s is %v, but it's expected to be %v", branchName, branch.IsDeleted, deleted)
   190  		commitID, err := gitRepo.GetBranchCommitID(branchName)
   191  		require.NoError(t, err)
   192  		assert.Equal(t, commitID, branch.CommitID)
   193  	}
   194  
   195  	require.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, repo.ID))
   196  }
   197  
   198  func TestPushPullRefs(t *testing.T) {
   199  	onGiteaRun(t, func(t *testing.T, u *url.URL) {
   200  		baseAPITestContext := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
   201  
   202  		u.Path = baseAPITestContext.GitPath()
   203  		u.User = url.UserPassword("user2", userPassword)
   204  
   205  		dstPath := t.TempDir()
   206  		doGitClone(dstPath, u)(t)
   207  
   208  		cmd := git.NewCommand(git.DefaultContext, "push", "--delete", "origin", "refs/pull/2/head")
   209  		stdout, stderr, err := cmd.RunStdString(&git.RunOpts{
   210  			Dir: dstPath,
   211  		})
   212  		assert.Error(t, err)
   213  		assert.Empty(t, stdout)
   214  		assert.False(t, strings.Contains(stderr, "[deleted]"), "stderr: %s", stderr)
   215  	})
   216  }