Add batched bootstrap resume edge-case coverage · Entire

Add batched bootstrap resume edge-case coverage

4b7a223→main·

Soph·3mo ago·2 files·+124 added/-1 removed

Sessions

e391e5e1f4c9View transcript

Changes

414 unmodified lines

Current rewrite note:
- Some of these are now covered, including empty source repo, tag force-retarget, duplicate/conflicting mappings, and tag creation when target objects already exist.
- Basic context cancellation coverage now exists, but in-flight cancellation and some harder batch-failure/cutover paths still need stronger coverage.
- Basic context cancellation coverage now exists.
- Batched bootstrap resume mismatch and final-tip cutover paths now have direct integration coverage.
- Some harder batch-failure injection and partial cutover failure paths still remain.

### 22. No benchmark coverage for the expensive paths

Mdocs/rewrite-issue-list.md+3/-1

32 unmodified lines

```go
package syncer

import (
    "github.com/soph/git-sync/internal/auth"
    "github.com/soph/git-sync/internal/gitproto"
    "github.com/soph/git-sync/internal/planner"
    bstrap "github.com/soph/git-sync/internal/strategy/bootstrap"
)

const testBranch = "master"

func TestBootstrap_IntegrationBatchedResumeMismatchFails(t *testing.T) {
    sourceRepo, sourceFS := newSourceRepo(t)
    makeLargeCommits(t, sourceRepo, sourceFS, 5, 200_000)

unrelatedRepo, unrelatedFS := newSourceRepo(t)
    makeCommits(t, unrelatedRepo, unrelatedFS, 1)
    unrelatedHead, err := unrelatedRepo.Reference(plumbing.NewBranchReferenceName(testBranch), true)
    if err != nil {
        t.Fatalf("resolve unrelated head: %v", err)
    }

targetRepo, err := git.Init(memory.NewStorage(), nil)
    if err != nil {
        t.Fatalf("init target repo: %v", err)
    }
    if err := copyRefsAndObjects(unrelatedRepo.Storer, targetRepo.Storer, nil); err != nil {
        t.Fatalf("copy unrelated objects: %v", err)
    }
    targetHead := unrelatedHead
    tempRef := planner.BootstrapTempRef(plumbing.NewBranchReferenceName(testBranch))
    if err := targetRepo.Storer.SetReference(plumbing.NewHashReference(tempRef, targetHead.Hash())); err != nil {
        t.Fatalf("set temp ref: %v", err)
    }

sourceServer := newSmartHTTPRepoServerV2(t, sourceRepo)
    targetServer := newSmartHTTPRepoServer(t, targetRepo)
    defer sourceServer.Close()
    defer targetServer.Close()

cfg := Config{
        Source:            Endpoint{URL: sourceServer.RepoURL()},
        Target:            Endpoint{URL: targetServer.RepoURL()},
        ProtocolMode:      protocolModeAuto,
        BatchMaxPackBytes: 350_000,
    }

s, err := newSession(context.Background(), cfg, false)
    if err != nil {
        t.Fatalf("new session: %v", err)
    }
    desired, _, err := planner.BuildDesiredRefs(s.sourceRefMap, planConfig(cfg))
    if err != nil {
        t.Fatalf("build desired refs: %v", err)
    }
    ref := desired[plumbing.NewBranchReferenceName(testBranch)]
    checkpoints, err := bstrap.PlanCheckpoints(context.Background(), bstrap.Params{
        SourceConn:    s.sourceConn,
        SourceService: s.sourceService,
        BatchMaxPack:  cfg.BatchMaxPackBytes,
    }, ref)
    if err != nil {
        t.Fatalf("plan checkpoints: %v", err)
    }
    if len(checkpoints) == 0 {
        t.Fatal("expected checkpoints for batched bootstrap")
    }
    for _, checkpoint := range checkpoints {
        if checkpoint == targetHead.Hash() {
            t.Fatalf("expected unrelated temp ref hash, got checkpoint collision at %s", checkpoint)
        }
    }

_, err = Bootstrap(context.Background(), cfg)
    if err == nil {
        t.Fatal("expected batched bootstrap resume mismatch to fail")
    }
    if !strings.Contains(err.Error(), "does not match any planned checkpoint") {
        t.Fatalf("unexpected error: %v", err)
    }
}

func TestBootstrap_IntegrationBatchedResumeAtFinalTipCutsOver(t *testing.T) {
    sourceRepo, sourceFS := newSourceRepo(t)
    makeLargeCommits(t, sourceRepo, sourceFS, 5, 200_000)
    sourceHead, err := sourceRepo.Reference(plumbing.NewBranchReferenceName(testBranch), true)
    if err != nil {
        t.Fatalf("resolve source head: %v", err)
    }

targetRepo, err := git.Init(memory.NewStorage(), nil)
    if err != nil {
        t.Fatalf("init target repo: %v", err)
    }
    if err := copyRefsAndObjects(sourceRepo.Storer, targetRepo.Storer, nil); err != nil {
        t.Fatalf("copy source objects: %v", err)
    }
    tempRef := planner.BootstrapTempRef(plumbing.NewBranchReferenceName(testBranch))
    if err := targetRepo.Storer.SetReference(plumbing.NewHashReference(tempRef, sourceHead.Hash())); err != nil {
        t.Fatalf("set temp ref: %v", err)
    }

result, err := Bootstrap(context.Background(), Config{
        Source:            Endpoint{URL: sourceServer.RepoURL()},
        Target:            Endpoint{URL: targetServer.RepoURL()},
        ProtocolMode:      protocolModeAuto,
        BatchMaxPackBytes: 350_000,
    })
    if err != nil {
        t.Fatalf("batched bootstrap final-tip cutover failed: %v", err)
    }
    if !result.Relay || result.RelayMode != "bootstrap-batch" {
        t.Fatalf("expected batched bootstrap result, got %+v", result)
    }
    targetHead, err := targetRepo.Reference(plumbing.NewBranchReferenceName(testBranch), true)
    if err != nil {
        t.Fatalf("resolve target head: %v", err)
    }
    if targetHead.Hash() != sourceHead.Hash() {
        t.Fatalf("expected target head %s, got %s", sourceHead.Hash(), targetHead.Hash())
    }
    if _, err := targetRepo.Reference(tempRef, true); err == nil {
        t.Fatalf("expected temp ref %s to be deleted", tempRef)
    }
}

func TestBootstrap_IntegrationBranchMapping(t *testing.T) {
    sourceRepo, sourceFS := newSourceRepo(t)
    makeCommits(t, sourceRepo, sourceFS, 3)