3f30f486d5
- 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 commitf864d18ad3
) (cherry picked from commit80f8620d0d
) [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 commit8b9ead4608
) (cherry picked from commit50eeaf1fbc
) (cherry picked from commitee6f32820e
) (cherry picked from commitbf337bed35
) (cherry picked from commit6be9501ec0
) (cherry picked from commitb39860570d
)
409 lines
14 KiB
Go
409 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)
|
|
})
|
|
}
|
|
}
|