1use dashmap::DashMap;
15use std::sync::Arc;
16pub use termivar_core::{Event, EventBuilder, EventSeverity, EventType};
17
18pub type EventHandler = Arc<dyn Fn(&Event) + Send + Sync>;
20
21pub struct Subscription {
23 pub event_type: EventType,
24 pub handler_id: String,
25}
26
27pub struct EventBus {
29 subscribers: Arc<DashMap<EventType, Vec<(String, EventHandler)>>>,
30 event_history: Arc<DashMap<String, Vec<Event>>>,
31 event_count: Arc<std::sync::atomic::AtomicU64>,
32}
33
34impl EventBus {
35 pub fn new() -> Self {
37 Self {
38 subscribers: Arc::new(DashMap::new()),
39 event_history: Arc::new(DashMap::new()),
40 event_count: Arc::new(std::sync::atomic::AtomicU64::new(0)),
41 }
42 }
43
44 pub fn subscribe(
46 &self,
47 event_type: EventType,
48 handler_id: impl Into<String>,
49 handler: EventHandler,
50 ) {
51 let handler_id = handler_id.into();
52 self.subscribers
53 .entry(event_type.clone())
54 .or_default()
55 .push((handler_id, handler));
56 }
57
58 pub fn unsubscribe(&self, event_type: &EventType, handler_id: &str) {
60 if let Some(mut handlers) = self.subscribers.get_mut(event_type) {
61 handlers.retain(|(id, _)| id != handler_id);
62 }
63 }
64
65 pub fn publish(&self, event: Event) {
67 self.event_count
68 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
69
70 self.event_history
72 .entry(event.event_type.as_str().to_string())
73 .or_default()
74 .push(event.clone());
75
76 if let Some(handlers) = self.subscribers.get(&event.event_type) {
78 for (_, handler) in handlers.iter() {
79 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
80 handler(&event);
81 }));
82 }
83 }
84 }
85
86 pub fn subscriber_count(&self, event_type: &EventType) -> usize {
88 self.subscribers
89 .get(event_type)
90 .map(|h| h.len())
91 .unwrap_or(0)
92 }
93
94 pub fn get_history(&self, event_type: &EventType) -> Vec<Event> {
96 self.event_history
97 .get(event_type.as_str())
98 .map(|h| h.clone())
99 .unwrap_or_default()
100 }
101
102 pub fn total_events(&self) -> u64 {
104 self.event_count.load(std::sync::atomic::Ordering::SeqCst)
105 }
106
107 pub fn clear_history(&self) {
109 self.event_history.clear();
110 }
111
112 pub fn get_all_events(&self) -> Vec<Event> {
114 self.event_history
115 .iter()
116 .flat_map(|ref_multi| ref_multi.value().clone())
117 .collect()
118 }
119
120 pub fn get_events_by_correlation(&self, correlation_id: &str) -> Vec<Event> {
122 self.get_all_events()
123 .into_iter()
124 .filter(|e| e.correlation_id == correlation_id)
125 .collect()
126 }
127
128 pub fn get_events_by_correlation_and_time(
130 &self,
131 correlation_id: &str,
132 start_ms: u64,
133 end_ms: u64,
134 ) -> Vec<Event> {
135 self.get_all_events()
136 .into_iter()
137 .filter(|e| {
138 e.correlation_id == correlation_id
139 && e.timestamp_ms >= start_ms
140 && e.timestamp_ms <= end_ms
141 })
142 .collect()
143 }
144
145 pub fn get_events_sorted(&self) -> Vec<Event> {
147 let mut events = self.get_all_events();
148 events.sort_by_key(|event| event.timestamp_ms);
149 events
150 }
151}
152
153impl Default for EventBus {
154 fn default() -> Self {
155 Self::new()
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162 use std::sync::atomic::AtomicBool;
163
164 #[test]
165 fn test_event_creation() {
166 let event = Event::new(EventType::ScanStarted, "scanner");
167 assert_eq!(event.event_type, EventType::ScanStarted);
168 assert_eq!(event.source, "scanner");
169 assert_eq!(event.version, 1);
170 assert!(!event.correlation_id.is_empty());
171 assert!(!event.event_id.is_empty());
172 }
173
174 #[test]
175 fn test_event_with_data() {
176 let event = Event::new(EventType::FindingFound, "plugin")
177 .with_data("severity", "HIGH")
178 .with_data("type", "XSS");
179
180 assert_eq!(event.data.get("severity"), Some(&"HIGH".to_string()));
181 assert_eq!(event.data.get("type"), Some(&"XSS".to_string()));
182 }
183
184 #[test]
185 fn test_event_severity() {
186 let event =
187 Event::new(EventType::ScanFailed, "worker").with_severity(EventSeverity::Critical);
188
189 assert_eq!(event.severity, EventSeverity::Critical);
190 }
191
192 #[test]
193 fn test_event_versioning() {
194 let event = Event::new(EventType::ScanStarted, "scanner").with_version(2);
195
196 assert_eq!(event.version, 2);
197 }
198
199 #[test]
200 fn test_event_correlation_id() {
201 let scan_id = "scan_12345";
202 let event = Event::new(EventType::FindingFound, "scanner").with_correlation_id(scan_id);
203
204 assert_eq!(event.correlation_id, scan_id);
205 }
206
207 #[test]
208 fn test_event_builder() {
209 let scan_id = "scan_abc";
210 let event = Event::builder(EventType::ScanStarted, "scanner")
211 .correlation_id(scan_id)
212 .version(1)
213 .severity(EventSeverity::Info)
214 .data("target", "http://example.com")
215 .build();
216
217 assert_eq!(event.correlation_id, scan_id);
218 assert_eq!(event.version, 1);
219 assert_eq!(event.severity, EventSeverity::Info);
220 assert_eq!(
221 event.data.get("target"),
222 Some(&"http://example.com".to_string())
223 );
224 }
225
226 #[test]
227 fn test_event_bus_creation() {
228 let bus = EventBus::new();
229 assert_eq!(bus.total_events(), 0);
230 }
231
232 #[test]
233 fn test_event_publishing() {
234 let bus = EventBus::new();
235 let event = Event::new(EventType::ScanStarted, "test");
236
237 bus.publish(event);
238 assert_eq!(bus.total_events(), 1);
239 }
240
241 #[test]
242 fn test_event_subscription() {
243 let bus = EventBus::new();
244 let called = Arc::new(AtomicBool::new(false));
245 let called_clone = called.clone();
246
247 bus.subscribe(
248 EventType::ScanStarted,
249 "test_handler",
250 Arc::new(move |_| {
251 called_clone.store(true, std::sync::atomic::Ordering::SeqCst);
252 }),
253 );
254
255 assert_eq!(bus.subscriber_count(&EventType::ScanStarted), 1);
256
257 let event = Event::new(EventType::ScanStarted, "test");
258 bus.publish(event);
259
260 assert!(called.load(std::sync::atomic::Ordering::SeqCst));
261 }
262
263 #[test]
264 fn test_multiple_subscribers() {
265 let bus = EventBus::new();
266 let count = Arc::new(std::sync::atomic::AtomicU32::new(0));
267
268 for i in 0..3 {
269 let count_clone = count.clone();
270 bus.subscribe(
271 EventType::FindingFound,
272 format!("handler_{i}"),
273 Arc::new(move |_| {
274 count_clone.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
275 }),
276 );
277 }
278
279 assert_eq!(bus.subscriber_count(&EventType::FindingFound), 3);
280
281 let event = Event::new(EventType::FindingFound, "test");
282 bus.publish(event);
283
284 assert_eq!(count.load(std::sync::atomic::Ordering::SeqCst), 3);
285 }
286
287 #[test]
288 fn test_event_unsubscribe() {
289 let bus = EventBus::new();
290
291 bus.subscribe(EventType::ScanCompleted, "handler1", Arc::new(|_| {}));
292 bus.subscribe(EventType::ScanCompleted, "handler2", Arc::new(|_| {}));
293
294 assert_eq!(bus.subscriber_count(&EventType::ScanCompleted), 2);
295
296 bus.unsubscribe(&EventType::ScanCompleted, "handler1");
297 assert_eq!(bus.subscriber_count(&EventType::ScanCompleted), 1);
298 }
299
300 #[test]
301 fn test_event_history() {
302 let bus = EventBus::new();
303
304 for i in 0..5 {
305 let event = Event::new(EventType::WorkerFinished, format!("worker_{i}"));
306 bus.publish(event);
307 }
308
309 let history = bus.get_history(&EventType::WorkerFinished);
310 assert_eq!(history.len(), 5);
311 }
312
313 #[test]
314 fn test_event_severity_ordering() {
315 assert!(EventSeverity::Critical > EventSeverity::Error);
316 assert!(EventSeverity::Error > EventSeverity::Warning);
317 assert!(EventSeverity::Warning > EventSeverity::Info);
318 assert!(EventSeverity::Info > EventSeverity::Debug);
319 }
320
321 #[test]
322 fn test_get_all_events() {
323 let bus = EventBus::new();
324
325 bus.publish(Event::new(EventType::ScanStarted, "test"));
326 bus.publish(Event::new(EventType::FindingFound, "test"));
327 bus.publish(Event::new(EventType::WorkerFinished, "test"));
328
329 let all = bus.get_all_events();
330 assert_eq!(all.len(), 3);
331 }
332
333 #[test]
334 fn test_clear_history() {
335 let bus = EventBus::new();
336
337 bus.publish(Event::new(EventType::ScanStarted, "test"));
338 bus.publish(Event::new(EventType::FindingFound, "test"));
339
340 assert!(bus.total_events() > 0);
341
342 bus.clear_history();
343 assert_eq!(bus.get_all_events().len(), 0);
344 }
345
346 #[test]
347 fn test_events_by_correlation_id() {
348 let bus = EventBus::new();
349 let scan_id = "scan_001";
350
351 bus.publish(Event::new(EventType::ScanStarted, "test").with_correlation_id(scan_id));
353 bus.publish(Event::new(EventType::FindingFound, "test").with_correlation_id(scan_id));
354 bus.publish(Event::new(EventType::FindingFound, "test").with_correlation_id(scan_id));
355
356 bus.publish(Event::new(EventType::ScanStarted, "test").with_correlation_id("scan_002"));
358
359 let events = bus.get_events_by_correlation(scan_id);
360 assert_eq!(events.len(), 3);
361 assert!(events.iter().all(|e| e.correlation_id == scan_id));
362 }
363
364 #[test]
365 fn test_events_by_correlation_and_time() {
366 let bus = EventBus::new();
367 let scan_id = "scan_001";
368 let now = std::time::SystemTime::now()
369 .duration_since(std::time::UNIX_EPOCH)
370 .unwrap()
371 .as_millis() as u64;
372
373 let event1 = Event::builder(EventType::ScanStarted, "test")
375 .correlation_id(scan_id)
376 .timestamp_ms(now)
377 .build();
378 bus.publish(event1);
379
380 let event2 = Event::builder(EventType::FindingFound, "test")
382 .correlation_id(scan_id)
383 .timestamp_ms(now + 1000)
384 .build();
385 bus.publish(event2);
386
387 let events = bus.get_events_by_correlation_and_time(scan_id, now, now + 1000);
389 assert_eq!(events.len(), 2);
390 }
391
392 #[test]
393 fn test_events_sorted_by_timestamp() {
394 let bus = EventBus::new();
395
396 let now = std::time::SystemTime::now()
397 .duration_since(std::time::UNIX_EPOCH)
398 .unwrap()
399 .as_millis() as u64;
400
401 bus.publish(
403 Event::builder(EventType::FindingFound, "test")
404 .timestamp_ms(now + 2000)
405 .build(),
406 );
407 bus.publish(
408 Event::builder(EventType::ScanStarted, "test")
409 .timestamp_ms(now)
410 .build(),
411 );
412 bus.publish(
413 Event::builder(EventType::WorkerFinished, "test")
414 .timestamp_ms(now + 1000)
415 .build(),
416 );
417
418 let sorted = bus.get_events_sorted();
419 assert_eq!(sorted.len(), 3);
420 assert!(sorted[0].timestamp_ms <= sorted[1].timestamp_ms);
421 assert!(sorted[1].timestamp_ms <= sorted[2].timestamp_ms);
422 }
423
424 #[test]
425 fn test_event_uniqueness() {
426 let event1 = Event::new(EventType::ScanStarted, "test");
427 let event2 = Event::new(EventType::ScanStarted, "test");
428
429 assert_ne!(event1.event_id, event2.event_id);
431 assert_ne!(event1.correlation_id, event2.correlation_id);
432 }
433
434 #[test]
435 fn test_timestamp_str() {
436 let event = Event::builder(EventType::ScanStarted, "test")
437 .timestamp_ms(1234567890123)
438 .build();
439
440 assert_eq!(event.timestamp_str(), "1234567890.123");
441 }
442
443 #[test]
446 fn test_concurrent_publications_1000() {
447 let bus = Arc::new(EventBus::new());
448 let mut handles = vec![];
449
450 for i in 0..1000 {
451 let bus_clone = bus.clone();
452 let handle = std::thread::spawn(move || {
453 let event = Event::builder(EventType::FindingFound, format!("thread_{i}"))
454 .correlation_id("scan_concurrent")
455 .build();
456 bus_clone.publish(event);
457 });
458 handles.push(handle);
459 }
460
461 for handle in handles {
463 handle.join().unwrap();
464 }
465
466 assert_eq!(bus.total_events(), 1000);
468 let events = bus.get_events_by_correlation("scan_concurrent");
469 assert_eq!(events.len(), 1000);
470 }
471
472 #[tokio::test]
473 async fn test_concurrent_async_publications() {
474 let bus = Arc::new(EventBus::new());
475 let mut tasks = vec![];
476
477 for i in 0..500 {
478 let bus_clone = bus.clone();
479 let task = tokio::spawn(async move {
480 let event =
481 Event::builder(EventType::WorkerFinished, format!("async_worker_{i}")).build();
482 bus_clone.publish(event);
483 });
484 tasks.push(task);
485 }
486
487 for task in tasks {
489 task.await.unwrap();
490 }
491
492 assert_eq!(bus.total_events(), 500);
493 }
494
495 #[test]
496 fn test_subscriber_panic_isolation() {
497 let bus = Arc::new(EventBus::new());
498 bus.subscribe(
499 EventType::ScanStarted,
500 "panicking_handler",
501 Arc::new(|_| {
502 panic!("subscriber fixture panic");
503 }),
504 );
505
506 let normal_called = Arc::new(std::sync::atomic::AtomicBool::new(false));
507 let normal_clone = normal_called.clone();
508 bus.subscribe(
509 EventType::ScanStarted,
510 "normal_handler",
511 Arc::new(move |_| {
512 normal_clone.store(true, std::sync::atomic::Ordering::SeqCst);
513 }),
514 );
515
516 bus.publish(Event::new(EventType::ScanStarted, "test"));
517
518 assert!(normal_called.load(std::sync::atomic::Ordering::SeqCst));
519 assert_eq!(bus.total_events(), 1);
520 }
521
522 #[test]
523 fn test_unsubscribe_memory_cleanup() {
524 let bus = EventBus::new();
525
526 for i in 0..100 {
528 bus.subscribe(
529 EventType::FindingFound,
530 format!("handler_{i}"),
531 Arc::new(|_| {}),
532 );
533 }
534
535 assert_eq!(bus.subscriber_count(&EventType::FindingFound), 100);
536
537 for i in 0..100 {
539 bus.unsubscribe(&EventType::FindingFound, &format!("handler_{i}"));
540 }
541
542 assert_eq!(bus.subscriber_count(&EventType::FindingFound), 0);
544 }
545
546 #[test]
547 fn test_event_order_preservation() {
548 let bus = EventBus::new();
549 let scan_id = "order_test";
550 let now = std::time::SystemTime::now()
551 .duration_since(std::time::UNIX_EPOCH)
552 .unwrap()
553 .as_millis() as u64;
554
555 for i in 0..100 {
557 let event = Event::builder(EventType::FindingFound, "test")
558 .correlation_id(scan_id)
559 .timestamp_ms(now + i as u64)
560 .build();
561 bus.publish(event);
562 }
563
564 let events = bus.get_events_sorted();
565
566 for i in 0..events.len() - 1 {
568 assert!(
569 events[i].timestamp_ms <= events[i + 1].timestamp_ms,
570 "Events not in chronological order"
571 );
572 }
573 }
574
575 #[tokio::test]
576 async fn test_slow_subscriber_nonblocking() {
577 let bus = Arc::new(EventBus::new());
578 let slow_counter = Arc::new(std::sync::atomic::AtomicU32::new(0));
579 let fast_counter = Arc::new(std::sync::atomic::AtomicU32::new(0));
580
581 let slow_clone = slow_counter.clone();
583 bus.subscribe(
584 EventType::WorkerFinished,
585 "slow",
586 Arc::new(move |_| {
587 std::thread::sleep(std::time::Duration::from_millis(10));
589 slow_clone.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
590 }),
591 );
592
593 let fast_clone = fast_counter.clone();
595 bus.subscribe(
596 EventType::WorkerFinished,
597 "fast",
598 Arc::new(move |_| {
599 fast_clone.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
600 }),
601 );
602
603 for _ in 0..10 {
605 let event = Event::new(EventType::WorkerFinished, "test");
606 bus.publish(event);
607 }
608
609 assert_eq!(fast_counter.load(std::sync::atomic::Ordering::SeqCst), 10);
611 assert_eq!(slow_counter.load(std::sync::atomic::Ordering::SeqCst), 10);
612 }
613
614 #[test]
615 fn test_concurrent_subscribe_unsubscribe() {
616 let bus = Arc::new(EventBus::new());
617 let mut handles = vec![];
618
619 for i in 0..200 {
621 let bus_clone = bus.clone();
622 let handle = std::thread::spawn(move || {
623 if i % 2 == 0 {
624 bus_clone.subscribe(
626 EventType::ScanCompleted,
627 format!("handler_{i}"),
628 Arc::new(|_| {}),
629 );
630 } else if i > 1 {
631 bus_clone.unsubscribe(&EventType::ScanCompleted, &format!("handler_{}", i - 1));
633 }
634 });
635 handles.push(handle);
636 }
637
638 for handle in handles {
639 handle.join().unwrap();
640 }
641
642 let count = bus.subscriber_count(&EventType::ScanCompleted);
644 assert!(count <= 200);
645 }
646
647 #[test]
648 fn test_large_event_payload() {
649 let bus = EventBus::new();
650 let large_data = "x".repeat(1_000_000); let event = Event::builder(EventType::FindingFound, "test")
653 .data("large_payload", large_data.clone())
654 .build();
655
656 bus.publish(event);
657
658 let retrieved = bus.get_all_events();
659 assert_eq!(retrieved.len(), 1);
660 assert_eq!(retrieved[0].data.get("large_payload"), Some(&large_data));
661 }
662
663 #[test]
664 fn test_many_subscribers_single_event() {
665 let bus = Arc::new(EventBus::new());
666 let counter = Arc::new(std::sync::atomic::AtomicU32::new(0));
667
668 for i in 0..500 {
670 let counter_clone = counter.clone();
671 bus.subscribe(
672 EventType::AlertTriggered,
673 format!("listener_{i}"),
674 Arc::new(move |_| {
675 counter_clone.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
676 }),
677 );
678 }
679
680 assert_eq!(bus.subscriber_count(&EventType::AlertTriggered), 500);
681
682 let event = Event::new(EventType::AlertTriggered, "test");
683 bus.publish(event);
684
685 assert_eq!(counter.load(std::sync::atomic::Ordering::SeqCst), 500);
687 }
688
689 #[test]
690 fn test_event_history_memory_pressure() {
691 let bus = EventBus::new();
692
693 for i in 0..10_000 {
695 let event = Event::builder(EventType::FindingFound, "test")
696 .data("index", i.to_string())
697 .build();
698 bus.publish(event);
699 }
700
701 assert_eq!(bus.total_events(), 10_000);
702
703 let all = bus.get_all_events();
705 assert_eq!(all.len(), 10_000);
706
707 bus.clear_history();
709 assert_eq!(bus.get_all_events().len(), 0);
710 assert_eq!(bus.total_events(), 10_000); }
712
713 #[tokio::test]
714 async fn test_concurrent_correlation_queries() {
715 let bus = Arc::new(EventBus::new());
716
717 for scan_id in 0..10 {
719 for _ in 0..100 {
720 let event = Event::builder(EventType::FindingFound, "test")
721 .correlation_id(format!("scan_{scan_id}"))
722 .build();
723 bus.publish(event);
724 }
725 }
726
727 let mut tasks = vec![];
729 for scan_id in 0..10 {
730 let bus_clone = bus.clone();
731 let task = tokio::spawn(async move {
732 let events = bus_clone.get_events_by_correlation(&format!("scan_{scan_id}"));
733 assert_eq!(events.len(), 100);
734 });
735 tasks.push(task);
736 }
737
738 for task in tasks {
739 task.await.unwrap();
740 }
741 }
742}