From 484f1254cae271d264718eb8f261376e263a07cc Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 8 Oct 2026 06:25:56 -0700 Subject: [PATCH] test(ecstore): pin the persisted bucket-config XML bytes for 13 families (#8408) Before any DTO or codec is replaced, pin the exact bytes that deserialize followed by serialize produces for every persisted bucket-configuration XML family in its three source shapes (AWS documentation, MinIO-written, RustFS-written), and the bytes RustFS writes today for a typed value. Byte equality only: a structural comparison would let a codec replacement change the persisted encoding unnoticed. The generator script refuses to overwrite existing goldens without --force, refuses on dirty codec paths, cannot report success from its write mode, and records the generating commit in the fixtures README. Refs rustfs/backlog#2744, rustfs/backlog#2734 --- crates/ecstore/tests/bucket_config_goldens.rs | 495 ++++++++++++++++++ .../fixtures/bucket-config-goldens/README.md | 58 ++ .../accelerate/aws.in.xml | 4 + .../accelerate/aws.out.xml | 1 + .../accelerate/minio.in.xml | 1 + .../accelerate/minio.out.xml | 1 + .../accelerate/rustfs.in.xml | 1 + .../accelerate/rustfs.out.xml | 1 + .../bucket-config-goldens/cors/aws.in.xml | 17 + .../bucket-config-goldens/cors/aws.out.xml | 1 + .../bucket-config-goldens/cors/minio.in.xml | 1 + .../bucket-config-goldens/cors/minio.out.xml | 1 + .../bucket-config-goldens/cors/rustfs.in.xml | 1 + .../bucket-config-goldens/cors/rustfs.out.xml | 1 + .../encryption/aws.in.xml | 10 + .../encryption/aws.out.xml | 1 + .../encryption/minio.in.xml | 1 + .../encryption/minio.out.xml | 1 + .../encryption/rustfs.in.xml | 1 + .../encryption/rustfs.out.xml | 1 + .../lifecycle/aws.in.xml | 49 ++ .../lifecycle/aws.out.xml | 1 + .../lifecycle/minio.in.xml | 1 + .../lifecycle/minio.out.xml | 1 + .../lifecycle/rustfs.in.xml | 1 + .../lifecycle/rustfs.out.xml | 1 + .../bucket-config-goldens/logging/aws.in.xml | 20 + .../bucket-config-goldens/logging/aws.out.xml | 1 + .../logging/minio.in.xml | 1 + .../logging/minio.out.xml | 1 + .../logging/rustfs.in.xml | 1 + .../logging/rustfs.out.xml | 1 + .../notification/aws.in.xml | 30 ++ .../notification/aws.out.xml | 1 + .../notification/minio.in.xml | 1 + .../notification/minio.out.xml | 1 + .../notification/rustfs.in.xml | 1 + .../notification/rustfs.out.xml | 1 + .../object_lock/aws.in.xml | 10 + .../object_lock/aws.out.xml | 1 + .../object_lock/minio.in.xml | 1 + .../object_lock/minio.out.xml | 1 + .../object_lock/rustfs.in.xml | 1 + .../object_lock/rustfs.out.xml | 1 + .../public_access_block/aws.in.xml | 7 + .../public_access_block/aws.out.xml | 1 + .../public_access_block/minio.in.xml | 1 + .../public_access_block/minio.out.xml | 1 + .../public_access_block/rustfs.in.xml | 1 + .../public_access_block/rustfs.out.xml | 1 + .../replication/aws.in.xml | 12 + .../replication/aws.out.xml | 1 + .../replication/minio.in.xml | 1 + .../replication/minio.out.xml | 1 + .../replication/rustfs.in.xml | 1 + .../replication/rustfs.out.xml | 1 + .../request_payment/aws.in.xml | 4 + .../request_payment/aws.out.xml | 1 + .../request_payment/minio.in.xml | 1 + .../request_payment/minio.out.xml | 1 + .../request_payment/rustfs.in.xml | 1 + .../request_payment/rustfs.out.xml | 1 + .../bucket-config-goldens/tagging/aws.in.xml | 13 + .../bucket-config-goldens/tagging/aws.out.xml | 1 + .../tagging/minio.in.xml | 1 + .../tagging/minio.out.xml | 1 + .../tagging/rustfs.in.xml | 1 + .../tagging/rustfs.out.xml | 1 + .../versioning/aws.in.xml | 5 + .../versioning/aws.out.xml | 1 + .../versioning/minio.in.xml | 1 + .../versioning/minio.out.xml | 1 + .../versioning/rustfs.in.xml | 1 + .../versioning/rustfs.out.xml | 1 + .../bucket-config-goldens/website/aws.in.xml | 23 + .../bucket-config-goldens/website/aws.out.xml | 1 + .../website/minio.in.xml | 1 + .../website/minio.out.xml | 1 + .../website/rustfs.in.xml | 1 + .../website/rustfs.out.xml | 1 + scripts/README.md | 1 + scripts/gen_bucket_config_goldens.sh | 117 +++++ 82 files changed, 940 insertions(+) create mode 100644 crates/ecstore/tests/bucket_config_goldens.rs create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/README.md create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/aws.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/aws.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/minio.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/minio.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/rustfs.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/rustfs.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/cors/aws.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/cors/aws.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/cors/minio.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/cors/minio.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/cors/rustfs.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/cors/rustfs.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/aws.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/aws.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/minio.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/minio.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/rustfs.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/rustfs.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/aws.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/aws.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/minio.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/minio.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/rustfs.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/rustfs.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/logging/aws.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/logging/aws.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/logging/minio.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/logging/minio.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/logging/rustfs.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/logging/rustfs.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/notification/aws.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/notification/aws.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/notification/minio.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/notification/minio.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/notification/rustfs.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/notification/rustfs.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/aws.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/aws.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/minio.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/minio.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/rustfs.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/rustfs.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/aws.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/aws.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/minio.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/minio.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/rustfs.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/rustfs.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/replication/aws.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/replication/aws.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/replication/minio.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/replication/minio.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/replication/rustfs.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/replication/rustfs.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/aws.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/aws.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/minio.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/minio.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/rustfs.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/rustfs.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/aws.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/aws.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/minio.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/minio.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/rustfs.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/rustfs.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/aws.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/aws.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/minio.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/minio.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/rustfs.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/rustfs.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/website/aws.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/website/aws.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/website/minio.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/website/minio.out.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/website/rustfs.in.xml create mode 100644 crates/ecstore/tests/fixtures/bucket-config-goldens/website/rustfs.out.xml create mode 100755 scripts/gen_bucket_config_goldens.sh diff --git a/crates/ecstore/tests/bucket_config_goldens.rs b/crates/ecstore/tests/bucket_config_goldens.rs new file mode 100644 index 000000000..453569942 --- /dev/null +++ b/crates/ecstore/tests/bucket_config_goldens.rs @@ -0,0 +1,495 @@ +// Copyright 2026 RustFS Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Byte-level goldens for the 13 persisted bucket-configuration XML families +//! (rustfs/backlog#2744). +//! +//! Responsible for: pinning the exact bytes that `deserialize` followed by +//! `serialize` produces for every family in its three source shapes (AWS +//! documentation shape, MinIO-written shape, RustFS-written shape), and the +//! exact bytes RustFS writes for a typed value. Only bytes are compared; a +//! structural comparison would let a codec replacement change the persisted +//! encoding without failing here. +//! +//! Not responsible for: validating configurations, touching the bucket +//! metadata store, or choosing a canonical shape. +//! +//! Upstream: `rustfs_ecstore::api::bucket::utils::{deserialize, serialize}` +//! and the s3s DTOs they are generic over. Downstream: the DTO/codec +//! replacement (T1.4) and the persisted-bytes comparison (T3.7), which must +//! keep every golden green without regenerating it. Regeneration is reserved +//! to `scripts/gen_bucket_config_goldens.sh`, which sets +//! `BUCKET_CONFIG_GOLDENS_WRITE`: in that mode every test writes its files +//! and then fails on purpose, so a write run never prints a green line. + +use rustfs_ecstore::api::bucket::utils::{deserialize, serialize}; +use s3s::dto::{ + AbortIncompleteMultipartUpload, AccelerateConfiguration, BucketAccelerateStatus, BucketLifecycleConfiguration, + BucketLoggingStatus, BucketVersioningStatus, CORSConfiguration, CORSRule, Condition, DefaultRetention, DelMarkerExpiration, + DeleteMarkerReplication, DeleteMarkerReplicationStatus, DeleteReplication, DeleteReplicationStatus, Destination, + ErrorDocument, Event, ExcludedPrefix, ExistingObjectReplication, ExistingObjectReplicationStatus, ExpirationStatus, + FilterRule, FilterRuleName, IndexDocument, LifecycleExpiration, LifecycleRule, LifecycleRuleAndOperator, LifecycleRuleFilter, + LoggingEnabled, NoncurrentVersionExpiration, NotificationConfiguration, NotificationConfigurationFilter, + ObjectLockConfiguration, ObjectLockEnabled, ObjectLockRetentionMode, ObjectLockRule, Payer, PublicAccessBlockConfiguration, + QueueConfiguration, Redirect, ReplicaModifications, ReplicaModificationsStatus, ReplicationConfiguration, ReplicationRule, + ReplicationRuleAndOperator, ReplicationRuleFilter, ReplicationRuleStatus, RequestPaymentConfiguration, RoutingRule, + S3KeyFilter, ServerSideEncryption, ServerSideEncryptionByDefault, ServerSideEncryptionConfiguration, + ServerSideEncryptionRule, SourceSelectionCriteria, StorageClass, Tag, Tagging, Transition, TransitionStorageClass, + VersioningConfiguration, WebsiteConfiguration, +}; +use s3s::xml; +use std::path::{Path, PathBuf}; + +/// Set only by `scripts/gen_bucket_config_goldens.sh`. Every pair test then +/// writes its `.out.xml` (and, for the `rustfs` shape, its `.in.xml`) and +/// fails on purpose, so a write run can never be mistaken for a verification. +const WRITE_ENV: &str = "BUCKET_CONFIG_GOLDENS_WRITE"; + +fn goldens_dir() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests") + .join("fixtures") + .join("bucket-config-goldens") +} + +fn pair_path(family: &str, shape: &str, side: &str) -> PathBuf { + goldens_dir().join(family).join(format!("{shape}.{side}.xml")) +} + +fn read(path: &Path) -> Vec { + std::fs::read(path).unwrap_or_else(|err| panic!("read {}: {err}", path.display())) +} + +fn write(path: &Path, bytes: &[u8]) { + std::fs::write(path, bytes).unwrap_or_else(|err| panic!("write {}: {err}", path.display())); +} + +fn contains(haystack: &[u8], needle: &[u8]) -> bool { + haystack.windows(needle.len()).any(|window| window == needle) +} + +fn round_trip(input: &[u8], label: &str) -> Vec +where + T: for<'xml> xml::Deserialize<'xml> + xml::Serialize, +{ + let parsed: T = deserialize(input).unwrap_or_else(|err| panic!("{label}: deserialize failed: {err}")); + serialize(&parsed).unwrap_or_else(|err| panic!("{label}: serialize failed: {err}")) +} + +/// Byte comparison with a readable report. Parsed values are never compared. +fn assert_bytes_eq(actual: &[u8], expected: &[u8], context: &str) { + if actual != expected { + panic!( + "{context}\n--- expected ({} bytes) ---\n{}\n--- actual ({} bytes) ---\n{}\n", + expected.len(), + String::from_utf8_lossy(expected), + actual.len(), + String::from_utf8_lossy(actual), + ); + } +} + +/// Checks one `/` pair. `typed` is the value RustFS writes for +/// the `rustfs` shape; the `aws` and `minio` shapes pass `None`. +fn check_pair(family: &str, shape: &str, typed: Option<&T>) +where + T: for<'xml> xml::Deserialize<'xml> + xml::Serialize, +{ + let in_path = pair_path(family, shape, "in"); + let out_path = pair_path(family, shape, "out"); + let label = format!("{family}/{shape}"); + + if std::env::var_os(WRITE_ENV).is_some() { + if let Some(value) = typed { + let written = serialize(value).unwrap_or_else(|err| panic!("{label}: typed value failed to serialize: {err}")); + write(&in_path, &written); + } + let input = read(&in_path); + write(&out_path, &round_trip::(&input, &label)); + panic!( + "{WRITE_ENV} is set: wrote {} without verifying anything; rerun without {WRITE_ENV}", + out_path.display() + ); + } + + let input = read(&in_path); + let expected = read(&out_path); + if let Some(value) = typed { + let written = serialize(value).unwrap_or_else(|err| panic!("{label}: typed value failed to serialize: {err}")); + assert_bytes_eq( + &written, + &input, + &format!( + "{label}: RustFS no longer writes the pinned bytes for the typed value ({})", + in_path.display() + ), + ); + } + let actual = round_trip::(&input, &label); + assert_bytes_eq( + &actual, + &expected, + &format!("{label}: deserialize->serialize bytes drifted from {}", out_path.display()), + ); + let again = round_trip::(&expected, &label); + assert_bytes_eq( + &again, + &expected, + &format!("{label}: the golden is not a fixed point of deserialize->serialize"), + ); +} + +/// The typed values behind every `rustfs.in.xml`: what RustFS writes today +/// through `serialize` when it persists a configuration it built itself. +/// MinIO-only members (`DelMarkerExpiration`, `ExcludeFolders`, +/// `ExcludedPrefixes`, `DeleteReplication`) are included where RustFS accepts +/// them, so the `rustfs` shape is the one that pins their encoding. +mod rustfs_values { + use super::*; + + fn tag(key: &str, value: &str) -> Tag { + Tag { + key: Some(key.to_owned()), + value: Some(value.to_owned()), + } + } + + pub(super) fn notification() -> NotificationConfiguration { + NotificationConfiguration { + queue_configurations: Some(vec![QueueConfiguration { + events: vec![ + Event::from("s3:ObjectCreated:*".to_owned()), + Event::from("s3:ObjectRemoved:*".to_owned()), + ], + filter: Some(NotificationConfigurationFilter { + key: Some(S3KeyFilter { + filter_rules: Some(vec![FilterRule { + name: Some(FilterRuleName::from_static(FilterRuleName::PREFIX)), + value: Some("uploads/".to_owned()), + }]), + }), + }), + id: Some("rustfs-webhook".to_owned()), + queue_arn: "arn:rustfs:sqs::primary:webhook".to_owned(), + }]), + ..Default::default() + } + } + + pub(super) fn lifecycle() -> BucketLifecycleConfiguration { + BucketLifecycleConfiguration { + rules: vec![ + LifecycleRule { + abort_incomplete_multipart_upload: Some(AbortIncompleteMultipartUpload { + days_after_initiation: Some(3), + }), + del_marker_expiration: Some(DelMarkerExpiration { days: Some(7) }), + expiration: Some(LifecycleExpiration { + days: Some(30), + ..Default::default() + }), + filter: Some(LifecycleRuleFilter { + prefix: Some("tmp/".to_owned()), + ..Default::default() + }), + id: Some("expire-tmp".to_owned()), + noncurrent_version_expiration: Some(NoncurrentVersionExpiration { + newer_noncurrent_versions: Some(2), + noncurrent_days: Some(7), + }), + noncurrent_version_transitions: None, + prefix: None, + status: ExpirationStatus::from_static(ExpirationStatus::ENABLED), + transitions: None, + }, + LifecycleRule { + abort_incomplete_multipart_upload: None, + del_marker_expiration: None, + expiration: None, + filter: Some(LifecycleRuleFilter { + and: Some(LifecycleRuleAndOperator { + prefix: Some("archive/".to_owned()), + tags: Some(vec![tag("tier", "cold")]), + ..Default::default() + }), + ..Default::default() + }), + id: Some("tier-cold".to_owned()), + noncurrent_version_expiration: None, + noncurrent_version_transitions: None, + prefix: None, + status: ExpirationStatus::from_static(ExpirationStatus::ENABLED), + transitions: Some(vec![Transition { + days: Some(90), + storage_class: Some(TransitionStorageClass::from_static("COLDTIER")), + ..Default::default() + }]), + }, + ], + ..Default::default() + } + } + + pub(super) fn object_lock() -> ObjectLockConfiguration { + ObjectLockConfiguration { + object_lock_enabled: Some(ObjectLockEnabled::from_static(ObjectLockEnabled::ENABLED)), + rule: Some(ObjectLockRule { + default_retention: Some(DefaultRetention { + days: Some(30), + mode: Some(ObjectLockRetentionMode::from_static(ObjectLockRetentionMode::GOVERNANCE)), + years: None, + }), + }), + } + } + + pub(super) fn versioning() -> VersioningConfiguration { + VersioningConfiguration { + exclude_folders: Some(true), + excluded_prefixes: Some(vec![ + ExcludedPrefix { + prefix: Some("tmp/".to_owned()), + }, + ExcludedPrefix { + prefix: Some("cache/".to_owned()), + }, + ]), + mfa_delete: None, + status: Some(BucketVersioningStatus::from_static(BucketVersioningStatus::ENABLED)), + } + } + + pub(super) fn encryption() -> ServerSideEncryptionConfiguration { + ServerSideEncryptionConfiguration { + rules: vec![ServerSideEncryptionRule { + apply_server_side_encryption_by_default: Some(ServerSideEncryptionByDefault { + kms_master_key_id: Some("rustfs-bucket-key".to_owned()), + sse_algorithm: ServerSideEncryption::from_static(ServerSideEncryption::AWS_KMS), + }), + blocked_encryption_types: None, + bucket_key_enabled: Some(false), + }], + } + } + + pub(super) fn tagging() -> Tagging { + Tagging { + tag_set: vec![tag("team", "storage"), tag("env", "staging")], + } + } + + pub(super) fn replication() -> ReplicationConfiguration { + ReplicationConfiguration { + role: String::new(), + rules: vec![ReplicationRule { + delete_marker_replication: Some(DeleteMarkerReplication { + status: Some(DeleteMarkerReplicationStatus::from_static(DeleteMarkerReplicationStatus::ENABLED)), + }), + delete_replication: Some(DeleteReplication { + status: DeleteReplicationStatus::from_static(DeleteReplicationStatus::ENABLED), + }), + destination: Destination { + bucket: "arn:rustfs:replication::0b6f3c2a-5e6a-4d2b-9c1e-7a8f9d0e1b2c:interop-dr".to_owned(), + storage_class: Some(StorageClass::from_static(StorageClass::STANDARD)), + ..Default::default() + }, + existing_object_replication: Some(ExistingObjectReplication { + status: ExistingObjectReplicationStatus::from_static(ExistingObjectReplicationStatus::ENABLED), + }), + filter: Some(ReplicationRuleFilter { + and: Some(ReplicationRuleAndOperator { + prefix: Some("docs/".to_owned()), + tags: Some(vec![tag("replicate", "yes")]), + }), + ..Default::default() + }), + id: Some("rustfs-dr".to_owned()), + prefix: None, + priority: Some(1), + source_selection_criteria: Some(SourceSelectionCriteria { + replica_modifications: Some(ReplicaModifications { + status: ReplicaModificationsStatus::from_static(ReplicaModificationsStatus::ENABLED), + }), + sse_kms_encrypted_objects: None, + }), + status: ReplicationRuleStatus::from_static(ReplicationRuleStatus::ENABLED), + }], + } + } + + pub(super) fn cors() -> CORSConfiguration { + CORSConfiguration { + cors_rules: vec![CORSRule { + allowed_headers: Some(vec!["Authorization".to_owned(), "Content-Type".to_owned()]), + allowed_methods: vec!["GET".to_owned(), "PUT".to_owned()], + allowed_origins: vec!["https://app.example.internal".to_owned()], + expose_headers: Some(vec!["ETag".to_owned(), "x-amz-request-id".to_owned()]), + id: Some("rustfs-app".to_owned()), + max_age_seconds: Some(3600), + }], + } + } + + pub(super) fn logging() -> BucketLoggingStatus { + BucketLoggingStatus { + logging_enabled: Some(LoggingEnabled { + target_bucket: "rustfs-access-logs".to_owned(), + target_grants: None, + target_object_key_format: None, + target_prefix: "buckets/interop/".to_owned(), + }), + } + } + + pub(super) fn website() -> WebsiteConfiguration { + WebsiteConfiguration { + error_document: Some(ErrorDocument { + key: "404.html".to_owned(), + }), + index_document: Some(IndexDocument { + suffix: "index.html".to_owned(), + }), + redirect_all_requests_to: None, + routing_rules: Some(vec![RoutingRule { + condition: Some(Condition { + http_error_code_returned_equals: None, + key_prefix_equals: Some("legacy/".to_owned()), + }), + redirect: Redirect { + host_name: None, + http_redirect_code: Some("301".to_owned()), + protocol: None, + replace_key_prefix_with: Some("current/".to_owned()), + replace_key_with: None, + }, + }]), + } + } + + pub(super) fn accelerate() -> AccelerateConfiguration { + AccelerateConfiguration { + status: Some(BucketAccelerateStatus::from_static(BucketAccelerateStatus::SUSPENDED)), + } + } + + pub(super) fn request_payment() -> RequestPaymentConfiguration { + RequestPaymentConfiguration { + payer: Payer::from_static(Payer::BUCKET_OWNER), + } + } + + pub(super) fn public_access_block() -> PublicAccessBlockConfiguration { + PublicAccessBlockConfiguration { + block_public_acls: Some(true), + block_public_policy: Some(false), + ignore_public_acls: Some(true), + restrict_public_buckets: Some(false), + } + } +} + +/// One test per `/` pair: 13 families x 3 shapes = 39. +mod bucket_config_goldens { + use super::*; + + macro_rules! golden { + ($name:ident, $family:literal, $shape:literal, $ty:ty) => { + #[test] + fn $name() { + check_pair::<$ty>($family, $shape, None); + } + }; + ($name:ident, $family:literal, rustfs = $value:expr) => { + #[test] + fn $name() { + check_pair($family, "rustfs", Some(&$value)); + } + }; + } + + golden!(notification_aws, "notification", "aws", NotificationConfiguration); + golden!(notification_minio, "notification", "minio", NotificationConfiguration); + golden!(notification_rustfs, "notification", rustfs = rustfs_values::notification()); + + golden!(lifecycle_aws, "lifecycle", "aws", BucketLifecycleConfiguration); + golden!(lifecycle_minio, "lifecycle", "minio", BucketLifecycleConfiguration); + golden!(lifecycle_rustfs, "lifecycle", rustfs = rustfs_values::lifecycle()); + + golden!(object_lock_aws, "object_lock", "aws", ObjectLockConfiguration); + golden!(object_lock_minio, "object_lock", "minio", ObjectLockConfiguration); + golden!(object_lock_rustfs, "object_lock", rustfs = rustfs_values::object_lock()); + + golden!(versioning_aws, "versioning", "aws", VersioningConfiguration); + golden!(versioning_minio, "versioning", "minio", VersioningConfiguration); + golden!(versioning_rustfs, "versioning", rustfs = rustfs_values::versioning()); + + golden!(encryption_aws, "encryption", "aws", ServerSideEncryptionConfiguration); + golden!(encryption_minio, "encryption", "minio", ServerSideEncryptionConfiguration); + golden!(encryption_rustfs, "encryption", rustfs = rustfs_values::encryption()); + + golden!(tagging_aws, "tagging", "aws", Tagging); + golden!(tagging_minio, "tagging", "minio", Tagging); + golden!(tagging_rustfs, "tagging", rustfs = rustfs_values::tagging()); + + /// `replication/aws.in.xml` is the unknown-element sample from + /// `crates/replication/src/config.rs` + /// (`s3_xml_parser_discards_unknown_replication_elements_before_validation`). + /// Beyond the byte golden, the pair proves the root-level `FutureTopLevel` + /// element is skipped on read and never re-emitted on write. + #[test] + fn replication_aws() { + check_pair::("replication", "aws", None); + let input = read(&pair_path("replication", "aws", "in")); + let output = read(&pair_path("replication", "aws", "out")); + assert!( + contains(&input, b"future"), + "the sample must still carry the unknown element" + ); + assert!( + !contains(&output, b"FutureTopLevel"), + "an unknown element must not be re-emitted: {}", + String::from_utf8_lossy(&output) + ); + } + golden!(replication_minio, "replication", "minio", ReplicationConfiguration); + golden!(replication_rustfs, "replication", rustfs = rustfs_values::replication()); + + golden!(cors_aws, "cors", "aws", CORSConfiguration); + golden!(cors_minio, "cors", "minio", CORSConfiguration); + golden!(cors_rustfs, "cors", rustfs = rustfs_values::cors()); + + golden!(logging_aws, "logging", "aws", BucketLoggingStatus); + golden!(logging_minio, "logging", "minio", BucketLoggingStatus); + golden!(logging_rustfs, "logging", rustfs = rustfs_values::logging()); + + golden!(website_aws, "website", "aws", WebsiteConfiguration); + golden!(website_minio, "website", "minio", WebsiteConfiguration); + golden!(website_rustfs, "website", rustfs = rustfs_values::website()); + + golden!(accelerate_aws, "accelerate", "aws", AccelerateConfiguration); + golden!(accelerate_minio, "accelerate", "minio", AccelerateConfiguration); + golden!(accelerate_rustfs, "accelerate", rustfs = rustfs_values::accelerate()); + + golden!(request_payment_aws, "request_payment", "aws", RequestPaymentConfiguration); + golden!(request_payment_minio, "request_payment", "minio", RequestPaymentConfiguration); + golden!(request_payment_rustfs, "request_payment", rustfs = rustfs_values::request_payment()); + + golden!(public_access_block_aws, "public_access_block", "aws", PublicAccessBlockConfiguration); + golden!(public_access_block_minio, "public_access_block", "minio", PublicAccessBlockConfiguration); + golden!( + public_access_block_rustfs, + "public_access_block", + rustfs = rustfs_values::public_access_block() + ); +} diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/README.md b/crates/ecstore/tests/fixtures/bucket-config-goldens/README.md new file mode 100644 index 000000000..79ed5ebd6 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/README.md @@ -0,0 +1,58 @@ +# Persisted bucket-configuration XML goldens (rustfs/backlog#2744) + +Byte-level goldens for the 13 XML families that `BucketMetadata` persists as +`*_config_xml`. They pin what `rustfs_ecstore::api::bucket::utils::deserialize` +followed by `serialize` emits today, so that the DTO/codec replacement (T1.4) +and the persisted-bytes comparison (T3.7) can be proven byte-identical: an +older binary must keep reading what a newer one writes, and the reverse. + +Generated at commit: `6b1554003ebf8f2037ffb7da9c9b906527e758da` (2026-10-07T17:40:56Z) + +Checked by `crates/ecstore/tests/bucket_config_goldens.rs` +(`cargo nextest run -p rustfs-ecstore bucket_config_goldens`, 39 tests). +Generated once by `scripts/gen_bucket_config_goldens.sh`, which records the +commit above and refuses to run again unless `--force` is passed. Do not +regenerate after a DTO or codec replacement: at that point a diff here is the +finding, not noise. + +## Layout + +`/.in.xml` is an input; `/.out.xml` is the exact +bytes of `serialize(deserialize(in))` on the generating commit. The test +asserts `bytes == out` (never structural equality), that `out` is a fixed point +of the same round trip, and for the `rustfs` shape that `serialize(typed +value)` still equals `rustfs.in.xml`. + +| Shape | Source | +| --- | --- | +| `aws` | Written for this corpus from the public S3 XML schema in the shape of the AWS documentation examples: XML declaration, `xmlns` on the root, indented. No AWS prose is copied. | +| `minio` | The bytes a MinIO server persists. Seven families are carved verbatim out of `../minio/bucket_metadata.blob.hex` (MinIO `RELEASE.2025-07-23T15-54-02Z`); the rest are hand-shaped as compact Go `encoding/xml` output with the S3 namespace on the root, because MinIO does not persist those families. | +| `rustfs` | The bytes RustFS writes today through `serialize` for a typed value (`rustfs_values` in the test). This shape carries the MinIO-only members RustFS accepts (`DelMarkerExpiration`, `ExcludeFolders`, `ExcludedPrefixes`, `DeleteReplication`). | + +| Family | Root element | `minio.in.xml` provenance | +| --- | --- | --- | +| `notification` | `NotificationConfiguration` | MinIO fixture | +| `lifecycle` | `LifecycleConfiguration` | MinIO fixture (carries the MinIO `ExpiryUpdatedAt` extension) | +| `object_lock` | `ObjectLockConfiguration` | MinIO fixture | +| `versioning` | `VersioningConfiguration` | MinIO fixture | +| `encryption` | `ServerSideEncryptionConfiguration` | MinIO fixture | +| `tagging` | `Tagging` | MinIO fixture | +| `replication` | `ReplicationConfiguration` | MinIO fixture | +| `cors` | `CORSConfiguration` | hand-shaped | +| `logging` | `BucketLoggingStatus` | hand-shaped | +| `website` | `WebsiteConfiguration` | hand-shaped | +| `accelerate` | `AccelerateConfiguration` | hand-shaped | +| `request_payment` | `RequestPaymentConfiguration` | hand-shaped | +| `public_access_block` | `PublicAccessBlockConfiguration` | hand-shaped | + +## Unknown elements + +`replication/aws.in.xml` is the unknown-element sample from +`crates/replication/src/config.rs` +(`s3_xml_parser_discards_unknown_replication_elements_before_validation`), +re-indented to two spaces. Its golden proves the root-level `FutureTopLevel` +element is skipped on read and never re-emitted on write, which is the +persisted-configuration exception recorded in the root `AGENTS.md`. On the +generating commit every one of the 13 root elements skips unknown children; +nested members (`Rule`, `Filter`, `Destination`, ...) reject them. A +replacement codec must keep that boundary where it is. diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/aws.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/aws.in.xml new file mode 100644 index 000000000..31dbe22dc --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/aws.in.xml @@ -0,0 +1,4 @@ + + + Enabled + diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/aws.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/aws.out.xml new file mode 100644 index 000000000..fe2112a4a --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/aws.out.xml @@ -0,0 +1 @@ +Enabled \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/minio.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/minio.in.xml new file mode 100644 index 000000000..ab4c07e22 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/minio.in.xml @@ -0,0 +1 @@ +Suspended \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/minio.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/minio.out.xml new file mode 100644 index 000000000..561693d13 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/minio.out.xml @@ -0,0 +1 @@ +Suspended \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/rustfs.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/rustfs.in.xml new file mode 100644 index 000000000..561693d13 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/rustfs.in.xml @@ -0,0 +1 @@ +Suspended \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/rustfs.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/rustfs.out.xml new file mode 100644 index 000000000..561693d13 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/accelerate/rustfs.out.xml @@ -0,0 +1 @@ +Suspended \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/cors/aws.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/cors/aws.in.xml new file mode 100644 index 000000000..c09c3f379 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/cors/aws.in.xml @@ -0,0 +1,17 @@ + + + + AllowWebApp + https://www.example.com + PUT + POST + DELETE + * + x-amz-server-side-encryption + 3000 + + + * + GET + + diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/cors/aws.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/cors/aws.out.xml new file mode 100644 index 000000000..8cfdd133a --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/cors/aws.out.xml @@ -0,0 +1 @@ +*PUTPOSTDELETEhttps://www.example.comx-amz-server-side-encryptionAllowWebApp3000GET* \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/cors/minio.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/cors/minio.in.xml new file mode 100644 index 000000000..07104308b --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/cors/minio.in.xml @@ -0,0 +1 @@ +consolehttps://console.example.internalGETHEADAuthorizationContent-TypeETag600 \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/cors/minio.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/cors/minio.out.xml new file mode 100644 index 000000000..9efddcafd --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/cors/minio.out.xml @@ -0,0 +1 @@ +AuthorizationContent-TypeGETHEADhttps://console.example.internalETagconsole600 \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/cors/rustfs.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/cors/rustfs.in.xml new file mode 100644 index 000000000..812774365 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/cors/rustfs.in.xml @@ -0,0 +1 @@ +AuthorizationContent-TypeGETPUThttps://app.example.internalETagx-amz-request-idrustfs-app3600 \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/cors/rustfs.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/cors/rustfs.out.xml new file mode 100644 index 000000000..812774365 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/cors/rustfs.out.xml @@ -0,0 +1 @@ +AuthorizationContent-TypeGETPUThttps://app.example.internalETagx-amz-request-idrustfs-app3600 \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/aws.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/aws.in.xml new file mode 100644 index 000000000..36fad7d63 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/aws.in.xml @@ -0,0 +1,10 @@ + + + + + aws:kms + arn:aws:kms:us-east-1:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab + + true + + diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/aws.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/aws.out.xml new file mode 100644 index 000000000..b52d521f9 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/aws.out.xml @@ -0,0 +1 @@ +arn:aws:kms:us-east-1:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890abaws:kmstrue \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/minio.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/minio.in.xml new file mode 100644 index 000000000..4c5ca385c --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/minio.in.xml @@ -0,0 +1 @@ +AES256 \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/minio.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/minio.out.xml new file mode 100644 index 000000000..4b30c39d6 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/minio.out.xml @@ -0,0 +1 @@ +AES256 \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/rustfs.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/rustfs.in.xml new file mode 100644 index 000000000..aff4b4c2e --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/rustfs.in.xml @@ -0,0 +1 @@ +rustfs-bucket-keyaws:kmsfalse \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/rustfs.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/rustfs.out.xml new file mode 100644 index 000000000..aff4b4c2e --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/encryption/rustfs.out.xml @@ -0,0 +1 @@ +rustfs-bucket-keyaws:kmsfalse \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/aws.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/aws.in.xml new file mode 100644 index 000000000..b4aaf272d --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/aws.in.xml @@ -0,0 +1,49 @@ + + + + Archive and then delete rule + + + projectdocs/ + + department + finance + + 1024 + + + Enabled + + 30 + STANDARD_IA + + + 365 + GLACIER + + + 3650 + + + 90 + GLACIER + + + 400 + 3 + + + 7 + + + + Expire delete markers + + logs/ + + Disabled + + true + + + diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/aws.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/aws.out.xml new file mode 100644 index 000000000..b13a97dbf --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/aws.out.xml @@ -0,0 +1 @@ +736501024projectdocs/departmentfinanceArchive and then delete rule340090GLACIEREnabled30STANDARD_IA365GLACIERtruelogs/Expire delete markersDisabled \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/minio.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/minio.in.xml new file mode 100644 index 000000000..f9eecb33e --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/minio.in.xml @@ -0,0 +1 @@ +d96i4g89k8h26a95st60Enabled3072026-07-07T15:58:57.337315Z \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/minio.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/minio.out.xml new file mode 100644 index 000000000..a596877ac --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/minio.out.xml @@ -0,0 +1 @@ +2026-07-07T15:58:57.337Z30d96i4g89k8h26a95st607Enabled \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/rustfs.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/rustfs.in.xml new file mode 100644 index 000000000..a28f7d809 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/rustfs.in.xml @@ -0,0 +1 @@ +3730tmp/expire-tmp27Enabledarchive/tiercoldtier-coldEnabled90COLDTIER \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/rustfs.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/rustfs.out.xml new file mode 100644 index 000000000..a28f7d809 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/lifecycle/rustfs.out.xml @@ -0,0 +1 @@ +3730tmp/expire-tmp27Enabledarchive/tiercoldtier-coldEnabled90COLDTIER \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/logging/aws.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/logging/aws.in.xml new file mode 100644 index 000000000..e62471432 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/logging/aws.in.xml @@ -0,0 +1,20 @@ + + + + amzn-s3-demo-logging-bucket + logs/ + + + + user@example.com + + READ + + + + + EventTime + + + + diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/logging/aws.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/logging/aws.out.xml new file mode 100644 index 000000000..4a58ba50d --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/logging/aws.out.xml @@ -0,0 +1 @@ +amzn-s3-demo-logging-bucketuser@example.comREADEventTimelogs/ \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/logging/minio.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/logging/minio.in.xml new file mode 100644 index 000000000..6825ccaa7 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/logging/minio.in.xml @@ -0,0 +1 @@ +interop-logsinterop/ \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/logging/minio.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/logging/minio.out.xml new file mode 100644 index 000000000..ac963228b --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/logging/minio.out.xml @@ -0,0 +1 @@ +interop-logsinterop/ \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/logging/rustfs.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/logging/rustfs.in.xml new file mode 100644 index 000000000..bae5acad5 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/logging/rustfs.in.xml @@ -0,0 +1 @@ +rustfs-access-logsbuckets/interop/ \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/logging/rustfs.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/logging/rustfs.out.xml new file mode 100644 index 000000000..bae5acad5 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/logging/rustfs.out.xml @@ -0,0 +1 @@ +rustfs-access-logsbuckets/interop/ \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/notification/aws.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/notification/aws.in.xml new file mode 100644 index 000000000..16202844a --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/notification/aws.in.xml @@ -0,0 +1,30 @@ + + + + ImageUploads + arn:aws:sns:us-east-1:123456789012:s3-events + s3:ObjectCreated:* + + + + prefix + images/ + + + suffix + .jpg + + + + + + Deletions + arn:aws:sqs:us-east-1:123456789012:s3-deletes + s3:ObjectRemoved:* + + + Thumbnails + arn:aws:lambda:us-east-1:123456789012:function:thumbnail + s3:ObjectCreated:Put + + diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/notification/aws.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/notification/aws.out.xml new file mode 100644 index 000000000..3f7fbeca9 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/notification/aws.out.xml @@ -0,0 +1 @@ +s3:ObjectCreated:PutThumbnailsarn:aws:lambda:us-east-1:123456789012:function:thumbnails3:ObjectRemoved:*Deletionsarn:aws:sqs:us-east-1:123456789012:s3-deletess3:ObjectCreated:*prefiximages/suffix.jpgImageUploadsarn:aws:sns:us-east-1:123456789012:s3-events \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/notification/minio.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/notification/minio.in.xml new file mode 100644 index 000000000..cba96f421 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/notification/minio.in.xml @@ -0,0 +1 @@ +s3:ObjectCreated:*arn:minio:sqs::primary:webhook \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/notification/minio.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/notification/minio.out.xml new file mode 100644 index 000000000..5879b0e35 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/notification/minio.out.xml @@ -0,0 +1 @@ +s3:ObjectCreated:*arn:minio:sqs::primary:webhook \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/notification/rustfs.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/notification/rustfs.in.xml new file mode 100644 index 000000000..47708e353 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/notification/rustfs.in.xml @@ -0,0 +1 @@ +s3:ObjectCreated:*s3:ObjectRemoved:*prefixuploads/rustfs-webhookarn:rustfs:sqs::primary:webhook \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/notification/rustfs.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/notification/rustfs.out.xml new file mode 100644 index 000000000..47708e353 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/notification/rustfs.out.xml @@ -0,0 +1 @@ +s3:ObjectCreated:*s3:ObjectRemoved:*prefixuploads/rustfs-webhookarn:rustfs:sqs::primary:webhook \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/aws.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/aws.in.xml new file mode 100644 index 000000000..a33a606cf --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/aws.in.xml @@ -0,0 +1,10 @@ + + + Enabled + + + COMPLIANCE + 1 + + + diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/aws.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/aws.out.xml new file mode 100644 index 000000000..16912b1ca --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/aws.out.xml @@ -0,0 +1 @@ +EnabledCOMPLIANCE1 \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/minio.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/minio.in.xml new file mode 100644 index 000000000..b811df7ed --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/minio.in.xml @@ -0,0 +1 @@ +EnabledGOVERNANCE7 \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/minio.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/minio.out.xml new file mode 100644 index 000000000..7e06bb0ad --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/minio.out.xml @@ -0,0 +1 @@ +Enabled7GOVERNANCE \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/rustfs.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/rustfs.in.xml new file mode 100644 index 000000000..b0fa52d34 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/rustfs.in.xml @@ -0,0 +1 @@ +Enabled30GOVERNANCE \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/rustfs.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/rustfs.out.xml new file mode 100644 index 000000000..b0fa52d34 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/object_lock/rustfs.out.xml @@ -0,0 +1 @@ +Enabled30GOVERNANCE \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/aws.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/aws.in.xml new file mode 100644 index 000000000..2004839f2 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/aws.in.xml @@ -0,0 +1,7 @@ + + + TRUE + TRUE + TRUE + TRUE + diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/aws.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/aws.out.xml new file mode 100644 index 000000000..5c1124dcd --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/aws.out.xml @@ -0,0 +1 @@ +truetruetruetrue \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/minio.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/minio.in.xml new file mode 100644 index 000000000..f66476ef0 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/minio.in.xml @@ -0,0 +1 @@ +truefalsetruefalse \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/minio.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/minio.out.xml new file mode 100644 index 000000000..2c799a1e2 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/minio.out.xml @@ -0,0 +1 @@ +truetruefalsefalse \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/rustfs.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/rustfs.in.xml new file mode 100644 index 000000000..c3a75d692 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/rustfs.in.xml @@ -0,0 +1 @@ +truefalsetruefalse \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/rustfs.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/rustfs.out.xml new file mode 100644 index 000000000..c3a75d692 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/public_access_block/rustfs.out.xml @@ -0,0 +1 @@ +truefalsetruefalse \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/replication/aws.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/replication/aws.in.xml new file mode 100644 index 000000000..245ef3cfe --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/replication/aws.in.xml @@ -0,0 +1,12 @@ + + + + future + + unknown + Enabled + + arn:aws:s3:::destination + + + diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/replication/aws.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/replication/aws.out.xml new file mode 100644 index 000000000..a7122f86e --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/replication/aws.out.xml @@ -0,0 +1 @@ +arn:aws:s3:::destinationunknownEnabled \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/replication/minio.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/replication/minio.in.xml new file mode 100644 index 000000000..0220c493b --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/replication/minio.in.xml @@ -0,0 +1 @@ +d96i4m09k8h2vldifkagEnabled1EnabledEnabledarn:minio:replication::ef5859af-120a-4218-94b5-be23470f3c60:interop-drEnabledEnabled \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/replication/minio.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/replication/minio.out.xml new file mode 100644 index 000000000..f949fe8c8 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/replication/minio.out.xml @@ -0,0 +1 @@ +EnabledEnabledarn:minio:replication::ef5859af-120a-4218-94b5-be23470f3c60:interop-drEnabledd96i4m09k8h2vldifkag1EnabledEnabled \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/replication/rustfs.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/replication/rustfs.in.xml new file mode 100644 index 000000000..f6cc535bd --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/replication/rustfs.in.xml @@ -0,0 +1 @@ +EnabledEnabledarn:rustfs:replication::0b6f3c2a-5e6a-4d2b-9c1e-7a8f9d0e1b2c:interop-drSTANDARDEnableddocs/replicateyesrustfs-dr1EnabledEnabled \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/replication/rustfs.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/replication/rustfs.out.xml new file mode 100644 index 000000000..f6cc535bd --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/replication/rustfs.out.xml @@ -0,0 +1 @@ +EnabledEnabledarn:rustfs:replication::0b6f3c2a-5e6a-4d2b-9c1e-7a8f9d0e1b2c:interop-drSTANDARDEnableddocs/replicateyesrustfs-dr1EnabledEnabled \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/aws.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/aws.in.xml new file mode 100644 index 000000000..b584c6ff8 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/aws.in.xml @@ -0,0 +1,4 @@ + + + Requester + diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/aws.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/aws.out.xml new file mode 100644 index 000000000..6eccecbd2 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/aws.out.xml @@ -0,0 +1 @@ +Requester \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/minio.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/minio.in.xml new file mode 100644 index 000000000..f4f838425 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/minio.in.xml @@ -0,0 +1 @@ +BucketOwner \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/minio.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/minio.out.xml new file mode 100644 index 000000000..f55e7ef36 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/minio.out.xml @@ -0,0 +1 @@ +BucketOwner \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/rustfs.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/rustfs.in.xml new file mode 100644 index 000000000..f55e7ef36 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/rustfs.in.xml @@ -0,0 +1 @@ +BucketOwner \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/rustfs.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/rustfs.out.xml new file mode 100644 index 000000000..f55e7ef36 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/request_payment/rustfs.out.xml @@ -0,0 +1 @@ +BucketOwner \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/aws.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/aws.in.xml new file mode 100644 index 000000000..6a595170c --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/aws.in.xml @@ -0,0 +1,13 @@ + + + + + Project + Project One + + + User + jsmith + + + diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/aws.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/aws.out.xml new file mode 100644 index 000000000..b587c1f57 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/aws.out.xml @@ -0,0 +1 @@ +ProjectProject OneUserjsmith \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/minio.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/minio.in.xml new file mode 100644 index 000000000..2428f642a --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/minio.in.xml @@ -0,0 +1 @@ +teamstorageenvprod \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/minio.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/minio.out.xml new file mode 100644 index 000000000..2428f642a --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/minio.out.xml @@ -0,0 +1 @@ +teamstorageenvprod \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/rustfs.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/rustfs.in.xml new file mode 100644 index 000000000..1b21a766e --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/rustfs.in.xml @@ -0,0 +1 @@ +teamstorageenvstaging \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/rustfs.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/rustfs.out.xml new file mode 100644 index 000000000..1b21a766e --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/tagging/rustfs.out.xml @@ -0,0 +1 @@ +teamstorageenvstaging \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/aws.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/aws.in.xml new file mode 100644 index 000000000..c8fabb9d4 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/aws.in.xml @@ -0,0 +1,5 @@ + + + Enabled + Disabled + diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/aws.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/aws.out.xml new file mode 100644 index 000000000..9682cd2ce --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/aws.out.xml @@ -0,0 +1 @@ +DisabledEnabled \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/minio.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/minio.in.xml new file mode 100644 index 000000000..c1f419417 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/minio.in.xml @@ -0,0 +1 @@ +Enabled \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/minio.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/minio.out.xml new file mode 100644 index 000000000..d44a53ecf --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/minio.out.xml @@ -0,0 +1 @@ +Enabled \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/rustfs.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/rustfs.in.xml new file mode 100644 index 000000000..9777f68c4 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/rustfs.in.xml @@ -0,0 +1 @@ +truetmp/cache/Enabled \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/rustfs.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/rustfs.out.xml new file mode 100644 index 000000000..9777f68c4 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/versioning/rustfs.out.xml @@ -0,0 +1 @@ +truetmp/cache/Enabled \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/website/aws.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/website/aws.in.xml new file mode 100644 index 000000000..fa17d400f --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/website/aws.in.xml @@ -0,0 +1,23 @@ + + + + index.html + + + error.html + + + + + docs/ + 404 + + + https + www.example.com + documents/ + 301 + + + + diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/website/aws.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/website/aws.out.xml new file mode 100644 index 000000000..93a770631 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/website/aws.out.xml @@ -0,0 +1 @@ +error.htmlindex.html404docs/www.example.com301httpsdocuments/ \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/website/minio.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/website/minio.in.xml new file mode 100644 index 000000000..d16f36225 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/website/minio.in.xml @@ -0,0 +1 @@ +index.html404.html \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/website/minio.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/website/minio.out.xml new file mode 100644 index 000000000..c3b7c1e37 --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/website/minio.out.xml @@ -0,0 +1 @@ +404.htmlindex.html \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/website/rustfs.in.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/website/rustfs.in.xml new file mode 100644 index 000000000..eab21872e --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/website/rustfs.in.xml @@ -0,0 +1 @@ +404.htmlindex.htmllegacy/301current/ \ No newline at end of file diff --git a/crates/ecstore/tests/fixtures/bucket-config-goldens/website/rustfs.out.xml b/crates/ecstore/tests/fixtures/bucket-config-goldens/website/rustfs.out.xml new file mode 100644 index 000000000..eab21872e --- /dev/null +++ b/crates/ecstore/tests/fixtures/bucket-config-goldens/website/rustfs.out.xml @@ -0,0 +1 @@ +404.htmlindex.htmllegacy/301current/ \ No newline at end of file diff --git a/scripts/README.md b/scripts/README.md index ffa67c455..2dab2e007 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -55,6 +55,7 @@ their issue closes. | `e2e-run.sh` | ci-gate | Boots a rustfs server and runs the `s3s-e2e` black-box conformance tool against it | ci.yml `e2e-tests` jobs; `docs/testing/README.md` | | `run_ecstore_validation_suite.sh` | dev-tool | ecstore black-box validation suite (`quick`/`full`/`destructive`/`fuzz` profiles) | `docs/testing/README.md`, `docs/testing/ecstore-validation-suite-design.md` | | `run_e2e_tests.sh` | dev-tool | Local `e2e_test` crate runner (starts a server, applies filters, cleans up) | `crates/e2e_test/README.md` | +| `gen_bucket_config_goldens.sh` | dev-tool | One-shot generator for the 13-family persisted bucket-config XML byte goldens; refuses to overwrite without `--force` and records the generating commit | `crates/ecstore/tests/fixtures/bucket-config-goldens/README.md`; `crates/ecstore/tests/bucket_config_goldens.rs` | | `run.sh` | dev-tool | Local rustfs startup wrapper | `make e2e-server`; Justfile | | `run.ps1` | dev-tool | Windows counterpart of `run.sh` | — | | `probe.sh` | dev-tool | Probe-style e2e run | `make probe-e2e` | diff --git a/scripts/gen_bucket_config_goldens.sh b/scripts/gen_bucket_config_goldens.sh new file mode 100755 index 000000000..b19f9ee50 --- /dev/null +++ b/scripts/gen_bucket_config_goldens.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash +# Generates the byte-level goldens for the 13 persisted bucket-configuration +# XML families (rustfs/backlog#2744). +# +# For every `crates/ecstore/tests/fixtures/bucket-config-goldens//` +# the test binary `bucket_config_goldens` writes, in write mode: +# - `rustfs.in.xml` the bytes RustFS writes today for the typed value +# - `.out.xml` the exact bytes of deserialize -> serialize for each +# `.in.xml` (shapes: aws, minio, rustfs) +# +# The goldens are the baseline that the DTO/codec replacement (T1.4) and the +# persisted-bytes comparison (T3.7) are judged against, so this script is +# meant to run ONCE on the pre-replacement baseline. It refuses to run when +# generated files already exist (pass --force to regenerate on purpose) and +# when the codec-affecting paths carry uncommitted changes (the commit it +# records must describe the codec that produced the bytes). +# +# Write mode never reports success: every pair test writes its files and then +# fails on purpose. The verification that follows is the only green line, and +# it must report exactly 39 passed. +# +# Usage: scripts/gen_bucket_config_goldens.sh [--force] + +set -euo pipefail + +cd "$(dirname "$0")/.." + +GOLDENS_DIR="crates/ecstore/tests/fixtures/bucket-config-goldens" +README="$GOLDENS_DIR/README.md" +TEST_BINARY="bucket_config_goldens" +WRITE_ENV="BUCKET_CONFIG_GOLDENS_WRITE" +EXPECTED_FAMILIES=13 +EXPECTED_PAIRS=39 + +usage() { + printf 'Usage: %s [--force]\n' "$0" + printf ' --force regenerate even though generated files already exist\n' +} + +force=0 +for arg in "$@"; do + case "$arg" in + --force) force=1 ;; + -h | --help) + usage + exit 0 + ;; + *) + printf 'unknown argument: %s\n' "$arg" >&2 + usage >&2 + exit 2 + ;; + esac +done + +if [[ ! -d "$GOLDENS_DIR" || ! -f "$README" ]]; then + printf 'missing %s or its README.md\n' "$GOLDENS_DIR" >&2 + exit 1 +fi + +family_count="$(find "$GOLDENS_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')" +if [[ "$family_count" -ne "$EXPECTED_FAMILIES" ]]; then + printf 'expected %s family directories under %s, found %s\n' "$EXPECTED_FAMILIES" "$GOLDENS_DIR" "$family_count" >&2 + exit 1 +fi + +existing="$(find "$GOLDENS_DIR" -type f \( -name '*.out.xml' -o -name 'rustfs.in.xml' \) | sort)" +if [[ -n "$existing" && "$force" -ne 1 ]]; then + printf 'refusing: generated files already exist; pass --force to regenerate on purpose\n%s\n' "$existing" >&2 + exit 1 +fi + +dirty="$(git status --porcelain -- crates/ecstore/src Cargo.toml Cargo.lock)" +if [[ -n "$dirty" ]]; then + printf 'refusing: codec-affecting paths have uncommitted changes, so the recorded commit would not describe the codec that produced the bytes\n%s\n' "$dirty" >&2 + exit 1 +fi + +log="$(mktemp -t bucket-config-goldens.XXXXXX)" +trap 'rm -f "$log"' EXIT + +if [[ -n "$existing" ]]; then + printf 'removing previously generated files (--force)\n' + printf '%s\n' "$existing" | xargs rm -f +fi + +printf 'writing goldens (every pair test fails on purpose in write mode)...\n' +env "$WRITE_ENV=1" cargo nextest run -p rustfs-ecstore --test "$TEST_BINARY" --no-fail-fast >"$log" 2>&1 || true + +out_count="$(find "$GOLDENS_DIR" -type f -name '*.out.xml' | wc -l | tr -d ' ')" +in_count="$(find "$GOLDENS_DIR" -type f -name 'rustfs.in.xml' | wc -l | tr -d ' ')" +if [[ "$out_count" -ne "$EXPECTED_PAIRS" || "$in_count" -ne "$EXPECTED_FAMILIES" ]]; then + cat "$log" >&2 + printf 'write mode produced %s/%s .out.xml and %s/%s rustfs.in.xml files\n' \ + "$out_count" "$EXPECTED_PAIRS" "$in_count" "$EXPECTED_FAMILIES" >&2 + exit 1 +fi + +printf 'verifying the generated goldens...\n' +cargo nextest run -p rustfs-ecstore --test "$TEST_BINARY" 2>&1 | tee "$log" +if ! grep -Eq "Summary.* ${EXPECTED_PAIRS} tests run: ${EXPECTED_PAIRS} passed" "$log"; then + printf 'verification did not report %s passed\n' "$EXPECTED_PAIRS" >&2 + exit 1 +fi + +commit="$(git rev-parse HEAD)" +stamp="$(date -u +%Y-%m-%dT%H:%M:%SZ)" +line="Generated at commit: \`$commit\` ($stamp)" +tmp="$(mktemp -t bucket-config-goldens-readme.XXXXXX)" +awk -v line="$line" '/^Generated at commit:/ { print line; next } { print }' "$README" >"$tmp" +mv "$tmp" "$README" +if ! grep -Fq "$line" "$README"; then + printf 'failed to record the generating commit in %s\n' "$README" >&2 + exit 1 +fi + +printf 'generated %s pairs across %s families at %s\n' "$EXPECTED_PAIRS" "$EXPECTED_FAMILIES" "$commit"