code.gitea.io/gitea@v1.22.3/models/repo/repo_list_test.go (about)

     1  // Copyright 2017 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package repo_test
     5  
     6  import (
     7  	"strings"
     8  	"testing"
     9  
    10  	"code.gitea.io/gitea/models/db"
    11  	repo_model "code.gitea.io/gitea/models/repo"
    12  	"code.gitea.io/gitea/models/unittest"
    13  	"code.gitea.io/gitea/modules/optional"
    14  
    15  	"github.com/stretchr/testify/assert"
    16  )
    17  
    18  func getTestCases() []struct {
    19  	name  string
    20  	opts  *repo_model.SearchRepoOptions
    21  	count int
    22  } {
    23  	testCases := []struct {
    24  		name  string
    25  		opts  *repo_model.SearchRepoOptions
    26  		count int
    27  	}{
    28  		{
    29  			name:  "PublicRepositoriesByName",
    30  			opts:  &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, Collaborate: optional.Some(false)},
    31  			count: 7,
    32  		},
    33  		{
    34  			name:  "PublicAndPrivateRepositoriesByName",
    35  			opts:  &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, Collaborate: optional.Some(false)},
    36  			count: 14,
    37  		},
    38  		{
    39  			name:  "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFirstPage",
    40  			opts:  &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
    41  			count: 14,
    42  		},
    43  		{
    44  			name:  "PublicAndPrivateRepositoriesByNameWithPagesizeLimitSecondPage",
    45  			opts:  &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 2, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
    46  			count: 14,
    47  		},
    48  		{
    49  			name:  "PublicAndPrivateRepositoriesByNameWithPagesizeLimitThirdPage",
    50  			opts:  &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
    51  			count: 14,
    52  		},
    53  		{
    54  			name:  "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFourthPage",
    55  			opts:  &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
    56  			count: 14,
    57  		},
    58  		{
    59  			name:  "PublicRepositoriesOfUser",
    60  			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Collaborate: optional.Some(false)},
    61  			count: 2,
    62  		},
    63  		{
    64  			name:  "PublicRepositoriesOfUser2",
    65  			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Collaborate: optional.Some(false)},
    66  			count: 0,
    67  		},
    68  		{
    69  			name:  "PublicRepositoriesOfOrg3",
    70  			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Collaborate: optional.Some(false)},
    71  			count: 2,
    72  		},
    73  		{
    74  			name:  "PublicAndPrivateRepositoriesOfUser",
    75  			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, Collaborate: optional.Some(false)},
    76  			count: 4,
    77  		},
    78  		{
    79  			name:  "PublicAndPrivateRepositoriesOfUser2",
    80  			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, Collaborate: optional.Some(false)},
    81  			count: 0,
    82  		},
    83  		{
    84  			name:  "PublicAndPrivateRepositoriesOfOrg3",
    85  			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true, Collaborate: optional.Some(false)},
    86  			count: 4,
    87  		},
    88  		{
    89  			name:  "PublicRepositoriesOfUserIncludingCollaborative",
    90  			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15},
    91  			count: 5,
    92  		},
    93  		{
    94  			name:  "PublicRepositoriesOfUser2IncludingCollaborative",
    95  			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18},
    96  			count: 1,
    97  		},
    98  		{
    99  			name:  "PublicRepositoriesOfOrg3IncludingCollaborative",
   100  			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20},
   101  			count: 3,
   102  		},
   103  		{
   104  			name:  "PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
   105  			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true},
   106  			count: 9,
   107  		},
   108  		{
   109  			name:  "PublicAndPrivateRepositoriesOfUser2IncludingCollaborative",
   110  			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true},
   111  			count: 4,
   112  		},
   113  		{
   114  			name:  "PublicAndPrivateRepositoriesOfOrg3IncludingCollaborative",
   115  			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true},
   116  			count: 7,
   117  		},
   118  		{
   119  			name:  "PublicRepositoriesOfOrganization",
   120  			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Collaborate: optional.Some(false)},
   121  			count: 1,
   122  		},
   123  		{
   124  			name:  "PublicAndPrivateRepositoriesOfOrganization",
   125  			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Private: true, Collaborate: optional.Some(false)},
   126  			count: 2,
   127  		},
   128  		{
   129  			name:  "AllPublic/PublicRepositoriesByName",
   130  			opts:  &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, AllPublic: true, Collaborate: optional.Some(false)},
   131  			count: 7,
   132  		},
   133  		{
   134  			name:  "AllPublic/PublicAndPrivateRepositoriesByName",
   135  			opts:  &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, AllPublic: true, Collaborate: optional.Some(false)},
   136  			count: 14,
   137  		},
   138  		{
   139  			name:  "AllPublic/PublicRepositoriesOfUserIncludingCollaborative",
   140  			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: optional.Some(false)},
   141  			count: 33,
   142  		},
   143  		{
   144  			name:  "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
   145  			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: optional.Some(false)},
   146  			count: 38,
   147  		},
   148  		{
   149  			name:  "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName",
   150  			opts:  &repo_model.SearchRepoOptions{Keyword: "test", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true},
   151  			count: 15,
   152  		},
   153  		{
   154  			name:  "AllPublic/PublicAndPrivateRepositoriesOfUser2IncludingCollaborativeByName",
   155  			opts:  &repo_model.SearchRepoOptions{Keyword: "test", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, AllPublic: true},
   156  			count: 13,
   157  		},
   158  		{
   159  			name:  "AllPublic/PublicRepositoriesOfOrganization",
   160  			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: optional.Some(false), Template: optional.Some(false)},
   161  			count: 33,
   162  		},
   163  		{
   164  			name:  "AllTemplates",
   165  			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Template: optional.Some(true)},
   166  			count: 2,
   167  		},
   168  		{
   169  			name:  "OwnerSlashRepoSearch",
   170  			opts:  &repo_model.SearchRepoOptions{Keyword: "user/repo2", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, OwnerID: 0},
   171  			count: 2,
   172  		},
   173  		{
   174  			name:  "OwnerSlashSearch",
   175  			opts:  &repo_model.SearchRepoOptions{Keyword: "user20/", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, OwnerID: 0},
   176  			count: 4,
   177  		},
   178  	}
   179  
   180  	return testCases
   181  }
   182  
   183  func TestSearchRepository(t *testing.T) {
   184  	assert.NoError(t, unittest.PrepareTestDatabase())
   185  
   186  	// test search public repository on explore page
   187  	repos, count, err := repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
   188  		ListOptions: db.ListOptions{
   189  			Page:     1,
   190  			PageSize: 10,
   191  		},
   192  		Keyword:     "repo_12",
   193  		Collaborate: optional.Some(false),
   194  	})
   195  
   196  	assert.NoError(t, err)
   197  	if assert.Len(t, repos, 1) {
   198  		assert.Equal(t, "test_repo_12", repos[0].Name)
   199  	}
   200  	assert.Equal(t, int64(1), count)
   201  
   202  	repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
   203  		ListOptions: db.ListOptions{
   204  			Page:     1,
   205  			PageSize: 10,
   206  		},
   207  		Keyword:     "test_repo",
   208  		Collaborate: optional.Some(false),
   209  	})
   210  
   211  	assert.NoError(t, err)
   212  	assert.Equal(t, int64(2), count)
   213  	assert.Len(t, repos, 2)
   214  
   215  	// test search private repository on explore page
   216  	repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
   217  		ListOptions: db.ListOptions{
   218  			Page:     1,
   219  			PageSize: 10,
   220  		},
   221  		Keyword:     "repo_13",
   222  		Private:     true,
   223  		Collaborate: optional.Some(false),
   224  	})
   225  
   226  	assert.NoError(t, err)
   227  	if assert.Len(t, repos, 1) {
   228  		assert.Equal(t, "test_repo_13", repos[0].Name)
   229  	}
   230  	assert.Equal(t, int64(1), count)
   231  
   232  	repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
   233  		ListOptions: db.ListOptions{
   234  			Page:     1,
   235  			PageSize: 10,
   236  		},
   237  		Keyword:     "test_repo",
   238  		Private:     true,
   239  		Collaborate: optional.Some(false),
   240  	})
   241  
   242  	assert.NoError(t, err)
   243  	assert.Equal(t, int64(3), count)
   244  	assert.Len(t, repos, 3)
   245  
   246  	// Test non existing owner
   247  	repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{OwnerID: unittest.NonexistentID})
   248  
   249  	assert.NoError(t, err)
   250  	assert.Empty(t, repos)
   251  	assert.Equal(t, int64(0), count)
   252  
   253  	// Test search within description
   254  	repos, count, err = repo_model.SearchRepository(db.DefaultContext, &repo_model.SearchRepoOptions{
   255  		ListOptions: db.ListOptions{
   256  			Page:     1,
   257  			PageSize: 10,
   258  		},
   259  		Keyword:            "description_14",
   260  		Collaborate:        optional.Some(false),
   261  		IncludeDescription: true,
   262  	})
   263  
   264  	assert.NoError(t, err)
   265  	if assert.Len(t, repos, 1) {
   266  		assert.Equal(t, "test_repo_14", repos[0].Name)
   267  	}
   268  	assert.Equal(t, int64(1), count)
   269  
   270  	// Test NOT search within description
   271  	repos, count, err = repo_model.SearchRepository(db.DefaultContext, &repo_model.SearchRepoOptions{
   272  		ListOptions: db.ListOptions{
   273  			Page:     1,
   274  			PageSize: 10,
   275  		},
   276  		Keyword:            "description_14",
   277  		Collaborate:        optional.Some(false),
   278  		IncludeDescription: false,
   279  	})
   280  
   281  	assert.NoError(t, err)
   282  	assert.Empty(t, repos)
   283  	assert.Equal(t, int64(0), count)
   284  
   285  	testCases := getTestCases()
   286  
   287  	for _, testCase := range testCases {
   288  		t.Run(testCase.name, func(t *testing.T) {
   289  			repos, count, err := repo_model.SearchRepositoryByName(db.DefaultContext, testCase.opts)
   290  
   291  			assert.NoError(t, err)
   292  			assert.Equal(t, int64(testCase.count), count)
   293  
   294  			page := testCase.opts.Page
   295  			if page <= 0 {
   296  				page = 1
   297  			}
   298  			expectedLen := testCase.opts.PageSize
   299  			if testCase.opts.PageSize*page > testCase.count+testCase.opts.PageSize {
   300  				expectedLen = 0
   301  			} else if testCase.opts.PageSize*page > testCase.count {
   302  				expectedLen = testCase.count % testCase.opts.PageSize
   303  			}
   304  			if assert.Len(t, repos, expectedLen) {
   305  				for _, repo := range repos {
   306  					assert.NotEmpty(t, repo.Name)
   307  
   308  					if len(testCase.opts.Keyword) > 0 {
   309  						// Keyword match condition is different for search terms of form "owner/repo"
   310  						if strings.Count(testCase.opts.Keyword, "/") == 1 {
   311  							// May still match as a whole...
   312  							wholeMatch := strings.Contains(repo.Name, testCase.opts.Keyword)
   313  
   314  							pieces := strings.Split(testCase.opts.Keyword, "/")
   315  							ownerName := pieces[0]
   316  							repoName := pieces[1]
   317  							// ... or match in parts
   318  							splitMatch := strings.Contains(repo.OwnerName, ownerName) && strings.Contains(repo.Name, repoName)
   319  
   320  							assert.True(t, wholeMatch || splitMatch, "Keyword '%s' does not match repo '%s/%s'", testCase.opts.Keyword, repo.Owner.Name, repo.Name)
   321  						} else {
   322  							assert.Contains(t, repo.Name, testCase.opts.Keyword)
   323  						}
   324  					}
   325  
   326  					if !testCase.opts.Private {
   327  						assert.False(t, repo.IsPrivate)
   328  					}
   329  
   330  					if testCase.opts.Fork.Value() && testCase.opts.Mirror.Value() {
   331  						assert.True(t, repo.IsFork && repo.IsMirror)
   332  					} else {
   333  						if testCase.opts.Fork.Has() {
   334  							assert.Equal(t, testCase.opts.Fork.Value(), repo.IsFork)
   335  						}
   336  
   337  						if testCase.opts.Mirror.Has() {
   338  							assert.Equal(t, testCase.opts.Mirror.Value(), repo.IsMirror)
   339  						}
   340  					}
   341  
   342  					if testCase.opts.OwnerID > 0 && !testCase.opts.AllPublic {
   343  						if testCase.opts.Collaborate.Has() {
   344  							if testCase.opts.Collaborate.Value() {
   345  								assert.NotEqual(t, testCase.opts.OwnerID, repo.Owner.ID)
   346  							} else {
   347  								assert.Equal(t, testCase.opts.OwnerID, repo.Owner.ID)
   348  							}
   349  						}
   350  					}
   351  				}
   352  			}
   353  		})
   354  	}
   355  }
   356  
   357  func TestCountRepository(t *testing.T) {
   358  	assert.NoError(t, unittest.PrepareTestDatabase())
   359  
   360  	testCases := getTestCases()
   361  
   362  	for _, testCase := range testCases {
   363  		t.Run(testCase.name, func(t *testing.T) {
   364  			count, err := repo_model.CountRepository(db.DefaultContext, testCase.opts)
   365  
   366  			assert.NoError(t, err)
   367  			assert.Equal(t, int64(testCase.count), count)
   368  		})
   369  	}
   370  }
   371  
   372  func TestSearchRepositoryByTopicName(t *testing.T) {
   373  	assert.NoError(t, unittest.PrepareTestDatabase())
   374  
   375  	testCases := []struct {
   376  		name  string
   377  		opts  *repo_model.SearchRepoOptions
   378  		count int
   379  	}{
   380  		{
   381  			name:  "AllPublic/SearchPublicRepositoriesFromTopicAndName",
   382  			opts:  &repo_model.SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql"},
   383  			count: 2,
   384  		},
   385  		{
   386  			name:  "AllPublic/OnlySearchPublicRepositoriesFromTopic",
   387  			opts:  &repo_model.SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql", TopicOnly: true},
   388  			count: 1,
   389  		},
   390  		{
   391  			name:  "AllPublic/OnlySearchMultipleKeywordPublicRepositoriesFromTopic",
   392  			opts:  &repo_model.SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql,golang", TopicOnly: true},
   393  			count: 2,
   394  		},
   395  	}
   396  
   397  	for _, testCase := range testCases {
   398  		t.Run(testCase.name, func(t *testing.T) {
   399  			_, count, err := repo_model.SearchRepositoryByName(db.DefaultContext, testCase.opts)
   400  			assert.NoError(t, err)
   401  			assert.Equal(t, int64(testCase.count), count)
   402  		})
   403  	}
   404  }