From 8f6121694a20bab8519aa33033ab5fec64c8fcfa Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Thu, 22 Oct 2026 14:34:52 -0700 Subject: [PATCH] test: fix HEAD bucket assertions and FUSE failover startup timing (#11967) test: fix HEAD bucket assertions and warm up failover clusters --- test/fuse_failover/framework_test.go | 15 +++++++++++++++ weed/s3api/s3api_head_bucket_test.go | 10 ++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/test/fuse_failover/framework_test.go b/test/fuse_failover/framework_test.go index e30c6b3f05..1dd3b59c6d 100644 --- a/test/fuse_failover/framework_test.go +++ b/test/fuse_failover/framework_test.go @@ -110,6 +110,21 @@ func startFailoverCluster(t testing.TB, numVolumes, numMounts int) *failoverClus "mount %d not ready\n%s", i, c.tailLog(fmt.Sprintf("mount%d", i))) } require.NoError(t, c.WaitForVolumeServers(60*time.Second)) + + // A mounted filesystem and registered volume servers do not yet guarantee + // that the lock ring has settled or the first replicated volume is ready. + // Exercise the write/read path during setup so the append latency checks + // measure failover on a running cluster rather than its cold start. + probeName := ".failover-ready" + probeData := []byte("ready\n") + probePath := filepath.Join(c.MountDir(0), probeName) + require.NoError(t, os.WriteFile(probePath, probeData, 0644), + "readiness write failed\n%s", c.tailLog("mount0")) + for i := 0; i < numMounts; i++ { + _, ready := waitForContent(filepath.Join(c.MountDir(i), probeName), probeData, convergeTimeout) + require.True(t, ready, "mount %d did not observe readiness write\n%s", i, c.tailLog(fmt.Sprintf("mount%d", i))) + } + require.NoError(t, os.Remove(probePath)) return c } diff --git a/weed/s3api/s3api_head_bucket_test.go b/weed/s3api/s3api_head_bucket_test.go index 0c5cbf82ad..d83308c57b 100644 --- a/weed/s3api/s3api_head_bucket_test.go +++ b/weed/s3api/s3api_head_bucket_test.go @@ -5,7 +5,6 @@ import ( "net/http" "net/http/httptest" "path" - "strings" "testing" "github.com/gorilla/mux" @@ -56,19 +55,16 @@ func TestHeadBucketSeparatesLookupFailureFromMissingBucket(t *testing.T) { name string filer *fakeLookupFiler wantCode int - wantBody string }{ { name: "transient lookup failure", filer: &fakeLookupFiler{lookupErr: status.Error(codes.Internal, "filer store unavailable")}, wantCode: http.StatusInternalServerError, - wantBody: "InternalError", }, { name: "missing bucket", filer: &fakeLookupFiler{}, wantCode: http.StatusNotFound, - wantBody: "NoSuchBucket", }, { name: "existing bucket", @@ -88,8 +84,10 @@ func TestHeadBucketSeparatesLookupFailureFromMissingBucket(t *testing.T) { if rr.Code != tc.wantCode { t.Fatalf("status = %d, want %d: %s", rr.Code, tc.wantCode, rr.Body.String()) } - if tc.wantBody != "" && !strings.Contains(rr.Body.String(), tc.wantBody) { - t.Fatalf("body = %s, want %s", rr.Body.String(), tc.wantBody) + // HEAD returns the status without an XML error body, including for + // lookup failures and missing buckets. + if rr.Body.Len() != 0 { + t.Fatalf("HEAD response must have an empty body, got %q", rr.Body.String()) } }) }