From 8cf42a5abb2a6766aa3d86cd07531c834151a74e Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 9 May 2026 18:54:15 -0700 Subject: [PATCH] test(s3/lifecycle): assert per-goroutine errors in fake-server concurrent test (#9393) test(s3/lifecycle): assert per-goroutine errors in concurrent fake test The previous TestFake_ConcurrentCallsSerializeWithoutDeadlock dropped the err return from each LifecycleDelete call, so a regression in the concurrent path could pass the length-only assertion. Capture each err on a buffered channel and require.NoError after wg.Wait(). --- .../s3lifecycle/lifecycletest/fakeserver_test.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/weed/s3api/s3lifecycle/lifecycletest/fakeserver_test.go b/weed/s3api/s3lifecycle/lifecycletest/fakeserver_test.go index ca9f2439f..7eac54d7b 100644 --- a/weed/s3api/s3lifecycle/lifecycletest/fakeserver_test.go +++ b/weed/s3api/s3lifecycle/lifecycletest/fakeserver_test.go @@ -193,17 +193,25 @@ func TestFake_NilRequestUsesDefault(t *testing.T) { func TestFake_ConcurrentCallsSerializeWithoutDeadlock(t *testing.T) { // The dispatcher fans dispatch across many goroutines; the fake - // must not livelock or drop records under concurrent load. + // must not livelock or drop records under concurrent load. Capture + // each goroutine's err so a regression in concurrent paths surfaces + // instead of being masked by length-only assertions. f := NewFakeLifecycleServer() const N = 64 var wg sync.WaitGroup + errCh := make(chan error, N) wg.Add(N) for i := 0; i < N; i++ { go func() { defer wg.Done() - _, _ = f.LifecycleDelete(context.Background(), &s3_lifecycle_pb.LifecycleDeleteRequest{Bucket: "b", ObjectPath: "k"}) + _, err := f.LifecycleDelete(context.Background(), &s3_lifecycle_pb.LifecycleDeleteRequest{Bucket: "b", ObjectPath: "k"}) + errCh <- err }() } wg.Wait() + close(errCh) + for err := range errCh { + require.NoError(t, err) + } assert.Len(t, f.Recorded(), N) }