From 79378a70d08d3e82fa3a3b10aa3a94b6e2d4e936 Mon Sep 17 00:00:00 2001 From: overtrue Date: Sun, 23 Aug 2026 06:07:13 +0800 Subject: [PATCH] test(e2e): activate policy variable coverage --- .config/nextest.toml | 2 +- crates/e2e_test/README.md | 2 +- crates/e2e_test/src/policy/README.md | 19 +- crates/e2e_test/src/policy/mod.rs | 2 - .../src/policy/policy_variables_test.rs | 223 ++++++++--------- crates/e2e_test/src/policy/test_env.rs | 100 -------- crates/e2e_test/src/policy/test_runner.rs | 230 ------------------ 7 files changed, 104 insertions(+), 474 deletions(-) delete mode 100644 crates/e2e_test/src/policy/test_env.rs delete mode 100644 crates/e2e_test/src/policy/test_runner.rs diff --git a/.config/nextest.toml b/.config/nextest.toml index 097c79e3e..b98f052ac 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -399,7 +399,7 @@ path = "junit.xml" # `e2e-smoke` (20 fast) and `e2e-repl-nightly` (55 slow) lanes and reserves # it for those, so e2e-full does not double-run it. # * #[ignore]d tests — nextest skips them by default (no --run-ignored); the -# manual-localhost:9000 reliant/policy tests are ci-13's migration. +# manual-localhost:9000 reliant tests are ci-13's migration. # # Each e2e test spawns its own single-node rustfs server on a random port with # an isolated temp dir (crates/e2e_test/src/common.rs), so the set is diff --git a/crates/e2e_test/README.md b/crates/e2e_test/README.md index 24567ac6f..434293a9d 100644 --- a/crates/e2e_test/README.md +++ b/crates/e2e_test/README.md @@ -72,7 +72,7 @@ The reason string on each attribute is the classifier. Current classes: - **Needs a pre-started server** — `"requires running RustFS server at localhost:9000"` / `"Connects to existing rustfs server"`. These are the - `reliant/*` and `policy/test_runner` tests; start a server first (e.g. + `reliant/*` tests; start a server first (e.g. [`scripts/run_e2e_tests.sh`](../../scripts/run_e2e_tests.sh)) or use `--run-ignored`. - **Heavy / external tool** — `"Starts a rustfs server; enable when running diff --git a/crates/e2e_test/src/policy/README.md b/crates/e2e_test/src/policy/README.md index 16d4a4dc2..9ee03f9e3 100644 --- a/crates/e2e_test/src/policy/README.md +++ b/crates/e2e_test/src/policy/README.md @@ -11,29 +11,20 @@ The tests cover the following AWS policy variable scenarios: 3. **Variable concatenation** - Combining variables with static text like `prefix-${aws:username}-suffix` 4. **Nested variables** - Complex nested variable patterns like `${${aws:username}-test}` 5. **Deny scenarios** - Testing deny policies with variables +6. **STS credentials** - Variable resolution inherited by temporary credentials ## Prerequisites -- RustFS server binary - `awscurl` utility for admin API calls - AWS SDK for Rust (included in the project) ## Running Tests -### Run All Policy Tests Using Unified Test Runner - -```bash -# Run all policy tests with comprehensive reporting -# Note: Requires a RustFS server running on localhost:9000 -cargo test -p e2e_test policy::test_runner::test_policy_full_suite -- --nocapture --ignored --test-threads=1 - -# Run only critical policy tests -cargo test -p e2e_test policy::test_runner::test_policy_critical_suite -- --nocapture --ignored --test-threads=1 -``` - ### Run All Policy Tests ```bash # From the project root directory -cargo test -p e2e_test policy:: -- --nocapture --ignored --test-threads=1 -``` \ No newline at end of file +cargo test -p e2e_test policy:: -- --nocapture +``` + +Each test starts an isolated RustFS server on a dynamically allocated local port and cleans it up afterward. diff --git a/crates/e2e_test/src/policy/mod.rs b/crates/e2e_test/src/policy/mod.rs index 6efa597ab..4a6061c00 100644 --- a/crates/e2e_test/src/policy/mod.rs +++ b/crates/e2e_test/src/policy/mod.rs @@ -18,5 +18,3 @@ //! including single-value, multi-value, and nested variable scenarios. mod policy_variables_test; -mod test_env; -mod test_runner; diff --git a/crates/e2e_test/src/policy/policy_variables_test.rs b/crates/e2e_test/src/policy/policy_variables_test.rs index e9b603822..0dd096046 100644 --- a/crates/e2e_test/src/policy/policy_variables_test.rs +++ b/crates/e2e_test/src/policy/policy_variables_test.rs @@ -14,14 +14,17 @@ //! Tests for AWS IAM policy variables with single-value, multi-value, and nested scenarios -use crate::common::{awscurl_delete, awscurl_put, init_logging}; -use crate::policy::test_env::PolicyTestEnvironment; +use crate::common::{ + RustFSTestEnvironment, awscurl_delete, awscurl_put, build_test_s3_config, build_test_sts_client, init_logging, +}; +use aws_sdk_s3::Client; +use aws_sdk_s3::error::ProvideErrorMetadata; use aws_sdk_s3::primitives::ByteStream; use tracing::info; /// Helper function to create a regular user with given credentials async fn create_user( - env: &PolicyTestEnvironment, + env: &RustFSTestEnvironment, username: &str, password: &str, ) -> Result<(), Box> { @@ -36,20 +39,9 @@ async fn create_user( Ok(()) } -/// Helper function to create an STS user with given credentials -async fn create_sts_user( - env: &PolicyTestEnvironment, - username: &str, - password: &str, -) -> Result<(), Box> { - // For STS, we create a regular user first, then use it to assume roles - create_user(env, username, password).await?; - Ok(()) -} - /// Helper function to create and attach a policy async fn create_and_attach_policy( - env: &PolicyTestEnvironment, + env: &RustFSTestEnvironment, policy_name: &str, username: &str, policy_document: serde_json::Value, @@ -70,9 +62,9 @@ async fn create_and_attach_policy( } /// Helper function to clean up test resources -async fn cleanup_user_and_policy(env: &PolicyTestEnvironment, username: &str, policy_name: &str) { +async fn cleanup_user_and_policy(env: &RustFSTestEnvironment, username: &str, policy_name: &str) { // Create admin client for cleanup - let admin_client = env.create_s3_client(&env.access_key, &env.secret_key); + let admin_client = env.create_s3_client(); // Delete buckets that might have been created by this user let bucket_patterns = [ @@ -84,7 +76,7 @@ async fn cleanup_user_and_policy(env: &PolicyTestEnvironment, username: &str, po format!("{username}-test"), format!("{username}-sts-bucket"), format!("{username}-service-bucket"), - "private-test-bucket".to_string(), // For deny test + format!("{username}-private-bucket"), ]; // Try to delete objects and buckets @@ -121,24 +113,18 @@ async fn cleanup_user_and_policy(env: &PolicyTestEnvironment, username: &str, po /// Test AWS policy variables with single-value scenarios #[tokio::test(flavor = "multi_thread")] -#[ignore = "Starts a rustfs server; enable when running full E2E"] pub async fn test_aws_policy_variables_single_value() -> Result<(), Box> { - test_aws_policy_variables_single_value_impl().await -} - -/// Implementation function for single-value policy variables test -pub async fn test_aws_policy_variables_single_value_impl() -> Result<(), Box> { init_logging(); info!("Starting AWS policy variables single-value test"); - let env = PolicyTestEnvironment::with_address("127.0.0.1:9000").await?; + let mut env = RustFSTestEnvironment::new().await?; + env.start_rustfs_server(vec![]).await?; test_aws_policy_variables_single_value_impl_with_env(&env).await } -/// Implementation function for single-value policy variables test with shared environment -pub async fn test_aws_policy_variables_single_value_impl_with_env( - env: &PolicyTestEnvironment, +async fn test_aws_policy_variables_single_value_impl_with_env( + env: &RustFSTestEnvironment, ) -> Result<(), Box> { // Create test user let test_user = "testuser1"; @@ -198,9 +184,7 @@ pub async fn test_aws_policy_variables_single_value_impl_with_env( awscurl_put(&attach_policy_url, "", &env.access_key, &env.secret_key).await?; // Create S3 client for test user - let test_client = env.create_s3_client(test_user, test_password); - - tokio::time::sleep(std::time::Duration::from_millis(500)).await; + let test_client = env.create_s3_client_with_credentials(test_user, test_password); // Test 1: User should be able to list buckets (allowed by policy) info!("Test 1: User listing buckets"); @@ -257,11 +241,13 @@ pub async fn test_aws_policy_variables_single_value_impl_with_env( // Test 6: User should NOT be able to create bucket NOT matching username pattern info!("Test 6: User attempting to create bucket NOT matching pattern"); let other_bucket_name = "other-user-bucket"; - let create_other_result = test_client.create_bucket().bucket(other_bucket_name).send().await; - if create_other_result.is_ok() { - cleanup().await; - return Err("User should NOT be able to create bucket NOT matching username pattern".into()); - } + let denied = test_client + .create_bucket() + .bucket(other_bucket_name) + .send() + .await + .expect_err("a bucket outside the username pattern must be denied"); + assert_eq!(denied.as_service_error().and_then(ProvideErrorMetadata::code), Some("AccessDenied")); // Cleanup info!("Cleaning up test resources"); @@ -273,24 +259,18 @@ pub async fn test_aws_policy_variables_single_value_impl_with_env( /// Test AWS policy variables with multi-value scenarios #[tokio::test(flavor = "multi_thread")] -#[ignore = "Starts a rustfs server; enable when running full E2E"] pub async fn test_aws_policy_variables_multi_value() -> Result<(), Box> { - test_aws_policy_variables_multi_value_impl().await -} - -/// Implementation function for multi-value policy variables test -pub async fn test_aws_policy_variables_multi_value_impl() -> Result<(), Box> { init_logging(); info!("Starting AWS policy variables multi-value test"); - let env = PolicyTestEnvironment::with_address("127.0.0.1:9000").await?; + let mut env = RustFSTestEnvironment::new().await?; + env.start_rustfs_server(vec![]).await?; test_aws_policy_variables_multi_value_impl_with_env(&env).await } -/// Implementation function for multi-value policy variables test with shared environment -pub async fn test_aws_policy_variables_multi_value_impl_with_env( - env: &PolicyTestEnvironment, +async fn test_aws_policy_variables_multi_value_impl_with_env( + env: &RustFSTestEnvironment, ) -> Result<(), Box> { // Create test user let test_user = "testuser2"; @@ -338,7 +318,7 @@ pub async fn test_aws_policy_variables_multi_value_impl_with_env( create_and_attach_policy(env, policy_name, test_user, policy_document).await?; // Create S3 client for test user - let test_client = env.create_s3_client(test_user, test_password); + let test_client = env.create_s3_client_with_credentials(test_user, test_password); // Test 1: User should be able to create buckets matching any of the multi-value patterns info!("Test 1: User creating first bucket matching multi-value pattern"); @@ -368,11 +348,13 @@ pub async fn test_aws_policy_variables_multi_value_impl_with_env( // Test 4: User should NOT be able to create bucket NOT matching any multi-value pattern info!("Test 4: User attempting to create bucket NOT matching any pattern"); let other_bucket_name = format!("{test_user}-other-bucket"); - let create_other_result = test_client.create_bucket().bucket(&other_bucket_name).send().await; - if create_other_result.is_ok() { - cleanup().await; - return Err("User should NOT be able to create bucket NOT matching any multi-value pattern".into()); - } + let denied = test_client + .create_bucket() + .bucket(&other_bucket_name) + .send() + .await + .expect_err("a bucket outside all allowed patterns must be denied"); + assert_eq!(denied.as_service_error().and_then(ProvideErrorMetadata::code), Some("AccessDenied")); // Test 5: User should be able to list objects in their allowed buckets info!("Test 5: User listing objects in allowed buckets"); @@ -398,24 +380,18 @@ pub async fn test_aws_policy_variables_multi_value_impl_with_env( /// Test AWS policy variables with variable concatenation #[tokio::test(flavor = "multi_thread")] -#[ignore = "Starts a rustfs server; enable when running full E2E"] pub async fn test_aws_policy_variables_concatenation() -> Result<(), Box> { - test_aws_policy_variables_concatenation_impl().await -} - -/// Implementation function for concatenation policy variables test -pub async fn test_aws_policy_variables_concatenation_impl() -> Result<(), Box> { init_logging(); info!("Starting AWS policy variables concatenation test"); - let env = PolicyTestEnvironment::with_address("127.0.0.1:9000").await?; + let mut env = RustFSTestEnvironment::new().await?; + env.start_rustfs_server(vec![]).await?; test_aws_policy_variables_concatenation_impl_with_env(&env).await } -/// Implementation function for concatenation policy variables test with shared environment -pub async fn test_aws_policy_variables_concatenation_impl_with_env( - env: &PolicyTestEnvironment, +async fn test_aws_policy_variables_concatenation_impl_with_env( + env: &RustFSTestEnvironment, ) -> Result<(), Box> { // Create test user let test_user = "testuser3"; @@ -455,10 +431,7 @@ pub async fn test_aws_policy_variables_concatenation_impl_with_env( create_and_attach_policy(env, policy_name, test_user, policy_document).await?; // Create S3 client for test user - let test_client = env.create_s3_client(test_user, test_password); - - // Add a small delay to allow policy to propagate - tokio::time::sleep(std::time::Duration::from_millis(500)).await; + let test_client = env.create_s3_client_with_credentials(test_user, test_password); // Test: User should be able to create bucket matching concatenated pattern info!("Test: User creating bucket matching concatenated pattern"); @@ -487,41 +460,30 @@ pub async fn test_aws_policy_variables_concatenation_impl_with_env( /// Test AWS policy variables with nested scenarios #[tokio::test(flavor = "multi_thread")] -#[ignore = "Starts a rustfs server; enable when running full E2E"] pub async fn test_aws_policy_variables_nested() -> Result<(), Box> { - test_aws_policy_variables_nested_impl().await -} - -/// Implementation function for nested policy variables test -pub async fn test_aws_policy_variables_nested_impl() -> Result<(), Box> { init_logging(); info!("Starting AWS policy variables nested test"); - let env = PolicyTestEnvironment::with_address("127.0.0.1:9000").await?; + let mut env = RustFSTestEnvironment::new().await?; + env.start_rustfs_server(vec![]).await?; test_aws_policy_variables_nested_impl_with_env(&env).await } /// Test AWS policy variables with STS temporary credentials #[tokio::test(flavor = "multi_thread")] -#[ignore = "Starts a rustfs server; enable when running full E2E"] pub async fn test_aws_policy_variables_sts() -> Result<(), Box> { - test_aws_policy_variables_sts_impl().await -} - -/// Implementation function for STS policy variables test -pub async fn test_aws_policy_variables_sts_impl() -> Result<(), Box> { init_logging(); info!("Starting AWS policy variables STS test"); - let env = PolicyTestEnvironment::with_address("127.0.0.1:9000").await?; + let mut env = RustFSTestEnvironment::new().await?; + env.start_rustfs_server(vec![]).await?; test_aws_policy_variables_sts_impl_with_env(&env).await } -/// Implementation function for nested policy variables test with shared environment -pub async fn test_aws_policy_variables_nested_impl_with_env( - env: &PolicyTestEnvironment, +async fn test_aws_policy_variables_nested_impl_with_env( + env: &RustFSTestEnvironment, ) -> Result<(), Box> { // Create test user let test_user = "testuser4"; @@ -561,10 +523,7 @@ pub async fn test_aws_policy_variables_nested_impl_with_env( create_and_attach_policy(env, policy_name, test_user, policy_document).await?; // Create S3 client for test user - let test_client = env.create_s3_client(test_user, test_password); - - // Add a small delay to allow policy to propagate - tokio::time::sleep(std::time::Duration::from_millis(500)).await; + let test_client = env.create_s3_client_with_credentials(test_user, test_password); // Test nested variable resolution info!("Test: Nested variable resolution"); @@ -581,14 +540,14 @@ pub async fn test_aws_policy_variables_nested_impl_with_env( return Err(format!("User should be able to create bucket with nested variable: {e}").into()); } - // Verify bucket creation fails with unresolved variable - let unresolved_bucket = format!("${{}}-test {test_user}"); - let create_unresolved = test_client.create_bucket().bucket(&unresolved_bucket).send().await; - - if create_unresolved.is_ok() { - cleanup().await; - return Err("User should NOT be able to create bucket with unresolved variable".into()); - } + // Verify a valid bucket name outside the resolved resource is denied. + let denied = test_client + .create_bucket() + .bucket("other-user-test") + .send() + .await + .expect_err("a bucket outside the resolved nested variable must be denied"); + assert_eq!(denied.as_service_error().and_then(ProvideErrorMetadata::code), Some("AccessDenied")); // Cleanup info!("Cleaning up test resources"); @@ -598,9 +557,8 @@ pub async fn test_aws_policy_variables_nested_impl_with_env( Ok(()) } -/// Implementation function for STS policy variables test with shared environment -pub async fn test_aws_policy_variables_sts_impl_with_env( - env: &PolicyTestEnvironment, +async fn test_aws_policy_variables_sts_impl_with_env( + env: &RustFSTestEnvironment, ) -> Result<(), Box> { // Create test user for STS let test_user = "testuser-sts"; @@ -612,8 +570,7 @@ pub async fn test_aws_policy_variables_sts_impl_with_env( cleanup_user_and_policy(env, test_user, policy_name).await; }; - // Create STS user - create_sts_user(env, test_user, test_password).await?; + create_user(env, test_user, test_password).await?; // Create policy with STS-compatible variables let policy_document = serde_json::json!({ @@ -624,6 +581,11 @@ pub async fn test_aws_policy_variables_sts_impl_with_env( "Action": ["s3:ListAllMyBuckets"], "Resource": ["arn:aws:s3:::*"] }, + { + "Effect": "Allow", + "Action": ["sts:AssumeRole"], + "Resource": ["arn:aws:s3:::*"] + }, { "Effect": "Allow", "Action": ["s3:CreateBucket"], @@ -631,7 +593,12 @@ pub async fn test_aws_policy_variables_sts_impl_with_env( }, { "Effect": "Allow", - "Action": ["s3:ListBucket", "s3:PutObject", "s3:GetObject"], + "Action": ["s3:ListBucket"], + "Resource": [format!("arn:aws:s3:::{}-sts-bucket", "${aws:username}")] + }, + { + "Effect": "Allow", + "Action": ["s3:PutObject", "s3:GetObject"], "Resource": [format!("arn:aws:s3:::{}-sts-bucket/*", "${aws:username}")] } ] @@ -639,11 +606,22 @@ pub async fn test_aws_policy_variables_sts_impl_with_env( create_and_attach_policy(env, policy_name, test_user, policy_document).await?; - // Create S3 client for test user - let test_client = env.create_s3_client(test_user, test_password); - - // Add a small delay to allow policy to propagate - tokio::time::sleep(std::time::Duration::from_millis(500)).await; + let assumed = build_test_sts_client(&env.url, test_user, test_password, None, "policy-variable-sts") + .assume_role() + .role_arn("arn:aws:iam::123456789012:role/policy-variable") + .role_session_name("policy-variable-e2e") + .send() + .await?; + let credentials = assumed + .credentials() + .ok_or("AssumeRole response should contain temporary credentials")?; + let test_client = Client::from_conf(build_test_s3_config( + &env.url, + credentials.access_key_id(), + credentials.secret_access_key(), + Some(credentials.session_token()), + "policy-variable-sts-session", + )); // Test: User should be able to create bucket matching STS pattern info!("Test: User creating bucket matching STS pattern"); @@ -699,24 +677,18 @@ pub async fn test_aws_policy_variables_sts_impl_with_env( /// Test AWS policy variables with deny scenarios #[tokio::test(flavor = "multi_thread")] -#[ignore = "Starts a rustfs server; enable when running full E2E"] pub async fn test_aws_policy_variables_deny() -> Result<(), Box> { - test_aws_policy_variables_deny_impl().await -} - -/// Implementation function for deny policy variables test -pub async fn test_aws_policy_variables_deny_impl() -> Result<(), Box> { init_logging(); info!("Starting AWS policy variables deny test"); - let env = PolicyTestEnvironment::with_address("127.0.0.1:9000").await?; + let mut env = RustFSTestEnvironment::new().await?; + env.start_rustfs_server(vec![]).await?; test_aws_policy_variables_deny_impl_with_env(&env).await } -/// Implementation function for deny policy variables test with shared environment -pub async fn test_aws_policy_variables_deny_impl_with_env( - env: &PolicyTestEnvironment, +async fn test_aws_policy_variables_deny_impl_with_env( + env: &RustFSTestEnvironment, ) -> Result<(), Box> { // Create test user let test_user = "testuser5"; @@ -759,10 +731,7 @@ pub async fn test_aws_policy_variables_deny_impl_with_env( create_and_attach_policy(env, policy_name, test_user, policy_document).await?; // Create S3 client for test user - let test_client = env.create_s3_client(test_user, test_password); - - // Add a small delay to allow policy to propagate - tokio::time::sleep(std::time::Duration::from_millis(500)).await; + let test_client = env.create_s3_client_with_credentials(test_user, test_password); // Test 1: User should be able to create bucket matching username pattern info!("Test 1: User creating bucket matching username pattern"); @@ -775,12 +744,14 @@ pub async fn test_aws_policy_variables_deny_impl_with_env( // Test 2: User should NOT be able to create bucket with "private" in the name (deny rule) info!("Test 2: User attempting to create bucket with 'private' in name (should be denied)"); - let private_bucket_name = "private-test-bucket"; - let create_private_result = test_client.create_bucket().bucket(private_bucket_name).send().await; - if create_private_result.is_ok() { - cleanup().await; - return Err("User should NOT be able to create bucket with 'private' in name due to deny rule".into()); - } + let private_bucket_name = format!("{test_user}-private-bucket"); + let denied = test_client + .create_bucket() + .bucket(&private_bucket_name) + .send() + .await + .expect_err("the explicit deny must reject a matching bucket name"); + assert_eq!(denied.as_service_error().and_then(ProvideErrorMetadata::code), Some("AccessDenied")); // Cleanup info!("Cleaning up test resources"); diff --git a/crates/e2e_test/src/policy/test_env.rs b/crates/e2e_test/src/policy/test_env.rs deleted file mode 100644 index 6e7392a03..000000000 --- a/crates/e2e_test/src/policy/test_env.rs +++ /dev/null @@ -1,100 +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 -// -// 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. - -//! Custom test environment for policy variables tests -//! -//! This module provides a custom test environment that doesn't automatically -//! stop servers when destroyed, addressing the server stopping issue. - -use aws_sdk_s3::Client; -use aws_sdk_s3::config::{Config, Credentials, Region}; -use std::net::TcpStream; -use std::time::Duration; -use tokio::time::sleep; -use tracing::{info, warn}; - -// Default credentials -const DEFAULT_ACCESS_KEY: &str = "rustfsadmin"; -const DEFAULT_SECRET_KEY: &str = "rustfsadmin"; - -/// Custom test environment that doesn't automatically stop servers -pub struct PolicyTestEnvironment { - pub temp_dir: String, - pub address: String, - pub url: String, - pub access_key: String, - pub secret_key: String, -} - -impl PolicyTestEnvironment { - /// Create a new test environment with specific address - /// This environment won't stop any server when dropped - pub async fn with_address(address: &str) -> Result> { - let temp_dir = format!("/tmp/rustfs_policy_test_{}", uuid::Uuid::new_v4()); - tokio::fs::create_dir_all(&temp_dir).await?; - - let url = format!("http://{address}"); - - Ok(Self { - temp_dir, - address: address.to_string(), - url, - access_key: DEFAULT_ACCESS_KEY.to_string(), - secret_key: DEFAULT_SECRET_KEY.to_string(), - }) - } - - /// Create an AWS S3 client configured for this RustFS instance - pub fn create_s3_client(&self, access_key: &str, secret_key: &str) -> Client { - let credentials = Credentials::new(access_key, secret_key, None, None, "policy-test"); - let config = Config::builder() - .credentials_provider(credentials) - .region(Region::new("us-east-1")) - .endpoint_url(&self.url) - .force_path_style(true) - .behavior_version_latest() - .build(); - Client::from_conf(config) - } - - /// Wait for RustFS server to be ready by checking TCP connectivity - pub async fn wait_for_server_ready(&self) -> Result<(), Box> { - info!("Waiting for RustFS server to be ready on {}", self.address); - - for i in 0..30 { - if TcpStream::connect(&self.address).is_ok() { - info!("✅ RustFS server is ready after {} attempts", i + 1); - return Ok(()); - } - - if i == 29 { - return Err("RustFS server failed to become ready within 30 seconds".into()); - } - - sleep(Duration::from_secs(1)).await; - } - - Ok(()) - } -} - -// Implement Drop trait that doesn't stop servers -impl Drop for PolicyTestEnvironment { - fn drop(&mut self) { - // Clean up temp directory only, don't stop any server - if let Err(e) = std::fs::remove_dir_all(&self.temp_dir) { - warn!("Failed to clean up temp directory {}: {}", self.temp_dir, e); - } - } -} diff --git a/crates/e2e_test/src/policy/test_runner.rs b/crates/e2e_test/src/policy/test_runner.rs deleted file mode 100644 index 5402f72eb..000000000 --- a/crates/e2e_test/src/policy/test_runner.rs +++ /dev/null @@ -1,230 +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 -// -// 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. - -use crate::common::init_logging; -use crate::policy::test_env::PolicyTestEnvironment; -use std::time::Instant; -use tokio::time::{Duration, sleep}; -use tracing::{error, info}; - -/// Test case definition -#[derive(Debug, Clone)] -pub struct TestDefinition { - pub name: String, - pub is_critical: bool, -} - -impl TestDefinition { - pub fn new(name: impl Into, is_critical: bool) -> Self { - Self { - name: name.into(), - is_critical, - } - } -} - -/// Test result -#[derive(Debug, Clone)] -pub struct TestResult { - pub test_name: String, - pub success: bool, - pub error_message: Option, -} - -impl TestResult { - pub fn success(test_name: String) -> Self { - Self { - test_name, - success: true, - error_message: None, - } - } - - pub fn failure(test_name: String, error: String) -> Self { - Self { - test_name, - success: false, - error_message: Some(error), - } - } -} - -/// Test suite configuration -#[derive(Debug, Clone, Default)] -pub struct TestSuiteConfig { - pub include_critical_only: bool, -} - -/// Policy test suite -pub struct PolicyTestSuite { - tests: Vec, - config: TestSuiteConfig, -} - -impl PolicyTestSuite { - /// Create default test suite - pub fn new() -> Self { - let tests = vec![ - TestDefinition::new("test_aws_policy_variables_single_value", true), - TestDefinition::new("test_aws_policy_variables_multi_value", true), - TestDefinition::new("test_aws_policy_variables_concatenation", true), - TestDefinition::new("test_aws_policy_variables_nested", true), - TestDefinition::new("test_aws_policy_variables_deny", true), - TestDefinition::new("test_aws_policy_variables_sts", true), - ]; - - Self { - tests, - config: TestSuiteConfig::default(), - } - } - - /// Configure test suite - pub fn with_config(mut self, config: TestSuiteConfig) -> Self { - self.config = config; - self - } - - /// Run test suite - pub async fn run_test_suite(&self) -> Vec { - init_logging(); - info!("Starting Policy Variables test suite"); - - let start_time = Instant::now(); - let mut results = Vec::new(); - - // Create test environment - let env = match PolicyTestEnvironment::with_address("127.0.0.1:9000").await { - Ok(env) => env, - Err(e) => { - error!("Failed to create test environment: {}", e); - return vec![TestResult::failure("env_creation".into(), e.to_string())]; - } - }; - - // Wait for server to be ready - if env.wait_for_server_ready().await.is_err() { - error!("Server is not ready"); - return vec![TestResult::failure("server_check".into(), "Server not ready".into())]; - } - - // Filter tests - let tests_to_run: Vec<&TestDefinition> = self - .tests - .iter() - .filter(|test| !self.config.include_critical_only || test.is_critical) - .collect(); - - info!("Scheduled {} tests", tests_to_run.len()); - - // Run tests - for (i, test_def) in tests_to_run.iter().enumerate() { - info!("Running test {}/{}: {}", i + 1, tests_to_run.len(), test_def.name); - let test_start = Instant::now(); - - let result = self.run_single_test(test_def, &env).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())); - } - 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())); - } - } - - // Delay between tests to avoid resource conflicts - if i < tests_to_run.len() - 1 { - sleep(Duration::from_secs(2)).await; - } - } - - // Print summary - self.print_summary(&results, start_time.elapsed()); - - results - } - - /// Run a single test - async fn run_single_test( - &self, - test_def: &TestDefinition, - env: &PolicyTestEnvironment, - ) -> Result<(), Box> { - match test_def.name.as_str() { - "test_aws_policy_variables_single_value" => { - super::policy_variables_test::test_aws_policy_variables_single_value_impl_with_env(env).await - } - "test_aws_policy_variables_multi_value" => { - super::policy_variables_test::test_aws_policy_variables_multi_value_impl_with_env(env).await - } - "test_aws_policy_variables_concatenation" => { - super::policy_variables_test::test_aws_policy_variables_concatenation_impl_with_env(env).await - } - "test_aws_policy_variables_nested" => { - super::policy_variables_test::test_aws_policy_variables_nested_impl_with_env(env).await - } - "test_aws_policy_variables_deny" => { - super::policy_variables_test::test_aws_policy_variables_deny_impl_with_env(env).await - } - "test_aws_policy_variables_sts" => { - super::policy_variables_test::test_aws_policy_variables_sts_impl_with_env(env).await - } - _ => Err(format!("Test {} not implemented", test_def.name).into()), - } - } - - /// Print test summary - fn print_summary(&self, results: &[TestResult], total_duration: Duration) { - info!("=== Test Suite Summary ==="); - info!("Total duration: {:.2}s", total_duration.as_secs_f64()); - info!("Total tests: {}", results.len()); - - let passed = results.iter().filter(|r| r.success).count(); - let failed = results.len() - passed; - let success_rate = (passed as f64 / results.len() as f64) * 100.0; - - info!("Passed: {} | Failed: {}", passed, failed); - info!("Success rate: {:.1}%", success_rate); - - if failed > 0 { - error!("Failed tests:"); - for result in results.iter().filter(|r| !r.success) { - error!(" - {}: {}", result.test_name, result.error_message.as_ref().unwrap()); - } - } - } -} - -/// Test suite -#[tokio::test] -#[ignore = "Connects to existing rustfs server"] -async fn test_policy_critical_suite() -> Result<(), Box> { - let config = TestSuiteConfig { - include_critical_only: true, - }; - let suite = PolicyTestSuite::new().with_config(config); - let results = suite.run_test_suite().await; - - let failed = results.iter().filter(|r| !r.success).count(); - if failed > 0 { - return Err(format!("Critical tests failed: {failed} failures").into()); - } - - info!("All critical tests passed"); - Ok(()) -}