# Cover mid-stream v2 fetch failures in syncer

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

## Sessions

a8ad0d3d78a5View transcript

## Changes

2

- docs
  
  - Mrewrite-issue-list.md+2/-1

- internal/syncer
  
  - Mintegration_test.go+120

```
445 unmodified lines

- Injected checkpoint pack failure after partial batched progress is now covered end-to-end, including successful resume on retry.
- `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.
- Some harder transport-interruption and broader end-to-end cancellation paths still remain, but the remaining gap is narrower than the original list.
- Syncer integration coverage now also includes 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.
- Some harder transport-interruption paths still remain, but the remaining gap is narrower than the original list.

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

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

```
163 unmodified lines

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

targetRepo, err := git.Init(memory.NewStorage())
	if err != nil {
		t.Fatalf("init target repo: %%v", err)
	}
	if err := copyRefsAndObjects(sourceRepo.Storer, targetRepo.Storer, []plumbing.ReferenceName{plumbing.NewBranchReferenceName(testBranch)}); err != nil {
		t.Fatalf("copy target baseline: %%v", err)
	}
	makeCommits(t, sourceRepo, sourceFS, 1)

sourceServer := newSmartHTTPRepoServerV2(t, sourceRepo)
	sourceServer.uploadPackV2FetchRaw = func(w http.ResponseWriter, _ v2TestCommandRequest, body []byte) bool {
		var buf bytes.Buffer
		if _, err := pktline.WriteString(&buf, "packfile\n"); err != nil {
			t.Fatalf("write packfile prelude: %%v", err)
		}
		buf.WriteString("zzzz")
		w.Header().Set("Content-Type", fmt.Sprintf("application/x-%%s-result", serviceUploadPack))
		if _, err := w.Write(buf.Bytes()); err != nil {
			t.Fatalf("write malformed v2 fetch response: %%v", err)
		}
		sourceServer.recordMetric(serviceUploadPack, metricPack, int64(len(body)), int64(buf.Len()), 1, 1)
		return true
	}
	targetServer := newSmartHTTPRepoServer(t, targetRepo)
	defer sourceServer.Close()
	defer targetServer.Close()

_, err = Run(context.Background(), Config{
		Source:       Endpoint{URL: sourceServer.RepoURL()},
		Target:       Endpoint{URL: targetServer.RepoURL()},
		ProtocolMode: protocolModeV2,
	})
	if err == nil {
		t.Fatal("expected malformed mid-stream v2 fetch to fail")
	}
}

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

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

sourceServer := newSmartHTTPRepoServerV2(t, sourceRepo)
	sourceServer.uploadPackV2FetchRaw = func(w http.ResponseWriter, _ v2TestCommandRequest, body []byte) bool {
		w.Header().Set("Content-Type", fmt.Sprintf("application/x-%%s-result", serviceUploadPack))
		flusher, ok := w.(http.Flusher)
		if !ok {
			t.Fatal("expected flusher")
		}
		if _, err := pktline.WriteString(w, "packfile\n"); err != nil {
			t.Fatalf("write packfile prelude: %%v", err)
		}
		flusher.Flush()
		select {
		case started <- struct{}{}:
		default:
		}
		<-release
		if _, err := io.WriteString(w, "zzzz"); err != nil && !isConnectionCloseError(err) {
			t.Fatalf("write interrupted packet tail: %%v", err)
		}
		sourceServer.recordMetric(serviceUploadPack, metricPack, int64(len(body)), 0, 1, 1)
		return true
	}
	targetServer := newSmartHTTPRepoServer(t, targetRepo)
	defer sourceServer.Close()
	defer targetServer.Close()

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

select {
	case <-started:
	case <-time.After(2 * time.Second):
		t.Fatal("expected v2 fetch response to start before cancellation")
	}
	cancel()
	close(release)

select {
	case err = <-done:
	case <-time.After(2 * time.Second):
		t.Fatal("expected run 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 TestRun_IntegrationPlanSuggestsBootstrapOnEmptyTarget(t *testing.T) {
	sourceRepo, sourceFS := newSourceRepo(t)
	makeCommits(t, sourceRepo, sourceFS, 2)
}`
