diff --git a/crates/e2e_test/src/sts_query_compat_test.rs b/crates/e2e_test/src/sts_query_compat_test.rs index 134b3bfc6..3a703be6d 100644 --- a/crates/e2e_test/src/sts_query_compat_test.rs +++ b/crates/e2e_test/src/sts_query_compat_test.rs @@ -740,7 +740,7 @@ async fn test_bucket_tags_opa_contract() -> TestResult { .version_id(version) .send() .await?; - opa.expect_bucket_tags("s3:GetObject", source, Some("finance")).await?; + opa.expect_bucket_tags("s3:GetObjectVersion", source, Some("finance")).await?; client.list_objects_v2().bucket(source).send().await?; opa.expect_bucket_tags("s3:ListBucket", source, Some("finance")).await?; client.list_object_versions().bucket(source).send().await?; diff --git a/crates/ecstore/src/store/list_objects.rs b/crates/ecstore/src/store/list_objects.rs index 13721bd4e..ce7eb0006 100644 --- a/crates/ecstore/src/store/list_objects.rs +++ b/crates/ecstore/src/store/list_objects.rs @@ -9221,7 +9221,7 @@ mod test { }; let snapshot = PersistentListMetadataObject::from_object_info(&object); assert!(snapshot.to_object_info("bucket").etag.is_none()); - let mut old_snapshot = snapshot.clone(); + let mut old_snapshot = snapshot; old_snapshot.etag = object.etag.clone(); let old_contents = format!( "# rustfs-listobjects-key-only-v2\n{}\n", diff --git a/crates/heal/tests/heal_concurrent_delete_test.rs b/crates/heal/tests/heal_concurrent_delete_test.rs index 61657c4b7..2770295dc 100644 --- a/crates/heal/tests/heal_concurrent_delete_test.rs +++ b/crates/heal/tests/heal_concurrent_delete_test.rs @@ -563,9 +563,31 @@ fn admin_root_completes_after_unversioned_delete() { run_delete_case(DeleteCase::Unversioned); } -#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +#[test] #[serial] -async fn admin_root_rejects_recreated_bucket_before_successor_repair() { +fn admin_root_rejects_recreated_bucket_before_successor_repair() { + // Incarnation-bound repairs use a spawned owner; retain the server's stack + // budget for the whole scenario. block_on alone would poll that future on + // libtest's smaller calling-thread stack. + let stack_size = 8 * 1024 * 1024; + std::thread::Builder::new() + .name("heal-concurrent-delete".to_owned()) + .stack_size(stack_size) + .spawn(move || { + let runtime = tokio::runtime::Builder::new_multi_thread() + .worker_threads(4) + .thread_stack_size(stack_size) + .enable_all() + .build() + .expect("concurrent delete runtime"); + runtime.block_on(recreated_bucket_repair()); + }) + .expect("concurrent delete scenario thread starts") + .join() + .expect("concurrent delete scenario must not panic"); +} + +async fn recreated_bucket_repair() { let directory = tempfile::tempdir().expect("recreated bucket fixture"); let env = TestECStoreEnv::builder().base_dir(directory.path()).build().await; env.make_bucket(BUCKET, false).await; diff --git a/rustfs/src/app/lifecycle_transition_api_test.rs b/rustfs/src/app/lifecycle_transition_api_test.rs index a35a12446..c546cee72 100644 --- a/rustfs/src/app/lifecycle_transition_api_test.rs +++ b/rustfs/src/app/lifecycle_transition_api_test.rs @@ -964,7 +964,11 @@ async fn copy_object_allows_new_version_for_locked_destination_but_blocks_explic .await .expect_err("explicit CopyObject overwrite of a locked version should be blocked"); - assert_eq!(err.code(), &s3s::S3ErrorCode::AccessDenied); + assert_eq!(err.code(), &s3s::S3ErrorCode::InvalidArgument); + assert_eq!( + err.message(), + Some("Invalid argument: A destination versionId requires replication authorization") + ); assert_eq!(read_object_bytes(&ecstore, bucket.as_str(), dst_object).await, source_payload); assert_eq!(live_object_version_count(&ecstore, bucket.as_str(), dst_object).await, 2); } diff --git a/rustfs/src/storage/access.rs b/rustfs/src/storage/access.rs index 38109dc38..316eee5fc 100644 --- a/rustfs/src/storage/access.rs +++ b/rustfs/src/storage/access.rs @@ -4343,15 +4343,8 @@ mod tests { (S3Action::DeleteObjectTaggingAction, S3Action::DeleteObjectVersionTaggingAction), ] { for version in [None, Some("null"), Some("0194e0f1-0000-7000-8000-000000000000")] { - let action = versioned_tagging_action(current.clone(), version); - assert_eq!( - action, - Action::S3Action(if version.is_some() { - versioned.clone() - } else { - current.clone() - }) - ); + let action = versioned_tagging_action(current, version); + assert_eq!(action, Action::S3Action(if version.is_some() { versioned } else { current })); let args = Args { account: "writer", groups: &groups, diff --git a/rustfs/src/storage/ecfs.rs b/rustfs/src/storage/ecfs.rs index f1c045f84..3ac66cacf 100644 --- a/rustfs/src/storage/ecfs.rs +++ b/rustfs/src/storage/ecfs.rs @@ -415,7 +415,12 @@ pub(crate) fn object_lock_retention_from_metadata(metadata: &HashMap