add Error test, fix clippy

This commit is contained in:
weisd
2025-06-09 11:29:23 +08:00
parent 96de65ebab
commit 91c099e35f
108 changed files with 1594 additions and 282 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ use iam::{
};
use madmin::GroupAddRemove;
use matchit::Params;
use s3s::{header::CONTENT_TYPE, s3_error, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result};
use s3s::{Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result, header::CONTENT_TYPE, s3_error};
use serde::Deserialize;
use serde_urlencoded::from_bytes;
use tracing::warn;
+1 -1
View File
@@ -3,7 +3,7 @@ use http::{HeaderMap, StatusCode};
use iam::{error::is_err_no_such_user, get_global_action_cred, store::MappedPolicy};
use matchit::Params;
use policy::policy::Policy;
use s3s::{header::CONTENT_TYPE, s3_error, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result};
use s3s::{Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result, header::CONTENT_TYPE, s3_error};
use serde::Deserialize;
use serde_urlencoded::from_bytes;
use std::collections::HashMap;
+1 -1
View File
@@ -16,7 +16,7 @@ use matchit::Params;
use policy::policy::action::{Action, AdminAction};
use policy::policy::{Args, Policy};
use s3s::S3ErrorCode::InvalidRequest;
use s3s::{header::CONTENT_TYPE, s3_error, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result};
use s3s::{Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result, header::CONTENT_TYPE, s3_error};
use serde::Deserialize;
use serde_urlencoded::from_bytes;
use std::collections::HashMap;
+2 -1
View File
@@ -10,8 +10,9 @@ use iam::{manager::get_token_signing_key, sys::SESSION_POLICY_NAME};
use matchit::Params;
use policy::{auth::get_new_credentials_with_metadata, policy::Policy};
use s3s::{
Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result,
dto::{AssumeRoleOutput, Credentials, Timestamp},
s3_error, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result,
s3_error,
};
use serde::Deserialize;
use serde_json::Value;
+2 -2
View File
@@ -1,9 +1,9 @@
use ecstore::{peer_rest_client::PeerRestClient, GLOBAL_Endpoints};
use ecstore::{GLOBAL_Endpoints, peer_rest_client::PeerRestClient};
use http::StatusCode;
use hyper::Uri;
use madmin::service_commands::ServiceTraceOpts;
use matchit::Params;
use s3s::{s3_error, Body, S3Request, S3Response, S3Result};
use s3s::{Body, S3Request, S3Response, S3Result, s3_error};
use tracing::warn;
use crate::admin::router::Operation;
+2 -2
View File
@@ -5,10 +5,10 @@ use iam::get_global_action_cred;
use madmin::{AccountStatus, AddOrUpdateUserReq};
use matchit::Params;
use policy::policy::{
action::{Action, AdminAction},
Args,
action::{Action, AdminAction},
};
use s3s::{header::CONTENT_TYPE, s3_error, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result};
use s3s::{Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result, header::CONTENT_TYPE, s3_error};
use serde::Deserialize;
use serde_urlencoded::from_bytes;
use tracing::warn;
+3 -3
View File
@@ -7,13 +7,13 @@ use iam::get_global_action_cred;
use iam::sys::SESSION_POLICY_NAME;
use policy::auth;
use policy::auth::get_claims_from_token_with_secret;
use s3s::S3Error;
use s3s::S3ErrorCode;
use s3s::S3Result;
use s3s::auth::S3Auth;
use s3s::auth::SecretKey;
use s3s::auth::SimpleAuth;
use s3s::s3_error;
use s3s::S3Error;
use s3s::S3ErrorCode;
use s3s::S3Result;
use serde_json::Value;
pub struct IAMAuth {
+2 -2
View File
@@ -1,10 +1,10 @@
use crate::license::get_license;
use axum::{
Router,
body::Body,
http::{Response, StatusCode},
response::IntoResponse,
routing::get,
Router,
};
use axum_extra::extract::Host;
use rustfs_config::{RUSTFS_TLS_CERT, RUSTFS_TLS_KEY};
@@ -12,7 +12,7 @@ use std::io;
use axum::response::Redirect;
use axum_server::tls_rustls::RustlsConfig;
use http::{header, Uri};
use http::{Uri, header};
use mime_guess::from_path;
use rust_embed::RustEmbed;
use serde::Serialize;
+235
View File
@@ -94,3 +94,238 @@ impl From<iam::error::Error> for ApiError {
serr.into()
}
}
#[cfg(test)]
mod tests {
use super::*;
use s3s::{S3Error, S3ErrorCode};
use std::io::{Error as IoError, ErrorKind};
#[test]
fn test_api_error_from_io_error() {
let io_error = IoError::new(ErrorKind::PermissionDenied, "permission denied");
let api_error: ApiError = io_error.into();
assert_eq!(api_error.code, S3ErrorCode::InternalError);
assert!(api_error.message.contains("permission denied"));
assert!(api_error.source.is_some());
}
#[test]
fn test_api_error_from_io_error_different_kinds() {
let test_cases = vec![
(ErrorKind::NotFound, "not found"),
(ErrorKind::InvalidInput, "invalid input"),
(ErrorKind::TimedOut, "timed out"),
(ErrorKind::WriteZero, "write zero"),
(ErrorKind::Other, "other error"),
];
for (kind, message) in test_cases {
let io_error = IoError::new(kind, message);
let api_error: ApiError = io_error.into();
assert_eq!(api_error.code, S3ErrorCode::InternalError);
assert!(api_error.message.contains(message));
assert!(api_error.source.is_some());
// Test that source can be downcast back to io::Error
let source = api_error.source.as_ref().unwrap();
let downcast_io_error = source.downcast_ref::<IoError>();
assert!(downcast_io_error.is_some());
assert_eq!(downcast_io_error.unwrap().kind(), kind);
}
}
#[test]
fn test_api_error_other_function() {
let custom_error = "Custom API error";
let api_error = ApiError::other(custom_error);
assert_eq!(api_error.code, S3ErrorCode::InternalError);
assert_eq!(api_error.message, custom_error);
assert!(api_error.source.is_some());
}
#[test]
fn test_api_error_other_function_with_complex_error() {
let io_error = IoError::new(ErrorKind::InvalidData, "complex error");
let api_error = ApiError::other(io_error);
assert_eq!(api_error.code, S3ErrorCode::InternalError);
assert!(api_error.message.contains("complex error"));
assert!(api_error.source.is_some());
// Test that source can be downcast back to io::Error
let source = api_error.source.as_ref().unwrap();
let downcast_io_error = source.downcast_ref::<IoError>();
assert!(downcast_io_error.is_some());
assert_eq!(downcast_io_error.unwrap().kind(), ErrorKind::InvalidData);
}
#[test]
fn test_api_error_from_storage_error() {
let storage_error = StorageError::BucketNotFound("test-bucket".to_string());
let api_error: ApiError = storage_error.into();
assert_eq!(api_error.code, S3ErrorCode::NoSuchBucket);
assert!(api_error.message.contains("test-bucket"));
assert!(api_error.source.is_some());
// Test that source can be downcast back to StorageError
let source = api_error.source.as_ref().unwrap();
let downcast_storage_error = source.downcast_ref::<StorageError>();
assert!(downcast_storage_error.is_some());
}
#[test]
fn test_api_error_from_storage_error_mappings() {
let test_cases = vec![
(StorageError::NotImplemented, S3ErrorCode::NotImplemented),
(
StorageError::InvalidArgument("test".into(), "test".into(), "test".into()),
S3ErrorCode::InvalidArgument,
),
(StorageError::MethodNotAllowed, S3ErrorCode::MethodNotAllowed),
(StorageError::BucketNotFound("test".into()), S3ErrorCode::NoSuchBucket),
(StorageError::BucketNotEmpty("test".into()), S3ErrorCode::BucketNotEmpty),
(StorageError::BucketNameInvalid("test".into()), S3ErrorCode::InvalidBucketName),
(
StorageError::ObjectNameInvalid("test".into(), "test".into()),
S3ErrorCode::InvalidArgument,
),
(StorageError::BucketExists("test".into()), S3ErrorCode::BucketAlreadyExists),
(StorageError::StorageFull, S3ErrorCode::ServiceUnavailable),
(StorageError::SlowDown, S3ErrorCode::SlowDown),
(StorageError::PrefixAccessDenied("test".into(), "test".into()), S3ErrorCode::AccessDenied),
(StorageError::ObjectNotFound("test".into(), "test".into()), S3ErrorCode::NoSuchKey),
(StorageError::ConfigNotFound, S3ErrorCode::NoSuchKey),
(StorageError::VolumeNotFound, S3ErrorCode::NoSuchBucket),
(StorageError::FileNotFound, S3ErrorCode::NoSuchKey),
(StorageError::FileVersionNotFound, S3ErrorCode::NoSuchVersion),
];
for (storage_error, expected_code) in test_cases {
let api_error: ApiError = storage_error.into();
assert_eq!(api_error.code, expected_code);
assert!(api_error.source.is_some());
}
}
#[test]
fn test_api_error_from_iam_error() {
let iam_error = iam::error::Error::other("IAM test error");
let api_error: ApiError = iam_error.into();
// IAM error is first converted to StorageError, then to ApiError
assert!(api_error.source.is_some());
assert!(api_error.message.contains("test error"));
}
#[test]
fn test_api_error_to_s3_error() {
let api_error = ApiError {
code: S3ErrorCode::NoSuchBucket,
message: "Bucket not found".to_string(),
source: Some(Box::new(IoError::new(ErrorKind::NotFound, "not found"))),
};
let s3_error: S3Error = api_error.into();
assert_eq!(*s3_error.code(), S3ErrorCode::NoSuchBucket);
assert!(s3_error.message().unwrap_or("").contains("Bucket not found"));
assert!(s3_error.source().is_some());
}
#[test]
fn test_api_error_to_s3_error_without_source() {
let api_error = ApiError {
code: S3ErrorCode::InvalidArgument,
message: "Invalid argument".to_string(),
source: None,
};
let s3_error: S3Error = api_error.into();
assert_eq!(*s3_error.code(), S3ErrorCode::InvalidArgument);
assert!(s3_error.message().unwrap_or("").contains("Invalid argument"));
}
#[test]
fn test_api_error_display() {
let api_error = ApiError {
code: S3ErrorCode::InternalError,
message: "Test error message".to_string(),
source: None,
};
assert_eq!(api_error.to_string(), "Test error message");
}
#[test]
fn test_api_error_debug() {
let api_error = ApiError {
code: S3ErrorCode::NoSuchKey,
message: "Object not found".to_string(),
source: Some(Box::new(IoError::new(ErrorKind::NotFound, "file not found"))),
};
let debug_str = format!("{:?}", api_error);
assert!(debug_str.contains("NoSuchKey"));
assert!(debug_str.contains("Object not found"));
}
#[test]
fn test_api_error_roundtrip_through_io_error() {
let original_io_error = IoError::new(ErrorKind::PermissionDenied, "original permission error");
// Convert to ApiError
let api_error: ApiError = original_io_error.into();
// Verify the conversion preserved the information
assert_eq!(api_error.code, S3ErrorCode::InternalError);
assert!(api_error.message.contains("original permission error"));
assert!(api_error.source.is_some());
// Test that we can downcast back to the original io::Error
let source = api_error.source.as_ref().unwrap();
let downcast_io_error = source.downcast_ref::<IoError>();
assert!(downcast_io_error.is_some());
assert_eq!(downcast_io_error.unwrap().kind(), ErrorKind::PermissionDenied);
assert!(downcast_io_error.unwrap().to_string().contains("original permission error"));
}
#[test]
fn test_api_error_chain_conversion() {
// Start with an io::Error
let io_error = IoError::new(ErrorKind::InvalidData, "invalid data");
// Convert to StorageError (simulating what happens in the codebase)
let storage_error = StorageError::other(io_error);
// Convert to ApiError
let api_error: ApiError = storage_error.into();
// Verify the chain is preserved
assert!(api_error.source.is_some());
// Check that we can still access the original error information
let source = api_error.source.as_ref().unwrap();
let downcast_storage_error = source.downcast_ref::<StorageError>();
assert!(downcast_storage_error.is_some());
}
#[test]
fn test_api_error_error_trait_implementation() {
let api_error = ApiError {
code: S3ErrorCode::InternalError,
message: "Test error".to_string(),
source: Some(Box::new(IoError::other("source error"))),
};
// Test that it implements std::error::Error
let error: &dyn std::error::Error = &api_error;
assert_eq!(error.to_string(), "Test error");
// ApiError doesn't implement Error::source() properly, so this would be None
// This is expected because ApiError is not a typical Error implementation
assert!(error.source().is_none());
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
mod service_state;
pub(crate) use service_state::wait_for_shutdown;
pub(crate) use service_state::SHUTDOWN_TIMEOUT;
pub(crate) use service_state::ServiceState;
pub(crate) use service_state::ServiceStateManager;
pub(crate) use service_state::ShutdownSignal;
pub(crate) use service_state::SHUTDOWN_TIMEOUT;
pub(crate) use service_state::wait_for_shutdown;
+3 -3
View File
@@ -1,8 +1,8 @@
use atomic_enum::atomic_enum;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::atomic::Ordering;
use std::time::Duration;
use tokio::signal::unix::{signal, SignalKind};
use tokio::signal::unix::{SignalKind, signal};
use tracing::info;
// a configurable shutdown timeout
@@ -10,7 +10,7 @@ pub(crate) const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(1);
#[cfg(target_os = "linux")]
fn notify_systemd(state: &str) {
use libsystemd::daemon::{notify, NotifyState};
use libsystemd::daemon::{NotifyState, notify};
use tracing::{debug, error};
let notify_state = match state {
"ready" => NotifyState::Ready,