mirror of https://github.com/go-gitea/gitea.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
468 lines
16 KiB
468 lines
16 KiB
// 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" |
|
user_model "code.gitea.io/gitea/models/user" |
|
"code.gitea.io/gitea/modules/optional" |
|
"code.gitea.io/gitea/modules/setting" |
|
"code.gitea.io/gitea/modules/structs" |
|
"code.gitea.io/gitea/modules/test" |
|
|
|
"github.com/stretchr/testify/assert" |
|
"github.com/stretchr/testify/require" |
|
) |
|
|
|
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: optional.Some(false)}, |
|
count: 7, |
|
}, |
|
{ |
|
name: "PublicAndPrivateRepositoriesByName", |
|
opts: repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, Collaborate: optional.Some(false)}, |
|
count: 14, |
|
}, |
|
{ |
|
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFirstPage", |
|
opts: repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 5}, Private: true, Collaborate: optional.Some(false)}, |
|
count: 14, |
|
}, |
|
{ |
|
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitSecondPage", |
|
opts: repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 2, PageSize: 5}, Private: true, Collaborate: optional.Some(false)}, |
|
count: 14, |
|
}, |
|
{ |
|
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitThirdPage", |
|
opts: repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: optional.Some(false)}, |
|
count: 14, |
|
}, |
|
{ |
|
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFourthPage", |
|
opts: repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: optional.Some(false)}, |
|
count: 14, |
|
}, |
|
{ |
|
name: "PublicRepositoriesOfUser", |
|
opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Collaborate: optional.Some(false)}, |
|
count: 2, |
|
}, |
|
{ |
|
name: "PublicRepositoriesOfUser2", |
|
opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Collaborate: optional.Some(false)}, |
|
count: 0, |
|
}, |
|
{ |
|
name: "PublicRepositoriesOfOrg3", |
|
opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Collaborate: optional.Some(false)}, |
|
count: 2, |
|
}, |
|
{ |
|
name: "PublicAndPrivateRepositoriesOfUser", |
|
opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, Collaborate: optional.Some(false)}, |
|
count: 4, |
|
}, |
|
{ |
|
name: "PublicAndPrivateRepositoriesOfUser2", |
|
opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, Collaborate: optional.Some(false)}, |
|
count: 0, |
|
}, |
|
{ |
|
name: "PublicAndPrivateRepositoriesOfOrg3", |
|
opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true, Collaborate: optional.Some(false)}, |
|
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: optional.Some(false)}, |
|
count: 1, |
|
}, |
|
{ |
|
name: "PublicAndPrivateRepositoriesOfOrganization", |
|
opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Private: true, Collaborate: optional.Some(false)}, |
|
count: 2, |
|
}, |
|
{ |
|
name: "AllPublic/PublicRepositoriesByName", |
|
opts: repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, AllPublic: true, Collaborate: optional.Some(false)}, |
|
count: 7, |
|
}, |
|
{ |
|
name: "AllPublic/PublicAndPrivateRepositoriesByName", |
|
opts: repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, AllPublic: true, Collaborate: optional.Some(false)}, |
|
count: 14, |
|
}, |
|
{ |
|
name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative", |
|
opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: optional.Some(false)}, |
|
count: 34, |
|
}, |
|
{ |
|
name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative", |
|
opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: optional.Some(false)}, |
|
count: 39, |
|
}, |
|
{ |
|
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: optional.Some(false), Template: optional.Some(false)}, |
|
count: 34, |
|
}, |
|
{ |
|
name: "AllTemplates", |
|
opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Template: optional.Some(true)}, |
|
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()) |
|
t.Run("SearchRepositoryPublic", testSearchRepositoryPublic) |
|
t.Run("SearchRepositoryPublicRestricted", testSearchRepositoryRestricted) |
|
t.Run("SearchRepositoryPrivate", testSearchRepositoryPrivate) |
|
t.Run("SearchRepositoryNonExistingOwner", testSearchRepositoryNonExistingOwner) |
|
t.Run("SearchRepositoryWithInDescription", testSearchRepositoryWithInDescription) |
|
t.Run("SearchRepositoryNotInDescription", testSearchRepositoryNotInDescription) |
|
t.Run("SearchRepositoryCases", testSearchRepositoryCases) |
|
} |
|
|
|
func testSearchRepositoryPublic(t *testing.T) { |
|
// test search public repository on explore page |
|
repos, count, err := repo_model.SearchRepositoryByName(t.Context(), repo_model.SearchRepoOptions{ |
|
ListOptions: db.ListOptions{ |
|
Page: 1, |
|
PageSize: 10, |
|
}, |
|
Keyword: "repo_12", |
|
Collaborate: optional.Some(false), |
|
}) |
|
|
|
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(t.Context(), repo_model.SearchRepoOptions{ |
|
ListOptions: db.ListOptions{ |
|
Page: 1, |
|
PageSize: 10, |
|
}, |
|
Keyword: "test_repo", |
|
Collaborate: optional.Some(false), |
|
}) |
|
|
|
assert.NoError(t, err) |
|
assert.Equal(t, int64(2), count) |
|
assert.Len(t, repos, 2) |
|
} |
|
|
|
func testSearchRepositoryRestricted(t *testing.T) { |
|
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) |
|
restrictedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29, IsRestricted: true}) |
|
|
|
performSearch := func(t *testing.T, user *user_model.User) (publicRepoIDs []int64) { |
|
repos, count, err := repo_model.SearchRepositoryByName(t.Context(), repo_model.SearchRepoOptions{ |
|
ListOptions: db.ListOptions{Page: 1, PageSize: 10000}, |
|
Actor: user, |
|
}) |
|
require.NoError(t, err) |
|
assert.Len(t, repos, int(count)) |
|
for _, repo := range repos { |
|
require.NoError(t, repo.LoadOwner(t.Context())) |
|
if repo.Owner.Visibility == structs.VisibleTypePublic && !repo.IsPrivate { |
|
publicRepoIDs = append(publicRepoIDs, repo.ID) |
|
} |
|
} |
|
return publicRepoIDs |
|
} |
|
|
|
normalPublicRepoIDs := performSearch(t, user2) |
|
require.Greater(t, len(normalPublicRepoIDs), 10) // quite a lot |
|
|
|
t.Run("RestrictedUser-NoSignInRequirement", func(t *testing.T) { |
|
// restricted user can also see public repositories if no "required sign-in" |
|
repoIDs := performSearch(t, restrictedUser) |
|
assert.ElementsMatch(t, normalPublicRepoIDs, repoIDs) |
|
}) |
|
|
|
defer test.MockVariableValue(&setting.Service.RequireSignInViewStrict, true)() |
|
|
|
t.Run("NormalUser-RequiredSignIn", func(t *testing.T) { |
|
// normal user can still see all public repos, not affected by "required sign-in" |
|
repoIDs := performSearch(t, user2) |
|
assert.ElementsMatch(t, normalPublicRepoIDs, repoIDs) |
|
}) |
|
t.Run("RestrictedUser-RequiredSignIn", func(t *testing.T) { |
|
// restricted user can see only their own repo |
|
repoIDs := performSearch(t, restrictedUser) |
|
assert.Equal(t, []int64{4}, repoIDs) |
|
}) |
|
} |
|
|
|
func testSearchRepositoryPrivate(t *testing.T) { |
|
// test search private repository on explore page |
|
repos, count, err := repo_model.SearchRepositoryByName(t.Context(), repo_model.SearchRepoOptions{ |
|
ListOptions: db.ListOptions{ |
|
Page: 1, |
|
PageSize: 10, |
|
}, |
|
Keyword: "repo_13", |
|
Private: true, |
|
Collaborate: optional.Some(false), |
|
}) |
|
|
|
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(t.Context(), repo_model.SearchRepoOptions{ |
|
ListOptions: db.ListOptions{ |
|
Page: 1, |
|
PageSize: 10, |
|
}, |
|
Keyword: "test_repo", |
|
Private: true, |
|
Collaborate: optional.Some(false), |
|
}) |
|
|
|
assert.NoError(t, err) |
|
assert.Equal(t, int64(3), count) |
|
assert.Len(t, repos, 3) |
|
} |
|
|
|
func testSearchRepositoryNonExistingOwner(t *testing.T) { |
|
repos, count, err := repo_model.SearchRepositoryByName(t.Context(), repo_model.SearchRepoOptions{OwnerID: unittest.NonexistentID}) |
|
|
|
assert.NoError(t, err) |
|
assert.Empty(t, repos) |
|
assert.Equal(t, int64(0), count) |
|
} |
|
|
|
func testSearchRepositoryWithInDescription(t *testing.T) { |
|
repos, count, err := repo_model.SearchRepository(t.Context(), repo_model.SearchRepoOptions{ |
|
ListOptions: db.ListOptions{ |
|
Page: 1, |
|
PageSize: 10, |
|
}, |
|
Keyword: "description_14", |
|
Collaborate: optional.Some(false), |
|
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) |
|
} |
|
|
|
func testSearchRepositoryNotInDescription(t *testing.T) { |
|
repos, count, err := repo_model.SearchRepository(t.Context(), repo_model.SearchRepoOptions{ |
|
ListOptions: db.ListOptions{ |
|
Page: 1, |
|
PageSize: 10, |
|
}, |
|
Keyword: "description_14", |
|
Collaborate: optional.Some(false), |
|
IncludeDescription: false, |
|
}) |
|
|
|
assert.NoError(t, err) |
|
assert.Empty(t, repos) |
|
assert.Equal(t, int64(0), count) |
|
} |
|
|
|
func testSearchRepositoryCases(t *testing.T) { |
|
testCases := getTestCases() |
|
|
|
for _, testCase := range testCases { |
|
t.Run(testCase.name, func(t *testing.T) { |
|
repos, count, err := repo_model.SearchRepositoryByName(t.Context(), 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.Value() && testCase.opts.Mirror.Value() { |
|
assert.True(t, repo.IsFork && repo.IsMirror) |
|
} else { |
|
if testCase.opts.Fork.Has() { |
|
assert.Equal(t, testCase.opts.Fork.Value(), repo.IsFork) |
|
} |
|
|
|
if testCase.opts.Mirror.Has() { |
|
assert.Equal(t, testCase.opts.Mirror.Value(), repo.IsMirror) |
|
} |
|
} |
|
|
|
if testCase.opts.OwnerID > 0 && !testCase.opts.AllPublic { |
|
if testCase.opts.Collaborate.Has() { |
|
if testCase.opts.Collaborate.Value() { |
|
assert.NotEqual(t, testCase.opts.OwnerID, repo.Owner.ID) |
|
} else { |
|
assert.Equal(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(t.Context(), 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(t.Context(), testCase.opts) |
|
assert.NoError(t, err) |
|
assert.Equal(t, int64(testCase.count), count) |
|
}) |
|
} |
|
}
|
|
|