Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Tobler <jtobler@gitlab.com>2023-06-27 19:08:32 +0300
committerJustin Tobler <jtobler@gitlab.com>2023-06-27 19:08:32 +0300
commit102e79c5a0d30882cf5185b1b760edaed307afb9 (patch)
treeb6c2bcbf0fbd2623817360a3904ea7949a342c65
parentf526f0d9c0a3a68429f07c34c075ede4a7faff10 (diff)
parent316c85c6d32c4cdbb8a6fc37d0bb2fd9e6505b5c (diff)
Merge branch 'ps-update-reference-reports-cause' into 'master'
Log cause of the failed reference update Closes #4677 See merge request https://gitlab.com/gitlab-org/gitaly/-/merge_requests/5950 Merged-by: Justin Tobler <jtobler@gitlab.com> Approved-by: Justin Tobler <jtobler@gitlab.com> Approved-by: Quang-Minh Nguyen <qmnguyen@gitlab.com> Reviewed-by: Pavlo Strokov <pstrokov@gitlab.com> Reviewed-by: Quang-Minh Nguyen <qmnguyen@gitlab.com> Reviewed-by: Justin Tobler <jtobler@gitlab.com> Co-authored-by: Pavlo Strokov <pstrokov@gitlab.com>
-rw-r--r--internal/git/updateref/update_with_hooks.go9
-rw-r--r--internal/gitaly/service/operations/apply_patch_test.go173
-rw-r--r--internal/gitaly/service/operations/branches_test.go15
-rw-r--r--internal/gitaly/service/operations/cherry_pick_test.go154
-rw-r--r--internal/gitaly/service/operations/commit_files_test.go6
-rw-r--r--internal/gitaly/service/operations/merge_test.go40
-rw-r--r--internal/gitaly/service/operations/revert_test.go86
-rw-r--r--internal/gitaly/service/operations/tags_test.go241
8 files changed, 445 insertions, 279 deletions
diff --git a/internal/git/updateref/update_with_hooks.go b/internal/git/updateref/update_with_hooks.go
index bb8599ff8..6efa375c6 100644
--- a/internal/git/updateref/update_with_hooks.go
+++ b/internal/git/updateref/update_with_hooks.go
@@ -115,6 +115,13 @@ type Error struct {
// OldOID and NewOID are the expected object IDs previous to and after the update if it
// would have succeeded.
OldOID, NewOID git.ObjectID
+ // Cause is an actual cause of the error.
+ Cause error
+}
+
+// Unwrap returns Cause.
+func (e Error) Unwrap() error {
+ return e.Cause
}
func (e Error) Error() string {
@@ -260,6 +267,7 @@ func (u *UpdaterWithHooks) UpdateReference(
Reference: reference,
OldOID: oldrev,
NewOID: newrev,
+ Cause: err,
}
}
@@ -272,6 +280,7 @@ func (u *UpdaterWithHooks) UpdateReference(
Reference: reference,
OldOID: oldrev,
NewOID: newrev,
+ Cause: err,
}
}
diff --git a/internal/gitaly/service/operations/apply_patch_test.go b/internal/gitaly/service/operations/apply_patch_test.go
index a1e4f94d7..15790e89a 100644
--- a/internal/gitaly/service/operations/apply_patch_test.go
+++ b/internal/gitaly/service/operations/apply_patch_test.go
@@ -58,6 +58,13 @@ To restore the original branch and stop patching, run "git am --abort".
newTree []gittest.TreeEntry
}
+ type expected struct {
+ oldOID string
+ err error
+ branchCreation bool
+ tree []gittest.TreeEntry
+ }
+
for _, tc := range []struct {
desc string
// sends a request to a non-existent repository
@@ -70,8 +77,6 @@ To restore the original branch and stop patching, run "git am --abort".
notSentByAuthor bool
// targetBranch is the branch where the patched commit goes.
targetBranch string
- // expectedOldOID is a function which provides the expectedOldOID given the repoPath.
- expectedOldOID func(repoPath string) string
// extraBranches are created with empty commits for verifying the correct base branch
// gets selected.
extraBranches []string
@@ -81,26 +86,28 @@ To restore the original branch and stop patching, run "git am --abort".
// in the series to its parent.
//
// After the patches are generated, they are applied sequentially on the base commit.
- patches []patchDescription
- expectedErr error
- expectedBranchCreation bool
- expectedTree []gittest.TreeEntry
+ patches []patchDescription
+ expected func(t *testing.T, repoPath string) expected
}{
{
desc: "non-existent repository",
targetBranch: git.DefaultBranch,
nonExistentRepository: true,
- expectedErr: testhelper.GitalyOrPraefect(
- testhelper.ToInterceptedMetadata(
- structerr.New("%w", storage.NewRepositoryNotFoundError(cfg.Storages[0].Name, "doesnt-exist")),
- ),
- testhelper.ToInterceptedMetadata(
- structerr.New(
- "mutator call: route repository mutator: get repository id: %w",
- storage.NewRepositoryNotFoundError(cfg.Storages[0].Name, "doesnt-exist"),
+ expected: func(t *testing.T, repoPath string) expected {
+ return expected{
+ err: testhelper.GitalyOrPraefect(
+ testhelper.ToInterceptedMetadata(
+ structerr.New("%w", storage.NewRepositoryNotFoundError(cfg.Storages[0].Name, "doesnt-exist")),
+ ),
+ testhelper.ToInterceptedMetadata(
+ structerr.New(
+ "mutator call: route repository mutator: get repository id: %w",
+ storage.NewRepositoryNotFoundError(cfg.Storages[0].Name, "doesnt-exist"),
+ ),
+ ),
),
- ),
- ),
+ }
+ },
},
{
desc: "creating the first branch does not work",
@@ -115,7 +122,11 @@ To restore the original branch and stop patching, run "git am --abort".
},
},
},
- expectedErr: status.Error(codes.Internal, "no default branch"),
+ expected: func(t *testing.T, repoPath string) expected {
+ return expected{
+ err: status.Error(codes.Internal, "no default branch"),
+ }
+ },
},
{
desc: "creating a new branch from HEAD works",
@@ -132,9 +143,13 @@ To restore the original branch and stop patching, run "git am --abort".
},
},
},
- expectedBranchCreation: true,
- expectedTree: []gittest.TreeEntry{
- {Mode: "100644", Path: "file", Content: "patch 1"},
+ expected: func(t *testing.T, repoPath string) expected {
+ return expected{
+ branchCreation: true,
+ tree: []gittest.TreeEntry{
+ {Mode: "100644", Path: "file", Content: "patch 1"},
+ },
+ }
},
},
{
@@ -152,9 +167,13 @@ To restore the original branch and stop patching, run "git am --abort".
},
},
},
- expectedBranchCreation: true,
- expectedTree: []gittest.TreeEntry{
- {Mode: "100644", Path: "file", Content: "patch 1"},
+ expected: func(t *testing.T, repoPath string) expected {
+ return expected{
+ branchCreation: true,
+ tree: []gittest.TreeEntry{
+ {Mode: "100644", Path: "file", Content: "patch 1"},
+ },
+ }
},
},
{
@@ -179,8 +198,12 @@ To restore the original branch and stop patching, run "git am --abort".
},
},
},
- expectedTree: []gittest.TreeEntry{
- {Mode: "100644", Path: "file", Content: "patch 2"},
+ expected: func(t *testing.T, repoPath string) expected {
+ return expected{
+ tree: []gittest.TreeEntry{
+ {Mode: "100644", Path: "file", Content: "patch 2"},
+ },
+ }
},
},
{
@@ -198,8 +221,12 @@ To restore the original branch and stop patching, run "git am --abort".
},
},
},
- expectedTree: []gittest.TreeEntry{
- {Mode: "100644", Path: "file", Content: "patch 1"},
+ expected: func(t *testing.T, repoPath string) expected {
+ return expected{
+ tree: []gittest.TreeEntry{
+ {Mode: "100644", Path: "file", Content: "patch 1"},
+ },
+ }
},
},
{
@@ -224,8 +251,12 @@ To restore the original branch and stop patching, run "git am --abort".
},
},
},
- expectedTree: []gittest.TreeEntry{
- {Mode: "100644", Path: "file", Content: "patch 1"},
+ expected: func(t *testing.T, repoPath string) expected {
+ return expected{
+ tree: []gittest.TreeEntry{
+ {Mode: "100644", Path: "file", Content: "patch 1"},
+ },
+ }
},
},
{
@@ -247,7 +278,11 @@ To restore the original branch and stop patching, run "git am --abort".
},
},
},
- expectedErr: errPatchingFailed,
+ expected: func(t *testing.T, repoPath string) expected {
+ return expected{
+ err: errPatchingFailed,
+ }
+ },
},
{
desc: "patching fails due to add-add conflict",
@@ -268,7 +303,11 @@ To restore the original branch and stop patching, run "git am --abort".
},
},
},
- expectedErr: errPatchingFailed,
+ expected: func(t *testing.T, repoPath string) expected {
+ return expected{
+ err: errPatchingFailed,
+ }
+ },
},
{
desc: "patch applies using rename detection",
@@ -289,8 +328,12 @@ To restore the original branch and stop patching, run "git am --abort".
},
},
},
- expectedTree: []gittest.TreeEntry{
- {Mode: "100644", Path: "moved-file", Content: "line 1\nline 2\nline 3\nline 4\nadded\n"},
+ expected: func(t *testing.T, repoPath string) expected {
+ return expected{
+ tree: []gittest.TreeEntry{
+ {Mode: "100644", Path: "moved-file", Content: "line 1\nline 2\nline 3\nline 4\nadded\n"},
+ },
+ }
},
},
{
@@ -310,7 +353,11 @@ To restore the original branch and stop patching, run "git am --abort".
},
},
},
- expectedErr: errPatchingFailed,
+ expected: func(t *testing.T, repoPath string) expected {
+ return expected{
+ err: errPatchingFailed,
+ }
+ },
},
{
desc: "existing branch + correct expectedOldOID",
@@ -326,11 +373,14 @@ To restore the original branch and stop patching, run "git am --abort".
},
},
},
- expectedTree: []gittest.TreeEntry{
- {Mode: "100644", Path: "file", Content: "patch 1"},
- },
- expectedOldOID: func(repoPath string) string {
- return text.ChompBytes(gittest.Exec(t, cfg, "-C", repoPath, "rev-parse", git.DefaultBranch))
+ expected: func(t *testing.T, repoPath string) expected {
+ oid := text.ChompBytes(gittest.Exec(t, cfg, "-C", repoPath, "rev-parse", git.DefaultBranch))
+ return expected{
+ tree: []gittest.TreeEntry{
+ {Mode: "100644", Path: "file", Content: "patch 1"},
+ },
+ oldOID: oid,
+ }
},
},
{
@@ -347,8 +397,12 @@ To restore the original branch and stop patching, run "git am --abort".
},
},
},
- expectedOldOID: func(repoPath string) string { return "foo" },
- expectedErr: structerr.NewInternal(fmt.Sprintf(`expected old object id not expected SHA format: invalid object ID: "foo", expected length %v, got 3`, gittest.DefaultObjectHash.EncodedLen())),
+ expected: func(t *testing.T, repoPath string) expected {
+ return expected{
+ oldOID: "foo",
+ err: structerr.NewInternal(fmt.Sprintf(`expected old object id not expected SHA format: invalid object ID: "foo", expected length %v, got 3`, gittest.DefaultObjectHash.EncodedLen())),
+ }
+ },
},
{
desc: "existing branch + valid but unavailable expectedOldOID",
@@ -364,8 +418,12 @@ To restore the original branch and stop patching, run "git am --abort".
},
},
},
- expectedOldOID: func(repoPath string) string { return gittest.DefaultObjectHash.ZeroOID.String() },
- expectedErr: structerr.NewInternal("expected old object cannot be resolved: reference not found"),
+ expected: func(t *testing.T, repoPath string) expected {
+ return expected{
+ oldOID: gittest.DefaultObjectHash.ZeroOID.String(),
+ err: structerr.NewInternal("expected old object cannot be resolved: reference not found"),
+ }
+ },
},
{
desc: "existing branch + expectedOldOID set to an old commit OID",
@@ -381,15 +439,21 @@ To restore the original branch and stop patching, run "git am --abort".
},
},
},
- expectedOldOID: func(repoPath string) string {
+ expected: func(t *testing.T, repoPath string) expected {
currentCommit := text.ChompBytes(gittest.Exec(t, cfg, "-C", repoPath, "rev-parse", git.DefaultBranch))
// add a new commit to the default branch so we can point at
// the old one, this is because by default the test only
// creates one commit
- gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(git.ObjectID(currentCommit)), gittest.WithBranch(git.DefaultBranch))
- return currentCommit
+ futureCommit := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(git.ObjectID(currentCommit)), gittest.WithBranch(git.DefaultBranch))
+ return expected{
+ oldOID: currentCommit,
+ err: testhelper.WithInterceptedMetadata(
+ structerr.NewInternal(`update reference: Could not update %s. Please refresh and try again.`, git.DefaultRef),
+ "stderr",
+ fmt.Sprintf("fatal: prepare: cannot lock ref 'refs/heads/main': is at %s but expected %s\n", futureCommit, currentCommit),
+ ),
+ }
},
- expectedErr: structerr.NewInternal(`update reference: Could not update %s. Please refresh and try again.`, git.DefaultRef),
},
} {
tc := tc
@@ -459,10 +523,7 @@ To restore the original branch and stop patching, run "git am --abort".
repoProto.RelativePath = "doesnt-exist"
}
- expectedOldOID := ""
- if tc.expectedOldOID != nil {
- expectedOldOID = tc.expectedOldOID(repoPath)
- }
+ expectedValues := tc.expected(t, repoPath)
require.NoError(t, stream.Send(&gitalypb.UserApplyPatchRequest{
UserApplyPatchRequestPayload: &gitalypb.UserApplyPatchRequest_Header_{
@@ -471,7 +532,7 @@ To restore the original branch and stop patching, run "git am --abort".
User: gittest.TestUser,
TargetBranch: []byte(tc.targetBranch),
Timestamp: requestTimestamp,
- ExpectedOldOid: expectedOldOID,
+ ExpectedOldOid: expectedValues.oldOID,
},
},
}))
@@ -504,8 +565,8 @@ To restore the original branch and stop patching, run "git am --abort".
}
actualResponse, err := stream.CloseAndRecv()
- if tc.expectedErr != nil {
- testhelper.RequireGrpcError(t, tc.expectedErr, err)
+ if expectedValues.err != nil {
+ testhelper.RequireGrpcError(t, expectedValues.err, err)
return
}
@@ -516,7 +577,7 @@ To restore the original branch and stop patching, run "git am --abort".
testhelper.ProtoEqual(t, &gitalypb.UserApplyPatchResponse{
BranchUpdate: &gitalypb.OperationBranchUpdate{
RepoCreated: false,
- BranchCreated: tc.expectedBranchCreation,
+ BranchCreated: expectedValues.branchCreation,
},
}, actualResponse)
@@ -549,7 +610,7 @@ To restore the original branch and stop patching, run "git am --abort".
actualCommit,
)
- gittest.RequireTree(t, cfg, repoPath, commitID, tc.expectedTree)
+ gittest.RequireTree(t, cfg, repoPath, commitID, expectedValues.tree)
})
}
}
diff --git a/internal/gitaly/service/operations/branches_test.go b/internal/gitaly/service/operations/branches_test.go
index 0a58649f1..8c93af9f8 100644
--- a/internal/gitaly/service/operations/branches_test.go
+++ b/internal/gitaly/service/operations/branches_test.go
@@ -28,6 +28,7 @@ import (
"gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testserver"
"gitlab.com/gitlab-org/gitaly/v16/internal/transaction/txinfo"
"gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb"
+ "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb/testproto"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
@@ -391,7 +392,11 @@ func TestUserCreateBranch_Failure(t *testing.T) {
branchName: "master",
startPoint: "master",
user: gittest.TestUser,
- err: status.Errorf(codes.FailedPrecondition, "Could not update refs/heads/master. Please refresh and try again."),
+ err: testhelper.WithInterceptedMetadata(
+ structerr.NewFailedPrecondition("Could not update refs/heads/master. Please refresh and try again."),
+ "stderr",
+ "fatal: prepare: cannot lock ref 'refs/heads/master': reference already exists\n",
+ ),
},
{
desc: "conflicting with refs/heads/improve/awesome",
@@ -592,7 +597,7 @@ func TestUserDeleteBranch(t *testing.T) {
repoProto, repoPath := gittest.CreateRepository(t, ctx, cfg)
firstCommit := gittest.WriteCommit(t, cfg, repoPath)
- gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("master"), gittest.WithParents(firstCommit))
+ secondCommit := gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("master"), gittest.WithParents(firstCommit))
gittest.Exec(t, cfg, "-C", repoPath, "branch", branchName, "master")
@@ -604,7 +609,11 @@ func TestUserDeleteBranch(t *testing.T) {
ExpectedOldOid: firstCommit.String(),
},
repoPath: repoPath,
- expectedErr: structerr.NewFailedPrecondition("reference update failed: Could not update refs/heads/random-branch. Please refresh and try again.").
+ expectedErr: structerr.NewFailedPrecondition("reference update failed: Could not update refs/heads/%s. Please refresh and try again.", branchName).
+ WithDetail(&testproto.ErrorMetadata{
+ Key: []byte("stderr"),
+ Value: []byte(fmt.Sprintf("fatal: prepare: cannot lock ref 'refs/heads/%s': is at %s but expected %s\n", branchName, secondCommit, firstCommit)),
+ }).
WithDetail(&gitalypb.UserDeleteBranchError{
Error: &gitalypb.UserDeleteBranchError_ReferenceUpdate{
ReferenceUpdate: &gitalypb.ReferenceUpdateError{
diff --git a/internal/gitaly/service/operations/cherry_pick_test.go b/internal/gitaly/service/operations/cherry_pick_test.go
index 3b487300f..87a30d55e 100644
--- a/internal/gitaly/service/operations/cherry_pick_test.go
+++ b/internal/gitaly/service/operations/cherry_pick_test.go
@@ -42,13 +42,12 @@ func TestUserCherryPick(t *testing.T) {
}
testCases := []struct {
- desc string
- setup func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse)
- expectedErr error
+ desc string
+ setup func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse, error)
}{
{
desc: "branch exists",
- setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse) {
+ setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse, error) {
gittest.Exec(t, data.cfg, "-C", data.repoPath, "branch", destinationBranch, "master")
return &gitalypb.UserCherryPickRequest{
@@ -57,14 +56,16 @@ func TestUserCherryPick(t *testing.T) {
Commit: data.cherryPickedCommit,
BranchName: []byte(destinationBranch),
Message: []byte("Cherry-picking " + data.cherryPickedCommit.Id),
- }, &gitalypb.UserCherryPickResponse{
+ },
+ &gitalypb.UserCherryPickResponse{
BranchUpdate: &gitalypb.OperationBranchUpdate{},
- }
+ },
+ nil
},
},
{
desc: "branch exists + expectedOldOID",
- setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse) {
+ setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse, error) {
gittest.Exec(t, data.cfg, "-C", data.repoPath, "branch", destinationBranch, "master")
return &gitalypb.UserCherryPickRequest{
@@ -74,14 +75,16 @@ func TestUserCherryPick(t *testing.T) {
BranchName: []byte(destinationBranch),
Message: []byte("Cherry-picking " + data.cherryPickedCommit.Id),
ExpectedOldOid: data.masterCommit,
- }, &gitalypb.UserCherryPickResponse{
+ },
+ &gitalypb.UserCherryPickResponse{
BranchUpdate: &gitalypb.OperationBranchUpdate{},
- }
+ },
+ nil
},
},
{
desc: "nonexistent branch + start_repository == repository",
- setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse) {
+ setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse, error) {
return &gitalypb.UserCherryPickRequest{
Repository: data.repoProto,
User: gittest.TestUser,
@@ -89,14 +92,16 @@ func TestUserCherryPick(t *testing.T) {
BranchName: []byte(destinationBranch),
Message: []byte("Cherry-picking " + data.cherryPickedCommit.Id),
StartBranchName: []byte("master"),
- }, &gitalypb.UserCherryPickResponse{
+ },
+ &gitalypb.UserCherryPickResponse{
BranchUpdate: &gitalypb.OperationBranchUpdate{BranchCreated: true},
- }
+ },
+ nil
},
},
{
desc: "nonexistent branch + start_repository != repository",
- setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse) {
+ setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse, error) {
return &gitalypb.UserCherryPickRequest{
Repository: data.repoProto,
User: gittest.TestUser,
@@ -105,14 +110,16 @@ func TestUserCherryPick(t *testing.T) {
Message: []byte("Cherry-picking " + data.cherryPickedCommit.Id),
StartBranchName: []byte("master"),
StartRepository: data.copyRepoProto,
- }, &gitalypb.UserCherryPickResponse{
+ },
+ &gitalypb.UserCherryPickResponse{
BranchUpdate: &gitalypb.OperationBranchUpdate{BranchCreated: true},
- }
+ },
+ nil
},
},
{
desc: "nonexistent branch + empty start_repository",
- setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse) {
+ setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse, error) {
return &gitalypb.UserCherryPickRequest{
Repository: data.repoProto,
User: gittest.TestUser,
@@ -120,14 +127,16 @@ func TestUserCherryPick(t *testing.T) {
BranchName: []byte(destinationBranch),
Message: []byte("Cherry-picking " + data.cherryPickedCommit.Id),
StartBranchName: []byte("master"),
- }, &gitalypb.UserCherryPickResponse{
+ },
+ &gitalypb.UserCherryPickResponse{
BranchUpdate: &gitalypb.OperationBranchUpdate{BranchCreated: true},
- }
+ },
+ nil
},
},
{
desc: "branch exists with dry run",
- setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse) {
+ setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse, error) {
gittest.Exec(t, data.cfg, "-C", data.repoPath, "branch", destinationBranch, "master")
return &gitalypb.UserCherryPickRequest{
@@ -138,14 +147,16 @@ func TestUserCherryPick(t *testing.T) {
Message: []byte("Cherry-picking " + data.cherryPickedCommit.Id),
StartBranchName: []byte("master"),
DryRun: true,
- }, &gitalypb.UserCherryPickResponse{
+ },
+ &gitalypb.UserCherryPickResponse{
BranchUpdate: &gitalypb.OperationBranchUpdate{},
- }
+ },
+ nil
},
},
{
desc: "nonexistent branch + start_repository == repository with dry run",
- setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse) {
+ setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse, error) {
return &gitalypb.UserCherryPickRequest{
Repository: data.repoProto,
User: gittest.TestUser,
@@ -154,14 +165,16 @@ func TestUserCherryPick(t *testing.T) {
Message: []byte("Cherry-picking " + data.cherryPickedCommit.Id),
StartBranchName: []byte("master"),
DryRun: true,
- }, &gitalypb.UserCherryPickResponse{
+ },
+ &gitalypb.UserCherryPickResponse{
BranchUpdate: &gitalypb.OperationBranchUpdate{BranchCreated: true},
- }
+ },
+ nil
},
},
{
desc: "nonexistent branch + start_repository != repository with dry run",
- setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse) {
+ setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse, error) {
return &gitalypb.UserCherryPickRequest{
Repository: data.repoProto,
User: gittest.TestUser,
@@ -171,14 +184,16 @@ func TestUserCherryPick(t *testing.T) {
StartBranchName: []byte("master"),
StartRepository: data.copyRepoProto,
DryRun: true,
- }, &gitalypb.UserCherryPickResponse{
+ },
+ &gitalypb.UserCherryPickResponse{
BranchUpdate: &gitalypb.OperationBranchUpdate{BranchCreated: true},
- }
+ },
+ nil
},
},
{
desc: "nonexistent branch + empty start_repository with dry run",
- setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse) {
+ setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse, error) {
return &gitalypb.UserCherryPickRequest{
Repository: data.repoProto,
User: gittest.TestUser,
@@ -187,53 +202,57 @@ func TestUserCherryPick(t *testing.T) {
Message: []byte("Cherry-picking " + data.cherryPickedCommit.Id),
StartBranchName: []byte("master"),
DryRun: true,
- }, &gitalypb.UserCherryPickResponse{
+ },
+ &gitalypb.UserCherryPickResponse{
BranchUpdate: &gitalypb.OperationBranchUpdate{BranchCreated: true},
- }
+ },
+ nil
},
},
{
desc: "invalid expectedOldOID",
- setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse) {
+ setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse, error) {
gittest.Exec(t, data.cfg, "-C", data.repoPath, "branch", destinationBranch, "master")
return &gitalypb.UserCherryPickRequest{
- Repository: data.repoProto,
- User: gittest.TestUser,
- Commit: data.cherryPickedCommit,
- BranchName: []byte(destinationBranch),
- Message: []byte("Cherry-picking " + data.cherryPickedCommit.Id),
- ExpectedOldOid: "foobar",
- }, &gitalypb.UserCherryPickResponse{}
+ Repository: data.repoProto,
+ User: gittest.TestUser,
+ Commit: data.cherryPickedCommit,
+ BranchName: []byte(destinationBranch),
+ Message: []byte("Cherry-picking " + data.cherryPickedCommit.Id),
+ ExpectedOldOid: "foobar",
+ },
+ &gitalypb.UserCherryPickResponse{},
+ testhelper.WithInterceptedMetadata(
+ structerr.NewInvalidArgument(fmt.Sprintf("invalid expected old object ID: invalid object ID: \"foobar\", expected length %v, got 6", gittest.DefaultObjectHash.EncodedLen())),
+ "old_object_id", "foobar")
},
- expectedErr: testhelper.WithInterceptedMetadata(
- structerr.NewInvalidArgument(fmt.Sprintf("invalid expected old object ID: invalid object ID: \"foobar\", expected length %v, got 6", gittest.DefaultObjectHash.EncodedLen())),
- "old_object_id", "foobar"),
},
{
desc: "valid but non-existent expectedOldOID",
- setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse) {
+ setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse, error) {
gittest.Exec(t, data.cfg, "-C", data.repoPath, "branch", destinationBranch, "master")
return &gitalypb.UserCherryPickRequest{
- Repository: data.repoProto,
- User: gittest.TestUser,
- Commit: data.cherryPickedCommit,
- BranchName: []byte(destinationBranch),
- Message: []byte("Cherry-picking " + data.cherryPickedCommit.Id),
- ExpectedOldOid: gittest.DefaultObjectHash.ZeroOID.String(),
- }, &gitalypb.UserCherryPickResponse{}
+ Repository: data.repoProto,
+ User: gittest.TestUser,
+ Commit: data.cherryPickedCommit,
+ BranchName: []byte(destinationBranch),
+ Message: []byte("Cherry-picking " + data.cherryPickedCommit.Id),
+ ExpectedOldOid: gittest.DefaultObjectHash.ZeroOID.String(),
+ },
+ &gitalypb.UserCherryPickResponse{},
+ testhelper.WithInterceptedMetadata(
+ structerr.NewInvalidArgument("cannot resolve expected old object ID: reference not found"),
+ "old_object_id", gittest.DefaultObjectHash.ZeroOID)
},
- expectedErr: testhelper.WithInterceptedMetadata(
- structerr.NewInvalidArgument("cannot resolve expected old object ID: reference not found"),
- "old_object_id", gittest.DefaultObjectHash.ZeroOID),
},
{
desc: "incorrect expectedOldOID",
- setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse) {
+ setup: func(data setupData) (*gitalypb.UserCherryPickRequest, *gitalypb.UserCherryPickResponse, error) {
gittest.Exec(t, data.cfg, "-C", data.repoPath, "branch", destinationBranch, "master")
- gittest.WriteCommit(t, data.cfg, data.repoPath,
+ commit := gittest.WriteCommit(t, data.cfg, data.repoPath,
gittest.WithParents(git.ObjectID(data.masterCommit)),
gittest.WithBranch("master"),
gittest.WithTreeEntries(
@@ -242,15 +261,20 @@ func TestUserCherryPick(t *testing.T) {
)
return &gitalypb.UserCherryPickRequest{
- Repository: data.repoProto,
- User: gittest.TestUser,
- Commit: data.cherryPickedCommit,
- BranchName: []byte("master"),
- Message: []byte("Cherry-picking " + data.cherryPickedCommit.Id),
- ExpectedOldOid: data.masterCommit,
- }, &gitalypb.UserCherryPickResponse{}
+ Repository: data.repoProto,
+ User: gittest.TestUser,
+ Commit: data.cherryPickedCommit,
+ BranchName: []byte("master"),
+ Message: []byte("Cherry-picking " + data.cherryPickedCommit.Id),
+ ExpectedOldOid: data.masterCommit,
+ },
+ &gitalypb.UserCherryPickResponse{},
+ testhelper.WithInterceptedMetadata(
+ structerr.NewInternal("update reference with hooks: Could not update refs/heads/master. Please refresh and try again."),
+ "stderr",
+ fmt.Sprintf("fatal: prepare: cannot lock ref 'refs/heads/master': is at %s but expected %s\n", commit, data.masterCommit),
+ )
},
- expectedErr: structerr.NewInternal("update reference with hooks: Could not update refs/heads/master. Please refresh and try again."),
},
}
@@ -292,7 +316,7 @@ func TestUserCherryPick(t *testing.T) {
copyCherryPickedCommit, err := copyRepo.ReadCommit(ctx, cherryPickCommitCopyID.Revision())
require.NoError(t, err)
- req, expectedResp := tc.setup(setupData{
+ req, expectedResp, expectedErr := tc.setup(setupData{
cfg: cfg,
repoPath: repoPath,
repoProto: repoProto,
@@ -304,8 +328,8 @@ func TestUserCherryPick(t *testing.T) {
})
resp, err := client.UserCherryPick(ctx, req)
- if tc.expectedErr != nil || err != nil {
- testhelper.RequireGrpcError(t, tc.expectedErr, err)
+ if expectedErr != nil || err != nil {
+ testhelper.RequireGrpcError(t, expectedErr, err)
return
}
diff --git a/internal/gitaly/service/operations/commit_files_test.go b/internal/gitaly/service/operations/commit_files_test.go
index 4ad7641b3..77ba2a760 100644
--- a/internal/gitaly/service/operations/commit_files_test.go
+++ b/internal/gitaly/service/operations/commit_files_test.go
@@ -1164,7 +1164,11 @@ func testSuccessfulUserCommitFilesRequest(t *testing.T, ctx context.Context) {
repoPath: repoPath,
branchName: "few-commits",
expectedOldOID: text.ChompBytes(gittest.Exec(t, cfg, "-C", repoPath, "rev-parse", "refs/heads/few-commits~1")),
- expectedError: structerr.NewFailedPrecondition("Could not update refs/heads/few-commits. Please refresh and try again."),
+ expectedError: testhelper.WithInterceptedMetadata(
+ structerr.NewFailedPrecondition("Could not update refs/heads/few-commits. Please refresh and try again."),
+ "stderr",
+ "fatal: prepare: cannot lock ref 'refs/heads/few-commits': is at 0031876facac3f2b2702a0e53a26e89939a42209 but expected bf6e164cac2dc32b1f391ca4290badcbe4ffc5fb\n",
+ ),
},
{
desc: "existing repo, new branch",
diff --git a/internal/gitaly/service/operations/merge_test.go b/internal/gitaly/service/operations/merge_test.go
index bc5c78e42..c48fbb848 100644
--- a/internal/gitaly/service/operations/merge_test.go
+++ b/internal/gitaly/service/operations/merge_test.go
@@ -28,6 +28,7 @@ import (
"gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testcfg"
"gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testserver"
"gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb"
+ "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb/testproto"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
@@ -169,7 +170,7 @@ func testUserMergeBranch(t *testing.T, ctx context.Context) {
desc: "incorrect expectedOldOID",
hooks: []string{},
setup: func(data setupData) setupResponse {
- gittest.WriteCommit(t, cfg, data.repoPath,
+ secondCommit := gittest.WriteCommit(t, cfg, data.repoPath,
gittest.WithParents(git.ObjectID(data.masterCommit)),
gittest.WithBranch(data.branch),
gittest.WithTreeEntries(
@@ -190,8 +191,12 @@ func testUserMergeBranch(t *testing.T, ctx context.Context) {
secondRequest: &gitalypb.UserMergeBranchRequest{Apply: true},
secondExpectedResponse: &gitalypb.OperationBranchUpdate{},
secondExpectedErr: func(response *gitalypb.UserMergeBranchResponse) error {
- return structerr.NewFailedPrecondition("Could not update refs/heads/master. Please refresh and try again.").WithDetail(
- &gitalypb.UserMergeBranchError{
+ return structerr.NewFailedPrecondition("Could not update refs/heads/master. Please refresh and try again.").
+ WithDetail(&testproto.ErrorMetadata{
+ Key: []byte("stderr"),
+ Value: []byte(fmt.Sprintf("fatal: prepare: cannot lock ref 'refs/heads/master': is at %s but expected %s\n", secondCommit, data.masterCommit)),
+ }).
+ WithDetail(&gitalypb.UserMergeBranchError{
Error: &gitalypb.UserMergeBranchError_ReferenceUpdate{
ReferenceUpdate: &gitalypb.ReferenceUpdateError{
ReferenceName: []byte("refs/heads/" + data.branch),
@@ -199,8 +204,7 @@ func testUserMergeBranch(t *testing.T, ctx context.Context) {
NewOid: response.GetCommitId(),
},
},
- },
- )
+ })
},
}
},
@@ -717,17 +721,23 @@ func testUserMergeBranchConcurrentUpdate(t *testing.T, ctx context.Context) {
require.NoError(t, mergeBidi.CloseSend(), "close send")
secondResponse, err := mergeBidi.Recv()
- testhelper.RequireGrpcError(t, structerr.NewFailedPrecondition("Could not update refs/heads/gitaly-merge-test-branch. Please refresh and try again.").WithDetail(
- &gitalypb.UserMergeBranchError{
- Error: &gitalypb.UserMergeBranchError_ReferenceUpdate{
- ReferenceUpdate: &gitalypb.ReferenceUpdateError{
- ReferenceName: []byte("refs/heads/" + mergeBranchName),
- OldOid: "281d3a76f31c812dbf48abce82ccf6860adedd81",
- NewOid: "f0165798887392f9148b55d54a832b005f93a38c",
+ testhelper.RequireGrpcError(t,
+ structerr.NewFailedPrecondition("Could not update refs/heads/gitaly-merge-test-branch. Please refresh and try again.").
+ WithDetail(&testproto.ErrorMetadata{
+ Key: []byte("stderr"),
+ Value: []byte("fatal: prepare: cannot lock ref 'refs/heads/gitaly-merge-test-branch': is at 549090fbeacc6607bc70648d3ba554c355e670c5 but expected 281d3a76f31c812dbf48abce82ccf6860adedd81\n"),
+ }).
+ WithDetail(&gitalypb.UserMergeBranchError{
+ Error: &gitalypb.UserMergeBranchError_ReferenceUpdate{
+ ReferenceUpdate: &gitalypb.ReferenceUpdateError{
+ ReferenceName: []byte("refs/heads/" + mergeBranchName),
+ OldOid: "281d3a76f31c812dbf48abce82ccf6860adedd81",
+ NewOid: "f0165798887392f9148b55d54a832b005f93a38c",
+ },
},
- },
- },
- ), err)
+ }),
+ err,
+ )
require.Nil(t, secondResponse)
commit, err := repo.ReadCommit(ctx, git.Revision(mergeBranchName))
diff --git a/internal/gitaly/service/operations/revert_test.go b/internal/gitaly/service/operations/revert_test.go
index 40c84ada0..16f167a9a 100644
--- a/internal/gitaly/service/operations/revert_test.go
+++ b/internal/gitaly/service/operations/revert_test.go
@@ -31,13 +31,13 @@ func TestUserRevert(t *testing.T) {
expectedCommitID string
repoPath string
request *gitalypb.UserRevertRequest
+ expectedResponse *gitalypb.UserRevertResponse
+ expectedError error
}
testCases := []struct {
- desc string
- setup func(t *testing.T, repoPath string, repoProto *gitalypb.Repository, repo *localrepo.Repo) setupData
- expectedResponse *gitalypb.UserRevertResponse
- expectedErr error
+ desc string
+ setup func(t *testing.T, repoPath string, repoProto *gitalypb.Repository, repo *localrepo.Repo) setupData
}{
{
desc: "successful",
@@ -57,10 +57,10 @@ func TestUserRevert(t *testing.T) {
BranchName: []byte(branchName),
Message: []byte("Reverting " + firstCommitID),
},
+ expectedResponse: &gitalypb.UserRevertResponse{BranchUpdate: &gitalypb.OperationBranchUpdate{}},
+ expectedError: nil,
}
},
- expectedResponse: &gitalypb.UserRevertResponse{BranchUpdate: &gitalypb.OperationBranchUpdate{}},
- expectedErr: nil,
},
{
desc: "nonexistent branch + start_repository == repository",
@@ -81,12 +81,12 @@ func TestUserRevert(t *testing.T) {
StartBranchName: []byte("master"),
Message: []byte("Reverting " + firstCommitID),
},
+ expectedResponse: &gitalypb.UserRevertResponse{BranchUpdate: &gitalypb.OperationBranchUpdate{
+ BranchCreated: true,
+ }},
+ expectedError: nil,
}
},
- expectedResponse: &gitalypb.UserRevertResponse{BranchUpdate: &gitalypb.OperationBranchUpdate{
- BranchCreated: true,
- }},
- expectedErr: nil,
},
{
desc: "nonexistent branch + start_repository != repository",
@@ -111,10 +111,10 @@ func TestUserRevert(t *testing.T) {
StartRepository: startRepoProto,
Message: []byte("Reverting " + firstCommitID),
},
+ expectedResponse: &gitalypb.UserRevertResponse{BranchUpdate: &gitalypb.OperationBranchUpdate{BranchCreated: true, RepoCreated: true}},
+ expectedError: nil,
}
},
- expectedResponse: &gitalypb.UserRevertResponse{BranchUpdate: &gitalypb.OperationBranchUpdate{BranchCreated: true, RepoCreated: true}},
- expectedErr: nil,
},
{
desc: "successful with dry run",
@@ -136,10 +136,10 @@ func TestUserRevert(t *testing.T) {
DryRun: true,
},
expectedCommitID: firstCommit.Id,
+ expectedResponse: &gitalypb.UserRevertResponse{BranchUpdate: &gitalypb.OperationBranchUpdate{}},
+ expectedError: nil,
}
},
- expectedResponse: &gitalypb.UserRevertResponse{BranchUpdate: &gitalypb.OperationBranchUpdate{}},
- expectedErr: nil,
},
{
desc: "nonexistent branch + start_repository == repository with dry run",
@@ -162,12 +162,12 @@ func TestUserRevert(t *testing.T) {
DryRun: true,
},
expectedCommitID: firstCommit.Id,
+ expectedResponse: &gitalypb.UserRevertResponse{BranchUpdate: &gitalypb.OperationBranchUpdate{
+ BranchCreated: true,
+ }},
+ expectedError: nil,
}
},
- expectedResponse: &gitalypb.UserRevertResponse{BranchUpdate: &gitalypb.OperationBranchUpdate{
- BranchCreated: true,
- }},
- expectedErr: nil,
},
{
desc: "nonexistent branch + start_repository != repository with dry run",
@@ -194,10 +194,10 @@ func TestUserRevert(t *testing.T) {
DryRun: true,
},
expectedCommitID: firstCommitID.String(),
+ expectedResponse: &gitalypb.UserRevertResponse{BranchUpdate: &gitalypb.OperationBranchUpdate{BranchCreated: true, RepoCreated: true}},
+ expectedError: nil,
}
},
- expectedResponse: &gitalypb.UserRevertResponse{BranchUpdate: &gitalypb.OperationBranchUpdate{BranchCreated: true, RepoCreated: true}},
- expectedErr: nil,
},
{
@@ -207,12 +207,12 @@ func TestUserRevert(t *testing.T) {
request: &gitalypb.UserRevertRequest{
Repository: nil,
},
+ expectedError: testhelper.GitalyOrPraefect(
+ structerr.NewInvalidArgument("%w", storage.ErrRepositoryNotSet),
+ structerr.NewInvalidArgument("repo scoped: %w", storage.ErrRepositoryNotSet),
+ ),
}
},
- expectedErr: testhelper.GitalyOrPraefect(
- structerr.NewInvalidArgument("%w", storage.ErrRepositoryNotSet),
- structerr.NewInvalidArgument("repo scoped: %w", storage.ErrRepositoryNotSet),
- ),
},
{
desc: "empty user",
@@ -221,9 +221,9 @@ func TestUserRevert(t *testing.T) {
request: &gitalypb.UserRevertRequest{
Repository: repoProto,
},
+ expectedError: structerr.NewInvalidArgument("empty User"),
}
},
- expectedErr: structerr.NewInvalidArgument("empty User"),
},
{
desc: "empty commit",
@@ -233,9 +233,9 @@ func TestUserRevert(t *testing.T) {
Repository: repoProto,
User: gittest.TestUser,
},
+ expectedError: structerr.NewInvalidArgument("empty Commit"),
}
},
- expectedErr: structerr.NewInvalidArgument("empty Commit"),
},
{
desc: "empty branch name",
@@ -253,9 +253,9 @@ func TestUserRevert(t *testing.T) {
Commit: firstCommit,
Message: []byte("Reverting " + firstCommitID),
},
+ expectedError: structerr.NewInvalidArgument("empty BranchName"),
}
},
- expectedErr: structerr.NewInvalidArgument("empty BranchName"),
},
{
desc: "empty message",
@@ -273,9 +273,9 @@ func TestUserRevert(t *testing.T) {
Commit: firstCommit,
BranchName: []byte(branchName),
},
+ expectedError: structerr.NewInvalidArgument("empty Message"),
}
},
- expectedErr: structerr.NewInvalidArgument("empty Message"),
},
{
desc: "successful + expectedOldOID",
@@ -296,10 +296,10 @@ func TestUserRevert(t *testing.T) {
Message: []byte("Reverting " + firstCommitID),
ExpectedOldOid: firstCommitID.String(),
},
+ expectedResponse: &gitalypb.UserRevertResponse{BranchUpdate: &gitalypb.OperationBranchUpdate{}},
+ expectedError: nil,
}
},
- expectedResponse: &gitalypb.UserRevertResponse{BranchUpdate: &gitalypb.OperationBranchUpdate{}},
- expectedErr: nil,
},
{
desc: "successful + invalid expectedOldOID",
@@ -319,11 +319,11 @@ func TestUserRevert(t *testing.T) {
Message: []byte("Reverting " + firstCommitID),
ExpectedOldOid: "foobar",
},
+ expectedError: testhelper.WithInterceptedMetadata(
+ structerr.NewInvalidArgument(fmt.Sprintf(`invalid expected old object ID: invalid object ID: "foobar", expected length %v, got 6`, gittest.DefaultObjectHash.EncodedLen())),
+ "old_object_id", "foobar"),
}
},
- expectedErr: testhelper.WithInterceptedMetadata(
- structerr.NewInvalidArgument(fmt.Sprintf(`invalid expected old object ID: invalid object ID: "foobar", expected length %v, got 6`, gittest.DefaultObjectHash.EncodedLen())),
- "old_object_id", "foobar"),
},
{
desc: "expectedOldOID with valid SHA, but not present in repo",
@@ -343,11 +343,11 @@ func TestUserRevert(t *testing.T) {
Message: []byte("Reverting " + firstCommitID),
ExpectedOldOid: gittest.DefaultObjectHash.ZeroOID.String(),
},
+ expectedError: testhelper.WithInterceptedMetadata(
+ structerr.NewInvalidArgument("cannot resolve expected old object ID: reference not found"),
+ "old_object_id", gittest.DefaultObjectHash.ZeroOID),
}
},
- expectedErr: testhelper.WithInterceptedMetadata(
- structerr.NewInvalidArgument("cannot resolve expected old object ID: reference not found"),
- "old_object_id", gittest.DefaultObjectHash.ZeroOID),
},
{
desc: "expectedOldOID pointing to old commit",
@@ -370,9 +370,13 @@ func TestUserRevert(t *testing.T) {
Message: []byte("Reverting " + secondCommitID),
ExpectedOldOid: firstCommitID.String(),
},
+ expectedError: testhelper.WithInterceptedMetadata(
+ structerr.NewInternal("update reference with hooks: Could not update refs/heads/%s. Please refresh and try again.", branchName),
+ "stderr",
+ fmt.Sprintf("fatal: prepare: cannot lock ref 'refs/heads/%s': is at %s but expected %s\n", branchName, secondCommitID, firstCommitID),
+ ),
}
},
- expectedErr: structerr.NewInternal("update reference with hooks: Could not update refs/heads/%s. Please refresh and try again.", branchName),
},
}
@@ -388,14 +392,14 @@ func TestUserRevert(t *testing.T) {
data := tc.setup(t, repoPath, repoProto, repo)
response, err := client.UserRevert(ctx, data.request)
- testhelper.RequireGrpcError(t, tc.expectedErr, err)
+ testhelper.RequireGrpcError(t, data.expectedError, err)
- if tc.expectedErr != nil {
+ if data.expectedError != nil {
return
}
branchCommitID := text.ChompBytes(gittest.Exec(t, cfg, "-C", data.repoPath, "rev-parse", branchName))
- tc.expectedResponse.BranchUpdate.CommitId = branchCommitID
+ data.expectedResponse.BranchUpdate.CommitId = branchCommitID
// For dry-run, we only skip the `update-ref` section, so a non-existent branch
// will be created by `UserRevert`. But, we need to ensure that the
@@ -404,7 +408,7 @@ func TestUserRevert(t *testing.T) {
require.Equal(t, data.expectedCommitID, branchCommitID, "dry run should point at expected commit")
}
- testhelper.ProtoEqual(t, tc.expectedResponse, response)
+ testhelper.ProtoEqual(t, data.expectedResponse, response)
})
}
}
diff --git a/internal/gitaly/service/operations/tags_test.go b/internal/gitaly/service/operations/tags_test.go
index aaf26a39d..4eb891c4b 100644
--- a/internal/gitaly/service/operations/tags_test.go
+++ b/internal/gitaly/service/operations/tags_test.go
@@ -33,206 +33,251 @@ func TestUserDeleteTag(t *testing.T) {
ctx := testhelper.Context(t)
ctx, cfg, client := setupOperationsServiceWithoutRepo(t, ctx)
- testCases := []struct {
- desc string
- setup func() (string, *gitalypb.UserDeleteTagRequest)
+ type setupData struct {
+ repoPath string
+ request *gitalypb.UserDeleteTagRequest
expectedResponse *gitalypb.UserDeleteTagResponse
+ expectedError error
expectedTags []string
- expectedErr error
+ }
+
+ testCases := []struct {
+ desc string
+ setup func(t *testing.T) setupData
}{
{
desc: "successful deletion",
- setup: func() (string, *gitalypb.UserDeleteTagRequest) {
+ setup: func(t *testing.T) setupData {
tagName := "mercury"
repoProto, repoPath := gittest.CreateRepository(t, ctx, cfg)
gittest.WriteCommit(t, cfg, repoPath, gittest.WithReference("refs/tags/"+tagName))
- return repoPath, &gitalypb.UserDeleteTagRequest{
- Repository: repoProto,
- TagName: []byte(tagName),
- User: gittest.TestUser,
+ return setupData{
+ repoPath: repoPath,
+ request: &gitalypb.UserDeleteTagRequest{
+ Repository: repoProto,
+ TagName: []byte(tagName),
+ User: gittest.TestUser,
+ },
+ expectedResponse: &gitalypb.UserDeleteTagResponse{},
}
},
- expectedResponse: &gitalypb.UserDeleteTagResponse{},
},
{
desc: "successful deletion + expectedOldOID",
- setup: func() (string, *gitalypb.UserDeleteTagRequest) {
+ setup: func(t *testing.T) setupData {
tagName := "venus"
repoProto, repoPath := gittest.CreateRepository(t, ctx, cfg)
commit := gittest.WriteCommit(t, cfg, repoPath, gittest.WithReference("refs/tags/"+tagName))
- return repoPath, &gitalypb.UserDeleteTagRequest{
- Repository: repoProto,
- TagName: []byte(tagName),
- User: gittest.TestUser,
- ExpectedOldOid: string(commit),
+ return setupData{
+ repoPath: repoPath,
+ request: &gitalypb.UserDeleteTagRequest{
+ Repository: repoProto,
+ TagName: []byte(tagName),
+ User: gittest.TestUser,
+ ExpectedOldOid: string(commit),
+ },
+ expectedResponse: &gitalypb.UserDeleteTagResponse{},
}
},
- expectedResponse: &gitalypb.UserDeleteTagResponse{},
},
{
desc: "possible to delete a tag called refs/tags/something",
- setup: func() (string, *gitalypb.UserDeleteTagRequest) {
+ setup: func(t *testing.T) setupData {
tagName := "refs/tags/earth"
repoProto, repoPath := gittest.CreateRepository(t, ctx, cfg)
gittest.WriteCommit(t, cfg, repoPath, gittest.WithReference("refs/tags/"+tagName))
- return repoPath, &gitalypb.UserDeleteTagRequest{
- Repository: repoProto,
- TagName: []byte(tagName),
- User: gittest.TestUser,
+ return setupData{
+ repoPath: repoPath,
+ request: &gitalypb.UserDeleteTagRequest{
+ Repository: repoProto,
+ TagName: []byte(tagName),
+ User: gittest.TestUser,
+ },
+ expectedResponse: &gitalypb.UserDeleteTagResponse{},
}
},
- expectedResponse: &gitalypb.UserDeleteTagResponse{},
},
{
desc: "no repository provided",
- setup: func() (string, *gitalypb.UserDeleteTagRequest) {
- tagName := "mars"
+ setup: func(t *testing.T) setupData {
_, repoPath := gittest.CreateRepository(t, ctx, cfg)
gittest.WriteCommit(t, cfg, repoPath)
- return repoPath, &gitalypb.UserDeleteTagRequest{
- TagName: []byte(tagName),
- User: gittest.TestUser,
+ return setupData{
+ repoPath: repoPath,
+ request: &gitalypb.UserDeleteTagRequest{
+ TagName: []byte("mars"),
+ User: gittest.TestUser,
+ },
+ expectedError: testhelper.GitalyOrPraefect(
+ structerr.NewInvalidArgument("%w", storage.ErrRepositoryNotSet),
+ structerr.NewInvalidArgument("repo scoped: %w", storage.ErrRepositoryNotSet),
+ ),
}
},
- expectedErr: testhelper.GitalyOrPraefect(
- structerr.NewInvalidArgument("%w", storage.ErrRepositoryNotSet),
- structerr.NewInvalidArgument("repo scoped: %w", storage.ErrRepositoryNotSet),
- ),
},
{
desc: "empty user",
- setup: func() (string, *gitalypb.UserDeleteTagRequest) {
+ setup: func(t *testing.T) setupData {
tagName := "jupiter"
repoProto, repoPath := gittest.CreateRepository(t, ctx, cfg)
gittest.WriteCommit(t, cfg, repoPath)
- return repoPath, &gitalypb.UserDeleteTagRequest{
- Repository: repoProto,
- TagName: []byte(tagName),
+ return setupData{
+ repoPath: repoPath,
+ request: &gitalypb.UserDeleteTagRequest{
+ Repository: repoProto,
+ TagName: []byte(tagName),
+ },
+ expectedError: structerr.NewInvalidArgument("empty user"),
}
},
- expectedErr: structerr.NewInvalidArgument("empty user"),
},
{
desc: "empty tag name",
- setup: func() (string, *gitalypb.UserDeleteTagRequest) {
+ setup: func(t *testing.T) setupData {
repoProto, repoPath := gittest.CreateRepository(t, ctx, cfg)
gittest.WriteCommit(t, cfg, repoPath)
- return repoPath, &gitalypb.UserDeleteTagRequest{
- Repository: repoProto,
- User: gittest.TestUser,
+ return setupData{
+ repoPath: repoPath,
+ request: &gitalypb.UserDeleteTagRequest{
+ Repository: repoProto,
+ User: gittest.TestUser,
+ },
+ expectedError: structerr.NewInvalidArgument("empty tag name"),
}
},
- expectedErr: structerr.NewInvalidArgument("empty tag name"),
},
{
desc: "non-existent tag name",
- setup: func() (string, *gitalypb.UserDeleteTagRequest) {
+ setup: func(t *testing.T) setupData {
tagName := "uranus"
repoProto, repoPath := gittest.CreateRepository(t, ctx, cfg)
gittest.WriteCommit(t, cfg, repoPath, gittest.WithReference("refs/tags/"+tagName))
- return repoPath, &gitalypb.UserDeleteTagRequest{
- Repository: repoProto,
- TagName: []byte("neptune"),
- User: gittest.TestUser,
+ return setupData{
+ repoPath: repoPath,
+ request: &gitalypb.UserDeleteTagRequest{
+ Repository: repoProto,
+ TagName: []byte("neptune"),
+ User: gittest.TestUser,
+ },
+ expectedError: structerr.NewFailedPrecondition("tag not found: %s", "neptune"),
+ expectedTags: []string{"uranus"},
}
},
- expectedErr: structerr.NewFailedPrecondition("tag not found: %s", "neptune"),
- expectedTags: []string{"uranus"},
},
{
desc: "space in tag name",
- setup: func() (string, *gitalypb.UserDeleteTagRequest) {
+ setup: func(t *testing.T) setupData {
tagName := "sun"
repoProto, repoPath := gittest.CreateRepository(t, ctx, cfg)
gittest.WriteCommit(t, cfg, repoPath, gittest.WithReference("refs/tags/"+tagName))
- return repoPath, &gitalypb.UserDeleteTagRequest{
- Repository: repoProto,
- TagName: []byte("milky way"),
- User: gittest.TestUser,
+ return setupData{
+ repoPath: repoPath,
+ request: &gitalypb.UserDeleteTagRequest{
+ Repository: repoProto,
+ TagName: []byte("milky way"),
+ User: gittest.TestUser,
+ },
+ expectedError: structerr.NewFailedPrecondition("tag not found: %s", "milky way"),
+ expectedTags: []string{"sun"},
}
},
- expectedErr: structerr.NewFailedPrecondition("tag not found: %s", "milky way"),
- expectedTags: []string{"sun"},
},
{
desc: "newline in tag name",
- setup: func() (string, *gitalypb.UserDeleteTagRequest) {
+ setup: func(t *testing.T) setupData {
tagName := "moon"
repoProto, repoPath := gittest.CreateRepository(t, ctx, cfg)
gittest.WriteCommit(t, cfg, repoPath, gittest.WithReference("refs/tags/"+tagName))
- return repoPath, &gitalypb.UserDeleteTagRequest{
- Repository: repoProto,
- TagName: []byte("Dog\nStar"),
- User: gittest.TestUser,
+ return setupData{
+ repoPath: repoPath,
+ request: &gitalypb.UserDeleteTagRequest{
+ Repository: repoProto,
+ TagName: []byte("Dog\nStar"),
+ User: gittest.TestUser,
+ },
+ expectedError: structerr.NewFailedPrecondition("tag not found: %s", "Dog\nStar"),
+ expectedTags: []string{"moon"},
}
},
- expectedErr: structerr.NewFailedPrecondition("tag not found: %s", "Dog\nStar"),
- expectedTags: []string{"moon"},
},
{
desc: "invalid expectedOldOID",
- setup: func() (string, *gitalypb.UserDeleteTagRequest) {
+ setup: func(t *testing.T) setupData {
tagName := "europa"
repoProto, repoPath := gittest.CreateRepository(t, ctx, cfg)
gittest.WriteCommit(t, cfg, repoPath, gittest.WithReference("refs/tags/"+tagName))
- return repoPath, &gitalypb.UserDeleteTagRequest{
- Repository: repoProto,
- TagName: []byte(tagName),
- User: gittest.TestUser,
- ExpectedOldOid: "io",
+ return setupData{
+ repoPath: repoPath,
+ request: &gitalypb.UserDeleteTagRequest{
+ Repository: repoProto,
+ TagName: []byte(tagName),
+ User: gittest.TestUser,
+ ExpectedOldOid: "io",
+ },
+ expectedError: testhelper.WithInterceptedMetadata(
+ structerr.NewInvalidArgument(fmt.Sprintf(`invalid expected old object ID: invalid object ID: "io", expected length %v, got 2`, gittest.DefaultObjectHash.EncodedLen())),
+ "old_object_id", "io"),
+
+ expectedTags: []string{"europa"},
}
},
- expectedErr: testhelper.WithInterceptedMetadata(
- structerr.NewInvalidArgument(fmt.Sprintf(`invalid expected old object ID: invalid object ID: "io", expected length %v, got 2`, gittest.DefaultObjectHash.EncodedLen())),
- "old_object_id", "io"),
- expectedTags: []string{"europa"},
},
{
desc: "valid expectedOldOID SHA but not present in repo",
- setup: func() (string, *gitalypb.UserDeleteTagRequest) {
+ setup: func(t *testing.T) setupData {
tagName := "europa"
repoProto, repoPath := gittest.CreateRepository(t, ctx, cfg)
gittest.WriteCommit(t, cfg, repoPath, gittest.WithReference("refs/tags/"+tagName))
- return repoPath, &gitalypb.UserDeleteTagRequest{
- Repository: repoProto,
- TagName: []byte(tagName),
- User: gittest.TestUser,
- ExpectedOldOid: gittest.DefaultObjectHash.ZeroOID.String(),
+ return setupData{
+ repoPath: repoPath,
+ request: &gitalypb.UserDeleteTagRequest{
+ Repository: repoProto,
+ TagName: []byte(tagName),
+ User: gittest.TestUser,
+ ExpectedOldOid: gittest.DefaultObjectHash.ZeroOID.String(),
+ },
+ expectedError: testhelper.WithInterceptedMetadata(
+ structerr.NewInvalidArgument("cannot resolve expected old object ID: reference not found"),
+ "old_object_id", gittest.DefaultObjectHash.ZeroOID),
+ expectedTags: []string{"europa"},
}
},
- expectedErr: testhelper.WithInterceptedMetadata(
- structerr.NewInvalidArgument("cannot resolve expected old object ID: reference not found"),
- "old_object_id", gittest.DefaultObjectHash.ZeroOID),
- expectedTags: []string{"europa"},
},
{
desc: "old ref expectedOldOID",
- setup: func() (string, *gitalypb.UserDeleteTagRequest) {
+ setup: func(t *testing.T) setupData {
tagName := "ganymede"
repoProto, repoPath := gittest.CreateRepository(t, ctx, cfg)
firstCommit := gittest.WriteCommit(t, cfg, repoPath)
- gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(firstCommit), gittest.WithReference("refs/tags/"+tagName))
+ secondCommit := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(firstCommit), gittest.WithReference("refs/tags/"+tagName))
- return repoPath, &gitalypb.UserDeleteTagRequest{
- Repository: repoProto,
- TagName: []byte(tagName),
- User: gittest.TestUser,
- ExpectedOldOid: firstCommit.String(),
+ return setupData{
+ repoPath: repoPath,
+ request: &gitalypb.UserDeleteTagRequest{
+ Repository: repoProto,
+ TagName: []byte(tagName),
+ User: gittest.TestUser,
+ ExpectedOldOid: firstCommit.String(),
+ },
+ expectedError: testhelper.WithInterceptedMetadata(
+ structerr.NewFailedPrecondition("Could not update refs/tags/%s. Please refresh and try again.", tagName),
+ "stderr",
+ fmt.Sprintf("fatal: prepare: cannot lock ref 'refs/tags/%s': is at %s but expected %s\n", tagName, secondCommit, firstCommit),
+ ),
+ expectedTags: []string{"ganymede"},
}
},
- expectedErr: structerr.NewFailedPrecondition("Could not update refs/tags/ganymede. Please refresh and try again."),
- expectedTags: []string{"ganymede"},
},
}
@@ -241,13 +286,13 @@ func TestUserDeleteTag(t *testing.T) {
t.Run(tc.desc, func(t *testing.T) {
t.Parallel()
- repoPath, request := tc.setup()
- response, err := client.UserDeleteTag(ctx, request)
- testhelper.RequireGrpcError(t, tc.expectedErr, err)
- testhelper.ProtoEqual(t, tc.expectedResponse, response)
+ setup := tc.setup(t)
+ response, err := client.UserDeleteTag(ctx, setup.request)
+ testhelper.RequireGrpcError(t, setup.expectedError, err)
+ testhelper.ProtoEqual(t, setup.expectedResponse, response)
- tags := text.ChompBytes(gittest.Exec(t, cfg, "-C", repoPath, "tag"))
- require.ElementsMatchf(t, tc.expectedTags, strings.Fields(tags), "tag name still exists in tags list")
+ tags := text.ChompBytes(gittest.Exec(t, cfg, "-C", setup.repoPath, "tag"))
+ require.ElementsMatchf(t, setup.expectedTags, strings.Fields(tags), "tag name still exists in tags list")
})
}
}