diff options
| author | 2026-03-07 19:40:27 +0800 | |
|---|---|---|
| committer | 2026-03-07 20:53:17 +0800 | |
| commit | 8aa2e9f0903a80c90a9d8308138439d6f8732050 (patch) | |
| tree | d2fecbf29b0eaa78da87b017005139960783f669 /receivepack/int_test.go | |
| parent | refstore/files: Implement batching (diff) | |
| signature | No signature | |
receivepack: Use refs
Diffstat (limited to 'receivepack/int_test.go')
| -rw-r--r-- | receivepack/int_test.go | 249 |
1 files changed, 239 insertions, 10 deletions
diff --git a/receivepack/int_test.go b/receivepack/int_test.go index a790741b..8f0d02e6 100644 --- a/receivepack/int_test.go +++ b/receivepack/int_test.go @@ -3,6 +3,7 @@ package receivepack_test import ( "context" "fmt" + "io" "strings" "testing" @@ -13,7 +14,7 @@ import ( // TODO: actually test with send-pack -func TestReceivePackDeleteOnlyReportsNotImplemented(t *testing.T) { +func TestReceivePackDeleteOnlyAtomicDeleteSucceeds(t *testing.T) { t.Parallel() //nolint:thelper @@ -32,11 +33,11 @@ func TestReceivePackDeleteOnlyReportsNotImplemented(t *testing.T) { ) input.WriteString(pktlineData( - commitID.String() + " " + objectid.Zero(algo).String() + " refs/heads/main\x00report-status delete-refs object-format=" + algo.String() + "\n", + commitID.String() + " " + objectid.Zero(algo).String() + " refs/heads/main\x00report-status atomic delete-refs object-format=" + algo.String() + "\n", )) input.WriteString("0000") - err := receivepack.ReceivePack(context.Background(), &output, &strings.Builder{}, strings.NewReader(input.String()), receivepack.Options{ + err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{ GitProtocol: "", Algorithm: algo, Refs: repo.Refs(), @@ -47,9 +48,119 @@ func TestReceivePackDeleteOnlyReportsNotImplemented(t *testing.T) { } got := output.String() - if !strings.Contains(got, "ng refs/heads/main ref updates not implemented yet\n") { + if !strings.Contains(got, "ok refs/heads/main\n") { t.Fatalf("unexpected receive-pack output %q", got) } + + if _, err := repo.Refs().Resolve("refs/heads/main"); err == nil { + t.Fatal("refs/heads/main still exists after delete push") + } + }) +} + +func TestReceivePackDeleteOnlyNonAtomicAppliesIndependentDeletes(t *testing.T) { + t.Parallel() + + //nolint:thelper + testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { + t.Parallel() + + testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo}) + _, _, commitID := testRepo.MakeCommit(t, "base") + _, _, staleID := testRepo.MakeCommit(t, "stale") + testRepo.UpdateRef(t, "refs/heads/main", commitID) + testRepo.UpdateRef(t, "refs/heads/topic", commitID) + + repo := testRepo.OpenRepository(t) + + var ( + input strings.Builder + output bufferWriteFlusher + ) + + input.WriteString(pktlineData( + staleID.String() + " " + objectid.Zero(algo).String() + " refs/heads/main\x00report-status delete-refs object-format=" + algo.String() + "\n", + )) + input.WriteString(pktlineData( + commitID.String() + " " + objectid.Zero(algo).String() + " refs/heads/topic\n", + )) + input.WriteString("0000") + + err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{ + GitProtocol: "", + Algorithm: algo, + Refs: repo.Refs(), + ExistingObjects: repo.Objects(), + }) + if err != nil { + t.Fatalf("ReceivePack: %v", err) + } + + got := output.String() + if !strings.Contains(got, "ng refs/heads/main ") || !strings.Contains(got, "ok refs/heads/topic\n") { + t.Fatalf("unexpected receive-pack output %q", got) + } + + if _, err := repo.Refs().Resolve("refs/heads/main"); err != nil { + t.Fatalf("Resolve(main): %v", err) + } + + if _, err := repo.Refs().Resolve("refs/heads/topic"); err == nil { + t.Fatal("refs/heads/topic still exists after successful delete") + } + }) +} + +func TestReceivePackDeleteOnlyAtomicFailureLeavesAllRefsUntouched(t *testing.T) { + t.Parallel() + + //nolint:thelper + testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { + t.Parallel() + + testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo}) + _, _, commitID := testRepo.MakeCommit(t, "base") + _, _, staleID := testRepo.MakeCommit(t, "stale") + testRepo.UpdateRef(t, "refs/heads/main", commitID) + testRepo.UpdateRef(t, "refs/heads/topic", commitID) + + repo := testRepo.OpenRepository(t) + + var ( + input strings.Builder + output bufferWriteFlusher + ) + + input.WriteString(pktlineData( + staleID.String() + " " + objectid.Zero(algo).String() + " refs/heads/main\x00report-status atomic delete-refs object-format=" + algo.String() + "\n", + )) + input.WriteString(pktlineData( + commitID.String() + " " + objectid.Zero(algo).String() + " refs/heads/topic\n", + )) + input.WriteString("0000") + + err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{ + GitProtocol: "", + Algorithm: algo, + Refs: repo.Refs(), + ExistingObjects: repo.Objects(), + }) + if err != nil { + t.Fatalf("ReceivePack: %v", err) + } + + got := output.String() + if !strings.Contains(got, "ng refs/heads/main ") || !strings.Contains(got, "ng refs/heads/topic ") { + t.Fatalf("unexpected receive-pack output %q", got) + } + + if _, err := repo.Refs().Resolve("refs/heads/main"); err != nil { + t.Fatalf("Resolve(main): %v", err) + } + + if _, err := repo.Refs().Resolve("refs/heads/topic"); err != nil { + t.Fatalf("Resolve(topic): %v", err) + } }) } @@ -74,7 +185,7 @@ func TestReceivePackAdvertisesResolvedHEAD(t *testing.T) { input.WriteString("0000") - err := receivepack.ReceivePack(context.Background(), &output, &strings.Builder{}, strings.NewReader(input.String()), receivepack.Options{ + err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{ Algorithm: algo, Refs: repo.Refs(), ExistingObjects: repo.Objects(), @@ -123,11 +234,11 @@ func TestReceivePackWithoutReportStatusWritesNoStatusPayload(t *testing.T) { ) input.WriteString(pktlineData( - commitID.String() + " " + objectid.Zero(algo).String() + " refs/heads/main\x00delete-refs object-format=" + algo.String() + "\n", + commitID.String() + " " + objectid.Zero(algo).String() + " refs/heads/main\x00delete-refs atomic object-format=" + algo.String() + "\n", )) input.WriteString("0000") - err := receivepack.ReceivePack(context.Background(), &output, &strings.Builder{}, strings.NewReader(input.String()), receivepack.Options{ + err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{ Algorithm: algo, Refs: repo.Refs(), ExistingObjects: repo.Objects(), @@ -162,11 +273,11 @@ func testReceivePackProtocolFallback(t *testing.T, gitProtocol string) { ) input.WriteString(pktlineData( - commitID.String() + " " + objectid.Zero(algo).String() + " refs/heads/main\x00report-status delete-refs object-format=" + algo.String() + "\n", + commitID.String() + " " + objectid.Zero(algo).String() + " refs/heads/main\x00report-status atomic delete-refs object-format=" + algo.String() + "\n", )) input.WriteString("0000") - err := receivepack.ReceivePack(context.Background(), &output, &strings.Builder{}, strings.NewReader(input.String()), receivepack.Options{ + err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{ GitProtocol: gitProtocol, Algorithm: algo, Refs: repo.Refs(), @@ -205,7 +316,7 @@ func TestReceivePackPackRequestWithoutObjectsRootReportsNotConfigured(t *testing )) input.WriteString("0000") - err := receivepack.ReceivePack(context.Background(), &output, &strings.Builder{}, strings.NewReader(input.String()), receivepack.Options{ + err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{ Algorithm: algo, Refs: repo.Refs(), ExistingObjects: repo.Objects(), @@ -221,6 +332,124 @@ func TestReceivePackPackRequestWithoutObjectsRootReportsNotConfigured(t *testing }) } +func TestReceivePackPackCreatePromotesObjectsAndUpdatesRef(t *testing.T) { + t.Parallel() + + //nolint:thelper + testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { + t.Parallel() + + sender := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo}) + _, _, commitID := sender.MakeCommit(t, "pushed commit") + + receiver := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true}) + repo := receiver.OpenRepository(t) + objectsRoot := receiver.OpenObjectsRoot(t) + + packStream := sender.PackObjectsReader(t, []string{commitID.String()}, false) + t.Cleanup(func() { + _ = packStream.Close() + }) + + var ( + input strings.Builder + output bufferWriteFlusher + ) + + input.WriteString(pktlineData( + objectid.Zero(algo).String() + " " + commitID.String() + " refs/heads/main\x00report-status-v2 atomic object-format=" + algo.String() + "\n", + )) + input.WriteString("0000") + + err := receivepack.ReceivePack( + context.Background(), + &output, + io.MultiReader(strings.NewReader(input.String()), packStream), + receivepack.Options{ + Algorithm: algo, + Refs: repo.Refs(), + ExistingObjects: repo.Objects(), + ObjectsRoot: objectsRoot, + }, + ) + if err != nil { + t.Fatalf("ReceivePack: %v", err) + } + + got := output.String() + if !strings.Contains(got, "unpack ok\n") || !strings.Contains(got, "ok refs/heads/main\n") { + t.Fatalf("unexpected receive-pack output %q", got) + } + + reopened := receiver.OpenRepository(t) + + resolved, err := reopened.Refs().ResolveFully("refs/heads/main") + if err != nil { + t.Fatalf("ResolveFully(main): %v", err) + } + + if resolved.ID != commitID { + t.Fatalf("refs/heads/main = %s, want %s", resolved.ID, commitID) + } + + if gotType := receiver.Run(t, "cat-file", "-t", commitID.String()); gotType != "commit" { + t.Fatalf("cat-file -t = %q, want commit", gotType) + } + + packs := receiver.Run(t, "count-objects", "-v") + if !strings.Contains(packs, "packs: 1") { + t.Fatalf("count-objects output missing promoted pack: %q", packs) + } + }) +} + +func TestReceivePackReportStatusV2IncludesRefDetails(t *testing.T) { + t.Parallel() + + //nolint:thelper + testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { + t.Parallel() + + testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo}) + _, _, commitID := testRepo.MakeCommit(t, "base") + testRepo.UpdateRef(t, "refs/heads/main", commitID) + + repo := testRepo.OpenRepository(t) + + var ( + input strings.Builder + output bufferWriteFlusher + ) + + input.WriteString(pktlineData( + commitID.String() + " " + objectid.Zero(algo).String() + " refs/heads/main\x00report-status-v2 atomic delete-refs object-format=" + algo.String() + "\n", + )) + input.WriteString("0000") + + err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{ + Algorithm: algo, + Refs: repo.Refs(), + ExistingObjects: repo.Objects(), + }) + if err != nil { + t.Fatalf("ReceivePack: %v", err) + } + + got := output.String() + if !strings.Contains(got, "option refname refs/heads/main\n") { + t.Fatalf("missing option refname in %q", got) + } + + if !strings.Contains(got, "option old-oid "+commitID.String()+"\n") { + t.Fatalf("missing option old-oid in %q", got) + } + + if !strings.Contains(got, "option new-oid "+objectid.Zero(algo).String()+"\n") { + t.Fatalf("missing option new-oid in %q", got) + } + }) +} + type bufferWriteFlusher struct { strings.Builder } |
