# Cover mid-stream push failures in bootstrap

`7b26009`→[main](/content/gh/entireio/git-sync/commits/main/index.html)·

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

## Sessions

a01557bd71c2View transcript

## Changes

2

- docs
  - Mrewrite-issue-list.md+2/-1
- internal/syncer
  - Mintegration_test.go+131

```
446 unmodified lines

446 unmodified lines

- `fetchToStoreV2` now also has direct coverage for cancellation after response parsing has started.
- V1 and v2 fetch paths now also have direct parser-level coverage for malformed sideband framing after a valid response prelude, both for returned streaming readers and for v2 fetch-to-store cleanup.
- Syncer integration coverage now also includes both v1 and v2 fetch failure and cancellation after a valid fetch response has already started streaming, using the in-memory smart-HTTP harness rather than only protocol-unit stubs.
- The remaining gap is now mostly around rarer transport interruption shapes on push/stream teardown rather than missing started-stream fetch coverage.
- Bootstrap integration coverage now also includes receive-pack cancellation and dropped-connection failures after the target has already started consuming a streamed push body.
- The remaining gap is now mostly around rarer teardown/reporting variants rather than missing started-stream fetch or push interruption coverage.

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

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

```
388 unmodified lines

388 unmodified lines

}
}

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

targetRepo, err := git.Init(memory.NewStorage())
	if err != nil {
		t.Fatalf("init target repo: %v", err)
	}

started := make(chan struct{}, 1)
	release := make(chan struct{})

sourceServer := newSmartHTTPRepoServerV2(t, sourceRepo)
	targetServer := newSmartHTTPRepoServer(t, targetRepo)
	targetServer.receivePackRaw = func(w http.ResponseWriter, r *http.Request) bool {
		defer r.Body.Close()
		buf := make([]byte, 32)
		n, err := r.Body.Read(buf)
		if n == 0 && err != nil {
			t.Fatalf("expected streamed push body bytes before cancellation, got err=%v", err)
		}
		select {
		case started <- struct{}{}:
		default:
		}
		<-release
		_, _ = r.Body.Read(buf)
		return true
	}
	defer sourceServer.Close()
	defer targetServer.Close()

ctx, cancel := context.WithCancel(context.Background())
	done := make(chan error, 1)
	go func() {
		_, err := Bootstrap(ctx, Config{
			Source: Endpoint{URL: sourceServer.RepoURL()},
			Target: Endpoint{URL: targetServer.RepoURL()},
		})
		done <- err
	}()

select {
	case <-started:
	case <-time.After(2 * time.Second):
		t.Fatal("expected receive-pack body consumption before cancellation")
	}
	cancel()
	close(release)

select {
	case err = <-done:
	case <-time.After(2 * time.Second):
		t.Fatal("expected bootstrap to return after cancellation")
	}
	if err == nil {
		t.Fatal("expected cancellation error")
	}
	if !errors.Is(err, context.Canceled) {
		t.Fatalf("expected context.Canceled, got %v", err)
	}
}

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

targetRepo, err := git.Init(memory.NewStorage())
	if err != nil {
		t.Fatalf("init target repo: %v", err)
	}

started := make(chan struct{}, 1)

sourceServer := newSmartHTTPRepoServerV2(t, sourceRepo)
	targetServer := newSmartHTTPRepoServer(t, targetRepo)
	targetServer.receivePackRaw = func(w http.ResponseWriter, r *http.Request) bool {
		defer r.Body.Close()
		buf := make([]byte, 32)
		n, err := r.Body.Read(buf)
		if n == 0 && err != nil {
			t.Fatalf("expected streamed push body bytes before disconnect, got err=%v", err)
		}
		select {
		case started <- struct{}{}:
		default:
		}
		hj, ok := w.(http.Hijacker)
		if !ok {
			t.Fatal("expected hijacker")
		}
		conn, _, err := hj.Hijack()
		if err != nil {
			t.Fatalf("hijack receive-pack connection: %v", err)
		}
		_ = conn.Close()
		return true
	}
	defer sourceServer.Close()
	defer targetServer.Close()

done := make(chan error, 1)
	go func() {
		_, err := Bootstrap(context.Background(), Config{
			Source: Endpoint{URL: sourceServer.RepoURL()},
			Target: Endpoint{URL: targetServer.RepoURL()},
		})
		done <- err
	}()

select {
	case <-started:
	case <-time.After(2 * time.Second):
		t.Fatal("expected receive-pack body consumption before disconnect")
	}

select {
	case err = <-done:
	case <-time.After(2 * time.Second):
		t.Fatal("expected bootstrap to return after dropped connection")
	}
	if err == nil {
		t.Fatal("expected push failure after dropped connection")
	}
}

func TestRun_IntegrationPlanSuggestsBootstrapOnEmptyTarget(t *testing.T) {
	sourceRepo, sourceFS := newSourceRepo(t)
	makeCommits(t, sourceRepo, sourceFS, 2)
	1224 unmodified lines

receivePackHook      func(*packp.UpdateRequests, bool) *packp.ReportStatus
	uploadPackRaw        func(http.ResponseWriter, *http.Request, []byte) bool
	uploadPackV2FetchRaw func(http.ResponseWriter, v2TestCommandRequest, []byte) bool
	receivePackRaw       func(http.ResponseWriter, *http.Request) bool

mu      sync.Mutex
	metrics []exchangeMetric
	335 unmodified lines

}

func (s *smartHTTPRepoServer) handleReceivePack(w http.ResponseWriter, r *http.Request) {
	if s.receivePackRaw != nil && s.receivePackRaw(w, r) {
		return
	}

body, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
