diff options
author | John Cai <jcai@gitlab.com> | 2022-07-06 18:33:08 +0300 |
---|---|---|
committer | John Cai <jcai@gitlab.com> | 2022-07-06 18:33:08 +0300 |
commit | 6944c95243e2b6ed8f783ae903cb9b03ad50e0f9 (patch) | |
tree | 3218dcc1d7f632a7823f51cccbe5441ad9c3c1f9 | |
parent | 1f20b94a3862bf070794390d40b6c91f80648f8f (diff) | |
parent | 353927b8f789876c8a6fc69fece765db7b20d1af (diff) |
Merge branch 'pks-gittest-drop-worktrees' into 'master'
gittest: Convert most tests that use worktrees
See merge request gitlab-org/gitaly!4666
-rw-r--r-- | internal/git/gittest/commit.go | 46 | ||||
-rw-r--r-- | internal/git/gittest/commit_test.go | 31 | ||||
-rw-r--r-- | internal/git/gittest/ref.go | 11 | ||||
-rw-r--r-- | internal/git/gittest/ref_test.go | 17 | ||||
-rw-r--r-- | internal/git/gittest/repo.go | 26 | ||||
-rw-r--r-- | internal/gitaly/service/diff/raw_test.go | 141 | ||||
-rw-r--r-- | internal/gitaly/service/operations/rebase_test.go | 289 | ||||
-rw-r--r-- | internal/gitaly/service/operations/squash_test.go | 58 | ||||
-rw-r--r-- | internal/gitaly/service/ref/list_refs_test.go | 64 | ||||
-rw-r--r-- | internal/gitaly/service/repository/archive_test.go | 127 | ||||
-rw-r--r-- | internal/gitaly/service/repository/search_files_test.go | 38 | ||||
-rw-r--r-- | internal/gitaly/service/repository/testhelper_test.go | 10 | ||||
-rw-r--r-- | internal/gitaly/service/wiki/find_page_test.go | 23 | ||||
-rw-r--r-- | internal/gitaly/service/wiki/testhelper_test.go | 8 |
14 files changed, 513 insertions, 376 deletions
diff --git a/internal/git/gittest/commit.go b/internal/git/gittest/commit.go index a3cb76864..d0a3f9a01 100644 --- a/internal/git/gittest/commit.go +++ b/internal/git/gittest/commit.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" "testing" + "time" "github.com/stretchr/testify/require" "gitlab.com/gitlab-org/gitaly/v15/internal/git" @@ -22,7 +23,10 @@ const ( type writeCommitConfig struct { branch string parents []git.ObjectID + authorDate time.Time + authorName string committerName string + committerDate time.Time message string treeEntries []TreeEntry treeID git.ObjectID @@ -77,6 +81,20 @@ func WithTree(treeID git.ObjectID) WriteCommitOption { } } +// WithAuthorName is an option for WriteCommit which will set the author name. +func WithAuthorName(name string) WriteCommitOption { + return func(cfg *writeCommitConfig) { + cfg.authorName = name + } +} + +// WithAuthorDate is an option for WriteCommit which will set the author date. +func WithAuthorDate(date time.Time) WriteCommitOption { + return func(cfg *writeCommitConfig) { + cfg.authorDate = date + } +} + // WithCommitterName is an option for WriteCommit which will set the committer name. func WithCommitterName(name string) WriteCommitOption { return func(cfg *writeCommitConfig) { @@ -84,6 +102,13 @@ func WithCommitterName(name string) WriteCommitOption { } } +// WithCommitterDate is an option for WriteCommit which will set the committer date. +func WithCommitterDate(date time.Time) WriteCommitOption { + return func(cfg *writeCommitConfig) { + cfg.committerDate = date + } +} + // WithAlternateObjectDirectory will cause the commit to be written into the given alternate object // directory. This can either be an absolute path or a relative path. In the latter case the path // is considered to be relative to the repository path. @@ -130,10 +155,22 @@ func WriteCommit(t testing.TB, cfg config.Cfg, repoPath string, opts ...WriteCom tree = parents[0].String() + "^{tree}" } + if writeCommitConfig.authorName == "" { + writeCommitConfig.authorName = committerName + } + + if writeCommitConfig.authorDate.IsZero() { + writeCommitConfig.authorDate = time.Date(2019, 11, 3, 11, 27, 59, 0, time.FixedZone("UTC+1", 1*60*60)) + } + if writeCommitConfig.committerName == "" { writeCommitConfig.committerName = committerName } + if writeCommitConfig.committerDate.IsZero() { + writeCommitConfig.committerDate = time.Date(2019, 11, 3, 11, 27, 59, 0, time.FixedZone("UTC+1", 1*60*60)) + } + // Use 'commit-tree' instead of 'commit' because we are in a bare // repository. What we do here is the same as "commit -m message // --allow-empty". @@ -156,6 +193,15 @@ func WriteCommit(t testing.TB, cfg config.Cfg, repoPath string, opts ...WriteCom ) } + env = append(env, + fmt.Sprintf("GIT_AUTHOR_DATE=%d %s", writeCommitConfig.authorDate.Unix(), writeCommitConfig.authorDate.Format("-0700")), + fmt.Sprintf("GIT_AUTHOR_NAME=%s", writeCommitConfig.authorName), + fmt.Sprintf("GIT_AUTHOR_EMAIL=%s", committerEmail), + fmt.Sprintf("GIT_COMMITTER_DATE=%d %s", writeCommitConfig.committerDate.Unix(), writeCommitConfig.committerDate.Format("-0700")), + fmt.Sprintf("GIT_COMMITTER_NAME=%s", writeCommitConfig.committerName), + fmt.Sprintf("GIT_COMMITTER_EMAIL=%s", committerEmail), + ) + for _, parent := range parents { commitArgs = append(commitArgs, "-p", parent.String()) } diff --git a/internal/git/gittest/commit_test.go b/internal/git/gittest/commit_test.go index 452fa8eed..c88d2af45 100644 --- a/internal/git/gittest/commit_test.go +++ b/internal/git/gittest/commit_test.go @@ -3,6 +3,7 @@ package gittest import ( "strings" "testing" + "time" "github.com/stretchr/testify/require" "gitlab.com/gitlab-org/gitaly/v15/internal/git" @@ -59,6 +60,36 @@ func TestWriteCommit(t *testing.T) { }, "\n"), }, { + desc: "with author", + opts: []WriteCommitOption{ + WithAuthorName("John Doe"), + WithAuthorDate(time.Date(2005, 4, 7, 15, 13, 13, 0, time.FixedZone("UTC-7", -7*60*60))), + }, + expectedCommit: strings.Join([]string{ + "tree 91639b9835ff541f312fd2735f639a50bf35d472", + "parent " + defaultParentID, + "author John Doe <scrooge@mcduck.com> 1112911993 -0700", + "committer " + defaultCommitter, + "", + "message", + }, "\n"), + }, + { + desc: "with commiter", + opts: []WriteCommitOption{ + WithCommitterName("John Doe"), + WithCommitterDate(time.Date(2005, 4, 7, 15, 13, 13, 0, time.FixedZone("UTC-7", -7*60*60))), + }, + expectedCommit: strings.Join([]string{ + "tree 91639b9835ff541f312fd2735f639a50bf35d472", + "parent " + defaultParentID, + "author Scrooge McDuck <scrooge@mcduck.com> 1572776879 +0100", + "committer John Doe <scrooge@mcduck.com> 1112911993 -0700", + "", + "message", + }, "\n"), + }, + { desc: "with no parents", opts: []WriteCommitOption{ WithParents(), diff --git a/internal/git/gittest/ref.go b/internal/git/gittest/ref.go index aedbcf3b6..bb1e5a104 100644 --- a/internal/git/gittest/ref.go +++ b/internal/git/gittest/ref.go @@ -3,11 +3,22 @@ package gittest import ( "testing" + "github.com/stretchr/testify/require" "gitlab.com/gitlab-org/gitaly/v15/internal/git" "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/config" + "gitlab.com/gitlab-org/gitaly/v15/internal/helper/text" ) // WriteRef writes a reference into the repository pointing to the given object ID. func WriteRef(t testing.TB, cfg config.Cfg, repoPath string, ref git.ReferenceName, oid git.ObjectID) { Exec(t, cfg, "-C", repoPath, "update-ref", ref.String(), oid.String()) } + +// ResolveRevision resolves the revision to an object ID. +func ResolveRevision(t testing.TB, cfg config.Cfg, repoPath string, revision string) git.ObjectID { + t.Helper() + output := Exec(t, cfg, "-C", repoPath, "rev-parse", "--verify", revision) + objectID, err := git.NewObjectIDFromHex(text.ChompBytes(output)) + require.NoError(t, err) + return objectID +} diff --git a/internal/git/gittest/ref_test.go b/internal/git/gittest/ref_test.go new file mode 100644 index 000000000..eed9287ee --- /dev/null +++ b/internal/git/gittest/ref_test.go @@ -0,0 +1,17 @@ +package gittest + +import ( + "testing" + + "github.com/stretchr/testify/require" + "gitlab.com/gitlab-org/gitaly/v15/internal/git" +) + +func TestResolveRevision(t *testing.T) { + cfg, _, repoPath := setup(t) + + require.Equal(t, + git.ObjectID("1e292f8fedd741b75372e19097c76d327140c312"), + ResolveRevision(t, cfg, repoPath, "refs/heads/master"), + ) +} diff --git a/internal/git/gittest/repo.go b/internal/git/gittest/repo.go index 5aa3e5df1..c74412570 100644 --- a/internal/git/gittest/repo.go +++ b/internal/git/gittest/repo.go @@ -237,9 +237,6 @@ func RewrittenRepository(ctx context.Context, t testing.TB, cfg config.Cfg, repo // InitRepoOpts contains options for InitRepo. type InitRepoOpts struct { - // WithWorktree determines whether the resulting Git repository should have a worktree or - // not. - WithWorktree bool // WithRelativePath determines the relative path of this repository. WithRelativePath string } @@ -256,22 +253,16 @@ func InitRepo(t testing.TB, cfg config.Cfg, storage config.Storage, opts ...Init relativePath := opt.WithRelativePath if relativePath == "" { - relativePath = NewRepositoryName(t, !opt.WithWorktree) + relativePath = NewRepositoryName(t, true) } repoPath := filepath.Join(storage.Path, relativePath) - args := []string{"init"} - if !opt.WithWorktree { - args = append(args, "--bare") - } + args := []string{"init", "--bare"} Exec(t, cfg, append(args, repoPath)...) repo := InitRepoDir(t, storage.Path, relativePath) repo.StorageName = storage.Name - if opt.WithWorktree { - repo.RelativePath = filepath.Join(repo.RelativePath, ".git") - } t.Cleanup(func() { require.NoError(t, os.RemoveAll(repoPath)) }) @@ -283,9 +274,6 @@ type CloneRepoOpts struct { // RelativePath determines the relative path of newly created Git repository. If unset, the // relative path is computed via NewRepositoryName. RelativePath string - // WithWorktree determines whether the resulting Git repository should have a worktree or - // not. - WithWorktree bool // SourceRepo determines the name of the source repository which shall be cloned. The source // repository is assumed to be relative to "_build/testrepos". If unset, defaults to // "gitlab-test.git". @@ -304,7 +292,7 @@ func CloneRepo(t testing.TB, cfg config.Cfg, storage config.Storage, opts ...Clo relativePath := opt.RelativePath if relativePath == "" { - relativePath = NewRepositoryName(t, !opt.WithWorktree) + relativePath = NewRepositoryName(t, true) } sourceRepo := opt.SourceRepo @@ -315,13 +303,7 @@ func CloneRepo(t testing.TB, cfg config.Cfg, storage config.Storage, opts ...Clo repo := InitRepoDir(t, storage.Path, relativePath) repo.StorageName = storage.Name - args := []string{"clone", "--no-hardlinks", "--dissociate"} - if !opt.WithWorktree { - args = append(args, "--bare") - } else { - // For non-bare repos the relative path is the .git folder inside the path - repo.RelativePath = filepath.Join(relativePath, ".git") - } + args := []string{"clone", "--no-hardlinks", "--dissociate", "--bare"} absolutePath := filepath.Join(storage.Path, relativePath) Exec(t, cfg, append(args, testRepositoryPath(t, sourceRepo), absolutePath)...) diff --git a/internal/gitaly/service/diff/raw_test.go b/internal/gitaly/service/diff/raw_test.go index bf6f7b4d9..eda44e902 100644 --- a/internal/gitaly/service/diff/raw_test.go +++ b/internal/gitaly/service/diff/raw_test.go @@ -1,56 +1,80 @@ package diff import ( - "fmt" "io" "regexp" "testing" + "time" "github.com/stretchr/testify/require" "gitlab.com/gitlab-org/gitaly/v15/internal/git/gittest" + "gitlab.com/gitlab-org/gitaly/v15/internal/git2go" + "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/config" "gitlab.com/gitlab-org/gitaly/v15/internal/testhelper" + "gitlab.com/gitlab-org/gitaly/v15/internal/testhelper/testcfg" "gitlab.com/gitlab-org/gitaly/v15/proto/go/gitalypb" "gitlab.com/gitlab-org/gitaly/v15/streamio" "google.golang.org/grpc/codes" ) -func TestSuccessfulRawDiffRequest(t *testing.T) { +func TestRawDiff_successful(t *testing.T) { + t.Parallel() + ctx := testhelper.Context(t) - cfg, repo, repoPath, client := setupDiffService(ctx, t) + cfg, repoProto, repoPath, client := setupDiffService(ctx, t) + testcfg.BuildGitalyGit2Go(t, cfg) - rightCommit := "e395f646b1499e8e0279445fc99a0596a65fab7e" - leftCommit := "8a0f2ee90d940bfb0ba1e14e8214b0649056e4ab" - rpcRequest := &gitalypb.RawDiffRequest{Repository: repo, RightCommitId: rightCommit, LeftCommitId: leftCommit} + gitCmdFactory := gittest.NewCommandFactory(t, cfg) + locator := config.NewLocator(cfg) + git2goExecutor := git2go.NewExecutor(cfg, gitCmdFactory, locator) - c, err := client.RawDiff(ctx, rpcRequest) - require.NoError(t, err) + leftCommit := "57290e673a4c87f51294f5216672cbc58d485d25" + rightCommit := "e395f646b1499e8e0279445fc99a0596a65fab7e" - _, sandboxRepoPath := gittest.CloneRepo(t, cfg, cfg.Storages[0], gittest.CloneRepoOpts{ - WithWorktree: true, + stream, err := client.RawDiff(ctx, &gitalypb.RawDiffRequest{ + Repository: repoProto, + LeftCommitId: leftCommit, + RightCommitId: rightCommit, }) + require.NoError(t, err) - reader := streamio.NewReader(func() ([]byte, error) { - response, err := c.Recv() + rawDiff, err := io.ReadAll(streamio.NewReader(func() ([]byte, error) { + response, err := stream.Recv() return response.GetData(), err - }) + })) + require.NoError(t, err) - committerName := "Scrooge McDuck" - committerEmail := "scrooge@mcduck.com" - gittest.Exec(t, cfg, "-C", sandboxRepoPath, "reset", "--hard", leftCommit) + signature := git2go.Signature{ + Name: "Scrooge McDuck", + Email: "scrooge@mcduck.com", + When: time.Unix(12345, 0), + } - gittest.ExecOpts(t, cfg, gittest.ExecConfig{Stdin: reader}, "-C", sandboxRepoPath, "apply") - gittest.Exec(t, cfg, "-C", sandboxRepoPath, "add", ".") - gittest.Exec(t, cfg, "-C", sandboxRepoPath, - "-c", fmt.Sprintf("user.name=%s", committerName), - "-c", fmt.Sprintf("user.email=%s", committerEmail), - "commit", "-m", "Applying received raw diff") + // Now that we have read the patch in we verify that it indeed round-trips to the same tree + // as the right commit is referring to by reapplying the diff on top of the left commit. + patchedCommitID, err := git2goExecutor.Apply(ctx, gittest.RewrittenRepository(ctx, t, cfg, repoProto), git2go.ApplyParams{ + Repository: repoPath, + Committer: signature, + ParentCommit: leftCommit, + Patches: git2go.NewSlicePatchIterator([]git2go.Patch{{ + Author: signature, + Message: "Applying received raw diff", + Diff: rawDiff, + }}), + }) + require.NoError(t, err) - expectedTreeStructure := gittest.Exec(t, cfg, "-C", repoPath, "ls-tree", "-r", rightCommit) - actualTreeStructure := gittest.Exec(t, cfg, "-C", sandboxRepoPath, "ls-tree", "-r", "HEAD") - require.Equal(t, expectedTreeStructure, actualTreeStructure) + // Peel both right commit and patched commit to their trees and assert that they refer to + // the same one. + require.Equal(t, + gittest.Exec(t, cfg, "-C", repoPath, "rev-parse", rightCommit+"^{tree}"), + gittest.Exec(t, cfg, "-C", repoPath, "rev-parse", patchedCommitID.String()+"^{tree}"), + ) } -func TestFailedRawDiffRequestDueToValidations(t *testing.T) { +func TestRawDiff_inputValidation(t *testing.T) { + t.Parallel() + ctx := testhelper.Context(t) _, repo, _, client := setupDiffService(ctx, t) @@ -96,35 +120,64 @@ func TestFailedRawDiffRequestDueToValidations(t *testing.T) { } } -func TestSuccessfulRawPatchRequest(t *testing.T) { +func TestRawPatch_successful(t *testing.T) { + t.Parallel() + ctx := testhelper.Context(t) - cfg, repo, repoPath, client := setupDiffService(ctx, t) + cfg, repoProto, repoPath, client := setupDiffService(ctx, t) + testcfg.BuildGitalyGit2Go(t, cfg) + + gitCmdFactory := gittest.NewCommandFactory(t, cfg) + locator := config.NewLocator(cfg) + git2goExecutor := git2go.NewExecutor(cfg, gitCmdFactory, locator) rightCommit := "e395f646b1499e8e0279445fc99a0596a65fab7e" leftCommit := "8a0f2ee90d940bfb0ba1e14e8214b0649056e4ab" - rpcRequest := &gitalypb.RawPatchRequest{Repository: repo, RightCommitId: rightCommit, LeftCommitId: leftCommit} - c, err := client.RawPatch(ctx, rpcRequest) + stream, err := client.RawPatch(ctx, &gitalypb.RawPatchRequest{ + Repository: repoProto, + LeftCommitId: leftCommit, + RightCommitId: rightCommit, + }) require.NoError(t, err) - reader := streamio.NewReader(func() ([]byte, error) { - response, err := c.Recv() + rawPatch, err := io.ReadAll(streamio.NewReader(func() ([]byte, error) { + response, err := stream.Recv() return response.GetData(), err - }) + })) + require.NoError(t, err) - _, sandboxRepoPath := gittest.CloneRepo(t, cfg, cfg.Storages[0], gittest.CloneRepoOpts{ - WithWorktree: true, - }) + signature := git2go.Signature{ + Name: "Scrooge McDuck", + Email: "scrooge@mcduck.com", + When: time.Unix(12345, 0), + } - gittest.Exec(t, cfg, "-C", sandboxRepoPath, "reset", "--hard", leftCommit) - gittest.ExecOpts(t, cfg, gittest.ExecConfig{Stdin: reader}, "-C", sandboxRepoPath, "am") + // Now that we have read the patch in we verify that it indeed round-trips to the same tree + // as the right commit is referring to by reapplying the diff on top of the left commit. + patchedCommitID, err := git2goExecutor.Apply(ctx, gittest.RewrittenRepository(ctx, t, cfg, repoProto), git2go.ApplyParams{ + Repository: repoPath, + Committer: signature, + ParentCommit: leftCommit, + Patches: git2go.NewSlicePatchIterator([]git2go.Patch{{ + Author: signature, + Message: "Applying received raw patch", + Diff: rawPatch, + }}), + }) + require.NoError(t, err) - expectedTreeStructure := gittest.Exec(t, cfg, "-C", repoPath, "ls-tree", "-r", rightCommit) - actualTreeStructure := gittest.Exec(t, cfg, "-C", sandboxRepoPath, "ls-tree", "-r", "HEAD") - require.Equal(t, expectedTreeStructure, actualTreeStructure) + // Peel both right commit and patched commit to their trees and assert that they refer to + // the same one. + require.Equal(t, + gittest.Exec(t, cfg, "-C", repoPath, "rev-parse", rightCommit+"^{tree}"), + gittest.Exec(t, cfg, "-C", repoPath, "rev-parse", patchedCommitID.String()+"^{tree}"), + ) } -func TestFailedRawPatchRequestDueToValidations(t *testing.T) { +func TestRawPatch_inputValidation(t *testing.T) { + t.Parallel() + ctx := testhelper.Context(t) _, repo, _, client := setupDiffService(ctx, t) @@ -170,7 +223,9 @@ func TestFailedRawPatchRequestDueToValidations(t *testing.T) { } } -func TestRawPatchContainsGitLabSignature(t *testing.T) { +func TestRawPatch_gitlabSignature(t *testing.T) { + t.Parallel() + ctx := testhelper.Context(t) _, repo, _, client := setupDiffService(ctx, t) diff --git a/internal/gitaly/service/operations/rebase_test.go b/internal/gitaly/service/operations/rebase_test.go index b1d1f5ce8..25f813af0 100644 --- a/internal/gitaly/service/operations/rebase_test.go +++ b/internal/gitaly/service/operations/rebase_test.go @@ -5,8 +5,6 @@ import ( "errors" "fmt" "io" - "path/filepath" - "strings" "testing" "time" @@ -14,7 +12,6 @@ import ( "gitlab.com/gitlab-org/gitaly/v15/internal/git" "gitlab.com/gitlab-org/gitaly/v15/internal/git/gittest" "gitlab.com/gitlab-org/gitaly/v15/internal/git/localrepo" - "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/config" "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/transaction" "gitlab.com/gitlab-org/gitaly/v15/internal/helper" "gitlab.com/gitlab-org/gitaly/v15/internal/metadata" @@ -30,8 +27,9 @@ import ( var rebaseBranchName = "many_files" -func TestSuccessfulUserRebaseConfirmableRequest(t *testing.T) { +func TestUserRebaseConfirmable_successful(t *testing.T) { t.Parallel() + ctx := testhelper.Context(t) ctx, cfg, repoProto, repoPath, client := setupOperationsService(t, ctx) @@ -45,7 +43,7 @@ func TestSuccessfulUserRebaseConfirmableRequest(t *testing.T) { Seed: gittest.SeedGitLabTest, }) - branchSha := getBranchSha(t, cfg, repoPath, rebaseBranchName) + branchOID := gittest.ResolveRevision(t, cfg, repoPath, rebaseBranchName) rebaseStream, err := client.UserRebaseConfirmable(ctx) require.NoError(t, err) @@ -53,7 +51,7 @@ func TestSuccessfulUserRebaseConfirmableRequest(t *testing.T) { preReceiveHookOutputPath := gittest.WriteEnvToCustomHook(t, repoPath, "pre-receive") postReceiveHookOutputPath := gittest.WriteEnvToCustomHook(t, repoPath, "post-receive") - headerRequest := buildHeaderRequest(repoProto, gittest.TestUser, "1", rebaseBranchName, branchSha, repoCopyProto, "master") + headerRequest := buildHeaderRequest(repoProto, gittest.TestUser, "1", rebaseBranchName, branchOID, repoCopyProto, "master") headerRequest.GetHeader().GitPushOptions = pushOptions require.NoError(t, rebaseStream.Send(headerRequest), "send header") @@ -75,10 +73,10 @@ func TestSuccessfulUserRebaseConfirmableRequest(t *testing.T) { _, err = repo.ReadCommit(ctx, git.Revision(firstResponse.GetRebaseSha())) require.NoError(t, err) - newBranchSha := getBranchSha(t, cfg, repoPath, rebaseBranchName) + newBranchCommit := gittest.ResolveRevision(t, cfg, repoPath, rebaseBranchName) - require.NotEqual(t, newBranchSha, branchSha) - require.Equal(t, newBranchSha, firstResponse.GetRebaseSha()) + require.NotEqual(t, newBranchCommit, branchOID) + require.Equal(t, newBranchCommit.String(), firstResponse.GetRebaseSha()) require.True(t, secondResponse.GetRebaseApplied(), "the second rebase is applied") @@ -191,7 +189,7 @@ func TestUserRebaseConfirmable_skipEmptyCommits(t *testing.T) { }, rebaseCommit) } -func TestUserRebaseConfirmableTransaction(t *testing.T) { +func TestUserRebaseConfirmable_transaction(t *testing.T) { t.Parallel() ctx := testhelper.Context(t) @@ -249,13 +247,13 @@ func TestUserRebaseConfirmableTransaction(t *testing.T) { ctx = metadata.IncomingToOutgoing(ctx) } - branchSha, err := repo.ResolveRevision(ctx, git.Revision(rebaseBranchName)) + branchCommitID, err := repo.ResolveRevision(ctx, git.Revision(rebaseBranchName)) require.NoError(t, err) rebaseStream, err := client.UserRebaseConfirmable(ctx) require.NoError(t, err) - headerRequest := buildHeaderRequest(repoProto, gittest.TestUser, "1", rebaseBranchName, branchSha.String(), repoProto, "master") + headerRequest := buildHeaderRequest(repoProto, gittest.TestUser, "1", rebaseBranchName, branchCommitID, repoProto, "master") require.NoError(t, rebaseStream.Send(headerRequest)) _, err = rebaseStream.Recv() require.NoError(t, err) @@ -279,7 +277,7 @@ func TestUserRebaseConfirmableTransaction(t *testing.T) { } } -func TestUserRebaseConfirmableStableCommitIDs(t *testing.T) { +func TestUserRebaseConfirmable_stableCommitIDs(t *testing.T) { t.Parallel() ctx := testhelper.Context(t) @@ -292,7 +290,7 @@ func TestUserRebaseConfirmableStableCommitIDs(t *testing.T) { require.NoError(t, err) committerDate := ×tamppb.Timestamp{Seconds: 100000000} - parentSha := getBranchSha(t, cfg, repoPath, "master") + parentSha := gittest.ResolveRevision(t, cfg, repoPath, "master") require.NoError(t, rebaseStream.Send(&gitalypb.UserRebaseConfirmableRequest{ UserRebaseConfirmableRequestPayload: &gitalypb.UserRebaseConfirmableRequest_Header_{ @@ -301,7 +299,7 @@ func TestUserRebaseConfirmableStableCommitIDs(t *testing.T) { User: gittest.TestUser, RebaseId: "1", Branch: []byte(rebaseBranchName), - BranchSha: getBranchSha(t, cfg, repoPath, rebaseBranchName), + BranchSha: gittest.ResolveRevision(t, cfg, repoPath, rebaseBranchName).String(), RemoteRepository: repoProto, RemoteBranch: []byte("master"), Timestamp: committerDate, @@ -330,7 +328,7 @@ func TestUserRebaseConfirmableStableCommitIDs(t *testing.T) { Body: []byte("Add a directory with many files to allow testing of default 1,000 entry limit\n\nFor performance reasons, GitLab will add a file viewer limit and only show\nthe first 1,000 entries in a directory. Having this directory with many\nempty files in the test project will make the test easy.\n"), BodySize: 283, Id: "c52b98024db0d3af0ccb20ed2a3a93a21cfbba87", - ParentIds: []string{parentSha}, + ParentIds: []string{parentSha.String()}, TreeId: "d0305132f880aa0ab4102e56a09cf1343ba34893", Author: &gitalypb.CommitAuthor{ Name: []byte("Drew Blessing"), @@ -349,7 +347,7 @@ func TestUserRebaseConfirmableStableCommitIDs(t *testing.T) { }, commit) } -func TestFailedRebaseUserRebaseConfirmableRequestDueToInvalidHeader(t *testing.T) { +func TestUserRebaseConfirmable_inputValidation(t *testing.T) { t.Parallel() ctx := testhelper.Context(t) @@ -357,7 +355,7 @@ func TestFailedRebaseUserRebaseConfirmableRequestDueToInvalidHeader(t *testing.T repoCopy, _ := gittest.CloneRepo(t, cfg, cfg.Storages[0]) - branchSha := getBranchSha(t, cfg, repoPath, rebaseBranchName) + branchCommitID := gittest.ResolveRevision(t, cfg, repoPath, rebaseBranchName) testCases := []struct { desc string @@ -365,15 +363,15 @@ func TestFailedRebaseUserRebaseConfirmableRequestDueToInvalidHeader(t *testing.T }{ { desc: "empty Repository", - req: buildHeaderRequest(nil, gittest.TestUser, "1", rebaseBranchName, branchSha, repoCopy, "master"), + req: buildHeaderRequest(nil, gittest.TestUser, "1", rebaseBranchName, branchCommitID, repoCopy, "master"), }, { desc: "empty User", - req: buildHeaderRequest(repo, nil, "1", rebaseBranchName, branchSha, repoCopy, "master"), + req: buildHeaderRequest(repo, nil, "1", rebaseBranchName, branchCommitID, repoCopy, "master"), }, { desc: "empty Branch", - req: buildHeaderRequest(repo, gittest.TestUser, "1", "", branchSha, repoCopy, "master"), + req: buildHeaderRequest(repo, gittest.TestUser, "1", "", branchCommitID, repoCopy, "master"), }, { desc: "empty BranchSha", @@ -381,15 +379,15 @@ func TestFailedRebaseUserRebaseConfirmableRequestDueToInvalidHeader(t *testing.T }, { desc: "empty RemoteRepository", - req: buildHeaderRequest(repo, gittest.TestUser, "1", rebaseBranchName, branchSha, nil, "master"), + req: buildHeaderRequest(repo, gittest.TestUser, "1", rebaseBranchName, branchCommitID, nil, "master"), }, { desc: "empty RemoteBranch", - req: buildHeaderRequest(repo, gittest.TestUser, "1", rebaseBranchName, branchSha, repoCopy, ""), + req: buildHeaderRequest(repo, gittest.TestUser, "1", rebaseBranchName, branchCommitID, repoCopy, ""), }, { desc: "invalid branch name", - req: buildHeaderRequest(repo, gittest.TestUser, "1", rebaseBranchName, branchSha, repoCopy, "+dev:master"), + req: buildHeaderRequest(repo, gittest.TestUser, "1", rebaseBranchName, branchCommitID, repoCopy, "+dev:master"), }, } @@ -408,7 +406,7 @@ func TestFailedRebaseUserRebaseConfirmableRequestDueToInvalidHeader(t *testing.T } } -func TestAbortedUserRebaseConfirmable(t *testing.T) { +func TestUserRebaseConfirmable_abortViaClose(t *testing.T) { t.Parallel() ctx := testhelper.Context(t) @@ -431,9 +429,9 @@ func TestAbortedUserRebaseConfirmable(t *testing.T) { testRepo, testRepoPath := gittest.CreateRepository(ctx, t, cfg, createRepoOpts) testRepoCopy, _ := gittest.CreateRepository(ctx, t, cfg, createRepoOpts) - branchSha := getBranchSha(t, cfg, testRepoPath, rebaseBranchName) + branchCommitID := gittest.ResolveRevision(t, cfg, testRepoPath, rebaseBranchName) - headerRequest := buildHeaderRequest(testRepo, gittest.TestUser, fmt.Sprintf("%v", i), rebaseBranchName, branchSha, testRepoCopy, "master") + headerRequest := buildHeaderRequest(testRepo, gittest.TestUser, fmt.Sprintf("%v", i), rebaseBranchName, branchCommitID, testRepoCopy, "master") rebaseStream, err := client.UserRebaseConfirmable(ctx) require.NoError(t, err) @@ -461,13 +459,13 @@ func TestAbortedUserRebaseConfirmable(t *testing.T) { require.Error(t, err) testhelper.RequireGrpcCode(t, err, tc.code) - newBranchSha := getBranchSha(t, cfg, testRepoPath, rebaseBranchName) - require.Equal(t, newBranchSha, branchSha, "branch should not change when the rebase is aborted") + newBranchCommitID := gittest.ResolveRevision(t, cfg, testRepoPath, rebaseBranchName) + require.Equal(t, newBranchCommitID, branchCommitID, "branch should not change when the rebase is aborted") }) } } -func TestFailedUserRebaseConfirmableDueToApplyBeingFalse(t *testing.T) { +func TestUserRebaseConfirmable_abortViaApply(t *testing.T) { t.Parallel() ctx := testhelper.Context(t) @@ -479,12 +477,12 @@ func TestFailedUserRebaseConfirmableDueToApplyBeingFalse(t *testing.T) { Seed: gittest.SeedGitLabTest, }) - branchSha := getBranchSha(t, cfg, repoPath, rebaseBranchName) + branchCommitID := gittest.ResolveRevision(t, cfg, repoPath, rebaseBranchName) rebaseStream, err := client.UserRebaseConfirmable(ctx) require.NoError(t, err) - headerRequest := buildHeaderRequest(repoProto, gittest.TestUser, "1", rebaseBranchName, branchSha, testRepoCopy, "master") + headerRequest := buildHeaderRequest(repoProto, gittest.TestUser, "1", rebaseBranchName, branchCommitID, testRepoCopy, "master") require.NoError(t, rebaseStream.Send(headerRequest), "send header") firstResponse, err := rebaseStream.Recv() @@ -504,18 +502,20 @@ func TestFailedUserRebaseConfirmableDueToApplyBeingFalse(t *testing.T) { _, err = repo.ReadCommit(ctx, git.Revision(firstResponse.GetRebaseSha())) require.Equal(t, localrepo.ErrObjectNotFound, err, "commit should have been discarded") - newBranchSha := getBranchSha(t, cfg, repoPath, rebaseBranchName) - require.Equal(t, branchSha, newBranchSha, "branch should not change when the rebase is not applied") - require.NotEqual(t, newBranchSha, firstResponse.GetRebaseSha(), "branch should not be the sha returned when the rebase is not applied") + newBranchCommitID := gittest.ResolveRevision(t, cfg, repoPath, rebaseBranchName) + require.Equal(t, branchCommitID, newBranchCommitID, "branch should not change when the rebase is not applied") + require.NotEqual(t, newBranchCommitID, firstResponse.GetRebaseSha(), "branch should not be the sha returned when the rebase is not applied") } -func TestFailedUserRebaseConfirmableRequestDueToPreReceiveError(t *testing.T) { +func TestUserRebaseConfirmable_preReceiveError(t *testing.T) { t.Parallel() testhelper.NewFeatureSets(featureflag.UserRebaseConfirmableImprovedErrorHandling). - Run(t, testFailedUserRebaseConfirmableRequestDueToPreReceiveError) + Run(t, testUserRebaseConfirmablePreReceiveError) } -func testFailedUserRebaseConfirmableRequestDueToPreReceiveError(t *testing.T, ctx context.Context) { +func testUserRebaseConfirmablePreReceiveError(t *testing.T, ctx context.Context) { + t.Parallel() + ctx, cfg, repoProto, repoPath, client := setupOperationsService(t, ctx) repo := localrepo.NewTestRepo(t, cfg, repoProto) @@ -523,7 +523,7 @@ func testFailedUserRebaseConfirmableRequestDueToPreReceiveError(t *testing.T, ct Seed: gittest.SeedGitLabTest, }) - branchSha := getBranchSha(t, cfg, repoPath, rebaseBranchName) + branchCommitID := gittest.ResolveRevision(t, cfg, repoPath, rebaseBranchName) hookContent := []byte("#!/bin/sh\necho 'failure'\nexit 1") @@ -534,7 +534,7 @@ func testFailedUserRebaseConfirmableRequestDueToPreReceiveError(t *testing.T, ct rebaseStream, err := client.UserRebaseConfirmable(ctx) require.NoError(t, err) - headerRequest := buildHeaderRequest(repoProto, gittest.TestUser, fmt.Sprintf("%v", i), rebaseBranchName, branchSha, repoCopyProto, "master") + headerRequest := buildHeaderRequest(repoProto, gittest.TestUser, fmt.Sprintf("%v", i), rebaseBranchName, branchCommitID, repoCopyProto, "master") require.NoError(t, rebaseStream.Send(headerRequest), "send header") firstResponse, err := rebaseStream.Recv() @@ -573,9 +573,9 @@ func testFailedUserRebaseConfirmableRequestDueToPreReceiveError(t *testing.T, ct require.NoError(t, err) } - newBranchSha := getBranchSha(t, cfg, repoPath, rebaseBranchName) - require.Equal(t, branchSha, newBranchSha, "branch should not change when the rebase fails due to PreReceiveError") - require.NotEqual(t, newBranchSha, firstResponse.GetRebaseSha(), "branch should not be the sha returned when the rebase fails due to PreReceiveError") + newBranchCommitID := gittest.ResolveRevision(t, cfg, repoPath, rebaseBranchName) + require.Equal(t, branchCommitID, newBranchCommitID, "branch should not change when the rebase fails due to PreReceiveError") + require.NotEqual(t, newBranchCommitID, firstResponse.GetRebaseSha(), "branch should not be the sha returned when the rebase fails due to PreReceiveError") }) } } @@ -587,6 +587,8 @@ func TestUserRebaseConfirmable_gitError(t *testing.T) { } func testFailedUserRebaseConfirmableDueToGitError(t *testing.T, ctx context.Context) { + t.Parallel() + ctx, cfg, repoProto, repoPath, client := setupOperationsService(t, ctx) repoCopyProto, _ := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ @@ -594,8 +596,8 @@ func testFailedUserRebaseConfirmableDueToGitError(t *testing.T, ctx context.Cont }) targetBranch := "rebase-encoding-failure-trigger" - targetBranchCommitID := getBranchSha(t, cfg, repoPath, targetBranch) - sourceBranchCommitID := getBranchSha(t, cfg, repoPath, "master") + targetBranchCommitID := gittest.ResolveRevision(t, cfg, repoPath, targetBranch) + sourceBranchCommitID := gittest.ResolveRevision(t, cfg, repoPath, "master") rebaseStream, err := client.UserRebaseConfirmable(ctx) require.NoError(t, err) @@ -615,8 +617,8 @@ func testFailedUserRebaseConfirmableDueToGitError(t *testing.T, ctx context.Cont []byte("README.md"), }, ConflictingCommitIds: []string{ - sourceBranchCommitID, - targetBranchCommitID, + sourceBranchCommitID.String(), + targetBranchCommitID.String(), }, }, }, @@ -630,138 +632,153 @@ func testFailedUserRebaseConfirmableDueToGitError(t *testing.T, ctx context.Cont require.Equal(t, io.EOF, err) } - newBranchSha := getBranchSha(t, cfg, repoPath, targetBranch) - require.Equal(t, targetBranchCommitID, newBranchSha, "branch should not change when the rebase fails due to GitError") + newBranchCommitID := gittest.ResolveRevision(t, cfg, repoPath, targetBranch) + require.Equal(t, targetBranchCommitID, newBranchCommitID, "branch should not change when the rebase fails due to GitError") } -func getBranchSha(t *testing.T, cfg config.Cfg, repoPath string, branchName string) string { - branchSha := string(gittest.Exec(t, cfg, "-C", repoPath, "rev-parse", branchName)) - return strings.TrimSpace(branchSha) -} - -func TestRebaseRequestWithDeletedFile(t *testing.T) { +func TestUserRebaseConfirmable_deletedFileInLocalRepo(t *testing.T) { t.Parallel() ctx := testhelper.Context(t) - ctx, cfg, repoProto, repoPath, client := setupOperationsService(t, ctx) - gittest.AddWorktree(t, cfg, repoPath, "worktree") - repoPath = filepath.Join(repoPath, "worktree") + ctx, cfg, client := setupOperationsServiceWithoutRepo(t, ctx) - repo := localrepo.NewTestRepo(t, cfg, repoProto) + localRepoProto, localRepoPath := gittest.CreateRepository(ctx, t, cfg) + localRepo := localrepo.NewTestRepo(t, cfg, localRepoProto) - repoCopyProto, _ := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ - Seed: gittest.SeedGitLabTest, - }) + remoteRepoProto, remoteRepoPath := gittest.CreateRepository(ctx, t, cfg) - branch := "rebase-delete-test" + // Write the root commit into both repositories as common history. + var rootCommitID git.ObjectID + for _, path := range []string{localRepoPath, remoteRepoPath} { + rootCommitID = gittest.WriteCommit(t, cfg, path, + gittest.WithParents(), + gittest.WithTreeEntries( + gittest.TreeEntry{Path: "change-me", Mode: "100644", Content: "unchanged contents"}, + gittest.TreeEntry{Path: "delete-me", Mode: "100644", Content: "useless stuff"}, + ), + ) + } - gittest.Exec(t, cfg, "-C", repoPath, "config", "user.name", string(gittest.TestUser.Name)) - gittest.Exec(t, cfg, "-C", repoPath, "config", "user.email", string(gittest.TestUser.Email)) - gittest.Exec(t, cfg, "-C", repoPath, "checkout", "-b", branch, "master~1") - gittest.Exec(t, cfg, "-C", repoPath, "rm", "README") - gittest.Exec(t, cfg, "-C", repoPath, "commit", "-a", "-m", "delete file") + // Write a commit into the local repository that deletes a single file. + localCommitID := gittest.WriteCommit(t, cfg, localRepoPath, + gittest.WithParents(rootCommitID), + gittest.WithTreeEntries( + gittest.TreeEntry{Path: "change-me", Mode: "100644", Content: "unchanged contents"}, + ), + gittest.WithBranch("local"), + ) - branchSha := getBranchSha(t, cfg, repoPath, branch) + // And then finally write a commit into the remote repository that changes a different file. + gittest.WriteCommit(t, cfg, remoteRepoPath, + gittest.WithParents(rootCommitID), + gittest.WithTreeEntries( + gittest.TreeEntry{Path: "change-me", Mode: "100644", Content: "modified contents"}, + gittest.TreeEntry{Path: "delete-me", Mode: "100644", Content: "useless stuff"}, + ), + gittest.WithBranch("remote"), + ) - rebaseStream, err := client.UserRebaseConfirmable(ctx) + // Send the first request to tell the server to perform the rebase. + stream, err := client.UserRebaseConfirmable(ctx) + require.NoError(t, err) + require.NoError(t, stream.Send(buildHeaderRequest( + localRepoProto, gittest.TestUser, "1", "local", localCommitID, remoteRepoProto, "remote", + ))) + firstResponse, err := stream.Recv() require.NoError(t, err) - headerRequest := buildHeaderRequest(repoProto, gittest.TestUser, "1", branch, branchSha, repoCopyProto, "master") - require.NoError(t, rebaseStream.Send(headerRequest), "send header") - - firstResponse, err := rebaseStream.Recv() - require.NoError(t, err, "receive first response") - - _, err = repo.ReadCommit(ctx, git.Revision(firstResponse.GetRebaseSha())) + _, err = localRepo.ReadCommit(ctx, git.Revision(firstResponse.GetRebaseSha())) require.Equal(t, localrepo.ErrObjectNotFound, err, "commit should not exist in the normal repo given that it is quarantined") - applyRequest := buildApplyRequest(true) - require.NoError(t, rebaseStream.Send(applyRequest), "apply rebase") - - secondResponse, err := rebaseStream.Recv() - require.NoError(t, err, "receive second response") + // Send the second request to tell the server to apply the rebase. + require.NoError(t, stream.Send(buildApplyRequest(true))) + secondResponse, err := stream.Recv() + require.NoError(t, err) + require.True(t, secondResponse.GetRebaseApplied()) - _, err = rebaseStream.Recv() + _, err = stream.Recv() require.Equal(t, io.EOF, err) - newBranchSha := getBranchSha(t, cfg, repoPath, branch) - - _, err = repo.ReadCommit(ctx, git.Revision(firstResponse.GetRebaseSha())) - require.NoError(t, err, "look up git commit after rebase is applied") + newBranchCommitID := gittest.ResolveRevision(t, cfg, localRepoPath, "local") - require.NotEqual(t, newBranchSha, branchSha) - require.Equal(t, newBranchSha, firstResponse.GetRebaseSha()) - - require.True(t, secondResponse.GetRebaseApplied(), "the second rebase is applied") + _, err = localRepo.ReadCommit(ctx, git.Revision(firstResponse.GetRebaseSha())) + require.NoError(t, err) + require.NotEqual(t, newBranchCommitID, localCommitID) + require.Equal(t, newBranchCommitID.String(), firstResponse.GetRebaseSha()) } -func TestRebaseOntoRemoteBranch(t *testing.T) { +func TestUserRebaseConfirmable_deletedFileInRemoteRepo(t *testing.T) { t.Parallel() ctx := testhelper.Context(t) - ctx, cfg, repoProto, repoPath, client := setupOperationsService(t, ctx) - - repo := localrepo.NewTestRepo(t, cfg, repoProto) - - remoteRepo, remoteRepoPath := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ - Seed: gittest.SeedGitLabTest, - }) - gittest.AddWorktree(t, cfg, remoteRepoPath, "worktree") - remoteRepoPath = filepath.Join(remoteRepoPath, "worktree") + ctx, cfg, client := setupOperationsServiceWithoutRepo(t, ctx) + + localRepoProto, localRepoPath := gittest.CreateRepository(ctx, t, cfg) + localRepo := localrepo.NewTestRepo(t, cfg, localRepoProto) + + remoteRepoProto, remoteRepoPath := gittest.CreateRepository(ctx, t, cfg) + + // Write the root commit into both repositories as common history. + var rootCommitID git.ObjectID + for _, path := range []string{localRepoPath, remoteRepoPath} { + rootCommitID = gittest.WriteCommit(t, cfg, path, + gittest.WithParents(), + gittest.WithTreeEntries( + gittest.TreeEntry{Path: "unchanged", Mode: "100644", Content: "unchanged contents"}, + gittest.TreeEntry{Path: "delete-me", Mode: "100644", Content: "useless stuff"}, + ), + gittest.WithBranch("local"), + ) + } - localBranch := "master" - localBranchHash := getBranchSha(t, cfg, repoPath, localBranch) + // Write a commit into the remote repository that deletes a file. + remoteCommitID := gittest.WriteCommit(t, cfg, remoteRepoPath, + gittest.WithParents(rootCommitID), + gittest.WithTreeEntries( + gittest.TreeEntry{Path: "unchanged", Mode: "100644", Content: "unchanged contents"}, + ), + gittest.WithBranch("remote"), + ) - remoteBranch := "remote-branch" - gittest.Exec(t, cfg, "-C", remoteRepoPath, "config", "user.name", string(gittest.TestUser.Name)) - gittest.Exec(t, cfg, "-C", remoteRepoPath, "config", "user.email", string(gittest.TestUser.Email)) - gittest.Exec(t, cfg, "-C", remoteRepoPath, "checkout", "-b", remoteBranch, "master") - gittest.Exec(t, cfg, "-C", remoteRepoPath, "rm", "README") - gittest.Exec(t, cfg, "-C", remoteRepoPath, "commit", "-a", "-m", "remove README") - remoteBranchHash := getBranchSha(t, cfg, remoteRepoPath, remoteBranch) + _, err := localRepo.ReadCommit(ctx, remoteCommitID.Revision()) + require.Equal(t, localrepo.ErrObjectNotFound, err, "remote commit should not yet exist in local repository") + // Send the first request to tell the server to perform the rebase. rebaseStream, err := client.UserRebaseConfirmable(ctx) require.NoError(t, err) - - _, err = repo.ReadCommit(ctx, git.Revision(remoteBranchHash)) - require.Equal(t, localrepo.ErrObjectNotFound, err, "remote commit does not yet exist in local repository") - - headerRequest := buildHeaderRequest(repoProto, gittest.TestUser, "1", localBranch, localBranchHash, remoteRepo, remoteBranch) - require.NoError(t, rebaseStream.Send(headerRequest), "send header") - + require.NoError(t, rebaseStream.Send(buildHeaderRequest( + localRepoProto, gittest.TestUser, "1", "local", rootCommitID, remoteRepoProto, "remote", + ))) firstResponse, err := rebaseStream.Recv() - require.NoError(t, err, "receive first response") + require.NoError(t, err) - _, err = repo.ReadCommit(ctx, git.Revision(remoteBranchHash)) + _, err = localRepo.ReadCommit(ctx, remoteCommitID.Revision()) require.Equal(t, localrepo.ErrObjectNotFound, err, "commit should not exist in the normal repo given that it is quarantined") - applyRequest := buildApplyRequest(true) - require.NoError(t, rebaseStream.Send(applyRequest), "apply rebase") - + // Send the second request to tell the server to apply the rebase. + require.NoError(t, rebaseStream.Send(buildApplyRequest(true))) secondResponse, err := rebaseStream.Recv() - require.NoError(t, err, "receive second response") + require.NoError(t, err) + require.True(t, secondResponse.GetRebaseApplied(), "the second rebase is applied") _, err = rebaseStream.Recv() require.Equal(t, io.EOF, err) - _, err = repo.ReadCommit(ctx, git.Revision(remoteBranchHash)) + _, err = localRepo.ReadCommit(ctx, remoteCommitID.Revision()) require.NoError(t, err) - rebasedBranchHash := getBranchSha(t, cfg, repoPath, localBranch) - - require.NotEqual(t, rebasedBranchHash, localBranchHash) - require.Equal(t, rebasedBranchHash, firstResponse.GetRebaseSha()) - - require.True(t, secondResponse.GetRebaseApplied(), "the second rebase is applied") + rebasedBranchCommitID := gittest.ResolveRevision(t, cfg, localRepoPath, "local") + require.NotEqual(t, rebasedBranchCommitID, rootCommitID) + require.Equal(t, rebasedBranchCommitID.String(), firstResponse.GetRebaseSha()) } -func TestRebaseFailedWithCode(t *testing.T) { +func TestUserRebaseConfirmable_failedWithCode(t *testing.T) { t.Parallel() ctx := testhelper.Context(t) ctx, cfg, repoProto, repoPath, client := setupOperationsService(t, ctx) - branchSha := getBranchSha(t, cfg, repoPath, rebaseBranchName) + branchCommitID := gittest.ResolveRevision(t, cfg, repoPath, rebaseBranchName) testCases := []struct { desc string @@ -774,7 +791,7 @@ func TestRebaseFailedWithCode(t *testing.T) { repo := proto.Clone(repoProto).(*gitalypb.Repository) repo.StorageName = "@this-storage-does-not-exist" - return buildHeaderRequest(repo, gittest.TestUser, "1", rebaseBranchName, branchSha, repo, "master") + return buildHeaderRequest(repo, gittest.TestUser, "1", rebaseBranchName, branchCommitID, repo, "master") }, expectedCode: codes.InvalidArgument, }, @@ -784,7 +801,7 @@ func TestRebaseFailedWithCode(t *testing.T) { repo := proto.Clone(repoProto).(*gitalypb.Repository) repo.RelativePath = "" - return buildHeaderRequest(repo, gittest.TestUser, "1", rebaseBranchName, branchSha, repo, "master") + return buildHeaderRequest(repo, gittest.TestUser, "1", rebaseBranchName, branchCommitID, repo, "master") }, expectedCode: codes.InvalidArgument, }, @@ -824,7 +841,7 @@ func rebaseRecvTimeout(bidi gitalypb.OperationService_UserRebaseConfirmableClien } } -func buildHeaderRequest(repo *gitalypb.Repository, user *gitalypb.User, rebaseID string, branchName string, branchSha string, remoteRepo *gitalypb.Repository, remoteBranch string) *gitalypb.UserRebaseConfirmableRequest { +func buildHeaderRequest(repo *gitalypb.Repository, user *gitalypb.User, rebaseID string, branchName string, commitID git.ObjectID, remoteRepo *gitalypb.Repository, remoteBranch string) *gitalypb.UserRebaseConfirmableRequest { return &gitalypb.UserRebaseConfirmableRequest{ UserRebaseConfirmableRequestPayload: &gitalypb.UserRebaseConfirmableRequest_Header_{ Header: &gitalypb.UserRebaseConfirmableRequest_Header{ @@ -832,7 +849,7 @@ func buildHeaderRequest(repo *gitalypb.Repository, user *gitalypb.User, rebaseID User: user, RebaseId: rebaseID, Branch: []byte(branchName), - BranchSha: branchSha, + BranchSha: commitID.String(), RemoteRepository: remoteRepo, RemoteBranch: []byte(remoteBranch), }, diff --git a/internal/gitaly/service/operations/squash_test.go b/internal/gitaly/service/operations/squash_test.go index 72d741ac1..387099e9a 100644 --- a/internal/gitaly/service/operations/squash_test.go +++ b/internal/gitaly/service/operations/squash_test.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "os" - "path/filepath" "strings" "testing" @@ -323,43 +322,52 @@ func TestUserSquash_renames(t *testing.T) { t.Parallel() ctx := testhelper.Context(t) - ctx, cfg, repoProto, repoPath, client := setupOperationsService(t, ctx) + ctx, cfg, client := setupOperationsServiceWithoutRepo(t, ctx) + + repoProto, repoPath := gittest.CreateRepository(ctx, t, cfg) - gittest.AddWorktree(t, cfg, repoPath, "worktree") - repoPath = filepath.Join(repoPath, "worktree") + gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(), gittest.WithBranch("main")) repo := localrepo.NewTestRepo(t, cfg, repoProto) originalFilename := "original-file.txt" renamedFilename := "renamed-file.txt" - gittest.Exec(t, cfg, "-C", repoPath, "checkout", "-b", "squash-rename-test", "master") - require.NoError(t, os.WriteFile(filepath.Join(repoPath, originalFilename), []byte("This is a test"), 0o644)) - gittest.Exec(t, cfg, "-C", repoPath, "add", ".") - gittest.Exec(t, cfg, "-C", repoPath, "commit", "-m", "test file") - - startCommitID := text.ChompBytes(gittest.Exec(t, cfg, "-C", repoPath, "rev-parse", "HEAD")) - - gittest.Exec(t, cfg, "-C", repoPath, "mv", originalFilename, renamedFilename) - gittest.Exec(t, cfg, "-C", repoPath, "commit", "-a", "-m", "renamed test file") + rootCommitID := gittest.WriteCommit(t, cfg, repoPath, + gittest.WithParents(), + gittest.WithTreeEntries( + gittest.TreeEntry{Path: originalFilename, Mode: "100644", Content: "This is a test"}, + ), + ) - // Modify the original file in another branch - gittest.Exec(t, cfg, "-C", repoPath, "checkout", "-b", "squash-rename-branch", startCommitID) - require.NoError(t, os.WriteFile(filepath.Join(repoPath, originalFilename), []byte("This is a change"), 0o644)) - gittest.Exec(t, cfg, "-C", repoPath, "commit", "-a", "-m", "test") + startCommitID := gittest.WriteCommit(t, cfg, repoPath, + gittest.WithParents(rootCommitID), + gittest.WithTreeEntries( + gittest.TreeEntry{Path: renamedFilename, Mode: "100644", Content: "This is a test"}, + ), + ) - require.NoError(t, os.WriteFile(filepath.Join(repoPath, originalFilename), []byte("This is another change"), 0o644)) - gittest.Exec(t, cfg, "-C", repoPath, "commit", "-a", "-m", "test") + changedCommitID := gittest.WriteCommit(t, cfg, repoPath, + gittest.WithParents(rootCommitID), + gittest.WithTreeEntries( + gittest.TreeEntry{Path: originalFilename, Mode: "100644", Content: "This is a change"}, + ), + ) - endCommitID := text.ChompBytes(gittest.Exec(t, cfg, "-C", repoPath, "rev-parse", "HEAD")) + endCommitID := gittest.WriteCommit(t, cfg, repoPath, + gittest.WithParents(changedCommitID), + gittest.WithTreeEntries( + gittest.TreeEntry{Path: originalFilename, Mode: "100644", Content: "This is another change"}, + ), + ) request := &gitalypb.UserSquashRequest{ Repository: repoProto, User: gittest.TestUser, Author: author, CommitMessage: commitMessage, - StartSha: startCommitID, - EndSha: endCommitID, + StartSha: startCommitID.String(), + EndSha: endCommitID.String(), } response, err := client.UserSquash(ctx, request) @@ -367,7 +375,7 @@ func TestUserSquash_renames(t *testing.T) { commit, err := repo.ReadCommit(ctx, git.Revision(response.SquashSha)) require.NoError(t, err) - require.Equal(t, []string{startCommitID}, commit.ParentIds) + require.Equal(t, []string{startCommitID.String()}, commit.ParentIds) require.Equal(t, author.Name, commit.Author.Name) require.Equal(t, author.Email, commit.Author.Email) require.Equal(t, gittest.TestUser.Name, commit.Committer.Name) @@ -375,6 +383,10 @@ func TestUserSquash_renames(t *testing.T) { require.Equal(t, gittest.TimezoneOffset, string(commit.Committer.Timezone)) require.Equal(t, gittest.TimezoneOffset, string(commit.Author.Timezone)) require.Equal(t, commitMessage, commit.Subject) + + gittest.RequireTree(t, cfg, repoPath, response.SquashSha, []gittest.TreeEntry{ + {Path: renamedFilename, Mode: "100644", Content: "This is another change", OID: "1b2ae89cca65f0d514f677981f012d708df651fc"}, + }) } func TestUserSquash_missingFileOnTargetBranch(t *testing.T) { diff --git a/internal/gitaly/service/ref/list_refs_test.go b/internal/gitaly/service/ref/list_refs_test.go index 239c7ecd9..b9df9c5a1 100644 --- a/internal/gitaly/service/ref/list_refs_test.go +++ b/internal/gitaly/service/ref/list_refs_test.go @@ -2,11 +2,10 @@ package ref import ( "io" - "path/filepath" "testing" + "time" "github.com/stretchr/testify/require" - "gitlab.com/gitlab-org/gitaly/v15/internal/git" "gitlab.com/gitlab-org/gitaly/v15/internal/git/gittest" "gitlab.com/gitlab-org/gitaly/v15/internal/helper/text" "gitlab.com/gitlab-org/gitaly/v15/internal/testhelper" @@ -19,28 +18,21 @@ func TestServer_ListRefs(t *testing.T) { cfg, _, _, client := setupRefService(ctx, t) repo, repoPath := gittest.CreateRepository(ctx, t, cfg) - // Checking out a worktree in an empty repository is not possible, so we must first write an empty commit. - gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch(git.DefaultBranch), gittest.WithParents()) - gittest.AddWorktree(t, cfg, repoPath, "worktree") - repoPath = filepath.Join(repoPath, "worktree") - // The worktree is detached, checkout the main so the branch pointer advances - // as we commit. - gittest.Exec(t, cfg, "-C", repoPath, "checkout", "main") - oldCommit := text.ChompBytes(gittest.Exec(t, cfg, "-C", repoPath, "rev-parse", "refs/heads/main")) - - gittest.Exec(t, cfg, "-C", repoPath, "commit", "--allow-empty", "-m", "commit message") - gittest.Exec(t, cfg, "-C", repoPath, "commit", "--amend", "--date", "Wed Feb 16 14:01 2011 +0100", "--allow-empty", "--no-edit") - commit := text.ChompBytes(gittest.Exec(t, cfg, "-C", repoPath, "rev-parse", "refs/heads/main")) + oldCommitID := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents()) + newCommitID := gittest.WriteCommit(t, cfg, repoPath, + gittest.WithParents(oldCommitID), + gittest.WithAuthorDate(time.Date(2011, 2, 16, 14, 1, 0, 0, time.FixedZone("UTC+1", +1*60*60))), + ) for _, cmd := range [][]string{ - {"update-ref", "refs/heads/main", commit}, - {"tag", "lightweight-tag", commit}, + {"update-ref", "refs/heads/main", newCommitID.String()}, + {"tag", "lightweight-tag", newCommitID.String()}, {"tag", "-m", "tag message", "annotated-tag", "refs/heads/main"}, {"symbolic-ref", "refs/heads/symbolic", "refs/heads/main"}, - {"update-ref", "refs/remote/remote-name/remote-branch", commit}, + {"update-ref", "refs/remote/remote-name/remote-branch", newCommitID.String()}, {"symbolic-ref", "HEAD", "refs/heads/main"}, - {"update-ref", "refs/heads/old", oldCommit}, + {"update-ref", "refs/heads/old", oldCommitID.String()}, } { gittest.Exec(t, cfg, append([]string{"-C", repoPath}, cmd...)...) } @@ -111,7 +103,7 @@ func TestServer_ListRefs(t *testing.T) { }, }, expected: []*gitalypb.ListRefsResponse_Reference{ - {Name: []byte("refs/heads/main"), Target: commit}, + {Name: []byte("refs/heads/main"), Target: newCommitID.String()}, }, }, { @@ -121,12 +113,12 @@ func TestServer_ListRefs(t *testing.T) { Patterns: [][]byte{[]byte("refs/")}, }, expected: []*gitalypb.ListRefsResponse_Reference{ - {Name: []byte("refs/heads/main"), Target: commit}, - {Name: []byte("refs/heads/old"), Target: oldCommit}, - {Name: []byte("refs/heads/symbolic"), Target: commit}, - {Name: []byte("refs/remote/remote-name/remote-branch"), Target: commit}, + {Name: []byte("refs/heads/main"), Target: newCommitID.String()}, + {Name: []byte("refs/heads/old"), Target: oldCommitID.String()}, + {Name: []byte("refs/heads/symbolic"), Target: newCommitID.String()}, + {Name: []byte("refs/remote/remote-name/remote-branch"), Target: newCommitID.String()}, {Name: []byte("refs/tags/annotated-tag"), Target: annotatedTagOID}, - {Name: []byte("refs/tags/lightweight-tag"), Target: commit}, + {Name: []byte("refs/tags/lightweight-tag"), Target: newCommitID.String()}, }, }, { @@ -140,9 +132,9 @@ func TestServer_ListRefs(t *testing.T) { }, }, expected: []*gitalypb.ListRefsResponse_Reference{ - {Name: []byte("refs/heads/old"), Target: oldCommit}, - {Name: []byte("refs/heads/main"), Target: commit}, - {Name: []byte("refs/heads/symbolic"), Target: commit}, + {Name: []byte("refs/heads/old"), Target: oldCommitID.String()}, + {Name: []byte("refs/heads/main"), Target: newCommitID.String()}, + {Name: []byte("refs/heads/symbolic"), Target: newCommitID.String()}, }, }, { @@ -152,11 +144,11 @@ func TestServer_ListRefs(t *testing.T) { Patterns: [][]byte{[]byte("refs/heads/*"), []byte("refs/tags/*")}, }, expected: []*gitalypb.ListRefsResponse_Reference{ - {Name: []byte("refs/heads/main"), Target: commit}, - {Name: []byte("refs/heads/old"), Target: oldCommit}, - {Name: []byte("refs/heads/symbolic"), Target: commit}, + {Name: []byte("refs/heads/main"), Target: newCommitID.String()}, + {Name: []byte("refs/heads/old"), Target: oldCommitID.String()}, + {Name: []byte("refs/heads/symbolic"), Target: newCommitID.String()}, {Name: []byte("refs/tags/annotated-tag"), Target: annotatedTagOID}, - {Name: []byte("refs/tags/lightweight-tag"), Target: commit}, + {Name: []byte("refs/tags/lightweight-tag"), Target: newCommitID.String()}, }, }, { @@ -167,12 +159,12 @@ func TestServer_ListRefs(t *testing.T) { Patterns: [][]byte{[]byte("refs/heads/*"), []byte("refs/tags/*")}, }, expected: []*gitalypb.ListRefsResponse_Reference{ - {Name: []byte("HEAD"), Target: commit}, - {Name: []byte("refs/heads/main"), Target: commit}, - {Name: []byte("refs/heads/old"), Target: oldCommit}, - {Name: []byte("refs/heads/symbolic"), Target: commit}, + {Name: []byte("HEAD"), Target: newCommitID.String()}, + {Name: []byte("refs/heads/main"), Target: newCommitID.String()}, + {Name: []byte("refs/heads/old"), Target: oldCommitID.String()}, + {Name: []byte("refs/heads/symbolic"), Target: newCommitID.String()}, {Name: []byte("refs/tags/annotated-tag"), Target: annotatedTagOID}, - {Name: []byte("refs/tags/lightweight-tag"), Target: commit}, + {Name: []byte("refs/tags/lightweight-tag"), Target: newCommitID.String()}, }, }, } { diff --git a/internal/gitaly/service/repository/archive_test.go b/internal/gitaly/service/repository/archive_test.go index cdb347617..d66944817 100644 --- a/internal/gitaly/service/repository/archive_test.go +++ b/internal/gitaly/service/repository/archive_test.go @@ -31,8 +31,9 @@ const ( lfsBody = "hello world\n" ) -func TestGetArchiveSuccess(t *testing.T) { +func TestGetArchive_success(t *testing.T) { t.Parallel() + ctx := testhelper.Context(t) _, repo, _, client := setupRepositoryService(ctx, t) @@ -148,13 +149,7 @@ func TestGetArchiveSuccess(t *testing.T) { data, err := consumeArchive(stream) require.NoError(t, err) - archiveFile, err := os.Create(filepath.Join(testhelper.TempDir(t), "archive")) - require.NoError(t, err) - - _, err = archiveFile.Write(data) - require.NoError(t, err) - - contents := string(compressedFileContents(t, format, archiveFile.Name())) + contents := compressedFileContents(t, format, data) for _, content := range tc.contents { require.Contains(t, contents, tc.prefix+content) @@ -282,8 +277,9 @@ func TestGetArchive_includeLfsBlobs(t *testing.T) { } } -func TestGetArchiveFailure(t *testing.T) { +func TestGetArchive_inputValidation(t *testing.T) { t.Parallel() + ctx := testhelper.Context(t) _, repo, _, client := setupRepositoryService(ctx, t) @@ -408,68 +404,57 @@ func TestGetArchiveFailure(t *testing.T) { } } -func TestGetArchivePathInjection(t *testing.T) { +func TestGetArchive_pathInjection(t *testing.T) { t.Parallel() - ctx := testhelper.Context(t) - cfg, repo, repoPath, client := setupRepositoryServiceWithWorktree(ctx, t) - - // Adding a temp directory representing the .ssh directory - sshDirectory := testhelper.TempDir(t) - - // Adding an empty authorized_keys file - authorizedKeysPath := filepath.Join(sshDirectory, "authorized_keys") - - authorizedKeysFile, err := os.Create(authorizedKeysPath) - require.NoError(t, err) - require.NoError(t, authorizedKeysFile.Close()) - - // Create the directory on the repository - repoExploitPath := filepath.Join(repoPath, "--output=", authorizedKeysPath) - require.NoError(t, os.MkdirAll(repoExploitPath, os.ModeDir|0o755)) - - f, err := os.Create(filepath.Join(repoExploitPath, "id_12345.pub")) - require.NoError(t, err) - - evilPubKeyFile := `# - ssh-ed25519 my_super_evil_ssh_pubkey - #` - _, err = fmt.Fprint(f, evilPubKeyFile) - require.NoError(t, err) - require.NoError(t, f.Close()) - - // Add the directory to the repository - gittest.Exec(t, cfg, "-C", repoPath, "add", ".") - gittest.Exec(t, cfg, "-C", repoPath, "commit", "-m", "adding fake key file") - commitID := strings.TrimRight(string(gittest.Exec(t, cfg, "-C", repoPath, "rev-parse", "HEAD")), "\n") - - injectionPath := fmt.Sprintf("--output=%s", authorizedKeysPath) - - req := &gitalypb.GetArchiveRequest{ + ctx := testhelper.Context(t) + cfg, repo, repoPath, client := setupRepositoryService(ctx, t) + + // It used to be possible to inject options into `git-archive(1)`, with the worst outcome + // being that an adversary may create or overwrite arbitrary files in the filesystem in case + // they passed `--output`. + // + // We pretend that the adversary wants to override a fixed file `/non/existent`. In + // practice, thus would be something more interesting like `/etc/shadow` or `allowed_keys`. + // We then create a file inside the repository itself that has `--output=/non/existent` as + // relative path. This is done to verify that git-archive(1) indeed treats the parameter as + // a path and does not interpret it as an option. + outputPath := "/non/existent" + + commitID := gittest.WriteCommit(t, cfg, repoPath, gittest.WithTree(gittest.WriteTree(t, cfg, repoPath, []gittest.TreeEntry{ + {Path: "--output=", Mode: "040000", OID: gittest.WriteTree(t, cfg, repoPath, []gittest.TreeEntry{ + {Path: "non", Mode: "040000", OID: gittest.WriteTree(t, cfg, repoPath, []gittest.TreeEntry{ + {Path: "existent", Mode: "040000", OID: gittest.WriteTree(t, cfg, repoPath, []gittest.TreeEntry{ + {Path: "injected file", Mode: "100644", Content: "injected content"}, + })}, + })}, + })}, + }))) + + // And now we fire the request path our bogus path to try and overwrite the output path. + stream, err := client.GetArchive(ctx, &gitalypb.GetArchiveRequest{ Repository: repo, - CommitId: commitID, + CommitId: commitID.String(), Prefix: "", Format: gitalypb.GetArchiveRequest_TAR, - Path: []byte(injectionPath), - } - - stream, err := client.GetArchive(ctx, req) - require.NoError(t, err) - - _, err = consumeArchive(stream) + Path: []byte("--output=" + outputPath), + }) require.NoError(t, err) - authorizedKeysFile, err = os.Open(authorizedKeysPath) + content, err := consumeArchive(stream) require.NoError(t, err) - defer authorizedKeysFile.Close() - authorizedKeysFileBytes, err := io.ReadAll(authorizedKeysFile) - require.NoError(t, err) - authorizedKeysFileStat, err := authorizedKeysFile.Stat() - require.NoError(t, err) - - require.NotContains(t, string(authorizedKeysFileBytes), evilPubKeyFile) // this should fail first in pre-fix failing test - require.Zero(t, authorizedKeysFileStat.Size()) + require.NoFileExists(t, outputPath) + require.Equal(t, + strings.Join([]string{ + "/", + "/--output=/", + "/--output=/non/", + "/--output=/non/existent/", + "/--output=/non/existent/injected file", + }, "\n"), + compressedFileContents(t, gitalypb.GetArchiveRequest_TAR, content), + ) } func TestGetArchive_environment(t *testing.T) { @@ -547,19 +532,23 @@ func TestGetArchive_environment(t *testing.T) { } } -func compressedFileContents(t *testing.T, format gitalypb.GetArchiveRequest_Format, name string) []byte { +func compressedFileContents(t *testing.T, format gitalypb.GetArchiveRequest_Format, contents []byte) string { + path := filepath.Join(testhelper.TempDir(t), "archive") + require.NoError(t, os.WriteFile(path, contents, 0o644)) + switch format { case gitalypb.GetArchiveRequest_TAR: - return testhelper.MustRunCommand(t, nil, "tar", "tf", name) + return text.ChompBytes(testhelper.MustRunCommand(t, nil, "tar", "tf", path)) case gitalypb.GetArchiveRequest_TAR_GZ: - return testhelper.MustRunCommand(t, nil, "tar", "ztf", name) + return text.ChompBytes(testhelper.MustRunCommand(t, nil, "tar", "ztf", path)) case gitalypb.GetArchiveRequest_TAR_BZ2: - return testhelper.MustRunCommand(t, nil, "tar", "jtf", name) + return text.ChompBytes(testhelper.MustRunCommand(t, nil, "tar", "jtf", path)) case gitalypb.GetArchiveRequest_ZIP: - return testhelper.MustRunCommand(t, nil, "unzip", "-l", name) + return text.ChompBytes(testhelper.MustRunCommand(t, nil, "unzip", "-l", path)) + default: + require.FailNow(t, "unsupported archive format: %v", format) + return "" } - - return nil } func consumeArchive(stream gitalypb.RepositoryService_GetArchiveClient) ([]byte, error) { diff --git a/internal/gitaly/service/repository/search_files_test.go b/internal/gitaly/service/repository/search_files_test.go index 835bcbc4e..b314f50ae 100644 --- a/internal/gitaly/service/repository/search_files_test.go +++ b/internal/gitaly/service/repository/search_files_test.go @@ -2,10 +2,7 @@ package repository import ( "bytes" - "fmt" "io" - "os" - "path/filepath" "strings" "testing" @@ -152,55 +149,52 @@ func TestSearchFilesByContentSuccessful(t *testing.T) { func TestSearchFilesByContentLargeFile(t *testing.T) { t.Parallel() - ctx := testhelper.Context(t) - cfg, repo, repoPath, client := setupRepositoryServiceWithWorktree(ctx, t) + ctx := testhelper.Context(t) - committerName := "Scrooge McDuck" - committerEmail := "scrooge@mcduck.com" + cfg, repo, repoPath, client := setupRepositoryService(ctx, t) - largeFiles := []struct { + for _, tc := range []struct { + desc string filename string line string repeated int query string }{ { + desc: "large file", filename: "large_file_of_abcdefg_2mb", line: "abcdefghi\n", // 10 bytes repeated: 210000, query: "abcdefg", }, { + desc: "large file with unicode", filename: "large_file_of_unicode_1.5mb", line: "你见天吃了什么东西?\n", // 22 bytes repeated: 70000, query: "什么东西", }, - } - - for _, largeFile := range largeFiles { - t.Run(largeFile.filename, func(t *testing.T) { - require.NoError(t, os.WriteFile(filepath.Join(repoPath, largeFile.filename), bytes.Repeat([]byte(largeFile.line), largeFile.repeated), 0o644)) - // By default, the worktree is detached. Checkout master so the branch advances with the commit. - gittest.Exec(t, cfg, "-C", repoPath, "checkout", "master") - gittest.Exec(t, cfg, "-C", repoPath, "add", ".") - gittest.Exec(t, cfg, "-C", repoPath, - "-c", fmt.Sprintf("user.name=%s", committerName), - "-c", fmt.Sprintf("user.email=%s", committerEmail), "commit", "-m", "large file commit", "--", largeFile.filename) + } { + t.Run(tc.filename, func(t *testing.T) { + gittest.WriteCommit(t, cfg, repoPath, gittest.WithTreeEntries(gittest.TreeEntry{ + Path: tc.filename, + Mode: "100644", + Content: strings.Repeat(tc.line, tc.repeated), + }), gittest.WithBranch("master")) stream, err := client.SearchFilesByContent(ctx, &gitalypb.SearchFilesByContentRequest{ Repository: repo, - Query: largeFile.query, + Query: tc.query, Ref: []byte("master"), ChunkedResponse: true, }) require.NoError(t, err) - resp, err := consumeFilenameByContentChunked(stream) + response, err := consumeFilenameByContentChunked(stream) require.NoError(t, err) - require.Equal(t, largeFile.repeated, len(bytes.Split(bytes.TrimRight(resp[0], "\n"), []byte("\n")))) + require.Equal(t, tc.repeated, len(bytes.Split(bytes.TrimRight(response[0], "\n"), []byte("\n")))) }) } } diff --git a/internal/gitaly/service/repository/testhelper_test.go b/internal/gitaly/service/repository/testhelper_test.go index 5024918ca..79fb534d3 100644 --- a/internal/gitaly/service/repository/testhelper_test.go +++ b/internal/gitaly/service/repository/testhelper_test.go @@ -3,7 +3,6 @@ package repository import ( "context" "os" - "path/filepath" "reflect" "runtime" "testing" @@ -196,15 +195,6 @@ func setupRepositoryServiceWithoutRepo(t testing.TB, opts ...testserver.GitalySe return cfg, client } -func setupRepositoryServiceWithWorktree(ctx context.Context, t testing.TB, opts ...testserver.GitalyServerOpt) (config.Cfg, *gitalypb.Repository, string, gitalypb.RepositoryServiceClient) { - cfg, repo, repoPath, client := setupRepositoryService(ctx, t, opts...) - - gittest.AddWorktree(t, cfg, repoPath, "worktree") - repoPath = filepath.Join(repoPath, "worktree") - - return cfg, repo, repoPath, client -} - func gitalyOrPraefect(gitalyMsg, praefectMsg string) string { if testhelper.IsPraefectEnabled() { return praefectMsg diff --git a/internal/gitaly/service/wiki/find_page_test.go b/internal/gitaly/service/wiki/find_page_test.go index 885c4f5df..06f67a186 100644 --- a/internal/gitaly/service/wiki/find_page_test.go +++ b/internal/gitaly/service/wiki/find_page_test.go @@ -1,9 +1,7 @@ package wiki import ( - "fmt" "io" - "path/filepath" "testing" "github.com/stretchr/testify/require" @@ -473,17 +471,13 @@ func testSuccessfulWikiFindPageRequestWithTrailers(t *testing.T, cfg config.Cfg, gittest.WithMessage("main branch, empty commit"), ) + // Include UTF-8 to ensure encoding is handled. page1Name := "Home Pagé" - createTestWikiPage(t, cfg, client, wikiRepo, repoPath, createWikiPageOpts{title: page1Name}) - - gittest.AddWorktree(t, cfg, repoPath, "worktree") - worktreePath := filepath.Join(repoPath, "worktree") - gittest.Exec(t, cfg, "-C", worktreePath, "checkout", "main") - gittest.Exec(t, cfg, "-C", worktreePath, - // Include UTF-8 to ensure encoding is handled - "-c", fmt.Sprintf("user.name=%s", "Scróoge McDuck"), - "-c", fmt.Sprintf("user.email=%s", "scrooge@mcduck.com"), - "commit", "--amend", "-m", "Empty commit", "-s") + author := "Scróoge McDuck" + createTestWikiPage(t, cfg, client, wikiRepo, repoPath, createWikiPageOpts{ + title: page1Name, + authorName: author, + }) request := &gitalypb.WikiFindPageRequest{ Repository: wikiRepo, @@ -496,6 +490,7 @@ func testSuccessfulWikiFindPageRequestWithTrailers(t *testing.T, cfg config.Cfg, receivedPage := readFullWikiPageFromWikiFindPageClient(t, c) require.Equal(t, page1Name, string(receivedPage.Name)) - receivedContent := receivedPage.GetRawData() - require.NotNil(t, receivedContent) + require.NotNil(t, receivedPage.GetRawData()) + require.Equal(t, author, string(receivedPage.GetVersion().GetCommit().GetAuthor().GetName())) + require.Equal(t, author, string(receivedPage.GetVersion().GetCommit().GetCommitter().GetName())) } diff --git a/internal/gitaly/service/wiki/testhelper_test.go b/internal/gitaly/service/wiki/testhelper_test.go index 8fbed8ef4..5402870c9 100644 --- a/internal/gitaly/service/wiki/testhelper_test.go +++ b/internal/gitaly/service/wiki/testhelper_test.go @@ -29,6 +29,7 @@ type createWikiPageOpts struct { content []byte format string forceContentEmpty bool + authorName string } var mockPageContent = bytes.Repeat([]byte("Mock wiki page content"), 10000) @@ -121,8 +122,13 @@ func writeWikiPage(t *testing.T, client gitalypb.WikiServiceClient, wikiRepo *gi format = opts.format } + authorName := opts.authorName + if authorName == "" { + authorName = "Ahmad Sherif" + } + commitDetails := &gitalypb.WikiCommitDetails{ - Name: []byte("Ahmad Sherif"), + Name: []byte(authorName), Email: []byte("ahmad@gitlab.com"), Message: []byte("Add " + opts.title), UserId: int32(1), |