diff --git a/.config/e2e-full-selection.txt b/.config/e2e-full-selection.txt index bb9022327..dfad0f0bd 100644 --- a/.config/e2e-full-selection.txt +++ b/.config/e2e-full-selection.txt @@ -1,2 +1,2 @@ -sha256-darwin=b4ae71aa894e5c7795ae3eb8116f1777a7601d0f5db3898be2e48faf3329bd9b -sha256-linux=433debd9d9defa832986269abdf0f1d131597b2d7a417ce930e17c1fd47d85ba +sha256-darwin=9f767b37ed8b1c82da62ea441462d75487785c8086e56f08fb6f6cd89c6e2e52 +sha256-linux=fbdaf42b220958d4b1e8880e0f8b5a7992d38e21051bb60596dd4538424757d6 diff --git a/crates/e2e_test/src/kms/mod.rs b/crates/e2e_test/src/kms/mod.rs index 5e6b9fe19..3b849fa1b 100644 --- a/crates/e2e_test/src/kms/mod.rs +++ b/crates/e2e_test/src/kms/mod.rs @@ -39,9 +39,6 @@ mod kms_edge_cases_test; #[cfg(test)] mod kms_fault_recovery_test; -#[cfg(test)] -mod test_runner; - #[cfg(test)] mod bucket_default_encryption_test; diff --git a/crates/e2e_test/src/kms/test_runner.rs b/crates/e2e_test/src/kms/test_runner.rs deleted file mode 100644 index 558c14631..000000000 --- a/crates/e2e_test/src/kms/test_runner.rs +++ /dev/null @@ -1,499 +0,0 @@ -// 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 -// -#![allow(dead_code)] -// 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. - -//! Unified KMS test suite runner -//! -//! This module provides a unified interface for running KMS tests with categorization, -//! filtering, and comprehensive reporting capabilities. - -use crate::common::init_logging; -use std::time::Instant; -use tokio::time::{Duration, sleep}; -use tracing::{debug, error, info, warn}; - -/// Test category for organization and filtering -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum TestCategory { - CoreFunctionality, - MultipartEncryption, - EdgeCases, - FaultRecovery, - Comprehensive, - Performance, -} - -impl TestCategory { - pub fn as_str(&self) -> &'static str { - match self { - TestCategory::CoreFunctionality => "core-functionality", - TestCategory::MultipartEncryption => "multipart-encryption", - TestCategory::EdgeCases => "edge-cases", - TestCategory::FaultRecovery => "fault-recovery", - TestCategory::Comprehensive => "comprehensive", - TestCategory::Performance => "performance", - } - } -} - -/// Test definition with metadata -#[derive(Debug, Clone)] -pub struct TestDefinition { - pub name: String, - pub description: String, - pub category: TestCategory, - pub estimated_duration: Duration, - pub is_critical: bool, -} - -impl TestDefinition { - pub fn new( - name: impl Into, - description: impl Into, - category: TestCategory, - estimated_duration: Duration, - is_critical: bool, - ) -> Self { - Self { - name: name.into(), - description: description.into(), - category, - estimated_duration, - is_critical, - } - } -} - -/// Test execution result -#[derive(Debug, Clone)] -pub struct TestResult { - pub test_name: String, - pub category: TestCategory, - pub success: bool, - pub duration: Duration, - pub error_message: Option, -} - -impl TestResult { - pub fn success(test_name: String, category: TestCategory, duration: Duration) -> Self { - Self { - test_name, - category, - success: true, - duration, - error_message: None, - } - } - - pub fn failure(test_name: String, category: TestCategory, duration: Duration, error: String) -> Self { - Self { - test_name, - category, - success: false, - duration, - error_message: Some(error), - } - } -} - -/// Comprehensive test suite configuration -#[derive(Debug, Clone)] -pub struct TestSuiteConfig { - pub categories: Vec, - pub include_critical_only: bool, - pub max_duration: Option, - pub parallel_execution: bool, -} - -impl Default for TestSuiteConfig { - fn default() -> Self { - Self { - categories: vec![ - TestCategory::CoreFunctionality, - TestCategory::MultipartEncryption, - TestCategory::EdgeCases, - TestCategory::FaultRecovery, - TestCategory::Comprehensive, - ], - include_critical_only: false, - max_duration: None, - parallel_execution: false, - } - } -} - -/// Unified KMS test suite runner -pub struct KMSTestSuite { - tests: Vec, - config: TestSuiteConfig, -} - -impl KMSTestSuite { - /// Create a new test suite with default configuration - pub fn new() -> Self { - let tests = vec![ - // Core Functionality Tests - TestDefinition::new( - "test_local_kms_end_to_end", - "End-to-end KMS test with all encryption types", - TestCategory::CoreFunctionality, - Duration::from_secs(60), - true, - ), - TestDefinition::new( - "test_local_kms_key_isolation", - "Test KMS key isolation and security", - TestCategory::CoreFunctionality, - Duration::from_secs(45), - true, - ), - // Multipart Encryption Tests - TestDefinition::new( - "test_local_kms_multipart_upload", - "Test large file multipart upload with encryption", - TestCategory::MultipartEncryption, - Duration::from_secs(120), - true, - ), - TestDefinition::new( - "test_step1_basic_single_file_encryption", - "Basic single file encryption test", - TestCategory::MultipartEncryption, - Duration::from_secs(30), - false, - ), - TestDefinition::new( - "test_step2_basic_multipart_upload_without_encryption", - "Basic multipart upload without encryption", - TestCategory::MultipartEncryption, - Duration::from_secs(45), - false, - ), - TestDefinition::new( - "test_step3_multipart_upload_with_sse_s3", - "Multipart upload with SSE-S3 encryption", - TestCategory::MultipartEncryption, - Duration::from_secs(60), - true, - ), - TestDefinition::new( - "test_step4_large_multipart_upload_with_encryption", - "Large file multipart upload with encryption", - TestCategory::MultipartEncryption, - Duration::from_secs(90), - false, - ), - TestDefinition::new( - "test_step5_all_encryption_types_multipart", - "All encryption types multipart test", - TestCategory::MultipartEncryption, - Duration::from_secs(120), - true, - ), - // Edge Cases Tests - TestDefinition::new( - "test_kms_zero_byte_file_encryption", - "Test encryption of zero-byte files", - TestCategory::EdgeCases, - Duration::from_secs(20), - false, - ), - TestDefinition::new( - "test_kms_single_byte_file_encryption", - "Test encryption of single-byte files", - TestCategory::EdgeCases, - Duration::from_secs(20), - false, - ), - TestDefinition::new( - "test_kms_multipart_boundary_conditions", - "Test multipart upload boundary conditions", - TestCategory::EdgeCases, - Duration::from_secs(45), - false, - ), - TestDefinition::new( - "test_kms_invalid_key_scenarios", - "Test invalid key scenarios", - TestCategory::EdgeCases, - Duration::from_secs(30), - false, - ), - TestDefinition::new( - "test_kms_concurrent_encryption", - "Test concurrent encryption operations", - TestCategory::EdgeCases, - Duration::from_secs(60), - false, - ), - TestDefinition::new( - "test_kms_key_validation_security", - "Test key validation security", - TestCategory::EdgeCases, - Duration::from_secs(30), - false, - ), - // Fault Recovery Tests - TestDefinition::new( - "test_kms_key_directory_unavailable", - "Test KMS when key directory is unavailable", - TestCategory::FaultRecovery, - Duration::from_secs(45), - false, - ), - TestDefinition::new( - "test_kms_corrupted_key_files", - "Test KMS with corrupted key files", - TestCategory::FaultRecovery, - Duration::from_secs(30), - false, - ), - TestDefinition::new( - "test_kms_multipart_upload_interruption", - "Test multipart upload interruption recovery", - TestCategory::FaultRecovery, - Duration::from_secs(60), - false, - ), - TestDefinition::new( - "test_kms_resource_constraints", - "Test KMS under resource constraints", - TestCategory::FaultRecovery, - Duration::from_secs(90), - false, - ), - // Comprehensive Tests - TestDefinition::new( - "test_comprehensive_kms_full_workflow", - "Full KMS workflow comprehensive test", - TestCategory::Comprehensive, - Duration::from_secs(300), - true, - ), - TestDefinition::new( - "test_comprehensive_stress_test", - "KMS stress test with large datasets", - TestCategory::Comprehensive, - Duration::from_secs(400), - false, - ), - TestDefinition::new( - "test_comprehensive_key_isolation", - "Comprehensive key isolation test", - TestCategory::Comprehensive, - Duration::from_secs(180), - false, - ), - TestDefinition::new( - "test_comprehensive_concurrent_operations", - "Comprehensive concurrent operations test", - TestCategory::Comprehensive, - Duration::from_secs(240), - false, - ), - TestDefinition::new( - "test_comprehensive_performance_benchmark", - "KMS performance benchmark test", - TestCategory::Comprehensive, - Duration::from_secs(360), - false, - ), - ]; - - Self { - tests, - config: TestSuiteConfig::default(), - } - } - - /// Configure the test suite - pub fn with_config(mut self, config: TestSuiteConfig) -> Self { - self.config = config; - self - } - - /// Filter tests based on category - pub fn filter_by_category(&self, category: &TestCategory) -> Vec<&TestDefinition> { - self.tests.iter().filter(|test| &test.category == category).collect() - } - - /// Filter tests based on criticality - pub fn filter_critical_tests(&self) -> Vec<&TestDefinition> { - self.tests.iter().filter(|test| test.is_critical).collect() - } - - /// Get test summary by category - pub fn get_category_summary(&self) -> std::collections::HashMap> { - let mut summary = std::collections::HashMap::new(); - for test in &self.tests { - summary.entry(test.category.clone()).or_insert_with(Vec::new).push(test); - } - summary - } - - /// Run the complete test suite - pub async fn run_test_suite(&self) -> Vec { - init_logging(); - info!("πŸš€ Starting unified KMS test suite"); - - let start_time = Instant::now(); - let mut results = Vec::new(); - - // Filter tests based on configuration - let tests_to_run: Vec<&TestDefinition> = self - .tests - .iter() - .filter(|test| self.config.categories.contains(&test.category)) - .filter(|test| !self.config.include_critical_only || test.is_critical) - .collect(); - - info!("πŸ“Š Test plan: {} test(s) scheduled", tests_to_run.len()); - for (i, test) in tests_to_run.iter().enumerate() { - info!(" {}. {} ({})", i + 1, test.name, test.category.as_str()); - } - - // Execute tests - for (i, test_def) in tests_to_run.iter().enumerate() { - info!("πŸ§ͺ Running test {}/{}: {}", i + 1, tests_to_run.len(), test_def.name); - info!(" πŸ“ Description: {}", test_def.description); - info!(" 🏷️ Category: {}", test_def.category.as_str()); - info!(" ⏱️ Estimated duration: {:?}", test_def.estimated_duration); - - let test_start = Instant::now(); - let result = self.run_single_test(test_def).await; - let test_duration = test_start.elapsed(); - - match result { - Ok(_) => { - info!("βœ… Test passed: {} ({:.2}s)", test_def.name, test_duration.as_secs_f64()); - results.push(TestResult::success(test_def.name.clone(), test_def.category.clone(), test_duration)); - } - Err(e) => { - error!("❌ Test failed: {} ({:.2}s): {}", test_def.name, test_duration.as_secs_f64(), e); - results.push(TestResult::failure( - test_def.name.clone(), - test_def.category.clone(), - test_duration, - e.to_string(), - )); - } - } - - // Add delay between tests to avoid resource conflicts - if i < tests_to_run.len() - 1 { - debug!("⏸️ Waiting two seconds before the next test..."); - sleep(Duration::from_secs(2)).await; - } - } - - let total_duration = start_time.elapsed(); - self.print_test_summary(&results, total_duration); - - results - } - - /// Run a single test by dispatching to the appropriate test function - async fn run_single_test(&self, test_def: &TestDefinition) -> Result<(), Box> { - // This is a placeholder for test dispatch logic - // In a real implementation, this would dispatch to actual test functions - warn!("⚠️ Test '{}' is not implemented in the unified runner; skipping", test_def.name); - Ok(()) - } - - /// Print comprehensive test summary - fn print_test_summary(&self, results: &[TestResult], total_duration: Duration) { - info!("πŸ“Š KMS test suite summary"); - info!("⏱️ Total duration: {:.2} seconds", total_duration.as_secs_f64()); - info!("πŸ“ˆ Total tests: {}", results.len()); - - let passed = results.iter().filter(|r| r.success).count(); - let failed = results.iter().filter(|r| !r.success).count(); - - info!("βœ… Passed: {}", passed); - info!("❌ Failed: {}", failed); - info!("πŸ“Š Success rate: {:.1}%", (passed as f64 / results.len() as f64) * 100.0); - - // Summary by category - let mut category_summary: std::collections::HashMap = std::collections::HashMap::new(); - for result in results { - let (total, passed_count) = category_summary.entry(result.category.clone()).or_insert((0, 0)); - *total += 1; - if result.success { - *passed_count += 1; - } - } - - info!("πŸ“Š Category summary:"); - for (category, (total, passed_count)) in category_summary { - info!( - " 🏷️ {}: {}/{} ({:.1}%)", - category.as_str(), - passed_count, - total, - (passed_count as f64 / total as f64) * 100.0 - ); - } - - // List failed tests - if failed > 0 { - warn!("❌ Failing tests:"); - for result in results.iter().filter(|r| !r.success) { - warn!(" - {}: {}", result.test_name, result.error_message.as_deref().unwrap_or("Unknown error")); - } - } - } -} - -/// Quick test suite for critical tests only -#[tokio::test] -async fn test_kms_critical_suite() -> Result<(), Box> { - let config = TestSuiteConfig { - categories: vec![TestCategory::CoreFunctionality, TestCategory::MultipartEncryption], - include_critical_only: true, - max_duration: Some(Duration::from_secs(600)), // 10 minutes max - parallel_execution: false, - }; - - let suite = KMSTestSuite::new().with_config(config); - let results = suite.run_test_suite().await; - - let failed_count = results.iter().filter(|r| !r.success).count(); - if failed_count > 0 { - return Err(format!("Critical test suite failed: {failed_count} tests failed").into()); - } - - info!("βœ… All critical tests passed"); - Ok(()) -} - -/// Full comprehensive test suite -#[tokio::test] -async fn test_kms_full_suite() -> Result<(), Box> { - let suite = KMSTestSuite::new(); - let results = suite.run_test_suite().await; - - let total_tests = results.len(); - let failed_count = results.iter().filter(|r| !r.success).count(); - let success_rate = ((total_tests - failed_count) as f64 / total_tests as f64) * 100.0; - - info!("πŸ“Š Full suite success rate: {:.1}%", success_rate); - - // Allow up to 10% failure rate for non-critical tests - if success_rate < 90.0 { - return Err(format!("Test suite success rate too low: {success_rate:.1}%").into()); - } - - info!("βœ… Full test suite succeeded"); - Ok(()) -} diff --git a/docs/testing/e2e-suite-inventory.md b/docs/testing/e2e-suite-inventory.md index 7d80a802d..df2fc0847 100644 --- a/docs/testing/e2e-suite-inventory.md +++ b/docs/testing/e2e-suite-inventory.md @@ -58,7 +58,7 @@ | heal_erasure_disk_rebuild_test | 4 | πŸŒ™ | | inline_fast_path_cluster_test | 16 | | | internode_rpc_signature_e2e_test | 5 | | -| kms | 48 | | +| kms | 46 | | | leading_slash_key_test | 2 | βœ… | | lifecycle_regression_test | 4 | | | list_buckets_auth_test | 1 | βœ… | @@ -99,4 +99,4 @@ | tls_hot_reload_test | 1 | βœ… | | version_id_regression_test | 10 | βœ… | -**Total listed: 577 tests across 82 modules Β· PR smoke: 163 tests / 36 modules Β· merge/main full: 455 tests / 73 modules Β· nightly replication: 55 tests Β· nightly cluster faults: 28 tests / 7 modules Β· nightly protocols: 16 tests** Β· updated 2026-08-21. +**Total listed: 575 tests across 82 modules Β· PR smoke: 163 tests / 36 modules Β· merge/main full: 453 tests / 73 modules Β· nightly replication: 55 tests Β· nightly cluster faults: 28 tests / 7 modules Β· nightly protocols: 16 tests** Β· updated 2026-08-23.