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
115#[allow(dead_code)]
119pub struct DriftLearningEngine {
120 drift_engine: DataDriftEngine,
122 learning_config: LearningConfig,
124 traffic_learner: Option<Arc<RwLock<TrafficPatternLearner>>>,
126 persona_learner: Option<Arc<RwLock<PersonaBehaviorLearner>>>,
128 learned_patterns: Arc<RwLock<HashMap<String, LearnedPattern>>>,
130}
131
132#[derive(Debug, Clone)]
134pub struct LearnedPattern {
135 pub pattern_id: String,
137 pub pattern_type: PatternType,
139 pub parameters: HashMap<String, Value>,
141 pub confidence: f64,
143 pub sample_count: usize,
145 pub last_updated: chrono::DateTime<chrono::Utc>,
147}
148
149#[derive(Debug, Clone, PartialEq, Eq)]
151pub enum PatternType {
152 Latency,
154 ErrorRate,
156 RequestSequence,
158 PersonaBehavior,
160}
161
162impl DriftLearningEngine {
163 pub fn new(drift_config: DataDriftConfig, learning_config: LearningConfig) -> Result<Self> {
165 let drift_engine = DataDriftEngine::new(drift_config)?;
166
167 let traffic_learner = if learning_config.traffic_mirroring {
168 Some(Arc::new(RwLock::new(TrafficPatternLearner::new(learning_config.clone())?)))
169 } else {
170 None
171 };
172
173 let persona_learner = if learning_config.persona_adaptation {
174 Some(Arc::new(RwLock::new(PersonaBehaviorLearner::new(learning_config.clone())?)))
175 } else {
176 None
177 };
178
179 Ok(Self {
180 drift_engine,
181 learning_config,
182 traffic_learner,
183 persona_learner,
184 learned_patterns: Arc::new(RwLock::new(HashMap::new())),
185 })
186 }
187
188 pub fn drift_engine(&self) -> &DataDriftEngine {
190 &self.drift_engine
191 }
192
193 pub fn learning_config(&self) -> &LearningConfig {
195 &self.learning_config
196 }
197
198 pub fn update_learning_config(&mut self, config: LearningConfig) -> Result<()> {
200 self.learning_config = config;
201 Ok(())
202 }
203
204 pub async fn get_learned_patterns(&self) -> HashMap<String, LearnedPattern> {
206 self.learned_patterns.read().await.clone()
207 }
208
209 pub async fn apply_drift_with_learning(&self, data: Value) -> Result<Value> {
211 let mut data = self.drift_engine.apply_drift(data).await?;
213
214 if !self.learning_config.enabled {
216 return Ok(data);
217 }
218
219 let patterns = self.learned_patterns.read().await;
221 for (_pattern_id, pattern) in patterns.iter() {
222 if pattern.confidence < 0.5 {
224 continue; }
226
227 match pattern.pattern_type {
229 PatternType::Latency => {
230 }
232 PatternType::ErrorRate => {
233 }
235 PatternType::RequestSequence => {
236 }
238 PatternType::PersonaBehavior => {
239 if let Some(obj) = data.as_object_mut() {
241 for (key, value) in &pattern.parameters {
242 if let Some(existing) = obj.get(key) {
243 let blended =
245 self.blend_values(existing, value, pattern.confidence)?;
246 obj.insert(key.clone(), blended);
247 }
248 }
249 }
250 }
251 }
252 }
253
254 Ok(data)
255 }
256
257 fn blend_values(&self, existing: &Value, learned: &Value, confidence: f64) -> Result<Value> {
259 let weight = confidence * self.learning_config.sensitivity;
261
262 match (existing, learned) {
263 (Value::Number(existing_num), Value::Number(learned_num)) => {
264 if let (Some(existing_f64), Some(learned_f64)) =
265 (existing_num.as_f64(), learned_num.as_f64())
266 {
267 let blended = existing_f64 * (1.0 - weight) + learned_f64 * weight;
268 Ok(Value::from(blended))
269 } else {
270 Ok(existing.clone())
271 }
272 }
273 _ => Ok(existing.clone()), }
275 }
276}
277
278#[allow(dead_code)]
282pub struct TrafficPatternLearner {
283 config: LearningConfig,
285 pattern_window: Duration,
287 patterns: HashMap<String, TrafficPattern>,
289}
290
291#[derive(Debug, Clone)]
293#[allow(dead_code)]
294struct TrafficPattern {
295 pattern_id: String,
297 pattern_type: PatternType,
299 parameters: HashMap<String, Value>,
301 sample_count: usize,
303 first_seen: chrono::DateTime<chrono::Utc>,
305 last_seen: chrono::DateTime<chrono::Utc>,
307}
308
309impl TrafficPatternLearner {
310 pub fn new(config: LearningConfig) -> Result<Self> {
312 Ok(Self {
313 config,
314 pattern_window: Duration::from_secs(3600), patterns: HashMap::new(),
316 })
317 }
318
319 pub async fn analyze_traffic_patterns(
324 &mut self,
325 _database: &dyn std::any::Any, ) -> Result<Vec<LearnedPattern>> {
327 Ok(Vec::new())
329 }
330
331 #[allow(dead_code)]
336 pub async fn detect_latency_patterns_from_requests(
337 &self,
338 _requests: &[Value],
339 ) -> Result<Vec<LearnedPattern>> {
340 Ok(Vec::new())
342 }
343
344 #[allow(dead_code)]
433 async fn detect_error_patterns_internal(
434 &self,
435 _requests: &[Value],
436 ) -> Result<Vec<LearnedPattern>> {
437 use chrono::Utc;
438 use std::collections::HashMap;
439
440 let endpoint_errors: HashMap<String, (usize, usize)> = HashMap::new(); let mut patterns = Vec::new();
460
461 for (endpoint_key, (total, errors)) in endpoint_errors {
462 if total < 20 {
463 continue;
465 }
466
467 let error_rate = errors as f64 / total as f64;
468
469 if error_rate > 0.05 {
471 let mut parameters = HashMap::new();
472 parameters.insert("endpoint".to_string(), serde_json::json!(endpoint_key));
473 parameters.insert("error_rate".to_string(), serde_json::json!(error_rate));
474 parameters.insert("total_requests".to_string(), serde_json::json!(total));
475 parameters.insert("error_count".to_string(), serde_json::json!(errors));
476
477 let confidence = ((total as f64 / 100.0).min(1.0) * error_rate * 10.0).min(1.0);
479
480 patterns.push(LearnedPattern {
481 pattern_id: format!("error_rate_{}", endpoint_key.replace(['/', ' '], "_")),
482 pattern_type: PatternType::ErrorRate,
483 parameters,
484 confidence,
485 sample_count: total,
486 last_updated: Utc::now(),
487 });
488 }
489 }
490
491 Ok(patterns)
492 }
493
494 #[allow(dead_code)]
497 async fn detect_sequence_patterns_internal(
498 &self,
499 _requests: &[Value],
500 ) -> Result<Vec<LearnedPattern>> {
501 use chrono::Utc;
502 use std::collections::HashMap;
503
504 if _requests.len() < 50 {
506 return Ok(Vec::new());
508 }
509
510 let trace_sequences: HashMap<Option<String>, Vec<String>> = HashMap::new();
512
513 let mut sequence_counts: HashMap<String, usize> = HashMap::new();
526
527 for sequence in trace_sequences.values() {
528 if sequence.len() >= 2 {
529 let signature: Vec<String> = sequence.iter().take(3).cloned().collect();
531 let signature_str = signature.join(" -> ");
532 *sequence_counts.entry(signature_str).or_insert(0) += 1;
533 }
534 }
535
536 let mut patterns = Vec::new();
537
538 for (sequence_str, count) in sequence_counts {
539 if count >= 5 {
540 let mut parameters = HashMap::new();
542 parameters.insert("sequence".to_string(), serde_json::json!(sequence_str));
543 parameters.insert("occurrence_count".to_string(), serde_json::json!(count));
544
545 let confidence = (count as f64 / 20.0).min(1.0);
547
548 patterns.push(LearnedPattern {
549 pattern_id: format!(
550 "sequence_{}",
551 sequence_str.replace(['/', ' '], "_").replace("->", "_")
552 ),
553 pattern_type: PatternType::RequestSequence,
554 parameters,
555 confidence,
556 sample_count: count,
557 last_updated: Utc::now(),
558 });
559 }
560 }
561
562 Ok(patterns)
563 }
564
565 pub async fn detect_latency_patterns(&mut self) -> Result<Vec<LearnedPattern>> {
570 Ok(Vec::new())
572 }
573
574 pub async fn detect_error_patterns(&mut self) -> Result<Vec<LearnedPattern>> {
579 Ok(Vec::new())
581 }
582}
583
584pub struct PersonaBehaviorLearner {
588 config: LearningConfig,
590 behavior_history: HashMap<String, Vec<BehaviorEvent>>,
592}
593
594#[derive(Debug, Clone)]
596pub struct BehaviorEvent {
597 pub timestamp: chrono::DateTime<chrono::Utc>,
599 pub event_type: BehaviorEventType,
601 pub data: HashMap<String, Value>,
603}
604
605#[derive(Debug, Clone, PartialEq, Eq)]
607pub enum BehaviorEventType {
608 Request {
610 endpoint: String,
612 method: String,
614 },
615 RequestFailed {
617 endpoint: String,
619 status_code: u16,
621 },
622 RequestSucceededAfterFailure {
624 endpoint: String,
626 },
627 PatternDetected {
629 pattern: String,
631 },
632}
633
634impl PersonaBehaviorLearner {
635 pub fn new(config: LearningConfig) -> Result<Self> {
637 Ok(Self {
638 config,
639 behavior_history: HashMap::new(),
640 })
641 }
642
643 pub fn record_event(&mut self, persona_id: String, event: BehaviorEvent) {
645 if !self.config.enabled {
646 return;
647 }
648
649 if let Some(&enabled) = self.config.persona_learning.get(&persona_id) {
651 if !enabled {
652 return; }
654 }
655
656 let events = self.behavior_history.entry(persona_id).or_default();
657 events.push(event);
658
659 if events.len() > 1000 {
661 events.remove(0);
662 }
663 }
664
665 pub async fn analyze_persona_behavior(
667 &self,
668 persona_id: &str,
669 ) -> Result<Option<LearnedPattern>> {
670 if !self.config.enabled {
671 return Ok(None);
672 }
673
674 let events = match self.behavior_history.get(persona_id) {
675 Some(events) => events,
676 None => return Ok(None),
677 };
678
679 if events.len() < self.config.min_samples {
680 return Ok(None); }
682
683 let mut checkout_after_failure_count = 0;
686 let mut total_failures = 0;
687
688 for i in 1..events.len() {
689 if let BehaviorEventType::RequestFailed { .. } = events[i - 1].event_type {
690 total_failures += 1;
691 if let BehaviorEventType::Request { endpoint, .. } = &events[i].event_type {
692 if endpoint.contains("/checkout") {
693 checkout_after_failure_count += 1;
694 }
695 }
696 }
697 }
698
699 if total_failures > 0 && checkout_after_failure_count as f64 / total_failures as f64 > 0.5 {
700 let mut parameters = HashMap::new();
702 parameters.insert("retry_checkout_after_failure".to_string(), Value::from(true));
703 parameters.insert(
704 "retry_probability".to_string(),
705 Value::from(checkout_after_failure_count as f64 / total_failures as f64),
706 );
707
708 return Ok(Some(LearnedPattern {
709 pattern_id: format!("persona_{}_checkout_retry", persona_id),
710 pattern_type: PatternType::PersonaBehavior,
711 parameters,
712 confidence: (checkout_after_failure_count as f64 / total_failures as f64).min(1.0),
713 sample_count: total_failures,
714 last_updated: chrono::Utc::now(),
715 }));
716 }
717
718 Ok(None)
719 }
720
721 pub fn get_behavior_history(&self, persona_id: &str) -> Option<&Vec<BehaviorEvent>> {
723 self.behavior_history.get(persona_id)
724 }
725
726 pub async fn apply_learned_patterns_to_persona(
731 &self,
732 persona_id: &str,
733 persona_registry: &crate::PersonaRegistry,
734 ) -> Result<()> {
735 if !self.config.enabled {
736 return Ok(());
737 }
738
739 if let Some(pattern) = self.analyze_persona_behavior(persona_id).await? {
741 let mut learned_traits = HashMap::new();
743 for (key, value) in &pattern.parameters {
744 let trait_key = format!("learned_{}", key);
745 let trait_value = if let Some(s) = value.as_str() {
746 s.to_string()
747 } else if let Some(n) = value.as_f64() {
748 n.to_string()
749 } else if let Some(b) = value.as_bool() {
750 b.to_string()
751 } else {
752 value.to_string()
753 };
754 learned_traits.insert(trait_key, trait_value);
755 }
756
757 if !learned_traits.is_empty() {
759 persona_registry.update_persona(persona_id, learned_traits)?;
760 }
761 }
762
763 Ok(())
764 }
765}
766
767#[cfg(test)]
768mod tests {
769 use super::*;
770
771 #[test]
776 fn test_learning_config_default() {
777 let config = LearningConfig::default();
778 assert!(!config.enabled); assert_eq!(config.sensitivity, 0.2);
780 assert_eq!(config.min_samples, 10);
781 assert_eq!(config.decay, 0.05);
782 assert!(config.persona_adaptation);
783 assert!(config.traffic_mirroring);
784 }
785
786 #[test]
787 fn test_learning_config_serialize() {
788 let config = LearningConfig {
789 enabled: true,
790 mode: LearningMode::Statistical,
791 ..Default::default()
792 };
793 let json = serde_json::to_string(&config).unwrap();
794 assert!(json.contains("true"));
795 assert!(json.contains("statistical"));
796 }
797
798 #[test]
799 fn test_learning_config_deserialize() {
800 let json = r#"{"enabled": true, "mode": "hybrid", "sensitivity": 0.5}"#;
801 let config: LearningConfig = serde_json::from_str(json).unwrap();
802 assert!(config.enabled);
803 assert_eq!(config.mode, LearningMode::Hybrid);
804 assert!((config.sensitivity - 0.5).abs() < f64::EPSILON);
805 }
806
807 #[test]
808 fn test_learning_config_clone() {
809 let config = LearningConfig {
810 enabled: true,
811 sensitivity: 0.3,
812 ..Default::default()
813 };
814 let cloned = config.clone();
815 assert!(cloned.enabled);
816 assert!((cloned.sensitivity - 0.3).abs() < f64::EPSILON);
817 }
818
819 #[test]
820 fn test_learning_config_debug() {
821 let config = LearningConfig::default();
822 let debug_str = format!("{:?}", config);
823 assert!(debug_str.contains("sensitivity"));
824 assert!(debug_str.contains("enabled"));
825 }
826
827 #[test]
828 fn test_learning_config_endpoint_learning() {
829 let mut config = LearningConfig::default();
830 config.endpoint_learning.insert("/api/users".to_string(), true);
831 config.endpoint_learning.insert("/api/orders".to_string(), false);
832
833 assert_eq!(config.endpoint_learning.get("/api/users"), Some(&true));
834 assert_eq!(config.endpoint_learning.get("/api/orders"), Some(&false));
835 }
836
837 #[test]
838 fn test_learning_config_persona_learning() {
839 let mut config = LearningConfig::default();
840 config.persona_learning.insert("persona-1".to_string(), true);
841 config.persona_learning.insert("persona-2".to_string(), false);
842
843 assert_eq!(config.persona_learning.get("persona-1"), Some(&true));
844 assert_eq!(config.persona_learning.get("persona-2"), Some(&false));
845 }
846
847 #[test]
852 fn test_learning_mode_default() {
853 let mode = LearningMode::default();
854 assert_eq!(mode, LearningMode::Behavioral);
855 }
856
857 #[test]
858 fn test_learning_mode_eq() {
859 assert_eq!(LearningMode::Statistical, LearningMode::Statistical);
860 assert_ne!(LearningMode::Behavioral, LearningMode::Hybrid);
861 }
862
863 #[test]
864 fn test_learning_mode_serialize() {
865 let mode = LearningMode::Hybrid;
866 let json = serde_json::to_string(&mode).unwrap();
867 assert_eq!(json, "\"hybrid\"");
868 }
869
870 #[test]
871 fn test_learning_mode_deserialize() {
872 let json = "\"statistical\"";
873 let mode: LearningMode = serde_json::from_str(json).unwrap();
874 assert_eq!(mode, LearningMode::Statistical);
875 }
876
877 #[test]
878 fn test_learning_mode_clone() {
879 let mode = LearningMode::Hybrid;
880 let cloned = mode.clone();
881 assert_eq!(cloned, LearningMode::Hybrid);
882 }
883
884 #[test]
885 fn test_learning_mode_debug() {
886 let debug_str = format!("{:?}", LearningMode::Behavioral);
887 assert!(debug_str.contains("Behavioral"));
888 }
889
890 #[test]
895 fn test_drift_learning_engine_creation() {
896 let drift_config = DataDriftConfig::new();
897 let learning_config = LearningConfig::default();
898 let engine = DriftLearningEngine::new(drift_config, learning_config);
899 assert!(engine.is_ok());
900 }
901
902 #[test]
903 fn test_drift_learning_engine_with_traffic_mirroring_disabled() {
904 let drift_config = DataDriftConfig::new();
905 let learning_config = LearningConfig {
906 traffic_mirroring: false,
907 ..Default::default()
908 };
909 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
910 assert!(engine.traffic_learner.is_none());
911 }
912
913 #[test]
914 fn test_drift_learning_engine_with_persona_adaptation_disabled() {
915 let drift_config = DataDriftConfig::new();
916 let learning_config = LearningConfig {
917 persona_adaptation: false,
918 ..Default::default()
919 };
920 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
921 assert!(engine.persona_learner.is_none());
922 }
923
924 #[test]
925 fn test_drift_learning_engine_get_drift_engine() {
926 let drift_config = DataDriftConfig::new();
927 let learning_config = LearningConfig::default();
928 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
929 let _ = engine.drift_engine();
930 }
931
932 #[test]
933 fn test_drift_learning_engine_get_learning_config() {
934 let drift_config = DataDriftConfig::new();
935 let learning_config = LearningConfig {
936 enabled: true,
937 sensitivity: 0.5,
938 ..Default::default()
939 };
940 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
941 assert!(engine.learning_config().enabled);
942 assert!((engine.learning_config().sensitivity - 0.5).abs() < f64::EPSILON);
943 }
944
945 #[test]
946 fn test_drift_learning_engine_update_learning_config() {
947 let drift_config = DataDriftConfig::new();
948 let learning_config = LearningConfig::default();
949 let mut engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
950
951 let new_config = LearningConfig {
952 enabled: true,
953 sensitivity: 0.8,
954 ..Default::default()
955 };
956 engine.update_learning_config(new_config).unwrap();
957
958 assert!(engine.learning_config().enabled);
959 assert!((engine.learning_config().sensitivity - 0.8).abs() < f64::EPSILON);
960 }
961
962 #[tokio::test]
963 async fn test_drift_learning_engine_get_learned_patterns_empty() {
964 let drift_config = DataDriftConfig::new();
965 let learning_config = LearningConfig::default();
966 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
967
968 let patterns = engine.get_learned_patterns().await;
969 assert!(patterns.is_empty());
970 }
971
972 #[tokio::test]
973 async fn test_drift_learning_engine_apply_drift_with_learning_disabled() {
974 let drift_config = DataDriftConfig::new();
975 let learning_config = LearningConfig {
976 enabled: false,
977 ..Default::default()
978 };
979 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
980
981 let data = serde_json::json!({"value": 100});
982 let result = engine.apply_drift_with_learning(data.clone()).await.unwrap();
983 assert!(result.is_object());
985 }
986
987 #[tokio::test]
988 async fn test_drift_learning_engine_apply_drift_with_learning_enabled() {
989 let drift_config = DataDriftConfig::new();
990 let learning_config = LearningConfig {
991 enabled: true,
992 ..Default::default()
993 };
994 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
995
996 let data = serde_json::json!({"value": 100});
997 let result = engine.apply_drift_with_learning(data).await.unwrap();
998 assert!(result.is_object());
999 }
1000
1001 #[test]
1002 fn test_drift_learning_engine_blend_values_numeric() {
1003 let drift_config = DataDriftConfig::new();
1004 let learning_config = LearningConfig {
1005 sensitivity: 0.5,
1006 ..Default::default()
1007 };
1008 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
1009
1010 let existing = serde_json::json!(100.0);
1011 let learned = serde_json::json!(200.0);
1012 let result = engine.blend_values(&existing, &learned, 0.5).unwrap();
1013
1014 if let Some(n) = result.as_f64() {
1017 assert!((n - 125.0).abs() < 1.0);
1018 }
1019 }
1020
1021 #[test]
1022 fn test_drift_learning_engine_blend_values_non_numeric() {
1023 let drift_config = DataDriftConfig::new();
1024 let learning_config = LearningConfig::default();
1025 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
1026
1027 let existing = serde_json::json!("original");
1028 let learned = serde_json::json!("learned");
1029 let result = engine.blend_values(&existing, &learned, 0.5).unwrap();
1030
1031 assert_eq!(result, serde_json::json!("original"));
1033 }
1034
1035 #[test]
1040 fn test_learned_pattern_creation() {
1041 let pattern = LearnedPattern {
1042 pattern_id: "test-pattern".to_string(),
1043 pattern_type: PatternType::Latency,
1044 parameters: HashMap::new(),
1045 confidence: 0.9,
1046 sample_count: 100,
1047 last_updated: chrono::Utc::now(),
1048 };
1049 assert_eq!(pattern.pattern_id, "test-pattern");
1050 assert!((pattern.confidence - 0.9).abs() < f64::EPSILON);
1051 }
1052
1053 #[test]
1054 fn test_learned_pattern_clone() {
1055 let pattern = LearnedPattern {
1056 pattern_id: "cloneable".to_string(),
1057 pattern_type: PatternType::ErrorRate,
1058 parameters: HashMap::new(),
1059 confidence: 0.75,
1060 sample_count: 50,
1061 last_updated: chrono::Utc::now(),
1062 };
1063 let cloned = pattern.clone();
1064 assert_eq!(cloned.pattern_id, "cloneable");
1065 }
1066
1067 #[test]
1068 fn test_learned_pattern_debug() {
1069 let pattern = LearnedPattern {
1070 pattern_id: "debug-pattern".to_string(),
1071 pattern_type: PatternType::PersonaBehavior,
1072 parameters: HashMap::new(),
1073 confidence: 0.5,
1074 sample_count: 25,
1075 last_updated: chrono::Utc::now(),
1076 };
1077 let debug_str = format!("{:?}", pattern);
1078 assert!(debug_str.contains("debug-pattern"));
1079 }
1080
1081 #[test]
1086 fn test_pattern_type_eq() {
1087 assert_eq!(PatternType::Latency, PatternType::Latency);
1088 assert_ne!(PatternType::Latency, PatternType::ErrorRate);
1089 }
1090
1091 #[test]
1092 fn test_pattern_type_clone() {
1093 let pt = PatternType::RequestSequence;
1094 let cloned = pt.clone();
1095 assert_eq!(cloned, PatternType::RequestSequence);
1096 }
1097
1098 #[test]
1099 fn test_pattern_type_debug() {
1100 let debug_str = format!("{:?}", PatternType::PersonaBehavior);
1101 assert!(debug_str.contains("PersonaBehavior"));
1102 }
1103
1104 #[test]
1109 fn test_traffic_pattern_learner_new() {
1110 let config = LearningConfig::default();
1111 let learner = TrafficPatternLearner::new(config);
1112 assert!(learner.is_ok());
1113 }
1114
1115 #[tokio::test]
1116 async fn test_traffic_pattern_learner_analyze_traffic_patterns() {
1117 let config = LearningConfig::default();
1118 let mut learner = TrafficPatternLearner::new(config).unwrap();
1119 let patterns = learner.analyze_traffic_patterns(&()).await.unwrap();
1121 assert!(patterns.is_empty());
1122 }
1123
1124 #[tokio::test]
1125 async fn test_traffic_pattern_learner_detect_latency_patterns() {
1126 let config = LearningConfig::default();
1127 let mut learner = TrafficPatternLearner::new(config).unwrap();
1128 let patterns = learner.detect_latency_patterns().await.unwrap();
1129 assert!(patterns.is_empty()); }
1131
1132 #[tokio::test]
1133 async fn test_traffic_pattern_learner_detect_error_patterns() {
1134 let config = LearningConfig::default();
1135 let mut learner = TrafficPatternLearner::new(config).unwrap();
1136 let patterns = learner.detect_error_patterns().await.unwrap();
1137 assert!(patterns.is_empty()); }
1139
1140 #[test]
1145 fn test_persona_behavior_learner_new() {
1146 let config = LearningConfig::default();
1147 let learner = PersonaBehaviorLearner::new(config);
1148 assert!(learner.is_ok());
1149 }
1150
1151 #[test]
1152 fn test_persona_behavior_learner_record_event_disabled() {
1153 let config = LearningConfig {
1154 enabled: false,
1155 ..Default::default()
1156 };
1157 let mut learner = PersonaBehaviorLearner::new(config).unwrap();
1158
1159 learner.record_event(
1160 "persona-1".to_string(),
1161 BehaviorEvent {
1162 timestamp: chrono::Utc::now(),
1163 event_type: BehaviorEventType::Request {
1164 endpoint: "/api/test".to_string(),
1165 method: "GET".to_string(),
1166 },
1167 data: HashMap::new(),
1168 },
1169 );
1170
1171 assert!(learner.get_behavior_history("persona-1").is_none());
1173 }
1174
1175 #[test]
1176 fn test_persona_behavior_learner_record_event_persona_disabled() {
1177 let mut config = LearningConfig {
1178 enabled: true,
1179 ..Default::default()
1180 };
1181 config.persona_learning.insert("persona-1".to_string(), false);
1182 let mut learner = PersonaBehaviorLearner::new(config).unwrap();
1183
1184 learner.record_event(
1185 "persona-1".to_string(),
1186 BehaviorEvent {
1187 timestamp: chrono::Utc::now(),
1188 event_type: BehaviorEventType::Request {
1189 endpoint: "/api/test".to_string(),
1190 method: "GET".to_string(),
1191 },
1192 data: HashMap::new(),
1193 },
1194 );
1195
1196 assert!(learner.get_behavior_history("persona-1").is_none());
1198 }
1199
1200 #[tokio::test]
1201 async fn test_persona_behavior_learner() {
1202 let config = LearningConfig {
1203 enabled: true,
1204 persona_adaptation: true,
1205 ..Default::default()
1206 };
1207 let mut learner = PersonaBehaviorLearner::new(config).unwrap();
1208
1209 learner.record_event(
1211 "persona-1".to_string(),
1212 BehaviorEvent {
1213 timestamp: chrono::Utc::now(),
1214 event_type: BehaviorEventType::RequestFailed {
1215 endpoint: "/api/checkout".to_string(),
1216 status_code: 500,
1217 },
1218 data: HashMap::new(),
1219 },
1220 );
1221
1222 learner.record_event(
1224 "persona-1".to_string(),
1225 BehaviorEvent {
1226 timestamp: chrono::Utc::now(),
1227 event_type: BehaviorEventType::Request {
1228 endpoint: "/api/checkout".to_string(),
1229 method: "POST".to_string(),
1230 },
1231 data: HashMap::new(),
1232 },
1233 );
1234
1235 let pattern = learner.analyze_persona_behavior("persona-1").await.unwrap();
1237 assert!(pattern.is_none()); }
1239
1240 #[tokio::test]
1241 async fn test_persona_behavior_learner_get_behavior_history() {
1242 let config = LearningConfig {
1243 enabled: true,
1244 ..Default::default()
1245 };
1246 let mut learner = PersonaBehaviorLearner::new(config).unwrap();
1247
1248 learner.record_event(
1249 "persona-test".to_string(),
1250 BehaviorEvent {
1251 timestamp: chrono::Utc::now(),
1252 event_type: BehaviorEventType::Request {
1253 endpoint: "/api/users".to_string(),
1254 method: "GET".to_string(),
1255 },
1256 data: HashMap::new(),
1257 },
1258 );
1259
1260 let history = learner.get_behavior_history("persona-test");
1261 assert!(history.is_some());
1262 assert_eq!(history.unwrap().len(), 1);
1263 }
1264
1265 #[tokio::test]
1266 async fn test_persona_behavior_learner_analyze_nonexistent_persona() {
1267 let config = LearningConfig {
1268 enabled: true,
1269 ..Default::default()
1270 };
1271 let learner = PersonaBehaviorLearner::new(config).unwrap();
1272
1273 let pattern = learner.analyze_persona_behavior("nonexistent").await.unwrap();
1274 assert!(pattern.is_none());
1275 }
1276
1277 #[tokio::test]
1278 async fn test_persona_behavior_learner_analyze_disabled() {
1279 let config = LearningConfig {
1280 enabled: false,
1281 ..Default::default()
1282 };
1283 let learner = PersonaBehaviorLearner::new(config).unwrap();
1284
1285 let pattern = learner.analyze_persona_behavior("any").await.unwrap();
1286 assert!(pattern.is_none());
1287 }
1288
1289 #[test]
1290 fn test_persona_behavior_learner_event_limit() {
1291 let config = LearningConfig {
1292 enabled: true,
1293 ..Default::default()
1294 };
1295 let mut learner = PersonaBehaviorLearner::new(config).unwrap();
1296
1297 for i in 0..1050 {
1299 learner.record_event(
1300 "persona-limit".to_string(),
1301 BehaviorEvent {
1302 timestamp: chrono::Utc::now(),
1303 event_type: BehaviorEventType::Request {
1304 endpoint: format!("/api/test/{}", i),
1305 method: "GET".to_string(),
1306 },
1307 data: HashMap::new(),
1308 },
1309 );
1310 }
1311
1312 let history = learner.get_behavior_history("persona-limit").unwrap();
1313 assert_eq!(history.len(), 1000); }
1315
1316 #[test]
1321 fn test_behavior_event_creation() {
1322 let event = BehaviorEvent {
1323 timestamp: chrono::Utc::now(),
1324 event_type: BehaviorEventType::Request {
1325 endpoint: "/api/test".to_string(),
1326 method: "POST".to_string(),
1327 },
1328 data: HashMap::new(),
1329 };
1330 assert!(event.data.is_empty());
1331 }
1332
1333 #[test]
1334 fn test_behavior_event_clone() {
1335 let event = BehaviorEvent {
1336 timestamp: chrono::Utc::now(),
1337 event_type: BehaviorEventType::PatternDetected {
1338 pattern: "test-pattern".to_string(),
1339 },
1340 data: HashMap::new(),
1341 };
1342 let cloned = event.clone();
1343 if let BehaviorEventType::PatternDetected { pattern } = cloned.event_type {
1344 assert_eq!(pattern, "test-pattern");
1345 } else {
1346 panic!("Wrong event type after clone");
1347 }
1348 }
1349
1350 #[test]
1351 fn test_behavior_event_debug() {
1352 let event = BehaviorEvent {
1353 timestamp: chrono::Utc::now(),
1354 event_type: BehaviorEventType::RequestSucceededAfterFailure {
1355 endpoint: "/api/retry".to_string(),
1356 },
1357 data: HashMap::new(),
1358 };
1359 let debug_str = format!("{:?}", event);
1360 assert!(debug_str.contains("RequestSucceededAfterFailure"));
1361 }
1362
1363 #[test]
1368 fn test_behavior_event_type_request() {
1369 let event_type = BehaviorEventType::Request {
1370 endpoint: "/api/users".to_string(),
1371 method: "GET".to_string(),
1372 };
1373 if let BehaviorEventType::Request { endpoint, method } = event_type {
1374 assert_eq!(endpoint, "/api/users");
1375 assert_eq!(method, "GET");
1376 }
1377 }
1378
1379 #[test]
1380 fn test_behavior_event_type_request_failed() {
1381 let event_type = BehaviorEventType::RequestFailed {
1382 endpoint: "/api/orders".to_string(),
1383 status_code: 500,
1384 };
1385 if let BehaviorEventType::RequestFailed {
1386 endpoint,
1387 status_code,
1388 } = event_type
1389 {
1390 assert_eq!(endpoint, "/api/orders");
1391 assert_eq!(status_code, 500);
1392 }
1393 }
1394
1395 #[test]
1396 fn test_behavior_event_type_eq() {
1397 let a = BehaviorEventType::PatternDetected {
1398 pattern: "a".to_string(),
1399 };
1400 let b = BehaviorEventType::PatternDetected {
1401 pattern: "a".to_string(),
1402 };
1403 assert_eq!(a, b);
1404 }
1405
1406 #[test]
1407 fn test_behavior_event_type_clone() {
1408 let event_type = BehaviorEventType::RequestFailed {
1409 endpoint: "/test".to_string(),
1410 status_code: 404,
1411 };
1412 let cloned = event_type.clone();
1413 assert_eq!(cloned, event_type);
1414 }
1415
1416 #[test]
1417 fn test_behavior_event_type_debug() {
1418 let event_type = BehaviorEventType::Request {
1419 endpoint: "/debug".to_string(),
1420 method: "DELETE".to_string(),
1421 };
1422 let debug_str = format!("{:?}", event_type);
1423 assert!(debug_str.contains("Request"));
1424 assert!(debug_str.contains("/debug"));
1425 }
1426
1427 #[tokio::test]
1432 async fn test_full_learning_workflow() {
1433 let drift_config = DataDriftConfig::new();
1435 let learning_config = LearningConfig {
1436 enabled: true,
1437 min_samples: 2, ..Default::default()
1439 };
1440 let engine = DriftLearningEngine::new(drift_config, learning_config).unwrap();
1441
1442 let data = serde_json::json!({
1444 "user_id": "123",
1445 "amount": 100.0,
1446 "status": "pending"
1447 });
1448 let result = engine.apply_drift_with_learning(data).await.unwrap();
1449 assert!(result.is_object());
1450 }
1451
1452 #[tokio::test]
1453 async fn test_persona_behavior_pattern_detection() {
1454 let config = LearningConfig {
1455 enabled: true,
1456 min_samples: 5,
1457 ..Default::default()
1458 };
1459 let mut learner = PersonaBehaviorLearner::new(config).unwrap();
1460
1461 for _ in 0..10 {
1463 learner.record_event(
1464 "retry-persona".to_string(),
1465 BehaviorEvent {
1466 timestamp: chrono::Utc::now(),
1467 event_type: BehaviorEventType::RequestFailed {
1468 endpoint: "/api/payment".to_string(),
1469 status_code: 503,
1470 },
1471 data: HashMap::new(),
1472 },
1473 );
1474 learner.record_event(
1475 "retry-persona".to_string(),
1476 BehaviorEvent {
1477 timestamp: chrono::Utc::now(),
1478 event_type: BehaviorEventType::Request {
1479 endpoint: "/api/checkout".to_string(),
1480 method: "POST".to_string(),
1481 },
1482 data: HashMap::new(),
1483 },
1484 );
1485 }
1486
1487 let pattern = learner.analyze_persona_behavior("retry-persona").await.unwrap();
1489 assert!(pattern.is_some());
1490 let p = pattern.unwrap();
1491 assert_eq!(p.pattern_type, PatternType::PersonaBehavior);
1492 assert!(p.confidence > 0.0);
1493 }
1494}