rustchain/core/
error.rs

1use serde::{Deserialize, Serialize};
2use thiserror::Error;
3
4/// Structured configuration error types
5#[derive(Debug, Error, Clone, Serialize, Deserialize)]
6pub enum ConfigError {
7    #[error("Missing required configuration key: {key}")]
8    MissingKey { key: String },
9    #[error("Invalid configuration value for {key}: expected {expected}, got {actual}")]
10    InvalidValue {
11        key: String,
12        expected: String,
13        actual: String,
14    },
15    #[error("Configuration file not found: {path}")]
16    FileNotFound { path: String },
17    #[error("Failed to parse configuration: {reason}")]
18    ParseError { reason: String },
19    #[error("Plugin error: {message}")]
20    PluginError { message: String },
21}
22
23/// Structured LLM error types
24#[derive(Debug, Error, Clone, Serialize, Deserialize)]
25pub enum LlmError {
26    #[error("LLM service unavailable: {provider}")]
27    ServiceUnavailable { provider: String },
28    #[error("Authentication failed for {provider}: {reason}")]
29    AuthenticationFailed { provider: String, reason: String },
30    #[error("Request timeout after {timeout_ms}ms")]
31    Timeout { timeout_ms: u64 },
32    #[error("Invalid prompt: {reason}")]
33    InvalidPrompt { reason: String },
34    #[error("Rate limit exceeded for {provider}: retry after {retry_after_seconds}s")]
35    RateLimitExceeded {
36        provider: String,
37        retry_after_seconds: u64,
38    },
39    #[error("LLM responded with error: {message}")]
40    ResponseError { message: String },
41}
42
43/// Structured memory error types
44#[derive(Debug, Error, Clone, Serialize, Deserialize)]
45pub enum MemoryError {
46    #[error("Memory store not found: {store_id}")]
47    StoreNotFound { store_id: String },
48    #[error("Memory key not found: {key} in store {store_id}")]
49    KeyNotFound { key: String, store_id: String },
50    #[error("Memory store capacity exceeded: {current}/{max}")]
51    CapacityExceeded { current: usize, max: usize },
52    #[error("Invalid memory operation: {operation} on {store_type}")]
53    InvalidOperation {
54        operation: String,
55        store_type: String,
56    },
57    #[error("Memory serialization failed: {reason}")]
58    SerializationFailed { reason: String },
59    #[error("Memory store corrupted: {details}")]
60    Corrupted { details: String },
61}
62
63/// Structured tool error types
64#[derive(Debug, Error, Clone, Serialize, Deserialize)]
65pub enum ToolError {
66    #[error("Tool not found: {tool_name}")]
67    NotFound { tool_name: String },
68    #[error("Tool execution failed: {tool_name} - {reason}")]
69    ExecutionFailed { tool_name: String, reason: String },
70    #[error("Invalid tool parameters for {tool_name}: {details}")]
71    InvalidParameters { tool_name: String, details: String },
72    #[error("Tool timeout: {tool_name} exceeded {timeout_ms}ms")]
73    Timeout { tool_name: String, timeout_ms: u64 },
74    #[error("Tool permission denied: {tool_name} - {reason}")]
75    PermissionDenied { tool_name: String, reason: String },
76    #[error("Tool dependency missing: {tool_name} requires {dependency}")]
77    DependencyMissing {
78        tool_name: String,
79        dependency: String,
80    },
81}
82
83/// Structured execution error types
84#[derive(Debug, Error, Clone, Serialize, Deserialize)]
85pub enum ExecutionError {
86    #[error("Mission not found: {mission_id}")]
87    MissionNotFound { mission_id: String },
88    #[error("Step failed: {step_id} in mission {mission_id} - {reason}")]
89    StepFailed {
90        step_id: String,
91        mission_id: String,
92        reason: String,
93    },
94    #[error("Dependency cycle detected in mission {mission_id}: {cycle}")]
95    DependencyCycle { mission_id: String, cycle: String },
96    #[error("Resource exhausted: {resource} - {details}")]
97    ResourceExhausted { resource: String, details: String },
98    #[error("Execution timeout: mission {mission_id} exceeded {timeout_ms}ms")]
99    Timeout { mission_id: String, timeout_ms: u64 },
100    #[error("Invalid mission state: {state} for operation {operation}")]
101    InvalidState { state: String, operation: String },
102}
103
104/// Structured schema validation error types
105#[derive(Debug, Error, Clone, Serialize, Deserialize)]
106pub enum SchemaError {
107    #[error("Schema validation failed for {schema_name}: {}", errors.join(", "))]
108    ValidationFailed {
109        schema_name: String,
110        errors: Vec<String>,
111    },
112    #[error("Schema not found: {schema_name}")]
113    SchemaNotFound { schema_name: String },
114    #[error("Invalid schema definition: {schema_name} - {reason}")]
115    InvalidDefinition { schema_name: String, reason: String },
116    #[error("Schema version mismatch: expected {expected}, got {actual}")]
117    VersionMismatch { expected: String, actual: String },
118}
119
120/// Main RustChain error enum with structured error types
121#[derive(Debug, Error)]
122pub enum RustChainError {
123    #[error("IO error: {0}")]
124    Io(#[from] std::io::Error),
125
126    #[error("Configuration error: {0}")]
127    Config(#[from] ConfigError),
128
129    #[error("LLM error: {0}")]
130    Llm(#[from] LlmError),
131
132    #[error("Memory error: {0}")]
133    Memory(#[from] MemoryError),
134
135    #[error("Tool error: {0}")]
136    Tool(#[from] ToolError),
137
138    #[error("Execution error: {0}")]
139    Execution(#[from] ExecutionError),
140
141    #[error("Schema validation error: {0}")]
142    Schema(#[from] SchemaError),
143
144    #[error("Security error: {0}")]
145    Security(String),
146
147    #[error("JSON serialization error: {0}")]
148    Json(#[from] serde_json::Error),
149
150    #[error("YAML serialization error: {0}")]
151    Yaml(#[from] serde_yaml::Error),
152
153    #[error("Unknown error: {message}")]
154    Unknown { message: String },
155
156    #[error("Execution error: {0}")]
157    Exec(String),
158}
159
160pub type Result<T> = std::result::Result<T, RustChainError>;
161
162// Implementations for structured error constructors
163impl ConfigError {
164    pub fn missing_key(key: impl Into<String>) -> Self {
165        Self::MissingKey { key: key.into() }
166    }
167
168    pub fn invalid_value(
169        key: impl Into<String>,
170        expected: impl Into<String>,
171        actual: impl Into<String>,
172    ) -> Self {
173        Self::InvalidValue {
174            key: key.into(),
175            expected: expected.into(),
176            actual: actual.into(),
177        }
178    }
179
180    pub fn file_not_found(path: impl Into<String>) -> Self {
181        Self::FileNotFound { path: path.into() }
182    }
183
184    pub fn parse_error(reason: impl Into<String>) -> Self {
185        Self::ParseError {
186            reason: reason.into(),
187        }
188    }
189}
190
191impl LlmError {
192    pub fn service_unavailable(provider: impl Into<String>) -> Self {
193        Self::ServiceUnavailable {
194            provider: provider.into(),
195        }
196    }
197
198    pub fn authentication_failed(provider: impl Into<String>, reason: impl Into<String>) -> Self {
199        Self::AuthenticationFailed {
200            provider: provider.into(),
201            reason: reason.into(),
202        }
203    }
204
205    pub fn timeout(timeout_ms: u64) -> Self {
206        Self::Timeout { timeout_ms }
207    }
208
209    pub fn invalid_prompt(reason: impl Into<String>) -> Self {
210        Self::InvalidPrompt {
211            reason: reason.into(),
212        }
213    }
214
215    pub fn rate_limit_exceeded(provider: impl Into<String>, retry_after_seconds: u64) -> Self {
216        Self::RateLimitExceeded {
217            provider: provider.into(),
218            retry_after_seconds,
219        }
220    }
221
222    pub fn response_error(message: impl Into<String>) -> Self {
223        Self::ResponseError {
224            message: message.into(),
225        }
226    }
227}
228
229impl MemoryError {
230    pub fn store_not_found(store_id: impl Into<String>) -> Self {
231        Self::StoreNotFound {
232            store_id: store_id.into(),
233        }
234    }
235
236    pub fn key_not_found(key: impl Into<String>, store_id: impl Into<String>) -> Self {
237        Self::KeyNotFound {
238            key: key.into(),
239            store_id: store_id.into(),
240        }
241    }
242
243    pub fn capacity_exceeded(current: usize, max: usize) -> Self {
244        Self::CapacityExceeded { current, max }
245    }
246
247    pub fn invalid_operation(operation: impl Into<String>, store_type: impl Into<String>) -> Self {
248        Self::InvalidOperation {
249            operation: operation.into(),
250            store_type: store_type.into(),
251        }
252    }
253
254    pub fn serialization_failed(reason: impl Into<String>) -> Self {
255        Self::SerializationFailed {
256            reason: reason.into(),
257        }
258    }
259
260    pub fn corrupted(details: impl Into<String>) -> Self {
261        Self::Corrupted {
262            details: details.into(),
263        }
264    }
265}
266
267impl ToolError {
268    pub fn not_found(tool_name: impl Into<String>) -> Self {
269        Self::NotFound {
270            tool_name: tool_name.into(),
271        }
272    }
273
274    pub fn execution_failed(tool_name: impl Into<String>, reason: impl Into<String>) -> Self {
275        Self::ExecutionFailed {
276            tool_name: tool_name.into(),
277            reason: reason.into(),
278        }
279    }
280
281    pub fn invalid_parameters(tool_name: impl Into<String>, details: impl Into<String>) -> Self {
282        Self::InvalidParameters {
283            tool_name: tool_name.into(),
284            details: details.into(),
285        }
286    }
287
288    pub fn timeout(tool_name: impl Into<String>, timeout_ms: u64) -> Self {
289        Self::Timeout {
290            tool_name: tool_name.into(),
291            timeout_ms,
292        }
293    }
294
295    pub fn permission_denied(tool_name: impl Into<String>, reason: impl Into<String>) -> Self {
296        Self::PermissionDenied {
297            tool_name: tool_name.into(),
298            reason: reason.into(),
299        }
300    }
301
302    pub fn dependency_missing(tool_name: impl Into<String>, dependency: impl Into<String>) -> Self {
303        Self::DependencyMissing {
304            tool_name: tool_name.into(),
305            dependency: dependency.into(),
306        }
307    }
308}
309
310impl ExecutionError {
311    pub fn mission_not_found(mission_id: impl Into<String>) -> Self {
312        Self::MissionNotFound {
313            mission_id: mission_id.into(),
314        }
315    }
316
317    pub fn step_failed(
318        step_id: impl Into<String>,
319        mission_id: impl Into<String>,
320        reason: impl Into<String>,
321    ) -> Self {
322        Self::StepFailed {
323            step_id: step_id.into(),
324            mission_id: mission_id.into(),
325            reason: reason.into(),
326        }
327    }
328
329    pub fn dependency_cycle(mission_id: impl Into<String>, cycle: impl Into<String>) -> Self {
330        Self::DependencyCycle {
331            mission_id: mission_id.into(),
332            cycle: cycle.into(),
333        }
334    }
335
336    pub fn resource_exhausted(resource: impl Into<String>, details: impl Into<String>) -> Self {
337        Self::ResourceExhausted {
338            resource: resource.into(),
339            details: details.into(),
340        }
341    }
342
343    pub fn timeout(mission_id: impl Into<String>, timeout_ms: u64) -> Self {
344        Self::Timeout {
345            mission_id: mission_id.into(),
346            timeout_ms,
347        }
348    }
349
350    pub fn invalid_state(state: impl Into<String>, operation: impl Into<String>) -> Self {
351        Self::InvalidState {
352            state: state.into(),
353            operation: operation.into(),
354        }
355    }
356}
357
358impl SchemaError {
359    pub fn validation_failed(schema_name: impl Into<String>, errors: Vec<String>) -> Self {
360        Self::ValidationFailed {
361            schema_name: schema_name.into(),
362            errors,
363        }
364    }
365
366    pub fn schema_not_found(schema_name: impl Into<String>) -> Self {
367        Self::SchemaNotFound {
368            schema_name: schema_name.into(),
369        }
370    }
371
372    pub fn invalid_definition(schema_name: impl Into<String>, reason: impl Into<String>) -> Self {
373        Self::InvalidDefinition {
374            schema_name: schema_name.into(),
375            reason: reason.into(),
376        }
377    }
378
379    pub fn version_mismatch(expected: impl Into<String>, actual: impl Into<String>) -> Self {
380        Self::VersionMismatch {
381            expected: expected.into(),
382            actual: actual.into(),
383        }
384    }
385}
386
387// Keep compatibility for external crates that depend on anyhow
388impl From<anyhow::Error> for RustChainError {
389    fn from(e: anyhow::Error) -> Self {
390        RustChainError::Unknown {
391            message: e.to_string(),
392        }
393    }
394}
395
396// For backward compatibility during migration - these should be removed eventually
397impl From<String> for RustChainError {
398    fn from(e: String) -> Self {
399        RustChainError::Unknown { message: e }
400    }
401}
402
403impl From<&str> for RustChainError {
404    fn from(e: &str) -> Self {
405        RustChainError::Unknown {
406            message: e.to_string(),
407        }
408    }
409}
410
411#[cfg(test)]
412mod tests {
413    use super::*;
414    use serde_json;
415
416    #[test]
417    fn test_config_error_variants() {
418        let missing = ConfigError::missing_key("api_key");
419        assert!(missing.to_string().contains("Missing required configuration key: api_key"));
420
421        let invalid = ConfigError::invalid_value("timeout", "number", "string");
422        assert!(invalid.to_string().contains("Invalid configuration value for timeout"));
423        assert!(invalid.to_string().contains("expected number, got string"));
424
425        let not_found = ConfigError::file_not_found("/path/to/config.toml");
426        assert!(not_found.to_string().contains("Configuration file not found: /path/to/config.toml"));
427
428        let parse = ConfigError::parse_error("invalid TOML syntax");
429        assert!(parse.to_string().contains("Failed to parse configuration: invalid TOML syntax"));
430    }
431
432    #[test]
433    fn test_config_error_serialization() {
434        let error = ConfigError::missing_key("test_key");
435        
436        // Test serialization
437        let serialized = serde_json::to_string(&error).unwrap();
438        assert!(serialized.contains("MissingKey"));
439        assert!(serialized.contains("test_key"));
440        
441        // Test deserialization
442        let deserialized: ConfigError = serde_json::from_str(&serialized).unwrap();
443        match deserialized {
444            ConfigError::MissingKey { key } => assert_eq!(key, "test_key"),
445            _ => panic!("Deserialization failed"),
446        }
447    }
448
449    #[test]
450    fn test_llm_error_variants() {
451        let unavailable = LlmError::service_unavailable("openai");
452        assert!(unavailable.to_string().contains("LLM service unavailable: openai"));
453
454        let auth_failed = LlmError::authentication_failed("anthropic", "invalid API key");
455        assert!(auth_failed.to_string().contains("Authentication failed for anthropic"));
456        assert!(auth_failed.to_string().contains("invalid API key"));
457
458        let timeout = LlmError::timeout(30000);
459        assert!(timeout.to_string().contains("Request timeout after 30000ms"));
460
461        let invalid_prompt = LlmError::invalid_prompt("empty prompt");
462        assert!(invalid_prompt.to_string().contains("Invalid prompt: empty prompt"));
463
464        let rate_limit = LlmError::rate_limit_exceeded("openai", 60);
465        assert!(rate_limit.to_string().contains("Rate limit exceeded for openai"));
466        assert!(rate_limit.to_string().contains("retry after 60s"));
467
468        let response_error = LlmError::response_error("model returned error");
469        assert!(response_error.to_string().contains("LLM responded with error: model returned error"));
470    }
471
472    #[test]
473    fn test_llm_error_serialization() {
474        let error = LlmError::timeout(5000);
475        
476        let serialized = serde_json::to_string(&error).unwrap();
477        assert!(serialized.contains("Timeout"));
478        assert!(serialized.contains("5000"));
479        
480        let deserialized: LlmError = serde_json::from_str(&serialized).unwrap();
481        match deserialized {
482            LlmError::Timeout { timeout_ms } => assert_eq!(timeout_ms, 5000),
483            _ => panic!("Deserialization failed"),
484        }
485    }
486
487    #[test]
488    fn test_memory_error_variants() {
489        let store_not_found = MemoryError::store_not_found("main_store");
490        assert!(store_not_found.to_string().contains("Memory store not found: main_store"));
491
492        let key_not_found = MemoryError::key_not_found("user_data", "session_store");
493        assert!(key_not_found.to_string().contains("Memory key not found: user_data in store session_store"));
494
495        let capacity = MemoryError::capacity_exceeded(150, 100);
496        assert!(capacity.to_string().contains("Memory store capacity exceeded: 150/100"));
497
498        let invalid_op = MemoryError::invalid_operation("delete", "read_only");
499        assert!(invalid_op.to_string().contains("Invalid memory operation: delete on read_only"));
500
501        let serialization = MemoryError::serialization_failed("invalid JSON");
502        assert!(serialization.to_string().contains("Memory serialization failed: invalid JSON"));
503
504        let corrupted = MemoryError::corrupted("checksum mismatch");
505        assert!(corrupted.to_string().contains("Memory store corrupted: checksum mismatch"));
506    }
507
508    #[test]
509    fn test_memory_error_serialization() {
510        let error = MemoryError::capacity_exceeded(200, 100);
511        
512        let serialized = serde_json::to_string(&error).unwrap();
513        assert!(serialized.contains("CapacityExceeded"));
514        assert!(serialized.contains("200"));
515        assert!(serialized.contains("100"));
516        
517        let deserialized: MemoryError = serde_json::from_str(&serialized).unwrap();
518        match deserialized {
519            MemoryError::CapacityExceeded { current, max } => {
520                assert_eq!(current, 200);
521                assert_eq!(max, 100);
522            },
523            _ => panic!("Deserialization failed"),
524        }
525    }
526
527    #[test]
528    fn test_tool_error_variants() {
529        let not_found = ToolError::not_found("missing_tool");
530        assert!(not_found.to_string().contains("Tool not found: missing_tool"));
531
532        let execution_failed = ToolError::execution_failed("file_reader", "permission denied");
533        assert!(execution_failed.to_string().contains("Tool execution failed: file_reader - permission denied"));
534
535        let invalid_params = ToolError::invalid_parameters("calculator", "missing operands");
536        assert!(invalid_params.to_string().contains("Invalid tool parameters for calculator: missing operands"));
537
538        let timeout = ToolError::timeout("slow_tool", 10000);
539        assert!(timeout.to_string().contains("Tool timeout: slow_tool exceeded 10000ms"));
540
541        let permission = ToolError::permission_denied("system_tool", "insufficient privileges");
542        assert!(permission.to_string().contains("Tool permission denied: system_tool - insufficient privileges"));
543
544        let dependency = ToolError::dependency_missing("advanced_tool", "basic_tool");
545        assert!(dependency.to_string().contains("Tool dependency missing: advanced_tool requires basic_tool"));
546    }
547
548    #[test]
549    fn test_tool_error_serialization() {
550        let error = ToolError::timeout("test_tool", 5000);
551        
552        let serialized = serde_json::to_string(&error).unwrap();
553        assert!(serialized.contains("Timeout"));
554        assert!(serialized.contains("test_tool"));
555        assert!(serialized.contains("5000"));
556        
557        let deserialized: ToolError = serde_json::from_str(&serialized).unwrap();
558        match deserialized {
559            ToolError::Timeout { tool_name, timeout_ms } => {
560                assert_eq!(tool_name, "test_tool");
561                assert_eq!(timeout_ms, 5000);
562            },
563            _ => panic!("Deserialization failed"),
564        }
565    }
566
567    #[test]
568    fn test_execution_error_variants() {
569        let mission_not_found = ExecutionError::mission_not_found("mission_123");
570        assert!(mission_not_found.to_string().contains("Mission not found: mission_123"));
571
572        let step_failed = ExecutionError::step_failed("step_1", "mission_456", "validation error");
573        assert!(step_failed.to_string().contains("Step failed: step_1 in mission mission_456 - validation error"));
574
575        let cycle = ExecutionError::dependency_cycle("mission_789", "A -> B -> C -> A");
576        assert!(cycle.to_string().contains("Dependency cycle detected in mission mission_789: A -> B -> C -> A"));
577
578        let resource = ExecutionError::resource_exhausted("memory", "8GB limit reached");
579        assert!(resource.to_string().contains("Resource exhausted: memory - 8GB limit reached"));
580
581        let timeout = ExecutionError::timeout("mission_abc", 60000);
582        assert!(timeout.to_string().contains("Execution timeout: mission mission_abc exceeded 60000ms"));
583
584        let invalid_state = ExecutionError::invalid_state("running", "pause");
585        assert!(invalid_state.to_string().contains("Invalid mission state: running for operation pause"));
586    }
587
588    #[test]
589    fn test_execution_error_serialization() {
590        let error = ExecutionError::step_failed("test_step", "test_mission", "test_reason");
591        
592        let serialized = serde_json::to_string(&error).unwrap();
593        assert!(serialized.contains("StepFailed"));
594        assert!(serialized.contains("test_step"));
595        assert!(serialized.contains("test_mission"));
596        assert!(serialized.contains("test_reason"));
597        
598        let deserialized: ExecutionError = serde_json::from_str(&serialized).unwrap();
599        match deserialized {
600            ExecutionError::StepFailed { step_id, mission_id, reason } => {
601                assert_eq!(step_id, "test_step");
602                assert_eq!(mission_id, "test_mission");
603                assert_eq!(reason, "test_reason");
604            },
605            _ => panic!("Deserialization failed"),
606        }
607    }
608
609    #[test]
610    fn test_schema_error_variants() {
611        let validation_failed = SchemaError::validation_failed("user_schema", vec![
612            "missing field 'name'".to_string(),
613            "invalid email format".to_string(),
614        ]);
615        assert!(validation_failed.to_string().contains("Schema validation failed for user_schema"));
616        assert!(validation_failed.to_string().contains("missing field 'name'"));
617        assert!(validation_failed.to_string().contains("invalid email format"));
618
619        let not_found = SchemaError::schema_not_found("unknown_schema");
620        assert!(not_found.to_string().contains("Schema not found: unknown_schema"));
621
622        let invalid_def = SchemaError::invalid_definition("bad_schema", "circular reference");
623        assert!(invalid_def.to_string().contains("Invalid schema definition: bad_schema - circular reference"));
624
625        let version_mismatch = SchemaError::version_mismatch("2.0", "1.5");
626        assert!(version_mismatch.to_string().contains("Schema version mismatch: expected 2.0, got 1.5"));
627    }
628
629    #[test]
630    fn test_schema_error_serialization() {
631        let error = SchemaError::validation_failed("test_schema", vec!["error1".to_string(), "error2".to_string()]);
632        
633        let serialized = serde_json::to_string(&error).unwrap();
634        assert!(serialized.contains("ValidationFailed"));
635        assert!(serialized.contains("test_schema"));
636        assert!(serialized.contains("error1"));
637        assert!(serialized.contains("error2"));
638        
639        let deserialized: SchemaError = serde_json::from_str(&serialized).unwrap();
640        match deserialized {
641            SchemaError::ValidationFailed { schema_name, errors } => {
642                assert_eq!(schema_name, "test_schema");
643                assert_eq!(errors, vec!["error1", "error2"]);
644            },
645            _ => panic!("Deserialization failed"),
646        }
647    }
648
649    #[test]
650    fn test_rustchain_error_variants() {
651        // Test IO error conversion
652        let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
653        let rustchain_io: RustChainError = io_error.into();
654        assert!(rustchain_io.to_string().contains("IO error"));
655
656        // Test structured error conversions
657        let config_error = ConfigError::missing_key("test");
658        let rustchain_config: RustChainError = config_error.into();
659        assert!(rustchain_config.to_string().contains("Configuration error"));
660
661        let llm_error = LlmError::timeout(1000);
662        let rustchain_llm: RustChainError = llm_error.into();
663        assert!(rustchain_llm.to_string().contains("LLM error"));
664
665        let memory_error = MemoryError::store_not_found("test");
666        let rustchain_memory: RustChainError = memory_error.into();
667        assert!(rustchain_memory.to_string().contains("Memory error"));
668
669        let tool_error = ToolError::not_found("test");
670        let rustchain_tool: RustChainError = tool_error.into();
671        assert!(rustchain_tool.to_string().contains("Tool error"));
672
673        let execution_error = ExecutionError::mission_not_found("test");
674        let rustchain_execution: RustChainError = execution_error.into();
675        assert!(rustchain_execution.to_string().contains("Execution error"));
676
677        let schema_error = SchemaError::schema_not_found("test");
678        let rustchain_schema: RustChainError = schema_error.into();
679        assert!(rustchain_schema.to_string().contains("Schema validation error"));
680    }
681
682    #[test]
683    fn test_rustchain_error_json_yaml() {
684        // Test JSON error conversion
685        let json_error = serde_json::from_str::<serde_json::Value>("invalid json");
686        assert!(json_error.is_err());
687        let rustchain_json: RustChainError = json_error.unwrap_err().into();
688        assert!(rustchain_json.to_string().contains("JSON serialization error"));
689
690        // Test YAML error conversion - create a YAML error by deserializing invalid YAML
691        let yaml_error = serde_yaml::from_str::<serde_yaml::Value>("invalid: yaml: structure");
692        assert!(yaml_error.is_err());
693        let rustchain_yaml: RustChainError = yaml_error.unwrap_err().into();
694        assert!(rustchain_yaml.to_string().contains("YAML serialization error"));
695    }
696
697    #[test]
698    fn test_rustchain_error_unknown() {
699        let unknown = RustChainError::Unknown {
700            message: "test unknown error".to_string(),
701        };
702        assert!(unknown.to_string().contains("Unknown error: test unknown error"));
703    }
704
705    #[test]
706    fn test_rustchain_error_exec() {
707        let exec = RustChainError::Exec("execution failed".to_string());
708        assert!(exec.to_string().contains("Execution error: execution failed"));
709    }
710
711    #[test]
712    fn test_anyhow_conversion() {
713        let anyhow_error = anyhow::anyhow!("test anyhow error");
714        let rustchain_error: RustChainError = anyhow_error.into();
715        
716        match rustchain_error {
717            RustChainError::Unknown { message } => {
718                assert_eq!(message, "test anyhow error");
719            },
720            _ => panic!("Expected Unknown variant"),
721        }
722    }
723
724    #[test]
725    fn test_string_conversions() {
726        // Test String conversion
727        let string_error = "string error".to_string();
728        let rustchain_string: RustChainError = string_error.into();
729        
730        match rustchain_string {
731            RustChainError::Unknown { message } => {
732                assert_eq!(message, "string error");
733            },
734            _ => panic!("Expected Unknown variant"),
735        }
736
737        // Test &str conversion
738        let str_error = "str error";
739        let rustchain_str: RustChainError = str_error.into();
740        
741        match rustchain_str {
742            RustChainError::Unknown { message } => {
743                assert_eq!(message, "str error");
744            },
745            _ => panic!("Expected Unknown variant"),
746        }
747    }
748
749    #[test]
750    fn test_error_cloning() {
751        // Test that all structured errors can be cloned
752        let config = ConfigError::missing_key("test");
753        let _config_clone = config.clone();
754
755        let llm = LlmError::timeout(1000);
756        let _llm_clone = llm.clone();
757
758        let memory = MemoryError::store_not_found("test");
759        let _memory_clone = memory.clone();
760
761        let tool = ToolError::not_found("test");
762        let _tool_clone = tool.clone();
763
764        let execution = ExecutionError::mission_not_found("test");
765        let _execution_clone = execution.clone();
766
767        let schema = SchemaError::schema_not_found("test");
768        let _schema_clone = schema.clone();
769    }
770
771    #[test]
772    fn test_error_debug_formatting() {
773        let config_error = ConfigError::missing_key("api_key");
774        let debug_string = format!("{:?}", config_error);
775        assert!(debug_string.contains("MissingKey"));
776        assert!(debug_string.contains("api_key"));
777
778        let llm_error = LlmError::timeout(5000);
779        let debug_string = format!("{:?}", llm_error);
780        assert!(debug_string.contains("Timeout"));
781        assert!(debug_string.contains("5000"));
782    }
783
784    #[test]
785    fn test_result_type_alias() {
786        // Test that the Result type alias works correctly
787        fn test_function() -> Result<String> {
788            Ok("success".to_string())
789        }
790
791        fn test_error_function() -> Result<String> {
792            Err(ConfigError::missing_key("test").into())
793        }
794
795        let success = test_function();
796        assert!(success.is_ok());
797        assert_eq!(success.unwrap(), "success");
798
799        let error = test_error_function();
800        assert!(error.is_err());
801        
802        match error.unwrap_err() {
803            RustChainError::Config(ConfigError::MissingKey { key }) => {
804                assert_eq!(key, "test");
805            },
806            _ => panic!("Expected Config error"),
807        }
808    }
809
810    #[test]
811    fn test_complex_error_scenarios() {
812        // Test nested error scenarios that might occur in real usage
813        
814        // Complex validation error with multiple issues
815        let validation_errors = vec![
816            "field 'name' is required".to_string(),
817            "field 'email' must be valid email address".to_string(),
818            "field 'age' must be between 0 and 150".to_string(),
819        ];
820        let schema_error = SchemaError::validation_failed("user_registration", validation_errors);
821        let rustchain_error: RustChainError = schema_error.into();
822        
823        let error_string = rustchain_error.to_string();
824        assert!(error_string.contains("field 'name' is required"));
825        assert!(error_string.contains("field 'email' must be valid"));
826        assert!(error_string.contains("field 'age' must be between"));
827
828        // Complex execution error with detailed context
829        let execution_error = ExecutionError::step_failed(
830            "data_validation_step",
831            "user_onboarding_mission",
832            "Schema validation failed: multiple field errors"
833        );
834        
835        let error_string = execution_error.to_string();
836        assert!(error_string.contains("data_validation_step"));
837        assert!(error_string.contains("user_onboarding_mission"));
838        assert!(error_string.contains("Schema validation failed"));
839    }
840
841    #[test]
842    fn test_error_constructor_functions() {
843        // Test that all constructor functions work correctly with various input types
844        
845        // Test with &str inputs
846        let config1 = ConfigError::missing_key("test_key");
847        let config2 = ConfigError::invalid_value("timeout", "u64", "string");
848        assert!(config1.to_string().contains("test_key"));
849        assert!(config2.to_string().contains("timeout"));
850
851        // Test with String inputs  
852        let config3 = ConfigError::missing_key("test_key".to_string());
853        let config4 = ConfigError::file_not_found("/path/to/file".to_string());
854        assert!(config3.to_string().contains("test_key"));
855        assert!(config4.to_string().contains("/path/to/file"));
856
857        // Test LLM constructors
858        let llm1 = LlmError::service_unavailable("openai");
859        let llm2 = LlmError::authentication_failed("anthropic".to_string(), "invalid key".to_string());
860        assert!(llm1.to_string().contains("openai"));
861        assert!(llm2.to_string().contains("anthropic"));
862        assert!(llm2.to_string().contains("invalid key"));
863
864        // Test Memory constructors
865        let mem1 = MemoryError::key_not_found("user_data", "session_store");
866        let mem2 = MemoryError::invalid_operation("delete".to_string(), "readonly".to_string());
867        assert!(mem1.to_string().contains("user_data"));
868        assert!(mem1.to_string().contains("session_store"));
869        assert!(mem2.to_string().contains("delete"));
870        assert!(mem2.to_string().contains("readonly"));
871
872        // Test Tool constructors
873        let tool1 = ToolError::execution_failed("file_tool", "permission denied");
874        let tool2 = ToolError::dependency_missing("advanced_tool".to_string(), "basic_tool".to_string());
875        assert!(tool1.to_string().contains("file_tool"));
876        assert!(tool1.to_string().contains("permission denied"));
877        assert!(tool2.to_string().contains("advanced_tool"));
878        assert!(tool2.to_string().contains("basic_tool"));
879
880        // Test Execution constructors
881        let exec1 = ExecutionError::step_failed("step1", "mission1", "failed");
882        let exec2 = ExecutionError::dependency_cycle("mission2".to_string(), "A->B->A".to_string());
883        assert!(exec1.to_string().contains("step1"));
884        assert!(exec1.to_string().contains("mission1"));
885        assert!(exec1.to_string().contains("failed"));
886        assert!(exec2.to_string().contains("mission2"));
887        assert!(exec2.to_string().contains("A->B->A"));
888
889        // Test Schema constructors
890        let schema1 = SchemaError::schema_not_found("missing_schema");
891        let schema2 = SchemaError::version_mismatch("2.0".to_string(), "1.0".to_string());
892        assert!(schema1.to_string().contains("missing_schema"));
893        assert!(schema2.to_string().contains("expected 2.0, got 1.0"));
894    }
895
896    #[test]
897    fn test_edge_cases() {
898        // Test with empty strings
899        let config_empty = ConfigError::missing_key("");
900        assert!(config_empty.to_string().contains("Missing required configuration key: "));
901
902        let llm_empty = LlmError::service_unavailable("");
903        assert!(llm_empty.to_string().contains("LLM service unavailable: "));
904
905        // Test with very long strings
906        let long_string = "a".repeat(1000);
907        let config_long = ConfigError::parse_error(&long_string);
908        assert!(config_long.to_string().contains(&long_string));
909
910        // Test with special characters
911        let special_chars = "test@#$%^&*()_+-=[]{}|;':\",./<>?";
912        let memory_special = MemoryError::store_not_found(special_chars);
913        assert!(memory_special.to_string().contains(special_chars));
914
915        // Test zero values
916        let timeout_zero = LlmError::timeout(0);
917        assert!(timeout_zero.to_string().contains("timeout after 0ms"));
918
919        let capacity_zero = MemoryError::capacity_exceeded(0, 0);
920        assert!(capacity_zero.to_string().contains("0/0"));
921
922        // Test very large values
923        let timeout_large = ToolError::timeout("tool", u64::MAX);
924        assert!(timeout_large.to_string().contains(&u64::MAX.to_string()));
925    }
926}