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