1use crate::drift::{DataDriftConfig, DataDriftEngine};
13use crate::Result;
14use serde::{Deserialize, Serialize};
15use serde_json::Value;
16use std::collections::HashMap;
17use std::sync::Arc;
18use std::time::Duration;
19use tokio::sync::RwLock;
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct LearningConfig {
24 #[serde(default)]
26 pub enabled: bool,
27
28 #[serde(default)]
30 pub mode: LearningMode,
31
32 #[serde(default = "default_learning_rate")]
34 pub sensitivity: f64,
35
36 #[serde(default = "default_decay_rate")]
38 pub decay: f64,
39
40 #[serde(default = "default_min_samples")]
42 pub min_samples: usize,
43
44 #[serde(default = "default_update_interval")]
46 pub update_interval: Duration,
47
48 #[serde(default = "default_true")]
50 pub persona_adaptation: bool,
51
52 #[serde(default = "default_true")]
54 pub traffic_mirroring: bool,
55
56 #[serde(default)]
58 pub endpoint_learning: HashMap<String, bool>,
59
60 #[serde(default)]
62 pub persona_learning: HashMap<String, bool>,
63}
64
65fn default_learning_rate() -> f64 {
66 0.2 }
68
69fn default_decay_rate() -> f64 {
70 0.05 }
72
73fn default_min_samples() -> usize {
74 10 }
76
77fn default_update_interval() -> Duration {
78 Duration::from_secs(60) }
80
81fn default_true() -> bool {
82 true
83}
84
85impl Default for LearningConfig {
86 fn default() -> Self {
87 Self {
88 enabled: false, mode: LearningMode::Behavioral,
90 sensitivity: default_learning_rate(),
91 decay: default_decay_rate(),
92 min_samples: default_min_samples(),
93 update_interval: default_update_interval(),
94 persona_adaptation: true,
95 traffic_mirroring: true,
96 endpoint_learning: HashMap::new(),
97 persona_learning: HashMap::new(),
98 }
99 }
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
104#[serde(rename_all = "snake_case")]
105pub enum LearningMode {
106 #[default]
108 Behavioral,
109 Statistical,
111 Hybrid,
113}
114
115pub struct DriftLearningEngine {
119 drift_engine: DataDriftEngine,
121 learning_config: LearningConfig,
123 traffic_learner: Option<Arc<RwLock<TrafficPatternLearner>>>,
125 persona_learner: Option<Arc<RwLock<PersonaBehaviorLearner>>>,
127 learned_patterns: Arc<RwLock<HashMap<String, LearnedPattern>>>,
129}
130
131#[derive(Debug, Clone)]
133pub struct LearnedPattern {
134 pub pattern_id: String,
136 pub pattern_type: PatternType,
138 pub parameters: HashMap<String, Value>,
140 pub confidence: f64,
142 pub sample_count: usize,
144 pub last_updated: chrono::DateTime<chrono::Utc>,
146}
147
148#[derive(Debug, Clone, PartialEq, Eq)]
150pub enum PatternType {
151 Latency,
153 ErrorRate,
155 RequestSequence,
157 PersonaBehavior,
159}
160
161impl DriftLearningEngine {
162 pub fn new(drift_config: DataDriftConfig, learning_config: LearningConfig) -> Result<Self> {
164 let drift_engine = DataDriftEngine::new(drift_config)?;
165
166 let traffic_learner = if learning_config.traffic_mirroring {
167 Some(Arc::new(RwLock::new(TrafficPatternLearner::new(learning_config.clone())?)))
168 } else {
169 None
170 };
171
172 let persona_learner = if learning_config.persona_adaptation {
173 Some(Arc::new(RwLock::new(PersonaBehaviorLearner::new(learning_config.clone())?)))
174 } else {
175 None
176 };
177
178 Ok(Self {
179 drift_engine,
180 learning_config,
181 traffic_learner,
182 persona_learner,
183 learned_patterns: Arc::new(RwLock::new(HashMap::new())),
184 })
185 }
186
187 pub fn drift_engine(&self) -> &DataDriftEngine {
189 &self.drift_engine
190 }
191
192 pub fn learning_config(&self) -> &LearningConfig {
194 &self.learning_config
195 }
196
197 pub fn update_learning_config(&mut self, config: LearningConfig) -> Result<()> {
199 self.learning_config = config;
200 Ok(())
201 }
202
203 pub async fn get_learned_patterns(&self) -> HashMap<String, LearnedPattern> {
205 self.learned_patterns.read().await.clone()
206 }
207
208 pub async fn apply_drift_with_learning(&self, data: Value) -> Result<Value> {
210 let mut data = self.drift_engine.apply_drift(data).await?;
212
213 if !self.learning_config.enabled {
215 return Ok(data);
216 }
217
218 let patterns = self.learned_patterns.read().await;
220 for (pattern_id, pattern) in patterns.iter() {
221 if pattern.confidence < 0.5 {
223 continue; }
225
226 match pattern.pattern_type {
228 PatternType::Latency => {
229 }
231 PatternType::ErrorRate => {
232 }
234 PatternType::RequestSequence => {
235 }
237 PatternType::PersonaBehavior => {
238 if let Some(obj) = data.as_object_mut() {
240 for (key, value) in &pattern.parameters {
241 if let Some(existing) = obj.get(key) {
242 let blended =
244 self.blend_values(existing, value, pattern.confidence)?;
245 obj.insert(key.clone(), blended);
246 }
247 }
248 }
249 }
250 }
251 }
252
253 Ok(data)
254 }
255
256 fn blend_values(&self, existing: &Value, learned: &Value, confidence: f64) -> Result<Value> {
258 let weight = confidence * self.learning_config.sensitivity;
260
261 match (existing, learned) {
262 (Value::Number(existing_num), Value::Number(learned_num)) => {
263 if let (Some(existing_f64), Some(learned_f64)) =
264 (existing_num.as_f64(), learned_num.as_f64())
265 {
266 let blended = existing_f64 * (1.0 - weight) + learned_f64 * weight;
267 Ok(Value::from(blended))
268 } else {
269 Ok(existing.clone())
270 }
271 }
272 _ => Ok(existing.clone()), }
274 }
275}
276
277pub struct TrafficPatternLearner {
281 config: LearningConfig,
283 pattern_window: Duration,
285 patterns: HashMap<String, TrafficPattern>,
287}
288
289#[derive(Debug, Clone)]
291struct TrafficPattern {
292 pattern_id: String,
294 pattern_type: PatternType,
296 parameters: HashMap<String, Value>,
298 sample_count: usize,
300 first_seen: chrono::DateTime<chrono::Utc>,
302 last_seen: chrono::DateTime<chrono::Utc>,
304}
305
306impl TrafficPatternLearner {
307 pub fn new(config: LearningConfig) -> Result<Self> {
309 Ok(Self {
310 config,
311 pattern_window: Duration::from_secs(3600), patterns: HashMap::new(),
313 })
314 }
315
316 pub async fn analyze_traffic_patterns(
321 &mut self,
322 _database: &dyn std::any::Any, ) -> Result<Vec<LearnedPattern>> {
324 Ok(Vec::new())
326 }
327
328 #[allow(dead_code)]
333 pub async fn detect_latency_patterns_from_requests(
334 &self,
335 _requests: &[serde_json::Value],
336 ) -> Result<Vec<LearnedPattern>> {
337 Ok(Vec::new())
339 }
340
341 #[allow(dead_code)]
430 async fn detect_error_patterns_internal(
431 &self,
432 _requests: &[serde_json::Value],
433 ) -> Result<Vec<LearnedPattern>> {
434 use chrono::Utc;
435 use std::collections::HashMap;
436
437 let _requests = _requests;
439 let endpoint_errors: HashMap<String, (usize, usize)> = HashMap::new(); let mut patterns = Vec::new();
458
459 for (endpoint_key, (total, errors)) in endpoint_errors {
460 if total < 20 {
461 continue;
463 }
464
465 let error_rate = errors as f64 / total as f64;
466
467 if error_rate > 0.05 {
469 let mut parameters = HashMap::new();
470 parameters.insert("endpoint".to_string(), serde_json::json!(endpoint_key));
471 parameters.insert("error_rate".to_string(), serde_json::json!(error_rate));
472 parameters.insert("total_requests".to_string(), serde_json::json!(total));
473 parameters.insert("error_count".to_string(), serde_json::json!(errors));
474
475 let confidence = ((total as f64 / 100.0).min(1.0) * error_rate * 10.0).min(1.0);
477
478 patterns.push(LearnedPattern {
479 pattern_id: format!("error_rate_{}", endpoint_key.replace(['/', ' '], "_")),
480 pattern_type: PatternType::ErrorRate,
481 parameters,
482 confidence,
483 sample_count: total,
484 last_updated: Utc::now(),
485 });
486 }
487 }
488
489 Ok(patterns)
490 }
491
492 #[allow(dead_code)]
495 async fn detect_sequence_patterns_internal(
496 &self,
497 _requests: &[serde_json::Value],
498 ) -> Result<Vec<LearnedPattern>> {
499 use chrono::Utc;
500 use std::collections::HashMap;
501
502 let _requests = _requests;
504 if _requests.len() < 50 {
505 return Ok(Vec::new());
507 }
508
509 let trace_sequences: HashMap<Option<String>, Vec<String>> = HashMap::new();
511
512 let mut sequence_counts: HashMap<String, usize> = HashMap::new();
525
526 for sequence in trace_sequences.values() {
527 if sequence.len() >= 2 {
528 let signature: Vec<String> = sequence.iter().take(3).cloned().collect();
530 let signature_str = signature.join(" -> ");
531 *sequence_counts.entry(signature_str).or_insert(0) += 1;
532 }
533 }
534
535 let mut patterns = Vec::new();
536
537 for (sequence_str, count) in sequence_counts {
538 if count >= 5 {
539 let mut parameters = HashMap::new();
541 parameters.insert("sequence".to_string(), serde_json::json!(sequence_str));
542 parameters.insert("occurrence_count".to_string(), serde_json::json!(count));
543
544 let confidence = (count as f64 / 20.0).min(1.0);
546
547 patterns.push(LearnedPattern {
548 pattern_id: format!(
549 "sequence_{}",
550 sequence_str.replace(['/', ' '], "_").replace("->", "_")
551 ),
552 pattern_type: PatternType::RequestSequence,
553 parameters,
554 confidence,
555 sample_count: count,
556 last_updated: Utc::now(),
557 });
558 }
559 }
560
561 Ok(patterns)
562 }
563
564 pub async fn detect_latency_patterns(&mut self) -> Result<Vec<LearnedPattern>> {
569 Ok(Vec::new())
571 }
572
573 pub async fn detect_error_patterns(&mut self) -> Result<Vec<LearnedPattern>> {
578 Ok(Vec::new())
580 }
581}
582
583pub struct PersonaBehaviorLearner {
587 config: LearningConfig,
589 behavior_history: HashMap<String, Vec<BehaviorEvent>>,
591}
592
593#[derive(Debug, Clone)]
595pub struct BehaviorEvent {
596 pub timestamp: chrono::DateTime<chrono::Utc>,
598 pub event_type: BehaviorEventType,
600 pub data: HashMap<String, Value>,
602}
603
604#[derive(Debug, Clone, PartialEq, Eq)]
606pub enum BehaviorEventType {
607 Request {
609 endpoint: String,
611 method: String,
613 },
614 RequestFailed {
616 endpoint: String,
618 status_code: u16,
620 },
621 RequestSucceededAfterFailure {
623 endpoint: String,
625 },
626 PatternDetected {
628 pattern: String,
630 },
631}
632
633impl PersonaBehaviorLearner {
634 pub fn new(config: LearningConfig) -> Result<Self> {
636 Ok(Self {
637 config,
638 behavior_history: HashMap::new(),
639 })
640 }
641
642 pub fn record_event(&mut self, persona_id: String, event: BehaviorEvent) {
644 if !self.config.enabled {
645 return;
646 }
647
648 if let Some(&enabled) = self.config.persona_learning.get(&persona_id) {
650 if !enabled {
651 return; }
653 }
654
655 let events = self.behavior_history.entry(persona_id).or_default();
656 events.push(event);
657
658 if events.len() > 1000 {
660 events.remove(0);
661 }
662 }
663
664 pub async fn analyze_persona_behavior(
666 &self,
667 persona_id: &str,
668 ) -> Result<Option<LearnedPattern>> {
669 if !self.config.enabled {
670 return Ok(None);
671 }
672
673 let events = match self.behavior_history.get(persona_id) {
674 Some(events) => events,
675 None => return Ok(None),
676 };
677
678 if events.len() < self.config.min_samples {
679 return Ok(None); }
681
682 let mut checkout_after_failure_count = 0;
685 let mut total_failures = 0;
686
687 for i in 1..events.len() {
688 if let BehaviorEventType::RequestFailed { .. } = events[i - 1].event_type {
689 total_failures += 1;
690 if let BehaviorEventType::Request { endpoint, .. } = &events[i].event_type {
691 if endpoint.contains("/checkout") {
692 checkout_after_failure_count += 1;
693 }
694 }
695 }
696 }
697
698 if total_failures > 0 && checkout_after_failure_count as f64 / total_failures as f64 > 0.5 {
699 let mut parameters = HashMap::new();
701 parameters.insert("retry_checkout_after_failure".to_string(), Value::from(true));
702 parameters.insert(
703 "retry_probability".to_string(),
704 Value::from(checkout_after_failure_count as f64 / total_failures as f64),
705 );
706
707 return Ok(Some(LearnedPattern {
708 pattern_id: format!("persona_{}_checkout_retry", persona_id),
709 pattern_type: PatternType::PersonaBehavior,
710 parameters,
711 confidence: (checkout_after_failure_count as f64 / total_failures as f64).min(1.0),
712 sample_count: total_failures,
713 last_updated: chrono::Utc::now(),
714 }));
715 }
716
717 Ok(None)
718 }
719
720 pub fn get_behavior_history(&self, persona_id: &str) -> Option<&Vec<BehaviorEvent>> {
722 self.behavior_history.get(persona_id)
723 }
724
725 pub async fn apply_learned_patterns_to_persona(
730 &self,
731 persona_id: &str,
732 persona_registry: &crate::PersonaRegistry,
733 ) -> Result<()> {
734 if !self.config.enabled {
735 return Ok(());
736 }
737
738 if let Some(pattern) = self.analyze_persona_behavior(persona_id).await? {
740 let mut learned_traits = std::collections::HashMap::new();
742 for (key, value) in &pattern.parameters {
743 let trait_key = format!("learned_{}", key);
744 let trait_value = if let Some(s) = value.as_str() {
745 s.to_string()
746 } else if let Some(n) = value.as_f64() {
747 n.to_string()
748 } else if let Some(b) = value.as_bool() {
749 b.to_string()
750 } else {
751 value.to_string()
752 };
753 learned_traits.insert(trait_key, trait_value);
754 }
755
756 if !learned_traits.is_empty() {
758 persona_registry.update_persona(persona_id, learned_traits)?;
759 }
760 }
761
762 Ok(())
763 }
764}
765
766#[cfg(test)]
767mod tests {
768 use super::*;
769
770 #[test]
775 fn test_learning_config_default() {
776 let config = LearningConfig::default();
777 assert!(!config.enabled); assert_eq!(config.sensitivity, 0.2);
779 assert_eq!(config.min_samples, 10);
780 assert_eq!(config.decay, 0.05);
781 assert!(config.persona_adaptation);
782 assert!(config.traffic_mirroring);
783 }
784
785 #[test]
786 fn test_learning_config_serialize() {
787 let config = LearningConfig {
788 enabled: true,
789 mode: LearningMode::Statistical,
790 ..Default::default()
791 };
792 let json = serde_json::to_string(&config).unwrap();
793 assert!(json.contains("true"));
794 assert!(json.contains("statistical"));
795 }
796
797 #[test]
798 fn test_learning_config_deserialize() {
799 let json = r#"{"enabled": true, "mode": "hybrid", "sensitivity": 0.5}"#;
800 let config: LearningConfig = serde_json::from_str(json).unwrap();
801 assert!(config.enabled);
802 assert_eq!(config.mode, LearningMode::Hybrid);
803 assert!((config.sensitivity - 0.5).abs() < f64::EPSILON);
804 }
805
806 #[test]
807 fn test_learning_config_clone() {
808 let config = LearningConfig {
809 enabled: true,
810 sensitivity: 0.3,
811 ..Default::default()
812 };
813 let cloned = config.clone();
814 assert!(cloned.enabled);
815 assert!((cloned.sensitivity - 0.3).abs() < f64::EPSILON);
816 }
817
818 #[test]
819 fn test_learning_config_debug() {
820 let config = LearningConfig::default();
821 let debug_str = format!("{:?}", config);
822 assert!(debug_str.contains("sensitivity"));
823 assert!(debug_str.contains("enabled"));
824 }
825
826 #[test]
827 fn test_learning_config_endpoint_learning() {
828 let mut config = LearningConfig::default();
829 config.endpoint_learning.insert("/api/users".to_string(), true);
830 config.endpoint_learning.insert("/api/orders".to_string(), false);
831
832 assert_eq!(config.endpoint_learning.get("/api/users"), Some(&true));
833 assert_eq!(config.endpoint_learning.get("/api/orders"), Some(&false));
834 }
835
836 #[test]
837 fn test_learning_config_persona_learning() {
838 let mut config = LearningConfig::default();
839 config.persona_learning.insert("persona-1".to_string(), true);
840 config.persona_learning.insert("persona-2".to_string(), false);
841
842 assert_eq!(config.persona_learning.get("persona-1"), Some(&true));
843 assert_eq!(config.persona_learning.get("persona-2"), Some(&false));
844 }
845
846 #[test]
851 fn test_learning_mode_default() {
852 let mode = LearningMode::default();
853 assert_eq!(mode, LearningMode::Behavioral);
854 }
855
856 #[test]
857 fn test_learning_mode_eq() {
858 assert_eq!(LearningMode::Statistical, LearningMode::Statistical);
859 assert_ne!(LearningMode::Behavioral, LearningMode::Hybrid);
860 }
861
862 #[test]
863 fn test_learning_mode_serialize() {
864 let mode = LearningMode::Hybrid;
865 let json = serde_json::to_string(&mode).unwrap();
866 assert_eq!(json, "\"hybrid\"");
867 }
868
869 #[test]
870 fn test_learning_mode_deserialize() {
871 let json = "\"statistical\"";
872 let mode: LearningMode = serde_json::from_str(json).unwrap();
873 assert_eq!(mode, LearningMode::Statistical);
874 }
875
876 #[test]
877 fn test_learning_mode_clone() {
878 let mode = LearningMode::Hybrid;
879 let cloned = mode.clone();
880 assert_eq!(cloned, LearningMode::Hybrid);
881 }
882
883 #[test]
884 fn test_learning_mode_debug() {
885 let debug_str = format!("{:?}", LearningMode::Behavioral);
886 assert!(debug_str.contains("Behavioral"));
887 }
888
889 #[test]
894 fn test_drift_learning_engine_creation() {
895 let drift_config = DataDriftConfig::new();
896 let learning_config = LearningConfig::default();
897 let engine = DriftLearningEngine::new(drift_config, learning_config);
898 assert!(engine.is_ok());
899 }
900
901 #[test]
902 fn test_drift_learning_engine_with_traffic_mirroring_disabled() {
903 let drift_config = DataDriftConfig::new();
904 let learning_config = LearningConfig {
905 traffic_mirroring: false,
906 ..Default::default()
907 };
908 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
909 assert!(engine.traffic_learner.is_none());
910 }
911
912 #[test]
913 fn test_drift_learning_engine_with_persona_adaptation_disabled() {
914 let drift_config = DataDriftConfig::new();
915 let learning_config = LearningConfig {
916 persona_adaptation: false,
917 ..Default::default()
918 };
919 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
920 assert!(engine.persona_learner.is_none());
921 }
922
923 #[test]
924 fn test_drift_learning_engine_get_drift_engine() {
925 let drift_config = DataDriftConfig::new();
926 let learning_config = LearningConfig::default();
927 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
928 let _ = engine.drift_engine();
929 }
930
931 #[test]
932 fn test_drift_learning_engine_get_learning_config() {
933 let drift_config = DataDriftConfig::new();
934 let learning_config = LearningConfig {
935 enabled: true,
936 sensitivity: 0.5,
937 ..Default::default()
938 };
939 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
940 assert!(engine.learning_config().enabled);
941 assert!((engine.learning_config().sensitivity - 0.5).abs() < f64::EPSILON);
942 }
943
944 #[test]
945 fn test_drift_learning_engine_update_learning_config() {
946 let drift_config = DataDriftConfig::new();
947 let learning_config = LearningConfig::default();
948 let mut engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
949
950 let new_config = LearningConfig {
951 enabled: true,
952 sensitivity: 0.8,
953 ..Default::default()
954 };
955 engine.update_learning_config(new_config).unwrap();
956
957 assert!(engine.learning_config().enabled);
958 assert!((engine.learning_config().sensitivity - 0.8).abs() < f64::EPSILON);
959 }
960
961 #[tokio::test]
962 async fn test_drift_learning_engine_get_learned_patterns_empty() {
963 let drift_config = DataDriftConfig::new();
964 let learning_config = LearningConfig::default();
965 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
966
967 let patterns = engine.get_learned_patterns().await;
968 assert!(patterns.is_empty());
969 }
970
971 #[tokio::test]
972 async fn test_drift_learning_engine_apply_drift_with_learning_disabled() {
973 let drift_config = DataDriftConfig::new();
974 let learning_config = LearningConfig {
975 enabled: false,
976 ..Default::default()
977 };
978 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
979
980 let data = serde_json::json!({"value": 100});
981 let result = engine.apply_drift_with_learning(data.clone()).await.unwrap();
982 assert!(result.is_object());
984 }
985
986 #[tokio::test]
987 async fn test_drift_learning_engine_apply_drift_with_learning_enabled() {
988 let drift_config = DataDriftConfig::new();
989 let learning_config = LearningConfig {
990 enabled: true,
991 ..Default::default()
992 };
993 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
994
995 let data = serde_json::json!({"value": 100});
996 let result = engine.apply_drift_with_learning(data).await.unwrap();
997 assert!(result.is_object());
998 }
999
1000 #[test]
1001 fn test_drift_learning_engine_blend_values_numeric() {
1002 let drift_config = DataDriftConfig::new();
1003 let learning_config = LearningConfig {
1004 sensitivity: 0.5,
1005 ..Default::default()
1006 };
1007 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
1008
1009 let existing = serde_json::json!(100.0);
1010 let learned = serde_json::json!(200.0);
1011 let result = engine.blend_values(&existing, &learned, 0.5).unwrap();
1012
1013 if let Some(n) = result.as_f64() {
1016 assert!((n - 125.0).abs() < 1.0);
1017 }
1018 }
1019
1020 #[test]
1021 fn test_drift_learning_engine_blend_values_non_numeric() {
1022 let drift_config = DataDriftConfig::new();
1023 let learning_config = LearningConfig::default();
1024 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
1025
1026 let existing = serde_json::json!("original");
1027 let learned = serde_json::json!("learned");
1028 let result = engine.blend_values(&existing, &learned, 0.5).unwrap();
1029
1030 assert_eq!(result, serde_json::json!("original"));
1032 }
1033
1034 #[test]
1039 fn test_learned_pattern_creation() {
1040 let pattern = LearnedPattern {
1041 pattern_id: "test-pattern".to_string(),
1042 pattern_type: PatternType::Latency,
1043 parameters: HashMap::new(),
1044 confidence: 0.9,
1045 sample_count: 100,
1046 last_updated: chrono::Utc::now(),
1047 };
1048 assert_eq!(pattern.pattern_id, "test-pattern");
1049 assert!((pattern.confidence - 0.9).abs() < f64::EPSILON);
1050 }
1051
1052 #[test]
1053 fn test_learned_pattern_clone() {
1054 let pattern = LearnedPattern {
1055 pattern_id: "cloneable".to_string(),
1056 pattern_type: PatternType::ErrorRate,
1057 parameters: HashMap::new(),
1058 confidence: 0.75,
1059 sample_count: 50,
1060 last_updated: chrono::Utc::now(),
1061 };
1062 let cloned = pattern.clone();
1063 assert_eq!(cloned.pattern_id, "cloneable");
1064 }
1065
1066 #[test]
1067 fn test_learned_pattern_debug() {
1068 let pattern = LearnedPattern {
1069 pattern_id: "debug-pattern".to_string(),
1070 pattern_type: PatternType::PersonaBehavior,
1071 parameters: HashMap::new(),
1072 confidence: 0.5,
1073 sample_count: 25,
1074 last_updated: chrono::Utc::now(),
1075 };
1076 let debug_str = format!("{:?}", pattern);
1077 assert!(debug_str.contains("debug-pattern"));
1078 }
1079
1080 #[test]
1085 fn test_pattern_type_eq() {
1086 assert_eq!(PatternType::Latency, PatternType::Latency);
1087 assert_ne!(PatternType::Latency, PatternType::ErrorRate);
1088 }
1089
1090 #[test]
1091 fn test_pattern_type_clone() {
1092 let pt = PatternType::RequestSequence;
1093 let cloned = pt.clone();
1094 assert_eq!(cloned, PatternType::RequestSequence);
1095 }
1096
1097 #[test]
1098 fn test_pattern_type_debug() {
1099 let debug_str = format!("{:?}", PatternType::PersonaBehavior);
1100 assert!(debug_str.contains("PersonaBehavior"));
1101 }
1102
1103 #[test]
1108 fn test_traffic_pattern_learner_new() {
1109 let config = LearningConfig::default();
1110 let learner = TrafficPatternLearner::new(config);
1111 assert!(learner.is_ok());
1112 }
1113
1114 #[tokio::test]
1115 async fn test_traffic_pattern_learner_analyze_traffic_patterns() {
1116 let config = LearningConfig::default();
1117 let mut learner = TrafficPatternLearner::new(config).unwrap();
1118 let patterns = learner.analyze_traffic_patterns(&()).await.unwrap();
1120 assert!(patterns.is_empty());
1121 }
1122
1123 #[tokio::test]
1124 async fn test_traffic_pattern_learner_detect_latency_patterns() {
1125 let config = LearningConfig::default();
1126 let mut learner = TrafficPatternLearner::new(config).unwrap();
1127 let patterns = learner.detect_latency_patterns().await.unwrap();
1128 assert!(patterns.is_empty()); }
1130
1131 #[tokio::test]
1132 async fn test_traffic_pattern_learner_detect_error_patterns() {
1133 let config = LearningConfig::default();
1134 let mut learner = TrafficPatternLearner::new(config).unwrap();
1135 let patterns = learner.detect_error_patterns().await.unwrap();
1136 assert!(patterns.is_empty()); }
1138
1139 #[test]
1144 fn test_persona_behavior_learner_new() {
1145 let config = LearningConfig::default();
1146 let learner = PersonaBehaviorLearner::new(config);
1147 assert!(learner.is_ok());
1148 }
1149
1150 #[test]
1151 fn test_persona_behavior_learner_record_event_disabled() {
1152 let config = LearningConfig {
1153 enabled: false,
1154 ..Default::default()
1155 };
1156 let mut learner = PersonaBehaviorLearner::new(config).unwrap();
1157
1158 learner.record_event(
1159 "persona-1".to_string(),
1160 BehaviorEvent {
1161 timestamp: chrono::Utc::now(),
1162 event_type: BehaviorEventType::Request {
1163 endpoint: "/api/test".to_string(),
1164 method: "GET".to_string(),
1165 },
1166 data: HashMap::new(),
1167 },
1168 );
1169
1170 assert!(learner.get_behavior_history("persona-1").is_none());
1172 }
1173
1174 #[test]
1175 fn test_persona_behavior_learner_record_event_persona_disabled() {
1176 let mut config = LearningConfig {
1177 enabled: true,
1178 ..Default::default()
1179 };
1180 config.persona_learning.insert("persona-1".to_string(), false);
1181 let mut learner = PersonaBehaviorLearner::new(config).unwrap();
1182
1183 learner.record_event(
1184 "persona-1".to_string(),
1185 BehaviorEvent {
1186 timestamp: chrono::Utc::now(),
1187 event_type: BehaviorEventType::Request {
1188 endpoint: "/api/test".to_string(),
1189 method: "GET".to_string(),
1190 },
1191 data: HashMap::new(),
1192 },
1193 );
1194
1195 assert!(learner.get_behavior_history("persona-1").is_none());
1197 }
1198
1199 #[tokio::test]
1200 async fn test_persona_behavior_learner() {
1201 let config = LearningConfig {
1202 enabled: true,
1203 persona_adaptation: true,
1204 ..Default::default()
1205 };
1206 let mut learner = PersonaBehaviorLearner::new(config).unwrap();
1207
1208 learner.record_event(
1210 "persona-1".to_string(),
1211 BehaviorEvent {
1212 timestamp: chrono::Utc::now(),
1213 event_type: BehaviorEventType::RequestFailed {
1214 endpoint: "/api/checkout".to_string(),
1215 status_code: 500,
1216 },
1217 data: HashMap::new(),
1218 },
1219 );
1220
1221 learner.record_event(
1223 "persona-1".to_string(),
1224 BehaviorEvent {
1225 timestamp: chrono::Utc::now(),
1226 event_type: BehaviorEventType::Request {
1227 endpoint: "/api/checkout".to_string(),
1228 method: "POST".to_string(),
1229 },
1230 data: HashMap::new(),
1231 },
1232 );
1233
1234 let pattern = learner.analyze_persona_behavior("persona-1").await.unwrap();
1236 assert!(pattern.is_none()); }
1238
1239 #[tokio::test]
1240 async fn test_persona_behavior_learner_get_behavior_history() {
1241 let config = LearningConfig {
1242 enabled: true,
1243 ..Default::default()
1244 };
1245 let mut learner = PersonaBehaviorLearner::new(config).unwrap();
1246
1247 learner.record_event(
1248 "persona-test".to_string(),
1249 BehaviorEvent {
1250 timestamp: chrono::Utc::now(),
1251 event_type: BehaviorEventType::Request {
1252 endpoint: "/api/users".to_string(),
1253 method: "GET".to_string(),
1254 },
1255 data: HashMap::new(),
1256 },
1257 );
1258
1259 let history = learner.get_behavior_history("persona-test");
1260 assert!(history.is_some());
1261 assert_eq!(history.unwrap().len(), 1);
1262 }
1263
1264 #[tokio::test]
1265 async fn test_persona_behavior_learner_analyze_nonexistent_persona() {
1266 let config = LearningConfig {
1267 enabled: true,
1268 ..Default::default()
1269 };
1270 let learner = PersonaBehaviorLearner::new(config).unwrap();
1271
1272 let pattern = learner.analyze_persona_behavior("nonexistent").await.unwrap();
1273 assert!(pattern.is_none());
1274 }
1275
1276 #[tokio::test]
1277 async fn test_persona_behavior_learner_analyze_disabled() {
1278 let config = LearningConfig {
1279 enabled: false,
1280 ..Default::default()
1281 };
1282 let learner = PersonaBehaviorLearner::new(config).unwrap();
1283
1284 let pattern = learner.analyze_persona_behavior("any").await.unwrap();
1285 assert!(pattern.is_none());
1286 }
1287
1288 #[test]
1289 fn test_persona_behavior_learner_event_limit() {
1290 let config = LearningConfig {
1291 enabled: true,
1292 ..Default::default()
1293 };
1294 let mut learner = PersonaBehaviorLearner::new(config).unwrap();
1295
1296 for i in 0..1050 {
1298 learner.record_event(
1299 "persona-limit".to_string(),
1300 BehaviorEvent {
1301 timestamp: chrono::Utc::now(),
1302 event_type: BehaviorEventType::Request {
1303 endpoint: format!("/api/test/{}", i),
1304 method: "GET".to_string(),
1305 },
1306 data: HashMap::new(),
1307 },
1308 );
1309 }
1310
1311 let history = learner.get_behavior_history("persona-limit").unwrap();
1312 assert_eq!(history.len(), 1000); }
1314
1315 #[test]
1320 fn test_behavior_event_creation() {
1321 let event = BehaviorEvent {
1322 timestamp: chrono::Utc::now(),
1323 event_type: BehaviorEventType::Request {
1324 endpoint: "/api/test".to_string(),
1325 method: "POST".to_string(),
1326 },
1327 data: HashMap::new(),
1328 };
1329 assert!(event.data.is_empty());
1330 }
1331
1332 #[test]
1333 fn test_behavior_event_clone() {
1334 let event = BehaviorEvent {
1335 timestamp: chrono::Utc::now(),
1336 event_type: BehaviorEventType::PatternDetected {
1337 pattern: "test-pattern".to_string(),
1338 },
1339 data: HashMap::new(),
1340 };
1341 let cloned = event.clone();
1342 if let BehaviorEventType::PatternDetected { pattern } = cloned.event_type {
1343 assert_eq!(pattern, "test-pattern");
1344 } else {
1345 panic!("Wrong event type after clone");
1346 }
1347 }
1348
1349 #[test]
1350 fn test_behavior_event_debug() {
1351 let event = BehaviorEvent {
1352 timestamp: chrono::Utc::now(),
1353 event_type: BehaviorEventType::RequestSucceededAfterFailure {
1354 endpoint: "/api/retry".to_string(),
1355 },
1356 data: HashMap::new(),
1357 };
1358 let debug_str = format!("{:?}", event);
1359 assert!(debug_str.contains("RequestSucceededAfterFailure"));
1360 }
1361
1362 #[test]
1367 fn test_behavior_event_type_request() {
1368 let event_type = BehaviorEventType::Request {
1369 endpoint: "/api/users".to_string(),
1370 method: "GET".to_string(),
1371 };
1372 if let BehaviorEventType::Request { endpoint, method } = event_type {
1373 assert_eq!(endpoint, "/api/users");
1374 assert_eq!(method, "GET");
1375 }
1376 }
1377
1378 #[test]
1379 fn test_behavior_event_type_request_failed() {
1380 let event_type = BehaviorEventType::RequestFailed {
1381 endpoint: "/api/orders".to_string(),
1382 status_code: 500,
1383 };
1384 if let BehaviorEventType::RequestFailed {
1385 endpoint,
1386 status_code,
1387 } = event_type
1388 {
1389 assert_eq!(endpoint, "/api/orders");
1390 assert_eq!(status_code, 500);
1391 }
1392 }
1393
1394 #[test]
1395 fn test_behavior_event_type_eq() {
1396 let a = BehaviorEventType::PatternDetected {
1397 pattern: "a".to_string(),
1398 };
1399 let b = BehaviorEventType::PatternDetected {
1400 pattern: "a".to_string(),
1401 };
1402 assert_eq!(a, b);
1403 }
1404
1405 #[test]
1406 fn test_behavior_event_type_clone() {
1407 let event_type = BehaviorEventType::RequestFailed {
1408 endpoint: "/test".to_string(),
1409 status_code: 404,
1410 };
1411 let cloned = event_type.clone();
1412 assert_eq!(cloned, event_type);
1413 }
1414
1415 #[test]
1416 fn test_behavior_event_type_debug() {
1417 let event_type = BehaviorEventType::Request {
1418 endpoint: "/debug".to_string(),
1419 method: "DELETE".to_string(),
1420 };
1421 let debug_str = format!("{:?}", event_type);
1422 assert!(debug_str.contains("Request"));
1423 assert!(debug_str.contains("/debug"));
1424 }
1425
1426 #[tokio::test]
1431 async fn test_full_learning_workflow() {
1432 let drift_config = DataDriftConfig::new();
1434 let learning_config = LearningConfig {
1435 enabled: true,
1436 min_samples: 2, ..Default::default()
1438 };
1439 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
1440
1441 let data = serde_json::json!({
1443 "user_id": "123",
1444 "amount": 100.0,
1445 "status": "pending"
1446 });
1447 let result = engine.apply_drift_with_learning(data).await.unwrap();
1448 assert!(result.is_object());
1449 }
1450
1451 #[tokio::test]
1452 async fn test_persona_behavior_pattern_detection() {
1453 let config = LearningConfig {
1454 enabled: true,
1455 min_samples: 5,
1456 ..Default::default()
1457 };
1458 let mut learner = PersonaBehaviorLearner::new(config).unwrap();
1459
1460 for _ in 0..10 {
1462 learner.record_event(
1463 "retry-persona".to_string(),
1464 BehaviorEvent {
1465 timestamp: chrono::Utc::now(),
1466 event_type: BehaviorEventType::RequestFailed {
1467 endpoint: "/api/payment".to_string(),
1468 status_code: 503,
1469 },
1470 data: HashMap::new(),
1471 },
1472 );
1473 learner.record_event(
1474 "retry-persona".to_string(),
1475 BehaviorEvent {
1476 timestamp: chrono::Utc::now(),
1477 event_type: BehaviorEventType::Request {
1478 endpoint: "/api/checkout".to_string(),
1479 method: "POST".to_string(),
1480 },
1481 data: HashMap::new(),
1482 },
1483 );
1484 }
1485
1486 let pattern = learner.analyze_persona_behavior("retry-persona").await.unwrap();
1488 assert!(pattern.is_some());
1489 let p = pattern.unwrap();
1490 assert_eq!(p.pattern_type, PatternType::PersonaBehavior);
1491 assert!(p.confidence > 0.0);
1492 }
1493}