forked from gitea/gitea
Fix bug of branches API with tests (#25578)
Fix #25558 Extract from #22743 This PR added a repository's check when creating/deleting branches via API. Mirror repository and archive repository cannot do that.
This commit is contained in:
parent
1704c64a15
commit
de981c39e6
|
@ -0,0 +1,49 @@
|
||||||
|
-
|
||||||
|
id: 1
|
||||||
|
repo_id: 5
|
||||||
|
interval: 3600
|
||||||
|
enable_prune: false
|
||||||
|
updated_unix: 0
|
||||||
|
next_update_unix: 0
|
||||||
|
lfs_enabled: false
|
||||||
|
lfs_endpoint: ""
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 2
|
||||||
|
repo_id: 25
|
||||||
|
interval: 3600
|
||||||
|
enable_prune: false
|
||||||
|
updated_unix: 0
|
||||||
|
next_update_unix: 0
|
||||||
|
lfs_enabled: false
|
||||||
|
lfs_endpoint: ""
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 3
|
||||||
|
repo_id: 26
|
||||||
|
interval: 3600
|
||||||
|
enable_prune: false
|
||||||
|
updated_unix: 0
|
||||||
|
next_update_unix: 0
|
||||||
|
lfs_enabled: false
|
||||||
|
lfs_endpoint: ""
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 4
|
||||||
|
repo_id: 27
|
||||||
|
interval: 3600
|
||||||
|
enable_prune: false
|
||||||
|
updated_unix: 0
|
||||||
|
next_update_unix: 0
|
||||||
|
lfs_enabled: false
|
||||||
|
lfs_endpoint: ""
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 5
|
||||||
|
repo_id: 28
|
||||||
|
interval: 3600
|
||||||
|
enable_prune: false
|
||||||
|
updated_unix: 0
|
||||||
|
next_update_unix: 0
|
||||||
|
lfs_enabled: false
|
||||||
|
lfs_endpoint: ""
|
|
@ -141,7 +141,7 @@
|
||||||
num_projects: 0
|
num_projects: 0
|
||||||
num_closed_projects: 0
|
num_closed_projects: 0
|
||||||
is_private: true
|
is_private: true
|
||||||
is_empty: true
|
is_empty: false
|
||||||
is_archived: false
|
is_archived: false
|
||||||
is_mirror: true
|
is_mirror: true
|
||||||
status: 0
|
status: 0
|
||||||
|
|
|
@ -118,6 +118,21 @@ func DeleteBranch(ctx *context.APIContext) {
|
||||||
// "404":
|
// "404":
|
||||||
// "$ref": "#/responses/notFound"
|
// "$ref": "#/responses/notFound"
|
||||||
|
|
||||||
|
if ctx.Repo.Repository.IsEmpty {
|
||||||
|
ctx.Error(http.StatusNotFound, "", "Git Repository is empty.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.Repo.Repository.IsArchived {
|
||||||
|
ctx.Error(http.StatusForbidden, "", "Git Repository is archived.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.Repo.Repository.IsMirror {
|
||||||
|
ctx.Error(http.StatusForbidden, "", "Git Repository is a mirror.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
branchName := ctx.Params("*")
|
branchName := ctx.Params("*")
|
||||||
|
|
||||||
if ctx.Repo.Repository.IsEmpty {
|
if ctx.Repo.Repository.IsEmpty {
|
||||||
|
@ -195,17 +210,30 @@ func CreateBranch(ctx *context.APIContext) {
|
||||||
// responses:
|
// responses:
|
||||||
// "201":
|
// "201":
|
||||||
// "$ref": "#/responses/Branch"
|
// "$ref": "#/responses/Branch"
|
||||||
|
// "403":
|
||||||
|
// description: The branch is archived or a mirror.
|
||||||
// "404":
|
// "404":
|
||||||
// description: The old branch does not exist.
|
// description: The old branch does not exist.
|
||||||
// "409":
|
// "409":
|
||||||
// description: The branch with the same name already exists.
|
// description: The branch with the same name already exists.
|
||||||
|
|
||||||
opt := web.GetForm(ctx).(*api.CreateBranchRepoOption)
|
|
||||||
if ctx.Repo.Repository.IsEmpty {
|
if ctx.Repo.Repository.IsEmpty {
|
||||||
ctx.Error(http.StatusNotFound, "", "Git Repository is empty.")
|
ctx.Error(http.StatusNotFound, "", "Git Repository is empty.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.Repo.Repository.IsArchived {
|
||||||
|
ctx.Error(http.StatusForbidden, "", "Git Repository is archived.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.Repo.Repository.IsMirror {
|
||||||
|
ctx.Error(http.StatusForbidden, "", "Git Repository is a mirror.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
opt := web.GetForm(ctx).(*api.CreateBranchRepoOption)
|
||||||
|
|
||||||
var oldCommit *git.Commit
|
var oldCommit *git.Commit
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
@ -313,7 +341,12 @@ func ListBranches(ctx *context.APIContext) {
|
||||||
|
|
||||||
listOptions := utils.GetListOptions(ctx)
|
listOptions := utils.GetListOptions(ctx)
|
||||||
|
|
||||||
if !ctx.Repo.Repository.IsEmpty && ctx.Repo.GitRepo != nil {
|
if !ctx.Repo.Repository.IsEmpty {
|
||||||
|
if ctx.Repo.GitRepo == nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "Load git repository failed", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
branchOpts := git_model.FindBranchOptions{
|
branchOpts := git_model.FindBranchOptions{
|
||||||
ListOptions: listOptions,
|
ListOptions: listOptions,
|
||||||
RepoID: ctx.Repo.Repository.ID,
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
|
|
|
@ -3606,6 +3606,9 @@
|
||||||
"201": {
|
"201": {
|
||||||
"$ref": "#/responses/Branch"
|
"$ref": "#/responses/Branch"
|
||||||
},
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "The branch is archived or a mirror."
|
||||||
|
},
|
||||||
"404": {
|
"404": {
|
||||||
"description": "The old branch does not exist."
|
"description": "The old branch does not exist."
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
ref: refs/heads/master
|
|
@ -0,0 +1,6 @@
|
||||||
|
[core]
|
||||||
|
repositoryformatversion = 0
|
||||||
|
filemode = true
|
||||||
|
bare = true
|
||||||
|
ignorecase = true
|
||||||
|
precomposeunicode = true
|
|
@ -0,0 +1 @@
|
||||||
|
Unnamed repository; edit this file 'description' to name the repository.
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
ORI_DIR=`pwd`
|
||||||
|
SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)
|
||||||
|
cd "$ORI_DIR"
|
||||||
|
for i in `ls "$SHELL_FOLDER/post-receive.d"`; do
|
||||||
|
sh "$SHELL_FOLDER/post-receive.d/$i"
|
||||||
|
done
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" post-receive
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
ORI_DIR=`pwd`
|
||||||
|
SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)
|
||||||
|
cd "$ORI_DIR"
|
||||||
|
for i in `ls "$SHELL_FOLDER/pre-receive.d"`; do
|
||||||
|
sh "$SHELL_FOLDER/pre-receive.d/$i"
|
||||||
|
done
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" pre-receive
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
ORI_DIR=`pwd`
|
||||||
|
SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)
|
||||||
|
cd "$ORI_DIR"
|
||||||
|
for i in `ls "$SHELL_FOLDER/update.d"`; do
|
||||||
|
sh "$SHELL_FOLDER/update.d/$i" $1 $2 $3
|
||||||
|
done
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" update $1 $2 $3
|
|
@ -0,0 +1,6 @@
|
||||||
|
# git ls-files --others --exclude-from=.git/info/exclude
|
||||||
|
# Lines that start with '#' are comments.
|
||||||
|
# For a project mostly in C, the following would be a good set of
|
||||||
|
# exclude patterns (uncomment them if you want to use them):
|
||||||
|
# *.[oa]
|
||||||
|
# *~
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
2a47ca4b614a9f5a43abbd5ad851a54a616ffee6
|
|
@ -0,0 +1 @@
|
||||||
|
d22b4d4daa5be07329fcef6ed458f00cf3392da0
|
|
@ -0,0 +1,126 @@
|
||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/json"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/tests"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAPIRepoBranchesPlain(t *testing.T) {
|
||||||
|
onGiteaRun(t, func(*testing.T, *url.URL) {
|
||||||
|
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
|
||||||
|
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||||
|
session := loginUser(t, user1.LowerName)
|
||||||
|
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
|
||||||
|
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/user3/%s/branches", repo3.Name)) // a plain repo
|
||||||
|
link.RawQuery = url.Values{"token": {token}}.Encode()
|
||||||
|
resp := MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
|
||||||
|
bs, err := io.ReadAll(resp.Body)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
var branches []*api.Branch
|
||||||
|
assert.NoError(t, json.Unmarshal(bs, &branches))
|
||||||
|
assert.Len(t, branches, 2)
|
||||||
|
assert.EqualValues(t, "test_branch", branches[0].Name)
|
||||||
|
assert.EqualValues(t, "master", branches[1].Name)
|
||||||
|
|
||||||
|
link2, _ := url.Parse(fmt.Sprintf("/api/v1/repos/user3/%s/branches/test_branch", repo3.Name))
|
||||||
|
link2.RawQuery = url.Values{"token": {token}}.Encode()
|
||||||
|
resp = MakeRequest(t, NewRequest(t, "GET", link2.String()), http.StatusOK)
|
||||||
|
bs, err = io.ReadAll(resp.Body)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
var branch api.Branch
|
||||||
|
assert.NoError(t, json.Unmarshal(bs, &branch))
|
||||||
|
assert.EqualValues(t, "test_branch", branch.Name)
|
||||||
|
|
||||||
|
req := NewRequest(t, "POST", link.String())
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
req.Body = io.NopCloser(bytes.NewBufferString(`{"new_branch_name":"test_branch2", "old_branch_name": "test_branch", "old_ref_name":"refs/heads/test_branch"}`))
|
||||||
|
resp = MakeRequest(t, req, http.StatusCreated)
|
||||||
|
bs, err = io.ReadAll(resp.Body)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
var branch2 api.Branch
|
||||||
|
assert.NoError(t, json.Unmarshal(bs, &branch2))
|
||||||
|
assert.EqualValues(t, "test_branch2", branch2.Name)
|
||||||
|
assert.EqualValues(t, branch.Commit.ID, branch2.Commit.ID)
|
||||||
|
|
||||||
|
resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
|
||||||
|
bs, err = io.ReadAll(resp.Body)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
branches = []*api.Branch{}
|
||||||
|
assert.NoError(t, json.Unmarshal(bs, &branches))
|
||||||
|
assert.Len(t, branches, 3)
|
||||||
|
assert.EqualValues(t, "test_branch", branches[0].Name)
|
||||||
|
assert.EqualValues(t, "test_branch2", branches[1].Name)
|
||||||
|
assert.EqualValues(t, "master", branches[2].Name)
|
||||||
|
|
||||||
|
link3, _ := url.Parse(fmt.Sprintf("/api/v1/repos/user3/%s/branches/test_branch2", repo3.Name))
|
||||||
|
MakeRequest(t, NewRequest(t, "DELETE", link3.String()), http.StatusNotFound)
|
||||||
|
|
||||||
|
link3.RawQuery = url.Values{"token": {token}}.Encode()
|
||||||
|
MakeRequest(t, NewRequest(t, "DELETE", link3.String()), http.StatusNoContent)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAPIRepoBranchesMirror(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
repo5 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 5})
|
||||||
|
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||||
|
session := loginUser(t, user1.LowerName)
|
||||||
|
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
|
||||||
|
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/user3/%s/branches", repo5.Name)) // a mirror repo
|
||||||
|
link.RawQuery = url.Values{"token": {token}}.Encode()
|
||||||
|
resp := MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
|
||||||
|
bs, err := io.ReadAll(resp.Body)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
var branches []*api.Branch
|
||||||
|
assert.NoError(t, json.Unmarshal(bs, &branches))
|
||||||
|
assert.Len(t, branches, 2)
|
||||||
|
assert.EqualValues(t, "test_branch", branches[0].Name)
|
||||||
|
assert.EqualValues(t, "master", branches[1].Name)
|
||||||
|
|
||||||
|
link2, _ := url.Parse(fmt.Sprintf("/api/v1/repos/user3/%s/branches/test_branch", repo5.Name))
|
||||||
|
link2.RawQuery = url.Values{"token": {token}}.Encode()
|
||||||
|
resp = MakeRequest(t, NewRequest(t, "GET", link2.String()), http.StatusOK)
|
||||||
|
bs, err = io.ReadAll(resp.Body)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
var branch api.Branch
|
||||||
|
assert.NoError(t, json.Unmarshal(bs, &branch))
|
||||||
|
assert.EqualValues(t, "test_branch", branch.Name)
|
||||||
|
|
||||||
|
req := NewRequest(t, "POST", link.String())
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
req.Body = io.NopCloser(bytes.NewBufferString(`{"new_branch_name":"test_branch2", "old_branch_name": "test_branch", "old_ref_name":"refs/heads/test_branch"}`))
|
||||||
|
resp = MakeRequest(t, req, http.StatusForbidden)
|
||||||
|
bs, err = io.ReadAll(resp.Body)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, "{\"message\":\"Git Repository is a mirror.\",\"url\":\""+setting.AppURL+"api/swagger\"}\n", string(bs))
|
||||||
|
|
||||||
|
resp = MakeRequest(t, NewRequest(t, "DELETE", link2.String()), http.StatusForbidden)
|
||||||
|
bs, err = io.ReadAll(resp.Body)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, "{\"message\":\"Git Repository is a mirror.\",\"url\":\""+setting.AppURL+"api/swagger\"}\n", string(bs))
|
||||||
|
}
|
|
@ -34,7 +34,7 @@ func TestEmptyRepo(t *testing.T) {
|
||||||
"commit/1ae57b34ccf7e18373",
|
"commit/1ae57b34ccf7e18373",
|
||||||
"graph",
|
"graph",
|
||||||
}
|
}
|
||||||
emptyRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 5})
|
emptyRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 6})
|
||||||
assert.True(t, emptyRepo.IsEmpty)
|
assert.True(t, emptyRepo.IsEmpty)
|
||||||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: emptyRepo.OwnerID})
|
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: emptyRepo.OwnerID})
|
||||||
for _, subPath := range subPaths {
|
for _, subPath := range subPaths {
|
||||||
|
|
Loading…
Reference in New Issue