mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 11:01:25 +01:00
team repo add 5 org/repo was rejected, and team repo add 5 silently used
the repository of the current directory and failed with an internal
error. team repo add and remove now take the repositories as arguments,
resolve bare names inside the team's organization, refuse repositories of
other owners and --repo, and never discover a repository.
Teams can be addressed as ORG/TEAM by name or ID as well as by their
global ID. team list drops the always-empty ORG column and shows the
derived access ("write (code, issues, pulls)" instead of none), member
and repository counts; team view shows units, members and repositories.
--permission read or write now grants every repository unit on both
create and edit, and edit no longer shows the create default. team
create takes the name as ORG/NAME or a second argument. Member and
repository changes skip users and repositories already in the wanted
state with a warning, name them in confirmations and print ✓ lines. Every
command has examples, and teams, members, users, repositories and --unit
values complete.
Signed-off-by: bircni <bircni@icloud.com>
168 lines
7.1 KiB
Go
168 lines
7.1 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package services
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"testing"
|
|
|
|
gitea "gitea.dev/sdk"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestTeamRepositoryPolicyPreservesUnitPermissions(t *testing.T) {
|
|
mutations := 0
|
|
client := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, "/api/v1/teams/1", r.URL.Path)
|
|
if r.Method == http.MethodGet {
|
|
_, _ = w.Write([]byte(`{"id":1,"permission":"read","units_map":{"repo.code":"write","repo.issues":"read"}}`))
|
|
return
|
|
}
|
|
mutations++
|
|
body := requestJSON(t, r)
|
|
require.Equal(t, "read", body["permission"])
|
|
require.Equal(t, false, body["includes_all_repositories"])
|
|
require.Equal(t, map[string]any{"repo.code": "write", "repo.issues": "read"}, body["units_map"])
|
|
require.NotContains(t, body, "name")
|
|
require.NotContains(t, body, "description")
|
|
_, _ = w.Write([]byte(`{"id":1,"permission":"read","organization":{"username":"acme"}}`))
|
|
}))
|
|
service := Teams{Client: client}
|
|
allRepos := false
|
|
item, err := service.Edit(context.Background(), 1, TeamOptions{AllRepositories: &allRepos})
|
|
require.NoError(t, err)
|
|
require.Equal(t, "acme", item.Organization)
|
|
require.Equal(t, 1, mutations)
|
|
}
|
|
|
|
func TestTeamEditMergesUnitsUnlessReplaced(t *testing.T) {
|
|
for _, test := range []struct {
|
|
replace bool
|
|
reads int
|
|
want map[string]any
|
|
}{
|
|
{false, 1, map[string]any{"repo.code": "write", "repo.issues": "read", "repo.wiki": "write"}},
|
|
{true, 0, map[string]any{"repo.wiki": "write"}},
|
|
} {
|
|
reads := 0
|
|
client := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, "/api/v1/teams/5", r.URL.Path)
|
|
if r.Method == http.MethodGet {
|
|
reads++
|
|
_, _ = w.Write([]byte(`{"id":5,"permission":"write","units_map":{"repo.code":"write","repo.issues":"read","repo.wiki":"none"}}`))
|
|
return
|
|
}
|
|
body := requestJSON(t, r)
|
|
require.Equal(t, test.want, body["units_map"])
|
|
require.NotContains(t, body, "permission")
|
|
_, _ = w.Write([]byte(`{"id":5}`))
|
|
}))
|
|
_, err := (Teams{Client: client}).Edit(context.Background(), 5, TeamOptions{Units: map[string]string{"repo.wiki": "write"}, ReplaceUnits: test.replace})
|
|
require.NoError(t, err)
|
|
require.Equal(t, test.reads, reads)
|
|
}
|
|
}
|
|
|
|
func TestTeamCreateSendsPermissionAndUnits(t *testing.T) {
|
|
client := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, http.MethodPost, r.Method)
|
|
require.Equal(t, "/api/v1/orgs/acme/teams", r.URL.Path)
|
|
body := requestJSON(t, r)
|
|
require.Equal(t, "Developers", body["name"])
|
|
require.Equal(t, "write", body["permission"])
|
|
require.Equal(t, true, body["can_create_org_repo"])
|
|
require.Equal(t, false, body["includes_all_repositories"])
|
|
require.Equal(t, map[string]any{"repo.code": "write"}, body["units_map"])
|
|
w.WriteHeader(http.StatusCreated)
|
|
_, _ = w.Write([]byte(`{"id":3,"name":"Developers","permission":"write","can_create_org_repo":true,"units_map":{"repo.code":"write"},"organization":{"name":"acme"}}`))
|
|
}))
|
|
name, permission, createRepo, allRepos := "Developers", "write", true, false
|
|
item, err := (Teams{Client: client}).Create(context.Background(), "acme", TeamOptions{
|
|
Name: &name, Permission: &permission, CanCreateRepo: &createRepo, AllRepositories: &allRepos, Units: map[string]string{"repo.code": "write"},
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, Team{ID: 3, Name: "Developers", Organization: "acme", Permission: "write", CanCreateRepo: true, Units: map[string]string{"repo.code": "write"}}, item)
|
|
}
|
|
|
|
func TestTeamAccessRequests(t *testing.T) {
|
|
var requests []string
|
|
client := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
requests = append(requests, r.Method+" "+r.URL.RequestURI())
|
|
switch {
|
|
case r.URL.Path == "/api/v1/teams/3/members":
|
|
_, _ = w.Write([]byte(`[{"id":2,"login":"bob"}]`))
|
|
case r.URL.Path == "/api/v1/teams/3/repos":
|
|
_, _ = w.Write([]byte(`[{"id":4,"full_name":"acme/project","html_url":"https://gitea.test/acme/project"}]`))
|
|
default:
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|
|
}))
|
|
service := Teams{Client: client}
|
|
members, err := service.Members(context.Background(), 3, 5, 2)
|
|
require.NoError(t, err)
|
|
require.Equal(t, []OrganizationMember{{ID: 2, Username: "bob"}}, members.Items)
|
|
repos, err := service.Repositories(context.Background(), 3, 5, 1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "https://gitea.test/acme/project", repos.Items[0].URL)
|
|
require.NoError(t, service.SetMember(context.Background(), 3, "bob", false))
|
|
require.NoError(t, service.SetRepository(context.Background(), 3, "acme/project", true))
|
|
require.Equal(t, []string{
|
|
"GET /api/v1/teams/3/members?limit=5&page=2", "GET /api/v1/teams/3/repos?limit=5&page=1",
|
|
"DELETE /api/v1/teams/3/members/bob", "PUT /api/v1/teams/3/repos/acme/project",
|
|
}, requests)
|
|
}
|
|
|
|
func TestTeamFromKeepsAbsentValuesEmpty(t *testing.T) {
|
|
item := teamFrom(decode[gitea.Team](t, `{"id":1,"name":"Owners","permission":"owner","organization":null,"units_map":null}`))
|
|
require.Equal(t, Team{ID: 1, Name: "Owners", Permission: "owner", Units: map[string]string{}}, item)
|
|
}
|
|
|
|
func TestTeamAccessSummarizesUnits(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
permission string
|
|
units map[string]string
|
|
want string
|
|
}{
|
|
{"owner", UnitPolicy("write"), "owner"},
|
|
{"admin", nil, "admin"},
|
|
{"none", UnitPolicy("write"), "write"},
|
|
{"none", UnitPolicy("read"), "read"},
|
|
{"none", map[string]string{"repo.code": "write", "repo.issues": "write", "repo.pulls": "write", "repo.wiki": "none", "repo.ext_wiki": "read"}, "write (code, issues, pulls)"},
|
|
{"none", map[string]string{"repo.code": "write", "repo.wiki": "read"}, "custom"},
|
|
{"none", map[string]string{"repo.ext_issues": "read"}, "none"},
|
|
} {
|
|
require.Equal(t, tc.want, Team{Permission: tc.permission, Units: tc.units}.Access(), "%v", tc.units)
|
|
}
|
|
}
|
|
|
|
func TestTeamFindSearchesTheOrganization(t *testing.T) {
|
|
var queries []string
|
|
client := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path == "/api/v1/orgs/nope/teams/search" {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
return
|
|
}
|
|
require.Equal(t, "/api/v1/orgs/acme/teams/search", r.URL.Path)
|
|
queries = append(queries, r.URL.Query().Get("q"))
|
|
if r.URL.Query().Get("page") != "1" {
|
|
_, _ = w.Write([]byte(`{"ok":true,"data":[]}`))
|
|
return
|
|
}
|
|
_, _ = w.Write([]byte(`{"ok":true,"data":[{"id":2,"name":"dev"},{"id":3,"name":"Dev"},{"id":4,"name":"devops"}]}`))
|
|
}))
|
|
service := Teams{Client: client}
|
|
team, err := service.Find(context.Background(), "acme", "devops")
|
|
require.NoError(t, err)
|
|
require.Equal(t, Team{ID: 4, Name: "devops", Organization: "acme", Units: map[string]string{}}, team)
|
|
team, err = service.Find(context.Background(), "acme", "3")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "Dev", team.Name)
|
|
_, err = service.Find(context.Background(), "acme", "DEV")
|
|
require.EqualError(t, err, `team "DEV" is ambiguous in acme (IDs 2, 3); pass an ID`)
|
|
_, err = service.Find(context.Background(), "nope", "dev")
|
|
require.EqualError(t, err, `organization "nope" not found`)
|
|
require.Equal(t, []string{"devops", "devops", "", "", "DEV", "DEV"}, queries)
|
|
}
|