Merge pull request #297 from guojidan/improve-sql

Test: Add e2e_test case for sql && add script for e2e_test
This commit is contained in:
guojidan
2025-07-25 17:16:41 +08:00
committed by GitHub
5 changed files with 838 additions and 4 deletions
Generated
+113 -4
View File
@@ -672,6 +672,36 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "aws-config"
version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0baa720ebadea158c5bda642ac444a2af0cdf7bb66b46d1e4533de5d1f449d0"
dependencies = [
"aws-credential-types",
"aws-runtime",
"aws-sdk-sso",
"aws-sdk-ssooidc",
"aws-sdk-sts",
"aws-smithy-async",
"aws-smithy-http",
"aws-smithy-json",
"aws-smithy-runtime",
"aws-smithy-runtime-api",
"aws-smithy-types",
"aws-types",
"bytes",
"fastrand",
"hex",
"http 1.3.1",
"ring",
"time",
"tokio",
"tracing",
"url",
"zeroize",
]
[[package]]
name = "aws-credential-types"
version = "1.2.4"
@@ -734,9 +764,9 @@ dependencies = [
[[package]]
name = "aws-sdk-s3"
version = "1.98.0"
version = "1.99.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029e89cae7e628553643aecb3a3f054a0a0912ff0fd1f5d6a0b4fda421dce64b"
checksum = "b2d64d68c93000d5792b2a25fbeaafb90985fa80a1c8adfe93f24fb271296f5f"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -766,6 +796,73 @@ dependencies = [
"url",
]
[[package]]
name = "aws-sdk-sso"
version = "1.77.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18f2f37fea82468fe3f5a059542c05392ef680c4f7f00e0db02df8b6e5c7d0c6"
dependencies = [
"aws-credential-types",
"aws-runtime",
"aws-smithy-async",
"aws-smithy-http",
"aws-smithy-json",
"aws-smithy-runtime",
"aws-smithy-runtime-api",
"aws-smithy-types",
"aws-types",
"bytes",
"fastrand",
"http 0.2.12",
"regex-lite",
"tracing",
]
[[package]]
name = "aws-sdk-ssooidc"
version = "1.78.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecb4f6eada20e0193450cd48b12ed05e1e66baac86f39160191651b932f2b7d9"
dependencies = [
"aws-credential-types",
"aws-runtime",
"aws-smithy-async",
"aws-smithy-http",
"aws-smithy-json",
"aws-smithy-runtime",
"aws-smithy-runtime-api",
"aws-smithy-types",
"aws-types",
"bytes",
"fastrand",
"http 0.2.12",
"regex-lite",
"tracing",
]
[[package]]
name = "aws-sdk-sts"
version = "1.79.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "317377afba3498fca4948c5d32b399ef9a5ad35561a1e8a6f2ac7273dabf802d"
dependencies = [
"aws-credential-types",
"aws-runtime",
"aws-smithy-async",
"aws-smithy-http",
"aws-smithy-json",
"aws-smithy-query",
"aws-smithy-runtime",
"aws-smithy-runtime-api",
"aws-smithy-types",
"aws-smithy-xml",
"aws-types",
"fastrand",
"http 0.2.12",
"regex-lite",
"tracing",
]
[[package]]
name = "aws-sigv4"
version = "1.3.3"
@@ -904,6 +1001,16 @@ dependencies = [
"aws-smithy-runtime-api",
]
[[package]]
name = "aws-smithy-query"
version = "0.60.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2fbd61ceb3fe8a1cb7352e42689cec5335833cd9f94103a61e98f9bb61c64bb"
dependencies = [
"aws-smithy-types",
"urlencoding",
]
[[package]]
name = "aws-smithy-runtime"
version = "1.8.5"
@@ -982,9 +1089,9 @@ dependencies = [
[[package]]
name = "aws-types"
version = "1.3.7"
version = "1.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a322fec39e4df22777ed3ad8ea868ac2f94cd15e1a55f6ee8d8d6305057689a"
checksum = "b069d19bf01e46298eaedd7c6f283fe565a59263e53eebec945f3e6398f42390"
dependencies = [
"aws-credential-types",
"aws-smithy-async",
@@ -3466,6 +3573,8 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
name = "e2e_test"
version = "0.0.5"
dependencies = [
"aws-config",
"aws-sdk-s3",
"bytes",
"flatbuffers 25.2.10",
"futures",
+2
View File
@@ -39,3 +39,5 @@ rustfs-madmin.workspace = true
rustfs-filemeta.workspace = true
bytes.workspace = true
serial_test = "3.2.0"
aws-sdk-s3 = "1.99.0"
aws-config = "1.8.3"
+1
View File
@@ -14,3 +14,4 @@
mod lock;
mod node_interact_test;
mod sql;
+402
View File
@@ -0,0 +1,402 @@
#![cfg(test)]
// 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 aws_config::meta::region::RegionProviderChain;
use aws_sdk_s3::Client;
use aws_sdk_s3::config::{Credentials, Region};
use aws_sdk_s3::types::{
CsvInput, CsvOutput, ExpressionType, FileHeaderInfo, InputSerialization, JsonInput, JsonOutput, JsonType, OutputSerialization,
};
use bytes::Bytes;
use serial_test::serial;
use std::error::Error;
const ENDPOINT: &str = "http://localhost:9000";
const ACCESS_KEY: &str = "rustfsadmin";
const SECRET_KEY: &str = "rustfsadmin";
const BUCKET: &str = "test-sql-bucket";
const CSV_OBJECT: &str = "test-data.csv";
const JSON_OBJECT: &str = "test-data.json";
async fn create_aws_s3_client() -> Result<Client, Box<dyn Error>> {
let region_provider = RegionProviderChain::default_provider().or_else(Region::new("us-east-1"));
let shared_config = aws_config::defaults(aws_config::BehaviorVersion::latest())
.region(region_provider)
.credentials_provider(Credentials::new(ACCESS_KEY, SECRET_KEY, None, None, "static"))
.endpoint_url(ENDPOINT)
.load()
.await;
let client = Client::from_conf(
aws_sdk_s3::Config::from(&shared_config)
.to_builder()
.force_path_style(true) // Important for S3-compatible services
.build(),
);
Ok(client)
}
async fn setup_test_bucket(client: &Client) -> Result<(), Box<dyn Error>> {
match client.create_bucket().bucket(BUCKET).send().await {
Ok(_) => {}
Err(e) => {
let error_str = e.to_string();
if !error_str.contains("BucketAlreadyOwnedByYou") && !error_str.contains("BucketAlreadyExists") {
return Err(e.into());
}
}
}
Ok(())
}
async fn upload_test_csv(client: &Client) -> Result<(), Box<dyn Error>> {
let csv_data = "name,age,city\nAlice,30,New York\nBob,25,Los Angeles\nCharlie,35,Chicago\nDiana,28,Boston";
client
.put_object()
.bucket(BUCKET)
.key(CSV_OBJECT)
.body(Bytes::from(csv_data.as_bytes()).into())
.send()
.await?;
Ok(())
}
async fn upload_test_json(client: &Client) -> Result<(), Box<dyn Error>> {
let json_data = r#"{"name":"Alice","age":30,"city":"New York"}
{"name":"Bob","age":25,"city":"Los Angeles"}
{"name":"Charlie","age":35,"city":"Chicago"}
{"name":"Diana","age":28,"city":"Boston"}"#;
client
.put_object()
.bucket(BUCKET)
.key(JSON_OBJECT)
.body(Bytes::from(json_data.as_bytes()).into())
.send()
.await?;
Ok(())
}
async fn process_select_response(
mut event_stream: aws_sdk_s3::operation::select_object_content::SelectObjectContentOutput,
) -> Result<String, Box<dyn Error>> {
let mut total_data = Vec::new();
while let Ok(Some(event)) = event_stream.payload.recv().await {
match event {
aws_sdk_s3::types::SelectObjectContentEventStream::Records(records_event) => {
if let Some(payload) = records_event.payload {
let data = payload.into_inner();
total_data.extend_from_slice(&data);
}
}
aws_sdk_s3::types::SelectObjectContentEventStream::End(_) => {
break;
}
_ => {
// Handle other event types (Stats, Progress, Cont, etc.)
}
}
}
Ok(String::from_utf8(total_data)?)
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
#[serial]
#[ignore = "requires running RustFS server at localhost:9000"]
async fn test_select_object_content_csv_basic() -> Result<(), Box<dyn Error>> {
let client = create_aws_s3_client().await?;
setup_test_bucket(&client).await?;
upload_test_csv(&client).await?;
// Construct SelectObjectContent request - basic query
let sql = "SELECT * FROM S3Object WHERE age > 28";
let csv_input = CsvInput::builder().file_header_info(FileHeaderInfo::Use).build();
let input_serialization = InputSerialization::builder().csv(csv_input).build();
let csv_output = CsvOutput::builder().build();
let output_serialization = OutputSerialization::builder().csv(csv_output).build();
let response = client
.select_object_content()
.bucket(BUCKET)
.key(CSV_OBJECT)
.expression(sql)
.expression_type(ExpressionType::Sql)
.input_serialization(input_serialization)
.output_serialization(output_serialization)
.send()
.await?;
let result_str = process_select_response(response).await?;
println!("CSV Select result: {result_str}");
// Verify results contain records with age > 28
assert!(result_str.contains("Alice,30,New York"));
assert!(result_str.contains("Charlie,35,Chicago"));
assert!(!result_str.contains("Bob,25,Los Angeles"));
assert!(!result_str.contains("Diana,28,Boston"));
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
#[serial]
#[ignore = "requires running RustFS server at localhost:9000"]
async fn test_select_object_content_csv_aggregation() -> Result<(), Box<dyn Error>> {
let client = create_aws_s3_client().await?;
setup_test_bucket(&client).await?;
upload_test_csv(&client).await?;
// Construct aggregation query - use simpler approach
let sql = "SELECT name, age FROM S3Object WHERE age >= 25";
let csv_input = CsvInput::builder().file_header_info(FileHeaderInfo::Use).build();
let input_serialization = InputSerialization::builder().csv(csv_input).build();
let csv_output = CsvOutput::builder().build();
let output_serialization = OutputSerialization::builder().csv(csv_output).build();
let response = client
.select_object_content()
.bucket(BUCKET)
.key(CSV_OBJECT)
.expression(sql)
.expression_type(ExpressionType::Sql)
.input_serialization(input_serialization)
.output_serialization(output_serialization)
.send()
.await?;
let result_str = process_select_response(response).await?;
println!("CSV Aggregation result: {result_str}");
// Verify query results - should include records with age >= 25
assert!(result_str.contains("Alice"));
assert!(result_str.contains("Bob"));
assert!(result_str.contains("Charlie"));
assert!(result_str.contains("Diana"));
assert!(result_str.contains("30"));
assert!(result_str.contains("25"));
assert!(result_str.contains("35"));
assert!(result_str.contains("28"));
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
#[serial]
#[ignore = "requires running RustFS server at localhost:9000"]
async fn test_select_object_content_json_basic() -> Result<(), Box<dyn Error>> {
let client = create_aws_s3_client().await?;
setup_test_bucket(&client).await?;
upload_test_json(&client).await?;
// Construct JSON query
let sql = "SELECT s.name, s.age FROM S3Object s WHERE s.age > 28";
let json_input = JsonInput::builder().set_type(Some(JsonType::Document)).build();
let input_serialization = InputSerialization::builder().json(json_input).build();
let json_output = JsonOutput::builder().build();
let output_serialization = OutputSerialization::builder().json(json_output).build();
let response = client
.select_object_content()
.bucket(BUCKET)
.key(JSON_OBJECT)
.expression(sql)
.expression_type(ExpressionType::Sql)
.input_serialization(input_serialization)
.output_serialization(output_serialization)
.send()
.await?;
let result_str = process_select_response(response).await?;
println!("JSON Select result: {result_str}");
// Verify JSON query results
assert!(result_str.contains("Alice"));
assert!(result_str.contains("Charlie"));
assert!(result_str.contains("30"));
assert!(result_str.contains("35"));
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
#[serial]
#[ignore = "requires running RustFS server at localhost:9000"]
async fn test_select_object_content_csv_limit() -> Result<(), Box<dyn Error>> {
let client = create_aws_s3_client().await?;
setup_test_bucket(&client).await?;
upload_test_csv(&client).await?;
// Test LIMIT clause
let sql = "SELECT * FROM S3Object LIMIT 2";
let csv_input = CsvInput::builder().file_header_info(FileHeaderInfo::Use).build();
let input_serialization = InputSerialization::builder().csv(csv_input).build();
let csv_output = CsvOutput::builder().build();
let output_serialization = OutputSerialization::builder().csv(csv_output).build();
let response = client
.select_object_content()
.bucket(BUCKET)
.key(CSV_OBJECT)
.expression(sql)
.expression_type(ExpressionType::Sql)
.input_serialization(input_serialization)
.output_serialization(output_serialization)
.send()
.await?;
let result_str = process_select_response(response).await?;
println!("CSV Limit result: {result_str}");
// Verify only first 2 records are returned
let lines: Vec<&str> = result_str.lines().filter(|line| !line.trim().is_empty()).collect();
assert_eq!(lines.len(), 2, "Should return exactly 2 records");
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
#[serial]
#[ignore = "requires running RustFS server at localhost:9000"]
async fn test_select_object_content_csv_order_by() -> Result<(), Box<dyn Error>> {
let client = create_aws_s3_client().await?;
setup_test_bucket(&client).await?;
upload_test_csv(&client).await?;
// Test ORDER BY clause
let sql = "SELECT name, age FROM S3Object ORDER BY age DESC LIMIT 2";
let csv_input = CsvInput::builder().file_header_info(FileHeaderInfo::Use).build();
let input_serialization = InputSerialization::builder().csv(csv_input).build();
let csv_output = CsvOutput::builder().build();
let output_serialization = OutputSerialization::builder().csv(csv_output).build();
let response = client
.select_object_content()
.bucket(BUCKET)
.key(CSV_OBJECT)
.expression(sql)
.expression_type(ExpressionType::Sql)
.input_serialization(input_serialization)
.output_serialization(output_serialization)
.send()
.await?;
let result_str = process_select_response(response).await?;
println!("CSV Order By result: {result_str}");
// Verify ordered by age descending
let lines: Vec<&str> = result_str.lines().filter(|line| !line.trim().is_empty()).collect();
assert!(lines.len() >= 2, "Should return at least 2 records");
// Check if contains highest age records
assert!(result_str.contains("Charlie,35"));
assert!(result_str.contains("Alice,30"));
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
#[serial]
#[ignore = "requires running RustFS server at localhost:9000"]
async fn test_select_object_content_error_handling() -> Result<(), Box<dyn Error>> {
let client = create_aws_s3_client().await?;
setup_test_bucket(&client).await?;
upload_test_csv(&client).await?;
// Test invalid SQL query
let sql = "SELECT * FROM S3Object WHERE invalid_column > 10";
let csv_input = CsvInput::builder().file_header_info(FileHeaderInfo::Use).build();
let input_serialization = InputSerialization::builder().csv(csv_input).build();
let csv_output = CsvOutput::builder().build();
let output_serialization = OutputSerialization::builder().csv(csv_output).build();
// This query should fail because invalid_column doesn't exist
let result = client
.select_object_content()
.bucket(BUCKET)
.key(CSV_OBJECT)
.expression(sql)
.expression_type(ExpressionType::Sql)
.input_serialization(input_serialization)
.output_serialization(output_serialization)
.send()
.await;
// Verify query fails (expected behavior)
assert!(result.is_err(), "Query with invalid column should fail");
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
#[serial]
#[ignore = "requires running RustFS server at localhost:9000"]
async fn test_select_object_content_nonexistent_object() -> Result<(), Box<dyn Error>> {
let client = create_aws_s3_client().await?;
setup_test_bucket(&client).await?;
// Test query on nonexistent object
let sql = "SELECT * FROM S3Object";
let csv_input = CsvInput::builder().file_header_info(FileHeaderInfo::Use).build();
let input_serialization = InputSerialization::builder().csv(csv_input).build();
let csv_output = CsvOutput::builder().build();
let output_serialization = OutputSerialization::builder().csv(csv_output).build();
let result = client
.select_object_content()
.bucket(BUCKET)
.key("nonexistent.csv")
.expression(sql)
.expression_type(ExpressionType::Sql)
.input_serialization(input_serialization)
.output_serialization(output_serialization)
.send()
.await;
// Verify query fails (expected behavior)
assert!(result.is_err(), "Query on nonexistent object should fail");
Ok(())
}
+320
View File
@@ -0,0 +1,320 @@
#!/bin/bash
# E2E Test Runner Script
# Automatically starts RustFS instance, runs tests, and cleans up
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Default values
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
TARGET_DIR="$PROJECT_ROOT/target/debug"
RUSTFS_BINARY="$TARGET_DIR/rustfs"
DATA_DIR="$TARGET_DIR/rustfs_test_data"
RUSTFS_PID=""
TEST_FILTER=""
TEST_TYPE="all"
# Function to print colored output
print_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Function to show usage
show_usage() {
cat << EOF
Usage: $0 [OPTIONS]
Options:
-h, --help Show this help message
-t, --test <pattern> Run specific test(s) matching pattern
-f, --file <file> Run all tests in specific file (e.g., sql, basic)
-a, --all Run all e2e tests (default)
Examples:
$0 # Run all e2e tests
$0 -t test_select_object_content_csv_basic # Run specific test
$0 -f sql # Run all SQL tests
$0 -f reliant::sql # Run all tests in sql.rs file
EOF
}
# Function to cleanup on exit
cleanup() {
print_info "Cleaning up..."
# Stop RustFS if running
if [ ! -z "$RUSTFS_PID" ] && kill -0 "$RUSTFS_PID" 2>/dev/null; then
print_info "Stopping RustFS (PID: $RUSTFS_PID)..."
kill "$RUSTFS_PID" 2>/dev/null || true
sleep 2
# Force kill if still running
if kill -0 "$RUSTFS_PID" 2>/dev/null; then
print_warning "Force killing RustFS..."
kill -9 "$RUSTFS_PID" 2>/dev/null || true
fi
fi
# Clean up data directory
if [ -d "$DATA_DIR" ]; then
print_info "Removing test data directory: $DATA_DIR"
rm -rf "$DATA_DIR"
fi
print_success "Cleanup completed"
}
# Set trap to cleanup on exit
trap cleanup EXIT INT TERM
# Function to build RustFS
build_rustfs() {
print_info "Building RustFS..."
cd "$PROJECT_ROOT"
if ! cargo build --bin rustfs; then
print_error "Failed to build RustFS"
exit 1
fi
if [ ! -f "$RUSTFS_BINARY" ]; then
print_error "RustFS binary not found at: $RUSTFS_BINARY"
exit 1
fi
print_success "RustFS built successfully"
}
# Function to check if required tools are available
check_dependencies() {
local missing_tools=()
if ! command -v curl >/dev/null 2>&1; then
missing_tools+=("curl")
fi
if ! command -v cargo >/dev/null 2>&1; then
missing_tools+=("cargo")
fi
if [ ${#missing_tools[@]} -gt 0 ]; then
print_error "Missing required tools: ${missing_tools[*]}"
print_error "Please install the missing tools and try again"
exit 1
fi
}
# Function to start RustFS
start_rustfs() {
print_info "Starting RustFS instance..."
# Create data directory and logs directory
mkdir -p "$DATA_DIR"
mkdir -p "$TARGET_DIR/logs"
# Start RustFS in background with environment variables
cd "$TARGET_DIR"
RUSTFS_ACCESS_KEY=rustfsadmin RUSTFS_SECRET_KEY=rustfsadmin \
RUSTFS_OBS_LOG_DIRECTORY="$TARGET_DIR/logs" \
./rustfs --address :9000 "$DATA_DIR" > rustfs.log 2>&1 &
RUSTFS_PID=$!
print_info "RustFS started with PID: $RUSTFS_PID"
print_info "Data directory: $DATA_DIR"
print_info "Log file: $TARGET_DIR/rustfs.log"
print_info "RustFS logs directory: $TARGET_DIR/logs"
# Wait for RustFS to be ready
print_info "Waiting for RustFS to be ready..."
local max_attempts=15 # Reduced from 30 to 15 seconds
local attempt=0
while [ $attempt -lt $max_attempts ]; do
# Check if process is still running first (faster check)
if ! kill -0 "$RUSTFS_PID" 2>/dev/null; then
print_error "RustFS process died unexpectedly"
print_error "Log output:"
cat "$TARGET_DIR/rustfs.log" || true
exit 1
fi
# Try simple HTTP connection first (most reliable)
if curl -s --noproxy localhost --connect-timeout 2 --max-time 3 "http://localhost:9000/" >/dev/null 2>&1; then
print_success "RustFS is ready!"
return 0
fi
# Try health endpoint if available
if curl -s --noproxy localhost --connect-timeout 2 --max-time 3 "http://localhost:9000/health" >/dev/null 2>&1; then
print_success "RustFS is ready!"
return 0
fi
# Try port connectivity check (faster than HTTP)
if nc -z localhost 9000 2>/dev/null; then
print_info "Port 9000 is open, verifying HTTP response..."
if curl -s --noproxy localhost --connect-timeout 1 --max-time 2 "http://localhost:9000/" >/dev/null 2>&1; then
print_success "RustFS is ready!"
return 0
fi
fi
sleep 1
attempt=$((attempt + 1))
echo -n "."
done
echo
print_warning "RustFS health check failed within $max_attempts seconds"
print_info "Checking if RustFS process is still running..."
if kill -0 "$RUSTFS_PID" 2>/dev/null; then
print_info "RustFS process is still running (PID: $RUSTFS_PID)"
print_info "Trying final connection attempts..."
# Quick final attempts with shorter timeouts
for i in 1 2 3; do
if curl -s --noproxy localhost --connect-timeout 1 --max-time 2 "http://localhost:9000/" >/dev/null 2>&1; then
print_success "RustFS is now ready!"
return 0
fi
if nc -z localhost 9000 2>/dev/null; then
print_info "Port 9000 is accessible, continuing with tests..."
return 0
fi
sleep 1
done
print_warning "RustFS may be slow to respond, but process is running"
print_info "Continuing with tests anyway..."
return 0
else
print_error "RustFS process has died"
print_error "Log output:"
cat "$TARGET_DIR/rustfs.log" || true
return 1
fi
}
# Function to run tests
run_tests() {
print_info "Running e2e tests..."
cd "$PROJECT_ROOT"
local test_cmd="cargo test --package e2e_test --lib"
case "$TEST_TYPE" in
"specific")
test_cmd="$test_cmd -- $TEST_FILTER --exact --show-output --ignored"
print_info "Running specific test: $TEST_FILTER"
;;
"file")
test_cmd="$test_cmd -- $TEST_FILTER --show-output --ignored"
print_info "Running tests in file/module: $TEST_FILTER"
;;
"all")
test_cmd="$test_cmd -- --show-output --ignored"
print_info "Running all e2e tests"
;;
esac
print_info "Test command: $test_cmd"
if eval "$test_cmd"; then
print_success "All tests passed!"
return 0
else
print_error "Some tests failed!"
return 1
fi
}
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
show_usage
exit 0
;;
-t|--test)
TEST_FILTER="$2"
TEST_TYPE="specific"
shift 2
;;
-f|--file)
TEST_FILTER="$2"
TEST_TYPE="file"
shift 2
;;
-a|--all)
TEST_TYPE="all"
shift
;;
*)
print_error "Unknown option: $1"
show_usage
exit 1
;;
esac
done
# Main execution
main() {
print_info "Starting E2E Test Runner"
print_info "Project root: $PROJECT_ROOT"
print_info "Target directory: $TARGET_DIR"
# Check dependencies
check_dependencies
# Build RustFS
build_rustfs
# Start RustFS
if ! start_rustfs; then
print_error "Failed to start RustFS properly"
print_info "Checking if we can still run tests..."
if [ ! -z "$RUSTFS_PID" ] && kill -0 "$RUSTFS_PID" 2>/dev/null; then
print_info "RustFS process is still running, attempting to continue..."
else
print_error "RustFS is not running, cannot proceed with tests"
exit 1
fi
fi
# Run tests
local test_result=0
run_tests || test_result=$?
# Cleanup will be handled by trap
if [ $test_result -eq 0 ]; then
print_success "E2E tests completed successfully!"
exit 0
else
print_error "E2E tests failed!"
exit 1
fi
}
# Run main function
main "$@"