mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 19:11:25 +01:00
Drop additions that the UX plan does not ask for: - the interactive label edit form; label edit now requires a flag - label clone and the skip mode of label creation it needed - repo rename rewriting the Git remote or tea.repository; it now warns that the remote still points to the old name - the extra "source" remote on clones of pull mirrors - run artifact download, which duplicates run download --zip - 256-color and 24-bit label swatches; colors stay ANSI 16 - config list --show-source (config get keeps it) - the membership and access GETs before team member/repo add and remove; Gitea's PUT and DELETE are idempotent label edit/delete keep --by-name: it is the only way to address a label whose name is a number that is also another label's ID. Signed-off-by: bircni <bircni@icloud.com>
174 lines
8.8 KiB
Go
174 lines
8.8 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package app
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.dev/tea/internal/git"
|
|
"gitea.dev/tea/internal/settings"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestCloneValidatesRemoteBeforeGitAndReturnsJSON(t *testing.T) {
|
|
remote := ""
|
|
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, "/api/v1/repos/alice/project", r.URL.Path)
|
|
_, _ = fmt.Fprintf(w, `{"id":7,"full_name":"alice/project","clone_url":%q}`, remote)
|
|
}))
|
|
defer server.Close()
|
|
deps, out, diagnostics := serverDeps(t, server.URL)
|
|
prefix := credentialCall(t, deps, server.URL, "alice") + " "
|
|
runner := &fakeGit{results: map[string]string{prefix + "clone -- " + server.URL + "/alice/project.git destination": ""}}
|
|
deps.Git = runner
|
|
args := []string{"repo", "clone", "alice/project", "destination", "--no-input", "--json=id,full_name"}
|
|
parsed, err := url.Parse(server.URL)
|
|
require.NoError(t, err)
|
|
for _, invalid := range []string{"ssh://git@" + parsed.Hostname() + "/alice/project.git", "https://unrelated.example.org/alice/project.git", server.URL + "/bob/project.git", server.URL + "/alice/different.git"} {
|
|
remote = invalid
|
|
require.ErrorContains(t, Execute(context.Background(), deps, args), "clone URL does not match")
|
|
require.Empty(t, runner.calls)
|
|
require.Empty(t, out.String())
|
|
}
|
|
remote = server.URL + "/alice/project.git"
|
|
require.NoError(t, Execute(context.Background(), deps, args))
|
|
require.Equal(t, []string{prefix + "clone -- " + remote + " destination"}, runner.calls)
|
|
require.False(t, runner.prompt)
|
|
require.JSONEq(t, `{"id":7,"full_name":"alice/project"}`, out.String())
|
|
require.Empty(t, diagnostics.String())
|
|
out.Reset()
|
|
runner.calls = nil
|
|
runner.results[prefix+"clone --depth 2 -- "+remote+" destination"] = ""
|
|
require.NoError(t, Execute(context.Background(), deps, append(args, "--depth", "2")))
|
|
require.Equal(t, []string{prefix + "clone --depth 2 -- " + remote + " destination"}, runner.calls)
|
|
runner.calls = nil
|
|
require.ErrorContains(t, Execute(context.Background(), deps, append(args, "--depth", "-1")), "--depth must be nonnegative")
|
|
require.Empty(t, runner.calls)
|
|
}
|
|
|
|
func TestSSHCloneUsesSelectedAccountKeyWithoutPersistingIt(t *testing.T) {
|
|
remote := ""
|
|
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
_, _ = fmt.Fprintf(w, `{"id":7,"full_name":"alice/project","ssh_url":%q}`, remote)
|
|
}))
|
|
defer server.Close()
|
|
parsed, err := url.Parse(server.URL)
|
|
require.NoError(t, err)
|
|
remote = "git@" + parsed.Hostname() + ":alice/project.git"
|
|
deps, _, _ := serverDeps(t, server.URL)
|
|
configureHost(t, deps, server.URL, settings.Host{User: "alice", GitProtocol: "ssh", Users: map[string]settings.Account{
|
|
"alice": {AuthMethod: "token", CredentialStore: "file", SSHKey: "/keys/alice key"},
|
|
"bob": {AuthMethod: "token", CredentialStore: "file", SSHKey: "/keys/bob"},
|
|
}})
|
|
sshArgs, err := git.SSHArguments("/keys/alice key", false)
|
|
require.NoError(t, err)
|
|
call := strings.Join(append(sshArgs, "clone", "--", remote, "destination"), " ")
|
|
runner := &fakeGit{results: map[string]string{call: ""}}
|
|
deps.Git = runner
|
|
require.NoError(t, Execute(context.Background(), deps, []string{"repo", "clone", "alice/project", "destination", "--no-input"}))
|
|
require.Equal(t, []string{call}, runner.calls)
|
|
require.False(t, runner.prompt)
|
|
require.NoError(t, Execute(context.Background(), deps, []string{"config", "set", "ssh_agent", "true", "--host", server.URL, "--user", "alice"}))
|
|
runner.calls = nil
|
|
call = "clone -- " + remote + " destination"
|
|
runner.results[call] = ""
|
|
require.NoError(t, Execute(context.Background(), deps, []string{"repo", "clone", "alice/project", "destination", "--no-input"}))
|
|
require.Equal(t, []string{call}, runner.calls)
|
|
}
|
|
|
|
func TestCloneAddsUpstreamRemoteForForks(t *testing.T) {
|
|
remote, parent := "", ""
|
|
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, "/api/v1/repos/bob/project", r.URL.Path)
|
|
_, _ = fmt.Fprintf(w, `{"id":8,"name":"project","full_name":"bob/project","fork":true,"clone_url":%q,"parent":{"id":7,"full_name":"alice/project","default_branch":"main","clone_url":%q}}`, remote, parent)
|
|
}))
|
|
defer server.Close()
|
|
deps, out, _ := serverDeps(t, server.URL)
|
|
prefix := credentialCall(t, deps, server.URL, "alice") + " "
|
|
remote = server.URL + "/bob/project.git"
|
|
parent = server.URL + "/alice/project.git"
|
|
runner := &fakeGit{results: map[string]string{
|
|
prefix + "clone -- " + remote + " destination": "",
|
|
"remote add -- upstream " + parent: "",
|
|
"config branch.main.remote upstream": "",
|
|
"config branch.main.merge refs/heads/main": "",
|
|
"config branch.main.pushRemote origin": "",
|
|
}}
|
|
deps.Git = runner
|
|
cwd := t.TempDir()
|
|
require.NoError(t, Execute(context.Background(), deps, []string{"repo", "clone", "bob/project", "destination", "--cwd", cwd, "--no-input", "--json=id,full_name,fork"}))
|
|
require.Equal(t, []string{
|
|
prefix + "clone -- " + remote + " destination",
|
|
"remote add -- upstream " + parent,
|
|
"config branch.main.remote upstream",
|
|
"config branch.main.merge refs/heads/main",
|
|
"config branch.main.pushRemote origin",
|
|
}, runner.calls)
|
|
clone := filepath.Join(cwd, "destination")
|
|
require.Equal(t, []string{cwd, clone, clone, clone, clone}, runner.dirs)
|
|
require.JSONEq(t, `{"id":8,"full_name":"bob/project","fork":true}`, out.String())
|
|
}
|
|
|
|
func TestSSHCloneTrustsAPIEndpointUnlessSSHHostIsConfigured(t *testing.T) {
|
|
remote := "ssh://git@ssh.example.test:2222/alice/project.git"
|
|
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
_, _ = fmt.Fprintf(w, `{"id":7,"full_name":"alice/project","ssh_url":%q}`, remote)
|
|
}))
|
|
defer server.Close()
|
|
deps, _, _ := serverDeps(t, server.URL)
|
|
configure := func(sshHost string) {
|
|
configureHost(t, deps, server.URL, settings.Host{User: "alice", GitProtocol: "ssh", SSHHost: sshHost, Users: tokenAccounts(settings.StoreFile, "alice")})
|
|
}
|
|
sshArgs, err := git.SSHArguments("", false)
|
|
require.NoError(t, err)
|
|
call := strings.Join(append(sshArgs, "clone", "--", remote, "destination"), " ")
|
|
runner := &fakeGit{results: map[string]string{call: ""}}
|
|
deps.Git = runner
|
|
args := []string{"repo", "clone", "alice/project", "destination", "--no-input"}
|
|
configure("")
|
|
require.NoError(t, Execute(context.Background(), deps, args))
|
|
require.Equal(t, []string{call}, runner.calls)
|
|
runner.calls = nil
|
|
configure("git.example.test")
|
|
require.ErrorContains(t, Execute(context.Background(), deps, args), "clone URL does not match")
|
|
require.Empty(t, runner.calls)
|
|
configure("ssh.example.test:2222")
|
|
require.NoError(t, Execute(context.Background(), deps, args))
|
|
require.Equal(t, []string{call}, runner.calls)
|
|
runner.calls = nil
|
|
remote = "ssh://git@ssh.example.test:2222/alice/other.git"
|
|
configure("")
|
|
require.ErrorContains(t, Execute(context.Background(), deps, args), "clone URL does not match")
|
|
require.Empty(t, runner.calls)
|
|
}
|
|
|
|
func TestClonePassesGitFlagsAndNamesTheUpstreamRemote(t *testing.T) {
|
|
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
_, _ = fmt.Fprintf(w, `{"name":"project","full_name":"bob/project","fork":true,"clone_url":"http://%[1]s/bob/project.git","parent":{"full_name":"alice/project","default_branch":"main","clone_url":"http://%[1]s/alice/project.git"}}`, r.Host)
|
|
}))
|
|
defer server.Close()
|
|
deps, _, diagnostics := repoTTYDeps(t, server.URL)
|
|
clone := credentialCall(t, deps, server.URL, "alice", "clone", "--branch", "dev", "--", server.URL+"/bob/project.git")
|
|
parent := server.URL + "/alice/project.git"
|
|
runner := &fakeGit{results: map[string]string{
|
|
clone: "", "remote add -- parent " + parent: "", "config branch.main.remote parent": "",
|
|
"config branch.main.merge refs/heads/main": "", "config branch.main.pushRemote origin": "",
|
|
}}
|
|
deps.Git = runner
|
|
require.NoError(t, Execute(context.Background(), deps, []string{"repo", "clone", "bob/project", "-u", "parent", "--", "--branch", "dev"}))
|
|
require.Equal(t, []string{clone, "remote add -- parent " + parent, "config branch.main.remote parent", "config branch.main.merge refs/heads/main", "config branch.main.pushRemote origin"}, runner.calls)
|
|
require.Equal(t, "✓ Cloned bob/project into project\n✓ Added remote parent for alice/project; tea commands in the clone target it (change with tea repo set-default)\n", diagnostics.String())
|
|
runner.calls = nil
|
|
require.NoError(t, Execute(context.Background(), deps, []string{"repo", "clone", "bob/project", "--no-upstream", "--", "--branch", "dev"}))
|
|
require.Equal(t, []string{clone}, runner.calls)
|
|
require.ErrorContains(t, Execute(context.Background(), deps, []string{"repo", "clone", "bob/project", "a", "b"}), "too many arguments")
|
|
}
|