stem_rs/response/
events.rs

1//! Event parsing for Tor control protocol async notifications.
2//!
3//! This module provides parsing for asynchronous event messages received from
4//! Tor's control protocol. Events are notifications sent by Tor when certain
5//! conditions occur, such as bandwidth usage, circuit state changes, or log
6//! messages.
7//!
8//! # Event Format
9//!
10//! Events are sent with status code 650 and follow this format:
11//!
12//! ```text
13//! 650 EVENT_TYPE [positional_args] [KEY=value ...]
14//! ```
15//!
16//! Multi-line events use the extended format:
17//!
18//! ```text
19//! 650-EVENT_TYPE [args]
20//! 650-additional data
21//! 650 OK
22//! ```
23//!
24//! # Supported Event Types
25//!
26//! | Event | Description |
27//! |-------|-------------|
28//! | `BW` | Bandwidth usage (bytes read/written per second) |
29//! | `CIRC` | Circuit status changes |
30//! | `STREAM` | Stream status changes |
31//! | `ORCONN` | OR connection status changes |
32//! | `NOTICE`, `WARN`, `ERR` | Log messages at various levels |
33//! | `NEWDESC` | New relay descriptors available |
34//! | `ADDRMAP` | Address mapping changes |
35//! | `SIGNAL` | Signal received by Tor |
36//! | `HS_DESC` | Hidden service descriptor events |
37//! | And many more... | See [`EventType`] for full list |
38//!
39//! # Example
40//!
41//! ```rust
42//! use stem_rs::response::{ControlMessage, events::parse_event};
43//!
44//! // Parse a bandwidth event
45//! let msg = ControlMessage::from_str("650 BW 1024 2048", None, true).unwrap();
46//! let event = parse_event(&msg).unwrap();
47//!
48//! // Events are returned as ParsedEvent enum variants
49//! match event {
50//!     stem_rs::events::ParsedEvent::Bandwidth(bw) => {
51//!         println!("Read: {} bytes, Written: {} bytes", bw.read, bw.written);
52//!     }
53//!     _ => {}
54//! }
55//! ```
56//!
57//! # See Also
58//!
59//! - [`crate::events`]: Event type definitions and structures
60//! - [`crate::EventType`]: Enumeration of all event types
61//! - [Tor Control Protocol: Async Events](https://spec.torproject.org/control-spec/replies.html#async-events)
62
63use crate::events::ParsedEvent;
64use crate::{Error, EventType};
65
66use super::ControlMessage;
67
68pub use crate::events::{Event, ParsedEvent as EventEnum};
69
70/// Parses an event from a control message.
71///
72/// Extracts the event type and content from a 650-status message and
73/// returns the appropriate [`ParsedEvent`] variant.
74///
75/// # Arguments
76///
77/// * `message` - The control message to parse (must have status code 650)
78///
79/// # Returns
80///
81/// A [`ParsedEvent`] variant corresponding to the event type. Unknown
82/// event types are returned as [`ParsedEvent::Unknown`].
83///
84/// # Errors
85///
86/// Returns [`Error::Protocol`] if:
87/// - The message is empty
88/// - The status code is not 650
89/// - The event content is empty
90/// - The event-specific parsing fails
91///
92/// # Example
93///
94/// ```rust
95/// use stem_rs::response::{ControlMessage, events::parse_event};
96/// use stem_rs::events::ParsedEvent;
97///
98/// // Parse a circuit event
99/// let msg = ControlMessage::from_str("650 CIRC 1 BUILT", None, true).unwrap();
100/// let event = parse_event(&msg).unwrap();
101///
102/// match event {
103///     ParsedEvent::Circuit(circ) => {
104///         println!("Circuit {} status changed", circ.id.0);
105///     }
106///     _ => {}
107/// }
108///
109/// // Unknown events are still parsed
110/// let msg = ControlMessage::from_str("650 FUTURE_EVENT data", None, true).unwrap();
111/// let event = parse_event(&msg).unwrap();
112///
113/// match event {
114///     ParsedEvent::Unknown { event_type, content } => {
115///         println!("Unknown event: {} - {}", event_type, content);
116///     }
117///     _ => {}
118/// }
119/// ```
120pub fn parse_event(message: &ControlMessage) -> Result<ParsedEvent, Error> {
121    let content = message.content();
122    if content.is_empty() {
123        return Err(Error::Protocol("Empty event message".to_string()));
124    }
125
126    let (code, _, first_line) = &content[0];
127
128    if code != "650" {
129        return Err(Error::Protocol(format!(
130            "Expected event status code 650, got {}",
131            code
132        )));
133    }
134
135    let event_type = first_line
136        .split_whitespace()
137        .next()
138        .ok_or_else(|| Error::Protocol("Empty event content".to_string()))?;
139
140    let event_content = first_line
141        .strip_prefix(event_type)
142        .map(|s| s.trim_start())
143        .unwrap_or("");
144
145    let lines: Vec<String> = if content.len() > 1 {
146        content[1..]
147            .iter()
148            .filter(|(_, _, line)| !line.is_empty() && line != "OK")
149            .map(|(_, _, line)| line.clone())
150            .collect()
151    } else {
152        Vec::new()
153    };
154
155    ParsedEvent::parse(event_type, event_content, Some(&lines))
156}
157
158/// Converts an event type string to its corresponding [`EventType`] enum.
159///
160/// This function maps event type names (as they appear in control protocol
161/// messages) to the [`EventType`] enum used for event subscription.
162///
163/// # Arguments
164///
165/// * `event_type` - The event type string (case-insensitive)
166///
167/// # Returns
168///
169/// `Some(EventType)` if the event type is recognized, `None` otherwise.
170///
171/// # Example
172///
173/// ```rust
174/// use stem_rs::response::events::event_type_to_class;
175/// use stem_rs::EventType;
176///
177/// assert_eq!(event_type_to_class("BW"), Some(EventType::Bw));
178/// assert_eq!(event_type_to_class("CIRC"), Some(EventType::Circ));
179/// assert_eq!(event_type_to_class("bw"), Some(EventType::Bw)); // Case-insensitive
180/// assert_eq!(event_type_to_class("UNKNOWN"), None);
181/// ```
182///
183/// # Note
184///
185/// The `STATUS_CLIENT`, `STATUS_GENERAL`, and `STATUS_SERVER` event types
186/// all map to [`EventType::Status`] since they share the same structure.
187pub fn event_type_to_class(event_type: &str) -> Option<EventType> {
188    match event_type.to_uppercase().as_str() {
189        "ADDRMAP" => Some(EventType::AddrMap),
190        "BUILDTIMEOUT_SET" => Some(EventType::BuildTimeoutSet),
191        "BW" => Some(EventType::Bw),
192        "CELL_STATS" => Some(EventType::CellStats),
193        "CIRC" => Some(EventType::Circ),
194        "CIRC_BW" => Some(EventType::CircBw),
195        "CIRC_MINOR" => Some(EventType::CircMinor),
196        "CLIENTS_SEEN" => Some(EventType::ClientsSeen),
197        "CONF_CHANGED" => Some(EventType::ConfChanged),
198        "CONN_BW" => Some(EventType::ConnBw),
199        "DEBUG" => Some(EventType::Debug),
200        "DESCCHANGED" => Some(EventType::DescChanged),
201        "ERR" => Some(EventType::Err),
202        "GUARD" => Some(EventType::Guard),
203        "HS_DESC" => Some(EventType::HsDesc),
204        "HS_DESC_CONTENT" => Some(EventType::HsDescContent),
205        "INFO" => Some(EventType::Info),
206        "NETWORK_LIVENESS" => Some(EventType::NetworkLiveness),
207        "NEWCONSENSUS" => Some(EventType::NewConsensus),
208        "NEWDESC" => Some(EventType::NewDesc),
209        "NOTICE" => Some(EventType::Notice),
210        "NS" => Some(EventType::Ns),
211        "ORCONN" => Some(EventType::OrConn),
212        "SIGNAL" => Some(EventType::Signal),
213        "STATUS_CLIENT" | "STATUS_GENERAL" | "STATUS_SERVER" => Some(EventType::Status),
214        "STREAM" => Some(EventType::Stream),
215        "STREAM_BW" => Some(EventType::StreamBw),
216        "TRANSPORT_LAUNCHED" => Some(EventType::TransportLaunched),
217        "WARN" => Some(EventType::Warn),
218        _ => None,
219    }
220}
221
222/// Checks if an event type string is a known/recognized event type.
223///
224/// This is a convenience function that returns `true` if the event type
225/// can be mapped to an [`EventType`] enum variant.
226///
227/// # Arguments
228///
229/// * `event_type` - The event type string to check (case-insensitive)
230///
231/// # Returns
232///
233/// `true` if the event type is recognized, `false` otherwise.
234///
235/// # Example
236///
237/// ```rust
238/// use stem_rs::response::events::is_known_event_type;
239///
240/// assert!(is_known_event_type("BW"));
241/// assert!(is_known_event_type("CIRC"));
242/// assert!(is_known_event_type("stream")); // Case-insensitive
243/// assert!(!is_known_event_type("UNKNOWN_EVENT"));
244/// ```
245pub fn is_known_event_type(event_type: &str) -> bool {
246    event_type_to_class(event_type).is_some()
247}
248
249#[cfg(test)]
250mod tests {
251    use super::*;
252    use crate::Runlevel;
253
254    #[test]
255    fn test_parse_bandwidth_event() {
256        let msg = ControlMessage::from_str("650 BW 100 200", None, true).unwrap();
257        let event = parse_event(&msg).unwrap();
258        match event {
259            ParsedEvent::Bandwidth(bw) => {
260                assert_eq!(bw.read, 100);
261                assert_eq!(bw.written, 200);
262            }
263            _ => panic!("Expected bandwidth event"),
264        }
265    }
266
267    #[test]
268    fn test_parse_bandwidth_event_zero() {
269        let msg = ControlMessage::from_str("650 BW 0 0", None, true).unwrap();
270        let event = parse_event(&msg).unwrap();
271        match event {
272            ParsedEvent::Bandwidth(bw) => {
273                assert_eq!(bw.read, 0);
274                assert_eq!(bw.written, 0);
275            }
276            _ => panic!("Expected bandwidth event"),
277        }
278    }
279
280    #[test]
281    fn test_parse_circuit_event() {
282        let msg = ControlMessage::from_str("650 CIRC 1 BUILT", None, true).unwrap();
283        let event = parse_event(&msg).unwrap();
284        match event {
285            ParsedEvent::Circuit(circ) => {
286                assert_eq!(circ.id.0, "1");
287            }
288            _ => panic!("Expected circuit event"),
289        }
290    }
291
292    #[test]
293    fn test_parse_circuit_launched() {
294        let msg = ControlMessage::from_str("650 CIRC 7 LAUNCHED", None, true).unwrap();
295        let event = parse_event(&msg).unwrap();
296        match event {
297            ParsedEvent::Circuit(circ) => {
298                assert_eq!(circ.id.0, "7");
299            }
300            _ => panic!("Expected circuit event"),
301        }
302    }
303
304    #[test]
305    fn test_parse_log_event() {
306        let msg = ControlMessage::from_str("650 NOTICE test message", None, true).unwrap();
307        let event = parse_event(&msg).unwrap();
308        match event {
309            ParsedEvent::Log(log) => {
310                assert_eq!(log.runlevel, Runlevel::Notice);
311                assert_eq!(log.message, "test message");
312            }
313            _ => panic!("Expected log event"),
314        }
315    }
316
317    #[test]
318    fn test_parse_debug_log_event() {
319        let msg = ControlMessage::from_str(
320            "650 DEBUG connection_edge_process_relay_cell(): Got an extended cell! Yay.",
321            None,
322            true,
323        )
324        .unwrap();
325        let event = parse_event(&msg).unwrap();
326        match event {
327            ParsedEvent::Log(log) => {
328                assert_eq!(log.runlevel, Runlevel::Debug);
329                assert_eq!(
330                    log.message,
331                    "connection_edge_process_relay_cell(): Got an extended cell! Yay."
332                );
333            }
334            _ => panic!("Expected log event"),
335        }
336    }
337
338    #[test]
339    fn test_parse_info_log_event() {
340        let msg = ControlMessage::from_str(
341            "650 INFO circuit_finish_handshake(): Finished building circuit hop:",
342            None,
343            true,
344        )
345        .unwrap();
346        let event = parse_event(&msg).unwrap();
347        match event {
348            ParsedEvent::Log(log) => {
349                assert_eq!(log.runlevel, Runlevel::Info);
350                assert_eq!(
351                    log.message,
352                    "circuit_finish_handshake(): Finished building circuit hop:"
353                );
354            }
355            _ => panic!("Expected log event"),
356        }
357    }
358
359    #[test]
360    fn test_parse_stream_event_new() {
361        let msg = ControlMessage::from_str(
362            "650 STREAM 18 NEW 0 encrypted.google.com:443 SOURCE_ADDR=127.0.0.1:47849 PURPOSE=USER",
363            None,
364            true,
365        )
366        .unwrap();
367        let event = parse_event(&msg).unwrap();
368        match event {
369            ParsedEvent::Stream(stream) => {
370                assert_eq!(stream.id.0, "18");
371                assert_eq!(stream.target_host, "encrypted.google.com");
372                assert_eq!(stream.target_port, 443);
373            }
374            _ => panic!("Expected stream event"),
375        }
376    }
377
378    #[test]
379    fn test_parse_stream_event_succeeded() {
380        let msg =
381            ControlMessage::from_str("650 STREAM 18 SUCCEEDED 26 74.125.227.129:443", None, true)
382                .unwrap();
383        let event = parse_event(&msg).unwrap();
384        match event {
385            ParsedEvent::Stream(stream) => {
386                assert_eq!(stream.id.0, "18");
387                assert_eq!(stream.target_host, "74.125.227.129");
388                assert_eq!(stream.target_port, 443);
389            }
390            _ => panic!("Expected stream event"),
391        }
392    }
393
394    #[test]
395    fn test_event_type_mapping() {
396        assert_eq!(event_type_to_class("BW"), Some(EventType::Bw));
397        assert_eq!(event_type_to_class("CIRC"), Some(EventType::Circ));
398        assert_eq!(event_type_to_class("STREAM"), Some(EventType::Stream));
399        assert_eq!(event_type_to_class("UNKNOWN"), None);
400    }
401
402    #[test]
403    fn test_event_type_mapping_all_types() {
404        assert_eq!(event_type_to_class("ADDRMAP"), Some(EventType::AddrMap));
405        assert_eq!(
406            event_type_to_class("BUILDTIMEOUT_SET"),
407            Some(EventType::BuildTimeoutSet)
408        );
409        assert_eq!(event_type_to_class("BW"), Some(EventType::Bw));
410        assert_eq!(
411            event_type_to_class("CELL_STATS"),
412            Some(EventType::CellStats)
413        );
414        assert_eq!(event_type_to_class("CIRC"), Some(EventType::Circ));
415        assert_eq!(event_type_to_class("CIRC_BW"), Some(EventType::CircBw));
416        assert_eq!(
417            event_type_to_class("CIRC_MINOR"),
418            Some(EventType::CircMinor)
419        );
420        assert_eq!(
421            event_type_to_class("CLIENTS_SEEN"),
422            Some(EventType::ClientsSeen)
423        );
424        assert_eq!(
425            event_type_to_class("CONF_CHANGED"),
426            Some(EventType::ConfChanged)
427        );
428        assert_eq!(event_type_to_class("CONN_BW"), Some(EventType::ConnBw));
429        assert_eq!(event_type_to_class("DEBUG"), Some(EventType::Debug));
430        assert_eq!(
431            event_type_to_class("DESCCHANGED"),
432            Some(EventType::DescChanged)
433        );
434        assert_eq!(event_type_to_class("ERR"), Some(EventType::Err));
435        assert_eq!(event_type_to_class("GUARD"), Some(EventType::Guard));
436        assert_eq!(event_type_to_class("HS_DESC"), Some(EventType::HsDesc));
437        assert_eq!(
438            event_type_to_class("HS_DESC_CONTENT"),
439            Some(EventType::HsDescContent)
440        );
441        assert_eq!(event_type_to_class("INFO"), Some(EventType::Info));
442        assert_eq!(
443            event_type_to_class("NETWORK_LIVENESS"),
444            Some(EventType::NetworkLiveness)
445        );
446        assert_eq!(
447            event_type_to_class("NEWCONSENSUS"),
448            Some(EventType::NewConsensus)
449        );
450        assert_eq!(event_type_to_class("NEWDESC"), Some(EventType::NewDesc));
451        assert_eq!(event_type_to_class("NOTICE"), Some(EventType::Notice));
452        assert_eq!(event_type_to_class("NS"), Some(EventType::Ns));
453        assert_eq!(event_type_to_class("ORCONN"), Some(EventType::OrConn));
454        assert_eq!(event_type_to_class("SIGNAL"), Some(EventType::Signal));
455        assert_eq!(
456            event_type_to_class("STATUS_CLIENT"),
457            Some(EventType::Status)
458        );
459        assert_eq!(
460            event_type_to_class("STATUS_GENERAL"),
461            Some(EventType::Status)
462        );
463        assert_eq!(
464            event_type_to_class("STATUS_SERVER"),
465            Some(EventType::Status)
466        );
467        assert_eq!(event_type_to_class("STREAM"), Some(EventType::Stream));
468        assert_eq!(event_type_to_class("STREAM_BW"), Some(EventType::StreamBw));
469        assert_eq!(
470            event_type_to_class("TRANSPORT_LAUNCHED"),
471            Some(EventType::TransportLaunched)
472        );
473        assert_eq!(event_type_to_class("WARN"), Some(EventType::Warn));
474    }
475
476    #[test]
477    fn test_event_type_mapping_case_insensitive() {
478        assert_eq!(event_type_to_class("bw"), Some(EventType::Bw));
479        assert_eq!(event_type_to_class("Bw"), Some(EventType::Bw));
480        assert_eq!(event_type_to_class("circ"), Some(EventType::Circ));
481        assert_eq!(event_type_to_class("Circ"), Some(EventType::Circ));
482    }
483
484    #[test]
485    fn test_is_known_event_type() {
486        assert!(is_known_event_type("BW"));
487        assert!(is_known_event_type("CIRC"));
488        assert!(is_known_event_type("STREAM"));
489        assert!(!is_known_event_type("UNKNOWN_EVENT"));
490    }
491
492    #[test]
493    fn test_is_known_event_type_case_insensitive() {
494        assert!(is_known_event_type("bw"));
495        assert!(is_known_event_type("circ"));
496        assert!(is_known_event_type("stream"));
497    }
498
499    #[test]
500    fn test_parse_event_wrong_status_code() {
501        let msg = ControlMessage::from_str("250 OK", None, true).unwrap();
502        let result = parse_event(&msg);
503        assert!(result.is_err());
504        let err = result.unwrap_err();
505        assert!(err.to_string().contains("Expected event status code 650"));
506    }
507
508    #[test]
509    fn test_parse_signal_event() {
510        let msg = ControlMessage::from_str("650 SIGNAL NEWNYM", None, true).unwrap();
511        let event = parse_event(&msg).unwrap();
512        match event {
513            ParsedEvent::Signal(sig) => {
514                assert!(matches!(sig.signal, crate::Signal::Newnym));
515            }
516            _ => panic!("Expected signal event"),
517        }
518    }
519
520    #[test]
521    fn test_parse_network_liveness_up() {
522        let msg = ControlMessage::from_str("650 NETWORK_LIVENESS UP", None, true).unwrap();
523        let event = parse_event(&msg).unwrap();
524        match event {
525            ParsedEvent::NetworkLiveness(nl) => {
526                assert_eq!(nl.status, "UP");
527            }
528            _ => panic!("Expected network liveness event"),
529        }
530    }
531
532    #[test]
533    fn test_parse_network_liveness_down() {
534        let msg = ControlMessage::from_str("650 NETWORK_LIVENESS DOWN", None, true).unwrap();
535        let event = parse_event(&msg).unwrap();
536        match event {
537            ParsedEvent::NetworkLiveness(nl) => {
538                assert_eq!(nl.status, "DOWN");
539            }
540            _ => panic!("Expected network liveness event"),
541        }
542    }
543
544    #[test]
545    fn test_parse_guard_event_new() {
546        let msg = ControlMessage::from_str(
547            "650 GUARD ENTRY $36B5DBA788246E8369DBAF58577C6BC044A9A374 NEW",
548            None,
549            true,
550        )
551        .unwrap();
552        let event = parse_event(&msg).unwrap();
553        match event {
554            ParsedEvent::Guard(guard) => {
555                assert_eq!(
556                    guard.endpoint_fingerprint,
557                    "36B5DBA788246E8369DBAF58577C6BC044A9A374"
558                );
559            }
560            _ => panic!("Expected guard event"),
561        }
562    }
563
564    #[test]
565    fn test_parse_newdesc_single() {
566        let msg = ControlMessage::from_str(
567            "650 NEWDESC $B3FA3110CC6F42443F039220C134CBD2FC4F0493=Sakura",
568            None,
569            true,
570        )
571        .unwrap();
572        let event = parse_event(&msg).unwrap();
573        match event {
574            ParsedEvent::NewDesc(nd) => {
575                assert!(!nd.relays.is_empty());
576            }
577            _ => panic!("Expected newdesc event"),
578        }
579    }
580
581    #[test]
582    fn test_parse_status_general_event() {
583        let msg =
584            ControlMessage::from_str("650 STATUS_GENERAL NOTICE CONSENSUS_ARRIVED", None, true)
585                .unwrap();
586        let event = parse_event(&msg).unwrap();
587        match event {
588            ParsedEvent::Status(status) => {
589                assert_eq!(status.runlevel, Runlevel::Notice);
590                assert_eq!(status.action, "CONSENSUS_ARRIVED");
591            }
592            _ => panic!("Expected status event"),
593        }
594    }
595
596    #[test]
597    fn test_parse_status_client_event() {
598        let msg = ControlMessage::from_str("650 STATUS_CLIENT NOTICE ENOUGH_DIR_INFO", None, true)
599            .unwrap();
600        let event = parse_event(&msg).unwrap();
601        match event {
602            ParsedEvent::Status(status) => {
603                assert_eq!(status.runlevel, Runlevel::Notice);
604                assert_eq!(status.action, "ENOUGH_DIR_INFO");
605            }
606            _ => panic!("Expected status event"),
607        }
608    }
609
610    #[test]
611    fn test_parse_status_client_circuit_established() {
612        let msg =
613            ControlMessage::from_str("650 STATUS_CLIENT NOTICE CIRCUIT_ESTABLISHED", None, true)
614                .unwrap();
615        let event = parse_event(&msg).unwrap();
616        match event {
617            ParsedEvent::Status(status) => {
618                assert_eq!(status.runlevel, Runlevel::Notice);
619                assert_eq!(status.action, "CIRCUIT_ESTABLISHED");
620            }
621            _ => panic!("Expected status event"),
622        }
623    }
624
625    #[test]
626    fn test_parse_circ_bw_event() {
627        let msg =
628            ControlMessage::from_str("650 CIRC_BW ID=11 READ=272 WRITTEN=817", None, true).unwrap();
629        let event = parse_event(&msg).unwrap();
630        match event {
631            ParsedEvent::CircuitBandwidth(cb) => {
632                assert_eq!(cb.id.0, "11");
633                assert_eq!(cb.read, 272);
634                assert_eq!(cb.written, 817);
635                assert_eq!(cb.delivered_read, None);
636                assert_eq!(cb.delivered_written, None);
637                assert_eq!(cb.overhead_read, None);
638                assert_eq!(cb.overhead_written, None);
639            }
640            _ => panic!("Expected circuit bandwidth event"),
641        }
642    }
643
644    #[test]
645    fn test_parse_circ_bw_event_with_delivered_overhead() {
646        let msg = ControlMessage::from_str(
647            "650 CIRC_BW ID=11 READ=1000 WRITTEN=2000 DELIVERED_READ=800 DELIVERED_WRITTEN=1600 OVERHEAD_READ=200 OVERHEAD_WRITTEN=400",
648            None,
649            true,
650        )
651        .unwrap();
652        let event = parse_event(&msg).unwrap();
653        match event {
654            ParsedEvent::CircuitBandwidth(cb) => {
655                assert_eq!(cb.id.0, "11");
656                assert_eq!(cb.read, 1000);
657                assert_eq!(cb.written, 2000);
658                assert_eq!(cb.delivered_read, Some(800));
659                assert_eq!(cb.delivered_written, Some(1600));
660                assert_eq!(cb.overhead_read, Some(200));
661                assert_eq!(cb.overhead_written, Some(400));
662            }
663            _ => panic!("Expected circuit bandwidth event"),
664        }
665    }
666
667    #[test]
668    fn test_parse_conn_bw_event() {
669        let msg = ControlMessage::from_str(
670            "650 CONN_BW ID=11 TYPE=DIR READ=272 WRITTEN=817",
671            None,
672            true,
673        )
674        .unwrap();
675        let event = parse_event(&msg).unwrap();
676        match event {
677            ParsedEvent::ConnectionBandwidth(cb) => {
678                assert_eq!(cb.id, "11");
679                assert_eq!(cb.read, 272);
680                assert_eq!(cb.written, 817);
681            }
682            _ => panic!("Expected connection bandwidth event"),
683        }
684    }
685
686    #[test]
687    fn test_parse_orconn_closed() {
688        let msg = ControlMessage::from_str(
689            "650 ORCONN $A1130635A0CDA6F60C276FBF6994EFBD4ECADAB1~tama CLOSED REASON=DONE",
690            None,
691            true,
692        )
693        .unwrap();
694        let event = parse_event(&msg).unwrap();
695        match event {
696            ParsedEvent::OrConn(orconn) => {
697                assert_eq!(
698                    orconn.target,
699                    "$A1130635A0CDA6F60C276FBF6994EFBD4ECADAB1~tama"
700                );
701            }
702            _ => panic!("Expected orconn event"),
703        }
704    }
705
706    #[test]
707    fn test_parse_orconn_connected() {
708        let msg = ControlMessage::from_str(
709            "650 ORCONN 127.0.0.1:9000 CONNECTED NCIRCS=20 ID=18",
710            None,
711            true,
712        )
713        .unwrap();
714        let event = parse_event(&msg).unwrap();
715        match event {
716            ParsedEvent::OrConn(orconn) => {
717                assert_eq!(orconn.target, "127.0.0.1:9000");
718                assert_eq!(orconn.num_circuits, Some(20));
719                assert_eq!(orconn.id, Some("18".to_string()));
720            }
721            _ => panic!("Expected orconn event"),
722        }
723    }
724
725    #[test]
726    fn test_parse_orconn_launched() {
727        let msg = ControlMessage::from_str(
728            "650 ORCONN $7ED90E2833EE38A75795BA9237B0A4560E51E1A0=GreenDragon LAUNCHED",
729            None,
730            true,
731        )
732        .unwrap();
733        let event = parse_event(&msg).unwrap();
734        match event {
735            ParsedEvent::OrConn(orconn) => {
736                assert_eq!(
737                    orconn.target,
738                    "$7ED90E2833EE38A75795BA9237B0A4560E51E1A0=GreenDragon"
739                );
740            }
741            _ => panic!("Expected orconn event"),
742        }
743    }
744
745    #[test]
746    fn test_parse_unknown_event() {
747        let msg = ControlMessage::from_str("650 UNKNOWN_EVENT some content", None, true).unwrap();
748        let event = parse_event(&msg).unwrap();
749        match event {
750            ParsedEvent::Unknown {
751                event_type,
752                content,
753            } => {
754                assert_eq!(event_type, "UNKNOWN_EVENT");
755                assert_eq!(content, "some content");
756            }
757            _ => panic!("Expected unknown event"),
758        }
759    }
760
761    #[test]
762    fn test_parse_addrmap_event() {
763        let msg =
764            ControlMessage::from_str("650 ADDRMAP www.example.com 192.0.2.1 NEVER", None, true)
765                .unwrap();
766        let event = parse_event(&msg).unwrap();
767        match event {
768            ParsedEvent::AddrMap(am) => {
769                assert_eq!(am.hostname, "www.example.com");
770                assert_eq!(am.destination, Some("192.0.2.1".to_string()));
771            }
772            _ => panic!("Expected addrmap event"),
773        }
774    }
775
776    #[test]
777    fn test_parse_hs_desc_event() {
778        let msg = ControlMessage::from_str(
779            "650 HS_DESC REQUESTED ajhb7kljbiru65qo NO_AUTH $67B2BDA4264D8A189D9270E28B1D30A262838243=europa1 b3oeducbhjmbqmgw2i3jtz4fekkrinwj",
780            None,
781            true,
782        )
783        .unwrap();
784        let event = parse_event(&msg).unwrap();
785        match event {
786            ParsedEvent::HsDesc(hsd) => {
787                assert_eq!(hsd.address, "ajhb7kljbiru65qo");
788            }
789            _ => panic!("Expected hs_desc event"),
790        }
791    }
792}