From 801838fdbd1c2ae7aada11ea3ec023e86f93a599 Mon Sep 17 00:00:00 2001 From: Zhengchao An Date: Mon, 29 Jun 2026 10:32:09 +0800 Subject: [PATCH] test(ecstore): guard replication facade boundary (#4035) --- .../ecstore/src/bucket/replication/README.md | 4 ++ .../tests/replication_facade_compat_test.rs | 56 +++++++++++++++++++ crates/ecstore/tests/storage_api.rs | 7 +++ scripts/check_architecture_migration_rules.sh | 13 +++++ 4 files changed, 80 insertions(+) create mode 100644 crates/ecstore/tests/replication_facade_compat_test.rs diff --git a/crates/ecstore/src/bucket/replication/README.md b/crates/ecstore/src/bucket/replication/README.md index bdfe208b5..a5327830b 100644 --- a/crates/ecstore/src/bucket/replication/README.md +++ b/crates/ecstore/src/bucket/replication/README.md @@ -47,3 +47,7 @@ and lifecycle/heal scheduling paths. Start with `ReplicationRuntime` or `ReplicationEventSink`. Both can be added as narrow internal contracts while keeping current queue, MRF, resync, and target behavior unchanged. Do not start with a crate move. + +Current compatibility guard: `crates/ecstore/tests/replication_facade_compat_test.rs` +keeps the ECStore replication facade types covered while architecture rules +keep direct imports behind local `storage_api` boundaries. diff --git a/crates/ecstore/tests/replication_facade_compat_test.rs b/crates/ecstore/tests/replication_facade_compat_test.rs new file mode 100644 index 000000000..d94c87cff --- /dev/null +++ b/crates/ecstore/tests/replication_facade_compat_test.rs @@ -0,0 +1,56 @@ +// Copyright 2024 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. + +mod storage_api; + +use s3s::dto::ReplicationConfiguration; +use storage_api::replication_compat::{ + BucketStats, DeletedObjectReplicationInfo, DynReplicationPool, ObjectOpts, ReplicationConfigurationExt, ReplicationStats, + ResyncStatusType, +}; + +fn type_name() -> &'static str { + std::any::type_name::() +} + +fn type_name_unsized() -> &'static str { + std::any::type_name::() +} + +fn assert_replication_config_ext() {} + +#[test] +fn replication_facade_exports_config_extension_contract() { + assert_replication_config_ext::(); +} + +#[test] +fn replication_facade_exports_runtime_and_dto_types() { + assert_eq!(ResyncStatusType::default(), ResyncStatusType::NoResync); + assert!(!ResyncStatusType::default().is_valid()); + + let object_opts = ObjectOpts::default(); + assert!(object_opts.name.is_empty()); + + let deleted = DeletedObjectReplicationInfo::default(); + assert!(deleted.bucket.is_empty()); + + let _stats = ReplicationStats::new(); + let _bucket_stats = BucketStats::default(); + + assert!(type_name::().contains("ReplicationStats")); + assert!(type_name::().contains("BucketStats")); + assert!(type_name::().contains("DeletedObjectReplicationInfo")); + assert!(type_name_unsized::().contains("ReplicationPoolTrait")); +} diff --git a/crates/ecstore/tests/storage_api.rs b/crates/ecstore/tests/storage_api.rs index e828c00e0..42fd05fee 100644 --- a/crates/ecstore/tests/storage_api.rs +++ b/crates/ecstore/tests/storage_api.rs @@ -20,6 +20,13 @@ pub(crate) mod contract_compat { pub(crate) use super::{DiskStore, ECStore, Error, GetObjectReader, ObjectInfo, ObjectOptions, PutObjReader, SetDisks}; } +pub(crate) mod replication_compat { + pub(crate) use rustfs_ecstore::api::bucket::replication::{ + BucketStats, DeletedObjectReplicationInfo, DynReplicationPool, ObjectOpts, ReplicationConfigurationExt, ReplicationStats, + ResyncStatusType, + }; +} + pub(crate) mod legacy_bitrot_read { pub(crate) use super::{DiskOption, Endpoint, STORAGE_FORMAT_FILE, create_bitrot_reader, new_disk}; } diff --git a/scripts/check_architecture_migration_rules.sh b/scripts/check_architecture_migration_rules.sh index 0bf8d1121..38a1357a1 100755 --- a/scripts/check_architecture_migration_rules.sh +++ b/scripts/check_architecture_migration_rules.sh @@ -181,6 +181,7 @@ RUSTFS_APP_ADMIN_STORAGE_HELPER_ROOT_REEXPORT_HITS_FILE="${TMP_DIR}/rustfs_app_a EXTERNAL_TEST_ECSTORE_COMPAT_BYPASS_HITS_FILE="${TMP_DIR}/external_test_ecstore_compat_bypass_hits.txt" FUZZ_ECSTORE_COMPAT_BYPASS_HITS_FILE="${TMP_DIR}/fuzz_ecstore_compat_bypass_hits.txt" EXTERNAL_ECSTORE_API_BOUNDARY_HITS_FILE="${TMP_DIR}/external_ecstore_api_boundary_hits.txt" +REPLICATION_FACADE_BYPASS_HITS_FILE="${TMP_DIR}/replication_facade_bypass_hits.txt" LEGACY_ECSTORE_CONFIG_MODEL_HITS_FILE="${TMP_DIR}/legacy_ecstore_config_model_hits.txt" ECSTORE_ROOT_STORE_SET_DISK_MODULE_HITS_FILE="${TMP_DIR}/ecstore_root_store_set_disk_module_hits.txt" ECSTORE_ROOT_STORE_SUPPORT_MODULE_HITS_FILE="${TMP_DIR}/ecstore_root_store_support_module_hits.txt" @@ -2277,6 +2278,18 @@ if [[ -s "$EXTERNAL_ECSTORE_API_BOUNDARY_HITS_FILE" ]]; then report_failure "external ECStore API facade imports must stay in local storage_api boundary files: $(paste -sd '; ' "$EXTERNAL_ECSTORE_API_BOUNDARY_HITS_FILE")" fi +( + cd "$ROOT_DIR" + rg -n --with-filename 'rustfs_ecstore::api::bucket::replication' \ + rustfs/src crates/*/src crates/*/tests crates/ecstore/tests fuzz/fuzz_targets \ + --glob '*.rs' | + rg -v '^(rustfs/src/(admin/storage_api|app/storage_api|storage/storage_api)\.rs|crates/ecstore/tests/storage_api\.rs|crates/obs/src/metrics/storage_api\.rs|crates/scanner/src/storage_api\.rs|crates/scanner/tests/storage_api/mod\.rs|fuzz/fuzz_targets/(bucket_validation_storage_api|path_containment_storage_api)\.rs):' || true +) >"$REPLICATION_FACADE_BYPASS_HITS_FILE" + +if [[ -s "$REPLICATION_FACADE_BYPASS_HITS_FILE" ]]; then + report_failure "replication facade imports must stay in local storage_api boundaries: $(paste -sd '; ' "$REPLICATION_FACADE_BYPASS_HITS_FILE")" +fi + ( cd "$ROOT_DIR" {