mirror of
https://codeberg.org/forgejo/forgejo
synced 2024-11-27 12:16:10 +01:00
c7c12904ba
- The `<title>` element that lives inside the `<head>` element is an important element that gives browsers and search engine crawlers the title of the webpage, hence the element name. It's therefor important that this title is accurate. - Currently there are three issues with titles on repositories. It doesn't use the `FullName` and instead only uses the repository name, this doesn't distinguish which user or organisation the repository is on. It doesn't show the full treepath in the title when visiting an file inside a directory and instead only uses the latest path in treepath. It can show the repository name twice if the `.Title` variable also included the repository name such as on the repository homepage. - Use the repository's fullname (which include which user the repository is on) instead of just their name. - Display the repository's fullname if it isn't already in `.Title`. - Use the full treepath in the repository code view instead of just the last path. - Adds integration tests. - Adds a new repository (`repo59`) that has 3 depths for folders, which wasn't in any other fixture repository yet, so the full treepath for could be properly tested. - Resolves https://codeberg.org/forgejo/forgejo/issues/1276 (cherry picked from commitff9a6a2cda
) (cherry picked from commit76dffc8621
) (cherry picked from commitff0615b9d0
) (cherry picked from commit8712eaa394
) (cherry picked from commit0c11587582
) (cherry picked from commit3cbd9fb792
) Conflicts: tests/integration/repo_test.go https://codeberg.org/forgejo/forgejo/pulls/1512 (cherry picked from commitfbfdba8ae9
) Conflicts: models/fixtures/release.yml https://codeberg.org/forgejo/forgejo/pulls/1550 (cherry picked from commit8b2bf0534c
) (cherry picked from commitd706d9e222
) (cherry picked from commit6d46261a3f
) (cherry picked from commit87e8ad2307
) (cherry picked from commitf728a5e89d
) (cherry picked from commit97534cd4a1
) [GITEA] Improve HTML title on repositories (squash) do not double escape (cherry picked from commit22882fe25c
) (cherry picked from commit63e99df3d1
) (cherry picked from commitb65d777bc7
) (cherry picked from commit2961f4f632
) (cherry picked from commitf7f723628c
) (cherry picked from commit9ed7915826
) (cherry picked from commit6d83f86cf0
) (cherry picked from commit525f95cf1a
) (cherry picked from commitb68aebe0f5
)
410 lines
14 KiB
Go
410 lines
14 KiB
Go
// Copyright 2017 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package repo_test
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"code.gitea.io/gitea/models/db"
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
|
"code.gitea.io/gitea/models/unittest"
|
|
"code.gitea.io/gitea/modules/util"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func getTestCases() []struct {
|
|
name string
|
|
opts *repo_model.SearchRepoOptions
|
|
count int
|
|
} {
|
|
testCases := []struct {
|
|
name string
|
|
opts *repo_model.SearchRepoOptions
|
|
count int
|
|
}{
|
|
{
|
|
name: "PublicRepositoriesByName",
|
|
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, Collaborate: util.OptionalBoolFalse},
|
|
count: 7,
|
|
},
|
|
{
|
|
name: "PublicAndPrivateRepositoriesByName",
|
|
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, Collaborate: util.OptionalBoolFalse},
|
|
count: 14,
|
|
},
|
|
{
|
|
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFirstPage",
|
|
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
|
|
count: 14,
|
|
},
|
|
{
|
|
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitSecondPage",
|
|
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 2, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
|
|
count: 14,
|
|
},
|
|
{
|
|
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitThirdPage",
|
|
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
|
|
count: 14,
|
|
},
|
|
{
|
|
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFourthPage",
|
|
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
|
|
count: 14,
|
|
},
|
|
{
|
|
name: "PublicRepositoriesOfUser",
|
|
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Collaborate: util.OptionalBoolFalse},
|
|
count: 2,
|
|
},
|
|
{
|
|
name: "PublicRepositoriesOfUser2",
|
|
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Collaborate: util.OptionalBoolFalse},
|
|
count: 0,
|
|
},
|
|
{
|
|
name: "PublicRepositoriesOfOrg3",
|
|
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Collaborate: util.OptionalBoolFalse},
|
|
count: 2,
|
|
},
|
|
{
|
|
name: "PublicAndPrivateRepositoriesOfUser",
|
|
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, Collaborate: util.OptionalBoolFalse},
|
|
count: 4,
|
|
},
|
|
{
|
|
name: "PublicAndPrivateRepositoriesOfUser2",
|
|
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, Collaborate: util.OptionalBoolFalse},
|
|
count: 0,
|
|
},
|
|
{
|
|
name: "PublicAndPrivateRepositoriesOfOrg3",
|
|
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true, Collaborate: util.OptionalBoolFalse},
|
|
count: 4,
|
|
},
|
|
{
|
|
name: "PublicRepositoriesOfUserIncludingCollaborative",
|
|
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15},
|
|
count: 5,
|
|
},
|
|
{
|
|
name: "PublicRepositoriesOfUser2IncludingCollaborative",
|
|
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18},
|
|
count: 1,
|
|
},
|
|
{
|
|
name: "PublicRepositoriesOfOrg3IncludingCollaborative",
|
|
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20},
|
|
count: 3,
|
|
},
|
|
{
|
|
name: "PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
|
|
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true},
|
|
count: 9,
|
|
},
|
|
{
|
|
name: "PublicAndPrivateRepositoriesOfUser2IncludingCollaborative",
|
|
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true},
|
|
count: 4,
|
|
},
|
|
{
|
|
name: "PublicAndPrivateRepositoriesOfOrg3IncludingCollaborative",
|
|
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true},
|
|
count: 7,
|
|
},
|
|
{
|
|
name: "PublicRepositoriesOfOrganization",
|
|
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Collaborate: util.OptionalBoolFalse},
|
|
count: 1,
|
|
},
|
|
{
|
|
name: "PublicAndPrivateRepositoriesOfOrganization",
|
|
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Private: true, Collaborate: util.OptionalBoolFalse},
|
|
count: 2,
|
|
},
|
|
{
|
|
name: "AllPublic/PublicRepositoriesByName",
|
|
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, AllPublic: true, Collaborate: util.OptionalBoolFalse},
|
|
count: 7,
|
|
},
|
|
{
|
|
name: "AllPublic/PublicAndPrivateRepositoriesByName",
|
|
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, AllPublic: true, Collaborate: util.OptionalBoolFalse},
|
|
count: 14,
|
|
},
|
|
{
|
|
name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative",
|
|
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: util.OptionalBoolFalse},
|
|
count: 32,
|
|
},
|
|
{
|
|
name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
|
|
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: util.OptionalBoolFalse},
|
|
count: 37,
|
|
},
|
|
{
|
|
name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName",
|
|
opts: &repo_model.SearchRepoOptions{Keyword: "test", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true},
|
|
count: 15,
|
|
},
|
|
{
|
|
name: "AllPublic/PublicAndPrivateRepositoriesOfUser2IncludingCollaborativeByName",
|
|
opts: &repo_model.SearchRepoOptions{Keyword: "test", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, AllPublic: true},
|
|
count: 13,
|
|
},
|
|
{
|
|
name: "AllPublic/PublicRepositoriesOfOrganization",
|
|
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse, Template: util.OptionalBoolFalse},
|
|
count: 32,
|
|
},
|
|
{
|
|
name: "AllTemplates",
|
|
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Template: util.OptionalBoolTrue},
|
|
count: 2,
|
|
},
|
|
{
|
|
name: "OwnerSlashRepoSearch",
|
|
opts: &repo_model.SearchRepoOptions{Keyword: "user/repo2", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, OwnerID: 0},
|
|
count: 2,
|
|
},
|
|
{
|
|
name: "OwnerSlashSearch",
|
|
opts: &repo_model.SearchRepoOptions{Keyword: "user20/", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, OwnerID: 0},
|
|
count: 4,
|
|
},
|
|
}
|
|
|
|
return testCases
|
|
}
|
|
|
|
func TestSearchRepository(t *testing.T) {
|
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
|
|
|
// test search public repository on explore page
|
|
repos, count, err := repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
|
|
ListOptions: db.ListOptions{
|
|
Page: 1,
|
|
PageSize: 10,
|
|
},
|
|
Keyword: "repo_12",
|
|
Collaborate: util.OptionalBoolFalse,
|
|
})
|
|
|
|
assert.NoError(t, err)
|
|
if assert.Len(t, repos, 1) {
|
|
assert.Equal(t, "test_repo_12", repos[0].Name)
|
|
}
|
|
assert.Equal(t, int64(1), count)
|
|
|
|
repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
|
|
ListOptions: db.ListOptions{
|
|
Page: 1,
|
|
PageSize: 10,
|
|
},
|
|
Keyword: "test_repo",
|
|
Collaborate: util.OptionalBoolFalse,
|
|
})
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int64(2), count)
|
|
assert.Len(t, repos, 2)
|
|
|
|
// test search private repository on explore page
|
|
repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
|
|
ListOptions: db.ListOptions{
|
|
Page: 1,
|
|
PageSize: 10,
|
|
},
|
|
Keyword: "repo_13",
|
|
Private: true,
|
|
Collaborate: util.OptionalBoolFalse,
|
|
})
|
|
|
|
assert.NoError(t, err)
|
|
if assert.Len(t, repos, 1) {
|
|
assert.Equal(t, "test_repo_13", repos[0].Name)
|
|
}
|
|
assert.Equal(t, int64(1), count)
|
|
|
|
repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
|
|
ListOptions: db.ListOptions{
|
|
Page: 1,
|
|
PageSize: 10,
|
|
},
|
|
Keyword: "test_repo",
|
|
Private: true,
|
|
Collaborate: util.OptionalBoolFalse,
|
|
})
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int64(3), count)
|
|
assert.Len(t, repos, 3)
|
|
|
|
// Test non existing owner
|
|
repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{OwnerID: unittest.NonexistentID})
|
|
|
|
assert.NoError(t, err)
|
|
assert.Empty(t, repos)
|
|
assert.Equal(t, int64(0), count)
|
|
|
|
// Test search within description
|
|
repos, count, err = repo_model.SearchRepository(db.DefaultContext, &repo_model.SearchRepoOptions{
|
|
ListOptions: db.ListOptions{
|
|
Page: 1,
|
|
PageSize: 10,
|
|
},
|
|
Keyword: "description_14",
|
|
Collaborate: util.OptionalBoolFalse,
|
|
IncludeDescription: true,
|
|
})
|
|
|
|
assert.NoError(t, err)
|
|
if assert.Len(t, repos, 1) {
|
|
assert.Equal(t, "test_repo_14", repos[0].Name)
|
|
}
|
|
assert.Equal(t, int64(1), count)
|
|
|
|
// Test NOT search within description
|
|
repos, count, err = repo_model.SearchRepository(db.DefaultContext, &repo_model.SearchRepoOptions{
|
|
ListOptions: db.ListOptions{
|
|
Page: 1,
|
|
PageSize: 10,
|
|
},
|
|
Keyword: "description_14",
|
|
Collaborate: util.OptionalBoolFalse,
|
|
IncludeDescription: false,
|
|
})
|
|
|
|
assert.NoError(t, err)
|
|
assert.Empty(t, repos)
|
|
assert.Equal(t, int64(0), count)
|
|
|
|
testCases := getTestCases()
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
repos, count, err := repo_model.SearchRepositoryByName(db.DefaultContext, testCase.opts)
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int64(testCase.count), count)
|
|
|
|
page := testCase.opts.Page
|
|
if page <= 0 {
|
|
page = 1
|
|
}
|
|
expectedLen := testCase.opts.PageSize
|
|
if testCase.opts.PageSize*page > testCase.count+testCase.opts.PageSize {
|
|
expectedLen = 0
|
|
} else if testCase.opts.PageSize*page > testCase.count {
|
|
expectedLen = testCase.count % testCase.opts.PageSize
|
|
}
|
|
if assert.Len(t, repos, expectedLen) {
|
|
for _, repo := range repos {
|
|
assert.NotEmpty(t, repo.Name)
|
|
|
|
if len(testCase.opts.Keyword) > 0 {
|
|
// Keyword match condition is different for search terms of form "owner/repo"
|
|
if strings.Count(testCase.opts.Keyword, "/") == 1 {
|
|
// May still match as a whole...
|
|
wholeMatch := strings.Contains(repo.Name, testCase.opts.Keyword)
|
|
|
|
pieces := strings.Split(testCase.opts.Keyword, "/")
|
|
ownerName := pieces[0]
|
|
repoName := pieces[1]
|
|
// ... or match in parts
|
|
splitMatch := strings.Contains(repo.OwnerName, ownerName) && strings.Contains(repo.Name, repoName)
|
|
|
|
assert.True(t, wholeMatch || splitMatch, "Keyword '%s' does not match repo '%s/%s'", testCase.opts.Keyword, repo.Owner.Name, repo.Name)
|
|
} else {
|
|
assert.Contains(t, repo.Name, testCase.opts.Keyword)
|
|
}
|
|
}
|
|
|
|
if !testCase.opts.Private {
|
|
assert.False(t, repo.IsPrivate)
|
|
}
|
|
|
|
if testCase.opts.Fork == util.OptionalBoolTrue && testCase.opts.Mirror == util.OptionalBoolTrue {
|
|
assert.True(t, repo.IsFork || repo.IsMirror)
|
|
} else {
|
|
switch testCase.opts.Fork {
|
|
case util.OptionalBoolFalse:
|
|
assert.False(t, repo.IsFork)
|
|
case util.OptionalBoolTrue:
|
|
assert.True(t, repo.IsFork)
|
|
}
|
|
|
|
switch testCase.opts.Mirror {
|
|
case util.OptionalBoolFalse:
|
|
assert.False(t, repo.IsMirror)
|
|
case util.OptionalBoolTrue:
|
|
assert.True(t, repo.IsMirror)
|
|
}
|
|
}
|
|
|
|
if testCase.opts.OwnerID > 0 && !testCase.opts.AllPublic {
|
|
switch testCase.opts.Collaborate {
|
|
case util.OptionalBoolFalse:
|
|
assert.Equal(t, testCase.opts.OwnerID, repo.Owner.ID)
|
|
case util.OptionalBoolTrue:
|
|
assert.NotEqual(t, testCase.opts.OwnerID, repo.Owner.ID)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCountRepository(t *testing.T) {
|
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
|
|
|
testCases := getTestCases()
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
count, err := repo_model.CountRepository(db.DefaultContext, testCase.opts)
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int64(testCase.count), count)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSearchRepositoryByTopicName(t *testing.T) {
|
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
|
|
|
testCases := []struct {
|
|
name string
|
|
opts *repo_model.SearchRepoOptions
|
|
count int
|
|
}{
|
|
{
|
|
name: "AllPublic/SearchPublicRepositoriesFromTopicAndName",
|
|
opts: &repo_model.SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql"},
|
|
count: 2,
|
|
},
|
|
{
|
|
name: "AllPublic/OnlySearchPublicRepositoriesFromTopic",
|
|
opts: &repo_model.SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql", TopicOnly: true},
|
|
count: 1,
|
|
},
|
|
{
|
|
name: "AllPublic/OnlySearchMultipleKeywordPublicRepositoriesFromTopic",
|
|
opts: &repo_model.SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql,golang", TopicOnly: true},
|
|
count: 2,
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
_, count, err := repo_model.SearchRepositoryByName(db.DefaultContext, testCase.opts)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int64(testCase.count), count)
|
|
})
|
|
}
|
|
}
|