Files
gitea-tea/internal/services/repository_search_test.go
T
bircni eedbe9893d feat(repo): show repository columns, state and README
repo list and search repo printed bare names. They now render a table
with NAME, DESCRIPTION, INFO (public or private, fork, mirror, template,
archived, empty) and UPDATED under "Showing N of M repositories ...", an
empty-state message on stderr, and tab-separated rows with the URL when
piped. --type, --sort and --order complete and validate their values.

repo view shows the visibility and kind badges, stars, forks, open
issues and pull requests, the parent of a fork, the source of a mirror,
the default branch and the clone URLs, followed by the README of the
default branch (or --branch), rendered on a terminal. The repository
JSON gains empty, stars_count, forks_count, open_issues_count,
open_pr_count, topics and updated_at.

Signed-off-by: bircni <bircni@icloud.com>
2026-10-11 23:51:45 +02:00

143 lines
5.4 KiB
Go

// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package services
import (
"context"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRepositoryOwnerLookupOnlyFallsBackOnNotFound(t *testing.T) {
for _, status := range []int{http.StatusForbidden, http.StatusNotFound} {
t.Run(http.StatusText(status), func(t *testing.T) {
calls := []string{}
client := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
calls = append(calls, r.URL.Path)
if r.URL.Path == "/api/v1/orgs/owner" {
w.WriteHeader(status)
return
}
if r.URL.Path == "/api/v1/users/owner" {
_, _ = w.Write([]byte(`{"id":2}`))
return
}
_, _ = w.Write([]byte(`[]`))
}))
items, _, err := (Repositories{Client: client}).List(context.Background(), RepositoryListOptions{Owner: "owner", Limit: 30, Page: 1})
if status == http.StatusForbidden {
require.Error(t, err)
require.Equal(t, []string{"/api/v1/orgs/owner"}, calls)
} else {
require.NoError(t, err)
require.NotNil(t, items)
require.Equal(t, []string{"/api/v1/orgs/owner", "/api/v1/users/owner", "/api/v1/users/owner/repos"}, calls)
}
})
}
}
func TestRepositoryListSelectsCollection(t *testing.T) {
for name, test := range map[string]struct {
opts RepositoryListOptions
path string
}{
"account": {RepositoryListOptions{}, "/api/v1/user/repos"},
"user": {RepositoryListOptions{Owner: "bob"}, "/api/v1/users/bob/repos"},
"organization": {RepositoryListOptions{Owner: "acme"}, "/api/v1/orgs/acme/repos"},
"starred": {RepositoryListOptions{Starred: true}, "/api/v1/user/starred"},
"watched": {RepositoryListOptions{Watched: true}, "/api/v1/user/subscriptions"},
"owner first": {RepositoryListOptions{Owner: "bob", Starred: true}, "/api/v1/users/bob/repos"},
} {
t.Run(name, func(t *testing.T) {
var listed []string
client := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/v1/orgs/acme":
_, _ = w.Write([]byte(`{"id":3,"username":"acme"}`))
case "/api/v1/orgs/bob":
w.WriteHeader(http.StatusNotFound)
case "/api/v1/users/bob":
_, _ = w.Write([]byte(`{"id":2,"login":"bob"}`))
default:
listed = append(listed, r.URL.Path)
assert.Equal(t, "2", r.URL.Query().Get("page"))
assert.Equal(t, "1", r.URL.Query().Get("limit"))
w.Header().Set("X-Total-Count", "3")
_, _ = w.Write([]byte(`[{"id":2,"name":"second","fork":true}]`))
}
}))
test.opts.Limit, test.opts.Page = 1, 2
items, more, err := (Repositories{Client: client}).List(context.Background(), test.opts)
require.NoError(t, err)
require.Equal(t, []string{test.path}, listed)
require.Equal(t, []Repository{{ID: 2, Name: "second", Fork: true}}, items)
require.True(t, more)
})
}
}
func TestRepositoryListFiltersKind(t *testing.T) {
client := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(`[{"id":1,"fork":true},{"id":2,"mirror":true},{"id":3}]`))
}))
for kind, want := range map[string]int64{"fork": 1, "mirror": 2, "source": 3} {
items, _, err := (Repositories{Client: client}).List(context.Background(), RepositoryListOptions{Kind: kind, Limit: 30, Page: 1})
require.NoError(t, err)
require.Len(t, items, 1, kind)
require.Equal(t, want, items[0].ID, kind)
}
}
func TestRepositorySearchSendsExplicitFalseFilters(t *testing.T) {
client := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/api/v1/user" {
_, _ = w.Write([]byte(`{"id":7}`))
return
}
assert.Equal(t, "/api/v1/repos/search", r.URL.Path)
query := r.URL.Query()
assert.Equal(t, "false", query.Get("is_private"))
assert.Equal(t, "false", query.Get("archived"))
assert.Equal(t, "true", query.Get("topic"))
assert.Equal(t, "7", query.Get("priority_owner_id"))
assert.Equal(t, "two words", query.Get("q"))
assert.Equal(t, "true", query.Get("includeDesc"))
assert.Equal(t, "30", query.Get("limit"))
assert.Equal(t, "1", query.Get("page"))
_, _ = w.Write([]byte(`{"data":[]}`))
}))
value := false
page, err := (Repositories{Client: client}).Search(context.Background(), RepositorySearchOptions{Query: "two words", Private: &value, Archived: &value, Topic: true, Limit: 30, Page: 1})
require.NoError(t, err)
require.NotNil(t, page.Items)
require.Empty(t, page.Items)
}
func TestRepositorySearchScopesOwner(t *testing.T) {
client := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/v1/orgs/acme":
_, _ = w.Write([]byte(`{"id":3,"username":"acme"}`))
case "/api/v1/user":
_, _ = w.Write([]byte(`{"id":7,"login":"alice"}`))
default:
query := r.URL.Query()
assert.Equal(t, "3", query.Get("uid"))
assert.Equal(t, "true", query.Get("exclusive"))
assert.Equal(t, "fork", query.Get("mode"))
assert.Equal(t, "updated", query.Get("sort"))
assert.Equal(t, "desc", query.Get("order"))
assert.False(t, query.Has("is_private"))
_, _ = w.Write([]byte(`{"data":[{"id":4,"full_name":"acme/tool"}]}`))
}
}))
page, err := (Repositories{Client: client}).Search(context.Background(), RepositorySearchOptions{Owner: "acme", Kind: "fork", Sort: "updated", Order: "desc", Limit: 30, Page: 1})
require.NoError(t, err)
require.Equal(t, []Repository{{ID: 4, FullName: "acme/tool"}}, page.Items)
}