2017-06-07 02:27:49 +00:00
|
|
|
// Copyright 2017 The Gitea Authors. All rights reserved.
|
2024-02-25 10:58:23 +00:00
|
|
|
// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved.
|
2022-11-27 18:20:29 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2017-06-07 02:27:49 +00:00
|
|
|
|
2022-09-02 19:18:23 +00:00
|
|
|
package integration
|
2017-06-07 02:27:49 +00:00
|
|
|
|
|
|
|
import (
|
2017-10-15 15:06:07 +00:00
|
|
|
"fmt"
|
2017-06-07 02:27:49 +00:00
|
|
|
"net/http"
|
2017-12-03 22:46:01 +00:00
|
|
|
"net/http/httptest"
|
2024-02-09 14:57:08 +00:00
|
|
|
"net/url"
|
2024-02-27 18:08:16 +00:00
|
|
|
"strings"
|
2017-06-07 02:27:49 +00:00
|
|
|
"testing"
|
|
|
|
|
2024-02-09 14:57:08 +00:00
|
|
|
"code.gitea.io/gitea/models/db"
|
|
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
2021-11-16 08:53:21 +00:00
|
|
|
"code.gitea.io/gitea/models/unittest"
|
2021-11-24 09:49:20 +00:00
|
|
|
user_model "code.gitea.io/gitea/models/user"
|
2024-02-25 10:58:23 +00:00
|
|
|
"code.gitea.io/gitea/modules/setting"
|
2024-11-02 19:55:30 +00:00
|
|
|
"code.gitea.io/gitea/modules/structs"
|
2024-02-25 10:58:23 +00:00
|
|
|
"code.gitea.io/gitea/modules/test"
|
|
|
|
"code.gitea.io/gitea/routers"
|
2024-02-09 14:57:08 +00:00
|
|
|
repo_service "code.gitea.io/gitea/services/repository"
|
2022-09-02 19:18:23 +00:00
|
|
|
"code.gitea.io/gitea/tests"
|
2017-10-15 15:06:07 +00:00
|
|
|
|
2017-06-07 02:27:49 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
2024-07-30 19:41:10 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2017-06-07 02:27:49 +00:00
|
|
|
)
|
|
|
|
|
2017-12-03 22:46:01 +00:00
|
|
|
func testRepoFork(t *testing.T, session *TestSession, ownerName, repoName, forkOwnerName, forkRepoName string) *httptest.ResponseRecorder {
|
2024-02-09 14:57:08 +00:00
|
|
|
t.Helper()
|
|
|
|
|
2022-08-16 02:22:25 +00:00
|
|
|
forkOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: forkOwnerName})
|
2017-10-15 15:06:07 +00:00
|
|
|
|
2017-06-07 02:27:49 +00:00
|
|
|
// Step0: check the existence of the to-fork repo
|
2017-10-15 15:06:07 +00:00
|
|
|
req := NewRequestf(t, "GET", "/%s/%s", forkOwnerName, forkRepoName)
|
2021-11-18 01:33:06 +00:00
|
|
|
session.MakeRequest(t, req, http.StatusNotFound)
|
2017-06-07 02:27:49 +00:00
|
|
|
|
2024-02-09 14:57:08 +00:00
|
|
|
// Step1: visit the /fork page
|
|
|
|
forkURL := fmt.Sprintf("/%s/%s/fork", ownerName, repoName)
|
|
|
|
req = NewRequest(t, "GET", forkURL)
|
2021-11-18 01:33:06 +00:00
|
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
2017-06-07 02:27:49 +00:00
|
|
|
|
2024-02-09 14:57:08 +00:00
|
|
|
// Step2: fill the form of the forking
|
2017-06-17 16:29:59 +00:00
|
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
2024-02-09 14:57:08 +00:00
|
|
|
link, exists := htmlDoc.doc.Find(fmt.Sprintf("form.ui.form[action=\"%s\"]", forkURL)).Attr("action")
|
2017-06-07 02:27:49 +00:00
|
|
|
assert.True(t, exists, "The template has changed")
|
2017-10-15 15:06:07 +00:00
|
|
|
_, exists = htmlDoc.doc.Find(fmt.Sprintf(".owner.dropdown .item[data-value=\"%d\"]", forkOwner.ID)).Attr("data-value")
|
2024-08-14 09:43:42 +00:00
|
|
|
assert.True(t, exists, "Fork owner %q is not present in select box", forkOwnerName)
|
2017-06-17 04:49:45 +00:00
|
|
|
req = NewRequestWithValues(t, "POST", link, map[string]string{
|
|
|
|
"_csrf": htmlDoc.GetCSRF(),
|
2017-10-15 15:06:07 +00:00
|
|
|
"uid": fmt.Sprintf("%d", forkOwner.ID),
|
|
|
|
"repo_name": forkRepoName,
|
2017-06-17 04:49:45 +00:00
|
|
|
})
|
2022-06-20 10:02:49 +00:00
|
|
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
2017-06-07 02:27:49 +00:00
|
|
|
|
2024-02-09 14:57:08 +00:00
|
|
|
// Step3: check the existence of the forked repo
|
2017-10-15 15:06:07 +00:00
|
|
|
req = NewRequestf(t, "GET", "/%s/%s", forkOwnerName, forkRepoName)
|
2017-07-07 19:36:47 +00:00
|
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
2017-06-15 11:20:39 +00:00
|
|
|
|
|
|
|
return resp
|
2017-06-07 02:27:49 +00:00
|
|
|
}
|
|
|
|
|
2024-02-09 14:57:08 +00:00
|
|
|
func testRepoForkLegacyRedirect(t *testing.T, session *TestSession, ownerName, repoName string) {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: ownerName})
|
|
|
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: owner.ID, Name: repoName})
|
|
|
|
|
|
|
|
// Visit the /repo/fork/:id url
|
|
|
|
req := NewRequestf(t, "GET", "/repo/fork/%d", repo.ID)
|
|
|
|
resp := session.MakeRequest(t, req, http.StatusMovedPermanently)
|
|
|
|
|
|
|
|
assert.Equal(t, repo.Link()+"/fork", resp.Header().Get("Location"))
|
|
|
|
}
|
|
|
|
|
2017-06-07 02:27:49 +00:00
|
|
|
func TestRepoFork(t *testing.T) {
|
2024-02-09 14:57:08 +00:00
|
|
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
|
|
|
user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user5"})
|
|
|
|
session := loginUser(t, user5.Name)
|
|
|
|
|
|
|
|
t.Run("by name", func(t *testing.T) {
|
|
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
defer func() {
|
|
|
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user5.ID, Name: "repo1"})
|
|
|
|
repo_service.DeleteRepository(db.DefaultContext, user5, repo, false)
|
|
|
|
}()
|
|
|
|
testRepoFork(t, session, "user2", "repo1", "user5", "repo1")
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("legacy redirect", func(t *testing.T) {
|
|
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
testRepoForkLegacyRedirect(t, session, "user2", "repo1")
|
|
|
|
|
|
|
|
t.Run("private 404", func(t *testing.T) {
|
|
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
|
|
|
|
// Make sure the repo we try to fork is private
|
|
|
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 31, IsPrivate: true})
|
|
|
|
|
|
|
|
// user5 does not have access to user2/repo20
|
|
|
|
req := NewRequestf(t, "GET", "/repo/fork/%d", repo.ID) // user2/repo20
|
|
|
|
session.MakeRequest(t, req, http.StatusNotFound)
|
|
|
|
})
|
|
|
|
t.Run("authenticated private redirect", func(t *testing.T) {
|
|
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
|
|
|
|
// Make sure the repo we try to fork is private
|
|
|
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 31, IsPrivate: true})
|
|
|
|
|
|
|
|
// user1 has access to user2/repo20
|
|
|
|
session := loginUser(t, "user1")
|
|
|
|
req := NewRequestf(t, "GET", "/repo/fork/%d", repo.ID) // user2/repo20
|
|
|
|
session.MakeRequest(t, req, http.StatusMovedPermanently)
|
|
|
|
})
|
|
|
|
t.Run("no code unit", func(t *testing.T) {
|
|
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
|
|
|
|
// Make sure the repo we try to fork is private.
|
2024-05-09 13:49:37 +00:00
|
|
|
// We're also choosing user15/big_test_private_2, because it has the Code unit disabled.
|
2024-02-09 14:57:08 +00:00
|
|
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 20, IsPrivate: true})
|
|
|
|
|
|
|
|
// user1, even though an admin, can't fork a repo without a code unit.
|
|
|
|
session := loginUser(t, "user1")
|
|
|
|
req := NewRequestf(t, "GET", "/repo/fork/%d", repo.ID) // user15/big_test_private_2
|
|
|
|
session.MakeRequest(t, req, http.StatusNotFound)
|
|
|
|
})
|
|
|
|
})
|
2024-02-25 10:58:23 +00:00
|
|
|
|
2024-02-27 18:08:16 +00:00
|
|
|
t.Run("fork button", func(t *testing.T) {
|
|
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
|
|
|
|
req := NewRequest(t, "GET", "/user2/repo1/issues")
|
|
|
|
resp := MakeRequest(t, req, http.StatusOK)
|
|
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
|
|
|
|
|
|
forkButton := htmlDoc.Find("a[href*='/forks']")
|
|
|
|
assert.EqualValues(t, 1, forkButton.Length())
|
|
|
|
|
|
|
|
href, _ := forkButton.Attr("href")
|
|
|
|
assert.Equal(t, "/user2/repo1/forks", href)
|
|
|
|
assert.Equal(t, "0", strings.TrimSpace(forkButton.Text()))
|
|
|
|
|
|
|
|
t.Run("no fork button on empty repo", func(t *testing.T) {
|
|
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
|
|
|
|
// Create an empty repository
|
|
|
|
repo, err := repo_service.CreateRepository(db.DefaultContext, user5, user5, repo_service.CreateRepoOptions{
|
|
|
|
Name: "empty-repo",
|
|
|
|
AutoInit: false,
|
|
|
|
})
|
|
|
|
defer func() {
|
|
|
|
repo_service.DeleteRepository(db.DefaultContext, user5, repo, false)
|
|
|
|
}()
|
2024-07-30 19:41:10 +00:00
|
|
|
require.NoError(t, err)
|
2024-02-27 18:08:16 +00:00
|
|
|
assert.NotEmpty(t, repo)
|
|
|
|
|
|
|
|
// Load the repository home view
|
|
|
|
req := NewRequest(t, "GET", repo.HTMLURL())
|
|
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
|
|
|
|
|
|
// On an empty repo, the fork button is not present
|
|
|
|
htmlDoc.AssertElement(t, ".basic.button[href*='/fork']", false)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2024-02-25 10:58:23 +00:00
|
|
|
t.Run("DISABLE_FORKS", func(t *testing.T) {
|
|
|
|
defer test.MockVariableValue(&setting.Repository.DisableForks, true)()
|
|
|
|
defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())()
|
|
|
|
|
|
|
|
t.Run("fork button not present", func(t *testing.T) {
|
|
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
|
|
|
|
// The "Fork" button should not appear on the repo home
|
|
|
|
req := NewRequest(t, "GET", "/user2/repo1")
|
|
|
|
resp := MakeRequest(t, req, http.StatusOK)
|
|
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
|
|
htmlDoc.AssertElement(t, "[href=/user2/repo1/fork]", false)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("forking by URL", func(t *testing.T) {
|
|
|
|
t.Run("by name", func(t *testing.T) {
|
|
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
|
|
|
|
// Forking by URL should be Not Found
|
|
|
|
req := NewRequest(t, "GET", "/user2/repo1/fork")
|
|
|
|
session.MakeRequest(t, req, http.StatusNotFound)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("by legacy URL", func(t *testing.T) {
|
|
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
|
|
|
|
// Forking by legacy URL should be Not Found
|
|
|
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) // user2/repo1
|
|
|
|
req := NewRequestf(t, "GET", "/repo/fork/%d", repo.ID)
|
|
|
|
session.MakeRequest(t, req, http.StatusNotFound)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("fork listing", func(t *testing.T) {
|
|
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
|
|
|
|
// Listing the forks should be Not Found, too
|
|
|
|
req := NewRequest(t, "GET", "/user2/repo1/forks")
|
|
|
|
MakeRequest(t, req, http.StatusNotFound)
|
|
|
|
})
|
|
|
|
})
|
2024-02-09 14:57:08 +00:00
|
|
|
})
|
2017-10-15 15:06:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestRepoForkToOrg(t *testing.T) {
|
2024-02-09 14:57:08 +00:00
|
|
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
|
|
|
session := loginUser(t, "user2")
|
|
|
|
org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "org3"})
|
2017-10-15 15:06:07 +00:00
|
|
|
|
2024-02-09 14:57:08 +00:00
|
|
|
t.Run("by name", func(t *testing.T) {
|
|
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
defer func() {
|
|
|
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: org3.ID, Name: "repo1"})
|
|
|
|
repo_service.DeleteRepository(db.DefaultContext, org3, repo, false)
|
|
|
|
}()
|
|
|
|
|
|
|
|
testRepoFork(t, session, "user2", "repo1", "org3", "repo1")
|
|
|
|
|
|
|
|
// Check that no more forking is allowed as user2 owns repository
|
|
|
|
// and org3 organization that owner user2 is also now has forked this repository
|
|
|
|
req := NewRequest(t, "GET", "/user2/repo1")
|
|
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
|
|
_, exists := htmlDoc.doc.Find("a.ui.button[href^=\"/fork\"]").Attr("href")
|
|
|
|
assert.False(t, exists, "Forking should not be allowed anymore")
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("legacy redirect", func(t *testing.T) {
|
|
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
|
|
|
|
testRepoForkLegacyRedirect(t, session, "user2", "repo1")
|
|
|
|
})
|
|
|
|
})
|
2017-06-07 02:27:49 +00:00
|
|
|
}
|
2024-11-02 19:55:30 +00:00
|
|
|
|
|
|
|
func TestForkListPrivateRepo(t *testing.T) {
|
|
|
|
forkItemSelector := ".tw-flex.tw-items-center.tw-py-2"
|
|
|
|
|
|
|
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
|
|
|
session := loginUser(t, "user5")
|
|
|
|
org23 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 23, Visibility: structs.VisibleTypePrivate})
|
|
|
|
|
|
|
|
testRepoFork(t, session, "user2", "repo1", org23.Name, "repo1")
|
|
|
|
|
|
|
|
t.Run("Anomynous", func(t *testing.T) {
|
|
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
|
|
|
|
req := NewRequest(t, "GET", "/user2/repo1/forks")
|
|
|
|
resp := MakeRequest(t, req, http.StatusOK)
|
|
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
|
|
|
|
|
|
htmlDoc.AssertElement(t, forkItemSelector, false)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("Logged in", func(t *testing.T) {
|
|
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
|
|
|
|
req := NewRequest(t, "GET", "/user2/repo1/forks")
|
|
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
|
|
|
|
|
|
htmlDoc.AssertElement(t, forkItemSelector, true)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|