Cover mid-stream v2 fetch cancellation · Entire
Cover mid-stream v2 fetch cancellation
d0f0dfa→main·
Soph·3mo ago·2 files·+89 added/-1 removed
Sessions
52544e8d948cView transcript
Changes
2
docs
- Mrewrite-issue-list.md+2/-1
internal/gitproto
- Mfetch_test.go+87
434 unmodified lines
435
436
437
438
438
439
440
441
442
434 unmodified lines
- Batched bootstrap reruns now also cover the "target ref already created, temp ref cleanup still pending" recovery path.
- Injected temp-ref delete failure during batched cutover is now covered end-to-end, including successful recovery on retry.
- Injected checkpoint pack failure after partial batched progress is now covered end-to-end, including successful resume on retry.
- Some harder transport-interruption and malformed mid-stream failure paths still remain.
- `fetchToStoreV2` now also has direct coverage for cancellation after response parsing has started.
- Some harder transport-interruption and malformed mid-stream failure 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
162 unmodified lines
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
275 unmodified lines
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
162 unmodified lines
func (f closerFunc) Close() error { return f() }
type blockingPacketBody struct {
ctx context.Context
startedRead chan<- struct{}
first []byte
stage int
closed bool
}
func (b *blockingPacketBody) Read(p []byte) (int, error) {
switch b.stage {
case 0:
b.stage = 1
return copy(p, b.first), nil
default:
select {
case b.startedRead <- struct{}:{}
default:
}
<-b.ctx.Done()
return 0, b.ctx.Err()
}
}
func (b *blockingPacketBody) Close() error {
b.closed = true
return nil
}
func TestDecodeV2LSRefs(t *testing.T) {
// Build a valid ls-refs response:
// Each line: "<hash> <refname>\n"
275 unmodified lines
}
func TestFetchToStoreV2ContextCanceledMidStream(t *testing.T) {
startedRead := make(chan struct{}, 1)
ep, err := transport.NewEndpoint("https://example.com/repo.git")
if err != nil {
t.Fatalf("parse endpoint: %v", err)
}
ctx, cancel := context.WithCancel(context.Background())
conn := NewConn(ep, "source", nil, roundTripperFunc(func(req *http.Request) (*http.Response, error) {
body := &blockingPacketBody{
ctx: req.Context(),
startedRead: startedRead,
first: []byte(FormatPktLine("packfile\n")),
}
return &http.Response{
StatusCode: http.StatusOK,
Request: req,
Body: body,
}, nil
}))
caps := &V2Capabilities{
Caps: map[string]string{
"fetch": "",
},
}
desired := map[plumbing.ReferenceName]DesiredRef{
plumbing.NewBranchReferenceName("main"): {
SourceRef: plumbing.NewBranchReferenceName("main"),
TargetRef: plumbing.NewBranchReferenceName("main"),
SourceHash: plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
},
}
done := make(chan error, 1)
go func() {
done <- fetchToStoreV2(ctx, memory.NewStorage(), conn, caps, desired, nil)
})
select {
case <-startedRead:
case <-time.After(2 * time.Second):
t.Fatal("response body was not consumed before timeout")
}
cancel()
select {
case err = <-done:
case <-time.After(2 * time.Second):
t.Fatal("fetchToStoreV2 did not 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 TestFetchPackV1ClosesBodyOnDecodeError(t *testing.T) {
ep, err := transport.NewEndpoint("https://example.com/repo.git")
if err != nil {