use chrono::{Duration, Utc}; use vapora_agents::{ExecutionData, LearningProfile, TaskTypeExpertise}; #[test] fn test_per_task_type_expertise() { let mut profile = LearningProfile::new("agent-1".to_string()); let coding_expertise = TaskTypeExpertise { success_rate: 0.9, total_executions: 20, recent_success_rate: 0.95, avg_duration_ms: 120.0, learning_curve: Vec::new(), confidence: 1.0, }; profile.set_task_type_expertise("coding".to_string(), coding_expertise); assert_eq!(profile.get_task_type_score("coding"), 0.9); assert_eq!(profile.get_task_type_score("documentation"), 0.5); // Default } #[test] fn test_recency_bias_weighting() { let now = Utc::now(); let executions = vec![ ExecutionData { timestamp: now - Duration::hours(1), duration_ms: 100, success: true, }, ExecutionData { timestamp: now - Duration::days(8), duration_ms: 100, success: false, }, ]; let expertise = TaskTypeExpertise::from_executions(executions, "coding"); // Recent success should pull average up despite old failure assert!(expertise.recent_success_rate > 0.5); assert!(expertise.recent_success_rate > expertise.success_rate); } #[test] fn test_confidence_scaling() { let now = Utc::now(); // Few executions = low confidence let few_executions = vec![ExecutionData { timestamp: now, duration_ms: 100, success: true, }]; let few_expertise = TaskTypeExpertise::from_executions(few_executions, "coding"); assert!(few_expertise.confidence < 0.1); // Many executions = high confidence let many_executions: Vec<_> = (0..50) .map(|i| ExecutionData { timestamp: now - Duration::hours(i), duration_ms: 100, success: i % 2 == 0, }) .collect(); let many_expertise = TaskTypeExpertise::from_executions(many_executions, "coding"); assert_eq!(many_expertise.confidence, 1.0); // Capped at 1.0 } #[test] fn test_learning_curve_generation() { let now = Utc::now(); let executions = vec![ ExecutionData { timestamp: now - Duration::hours(25), duration_ms: 100, success: true, }, ExecutionData { timestamp: now - Duration::hours(24), duration_ms: 100, success: true, }, ExecutionData { timestamp: now - Duration::hours(1), duration_ms: 100, success: false, }, ]; let expertise = TaskTypeExpertise::from_executions(executions, "coding"); assert!(!expertise.learning_curve.is_empty()); // Curve should be chronologically sorted for i in 1..expertise.learning_curve.len() { assert!( expertise.learning_curve[i - 1].0 <= expertise.learning_curve[i].0, "Learning curve must be chronologically sorted" ); } } #[test] fn test_execution_update() { let now = Utc::now(); let mut expertise = TaskTypeExpertise { success_rate: 0.8, total_executions: 10, recent_success_rate: 0.8, avg_duration_ms: 100.0, learning_curve: Vec::new(), confidence: 0.5, }; let execution = ExecutionData { timestamp: now, duration_ms: 150, success: true, }; expertise.update_with_execution(&execution); assert_eq!(expertise.total_executions, 11); assert!(expertise.success_rate > 0.8); // Success added assert!(expertise.avg_duration_ms > 100.0); // Duration increased } #[test] fn test_multiple_task_types() { let mut profile = LearningProfile::new("agent-1".to_string()); let coding = TaskTypeExpertise { success_rate: 0.95, total_executions: 20, recent_success_rate: 0.95, avg_duration_ms: 120.0, learning_curve: Vec::new(), confidence: 1.0, }; let documentation = TaskTypeExpertise { success_rate: 0.75, total_executions: 15, recent_success_rate: 0.80, avg_duration_ms: 200.0, learning_curve: Vec::new(), confidence: 0.75, }; profile.set_task_type_expertise("coding".to_string(), coding); profile.set_task_type_expertise("documentation".to_string(), documentation); assert_eq!(profile.get_task_type_score("coding"), 0.95); assert_eq!(profile.get_task_type_score("documentation"), 0.75); assert_eq!(profile.get_confidence("coding"), 1.0); assert_eq!(profile.get_confidence("documentation"), 0.75); } #[test] fn test_empty_executions_default() { let expertise = TaskTypeExpertise::from_executions(Vec::new(), "coding"); assert_eq!(expertise.success_rate, 0.5); assert_eq!(expertise.total_executions, 0); assert_eq!(expertise.confidence, 0.0); }