From 3578baf501c5b71acf0df513d034ffe4fd23d8ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=AD=A3=E8=B6=85?= Date: Sun, 29 Mar 2026 19:23:30 +0800 Subject: [PATCH] test(object-lock): cover validation gaps (#2318) --- rustfs/src/app/bucket_usecase.rs | 22 ++++++++++++++++ rustfs/src/app/object_usecase.rs | 45 ++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/rustfs/src/app/bucket_usecase.rs b/rustfs/src/app/bucket_usecase.rs index b72d2ecac..bf8f041e5 100644 --- a/rustfs/src/app/bucket_usecase.rs +++ b/rustfs/src/app/bucket_usecase.rs @@ -1669,6 +1669,28 @@ mod tests { assert!(versioning_configuration_has_object_lock_incompatible_settings(&config)); } + #[test] + fn versioning_configuration_has_object_lock_incompatible_settings_rejects_exclude_folders() { + let config = VersioningConfiguration { + exclude_folders: Some(true), + ..Default::default() + }; + + assert!(versioning_configuration_has_object_lock_incompatible_settings(&config)); + } + + #[test] + fn versioning_configuration_has_object_lock_incompatible_settings_rejects_excluded_prefixes() { + let config = VersioningConfiguration { + excluded_prefixes: Some(vec![ExcludedPrefix { + prefix: Some("archive/".to_string()), + }]), + ..Default::default() + }; + + assert!(versioning_configuration_has_object_lock_incompatible_settings(&config)); + } + #[test] fn resolve_notification_region_prefers_global_region() { let binding = resolve_notification_region(Some("us-east-1".parse().unwrap()), Some("ap-southeast-1".parse().unwrap())); diff --git a/rustfs/src/app/object_usecase.rs b/rustfs/src/app/object_usecase.rs index 1d00abc1d..57e0ddefd 100644 --- a/rustfs/src/app/object_usecase.rs +++ b/rustfs/src/app/object_usecase.rs @@ -5189,6 +5189,51 @@ mod tests { assert_eq!(err.code(), &S3ErrorCode::MalformedXML); } + #[test] + fn validate_object_lock_configuration_rejects_missing_default_retention() { + let cfg = ObjectLockConfiguration { + object_lock_enabled: Some(ObjectLockEnabled::from_static(ObjectLockEnabled::ENABLED)), + rule: Some(ObjectLockRule { default_retention: None }), + }; + + let err = validate_object_lock_configuration_input(&cfg).unwrap_err(); + assert_eq!(err.code(), &S3ErrorCode::MalformedXML); + } + + #[test] + fn validate_object_lock_configuration_rejects_zero_days() { + let cfg = ObjectLockConfiguration { + object_lock_enabled: Some(ObjectLockEnabled::from_static(ObjectLockEnabled::ENABLED)), + rule: Some(ObjectLockRule { + default_retention: Some(DefaultRetention { + mode: Some(ObjectLockRetentionMode::from_static(ObjectLockRetentionMode::GOVERNANCE)), + days: Some(0), + years: None, + }), + }), + }; + + let err = validate_object_lock_configuration_input(&cfg).unwrap_err(); + assert_eq!(err.code(), &S3ErrorCode::Custom("InvalidRetentionPeriod".into())); + } + + #[test] + fn validate_object_lock_configuration_rejects_too_many_years() { + let cfg = ObjectLockConfiguration { + object_lock_enabled: Some(ObjectLockEnabled::from_static(ObjectLockEnabled::ENABLED)), + rule: Some(ObjectLockRule { + default_retention: Some(DefaultRetention { + mode: Some(ObjectLockRetentionMode::from_static(ObjectLockRetentionMode::COMPLIANCE)), + days: None, + years: Some(MAXIMUM_RETENTION_YEARS + 1), + }), + }), + }; + + let err = validate_object_lock_configuration_input(&cfg).unwrap_err(); + assert_eq!(err.code(), &S3ErrorCode::Custom("InvalidRetentionPeriod".into())); + } + #[tokio::test] async fn execute_put_object_retention_returns_internal_error_when_store_uninitialized() { let input = PutObjectRetentionInput::builder()