Cover mid-stream v1 fetch failures in syncer · Entire
Cover mid-stream v1 fetch failures in syncer
59b4a85→main·
Soph·3mo ago·2 files·+116 added/-2
Sessions
40dcb8c93a3dView transcript
Changes
2
- docs
- Mrewrite-issue-list.md+2/-2
- internal/syncer
- Mintegration_test.go+114
445 unmodified lines
446
447
448
449
450
449
450
451
452
453
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.
- 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.
- 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.
### 22. No benchmark coverage for the expensive paths
Mdocs/rewrite-issue-list.md+2/-2
278 unmodified lines
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
1222 unmodified lines
1617
1618
1619
1620
1621
1622
1623
198 unmodified lines
1822
1823
1824
1825
1826
1827
1828
1829
1830
278 unmodified lines
\t}
}
func TestRun_IntegrationV1FetchMalformedMidStreamFails(t *testing.T) {
\tsourceRepo, sourceFS := newSourceRepo(t)
\tmakeCommits(t, sourceRepo, sourceFS, 3)
\ttargetRepo, err := git.Init(memory.NewStorage())
\tif err != nil {
\t\tt.Fatalf("init target repo: %%v", err)
\t}
\tif err := copyRefsAndObjects(sourceRepo.Storer, targetRepo.Storer, []plumbing.ReferenceName{plumbing.NewBranchReferenceName(testBranch)}); err != nil {
\t\tt.Fatalf("copy target baseline: %%v", err)
\t}
\tmakeCommits(t, sourceRepo, sourceFS, 1)
\tsourceServer := newSmartHTTPRepoServer(t, sourceRepo)
\tsourceServer.uploadPackRaw = func(w http.ResponseWriter, _ *http.Request, body []byte) bool {
\tw.Header().Set("Content-Type", fmt.Sprintf("application/x-%%s-result", serviceUploadPack))
\tif _, err := io.WriteString(w, "0008NAK\nzzzz"); err != nil {
\t\tt.Fatalf("write malformed v1 fetch response: %%v", err)
\t}
\tsourceServer.recordMetric(serviceUploadPack, metricPack, int64(len(body)), int64(len("0008NAK\nzzzz")), 1, 1)
\treturn true
\t}
\ttargetServer := newSmartHTTPRepoServer(t, targetRepo)
\tdefer sourceServer.Close()
\tdefer targetServer.Close()
\t_, err = Run(context.Background(), Config{
\t\tSource: Endpoint{URL: sourceServer.RepoURL()},
\t\tTarget: Endpoint{URL: targetServer.RepoURL()},
\t\tProtocolMode: protocolModeV1,
\t})
\tif err == nil {
\t\tt.Fatal("expected malformed mid-stream v1 fetch to fail")
\t}
}
func TestRun_IntegrationV1FetchCanceledMidStreamFails(t *testing.T) {
\tsourceRepo, sourceFS := newSourceRepo(t)
\tmakeCommits(t, sourceRepo, sourceFS, 3)
\trelease := make(chan struct{})
\tstarted := make(chan struct{}, 1)
\tsourceServer := newSmartHTTPRepoServer(t, sourceRepo)
\tsourceServer.uploadPackRaw = func(w http.ResponseWriter, _ *http.Request, body []byte) bool {
\tw.Header().Set("Content-Type", fmt.Sprintf("application/x-%%s-result", serviceUploadPack))
\tflusher, ok := w.(http.Flusher)
\tif !ok {
\t\tt.Fatal("expected flusher")
\t}
\tif _, err := io.WriteString(w, "0008NAK\n"); err != nil {
\t\tt.Fatalf("write v1 NAK prelude: %%v", err)
\t}
\tflusher.Flush()
\tselect {
\tcase started <- struct{}{}:
\tdefault:
\t}
\t<-release
\tif _, err := io.WriteString(w, "zzzz"); err != nil && !isConnectionCloseError(err) {
\t\tt.Fatalf("write interrupted v1 packet tail: %%v", err)
\t}
\tsourceServer.recordMetric(serviceUploadPack, metricPack, int64(len(body)), 0, 1, 1)
\treturn true
\t}
\ttargetServer := newSmartHTTPRepoServer(t, targetRepo)
\tdefer sourceServer.Close()
\tdefer targetServer.Close()
\tctx, cancel := context.WithCancel(context.Background())
\tdone := make(chan error, 1)
\tgo func() {
\t\t_, err := Run(ctx, Config{
\t\t\tSource: Endpoint{URL: sourceServer.RepoURL()},
\t\t\tTarget: Endpoint{URL: targetServer.RepoURL()},
\t\t\tProtocolMode: protocolModeV1,
\t\t})
\t\tdone <- err
\t}()
\tselect {
\tcase <-started:
\tcase <-time.After(2 * time.Second):
\t\tt.Fatal("expected v1 fetch response to start before cancellation")
\t}
cancel()
close(release)
\tselect {
\tcase err = <-done:
\tcase <-time.After(2 * time.Second):
\t\tt.Fatal("expected run to return after cancellation")
\t}
\tif err == nil {
\t\tt.Fatal("expected cancellation error")
\t}
\tif !errors.Is(err, context.Canceled) {
\t\tt.Fatalf("expected context.Canceled, got %%v", err)
\t}
}
func TestRun_IntegrationPlanSuggestsBootstrapOnEmptyTarget(t *testing.T) {
\tsourceRepo, sourceFS := newSourceRepo(t)
\tmakeCommits(t, sourceRepo, sourceFS, 2)
1222 unmodified lines
\treceivePackNoThin bool
\tcommandHook func(*packp.UpdateRequests) *packp.ReportStatus
\treceivePackHook func(*packp.UpdateRequests, bool) *packp.ReportStatus
\tuploadPackRaw func(http.ResponseWriter, *http.Request, []byte) bool
\tuploadPackV2FetchRaw func(http.ResponseWriter, v2TestCommandRequest, []byte) bool
\tmu sync.Mutex
198 unmodified lines
\t\ts.handleUploadPackV2(w, r, body)
\t\treturn
}\n\tif s.uploadPackRaw != nil && s.uploadPackRaw(w, r, body) {
\t\treturn
\t}
\twantCount := strings.Count(string(body), "want ")
\thaveCount := strings.Count(string(body), "have ")