1use std::collections::HashMap;
10use std::{
11 sync::{Arc, Mutex},
12 time::{Duration, SystemTime},
13};
14
15use crate::core::{
16 error::LarkAPIError,
17 error_codes::{ErrorCategory, LarkErrorCode},
18 error_helper::ErrorHandlingCategory,
19};
20
21#[derive(Debug, Clone)]
23pub struct ErrorEvent {
24 pub error: LarkAPIError,
26 pub timestamp: SystemTime,
28 pub category: ErrorHandlingCategory,
30 pub error_code: Option<LarkErrorCode>,
32 pub is_retryable: bool,
34 pub processing_time: Option<Duration>,
36 pub context: HashMap<String, String>,
38}
39
40impl ErrorEvent {
41 pub fn from_error(error: LarkAPIError) -> Self {
43 let category = match &error {
44 LarkAPIError::ApiError { code, .. } => {
45 if let Some(error_code) = LarkErrorCode::from_code(*code) {
46 match error_code.category() {
47 ErrorCategory::Authentication => ErrorHandlingCategory::Authentication,
48 ErrorCategory::Permission => ErrorHandlingCategory::Permission,
49 ErrorCategory::Parameter => ErrorHandlingCategory::ClientError,
50 ErrorCategory::Resource => ErrorHandlingCategory::ClientError,
51 ErrorCategory::Server => ErrorHandlingCategory::ServerError,
52 ErrorCategory::Network => ErrorHandlingCategory::NetworkError,
53 ErrorCategory::RateLimit => ErrorHandlingCategory::RateLimit,
54 ErrorCategory::Other => ErrorHandlingCategory::Unknown,
55 }
56 } else {
57 ErrorHandlingCategory::Unknown
58 }
59 }
60 LarkAPIError::RequestError(_) => ErrorHandlingCategory::NetworkError,
61 LarkAPIError::MissingAccessToken => ErrorHandlingCategory::Authentication,
62 LarkAPIError::IllegalParamError(_) => ErrorHandlingCategory::ClientError,
63 _ => ErrorHandlingCategory::SystemError,
64 };
65
66 let error_code = match &error {
67 LarkAPIError::ApiError { code, .. } => LarkErrorCode::from_code(*code),
68 _ => None,
69 };
70
71 Self {
72 is_retryable: error.is_retryable(),
73 error,
74 timestamp: SystemTime::now(),
75 category,
76 error_code,
77 processing_time: None,
78 context: HashMap::new(),
79 }
80 }
81
82 pub fn with_context(mut self, key: &str, value: &str) -> Self {
84 self.context.insert(key.to_string(), value.to_string());
85 self
86 }
87
88 pub fn with_processing_time(mut self, duration: Duration) -> Self {
90 self.processing_time = Some(duration);
91 self
92 }
93
94 pub fn severity_level(&self) -> ErrorSeverity {
96 match &self.category {
97 ErrorHandlingCategory::Authentication => ErrorSeverity::Warning,
98 ErrorHandlingCategory::Permission => ErrorSeverity::Error,
99 ErrorHandlingCategory::ClientError => ErrorSeverity::Warning,
100 ErrorHandlingCategory::ServerError => ErrorSeverity::Critical,
101 ErrorHandlingCategory::NetworkError => ErrorSeverity::Warning,
102 ErrorHandlingCategory::RateLimit => ErrorSeverity::Warning,
103 ErrorHandlingCategory::SystemError => ErrorSeverity::Critical,
104 ErrorHandlingCategory::Unknown => ErrorSeverity::Error,
105 }
106 }
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
111pub enum ErrorSeverity {
112 Info,
114 Warning,
116 Error,
118 Critical,
120}
121
122impl ErrorSeverity {
123 pub fn weight(&self) -> u8 {
125 match self {
126 Self::Info => 1,
127 Self::Warning => 2,
128 Self::Error => 3,
129 Self::Critical => 4,
130 }
131 }
132
133 pub fn symbol(&self) -> &'static str {
135 match self {
136 Self::Info => "ℹ️",
137 Self::Warning => "⚠️",
138 Self::Error => "❌",
139 Self::Critical => "🚨",
140 }
141 }
142}
143
144#[derive(Debug, Clone, Default)]
146pub struct ErrorStatistics {
147 pub total_errors: u64,
149 pub errors_by_category: HashMap<ErrorHandlingCategory, u64>,
151 pub errors_by_code: HashMap<LarkErrorCode, u64>,
153 pub errors_by_severity: HashMap<ErrorSeverity, u64>,
155 pub retryable_errors: u64,
157 pub average_processing_time: Option<Duration>,
159 pub first_error_time: Option<SystemTime>,
161 pub last_error_time: Option<SystemTime>,
163}
164
165impl ErrorStatistics {
166 pub fn error_rate_per_minute(&self) -> f64 {
168 if let (Some(first), Some(last)) = (self.first_error_time, self.last_error_time) {
169 if let Ok(duration) = last.duration_since(first) {
170 let minutes = duration.as_secs_f64() / 60.0;
171 if minutes > 0.0 {
172 return self.total_errors as f64 / minutes;
173 }
174 }
175 }
176 0.0
177 }
178
179 pub fn most_common_category(&self) -> Option<ErrorHandlingCategory> {
181 self.errors_by_category
182 .iter()
183 .max_by_key(|(_, count)| *count)
184 .map(|(category, _)| *category)
185 }
186
187 pub fn highest_severity(&self) -> Option<ErrorSeverity> {
189 self.errors_by_severity
190 .keys()
191 .max_by_key(|severity| severity.weight())
192 .copied()
193 }
194
195 pub fn retryable_percentage(&self) -> f64 {
197 if self.total_errors == 0 {
198 0.0
199 } else {
200 (self.retryable_errors as f64 / self.total_errors as f64) * 100.0
201 }
202 }
203
204 pub fn print_summary(&self) {
206 println!("📊 错误统计摘要:");
207 println!(" 总错误数: {}", self.total_errors);
208 println!(" 错误率: {:.2} 错误/分钟", self.error_rate_per_minute());
209 println!(
210 " 可重试错误: {} ({:.1}%)",
211 self.retryable_errors,
212 self.retryable_percentage()
213 );
214
215 if let Some(category) = self.most_common_category() {
216 println!(" 最常见类别: {category:?}");
217 }
218
219 if let Some(severity) = self.highest_severity() {
220 println!(" 最高严重级别: {} {:?}", severity.symbol(), severity);
221 }
222
223 if let Some(avg_time) = self.average_processing_time {
224 println!(" 平均处理时间: {avg_time:?}");
225 }
226 }
227
228 pub fn print_detailed(&self) {
230 self.print_summary();
231
232 println!("\n📈 错误分类统计:");
233 for (category, count) in &self.errors_by_category {
234 let percentage = (*count as f64 / self.total_errors as f64) * 100.0;
235 println!(" {category:?}: {count} ({percentage:.1}%)");
236 }
237
238 println!("\n🔢 错误码统计:");
239 let mut sorted_codes: Vec<_> = self.errors_by_code.iter().collect();
240 sorted_codes.sort_by(|a, b| b.1.cmp(a.1));
241 for (code, count) in sorted_codes.iter().take(10) {
242 let percentage = (**count as f64 / self.total_errors as f64) * 100.0;
243 println!(" {code}: {count} ({percentage:.1}%)");
244 }
245
246 println!("\n⚠️ 严重级别统计:");
247 for severity in [
248 ErrorSeverity::Critical,
249 ErrorSeverity::Error,
250 ErrorSeverity::Warning,
251 ErrorSeverity::Info,
252 ] {
253 if let Some(count) = self.errors_by_severity.get(&severity) {
254 let percentage = (*count as f64 / self.total_errors as f64) * 100.0;
255 println!(
256 " {} {:?}: {} ({:.1}%)",
257 severity.symbol(),
258 severity,
259 count,
260 percentage
261 );
262 }
263 }
264 }
265}
266
267pub struct ErrorMonitor {
269 events: Arc<Mutex<Vec<ErrorEvent>>>,
271 statistics: Arc<Mutex<ErrorStatistics>>,
273 config: MonitorConfig,
275}
276
277#[derive(Debug, Clone)]
279pub struct MonitorConfig {
280 pub max_events: usize,
282 pub time_window: Duration,
284 pub auto_cleanup: bool,
286 pub alert_thresholds: AlertThresholds,
288}
289
290impl Default for MonitorConfig {
291 fn default() -> Self {
292 Self {
293 max_events: 1000,
294 time_window: Duration::from_secs(24 * 60 * 60), auto_cleanup: true,
296 alert_thresholds: AlertThresholds::default(),
297 }
298 }
299}
300
301#[derive(Debug, Clone)]
303pub struct AlertThresholds {
304 pub error_rate_per_minute: f64,
306 pub critical_errors_count: u64,
308 pub consecutive_failures: u32,
310}
311
312impl Default for AlertThresholds {
313 fn default() -> Self {
314 Self {
315 error_rate_per_minute: 10.0,
316 critical_errors_count: 5,
317 consecutive_failures: 3,
318 }
319 }
320}
321
322impl Default for ErrorMonitor {
323 fn default() -> Self {
324 Self::new(MonitorConfig::default())
325 }
326}
327
328impl ErrorMonitor {
329 pub fn new(config: MonitorConfig) -> Self {
331 Self {
332 events: Arc::new(Mutex::new(Vec::new())),
333 statistics: Arc::new(Mutex::new(ErrorStatistics::default())),
334 config,
335 }
336 }
337
338 pub fn record_error(&self, error: LarkAPIError) {
340 let event = ErrorEvent::from_error(error);
341 self.record_event(event);
342 }
343
344 pub fn record_error_with_context(&self, error: LarkAPIError, context: HashMap<String, String>) {
346 let mut event = ErrorEvent::from_error(error);
347 event.context = context;
348 self.record_event(event);
349 }
350
351 pub fn record_event(&self, event: ErrorEvent) {
353 if let Ok(mut stats) = self.statistics.lock() {
355 stats.total_errors += 1;
356
357 *stats.errors_by_category.entry(event.category).or_insert(0) += 1;
359
360 if let Some(code) = event.error_code {
362 *stats.errors_by_code.entry(code).or_insert(0) += 1;
363 }
364
365 let severity = event.severity_level();
367 *stats.errors_by_severity.entry(severity).or_insert(0) += 1;
368
369 if event.is_retryable {
371 stats.retryable_errors += 1;
372 }
373
374 if stats.first_error_time.is_none() {
376 stats.first_error_time = Some(event.timestamp);
377 }
378 stats.last_error_time = Some(event.timestamp);
379 }
380
381 if let Ok(mut events) = self.events.lock() {
383 events.push(event);
384
385 if self.config.auto_cleanup && events.len() > self.config.max_events {
387 let len = events.len();
388 let max_events = self.config.max_events;
389 events.drain(0..(len - max_events));
390 }
391 }
392
393 self.check_alerts();
395 }
396
397 pub fn get_statistics(&self) -> ErrorStatistics {
399 self.statistics.lock().unwrap().clone()
400 }
401
402 pub fn get_recent_events(&self, limit: usize) -> Vec<ErrorEvent> {
404 if let Ok(events) = self.events.lock() {
405 events.iter().rev().take(limit).cloned().collect()
406 } else {
407 Vec::new()
408 }
409 }
410
411 pub fn cleanup_old_events(&self) {
413 if let Ok(mut events) = self.events.lock() {
414 let cutoff_time = SystemTime::now() - self.config.time_window;
415 events.retain(|event| event.timestamp >= cutoff_time);
416 }
417 }
418
419 pub fn reset_statistics(&self) {
421 if let Ok(mut stats) = self.statistics.lock() {
422 *stats = ErrorStatistics::default();
423 }
424 if let Ok(mut events) = self.events.lock() {
425 events.clear();
426 }
427 }
428
429 fn check_alerts(&self) {
431 let stats = self.get_statistics();
432
433 if stats.error_rate_per_minute() > self.config.alert_thresholds.error_rate_per_minute {
435 self.trigger_alert(
436 AlertType::HighErrorRate,
437 format!("错误率过高: {:.2} 错误/分钟", stats.error_rate_per_minute()),
438 );
439 }
440
441 if let Some(critical_count) = stats.errors_by_severity.get(&ErrorSeverity::Critical) {
443 if *critical_count >= self.config.alert_thresholds.critical_errors_count {
444 self.trigger_alert(
445 AlertType::CriticalErrors,
446 format!("严重错误过多: {critical_count} 个"),
447 );
448 }
449 }
450 }
451
452 fn trigger_alert(&self, alert_type: AlertType, message: String) {
454 println!("🚨 告警 [{alert_type:?}]: {message}");
455 }
457
458 pub fn generate_report(&self) -> ErrorReport {
460 let stats = self.get_statistics();
461 let recent_events = self.get_recent_events(10);
462
463 ErrorReport {
464 statistics: stats,
465 recent_events,
466 generated_at: SystemTime::now(),
467 time_window: self.config.time_window,
468 }
469 }
470}
471
472#[derive(Debug)]
474enum AlertType {
475 HighErrorRate,
476 CriticalErrors,
477 #[allow(dead_code)]
478 ConsecutiveFailures,
479}
480
481#[derive(Debug)]
483pub struct ErrorReport {
484 pub statistics: ErrorStatistics,
486 pub recent_events: Vec<ErrorEvent>,
488 pub generated_at: SystemTime,
490 pub time_window: Duration,
492}
493
494impl ErrorReport {
495 pub fn print(&self) {
497 println!("📋 错误监控报告");
498 println!("生成时间: {:?}", self.generated_at);
499 println!("统计窗口: {:?}", self.time_window);
500 println!("{}", "=".repeat(50));
501
502 self.statistics.print_detailed();
503
504 println!("\n🕒 最近错误事件:");
505 for (i, event) in self.recent_events.iter().enumerate() {
506 println!(
507 " {}. [{:?}] {} {:?}",
508 i + 1,
509 event.timestamp,
510 event.severity_level().symbol(),
511 event.category
512 );
513 }
514 }
515
516 pub fn save_to_file(&self, path: &str) -> Result<(), std::io::Error> {
518 use std::{fs::File, io::Write};
519
520 let mut file = File::create(path)?;
521
522 writeln!(file, "错误监控报告")?;
523 writeln!(file, "生成时间: {:?}", self.generated_at)?;
524 writeln!(file, "统计窗口: {:?}", self.time_window)?;
525 writeln!(file, "{}", "=".repeat(50))?;
526
527 writeln!(file, "\n统计摘要:")?;
528 writeln!(file, "总错误数: {}", self.statistics.total_errors)?;
529 writeln!(
530 file,
531 "错误率: {:.2} 错误/分钟",
532 self.statistics.error_rate_per_minute()
533 )?;
534 writeln!(
535 file,
536 "可重试错误: {:.1}%",
537 self.statistics.retryable_percentage()
538 )?;
539
540 Ok(())
541 }
542}
543
544#[cfg(test)]
545mod tests {
546 use super::*;
547 use rstest::rstest;
548 use std::thread;
549
550 #[test]
551 fn test_error_event_creation() {
552 let error = LarkAPIError::api_error(403, "Forbidden", None);
553 let event = ErrorEvent::from_error(error);
554
555 assert_eq!(event.category, ErrorHandlingCategory::Permission);
556 assert_eq!(event.error_code, Some(LarkErrorCode::Forbidden));
557 assert!(!event.is_retryable);
558 }
559
560 #[test]
561 fn test_error_statistics() {
562 let stats = ErrorStatistics {
563 total_errors: 100,
564 retryable_errors: 60,
565 ..Default::default()
566 };
567
568 assert_eq!(stats.retryable_percentage(), 60.0);
569 }
570
571 #[test]
572 fn test_error_monitor() {
573 let monitor = ErrorMonitor::default();
574
575 monitor.record_error(LarkAPIError::api_error(403, "Forbidden", None));
577 monitor.record_error(LarkAPIError::api_error(500, "Server Error", None));
578
579 let stats = monitor.get_statistics();
580 assert_eq!(stats.total_errors, 2);
581 assert_eq!(stats.errors_by_category.len(), 2);
582 }
583
584 #[test]
585 fn test_error_severity() {
586 assert_eq!(ErrorSeverity::Critical.weight(), 4);
587 assert_eq!(ErrorSeverity::Warning.weight(), 2);
588 assert_eq!(ErrorSeverity::Critical.symbol(), "🚨");
589 }
590
591 #[rstest]
594 #[case(
595 LarkAPIError::api_error(401, "Unauthorized", None),
596 ErrorHandlingCategory::Authentication
597 )]
598 #[case(
599 LarkAPIError::api_error(403, "Forbidden", None),
600 ErrorHandlingCategory::Permission
601 )]
602 #[case(
603 LarkAPIError::api_error(400, "Bad Request", None),
604 ErrorHandlingCategory::ClientError
605 )]
606 #[case(
607 LarkAPIError::api_error(500, "Internal Server Error", None),
608 ErrorHandlingCategory::ServerError
609 )]
610 #[case(
611 LarkAPIError::api_error(429, "Too Many Requests", None),
612 ErrorHandlingCategory::RateLimit
613 )]
614 #[case(LarkAPIError::RequestError("Network timeout".to_string()), ErrorHandlingCategory::NetworkError)]
615 #[case(
616 LarkAPIError::MissingAccessToken,
617 ErrorHandlingCategory::Authentication
618 )]
619 #[case(LarkAPIError::IllegalParamError("invalid param".to_string()), ErrorHandlingCategory::ClientError)]
620 fn test_error_event_category_mapping(
621 #[case] error: LarkAPIError,
622 #[case] expected_category: ErrorHandlingCategory,
623 ) {
624 let event = ErrorEvent::from_error(error);
625 assert_eq!(event.category, expected_category);
626 }
627
628 #[test]
629 fn test_error_event_with_context() {
630 let error = LarkAPIError::api_error(400, "Bad Request", None);
631 let event = ErrorEvent::from_error(error)
632 .with_context("endpoint", "/api/test")
633 .with_context("user_id", "123");
634
635 assert_eq!(event.context.len(), 2);
636 assert_eq!(
637 event.context.get("endpoint"),
638 Some(&"/api/test".to_string())
639 );
640 assert_eq!(event.context.get("user_id"), Some(&"123".to_string()));
641 }
642
643 #[test]
644 fn test_error_event_with_processing_time() {
645 let error = LarkAPIError::api_error(500, "Server Error", None);
646 let processing_time = Duration::from_millis(150);
647 let event = ErrorEvent::from_error(error).with_processing_time(processing_time);
648
649 assert_eq!(event.processing_time, Some(processing_time));
650 }
651
652 #[rstest]
653 #[case(ErrorHandlingCategory::Authentication, ErrorSeverity::Warning)]
654 #[case(ErrorHandlingCategory::Permission, ErrorSeverity::Error)]
655 #[case(ErrorHandlingCategory::ClientError, ErrorSeverity::Warning)]
656 #[case(ErrorHandlingCategory::ServerError, ErrorSeverity::Critical)]
657 #[case(ErrorHandlingCategory::NetworkError, ErrorSeverity::Warning)]
658 #[case(ErrorHandlingCategory::RateLimit, ErrorSeverity::Warning)]
659 #[case(ErrorHandlingCategory::SystemError, ErrorSeverity::Critical)]
660 #[case(ErrorHandlingCategory::Unknown, ErrorSeverity::Error)]
661 fn test_error_event_severity_level(
662 #[case] category: ErrorHandlingCategory,
663 #[case] expected_severity: ErrorSeverity,
664 ) {
665 let error = LarkAPIError::api_error(400, "Test", None);
666 let mut event = ErrorEvent::from_error(error);
667 event.category = category;
668
669 assert_eq!(event.severity_level(), expected_severity);
670 }
671
672 #[test]
673 fn test_error_severity_weights() {
674 assert_eq!(ErrorSeverity::Info.weight(), 1);
675 assert_eq!(ErrorSeverity::Warning.weight(), 2);
676 assert_eq!(ErrorSeverity::Error.weight(), 3);
677 assert_eq!(ErrorSeverity::Critical.weight(), 4);
678
679 assert!(ErrorSeverity::Critical.weight() > ErrorSeverity::Error.weight());
681 assert!(ErrorSeverity::Error.weight() > ErrorSeverity::Warning.weight());
682 }
683
684 #[test]
685 fn test_error_severity_symbols() {
686 assert_eq!(ErrorSeverity::Info.symbol(), "ℹ️");
687 assert_eq!(ErrorSeverity::Warning.symbol(), "⚠️");
688 assert_eq!(ErrorSeverity::Error.symbol(), "❌");
689 assert_eq!(ErrorSeverity::Critical.symbol(), "🚨");
690 }
691
692 #[test]
693 fn test_error_statistics_default() {
694 let stats = ErrorStatistics::default();
695
696 assert_eq!(stats.total_errors, 0);
697 assert!(stats.errors_by_category.is_empty());
698 assert!(stats.errors_by_code.is_empty());
699 assert!(stats.errors_by_severity.is_empty());
700 assert_eq!(stats.retryable_errors, 0);
701 assert!(stats.average_processing_time.is_none());
702 assert!(stats.first_error_time.is_none());
703 assert!(stats.last_error_time.is_none());
704 }
705
706 #[test]
707 fn test_error_statistics_error_rate_calculation() {
708 let now = SystemTime::now();
709 let one_minute_ago = now - Duration::from_secs(60);
710
711 let stats = ErrorStatistics {
712 total_errors: 10,
713 first_error_time: Some(one_minute_ago),
714 last_error_time: Some(now),
715 ..Default::default()
716 };
717
718 let rate = stats.error_rate_per_minute();
719 assert!(rate > 9.0 && rate <= 10.0); }
721
722 #[test]
723 fn test_error_statistics_error_rate_no_time_range() {
724 let stats = ErrorStatistics {
725 total_errors: 10,
726 first_error_time: None,
727 last_error_time: None,
728 ..Default::default()
729 };
730
731 assert_eq!(stats.error_rate_per_minute(), 0.0);
732 }
733
734 #[test]
735 fn test_error_statistics_most_common_category() {
736 let mut stats = ErrorStatistics::default();
737 stats
738 .errors_by_category
739 .insert(ErrorHandlingCategory::Authentication, 5);
740 stats
741 .errors_by_category
742 .insert(ErrorHandlingCategory::Permission, 10);
743 stats
744 .errors_by_category
745 .insert(ErrorHandlingCategory::ClientError, 3);
746
747 assert_eq!(
748 stats.most_common_category(),
749 Some(ErrorHandlingCategory::Permission)
750 );
751 }
752
753 #[test]
754 fn test_error_statistics_highest_severity() {
755 let mut stats = ErrorStatistics::default();
756 stats.errors_by_severity.insert(ErrorSeverity::Warning, 5);
757 stats.errors_by_severity.insert(ErrorSeverity::Error, 3);
758 stats.errors_by_severity.insert(ErrorSeverity::Critical, 1);
759
760 assert_eq!(stats.highest_severity(), Some(ErrorSeverity::Critical));
761 }
762
763 #[test]
764 fn test_error_statistics_retryable_percentage() {
765 let stats = ErrorStatistics {
766 total_errors: 50,
767 retryable_errors: 20,
768 ..Default::default()
769 };
770
771 assert_eq!(stats.retryable_percentage(), 40.0);
772
773 let empty_stats = ErrorStatistics::default();
775 assert_eq!(empty_stats.retryable_percentage(), 0.0);
776 }
777
778 #[test]
779 fn test_error_statistics_print_methods() {
780 let mut stats = ErrorStatistics {
781 total_errors: 100,
782 retryable_errors: 30,
783 ..Default::default()
784 };
785 stats
786 .errors_by_category
787 .insert(ErrorHandlingCategory::ServerError, 50);
788 stats.errors_by_severity.insert(ErrorSeverity::Critical, 10);
789
790 stats.print_summary();
792 stats.print_detailed();
793 }
794
795 #[test]
796 fn test_monitor_config_default() {
797 let config = MonitorConfig::default();
798
799 assert_eq!(config.max_events, 1000);
800 assert_eq!(config.time_window, Duration::from_secs(24 * 60 * 60));
801 assert!(config.auto_cleanup);
802 }
803
804 #[test]
805 fn test_alert_thresholds_default() {
806 let thresholds = AlertThresholds::default();
807
808 assert_eq!(thresholds.error_rate_per_minute, 10.0);
809 assert_eq!(thresholds.critical_errors_count, 5);
810 assert_eq!(thresholds.consecutive_failures, 3);
811 }
812
813 #[test]
814 fn test_error_monitor_new() {
815 let config = MonitorConfig {
816 max_events: 500,
817 time_window: Duration::from_secs(3600),
818 auto_cleanup: false,
819 alert_thresholds: AlertThresholds::default(),
820 };
821 let monitor = ErrorMonitor::new(config.clone());
822
823 assert_eq!(monitor.config.max_events, 500);
824 assert_eq!(monitor.config.time_window, Duration::from_secs(3600));
825 assert!(!monitor.config.auto_cleanup);
826 }
827
828 #[test]
829 fn test_error_monitor_default() {
830 let monitor = ErrorMonitor::default();
831 assert_eq!(monitor.config.max_events, 1000);
832 }
833
834 #[test]
835 fn test_error_monitor_record_error() {
836 let monitor = ErrorMonitor::default();
837
838 monitor.record_error(LarkAPIError::api_error(403, "Forbidden", None));
839 monitor.record_error(LarkAPIError::api_error(500, "Server Error", None));
840 monitor.record_error(LarkAPIError::MissingAccessToken);
841
842 let stats = monitor.get_statistics();
843 assert_eq!(stats.total_errors, 3);
844 assert_eq!(stats.errors_by_category.len(), 3);
845 assert!(stats
846 .errors_by_category
847 .contains_key(&ErrorHandlingCategory::Permission));
848 assert!(stats
849 .errors_by_category
850 .contains_key(&ErrorHandlingCategory::ServerError));
851 assert!(stats
852 .errors_by_category
853 .contains_key(&ErrorHandlingCategory::Authentication));
854 }
855
856 #[test]
857 fn test_error_monitor_record_error_with_context() {
858 let monitor = ErrorMonitor::default();
859 let mut context = HashMap::new();
860 context.insert("user_id".to_string(), "123".to_string());
861 context.insert("endpoint".to_string(), "/api/test".to_string());
862
863 monitor
864 .record_error_with_context(LarkAPIError::api_error(400, "Bad Request", None), context);
865
866 let events = monitor.get_recent_events(1);
867 assert_eq!(events.len(), 1);
868 assert_eq!(events[0].context.len(), 2);
869 }
870
871 #[test]
872 fn test_error_monitor_get_recent_events() {
873 let monitor = ErrorMonitor::default();
874
875 for i in 0..5 {
877 monitor.record_error(LarkAPIError::api_error(400 + i, "Test Error", None));
878 }
879
880 let recent = monitor.get_recent_events(3);
882 assert_eq!(recent.len(), 3);
883
884 }
887
888 #[test]
889 fn test_error_monitor_auto_cleanup() {
890 let config = MonitorConfig {
891 max_events: 3,
892 auto_cleanup: true,
893 ..MonitorConfig::default()
894 };
895 let monitor = ErrorMonitor::new(config);
896
897 for i in 0..5 {
899 monitor.record_error(LarkAPIError::api_error(400 + i, "Test Error", None));
900 }
901
902 let recent = monitor.get_recent_events(10);
903 assert_eq!(recent.len(), 3); }
905
906 #[test]
907 fn test_error_monitor_cleanup_old_events() {
908 let config = MonitorConfig {
909 time_window: Duration::from_millis(100),
910 ..MonitorConfig::default()
911 };
912 let monitor = ErrorMonitor::new(config);
913
914 monitor.record_error(LarkAPIError::api_error(400, "Old Error", None));
915
916 thread::sleep(Duration::from_millis(150));
918
919 monitor.cleanup_old_events();
920
921 let recent = monitor.get_recent_events(10);
922 assert_eq!(recent.len(), 0); }
924
925 #[test]
926 fn test_error_monitor_reset_statistics() {
927 let monitor = ErrorMonitor::default();
928
929 monitor.record_error(LarkAPIError::api_error(403, "Forbidden", None));
930 monitor.record_error(LarkAPIError::api_error(500, "Server Error", None));
931
932 let stats_before = monitor.get_statistics();
933 assert_eq!(stats_before.total_errors, 2);
934
935 monitor.reset_statistics();
936
937 let stats_after = monitor.get_statistics();
938 assert_eq!(stats_after.total_errors, 0);
939 assert!(stats_after.errors_by_category.is_empty());
940
941 let events = monitor.get_recent_events(10);
942 assert_eq!(events.len(), 0);
943 }
944
945 #[test]
946 fn test_error_monitor_statistics_updates() {
947 let monitor = ErrorMonitor::default();
948
949 monitor.record_error(LarkAPIError::api_error(429, "Too Many Requests", None));
951 monitor.record_error(LarkAPIError::IllegalParamError("invalid".to_string()));
953
954 let stats = monitor.get_statistics();
955 assert_eq!(stats.total_errors, 2);
956 assert_eq!(stats.retryable_errors, 1);
957 assert!(stats.first_error_time.is_some());
958 assert!(stats.last_error_time.is_some());
959 }
960
961 #[test]
962 fn test_error_monitor_error_code_tracking() {
963 let monitor = ErrorMonitor::default();
964
965 monitor.record_error(LarkAPIError::api_error(403, "Forbidden", None));
966 monitor.record_error(LarkAPIError::api_error(403, "Forbidden", None));
967 monitor.record_error(LarkAPIError::api_error(500, "Server Error", None));
968
969 let stats = monitor.get_statistics();
970 assert_eq!(
971 stats.errors_by_code.get(&LarkErrorCode::Forbidden),
972 Some(&2)
973 );
974 assert_eq!(
975 stats
976 .errors_by_code
977 .get(&LarkErrorCode::InternalServerError),
978 Some(&1)
979 );
980 }
981
982 #[test]
983 fn test_error_monitor_generate_report() {
984 let monitor = ErrorMonitor::default();
985
986 monitor.record_error(LarkAPIError::api_error(403, "Forbidden", None));
987 monitor.record_error(LarkAPIError::api_error(500, "Server Error", None));
988
989 let report = monitor.generate_report();
990 assert_eq!(report.statistics.total_errors, 2);
991 assert!(report.recent_events.len() <= 10);
992 assert!(report.generated_at <= SystemTime::now());
993 assert_eq!(report.time_window, monitor.config.time_window);
994 }
995
996 #[test]
997 fn test_error_report_methods() {
998 let monitor = ErrorMonitor::default();
999 monitor.record_error(LarkAPIError::api_error(403, "Forbidden", None));
1000
1001 let report = monitor.generate_report();
1002
1003 report.print();
1005
1006 let temp_path = "/tmp/test_error_report.txt";
1008 let result = report.save_to_file(temp_path);
1009 assert!(result.is_ok());
1010
1011 let _ = std::fs::remove_file(temp_path);
1013 }
1014
1015 #[test]
1016 fn test_error_monitor_concurrent_access() {
1017 use std::sync::Arc;
1018 use std::thread;
1019
1020 let monitor = Arc::new(ErrorMonitor::default());
1021 let mut handles = vec![];
1022
1023 for i in 0..10 {
1025 let monitor_clone = Arc::clone(&monitor);
1026 let handle = thread::spawn(move || {
1027 monitor_clone.record_error(LarkAPIError::api_error(
1028 400 + i,
1029 "Concurrent Error",
1030 None,
1031 ));
1032 });
1033 handles.push(handle);
1034 }
1035
1036 for handle in handles {
1038 handle.join().unwrap();
1039 }
1040
1041 let stats = monitor.get_statistics();
1042 assert_eq!(stats.total_errors, 10);
1043 }
1044
1045 #[test]
1046 fn test_error_monitor_alert_triggering() {
1047 let config = MonitorConfig {
1048 alert_thresholds: AlertThresholds {
1049 critical_errors_count: 2,
1050 ..AlertThresholds::default()
1051 },
1052 ..MonitorConfig::default()
1053 };
1054 let monitor = ErrorMonitor::new(config);
1055
1056 monitor.record_error(LarkAPIError::api_error(500, "Server Error 1", None));
1058 monitor.record_error(LarkAPIError::api_error(500, "Server Error 2", None));
1059
1060 let stats = monitor.get_statistics();
1063 assert!(
1064 stats
1065 .errors_by_severity
1066 .get(&ErrorSeverity::Critical)
1067 .unwrap()
1068 >= &2
1069 );
1070 }
1071
1072 #[test]
1073 fn test_error_event_timestamp() {
1074 let error = LarkAPIError::api_error(400, "Test", None);
1075 let event = ErrorEvent::from_error(error);
1076 let now = SystemTime::now();
1077
1078 let diff = now.duration_since(event.timestamp).unwrap_or_default();
1080 assert!(diff.as_secs() < 1);
1081 }
1082}