mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-22 04:16:38 +00:00
ci(e2e): stabilize full-gate tooling (#5805)
This commit is contained in:
@@ -16,91 +16,17 @@
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::common::{RustFSTestEnvironment, init_logging};
|
||||
use crate::common::{RustFSTestEnvironment, init_logging, signed_s3_request};
|
||||
use aws_sdk_s3::error::ProvideErrorMetadata;
|
||||
use aws_sdk_s3::types::{
|
||||
AccelerateConfiguration, BucketAccelerateStatus, BucketLoggingStatus, IndexDocument, LoggingEnabled, Payer,
|
||||
RequestPaymentConfiguration, WebsiteConfiguration,
|
||||
};
|
||||
use http::Method;
|
||||
use http::header::CONTENT_TYPE;
|
||||
use serial_test::serial;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use tracing::info;
|
||||
|
||||
fn awscurl_binary_path() -> PathBuf {
|
||||
std::env::var_os("AWSCURL_PATH")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| PathBuf::from("awscurl"))
|
||||
}
|
||||
|
||||
fn awscurl_available() -> bool {
|
||||
Command::new(awscurl_binary_path()).arg("--version").output().is_ok()
|
||||
}
|
||||
|
||||
fn execute_s3_awscurl(
|
||||
method: &str,
|
||||
url: &str,
|
||||
access_key: &str,
|
||||
secret_key: &str,
|
||||
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let output = Command::new(awscurl_binary_path())
|
||||
.args([
|
||||
"--service",
|
||||
"s3",
|
||||
"--region",
|
||||
"us-east-1",
|
||||
"--access_key",
|
||||
access_key,
|
||||
"--secret_key",
|
||||
secret_key,
|
||||
"-i",
|
||||
"-X",
|
||||
method,
|
||||
url,
|
||||
])
|
||||
.output()?;
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
return Err(format!("awscurl failed: stderr='{stderr}', stdout='{stdout}'").into());
|
||||
}
|
||||
Ok(String::from_utf8_lossy(&output.stdout).to_string())
|
||||
}
|
||||
|
||||
fn parse_status(raw: &str) -> Option<u16> {
|
||||
raw.lines()
|
||||
.filter_map(|line| {
|
||||
if line.starts_with("HTTP/") {
|
||||
line.split_whitespace().nth(1)?.parse::<u16>().ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next_back()
|
||||
}
|
||||
|
||||
fn parse_body(raw: &str) -> String {
|
||||
if let Some(pos) = raw.rfind("\r\n\r\n") {
|
||||
return raw[pos + 4..].to_string();
|
||||
}
|
||||
if let Some(pos) = raw.rfind("\n\n") {
|
||||
return raw[pos + 2..].to_string();
|
||||
}
|
||||
String::new()
|
||||
}
|
||||
|
||||
fn parse_headers(raw: &str) -> String {
|
||||
let start = raw.rfind("HTTP/").unwrap_or(0);
|
||||
let tail = &raw[start..];
|
||||
if let Some(pos) = tail.find("\r\n\r\n") {
|
||||
return tail[..pos].to_string();
|
||||
}
|
||||
if let Some(pos) = tail.find("\n\n") {
|
||||
return tail[..pos].to_string();
|
||||
}
|
||||
tail.to_string()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_dummy_bucket_compatibility_endpoints() {
|
||||
@@ -470,10 +396,6 @@ mod tests {
|
||||
async fn test_dummy_bucket_endpoints_http_contracts() {
|
||||
init_logging();
|
||||
info!("Starting test: dummy-compat bucket API HTTP contracts");
|
||||
if !awscurl_available() {
|
||||
info!("Skipping test_dummy_bucket_endpoints_http_contracts: awscurl binary not found");
|
||||
return;
|
||||
}
|
||||
|
||||
let mut env = RustFSTestEnvironment::new().await.expect("Failed to create test environment");
|
||||
env.start_rustfs_server(vec![]).await.expect("Failed to start RustFS");
|
||||
@@ -488,56 +410,112 @@ mod tests {
|
||||
.await
|
||||
.expect("Failed to create bucket");
|
||||
|
||||
let logging_raw = execute_s3_awscurl("GET", &format!("{}/{bucket}?logging=", env.url), &env.access_key, &env.secret_key)
|
||||
.expect("GetBucketLogging HTTP request failed");
|
||||
assert_eq!(parse_status(&logging_raw), Some(200), "GetBucketLogging should return 200");
|
||||
let logging_body = parse_body(&logging_raw);
|
||||
let logging_response = signed_s3_request(
|
||||
Method::GET,
|
||||
&format!("{}/{bucket}?logging=", env.url),
|
||||
None,
|
||||
None,
|
||||
&env.access_key,
|
||||
&env.secret_key,
|
||||
)
|
||||
.await
|
||||
.expect("GetBucketLogging HTTP request failed");
|
||||
assert_eq!(logging_response.status(), 200, "GetBucketLogging should return 200");
|
||||
let logging_body = logging_response
|
||||
.text()
|
||||
.await
|
||||
.expect("Failed to read GetBucketLogging response body");
|
||||
assert!(
|
||||
logging_body.contains("<BucketLoggingStatus"),
|
||||
"GetBucketLogging response should contain BucketLoggingStatus XML, got: {logging_body}"
|
||||
);
|
||||
|
||||
let accel_raw = execute_s3_awscurl("GET", &format!("{}/{bucket}?accelerate=", env.url), &env.access_key, &env.secret_key)
|
||||
.expect("GetBucketAccelerateConfiguration HTTP request failed");
|
||||
assert_eq!(parse_status(&accel_raw), Some(200), "GetBucketAccelerateConfiguration should return 200");
|
||||
let accel_body = parse_body(&accel_raw);
|
||||
let accel_response = signed_s3_request(
|
||||
Method::GET,
|
||||
&format!("{}/{bucket}?accelerate=", env.url),
|
||||
None,
|
||||
None,
|
||||
&env.access_key,
|
||||
&env.secret_key,
|
||||
)
|
||||
.await
|
||||
.expect("GetBucketAccelerateConfiguration HTTP request failed");
|
||||
assert_eq!(accel_response.status(), 200, "GetBucketAccelerateConfiguration should return 200");
|
||||
let accel_body = accel_response
|
||||
.text()
|
||||
.await
|
||||
.expect("Failed to read GetBucketAccelerateConfiguration response body");
|
||||
assert!(
|
||||
accel_body.contains("<AccelerateConfiguration"),
|
||||
"GetBucketAccelerateConfiguration response should contain AccelerateConfiguration XML, got: {accel_body}"
|
||||
);
|
||||
|
||||
let payment_raw =
|
||||
execute_s3_awscurl("GET", &format!("{}/{bucket}?requestPayment=", env.url), &env.access_key, &env.secret_key)
|
||||
.expect("GetBucketRequestPayment HTTP request failed");
|
||||
assert_eq!(parse_status(&payment_raw), Some(200), "GetBucketRequestPayment should return 200");
|
||||
let payment_body = parse_body(&payment_raw);
|
||||
let payment_response = signed_s3_request(
|
||||
Method::GET,
|
||||
&format!("{}/{bucket}?requestPayment=", env.url),
|
||||
None,
|
||||
None,
|
||||
&env.access_key,
|
||||
&env.secret_key,
|
||||
)
|
||||
.await
|
||||
.expect("GetBucketRequestPayment HTTP request failed");
|
||||
assert_eq!(payment_response.status(), 200, "GetBucketRequestPayment should return 200");
|
||||
let payment_body = payment_response
|
||||
.text()
|
||||
.await
|
||||
.expect("Failed to read GetBucketRequestPayment response body");
|
||||
assert!(
|
||||
payment_body.contains("<Payer>BucketOwner</Payer>"),
|
||||
"GetBucketRequestPayment should return BucketOwner payer, got: {payment_body}"
|
||||
);
|
||||
|
||||
let website_raw = execute_s3_awscurl("GET", &format!("{}/{bucket}?website=", env.url), &env.access_key, &env.secret_key)
|
||||
.expect("GetBucketWebsite HTTP request failed");
|
||||
let website_response = signed_s3_request(
|
||||
Method::GET,
|
||||
&format!("{}/{bucket}?website=", env.url),
|
||||
None,
|
||||
None,
|
||||
&env.access_key,
|
||||
&env.secret_key,
|
||||
)
|
||||
.await
|
||||
.expect("GetBucketWebsite HTTP request failed");
|
||||
assert_eq!(
|
||||
parse_status(&website_raw),
|
||||
Some(404),
|
||||
website_response.status(),
|
||||
404,
|
||||
"GetBucketWebsite should return 404 when website config is absent"
|
||||
);
|
||||
let website_content_type = parse_headers(&website_raw).to_ascii_lowercase();
|
||||
let website_content_type = website_response
|
||||
.headers()
|
||||
.get(CONTENT_TYPE)
|
||||
.expect("GetBucketWebsite response should include Content-Type")
|
||||
.to_str()
|
||||
.expect("GetBucketWebsite Content-Type should be valid ASCII")
|
||||
.to_ascii_lowercase();
|
||||
assert!(
|
||||
website_content_type.contains("content-type:") && website_content_type.contains("xml"),
|
||||
website_content_type.contains("xml"),
|
||||
"GetBucketWebsite error response should be XML, got content-type: {website_content_type}"
|
||||
);
|
||||
let website_body = parse_body(&website_raw);
|
||||
let website_body = website_response
|
||||
.text()
|
||||
.await
|
||||
.expect("Failed to read GetBucketWebsite response body");
|
||||
assert!(
|
||||
website_body.contains("<Code>NoSuchWebsiteConfiguration</Code>"),
|
||||
"GetBucketWebsite should return NoSuchWebsiteConfiguration code, got: {website_body}"
|
||||
);
|
||||
|
||||
let delete_raw =
|
||||
execute_s3_awscurl("DELETE", &format!("{}/{bucket}?website=", env.url), &env.access_key, &env.secret_key)
|
||||
.expect("DeleteBucketWebsite HTTP request failed");
|
||||
assert_eq!(parse_status(&delete_raw), Some(204), "DeleteBucketWebsite should return 204");
|
||||
let delete_response = signed_s3_request(
|
||||
Method::DELETE,
|
||||
&format!("{}/{bucket}?website=", env.url),
|
||||
None,
|
||||
None,
|
||||
&env.access_key,
|
||||
&env.secret_key,
|
||||
)
|
||||
.await
|
||||
.expect("DeleteBucketWebsite HTTP request failed");
|
||||
assert_eq!(delete_response.status(), 204, "DeleteBucketWebsite should return 204");
|
||||
|
||||
env.stop_server();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user