code.gitea.io/gitea@v1.22.3/tests/integration/repo_branch_test.go (about) 1 // Copyright 2017 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package integration 5 6 import ( 7 "fmt" 8 "net/http" 9 "net/url" 10 "path" 11 "strings" 12 "testing" 13 14 auth_model "code.gitea.io/gitea/models/auth" 15 org_model "code.gitea.io/gitea/models/organization" 16 "code.gitea.io/gitea/models/perm" 17 repo_model "code.gitea.io/gitea/models/repo" 18 "code.gitea.io/gitea/models/unit" 19 "code.gitea.io/gitea/models/unittest" 20 api "code.gitea.io/gitea/modules/structs" 21 "code.gitea.io/gitea/modules/test" 22 "code.gitea.io/gitea/modules/translation" 23 "code.gitea.io/gitea/tests" 24 25 "github.com/PuerkitoBio/goquery" 26 "github.com/stretchr/testify/assert" 27 ) 28 29 func testCreateBranch(t testing.TB, session *TestSession, user, repo, oldRefSubURL, newBranchName string, expectedStatus int) string { 30 var csrf string 31 if expectedStatus == http.StatusNotFound { 32 // src/branch/branch_name may not container "_csrf" input, 33 // so we need to get it from cookies not from body 34 csrf = GetCSRFFromCookie(t, session, path.Join(user, repo, "src/branch/master")) 35 } else { 36 csrf = GetCSRFFromCookie(t, session, path.Join(user, repo, "src", oldRefSubURL)) 37 } 38 req := NewRequestWithValues(t, "POST", path.Join(user, repo, "branches/_new", oldRefSubURL), map[string]string{ 39 "_csrf": csrf, 40 "new_branch_name": newBranchName, 41 }) 42 resp := session.MakeRequest(t, req, expectedStatus) 43 if expectedStatus != http.StatusSeeOther { 44 return "" 45 } 46 return test.RedirectURL(resp) 47 } 48 49 func TestCreateBranch(t *testing.T) { 50 onGiteaRun(t, testCreateBranches) 51 } 52 53 func testCreateBranches(t *testing.T, giteaURL *url.URL) { 54 tests := []struct { 55 OldRefSubURL string 56 NewBranch string 57 CreateRelease string 58 FlashMessage string 59 ExpectedStatus int 60 }{ 61 { 62 OldRefSubURL: "branch/master", 63 NewBranch: "feature/test1", 64 ExpectedStatus: http.StatusSeeOther, 65 FlashMessage: translation.NewLocale("en-US").TrString("repo.branch.create_success", "feature/test1"), 66 }, 67 { 68 OldRefSubURL: "branch/master", 69 NewBranch: "", 70 ExpectedStatus: http.StatusSeeOther, 71 FlashMessage: translation.NewLocale("en-US").TrString("form.NewBranchName") + translation.NewLocale("en-US").TrString("form.require_error"), 72 }, 73 { 74 OldRefSubURL: "branch/master", 75 NewBranch: "feature=test1", 76 ExpectedStatus: http.StatusSeeOther, 77 FlashMessage: translation.NewLocale("en-US").TrString("repo.branch.create_success", "feature=test1"), 78 }, 79 { 80 OldRefSubURL: "branch/master", 81 NewBranch: strings.Repeat("b", 101), 82 ExpectedStatus: http.StatusSeeOther, 83 FlashMessage: translation.NewLocale("en-US").TrString("form.NewBranchName") + translation.NewLocale("en-US").TrString("form.max_size_error", "100"), 84 }, 85 { 86 OldRefSubURL: "branch/master", 87 NewBranch: "master", 88 ExpectedStatus: http.StatusSeeOther, 89 FlashMessage: translation.NewLocale("en-US").TrString("repo.branch.branch_already_exists", "master"), 90 }, 91 { 92 OldRefSubURL: "branch/master", 93 NewBranch: "master/test", 94 ExpectedStatus: http.StatusSeeOther, 95 FlashMessage: translation.NewLocale("en-US").TrString("repo.branch.branch_name_conflict", "master/test", "master"), 96 }, 97 { 98 OldRefSubURL: "commit/acd1d892867872cb47f3993468605b8aa59aa2e0", 99 NewBranch: "feature/test2", 100 ExpectedStatus: http.StatusNotFound, 101 }, 102 { 103 OldRefSubURL: "commit/65f1bf27bc3bf70f64657658635e66094edbcb4d", 104 NewBranch: "feature/test3", 105 ExpectedStatus: http.StatusSeeOther, 106 FlashMessage: translation.NewLocale("en-US").TrString("repo.branch.create_success", "feature/test3"), 107 }, 108 { 109 OldRefSubURL: "branch/master", 110 NewBranch: "v1.0.0", 111 CreateRelease: "v1.0.0", 112 ExpectedStatus: http.StatusSeeOther, 113 FlashMessage: translation.NewLocale("en-US").TrString("repo.branch.tag_collision", "v1.0.0"), 114 }, 115 { 116 OldRefSubURL: "tag/v1.0.0", 117 NewBranch: "feature/test4", 118 CreateRelease: "v1.0.1", 119 ExpectedStatus: http.StatusSeeOther, 120 FlashMessage: translation.NewLocale("en-US").TrString("repo.branch.create_success", "feature/test4"), 121 }, 122 } 123 for _, test := range tests { 124 session := loginUser(t, "user2") 125 if test.CreateRelease != "" { 126 createNewRelease(t, session, "/user2/repo1", test.CreateRelease, test.CreateRelease, false, false) 127 } 128 redirectURL := testCreateBranch(t, session, "user2", "repo1", test.OldRefSubURL, test.NewBranch, test.ExpectedStatus) 129 if test.ExpectedStatus == http.StatusSeeOther { 130 req := NewRequest(t, "GET", redirectURL) 131 resp := session.MakeRequest(t, req, http.StatusOK) 132 htmlDoc := NewHTMLParser(t, resp.Body) 133 assert.Contains(t, 134 strings.TrimSpace(htmlDoc.doc.Find(".ui.message").Text()), 135 test.FlashMessage, 136 ) 137 } 138 } 139 } 140 141 func TestCreateBranchInvalidCSRF(t *testing.T) { 142 defer tests.PrepareTestEnv(t)() 143 session := loginUser(t, "user2") 144 req := NewRequestWithValues(t, "POST", "user2/repo1/branches/_new/branch/master", map[string]string{ 145 "_csrf": "fake_csrf", 146 "new_branch_name": "test", 147 }) 148 resp := session.MakeRequest(t, req, http.StatusBadRequest) 149 assert.Contains(t, resp.Body.String(), "Invalid CSRF token") 150 } 151 152 func prepareBranch(t *testing.T, session *TestSession, repo *repo_model.Repository) { 153 baseRefSubURL := fmt.Sprintf("branch/%s", repo.DefaultBranch) 154 155 // create branch with no new commit 156 testCreateBranch(t, session, repo.OwnerName, repo.Name, baseRefSubURL, "no-commit", http.StatusSeeOther) 157 158 // create branch with commit 159 testCreateBranch(t, session, repo.OwnerName, repo.Name, baseRefSubURL, "new-commit", http.StatusSeeOther) 160 testAPINewFile(t, session, repo.OwnerName, repo.Name, "new-commit", "new-commit.txt", "new-commit") 161 162 // create deleted branch 163 testCreateBranch(t, session, repo.OwnerName, repo.Name, "branch/new-commit", "deleted-branch", http.StatusSeeOther) 164 testUIDeleteBranch(t, session, repo.OwnerName, repo.Name, "deleted-branch") 165 } 166 167 func testCreatePullToDefaultBranch(t *testing.T, session *TestSession, baseRepo, headRepo *repo_model.Repository, headBranch, title string) string { 168 srcRef := headBranch 169 if baseRepo.ID != headRepo.ID { 170 srcRef = fmt.Sprintf("%s/%s:%s", headRepo.OwnerName, headRepo.Name, headBranch) 171 } 172 resp := testPullCreate(t, session, baseRepo.OwnerName, baseRepo.Name, false, baseRepo.DefaultBranch, srcRef, title) 173 elem := strings.Split(test.RedirectURL(resp), "/") 174 // return pull request ID 175 return elem[4] 176 } 177 178 func prepareRepoPR(t *testing.T, baseSession, headSession *TestSession, baseRepo, headRepo *repo_model.Repository) { 179 // create opening PR 180 testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "branch/new-commit", "opening-pr", http.StatusSeeOther) 181 testCreatePullToDefaultBranch(t, baseSession, baseRepo, headRepo, "opening-pr", "opening pr") 182 183 // create closed PR 184 testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "branch/new-commit", "closed-pr", http.StatusSeeOther) 185 prID := testCreatePullToDefaultBranch(t, baseSession, baseRepo, headRepo, "closed-pr", "closed pr") 186 testIssueClose(t, baseSession, baseRepo.OwnerName, baseRepo.Name, prID) 187 188 // create closed PR with deleted branch 189 testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "branch/new-commit", "closed-pr-deleted", http.StatusSeeOther) 190 prID = testCreatePullToDefaultBranch(t, baseSession, baseRepo, headRepo, "closed-pr-deleted", "closed pr with deleted branch") 191 testIssueClose(t, baseSession, baseRepo.OwnerName, baseRepo.Name, prID) 192 testUIDeleteBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "closed-pr-deleted") 193 194 // create merged PR 195 testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "branch/new-commit", "merged-pr", http.StatusSeeOther) 196 prID = testCreatePullToDefaultBranch(t, baseSession, baseRepo, headRepo, "merged-pr", "merged pr") 197 testAPINewFile(t, headSession, headRepo.OwnerName, headRepo.Name, "merged-pr", fmt.Sprintf("new-commit-%s.txt", headRepo.Name), "new-commit") 198 testPullMerge(t, baseSession, baseRepo.OwnerName, baseRepo.Name, prID, repo_model.MergeStyleRebaseMerge, false) 199 200 // create merged PR with deleted branch 201 testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "branch/new-commit", "merged-pr-deleted", http.StatusSeeOther) 202 prID = testCreatePullToDefaultBranch(t, baseSession, baseRepo, headRepo, "merged-pr-deleted", "merged pr with deleted branch") 203 testAPINewFile(t, headSession, headRepo.OwnerName, headRepo.Name, "merged-pr-deleted", fmt.Sprintf("new-commit-%s-2.txt", headRepo.Name), "new-commit") 204 testPullMerge(t, baseSession, baseRepo.OwnerName, baseRepo.Name, prID, repo_model.MergeStyleRebaseMerge, true) 205 } 206 207 func checkRecentlyPushedNewBranches(t *testing.T, session *TestSession, repoPath string, expected []string) { 208 branches := make([]string, 0, 2) 209 req := NewRequest(t, "GET", repoPath) 210 resp := session.MakeRequest(t, req, http.StatusOK) 211 doc := NewHTMLParser(t, resp.Body) 212 doc.doc.Find(".ui.positive.message div a").Each(func(index int, branch *goquery.Selection) { 213 branches = append(branches, branch.Text()) 214 }) 215 assert.Equal(t, expected, branches) 216 } 217 218 func TestRecentlyPushedNewBranches(t *testing.T) { 219 defer tests.PrepareTestEnv(t)() 220 221 onGiteaRun(t, func(t *testing.T, u *url.URL) { 222 user1Session := loginUser(t, "user1") 223 user2Session := loginUser(t, "user2") 224 user12Session := loginUser(t, "user12") 225 user13Session := loginUser(t, "user13") 226 227 // prepare branch and PRs in original repo 228 repo10 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}) 229 prepareBranch(t, user12Session, repo10) 230 prepareRepoPR(t, user12Session, user12Session, repo10, repo10) 231 232 // outdated new branch should not be displayed 233 checkRecentlyPushedNewBranches(t, user12Session, "user12/repo10", []string{"new-commit"}) 234 235 // create a fork repo in public org 236 testRepoFork(t, user12Session, repo10.OwnerName, repo10.Name, "org25", "org25_fork_repo10", "new-commit") 237 orgPublicForkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: 25, Name: "org25_fork_repo10"}) 238 prepareRepoPR(t, user12Session, user12Session, repo10, orgPublicForkRepo) 239 240 // user12 is the owner of the repo10 and the organization org25 241 // in repo10, user12 has opening/closed/merged pr and closed/merged pr with deleted branch 242 checkRecentlyPushedNewBranches(t, user12Session, "user12/repo10", []string{"org25/org25_fork_repo10:new-commit", "new-commit"}) 243 244 userForkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 11}) 245 testCtx := NewAPITestContext(t, repo10.OwnerName, repo10.Name, auth_model.AccessTokenScopeWriteRepository) 246 t.Run("AddUser13AsCollaborator", doAPIAddCollaborator(testCtx, "user13", perm.AccessModeWrite)) 247 prepareBranch(t, user13Session, userForkRepo) 248 prepareRepoPR(t, user13Session, user13Session, repo10, userForkRepo) 249 250 // create branch with same name in different repo by user13 251 testCreateBranch(t, user13Session, repo10.OwnerName, repo10.Name, "branch/new-commit", "same-name-branch", http.StatusSeeOther) 252 testCreateBranch(t, user13Session, userForkRepo.OwnerName, userForkRepo.Name, "branch/new-commit", "same-name-branch", http.StatusSeeOther) 253 testCreatePullToDefaultBranch(t, user13Session, repo10, userForkRepo, "same-name-branch", "same name branch pr") 254 255 // user13 pushed 2 branches with the same name in repo10 and repo11 256 // and repo11's branch has a pr, but repo10's branch doesn't 257 // in this case, we should get repo10's branch but not repo11's branch 258 checkRecentlyPushedNewBranches(t, user13Session, "user12/repo10", []string{"same-name-branch", "user13/repo11:new-commit"}) 259 260 // create a fork repo in private org 261 testRepoFork(t, user1Session, repo10.OwnerName, repo10.Name, "private_org35", "org35_fork_repo10", "new-commit") 262 orgPrivateForkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: 35, Name: "org35_fork_repo10"}) 263 prepareRepoPR(t, user1Session, user1Session, repo10, orgPrivateForkRepo) 264 265 // user1 is the owner of private_org35 and no write permission to repo10 266 // so user1 can only see the branch in org35_fork_repo10 267 checkRecentlyPushedNewBranches(t, user1Session, "user12/repo10", []string{"private_org35/org35_fork_repo10:new-commit"}) 268 269 // user2 push a branch in private_org35 270 testCreateBranch(t, user2Session, orgPrivateForkRepo.OwnerName, orgPrivateForkRepo.Name, "branch/new-commit", "user-read-permission", http.StatusSeeOther) 271 // convert write permission to read permission for code unit 272 token := getTokenForLoggedInUser(t, user1Session, auth_model.AccessTokenScopeWriteOrganization) 273 req := NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/teams/%d", 24), &api.EditTeamOption{ 274 Name: "team24", 275 UnitsMap: map[string]string{"repo.code": "read"}, 276 }).AddTokenAuth(token) 277 MakeRequest(t, req, http.StatusOK) 278 teamUnit := unittest.AssertExistsAndLoadBean(t, &org_model.TeamUnit{TeamID: 24, Type: unit.TypeCode}) 279 assert.Equal(t, perm.AccessModeRead, teamUnit.AccessMode) 280 // user2 can see the branch as it is created by user2 281 checkRecentlyPushedNewBranches(t, user2Session, "user12/repo10", []string{"private_org35/org35_fork_repo10:user-read-permission"}) 282 }) 283 }