Cover mid-stream push failures in bootstrap · Entire

Cover mid-stream push failures in bootstrap

7b26009→main·

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

Sessions

a01557bd71c2View transcript

Changes

2

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)