mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 16:28:15 +00:00
Merge pull request #432 from rustfs/feat/improve-rustfs-storage-tests
feat: add comprehensive tests for rustfs storage module
This commit is contained in:
@@ -87,3 +87,428 @@ pub fn to_s3_error(err: Error) -> S3Error {
|
||||
|
||||
S3Error::with_message(S3ErrorCode::InternalError, format!(" ec err {}", err))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use s3s::S3ErrorCode;
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_not_implemented() {
|
||||
let storage_err = StorageError::NotImplemented;
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::NotImplemented);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_invalid_argument() {
|
||||
let storage_err = StorageError::InvalidArgument(
|
||||
"test-bucket".to_string(),
|
||||
"test-object".to_string(),
|
||||
"test-version".to_string(),
|
||||
);
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::InvalidArgument);
|
||||
assert!(s3_err.message().unwrap().contains("Invalid arguments provided"));
|
||||
assert!(s3_err.message().unwrap().contains("test-bucket"));
|
||||
assert!(s3_err.message().unwrap().contains("test-object"));
|
||||
assert!(s3_err.message().unwrap().contains("test-version"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_method_not_allowed() {
|
||||
let storage_err = StorageError::MethodNotAllowed;
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::MethodNotAllowed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_bucket_not_found() {
|
||||
let storage_err = StorageError::BucketNotFound("test-bucket".to_string());
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::NoSuchBucket);
|
||||
assert!(s3_err.message().unwrap().contains("bucket not found"));
|
||||
assert!(s3_err.message().unwrap().contains("test-bucket"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_bucket_not_empty() {
|
||||
let storage_err = StorageError::BucketNotEmpty("test-bucket".to_string());
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::BucketNotEmpty);
|
||||
assert!(s3_err.message().unwrap().contains("bucket not empty"));
|
||||
assert!(s3_err.message().unwrap().contains("test-bucket"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_bucket_name_invalid() {
|
||||
let storage_err = StorageError::BucketNameInvalid("invalid-bucket-name".to_string());
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::InvalidBucketName);
|
||||
assert!(s3_err.message().unwrap().contains("invalid bucket name"));
|
||||
assert!(s3_err.message().unwrap().contains("invalid-bucket-name"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_object_name_invalid() {
|
||||
let storage_err = StorageError::ObjectNameInvalid(
|
||||
"test-bucket".to_string(),
|
||||
"invalid-object".to_string(),
|
||||
);
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::InvalidArgument);
|
||||
assert!(s3_err.message().unwrap().contains("invalid object name"));
|
||||
assert!(s3_err.message().unwrap().contains("test-bucket"));
|
||||
assert!(s3_err.message().unwrap().contains("invalid-object"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_bucket_exists() {
|
||||
let storage_err = StorageError::BucketExists("existing-bucket".to_string());
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::BucketAlreadyExists);
|
||||
assert!(s3_err.message().unwrap().contains("existing-bucket"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_storage_full() {
|
||||
let storage_err = StorageError::StorageFull;
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::ServiceUnavailable);
|
||||
assert!(s3_err.message().unwrap().contains("Storage reached its minimum free drive threshold"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_slow_down() {
|
||||
let storage_err = StorageError::SlowDown;
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::SlowDown);
|
||||
assert!(s3_err.message().unwrap().contains("Please reduce your request rate"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_prefix_access_denied() {
|
||||
let storage_err = StorageError::PrefixAccessDenied(
|
||||
"test-bucket".to_string(),
|
||||
"test-prefix".to_string(),
|
||||
);
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::AccessDenied);
|
||||
assert!(s3_err.message().unwrap().contains("PrefixAccessDenied"));
|
||||
assert!(s3_err.message().unwrap().contains("test-bucket"));
|
||||
assert!(s3_err.message().unwrap().contains("test-prefix"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_invalid_upload_id_key_combination() {
|
||||
let storage_err = StorageError::InvalidUploadIDKeyCombination(
|
||||
"test-bucket".to_string(),
|
||||
"test-object".to_string(),
|
||||
);
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::InvalidArgument);
|
||||
assert!(s3_err.message().unwrap().contains("Invalid UploadID KeyCombination"));
|
||||
assert!(s3_err.message().unwrap().contains("test-bucket"));
|
||||
assert!(s3_err.message().unwrap().contains("test-object"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_malformed_upload_id() {
|
||||
let storage_err = StorageError::MalformedUploadID("malformed-id".to_string());
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::InvalidArgument);
|
||||
assert!(s3_err.message().unwrap().contains("Malformed UploadID"));
|
||||
assert!(s3_err.message().unwrap().contains("malformed-id"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_object_name_too_long() {
|
||||
let storage_err = StorageError::ObjectNameTooLong(
|
||||
"test-bucket".to_string(),
|
||||
"very-long-object-name".to_string(),
|
||||
);
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::InvalidArgument);
|
||||
assert!(s3_err.message().unwrap().contains("Object name too long"));
|
||||
assert!(s3_err.message().unwrap().contains("test-bucket"));
|
||||
assert!(s3_err.message().unwrap().contains("very-long-object-name"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_object_name_prefix_as_slash() {
|
||||
let storage_err = StorageError::ObjectNamePrefixAsSlash(
|
||||
"test-bucket".to_string(),
|
||||
"/invalid-object".to_string(),
|
||||
);
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::InvalidArgument);
|
||||
assert!(s3_err.message().unwrap().contains("Object name contains forward slash as prefix"));
|
||||
assert!(s3_err.message().unwrap().contains("test-bucket"));
|
||||
assert!(s3_err.message().unwrap().contains("/invalid-object"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_object_not_found() {
|
||||
let storage_err = StorageError::ObjectNotFound(
|
||||
"test-bucket".to_string(),
|
||||
"missing-object".to_string(),
|
||||
);
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::NoSuchKey);
|
||||
assert!(s3_err.message().unwrap().contains("test-bucket"));
|
||||
assert!(s3_err.message().unwrap().contains("missing-object"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_version_not_found() {
|
||||
let storage_err = StorageError::VersionNotFound(
|
||||
"test-bucket".to_string(),
|
||||
"test-object".to_string(),
|
||||
"missing-version".to_string(),
|
||||
);
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::NoSuchVersion);
|
||||
assert!(s3_err.message().unwrap().contains("test-bucket"));
|
||||
assert!(s3_err.message().unwrap().contains("test-object"));
|
||||
assert!(s3_err.message().unwrap().contains("missing-version"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_invalid_upload_id() {
|
||||
let storage_err = StorageError::InvalidUploadID(
|
||||
"test-bucket".to_string(),
|
||||
"test-object".to_string(),
|
||||
"invalid-upload-id".to_string(),
|
||||
);
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::InvalidPart);
|
||||
assert!(s3_err.message().unwrap().contains("Invalid upload id"));
|
||||
assert!(s3_err.message().unwrap().contains("test-bucket"));
|
||||
assert!(s3_err.message().unwrap().contains("test-object"));
|
||||
assert!(s3_err.message().unwrap().contains("invalid-upload-id"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_invalid_version_id() {
|
||||
let storage_err = StorageError::InvalidVersionID(
|
||||
"test-bucket".to_string(),
|
||||
"test-object".to_string(),
|
||||
"invalid-version-id".to_string(),
|
||||
);
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::InvalidArgument);
|
||||
assert!(s3_err.message().unwrap().contains("Invalid version id"));
|
||||
assert!(s3_err.message().unwrap().contains("test-bucket"));
|
||||
assert!(s3_err.message().unwrap().contains("test-object"));
|
||||
assert!(s3_err.message().unwrap().contains("invalid-version-id"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_data_movement_overwrite_err() {
|
||||
let storage_err = StorageError::DataMovementOverwriteErr(
|
||||
"test-bucket".to_string(),
|
||||
"test-object".to_string(),
|
||||
"test-version".to_string(),
|
||||
);
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::InvalidArgument);
|
||||
assert!(s3_err.message().unwrap().contains("invalid data movement operation"));
|
||||
assert!(s3_err.message().unwrap().contains("source and destination pool are the same"));
|
||||
assert!(s3_err.message().unwrap().contains("test-bucket"));
|
||||
assert!(s3_err.message().unwrap().contains("test-object"));
|
||||
assert!(s3_err.message().unwrap().contains("test-version"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_object_exists_as_directory() {
|
||||
let storage_err = StorageError::ObjectExistsAsDirectory(
|
||||
"test-bucket".to_string(),
|
||||
"directory-object".to_string(),
|
||||
);
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::InvalidArgument);
|
||||
assert!(s3_err.message().unwrap().contains("Object exists on"));
|
||||
assert!(s3_err.message().unwrap().contains("as directory"));
|
||||
assert!(s3_err.message().unwrap().contains("test-bucket"));
|
||||
assert!(s3_err.message().unwrap().contains("directory-object"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_insufficient_read_quorum() {
|
||||
let storage_err = StorageError::InsufficientReadQuorum;
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::SlowDown);
|
||||
assert!(s3_err.message().unwrap().contains("Storage resources are insufficient for the read operation"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_insufficient_write_quorum() {
|
||||
let storage_err = StorageError::InsufficientWriteQuorum;
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::SlowDown);
|
||||
assert!(s3_err.message().unwrap().contains("Storage resources are insufficient for the write operation"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_decommission_not_started() {
|
||||
let storage_err = StorageError::DecommissionNotStarted;
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::InvalidArgument);
|
||||
assert!(s3_err.message().unwrap().contains("Decommission Not Started"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_decommission_already_running() {
|
||||
let storage_err = StorageError::DecommissionAlreadyRunning;
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::InternalError);
|
||||
assert!(s3_err.message().unwrap().contains("Decommission already running"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_volume_not_found() {
|
||||
let storage_err = StorageError::VolumeNotFound("test-volume".to_string());
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::NoSuchBucket);
|
||||
assert!(s3_err.message().unwrap().contains("bucket not found"));
|
||||
assert!(s3_err.message().unwrap().contains("test-volume"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_invalid_part() {
|
||||
let storage_err = StorageError::InvalidPart(
|
||||
1,
|
||||
"expected-part".to_string(),
|
||||
"got-part".to_string(),
|
||||
);
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::InvalidPart);
|
||||
assert!(s3_err.message().unwrap().contains("Specified part could not be found"));
|
||||
assert!(s3_err.message().unwrap().contains("PartNumber"));
|
||||
assert!(s3_err.message().unwrap().contains("expected-part"));
|
||||
assert!(s3_err.message().unwrap().contains("got-part"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_done_for_now() {
|
||||
let storage_err = StorageError::DoneForNow;
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::InternalError);
|
||||
assert!(s3_err.message().unwrap().contains("DoneForNow"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_non_storage_error() {
|
||||
// Test with a non-StorageError
|
||||
let err = Error::from_string("Generic error message".to_string());
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::InternalError);
|
||||
assert!(s3_err.message().unwrap().contains("ec err"));
|
||||
assert!(s3_err.message().unwrap().contains("Generic error message"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_with_unicode_strings() {
|
||||
let storage_err = StorageError::BucketNotFound("测试桶".to_string());
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::NoSuchBucket);
|
||||
assert!(s3_err.message().unwrap().contains("bucket not found"));
|
||||
assert!(s3_err.message().unwrap().contains("测试桶"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_with_special_characters() {
|
||||
let storage_err = StorageError::ObjectNameInvalid(
|
||||
"bucket-with-@#$%".to_string(),
|
||||
"object-with-!@#$%^&*()".to_string(),
|
||||
);
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::InvalidArgument);
|
||||
assert!(s3_err.message().unwrap().contains("invalid object name"));
|
||||
assert!(s3_err.message().unwrap().contains("bucket-with-@#$%"));
|
||||
assert!(s3_err.message().unwrap().contains("object-with-!@#$%^&*()"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_with_empty_strings() {
|
||||
let storage_err = StorageError::BucketNotFound("".to_string());
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::NoSuchBucket);
|
||||
assert!(s3_err.message().unwrap().contains("bucket not found"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_s3_error_with_very_long_strings() {
|
||||
let long_bucket_name = "a".repeat(1000);
|
||||
let storage_err = StorageError::BucketNotFound(long_bucket_name.clone());
|
||||
let err = Error::new(storage_err);
|
||||
let s3_err = to_s3_error(err);
|
||||
|
||||
assert_eq!(*s3_err.code(), S3ErrorCode::NoSuchBucket);
|
||||
assert!(s3_err.message().unwrap().contains("bucket not found"));
|
||||
assert!(s3_err.message().unwrap().contains(&long_bucket_name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,3 +241,428 @@ lazy_static! {
|
||||
"x-amz-replication-status"
|
||||
];
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use http::{HeaderMap, HeaderValue};
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
fn create_test_headers() -> HeaderMap<HeaderValue> {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("content-type", HeaderValue::from_static("application/json"));
|
||||
headers.insert("x-amz-meta-custom", HeaderValue::from_static("custom-value"));
|
||||
headers.insert("x-rustfs-meta-internal", HeaderValue::from_static("internal-value"));
|
||||
headers.insert("cache-control", HeaderValue::from_static("no-cache"));
|
||||
headers
|
||||
}
|
||||
|
||||
fn create_test_metadata() -> HashMap<String, String> {
|
||||
let mut metadata = HashMap::new();
|
||||
metadata.insert("key1".to_string(), "value1".to_string());
|
||||
metadata.insert("key2".to_string(), "value2".to_string());
|
||||
metadata
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_del_opts_basic() {
|
||||
let headers = create_test_headers();
|
||||
let metadata = Some(create_test_metadata());
|
||||
|
||||
let result = del_opts("test-bucket", "test-object", None, &headers, metadata).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let opts = result.unwrap();
|
||||
assert!(opts.user_defined.is_some());
|
||||
assert_eq!(opts.version_id, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_del_opts_with_directory_object() {
|
||||
let headers = create_test_headers();
|
||||
|
||||
let result = del_opts("test-bucket", "test-dir/", None, &headers, None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let opts = result.unwrap();
|
||||
assert_eq!(opts.version_id, Some(Uuid::nil().to_string()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_del_opts_with_valid_version_id() {
|
||||
let headers = create_test_headers();
|
||||
let valid_uuid = Uuid::new_v4().to_string();
|
||||
|
||||
let result = del_opts("test-bucket", "test-object", Some(valid_uuid.clone()), &headers, None).await;
|
||||
|
||||
// This test may fail if versioning is not enabled for the bucket
|
||||
// In a real test environment, you would mock BucketVersioningSys
|
||||
match result {
|
||||
Ok(opts) => {
|
||||
assert_eq!(opts.version_id, Some(valid_uuid));
|
||||
}
|
||||
Err(_) => {
|
||||
// Expected if versioning is not enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_del_opts_with_invalid_version_id() {
|
||||
let headers = create_test_headers();
|
||||
let invalid_uuid = "invalid-uuid".to_string();
|
||||
|
||||
let result = del_opts("test-bucket", "test-object", Some(invalid_uuid), &headers, None).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
if let Err(err) = result {
|
||||
if let Some(storage_err) = err.downcast_ref::<StorageError>() {
|
||||
match storage_err {
|
||||
StorageError::InvalidVersionID(bucket, object, version) => {
|
||||
assert_eq!(bucket, "test-bucket");
|
||||
assert_eq!(object, "test-object");
|
||||
assert_eq!(version, "invalid-uuid");
|
||||
}
|
||||
_ => panic!("Expected InvalidVersionID error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_opts_basic() {
|
||||
let headers = create_test_headers();
|
||||
|
||||
let result = get_opts("test-bucket", "test-object", None, None, &headers).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let opts = result.unwrap();
|
||||
assert_eq!(opts.part_number, None);
|
||||
assert_eq!(opts.version_id, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_opts_with_part_number() {
|
||||
let headers = create_test_headers();
|
||||
|
||||
let result = get_opts("test-bucket", "test-object", None, Some(5), &headers).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let opts = result.unwrap();
|
||||
assert_eq!(opts.part_number, Some(5));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_opts_with_directory_object() {
|
||||
let headers = create_test_headers();
|
||||
|
||||
let result = get_opts("test-bucket", "test-dir/", None, None, &headers).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let opts = result.unwrap();
|
||||
assert_eq!(opts.version_id, Some(Uuid::nil().to_string()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_opts_with_invalid_version_id() {
|
||||
let headers = create_test_headers();
|
||||
let invalid_uuid = "invalid-uuid".to_string();
|
||||
|
||||
let result = get_opts("test-bucket", "test-object", Some(invalid_uuid), None, &headers).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
if let Err(err) = result {
|
||||
if let Some(storage_err) = err.downcast_ref::<StorageError>() {
|
||||
match storage_err {
|
||||
StorageError::InvalidVersionID(bucket, object, version) => {
|
||||
assert_eq!(bucket, "test-bucket");
|
||||
assert_eq!(object, "test-object");
|
||||
assert_eq!(version, "invalid-uuid");
|
||||
}
|
||||
_ => panic!("Expected InvalidVersionID error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_put_opts_basic() {
|
||||
let headers = create_test_headers();
|
||||
let metadata = Some(create_test_metadata());
|
||||
|
||||
let result = put_opts("test-bucket", "test-object", None, &headers, metadata).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let opts = result.unwrap();
|
||||
assert!(opts.user_defined.is_some());
|
||||
assert_eq!(opts.version_id, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_put_opts_with_directory_object() {
|
||||
let headers = create_test_headers();
|
||||
|
||||
let result = put_opts("test-bucket", "test-dir/", None, &headers, None).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let opts = result.unwrap();
|
||||
assert_eq!(opts.version_id, Some(Uuid::nil().to_string()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_put_opts_with_invalid_version_id() {
|
||||
let headers = create_test_headers();
|
||||
let invalid_uuid = "invalid-uuid".to_string();
|
||||
|
||||
let result = put_opts("test-bucket", "test-object", Some(invalid_uuid), &headers, None).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
if let Err(err) = result {
|
||||
if let Some(storage_err) = err.downcast_ref::<StorageError>() {
|
||||
match storage_err {
|
||||
StorageError::InvalidVersionID(bucket, object, version) => {
|
||||
assert_eq!(bucket, "test-bucket");
|
||||
assert_eq!(object, "test-object");
|
||||
assert_eq!(version, "invalid-uuid");
|
||||
}
|
||||
_ => panic!("Expected InvalidVersionID error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_copy_dst_opts() {
|
||||
let headers = create_test_headers();
|
||||
let metadata = Some(create_test_metadata());
|
||||
|
||||
let result = copy_dst_opts("test-bucket", "test-object", None, &headers, metadata).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let opts = result.unwrap();
|
||||
assert!(opts.user_defined.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_copy_src_opts() {
|
||||
let headers = create_test_headers();
|
||||
|
||||
let result = copy_src_opts("test-bucket", "test-object", &headers);
|
||||
|
||||
assert!(result.is_ok());
|
||||
let opts = result.unwrap();
|
||||
assert!(opts.user_defined.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_put_opts_from_headers() {
|
||||
let headers = create_test_headers();
|
||||
let metadata = Some(create_test_metadata());
|
||||
|
||||
let result = put_opts_from_headers(&headers, metadata);
|
||||
|
||||
assert!(result.is_ok());
|
||||
let opts = result.unwrap();
|
||||
assert!(opts.user_defined.is_some());
|
||||
let user_defined = opts.user_defined.unwrap();
|
||||
assert_eq!(user_defined.get("key1"), Some(&"value1".to_string()));
|
||||
assert_eq!(user_defined.get("key2"), Some(&"value2".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_default_opts_with_metadata() {
|
||||
let headers = create_test_headers();
|
||||
let metadata = Some(create_test_metadata());
|
||||
|
||||
let result = get_default_opts(&headers, metadata, false);
|
||||
|
||||
assert!(result.is_ok());
|
||||
let opts = result.unwrap();
|
||||
assert!(opts.user_defined.is_some());
|
||||
let user_defined = opts.user_defined.unwrap();
|
||||
assert_eq!(user_defined.get("key1"), Some(&"value1".to_string()));
|
||||
assert_eq!(user_defined.get("key2"), Some(&"value2".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_default_opts_without_metadata() {
|
||||
let headers = create_test_headers();
|
||||
|
||||
let result = get_default_opts(&headers, None, false);
|
||||
|
||||
assert!(result.is_ok());
|
||||
let opts = result.unwrap();
|
||||
assert!(opts.user_defined.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_metadata_basic() {
|
||||
let headers = create_test_headers();
|
||||
|
||||
let metadata = extract_metadata(&headers);
|
||||
|
||||
assert!(metadata.contains_key("content-type"));
|
||||
assert_eq!(metadata.get("content-type"), Some(&"application/json".to_string()));
|
||||
assert!(metadata.contains_key("cache-control"));
|
||||
assert_eq!(metadata.get("cache-control"), Some(&"no-cache".to_string()));
|
||||
assert!(metadata.contains_key("custom"));
|
||||
assert_eq!(metadata.get("custom"), Some(&"custom-value".to_string()));
|
||||
assert!(metadata.contains_key("internal"));
|
||||
assert_eq!(metadata.get("internal"), Some(&"internal-value".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_metadata_from_mime_amz_meta() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("x-amz-meta-user-id", HeaderValue::from_static("12345"));
|
||||
headers.insert("x-amz-meta-project", HeaderValue::from_static("test-project"));
|
||||
headers.insert("x-amz-meta-", HeaderValue::from_static("empty-key")); // Should be ignored
|
||||
|
||||
let mut metadata = HashMap::new();
|
||||
extract_metadata_from_mime(&headers, &mut metadata);
|
||||
|
||||
assert_eq!(metadata.get("user-id"), Some(&"12345".to_string()));
|
||||
assert_eq!(metadata.get("project"), Some(&"test-project".to_string()));
|
||||
assert!(!metadata.contains_key(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_metadata_from_mime_rustfs_meta() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("x-rustfs-meta-internal-id", HeaderValue::from_static("67890"));
|
||||
headers.insert("x-rustfs-meta-category", HeaderValue::from_static("documents"));
|
||||
|
||||
let mut metadata = HashMap::new();
|
||||
extract_metadata_from_mime(&headers, &mut metadata);
|
||||
|
||||
assert_eq!(metadata.get("internal-id"), Some(&"67890".to_string()));
|
||||
assert_eq!(metadata.get("category"), Some(&"documents".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_metadata_from_mime_supported_headers() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("content-type", HeaderValue::from_static("text/plain"));
|
||||
headers.insert("cache-control", HeaderValue::from_static("max-age=3600"));
|
||||
headers.insert("content-language", HeaderValue::from_static("en-US"));
|
||||
headers.insert("content-encoding", HeaderValue::from_static("gzip"));
|
||||
headers.insert("content-disposition", HeaderValue::from_static("attachment"));
|
||||
headers.insert("x-amz-storage-class", HeaderValue::from_static("STANDARD"));
|
||||
headers.insert("x-amz-tagging", HeaderValue::from_static("key1=value1&key2=value2"));
|
||||
headers.insert("expires", HeaderValue::from_static("Wed, 21 Oct 2015 07:28:00 GMT"));
|
||||
headers.insert("x-amz-replication-status", HeaderValue::from_static("COMPLETED"));
|
||||
|
||||
let mut metadata = HashMap::new();
|
||||
extract_metadata_from_mime(&headers, &mut metadata);
|
||||
|
||||
assert_eq!(metadata.get("content-type"), Some(&"text/plain".to_string()));
|
||||
assert_eq!(metadata.get("cache-control"), Some(&"max-age=3600".to_string()));
|
||||
assert_eq!(metadata.get("content-language"), Some(&"en-US".to_string()));
|
||||
assert_eq!(metadata.get("content-encoding"), Some(&"gzip".to_string()));
|
||||
assert_eq!(metadata.get("content-disposition"), Some(&"attachment".to_string()));
|
||||
assert_eq!(metadata.get("x-amz-storage-class"), Some(&"STANDARD".to_string()));
|
||||
assert_eq!(metadata.get("x-amz-tagging"), Some(&"key1=value1&key2=value2".to_string()));
|
||||
assert_eq!(metadata.get("expires"), Some(&"Wed, 21 Oct 2015 07:28:00 GMT".to_string()));
|
||||
assert_eq!(metadata.get("x-amz-replication-status"), Some(&"COMPLETED".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_metadata_from_mime_default_content_type() {
|
||||
let headers = HeaderMap::new();
|
||||
|
||||
let mut metadata = HashMap::new();
|
||||
extract_metadata_from_mime(&headers, &mut metadata);
|
||||
|
||||
assert_eq!(metadata.get("content-type"), Some(&"binary/octet-stream".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_metadata_from_mime_existing_content_type() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("content-type", HeaderValue::from_static("application/json"));
|
||||
|
||||
let mut metadata = HashMap::new();
|
||||
extract_metadata_from_mime(&headers, &mut metadata);
|
||||
|
||||
assert_eq!(metadata.get("content-type"), Some(&"application/json".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_metadata_from_mime_unicode_values() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("x-amz-meta-chinese", HeaderValue::from_bytes("测试值".as_bytes()).unwrap());
|
||||
headers.insert("x-rustfs-meta-emoji", HeaderValue::from_bytes("🚀".as_bytes()).unwrap());
|
||||
|
||||
let mut metadata = HashMap::new();
|
||||
extract_metadata_from_mime(&headers, &mut metadata);
|
||||
|
||||
assert_eq!(metadata.get("chinese"), Some(&"测试值".to_string()));
|
||||
assert_eq!(metadata.get("emoji"), Some(&"🚀".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_metadata_from_mime_unsupported_headers() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("authorization", HeaderValue::from_static("Bearer token"));
|
||||
headers.insert("host", HeaderValue::from_static("example.com"));
|
||||
headers.insert("user-agent", HeaderValue::from_static("test-agent"));
|
||||
|
||||
let mut metadata = HashMap::new();
|
||||
extract_metadata_from_mime(&headers, &mut metadata);
|
||||
|
||||
// These headers should not be included in metadata
|
||||
assert!(!metadata.contains_key("authorization"));
|
||||
assert!(!metadata.contains_key("host"));
|
||||
assert!(!metadata.contains_key("user-agent"));
|
||||
// But default content-type should be added
|
||||
assert_eq!(metadata.get("content-type"), Some(&"binary/octet-stream".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_supported_headers_constant() {
|
||||
let expected_headers = vec![
|
||||
"content-type",
|
||||
"cache-control",
|
||||
"content-language",
|
||||
"content-encoding",
|
||||
"content-disposition",
|
||||
"x-amz-storage-class",
|
||||
"x-amz-tagging",
|
||||
"expires",
|
||||
"x-amz-replication-status"
|
||||
];
|
||||
|
||||
assert_eq!(*SUPPORTED_HEADERS, expected_headers);
|
||||
assert_eq!(SUPPORTED_HEADERS.len(), 9);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_metadata_empty_headers() {
|
||||
let headers = HeaderMap::new();
|
||||
|
||||
let metadata = extract_metadata(&headers);
|
||||
|
||||
// Should only contain default content-type
|
||||
assert_eq!(metadata.len(), 1);
|
||||
assert_eq!(metadata.get("content-type"), Some(&"binary/octet-stream".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_metadata_mixed_headers() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("content-type", HeaderValue::from_static("application/xml"));
|
||||
headers.insert("x-amz-meta-version", HeaderValue::from_static("1.0"));
|
||||
headers.insert("x-rustfs-meta-source", HeaderValue::from_static("upload"));
|
||||
headers.insert("cache-control", HeaderValue::from_static("public"));
|
||||
headers.insert("authorization", HeaderValue::from_static("Bearer xyz")); // Should be ignored
|
||||
|
||||
let metadata = extract_metadata(&headers);
|
||||
|
||||
assert_eq!(metadata.get("content-type"), Some(&"application/xml".to_string()));
|
||||
assert_eq!(metadata.get("version"), Some(&"1.0".to_string()));
|
||||
assert_eq!(metadata.get("source"), Some(&"upload".to_string()));
|
||||
assert_eq!(metadata.get("cache-control"), Some(&"public".to_string()));
|
||||
assert!(!metadata.contains_key("authorization"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user