Merge pull request #405 from rustfs/feat/event-notifier-error-tests

feat: Add comprehensive test coverage for event-notifier error module
This commit is contained in:
安正超
2025-05-25 13:35:17 +08:00
committed by GitHub
2 changed files with 547 additions and 147 deletions
+175 -147
View File
@@ -1,58 +1,63 @@
# RustFS 项目 Cursor 规则
# RustFS Project Cursor Rules
## 项目概述
RustFS 是一个用 Rust 编写的高性能分布式对象存储系统,兼容 S3 API。项目采用模块化架构,支持纠删码存储、多租户管理、可观测性等企业级功能。
## Project Overview
RustFS is a high-performance distributed object storage system written in Rust, compatible with S3 API. The project adopts a modular architecture, supporting erasure coding storage, multi-tenant management, observability, and other enterprise-level features.
## 核心架构原则
## Core Architecture Principles
### 1. 模块化设计
- 项目采用 Cargo workspace 结构,包含多个独立的 crate
- 核心模块:`rustfs`(主服务)、`ecstore`(纠删码存储)、`common`(共享组件)
- 功能模块:`iam`(身份管理)、`madmin`(管理接口)、`crypto`(加密)等
- 工具模块:`cli`(命令行工具)、`crates/*`(工具库)
### 1. Modular Design
- Project uses Cargo workspace structure, containing multiple independent crates
- Core modules: `rustfs` (main service), `ecstore` (erasure coding storage), `common` (shared components)
- Functional modules: `iam` (identity management), `madmin` (management interface), `crypto` (encryption), etc.
- Tool modules: `cli` (command line tool), `crates/*` (utility libraries)
### 2. 异步编程模式
- 全面使用 `tokio` 异步运行时
- 优先使用 `async/await` 语法
- 使用 `async-trait` 处理 trait 中的异步方法
- 避免阻塞操作,必要时使用 `spawn_blocking`
### 2. Asynchronous Programming Pattern
- Comprehensive use of `tokio` async runtime
- Prioritize `async/await` syntax
- Use `async-trait` for async methods in traits
- Avoid blocking operations, use `spawn_blocking` when necessary
### 3. 错误处理策略
- 使用统一的错误类型 `common::error::Error`
- 支持错误链和上下文信息
- 使用 `thiserror` 定义具体错误类型
- 错误转换使用 `downcast_ref` 进行类型检查
### 3. Error Handling Strategy
- Use unified error type `common::error::Error`
- Support error chains and context information
- Use `thiserror` to define specific error types
- Error conversion uses `downcast_ref` for type checking
## 代码风格规范
## Code Style Guidelines
### 1. 格式化配置
### 1. Formatting Configuration
```toml
max_width = 130
fn_call_width = 90
single_line_let_else_max_width = 100
```
### 2. 命名约定
- 使用 `snake_case` 命名函数、变量、模块
- 使用 `PascalCase` 命名类型、trait、枚举
- 常量使用 `SCREAMING_SNAKE_CASE`
- 全局变量前缀 `GLOBAL_`,如 `GLOBAL_Endpoints`
### 2. Naming Conventions
- Use `snake_case` for functions, variables, modules
- Use `PascalCase` for types, traits, enums
- Constants use `SCREAMING_SNAKE_CASE`
- Global variables prefix `GLOBAL_`, e.g., `GLOBAL_Endpoints`
- Use meaningful and descriptive names for variables, functions, and methods
- Avoid meaningless names like `temp`, `data`, `foo`, `bar`, `test123`
- Choose names that clearly express the purpose and intent
### 3. 文档注释
- 公共 API 必须有文档注释
- 使用 `///` 进行文档注释
- 复杂函数添加 `# Examples` `# Parameters` 说明
- 错误情况使用 `# Errors` 说明
### 3. Documentation Comments
- Public APIs must have documentation comments
- Use `///` for documentation comments
- Complex functions add `# Examples` and `# Parameters` descriptions
- Error cases use `# Errors` descriptions
- Always use English for all comments and documentation
- Avoid meaningless comments like "debug 111" or placeholder text
### 4. 导入规范
- 标准库导入在最前面
- 第三方 crate 导入在中间
- 本项目内部导入在最后
- 使用 `use` 语句分组,组间空行分隔
### 4. Import Guidelines
- Standard library imports first
- Third-party crate imports in the middle
- Project internal imports last
- Group `use` statements with blank lines between groups
## 异步编程规范
## Asynchronous Programming Guidelines
### 1. Trait 定义
### 1. Trait Definition
```rust
#[async_trait::async_trait]
pub trait StorageAPI: Send + Sync {
@@ -60,9 +65,9 @@ pub trait StorageAPI: Send + Sync {
}
```
### 2. 错误处理
### 2. Error Handling
```rust
// 使用 ? 操作符传播错误
// Use ? operator to propagate errors
async fn example_function() -> Result<()> {
let data = read_file("path").await?;
process_data(data).await?;
@@ -70,30 +75,30 @@ async fn example_function() -> Result<()> {
}
```
### 3. 并发控制
- 使用 `Arc` `Mutex`/`RwLock` 进行共享状态管理
- 优先使用 `tokio::sync` 中的异步锁
- 避免长时间持有锁
### 3. Concurrency Control
- Use `Arc` and `Mutex`/`RwLock` for shared state management
- Prioritize async locks from `tokio::sync`
- Avoid holding locks for long periods
## 日志和追踪规范
## Logging and Tracing Guidelines
### 1. Tracing 使用
### 1. Tracing Usage
```rust
#[tracing::instrument(skip(self, data))]
async fn process_data(&self, data: &[u8]) -> Result<()> {
info!("Processing {} bytes", data.len());
// 实现逻辑
// Implementation logic
}
```
### 2. 日志级别
- `error!`: 系统错误,需要立即关注
- `warn!`: 警告信息,可能影响功能
- `info!`: 重要的业务信息
- `debug!`: 调试信息,开发时使用
- `trace!`: 详细的执行路径
### 2. Log Levels
- `error!`: System errors requiring immediate attention
- `warn!`: Warning information that may affect functionality
- `info!`: Important business information
- `debug!`: Debug information for development use
- `trace!`: Detailed execution paths
### 3. 结构化日志
### 3. Structured Logging
```rust
info!(
counter.rustfs_api_requests_total = 1_u64,
@@ -103,9 +108,9 @@ info!(
);
```
## 错误处理规范
## Error Handling Guidelines
### 1. 错误类型定义
### 1. Error Type Definition
```rust
#[derive(Debug, thiserror::Error)]
pub enum MyError {
@@ -116,7 +121,7 @@ pub enum MyError {
}
```
### 2. 错误转换
### 2. Error Conversion
```rust
pub fn to_s3_error(err: Error) -> S3Error {
if let Some(storage_err) = err.downcast_ref::<StorageError>() {
@@ -124,40 +129,40 @@ pub fn to_s3_error(err: Error) -> S3Error {
StorageError::ObjectNotFound(bucket, object) => {
s3_error!(NoSuchKey, "{}/{}", bucket, object)
}
// 其他错误类型...
// Other error types...
}
}
// 默认错误处理
// Default error handling
}
```
### 3. 错误上下文
### 3. Error Context
```rust
// 添加错误上下文
// Add error context
.map_err(|e| Error::from_string(format!("Failed to process {}: {}", path, e)))?
```
## 性能优化规范
## Performance Optimization Guidelines
### 1. 内存管理
- 使用 `Bytes` 而不是 `Vec<u8>` 进行零拷贝操作
- 避免不必要的克隆,使用引用传递
- 大对象使用 `Arc` 共享
### 1. Memory Management
- Use `Bytes` instead of `Vec<u8>` for zero-copy operations
- Avoid unnecessary cloning, use reference passing
- Use `Arc` for sharing large objects
### 2. 并发优化
### 2. Concurrency Optimization
```rust
// 使用 join_all 进行并发操作
// Use join_all for concurrent operations
let futures = disks.iter().map(|disk| disk.operation());
let results = join_all(futures).await;
```
### 3. 缓存策略
- 使用 `lazy_static` `OnceCell` 进行全局缓存
- 实现 LRU 缓存避免内存泄漏
### 3. Caching Strategy
- Use `lazy_static` or `OnceCell` for global caching
- Implement LRU cache to avoid memory leaks
## 测试规范
## Testing Guidelines
### 1. 单元测试
### 1. Unit Tests
```rust
#[cfg(test)]
mod tests {
@@ -178,31 +183,38 @@ mod tests {
}
```
### 2. 集成测试
- 使用 `e2e_test` 模块进行端到端测试
- 模拟真实的存储环境
### 2. Integration Tests
- Use `e2e_test` module for end-to-end testing
- Simulate real storage environments
## 安全规范
### 3. Test Quality Standards
- Write meaningful test cases that verify actual functionality
- Avoid placeholder or debug content like "debug 111", "test test", etc.
- Use descriptive test names that clearly indicate what is being tested
- Each test should have a clear purpose and verify specific behavior
- Test data should be realistic and representative of actual use cases
### 1. 内存安全
- 禁用 `unsafe` 代码(workspace.lints.rust.unsafe_code = "deny"
- 使用 `rustls` 而不是 `openssl`
## Security Guidelines
### 2. 认证授权
### 1. Memory Safety
- Disable `unsafe` code (workspace.lints.rust.unsafe_code = "deny")
- Use `rustls` instead of `openssl`
### 2. Authentication and Authorization
```rust
// 使用 IAM 系统进行权限检查
// Use IAM system for permission checks
let identity = iam.authenticate(&access_key, &secret_key).await?;
iam.authorize(&identity, &action, &resource).await?;
```
## 配置管理规范
## Configuration Management Guidelines
### 1. 环境变量
- 使用 `RUSTFS_` 前缀
- 支持配置文件和环境变量两种方式
- 提供合理的默认值
### 1. Environment Variables
- Use `RUSTFS_` prefix
- Support both configuration files and environment variables
- Provide reasonable default values
### 2. 配置结构
### 2. Configuration Structure
```rust
#[derive(Debug, Deserialize, Clone)]
pub struct Config {
@@ -213,13 +225,13 @@ pub struct Config {
}
```
## 依赖管理规范
## Dependency Management Guidelines
### 1. Workspace 依赖
- workspace 级别统一管理版本
- 使用 `workspace = true` 继承配置
### 1. Workspace Dependencies
- Manage versions uniformly at workspace level
- Use `workspace = true` to inherit configuration
### 2. 功能特性
### 2. Feature Flags
```rust
[features]
default = ["file"]
@@ -227,84 +239,84 @@ gpu = ["dep:nvml-wrapper"]
kafka = ["dep:rdkafka"]
```
## 部署和运维规范
## Deployment and Operations Guidelines
### 1. 容器化
- 提供 Dockerfile docker-compose 配置
- 支持多阶段构建优化镜像大小
### 1. Containerization
- Provide Dockerfile and docker-compose configuration
- Support multi-stage builds to optimize image size
### 2. 可观测性
- 集成 OpenTelemetry 进行分布式追踪
- 支持 Prometheus 指标收集
- 提供 Grafana 仪表板
### 2. Observability
- Integrate OpenTelemetry for distributed tracing
- Support Prometheus metrics collection
- Provide Grafana dashboards
### 3. 健康检查
### 3. Health Checks
```rust
// 实现健康检查端点
// Implement health check endpoint
async fn health_check() -> Result<HealthStatus> {
// 检查各个组件状态
// Check component status
}
```
## 代码审查清单
## Code Review Checklist
### 1. 功能性
- [ ] 是否正确处理所有错误情况
- [ ] 是否有适当的日志记录
- [ ] 是否有必要的测试覆盖
### 1. Functionality
- [ ] Are all error cases properly handled?
- [ ] Is there appropriate logging?
- [ ] Is there necessary test coverage?
### 2. 性能
- [ ] 是否避免了不必要的内存分配
- [ ] 是否正确使用了异步操作
- [ ] 是否有潜在的死锁风险
### 2. Performance
- [ ] Are unnecessary memory allocations avoided?
- [ ] Are async operations used correctly?
- [ ] Are there potential deadlock risks?
### 3. 安全性
- [ ] 是否正确验证输入参数
- [ ] 是否有适当的权限检查
- [ ] 是否避免了信息泄露
### 3. Security
- [ ] Are input parameters properly validated?
- [ ] Are there appropriate permission checks?
- [ ] Is information leakage avoided?
### 4. 可维护性
- [ ] 代码是否清晰易懂
- [ ] 是否遵循项目的架构模式
- [ ] 是否有适当的文档
### 4. Maintainability
- [ ] Is the code clear and understandable?
- [ ] Does it follow the project's architectural patterns?
- [ ] Is there appropriate documentation?
### 5. 代码提交
- [ ] 是否符合[代码提交规范](https://www.conventionalcommits.org/en/v1.0.0/)
- [ ] 提交的标题要精简,以英文为主,不要使用中文
### 5. Code Commits
- [ ] Does it comply with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)?
- [ ] Commit titles should be concise and in English, avoid Chinese
## 常用模式和最佳实践
## Common Patterns and Best Practices
### 1. 资源管理
### 1. Resource Management
```rust
// 使用 RAII 模式管理资源
// Use RAII pattern for resource management
pub struct ResourceGuard {
resource: Resource,
}
impl Drop for ResourceGuard {
fn drop(&mut self) {
// 清理资源
// Clean up resources
}
}
```
### 2. 配置注入
### 2. Dependency Injection
```rust
// 使用依赖注入模式
// Use dependency injection pattern
pub struct Service {
config: Arc<Config>,
storage: Arc<dyn StorageAPI>,
}
```
### 3. 优雅关闭
### 3. Graceful Shutdown
```rust
// 实现优雅关闭
// Implement graceful shutdown
async fn shutdown_gracefully(shutdown_rx: &mut Receiver<()>) {
tokio::select! {
_ = shutdown_rx.recv() => {
info!("Received shutdown signal");
// 执行清理操作
// Perform cleanup operations
}
_ = tokio::time::sleep(SHUTDOWN_TIMEOUT) => {
warn!("Shutdown timeout reached");
@@ -313,21 +325,37 @@ async fn shutdown_gracefully(shutdown_rx: &mut Receiver<()>) {
}
```
## 特定领域规范
## Domain-Specific Guidelines
### 1. 存储操作
- 所有存储操作必须支持纠删码
- 实现读写仲裁机制
- 支持数据完整性校验
### 1. Storage Operations
- All storage operations must support erasure coding
- Implement read/write quorum mechanisms
- Support data integrity verification
### 2. 网络通信
- 使用 gRPC 进行内部服务通信
- HTTP/HTTPS 支持 S3 兼容 API
- 实现连接池和重试机制
### 2. Network Communication
- Use gRPC for internal service communication
- HTTP/HTTPS support for S3-compatible API
- Implement connection pooling and retry mechanisms
### 3. 元数据管理
- 使用 FlatBuffers 进行序列化
- 支持版本控制和迁移
- 实现元数据缓存
### 3. Metadata Management
- Use FlatBuffers for serialization
- Support version control and migration
- Implement metadata caching
这些规则应该作为开发 RustFS 项目时的指导原则,确保代码质量、性能和可维护性。
These rules should serve as guiding principles when developing the RustFS project, ensuring code quality, performance, and maintainability.
### 4. Code Operations
- Always check the .cursorrules file before starting to ensure you understand the project guidelines
- Before starting any change or requirement development, first git checkout to main branch, then git pull to get the latest code
- For each feature or change to be developed, first create a branch, then git checkout to that branch
- Use English for all code comments, documentation, and variable names
- Write meaningful and descriptive names for variables, functions, and methods
- Avoid meaningless test content like "debug 111" or placeholder values
- Before each change, carefully read the existing code to ensure you understand the code structure and implementation, do not break existing logic implementation, do not introduce new issues
- Ensure each change provides sufficient test cases to guarantee code correctness
- Do not arbitrarily modify numbers and constants in test cases, carefully analyze their meaning to ensure test case correctness
- When writing or modifying tests, check existing test cases to ensure they have scientific naming and rigorous logic testing, if not compliant, modify test cases to ensure scientific and rigorous testing
- After each development completion, first git add . then git commit -m "feat: feature description" or "fix: issue description", ensure compliance with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
- After each development completion, first git push to remote repository
- After each change completion, summarize the changes, do not create summary files, provide a brief change description, ensure compliance with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
- Provide change descriptions needed for PR in the conversation, ensure compliance with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
+372
View File
@@ -44,3 +44,375 @@ impl Error {
Self::Custom(msg.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error as StdError;
use std::io;
use tokio::sync::mpsc;
#[test]
fn test_error_display() {
// 测试错误消息的显示
let custom_error = Error::custom("test message");
assert_eq!(custom_error.to_string(), "Custom error: test message");
let feature_error = Error::FeatureDisabled("test feature");
assert_eq!(feature_error.to_string(), "Feature disabled: test feature");
let event_bus_error = Error::EventBusStarted;
assert_eq!(event_bus_error.to_string(), "Event bus already started");
let missing_field_error = Error::MissingField("required_field");
assert_eq!(missing_field_error.to_string(), "necessary fields are missing:required_field");
let validation_error = Error::ValidationError("invalid format");
assert_eq!(validation_error.to_string(), "field verification failed:invalid format");
let config_error = Error::ConfigError("invalid config".to_string());
assert_eq!(config_error.to_string(), "Configuration error: invalid config");
}
#[test]
fn test_error_debug() {
// 测试错误的Debug实现
let custom_error = Error::custom("debug test");
let debug_str = format!("{:?}", custom_error);
assert!(debug_str.contains("Custom"));
assert!(debug_str.contains("debug test"));
let feature_error = Error::FeatureDisabled("debug feature");
let debug_str = format!("{:?}", feature_error);
assert!(debug_str.contains("FeatureDisabled"));
assert!(debug_str.contains("debug feature"));
}
#[test]
fn test_custom_error_creation() {
// 测试自定义错误的创建
let error = Error::custom("test custom error");
match error {
Error::Custom(msg) => assert_eq!(msg, "test custom error"),
_ => panic!("Expected Custom error variant"),
}
// 测试空字符串
let empty_error = Error::custom("");
match empty_error {
Error::Custom(msg) => assert_eq!(msg, ""),
_ => panic!("Expected Custom error variant"),
}
// 测试特殊字符
let special_error = Error::custom("测试中文 & special chars: !@#$%");
match special_error {
Error::Custom(msg) => assert_eq!(msg, "测试中文 & special chars: !@#$%"),
_ => panic!("Expected Custom error variant"),
}
}
#[test]
fn test_io_error_conversion() {
// 测试IO错误的转换
let io_error = io::Error::new(io::ErrorKind::NotFound, "file not found");
let converted_error: Error = io_error.into();
match converted_error {
Error::Io(err) => {
assert_eq!(err.kind(), io::ErrorKind::NotFound);
assert_eq!(err.to_string(), "file not found");
}
_ => panic!("Expected Io error variant"),
}
// 测试不同类型的IO错误
let permission_error = io::Error::new(io::ErrorKind::PermissionDenied, "access denied");
let converted: Error = permission_error.into();
assert!(matches!(converted, Error::Io(_)));
}
#[test]
fn test_serde_error_conversion() {
// 测试序列化错误的转换
let invalid_json = r#"{"invalid": json}"#;
let serde_error = serde_json::from_str::<serde_json::Value>(invalid_json).unwrap_err();
let converted_error: Error = serde_error.into();
match converted_error {
Error::Serde(_) => {
// 验证错误类型正确
assert!(converted_error.to_string().contains("Serialization error"));
}
_ => panic!("Expected Serde error variant"),
}
}
#[test]
fn test_config_error_conversion() {
// 测试配置错误的转换
let config_error = ConfigError::Message("invalid configuration".to_string());
let converted_error: Error = config_error.into();
match converted_error {
Error::Config(_) => {
assert!(converted_error.to_string().contains("Configuration loading error"));
}
_ => panic!("Expected Config error variant"),
}
}
#[tokio::test]
async fn test_channel_send_error_conversion() {
// 测试通道发送错误的转换
let (tx, rx) = mpsc::channel::<crate::event::Event>(1);
drop(rx); // 关闭接收端
// 创建一个测试事件
use crate::event::{Name, Metadata, Source, Bucket, Object, Identity};
use std::collections::HashMap;
let identity = Identity::new("test-user".to_string());
let bucket = Bucket::new("test-bucket".to_string(), identity.clone(), "arn:aws:s3:::test-bucket".to_string());
let object = Object::new(
"test-key".to_string(),
Some(1024),
Some("etag123".to_string()),
Some("text/plain".to_string()),
Some(HashMap::new()),
None,
"sequencer123".to_string(),
);
let metadata = Metadata::create("1.0".to_string(), "config1".to_string(), bucket, object);
let source = Source::new("localhost".to_string(), "8080".to_string(), "test-agent".to_string());
let test_event = crate::event::Event::builder()
.event_name(Name::ObjectCreatedPut)
.s3(metadata)
.source(source)
.build()
.unwrap();
let send_result = tx.send(test_event).await;
assert!(send_result.is_err());
let send_error = send_result.unwrap_err();
let boxed_error = Box::new(send_error);
let converted_error: Error = boxed_error.into();
match converted_error {
Error::ChannelSend(_) => {
assert!(converted_error.to_string().contains("Channel send error"));
}
_ => panic!("Expected ChannelSend error variant"),
}
}
#[test]
fn test_error_source_chain() {
// 测试错误源链
let io_error = io::Error::new(io::ErrorKind::InvalidData, "invalid data");
let converted_error: Error = io_error.into();
// 验证错误源
assert!(converted_error.source().is_some());
let source = converted_error.source().unwrap();
assert_eq!(source.to_string(), "invalid data");
}
#[test]
fn test_error_variants_exhaustive() {
// 测试所有错误变体的创建
let errors = vec![
Error::FeatureDisabled("test"),
Error::EventBusStarted,
Error::MissingField("field"),
Error::ValidationError("validation"),
Error::Custom("custom".to_string()),
Error::ConfigError("config".to_string()),
];
for error in errors {
// 验证每个错误都能正确显示
let error_str = error.to_string();
assert!(!error_str.is_empty());
// 验证每个错误都能正确调试
let debug_str = format!("{:?}", error);
assert!(!debug_str.is_empty());
}
}
#[test]
fn test_error_equality_and_matching() {
// 测试错误的模式匹配
let custom_error = Error::custom("test");
match custom_error {
Error::Custom(msg) => assert_eq!(msg, "test"),
_ => panic!("Pattern matching failed"),
}
let feature_error = Error::FeatureDisabled("feature");
match feature_error {
Error::FeatureDisabled(feature) => assert_eq!(feature, "feature"),
_ => panic!("Pattern matching failed"),
}
let event_bus_error = Error::EventBusStarted;
match event_bus_error {
Error::EventBusStarted => {}, // 正确匹配
_ => panic!("Pattern matching failed"),
}
}
#[test]
fn test_error_message_formatting() {
// 测试错误消息格式化
let test_cases = vec![
(Error::FeatureDisabled("kafka"), "Feature disabled: kafka"),
(Error::MissingField("bucket_name"), "necessary fields are missing:bucket_name"),
(Error::ValidationError("invalid email"), "field verification failed:invalid email"),
(Error::ConfigError("missing file".to_string()), "Configuration error: missing file"),
];
for (error, expected_message) in test_cases {
assert_eq!(error.to_string(), expected_message);
}
}
#[test]
fn test_error_memory_efficiency() {
// 测试错误类型的内存效率
use std::mem;
let size = mem::size_of::<Error>();
// 错误类型应该相对紧凑,考虑到包含多种错误类型,96字节是合理的
assert!(size <= 128, "Error size should be reasonable, got {} bytes", size);
// 测试Option<Error>的大小
let option_size = mem::size_of::<Option<Error>>();
assert!(option_size <= 136, "Option<Error> should be efficient, got {} bytes", option_size);
}
#[test]
fn test_error_thread_safety() {
// 测试错误类型的线程安全性
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
assert_send::<Error>();
assert_sync::<Error>();
}
#[test]
fn test_custom_error_edge_cases() {
// 测试自定义错误的边界情况
let long_message = "a".repeat(1000);
let long_error = Error::custom(&long_message);
match long_error {
Error::Custom(msg) => assert_eq!(msg.len(), 1000),
_ => panic!("Expected Custom error variant"),
}
// 测试包含换行符的消息
let multiline_error = Error::custom("line1\nline2\nline3");
match multiline_error {
Error::Custom(msg) => assert!(msg.contains('\n')),
_ => panic!("Expected Custom error variant"),
}
// 测试包含Unicode字符的消息
let unicode_error = Error::custom("🚀 Unicode test 测试 🎉");
match unicode_error {
Error::Custom(msg) => assert!(msg.contains('🚀')),
_ => panic!("Expected Custom error variant"),
}
}
#[test]
fn test_error_conversion_consistency() {
// 测试错误转换的一致性
let original_io_error = io::Error::new(io::ErrorKind::TimedOut, "timeout");
let error_message = original_io_error.to_string();
let converted: Error = original_io_error.into();
// 验证转换后的错误包含原始错误信息
assert!(converted.to_string().contains(&error_message));
}
#[test]
fn test_error_downcast() {
// 测试错误的向下转型
let io_error = io::Error::new(io::ErrorKind::Other, "test error");
let converted: Error = io_error.into();
// 验证可以获取源错误
if let Error::Io(ref inner) = converted {
assert_eq!(inner.to_string(), "test error");
assert_eq!(inner.kind(), io::ErrorKind::Other);
} else {
panic!("Expected Io error variant");
}
}
#[test]
fn test_error_chain_depth() {
// 测试错误链的深度
let root_cause = io::Error::new(io::ErrorKind::Other, "root cause");
let converted: Error = root_cause.into();
let mut depth = 0;
let mut current_error: &dyn StdError = &converted;
while let Some(source) = current_error.source() {
depth += 1;
current_error = source;
// 防止无限循环
if depth > 10 {
break;
}
}
assert!(depth > 0, "Error should have at least one source");
assert!(depth <= 3, "Error chain should not be too deep");
}
#[test]
fn test_static_str_lifetime() {
// 测试静态字符串生命周期
fn create_feature_error() -> Error {
Error::FeatureDisabled("static_feature")
}
let error = create_feature_error();
match error {
Error::FeatureDisabled(feature) => assert_eq!(feature, "static_feature"),
_ => panic!("Expected FeatureDisabled error variant"),
}
}
#[test]
fn test_error_formatting_consistency() {
// 测试错误格式化的一致性
let errors = vec![
Error::FeatureDisabled("test"),
Error::MissingField("field"),
Error::ValidationError("validation"),
Error::Custom("custom".to_string()),
];
for error in errors {
let display_str = error.to_string();
let debug_str = format!("{:?}", error);
// Display和Debug都不应该为空
assert!(!display_str.is_empty());
assert!(!debug_str.is_empty());
// Debug输出通常包含更多信息,但不是绝对的
// 这里我们只验证两者都有内容即可
assert!(debug_str.len() > 0);
assert!(display_str.len() > 0);
}
}
}