2017-10-15 22:59:24 +03:00
// Copyright 2017 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2017-10-15 22:59:24 +03:00
2022-09-02 22:18:23 +03:00
package integration
2017-10-15 22:59:24 +03:00
import (
2024-05-21 20:00:35 +03:00
"fmt"
2017-10-15 22:59:24 +03:00
"net/http"
2019-05-11 18:29:17 +03:00
"net/url"
2017-10-15 22:59:24 +03:00
"path"
"strings"
"testing"
2024-05-21 20:00:35 +03:00
auth_model "code.gitea.io/gitea/models/auth"
org_model "code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest"
api "code.gitea.io/gitea/modules/structs"
2017-12-16 00:11:02 +03:00
"code.gitea.io/gitea/modules/test"
2022-06-26 17:19:22 +03:00
"code.gitea.io/gitea/modules/translation"
2022-09-02 22:18:23 +03:00
"code.gitea.io/gitea/tests"
2017-12-16 00:11:02 +03:00
2024-05-21 20:00:35 +03:00
"github.com/PuerkitoBio/goquery"
2017-10-15 22:59:24 +03:00
"github.com/stretchr/testify/assert"
)
2017-12-24 03:33:34 +03:00
func testCreateBranch ( t testing . TB , session * TestSession , user , repo , oldRefSubURL , newBranchName string , expectedStatus int ) string {
2024-10-10 06:48:21 +03:00
csrf := GetUserCSRFToken ( t , session )
2017-10-30 05:04:25 +03:00
req := NewRequestWithValues ( t , "POST" , path . Join ( user , repo , "branches/_new" , oldRefSubURL ) , map [ string ] string {
2017-10-15 22:59:24 +03:00
"_csrf" : csrf ,
"new_branch_name" : newBranchName ,
} )
resp := session . MakeRequest ( t , req , expectedStatus )
2022-03-23 07:54:07 +03:00
if expectedStatus != http . StatusSeeOther {
2017-10-15 22:59:24 +03:00
return ""
}
2017-12-16 00:11:02 +03:00
return test . RedirectURL ( resp )
2017-10-15 22:59:24 +03:00
}
func TestCreateBranch ( t * testing . T ) {
2019-05-11 18:29:17 +03:00
onGiteaRun ( t , testCreateBranches )
}
func testCreateBranches ( t * testing . T , giteaURL * url . URL ) {
2017-10-15 22:59:24 +03:00
tests := [ ] struct {
2017-10-30 05:04:25 +03:00
OldRefSubURL string
NewBranch string
CreateRelease string
FlashMessage string
ExpectedStatus int
2017-10-15 22:59:24 +03:00
} {
{
2017-10-30 05:04:25 +03:00
OldRefSubURL : "branch/master" ,
NewBranch : "feature/test1" ,
2022-03-23 07:54:07 +03:00
ExpectedStatus : http . StatusSeeOther ,
2024-02-15 00:48:45 +03:00
FlashMessage : translation . NewLocale ( "en-US" ) . TrString ( "repo.branch.create_success" , "feature/test1" ) ,
2017-10-15 22:59:24 +03:00
} ,
{
2017-10-30 05:04:25 +03:00
OldRefSubURL : "branch/master" ,
NewBranch : "" ,
2022-03-23 07:54:07 +03:00
ExpectedStatus : http . StatusSeeOther ,
2024-02-15 00:48:45 +03:00
FlashMessage : translation . NewLocale ( "en-US" ) . TrString ( "form.NewBranchName" ) + translation . NewLocale ( "en-US" ) . TrString ( "form.require_error" ) ,
2017-10-15 22:59:24 +03:00
} ,
{
2017-10-30 05:04:25 +03:00
OldRefSubURL : "branch/master" ,
NewBranch : "feature=test1" ,
2022-03-23 07:54:07 +03:00
ExpectedStatus : http . StatusSeeOther ,
2024-02-15 00:48:45 +03:00
FlashMessage : translation . NewLocale ( "en-US" ) . TrString ( "repo.branch.create_success" , "feature=test1" ) ,
2017-10-15 22:59:24 +03:00
} ,
{
2017-10-30 05:04:25 +03:00
OldRefSubURL : "branch/master" ,
NewBranch : strings . Repeat ( "b" , 101 ) ,
2022-03-23 07:54:07 +03:00
ExpectedStatus : http . StatusSeeOther ,
2024-02-15 00:48:45 +03:00
FlashMessage : translation . NewLocale ( "en-US" ) . TrString ( "form.NewBranchName" ) + translation . NewLocale ( "en-US" ) . TrString ( "form.max_size_error" , "100" ) ,
2017-10-15 22:59:24 +03:00
} ,
{
2017-10-30 05:04:25 +03:00
OldRefSubURL : "branch/master" ,
NewBranch : "master" ,
2022-03-23 07:54:07 +03:00
ExpectedStatus : http . StatusSeeOther ,
2024-02-15 00:48:45 +03:00
FlashMessage : translation . NewLocale ( "en-US" ) . TrString ( "repo.branch.branch_already_exists" , "master" ) ,
2017-10-15 22:59:24 +03:00
} ,
{
2017-10-30 05:04:25 +03:00
OldRefSubURL : "branch/master" ,
NewBranch : "master/test" ,
2022-03-23 07:54:07 +03:00
ExpectedStatus : http . StatusSeeOther ,
2024-02-15 00:48:45 +03:00
FlashMessage : translation . NewLocale ( "en-US" ) . TrString ( "repo.branch.branch_name_conflict" , "master/test" , "master" ) ,
2017-10-15 22:59:24 +03:00
} ,
{
2017-10-30 05:04:25 +03:00
OldRefSubURL : "commit/acd1d892867872cb47f3993468605b8aa59aa2e0" ,
NewBranch : "feature/test2" ,
ExpectedStatus : http . StatusNotFound ,
2017-10-15 22:59:24 +03:00
} ,
{
2017-10-30 05:04:25 +03:00
OldRefSubURL : "commit/65f1bf27bc3bf70f64657658635e66094edbcb4d" ,
NewBranch : "feature/test3" ,
2022-03-23 07:54:07 +03:00
ExpectedStatus : http . StatusSeeOther ,
2024-02-15 00:48:45 +03:00
FlashMessage : translation . NewLocale ( "en-US" ) . TrString ( "repo.branch.create_success" , "feature/test3" ) ,
2017-10-15 22:59:24 +03:00
} ,
{
2017-10-30 05:04:25 +03:00
OldRefSubURL : "branch/master" ,
NewBranch : "v1.0.0" ,
CreateRelease : "v1.0.0" ,
2022-03-23 07:54:07 +03:00
ExpectedStatus : http . StatusSeeOther ,
2024-02-15 00:48:45 +03:00
FlashMessage : translation . NewLocale ( "en-US" ) . TrString ( "repo.branch.tag_collision" , "v1.0.0" ) ,
2017-10-15 22:59:24 +03:00
} ,
{
2017-10-30 05:04:25 +03:00
OldRefSubURL : "tag/v1.0.0" ,
NewBranch : "feature/test4" ,
2020-11-09 00:24:54 +03:00
CreateRelease : "v1.0.1" ,
2022-03-23 07:54:07 +03:00
ExpectedStatus : http . StatusSeeOther ,
2024-02-15 00:48:45 +03:00
FlashMessage : translation . NewLocale ( "en-US" ) . TrString ( "repo.branch.create_success" , "feature/test4" ) ,
2017-10-15 22:59:24 +03:00
} ,
}
for _ , test := range tests {
session := loginUser ( t , "user2" )
if test . CreateRelease != "" {
createNewRelease ( t , session , "/user2/repo1" , test . CreateRelease , test . CreateRelease , false , false )
}
2017-10-30 05:04:25 +03:00
redirectURL := testCreateBranch ( t , session , "user2" , "repo1" , test . OldRefSubURL , test . NewBranch , test . ExpectedStatus )
2022-03-23 07:54:07 +03:00
if test . ExpectedStatus == http . StatusSeeOther {
2017-10-15 22:59:24 +03:00
req := NewRequest ( t , "GET" , redirectURL )
resp := session . MakeRequest ( t , req , http . StatusOK )
htmlDoc := NewHTMLParser ( t , resp . Body )
2023-07-08 06:19:00 +03:00
assert . Contains ( t ,
2017-10-15 22:59:24 +03:00
strings . TrimSpace ( htmlDoc . doc . Find ( ".ui.message" ) . Text ( ) ) ,
2023-07-08 06:19:00 +03:00
test . FlashMessage ,
2017-10-15 22:59:24 +03:00
)
}
}
}
func TestCreateBranchInvalidCSRF ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2017-10-15 22:59:24 +03:00
session := loginUser ( t , "user2" )
2017-10-30 05:04:25 +03:00
req := NewRequestWithValues ( t , "POST" , "user2/repo1/branches/_new/branch/master" , map [ string ] string {
2017-10-15 22:59:24 +03:00
"_csrf" : "fake_csrf" ,
"new_branch_name" : "test" ,
} )
2024-09-18 10:17:25 +03:00
resp := session . MakeRequest ( t , req , http . StatusBadRequest )
assert . Contains ( t , resp . Body . String ( ) , "Invalid CSRF token" )
2017-10-15 22:59:24 +03:00
}
2024-05-21 20:00:35 +03:00
func prepareBranch ( t * testing . T , session * TestSession , repo * repo_model . Repository ) {
baseRefSubURL := fmt . Sprintf ( "branch/%s" , repo . DefaultBranch )
// create branch with no new commit
testCreateBranch ( t , session , repo . OwnerName , repo . Name , baseRefSubURL , "no-commit" , http . StatusSeeOther )
// create branch with commit
testCreateBranch ( t , session , repo . OwnerName , repo . Name , baseRefSubURL , "new-commit" , http . StatusSeeOther )
testAPINewFile ( t , session , repo . OwnerName , repo . Name , "new-commit" , "new-commit.txt" , "new-commit" )
// create deleted branch
testCreateBranch ( t , session , repo . OwnerName , repo . Name , "branch/new-commit" , "deleted-branch" , http . StatusSeeOther )
testUIDeleteBranch ( t , session , repo . OwnerName , repo . Name , "deleted-branch" )
}
func testCreatePullToDefaultBranch ( t * testing . T , session * TestSession , baseRepo , headRepo * repo_model . Repository , headBranch , title string ) string {
srcRef := headBranch
if baseRepo . ID != headRepo . ID {
srcRef = fmt . Sprintf ( "%s/%s:%s" , headRepo . OwnerName , headRepo . Name , headBranch )
}
resp := testPullCreate ( t , session , baseRepo . OwnerName , baseRepo . Name , false , baseRepo . DefaultBranch , srcRef , title )
elem := strings . Split ( test . RedirectURL ( resp ) , "/" )
// return pull request ID
return elem [ 4 ]
}
func prepareRepoPR ( t * testing . T , baseSession , headSession * TestSession , baseRepo , headRepo * repo_model . Repository ) {
// create opening PR
testCreateBranch ( t , headSession , headRepo . OwnerName , headRepo . Name , "branch/new-commit" , "opening-pr" , http . StatusSeeOther )
testCreatePullToDefaultBranch ( t , baseSession , baseRepo , headRepo , "opening-pr" , "opening pr" )
// create closed PR
testCreateBranch ( t , headSession , headRepo . OwnerName , headRepo . Name , "branch/new-commit" , "closed-pr" , http . StatusSeeOther )
prID := testCreatePullToDefaultBranch ( t , baseSession , baseRepo , headRepo , "closed-pr" , "closed pr" )
testIssueClose ( t , baseSession , baseRepo . OwnerName , baseRepo . Name , prID )
// create closed PR with deleted branch
testCreateBranch ( t , headSession , headRepo . OwnerName , headRepo . Name , "branch/new-commit" , "closed-pr-deleted" , http . StatusSeeOther )
prID = testCreatePullToDefaultBranch ( t , baseSession , baseRepo , headRepo , "closed-pr-deleted" , "closed pr with deleted branch" )
testIssueClose ( t , baseSession , baseRepo . OwnerName , baseRepo . Name , prID )
testUIDeleteBranch ( t , headSession , headRepo . OwnerName , headRepo . Name , "closed-pr-deleted" )
// create merged PR
testCreateBranch ( t , headSession , headRepo . OwnerName , headRepo . Name , "branch/new-commit" , "merged-pr" , http . StatusSeeOther )
prID = testCreatePullToDefaultBranch ( t , baseSession , baseRepo , headRepo , "merged-pr" , "merged pr" )
testAPINewFile ( t , headSession , headRepo . OwnerName , headRepo . Name , "merged-pr" , fmt . Sprintf ( "new-commit-%s.txt" , headRepo . Name ) , "new-commit" )
testPullMerge ( t , baseSession , baseRepo . OwnerName , baseRepo . Name , prID , repo_model . MergeStyleRebaseMerge , false )
// create merged PR with deleted branch
testCreateBranch ( t , headSession , headRepo . OwnerName , headRepo . Name , "branch/new-commit" , "merged-pr-deleted" , http . StatusSeeOther )
prID = testCreatePullToDefaultBranch ( t , baseSession , baseRepo , headRepo , "merged-pr-deleted" , "merged pr with deleted branch" )
testAPINewFile ( t , headSession , headRepo . OwnerName , headRepo . Name , "merged-pr-deleted" , fmt . Sprintf ( "new-commit-%s-2.txt" , headRepo . Name ) , "new-commit" )
testPullMerge ( t , baseSession , baseRepo . OwnerName , baseRepo . Name , prID , repo_model . MergeStyleRebaseMerge , true )
}
func checkRecentlyPushedNewBranches ( t * testing . T , session * TestSession , repoPath string , expected [ ] string ) {
branches := make ( [ ] string , 0 , 2 )
req := NewRequest ( t , "GET" , repoPath )
resp := session . MakeRequest ( t , req , http . StatusOK )
doc := NewHTMLParser ( t , resp . Body )
doc . doc . Find ( ".ui.positive.message div a" ) . Each ( func ( index int , branch * goquery . Selection ) {
branches = append ( branches , branch . Text ( ) )
} )
assert . Equal ( t , expected , branches )
}
func TestRecentlyPushedNewBranches ( t * testing . T ) {
onGiteaRun ( t , func ( t * testing . T , u * url . URL ) {
user1Session := loginUser ( t , "user1" )
user2Session := loginUser ( t , "user2" )
user12Session := loginUser ( t , "user12" )
user13Session := loginUser ( t , "user13" )
// prepare branch and PRs in original repo
repo10 := unittest . AssertExistsAndLoadBean ( t , & repo_model . Repository { ID : 10 } )
prepareBranch ( t , user12Session , repo10 )
prepareRepoPR ( t , user12Session , user12Session , repo10 , repo10 )
// outdated new branch should not be displayed
checkRecentlyPushedNewBranches ( t , user12Session , "user12/repo10" , [ ] string { "new-commit" } )
// create a fork repo in public org
testRepoFork ( t , user12Session , repo10 . OwnerName , repo10 . Name , "org25" , "org25_fork_repo10" , "new-commit" )
orgPublicForkRepo := unittest . AssertExistsAndLoadBean ( t , & repo_model . Repository { OwnerID : 25 , Name : "org25_fork_repo10" } )
prepareRepoPR ( t , user12Session , user12Session , repo10 , orgPublicForkRepo )
// user12 is the owner of the repo10 and the organization org25
// in repo10, user12 has opening/closed/merged pr and closed/merged pr with deleted branch
checkRecentlyPushedNewBranches ( t , user12Session , "user12/repo10" , [ ] string { "org25/org25_fork_repo10:new-commit" , "new-commit" } )
userForkRepo := unittest . AssertExistsAndLoadBean ( t , & repo_model . Repository { ID : 11 } )
testCtx := NewAPITestContext ( t , repo10 . OwnerName , repo10 . Name , auth_model . AccessTokenScopeWriteRepository )
t . Run ( "AddUser13AsCollaborator" , doAPIAddCollaborator ( testCtx , "user13" , perm . AccessModeWrite ) )
prepareBranch ( t , user13Session , userForkRepo )
prepareRepoPR ( t , user13Session , user13Session , repo10 , userForkRepo )
// create branch with same name in different repo by user13
testCreateBranch ( t , user13Session , repo10 . OwnerName , repo10 . Name , "branch/new-commit" , "same-name-branch" , http . StatusSeeOther )
testCreateBranch ( t , user13Session , userForkRepo . OwnerName , userForkRepo . Name , "branch/new-commit" , "same-name-branch" , http . StatusSeeOther )
testCreatePullToDefaultBranch ( t , user13Session , repo10 , userForkRepo , "same-name-branch" , "same name branch pr" )
// user13 pushed 2 branches with the same name in repo10 and repo11
// and repo11's branch has a pr, but repo10's branch doesn't
// in this case, we should get repo10's branch but not repo11's branch
checkRecentlyPushedNewBranches ( t , user13Session , "user12/repo10" , [ ] string { "same-name-branch" , "user13/repo11:new-commit" } )
// create a fork repo in private org
testRepoFork ( t , user1Session , repo10 . OwnerName , repo10 . Name , "private_org35" , "org35_fork_repo10" , "new-commit" )
orgPrivateForkRepo := unittest . AssertExistsAndLoadBean ( t , & repo_model . Repository { OwnerID : 35 , Name : "org35_fork_repo10" } )
prepareRepoPR ( t , user1Session , user1Session , repo10 , orgPrivateForkRepo )
// user1 is the owner of private_org35 and no write permission to repo10
// so user1 can only see the branch in org35_fork_repo10
checkRecentlyPushedNewBranches ( t , user1Session , "user12/repo10" , [ ] string { "private_org35/org35_fork_repo10:new-commit" } )
// user2 push a branch in private_org35
testCreateBranch ( t , user2Session , orgPrivateForkRepo . OwnerName , orgPrivateForkRepo . Name , "branch/new-commit" , "user-read-permission" , http . StatusSeeOther )
// convert write permission to read permission for code unit
token := getTokenForLoggedInUser ( t , user1Session , auth_model . AccessTokenScopeWriteOrganization )
req := NewRequestWithJSON ( t , "PATCH" , fmt . Sprintf ( "/api/v1/teams/%d" , 24 ) , & api . EditTeamOption {
Name : "team24" ,
UnitsMap : map [ string ] string { "repo.code" : "read" } ,
} ) . AddTokenAuth ( token )
MakeRequest ( t , req , http . StatusOK )
teamUnit := unittest . AssertExistsAndLoadBean ( t , & org_model . TeamUnit { TeamID : 24 , Type : unit . TypeCode } )
assert . Equal ( t , perm . AccessModeRead , teamUnit . AccessMode )
// user2 can see the branch as it is created by user2
checkRecentlyPushedNewBranches ( t , user2Session , "user12/repo10" , [ ] string { "private_org35/org35_fork_repo10:user-read-permission" } )
} )
}