From 7948f7a94504f2bf4b99d432508481fa1a45c38c Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 9 May 2026 23:22:50 -0700 Subject: [PATCH] test(s3/lifecycle): integration coverage for empty bucket sweep no-op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A bucket carrying lifecycle config but no objects must produce a successful sweep — no hangs, no errors, no dispatches. Pinned because the bootstrap walker iterates bucket directories, and an empty directory is a corner of that traversal that's easy to break (slice-bounds bug on the first listing returning zero entries). Asserts: worker logs "loaded lifecycle for" and "shards 0-15 complete", no FATAL output, bucket still exists after the sweep. --- .../s3_lifecycle_empty_bucket_test.go | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 test/s3/lifecycle/s3_lifecycle_empty_bucket_test.go diff --git a/test/s3/lifecycle/s3_lifecycle_empty_bucket_test.go b/test/s3/lifecycle/s3_lifecycle_empty_bucket_test.go new file mode 100644 index 000000000..4936a8e7c --- /dev/null +++ b/test/s3/lifecycle/s3_lifecycle_empty_bucket_test.go @@ -0,0 +1,46 @@ +// Empty bucket sweep — no-op safety integration scenario. +package lifecycle + +import ( + "context" + "strings" + "testing" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/s3" + "github.com/stretchr/testify/require" +) + +// TestLifecycleEmptyBucketSweepIsNoOp: a bucket carrying lifecycle +// config but no objects must produce a successful sweep — no hangs, +// no errors, no dispatches. Pinned because the bootstrap walker +// iterates bucket directories, and an empty directory is a corner of +// that traversal that's easy to break (e.g. a slice-bounds bug on +// the first listing returning zero entries). +func TestLifecycleEmptyBucketSweepIsNoOp(t *testing.T) { + c := s3Client(t) + _, fcClose := filerClient(t) + defer fcClose() + + bucket := uniqueBucket("empty") + mustCreateBucket(t, c, bucket) + putExpirationLifecycle(t, c, bucket, "anywhere/", 1) + + // No PUTs. The bucket is empty. + + out := runLifecycleShard(t) + t.Logf("sweep output:\n%s", out) + + // Sanity: the worker must report it loaded the bucket's config and + // completed without errors. The runLifecycleShard helper already + // fails on FATAL; pin the success markers here too so a regression + // that produces a half-shaped output (no completion) is caught. + require.Contains(t, out, "loaded lifecycle for", "worker must report config load") + require.Contains(t, out, "shards 0-15 complete", "worker must complete all shards") + require.False(t, strings.Contains(out, "FATAL"), "worker must not produce FATAL output") + + // Bucket still exists after the sweep — the worker doesn't drop + // empty buckets as a side effect. + _, err := c.HeadBucket(context.Background(), &s3.HeadBucketInput{Bucket: aws.String(bucket)}) + require.NoError(t, err, "empty bucket must still exist after sweep") +}