// Copyright 2026 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package app import ( "context" "errors" "net/http" "strings" "testing" "gitea.dev/tea/internal/clierr" "github.com/stretchr/testify/require" ) // syncGit answers the Git commands of a local repo sync in a clone whose origin is url, with // experiment checked out and main one commit behind origin. type syncGit struct { url, inside string calls []string } func (g *syncGit) Output(_ context.Context, _ string, _ bool, args ...string) (string, error) { for len(args) > 2 && args[0] == "-c" { args = args[2:] } call := strings.Join(args, " ") g.calls = append(g.calls, call) switch call { case "rev-parse --is-inside-work-tree": if g.inside == "" { return "", errors.New("not a git repository") } return g.inside, nil case "remote get-url origin": return g.url, nil case "symbolic-ref --quiet HEAD": return "refs/heads/experiment", nil case "rev-parse --verify --quiet refs/heads/main^{commit}": return "1111111aaaa", nil case "rev-parse --verify FETCH_HEAD^{commit}": return "2222222bbbb", nil } return "", nil } func TestRepoSyncFastForwardsWithoutSwitchingBranches(t *testing.T) { server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { t.Errorf("unexpected request %s %s", r.Method, r.URL.Path) })) defer server.Close() deps, out, diagnostics := repoTTYDeps(t, server.URL) runner := &syncGit{url: server.URL + "/alice/project.git", inside: "true"} deps.Git = runner require.NoError(t, Execute(context.Background(), deps, []string{"repo", "sync", "-R", "alice/project", "--remote", "origin", "-b", "main"})) require.Empty(t, out.String()) require.Equal(t, "✓ Synced main from origin/main (fast-forward 1111111..2222222)\n", diagnostics.String()) require.Contains(t, runner.calls, "update-ref -m tea repo sync refs/heads/main 2222222bbbb 1111111aaaa") for _, call := range runner.calls { require.False(t, strings.HasPrefix(call, "switch") || strings.HasPrefix(call, "checkout") || strings.HasPrefix(call, "merge "), call) } } func TestRepoSyncOutsideACloneExplainsWhatItNeeds(t *testing.T) { server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { t.Errorf("unexpected request %s %s", r.Method, r.URL.Path) })) defer server.Close() deps, _, _ := serverDeps(t, server.URL) deps.Git = &syncGit{} err := Execute(context.Background(), deps, []string{"repo", "sync", "-R", "alice/project", "-b", "main"}) require.Equal(t, clierr.Usage, clierr.ExitCode(err), "%v", err) require.ErrorContains(t, err, "repo sync must run inside a Git clone of alice/project") }