test(e2e): gate protocol runner by requested features (#2912)

This commit is contained in:
Henry Guo
2026-05-11 21:59:24 +08:00
committed by GitHub
parent aba65a448c
commit 941986b331
3 changed files with 192 additions and 40 deletions
+3 -3
View File
@@ -16,9 +16,9 @@ RUSTFS_BUILD_FEATURES=ftps,webdav,sftp cargo test --package e2e_test test_protoc
```
`RUSTFS_BUILD_FEATURES` controls which features the test rustfs binary is
built with. The protocol test runner schedules every entry (FTPS, WebDAV,
SFTP) regardless of the feature set, so the binary must include every
protocol the runner spawns or the corresponding entries will fail.
built with. When this variable is set, the protocol test runner schedules
only entries whose protocol is present in the requested feature list. Leave
it unset to run every protocol entry.
`--test-threads=1` is required because every entry spawns a rustfs server
on fixed bind ports.
+121 -28
View File
@@ -15,6 +15,7 @@
//! Protocol test runner
use crate::common::init_logging;
use crate::common::{requested_rustfs_build_features, rustfs_build_feature_enabled};
use crate::protocols::ftps_core::test_ftps_core_operations;
use crate::protocols::sftp_compliance::{
test_sftp_compliance_readonly, test_sftp_compliance_standalone, test_sftp_compliance_suite,
@@ -59,36 +60,22 @@ pub struct ProtocolTestSuite {
#[derive(Debug, Clone)]
struct TestDefinition {
name: String,
name: &'static str,
required_feature: &'static str,
}
impl ProtocolTestSuite {
/// Create default test suite
pub fn new() -> Self {
let tests = vec![
TestDefinition {
name: "test_ftps_core_operations".to_string(),
},
TestDefinition {
name: "test_webdav_core_operations".to_string(),
},
TestDefinition {
name: "test_sftp_core_operations".to_string(),
},
TestDefinition {
name: "test_sftp_compliance_suite".to_string(),
},
TestDefinition {
name: "test_sftp_compliance_readonly".to_string(),
},
TestDefinition {
name: "test_sftp_idle_timeout_disconnects".to_string(),
},
TestDefinition {
name: "test_sftp_compliance_standalone".to_string(),
},
];
let requested_features = requested_rustfs_build_features();
Self::with_requested_features(requested_features.as_deref())
}
fn with_requested_features(requested_features: Option<&str>) -> Self {
let tests = all_protocol_tests()
.into_iter()
.filter(|test| rustfs_build_feature_enabled(requested_features, test.required_feature))
.collect();
Self { tests }
}
@@ -104,7 +91,7 @@ impl ProtocolTestSuite {
// Run tests
for (i, test_def) in self.tests.iter().enumerate() {
let test_description = match test_def.name.as_str() {
let test_description = match test_def.name {
"test_ftps_core_operations" => {
info!("=== Starting FTPS Module Test ===");
"FTPS core operations (put, ls, mkdir, rmdir, delete)"
@@ -147,11 +134,11 @@ impl ProtocolTestSuite {
match result {
Ok(_) => {
info!("Test passed: {} ({:.2}s)", test_def.name, test_duration.as_secs_f64());
results.push(TestResult::success(test_def.name.clone()));
results.push(TestResult::success(test_def.name.to_string()));
}
Err(e) => {
error!("Test failed: {} ({:.2}s): {}", test_def.name, test_duration.as_secs_f64(), e);
results.push(TestResult::failure(test_def.name.clone(), e.to_string()));
results.push(TestResult::failure(test_def.name.to_string(), e.to_string()));
}
}
@@ -169,7 +156,7 @@ impl ProtocolTestSuite {
/// Run a single test
async fn run_single_test(&self, test_def: &TestDefinition) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
match test_def.name.as_str() {
match test_def.name {
"test_ftps_core_operations" => test_ftps_core_operations().await.map_err(|e| e.into()),
"test_webdav_core_operations" => test_webdav_core_operations().await.map_err(|e| e.into()),
"test_sftp_core_operations" => test_sftp_core_operations().await.map_err(|e| e.into()),
@@ -186,6 +173,10 @@ impl ProtocolTestSuite {
info!("=== Test Suite Summary ===");
info!("Total duration: {:.2}s", total_duration.as_secs_f64());
info!("Total tests: {}", results.len());
if results.is_empty() {
info!("No protocol tests scheduled for the requested feature set");
return;
}
let passed = results.iter().filter(|r| r.success).count();
let failed = results.len() - passed;
@@ -203,6 +194,39 @@ impl ProtocolTestSuite {
}
}
fn all_protocol_tests() -> Vec<TestDefinition> {
vec![
TestDefinition {
name: "test_ftps_core_operations",
required_feature: "ftps",
},
TestDefinition {
name: "test_webdav_core_operations",
required_feature: "webdav",
},
TestDefinition {
name: "test_sftp_core_operations",
required_feature: "sftp",
},
TestDefinition {
name: "test_sftp_compliance_suite",
required_feature: "sftp",
},
TestDefinition {
name: "test_sftp_compliance_readonly",
required_feature: "sftp",
},
TestDefinition {
name: "test_sftp_idle_timeout_disconnects",
required_feature: "sftp",
},
TestDefinition {
name: "test_sftp_compliance_standalone",
required_feature: "sftp",
},
]
}
/// Test suite
#[tokio::test]
#[serial]
@@ -218,3 +242,72 @@ async fn test_protocol_core_suite() -> Result<(), Box<dyn std::error::Error + Se
info!("All protocol tests passed");
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn scheduled_names(suite: ProtocolTestSuite) -> Vec<&'static str> {
suite.tests.into_iter().map(|test| test.name).collect()
}
#[test]
fn schedules_all_protocol_tests_without_feature_filter() {
let names = scheduled_names(ProtocolTestSuite::with_requested_features(None));
assert_eq!(names.len(), 7);
assert!(names.contains(&"test_ftps_core_operations"));
assert!(names.contains(&"test_webdav_core_operations"));
assert!(names.contains(&"test_sftp_core_operations"));
assert!(names.contains(&"test_sftp_compliance_standalone"));
}
#[test]
fn schedules_only_requested_non_sftp_protocols() {
let names = scheduled_names(ProtocolTestSuite::with_requested_features(Some("ftps, webdav")));
assert_eq!(names, vec!["test_ftps_core_operations", "test_webdav_core_operations"]);
}
#[test]
fn schedules_all_sftp_entries_for_sftp_feature() {
let names = scheduled_names(ProtocolTestSuite::with_requested_features(Some("sftp")));
assert_eq!(
names,
vec![
"test_sftp_core_operations",
"test_sftp_compliance_suite",
"test_sftp_compliance_readonly",
"test_sftp_idle_timeout_disconnects",
"test_sftp_compliance_standalone",
]
);
}
#[test]
fn feature_filter_is_case_insensitive() {
let names = scheduled_names(ProtocolTestSuite::with_requested_features(Some("SFTP")));
assert_eq!(names.len(), 5);
assert!(names.iter().all(|name| name.contains("sftp")));
}
#[test]
fn full_feature_schedules_all_protocol_tests() {
let names = scheduled_names(ProtocolTestSuite::with_requested_features(Some("full")));
assert_eq!(names.len(), 7);
assert!(names.contains(&"test_ftps_core_operations"));
assert!(names.contains(&"test_webdav_core_operations"));
assert!(names.contains(&"test_sftp_core_operations"));
assert!(names.contains(&"test_sftp_compliance_standalone"));
}
#[test]
fn schedules_no_tests_when_requested_features_have_no_protocols() {
let names = scheduled_names(ProtocolTestSuite::with_requested_features(Some("diagnostics")));
assert!(names.is_empty());
}
}