1use crate::{
7 application::dto::{
8 id_dto::{SessionIdDto, StreamIdDto},
9 priority_dto::{FromDto, ToDto},
10 },
11 domain::{
12 DomainError,
13 events::{DomainEvent, EventId, PerformanceMetrics},
14 value_objects::{SessionId, StreamId},
15 },
16};
17use chrono::{DateTime, Utc};
18use serde::{Deserialize, Serialize};
19
20#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
22#[serde(tag = "event_type", rename_all = "snake_case")]
23pub enum DomainEventDto {
24 SessionActivated {
26 session_id: SessionIdDto,
27 timestamp: DateTime<Utc>,
28 },
29
30 SessionClosed {
32 session_id: SessionIdDto,
33 timestamp: DateTime<Utc>,
34 },
35
36 SessionExpired {
38 session_id: SessionIdDto,
39 timestamp: DateTime<Utc>,
40 },
41
42 SessionTimedOut {
44 session_id: SessionIdDto,
45 original_state: String, timeout_duration: u64,
47 timestamp: DateTime<Utc>,
48 },
49
50 SessionTimeoutExtended {
52 session_id: SessionIdDto,
53 additional_seconds: u64,
54 new_expires_at: DateTime<Utc>,
55 timestamp: DateTime<Utc>,
56 },
57
58 StreamCreated {
60 session_id: SessionIdDto,
61 stream_id: StreamIdDto,
62 timestamp: DateTime<Utc>,
63 },
64
65 StreamStarted {
67 session_id: SessionIdDto,
68 stream_id: StreamIdDto,
69 timestamp: DateTime<Utc>,
70 },
71
72 StreamCompleted {
74 session_id: SessionIdDto,
75 stream_id: StreamIdDto,
76 timestamp: DateTime<Utc>,
77 },
78
79 StreamFailed {
81 session_id: SessionIdDto,
82 stream_id: StreamIdDto,
83 error: String,
84 timestamp: DateTime<Utc>,
85 },
86
87 StreamCancelled {
89 session_id: SessionIdDto,
90 stream_id: StreamIdDto,
91 timestamp: DateTime<Utc>,
92 },
93
94 SkeletonGenerated {
96 session_id: SessionIdDto,
97 stream_id: StreamIdDto,
98 frame_size_bytes: u64,
99 timestamp: DateTime<Utc>,
100 },
101
102 PatchFramesGenerated {
104 session_id: SessionIdDto,
105 stream_id: StreamIdDto,
106 frame_count: usize,
107 total_bytes: u64,
108 highest_priority: u8,
109 timestamp: DateTime<Utc>,
110 },
111
112 FramesBatched {
114 session_id: SessionIdDto,
115 frame_count: usize,
116 timestamp: DateTime<Utc>,
117 },
118
119 PriorityThresholdAdjusted {
121 session_id: SessionIdDto,
122 old_threshold: u8,
123 new_threshold: u8,
124 reason: String,
125 timestamp: DateTime<Utc>,
126 },
127
128 StreamConfigUpdated {
130 session_id: SessionIdDto,
131 stream_id: StreamIdDto,
132 timestamp: DateTime<Utc>,
133 },
134
135 PerformanceMetricsRecorded {
137 session_id: SessionIdDto,
138 metrics: PerformanceMetricsDto,
139 timestamp: DateTime<Utc>,
140 },
141
142 BackpressureReceived {
144 session_id: SessionIdDto,
145 stream_id: Option<StreamIdDto>,
146 signal: String,
147 timestamp: DateTime<Utc>,
148 },
149
150 StreamPaused {
152 session_id: SessionIdDto,
153 stream_id: StreamIdDto,
154 reason: String,
155 timestamp: DateTime<Utc>,
156 },
157
158 StreamResumed {
160 session_id: SessionIdDto,
161 stream_id: StreamIdDto,
162 paused_duration_ms: u64,
163 timestamp: DateTime<Utc>,
164 },
165
166 CreditsUpdated {
168 session_id: SessionIdDto,
169 available_credits: usize,
170 max_credits: usize,
171 timestamp: DateTime<Utc>,
172 },
173}
174
175#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
177pub struct PerformanceMetricsDto {
178 pub frames_per_second: f64,
179 pub bytes_per_second: f64,
180 pub average_frame_size: f64,
181 pub priority_distribution: PriorityDistributionDto,
182 pub latency_ms: Option<u64>,
183}
184
185pub type PriorityDistributionDto = crate::domain::events::PriorityDistribution;
188
189#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
191pub struct EventIdDto {
192 uuid: uuid::Uuid,
193}
194
195impl EventIdDto {
196 pub fn new(uuid: uuid::Uuid) -> Self {
198 Self { uuid }
199 }
200
201 pub fn uuid(self) -> uuid::Uuid {
203 self.uuid
204 }
205
206 pub fn generate() -> Self {
208 Self {
209 uuid: uuid::Uuid::new_v4(),
210 }
211 }
212}
213
214impl std::fmt::Display for EventIdDto {
215 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216 write!(f, "{}", self.uuid)
217 }
218}
219
220impl From<DomainEvent> for DomainEventDto {
223 fn from(event: DomainEvent) -> Self {
224 match event {
225 DomainEvent::SessionActivated {
226 session_id,
227 timestamp,
228 } => Self::SessionActivated {
229 session_id: session_id.to_dto(),
230 timestamp,
231 },
232 DomainEvent::SessionClosed {
233 session_id,
234 timestamp,
235 } => Self::SessionClosed {
236 session_id: session_id.to_dto(),
237 timestamp,
238 },
239 DomainEvent::SessionExpired {
240 session_id,
241 timestamp,
242 } => Self::SessionExpired {
243 session_id: session_id.to_dto(),
244 timestamp,
245 },
246 DomainEvent::StreamCreated {
247 session_id,
248 stream_id,
249 timestamp,
250 } => Self::StreamCreated {
251 session_id: session_id.to_dto(),
252 stream_id: stream_id.to_dto(),
253 timestamp,
254 },
255 DomainEvent::StreamStarted {
256 session_id,
257 stream_id,
258 timestamp,
259 } => Self::StreamStarted {
260 session_id: session_id.to_dto(),
261 stream_id: stream_id.to_dto(),
262 timestamp,
263 },
264 DomainEvent::StreamCompleted {
265 session_id,
266 stream_id,
267 timestamp,
268 } => Self::StreamCompleted {
269 session_id: session_id.to_dto(),
270 stream_id: stream_id.to_dto(),
271 timestamp,
272 },
273 DomainEvent::StreamFailed {
274 session_id,
275 stream_id,
276 error,
277 timestamp,
278 } => Self::StreamFailed {
279 session_id: session_id.to_dto(),
280 stream_id: stream_id.to_dto(),
281 error,
282 timestamp,
283 },
284 DomainEvent::StreamCancelled {
285 session_id,
286 stream_id,
287 timestamp,
288 } => Self::StreamCancelled {
289 session_id: session_id.to_dto(),
290 stream_id: stream_id.to_dto(),
291 timestamp,
292 },
293 DomainEvent::SkeletonGenerated {
294 session_id,
295 stream_id,
296 frame_size_bytes,
297 timestamp,
298 } => Self::SkeletonGenerated {
299 session_id: session_id.to_dto(),
300 stream_id: stream_id.to_dto(),
301 frame_size_bytes,
302 timestamp,
303 },
304 DomainEvent::PatchFramesGenerated {
305 session_id,
306 stream_id,
307 frame_count,
308 total_bytes,
309 highest_priority,
310 timestamp,
311 } => Self::PatchFramesGenerated {
312 session_id: session_id.to_dto(),
313 stream_id: stream_id.to_dto(),
314 frame_count,
315 total_bytes,
316 highest_priority,
317 timestamp,
318 },
319 DomainEvent::FramesBatched {
320 session_id,
321 frame_count,
322 timestamp,
323 } => Self::FramesBatched {
324 session_id: session_id.to_dto(),
325 frame_count,
326 timestamp,
327 },
328 DomainEvent::PriorityThresholdAdjusted {
329 session_id,
330 old_threshold,
331 new_threshold,
332 reason,
333 timestamp,
334 } => Self::PriorityThresholdAdjusted {
335 session_id: session_id.to_dto(),
336 old_threshold,
337 new_threshold,
338 reason,
339 timestamp,
340 },
341 DomainEvent::StreamConfigUpdated {
342 session_id,
343 stream_id,
344 timestamp,
345 } => Self::StreamConfigUpdated {
346 session_id: session_id.to_dto(),
347 stream_id: stream_id.to_dto(),
348 timestamp,
349 },
350 DomainEvent::PerformanceMetricsRecorded {
351 session_id,
352 metrics,
353 timestamp,
354 } => Self::PerformanceMetricsRecorded {
355 session_id: session_id.to_dto(),
356 metrics: metrics.into(),
357 timestamp,
358 },
359 DomainEvent::SessionTimedOut {
360 session_id,
361 original_state,
362 timeout_duration,
363 timestamp,
364 } => Self::SessionTimedOut {
365 session_id: session_id.to_dto(),
366 original_state: format!("{:?}", original_state),
367 timeout_duration,
368 timestamp,
369 },
370 DomainEvent::SessionTimeoutExtended {
371 session_id,
372 additional_seconds,
373 new_expires_at,
374 timestamp,
375 } => Self::SessionTimeoutExtended {
376 session_id: session_id.to_dto(),
377 additional_seconds,
378 new_expires_at,
379 timestamp,
380 },
381 DomainEvent::BackpressureReceived {
382 session_id,
383 stream_id,
384 signal,
385 timestamp,
386 } => Self::BackpressureReceived {
387 session_id: session_id.to_dto(),
388 stream_id: stream_id.map(|id| id.to_dto()),
389 signal: format!("{:?}", signal),
390 timestamp,
391 },
392 DomainEvent::StreamPaused {
393 session_id,
394 stream_id,
395 reason,
396 timestamp,
397 } => Self::StreamPaused {
398 session_id: session_id.to_dto(),
399 stream_id: stream_id.to_dto(),
400 reason,
401 timestamp,
402 },
403 DomainEvent::StreamResumed {
404 session_id,
405 stream_id,
406 paused_duration_ms,
407 timestamp,
408 } => Self::StreamResumed {
409 session_id: session_id.to_dto(),
410 stream_id: stream_id.to_dto(),
411 paused_duration_ms,
412 timestamp,
413 },
414 DomainEvent::CreditsUpdated {
415 session_id,
416 available_credits,
417 max_credits,
418 timestamp,
419 } => Self::CreditsUpdated {
420 session_id: session_id.to_dto(),
421 available_credits,
422 max_credits,
423 timestamp,
424 },
425 }
426 }
427}
428
429impl ToDto<DomainEventDto> for DomainEvent {
430 fn to_dto(self) -> DomainEventDto {
431 DomainEventDto::from(self)
432 }
433}
434
435impl FromDto<DomainEventDto> for DomainEvent {
436 type Error = DomainError;
437
438 fn from_dto(dto: DomainEventDto) -> Result<Self, Self::Error> {
439 match dto {
440 DomainEventDto::SessionActivated {
441 session_id,
442 timestamp,
443 } => Ok(Self::SessionActivated {
444 session_id: SessionId::from_dto(session_id)?,
445 timestamp,
446 }),
447 DomainEventDto::SessionClosed {
448 session_id,
449 timestamp,
450 } => Ok(Self::SessionClosed {
451 session_id: SessionId::from_dto(session_id)?,
452 timestamp,
453 }),
454 DomainEventDto::SessionExpired {
455 session_id,
456 timestamp,
457 } => Ok(Self::SessionExpired {
458 session_id: SessionId::from_dto(session_id)?,
459 timestamp,
460 }),
461 DomainEventDto::StreamCreated {
462 session_id,
463 stream_id,
464 timestamp,
465 } => Ok(Self::StreamCreated {
466 session_id: SessionId::from_dto(session_id)?,
467 stream_id: StreamId::from_dto(stream_id)?,
468 timestamp,
469 }),
470 DomainEventDto::StreamStarted {
471 session_id,
472 stream_id,
473 timestamp,
474 } => Ok(Self::StreamStarted {
475 session_id: SessionId::from_dto(session_id)?,
476 stream_id: StreamId::from_dto(stream_id)?,
477 timestamp,
478 }),
479 DomainEventDto::StreamCompleted {
480 session_id,
481 stream_id,
482 timestamp,
483 } => Ok(Self::StreamCompleted {
484 session_id: SessionId::from_dto(session_id)?,
485 stream_id: StreamId::from_dto(stream_id)?,
486 timestamp,
487 }),
488 DomainEventDto::StreamFailed {
489 session_id,
490 stream_id,
491 error,
492 timestamp,
493 } => Ok(Self::StreamFailed {
494 session_id: SessionId::from_dto(session_id)?,
495 stream_id: StreamId::from_dto(stream_id)?,
496 error,
497 timestamp,
498 }),
499 DomainEventDto::StreamCancelled {
500 session_id,
501 stream_id,
502 timestamp,
503 } => Ok(Self::StreamCancelled {
504 session_id: SessionId::from_dto(session_id)?,
505 stream_id: StreamId::from_dto(stream_id)?,
506 timestamp,
507 }),
508 DomainEventDto::SkeletonGenerated {
509 session_id,
510 stream_id,
511 frame_size_bytes,
512 timestamp,
513 } => Ok(Self::SkeletonGenerated {
514 session_id: SessionId::from_dto(session_id)?,
515 stream_id: StreamId::from_dto(stream_id)?,
516 frame_size_bytes,
517 timestamp,
518 }),
519 DomainEventDto::PatchFramesGenerated {
520 session_id,
521 stream_id,
522 frame_count,
523 total_bytes,
524 highest_priority,
525 timestamp,
526 } => Ok(Self::PatchFramesGenerated {
527 session_id: SessionId::from_dto(session_id)?,
528 stream_id: StreamId::from_dto(stream_id)?,
529 frame_count,
530 total_bytes,
531 highest_priority,
532 timestamp,
533 }),
534 DomainEventDto::FramesBatched {
535 session_id,
536 frame_count,
537 timestamp,
538 } => Ok(Self::FramesBatched {
539 session_id: SessionId::from_dto(session_id)?,
540 frame_count,
541 timestamp,
542 }),
543 DomainEventDto::PriorityThresholdAdjusted {
544 session_id,
545 old_threshold,
546 new_threshold,
547 reason,
548 timestamp,
549 } => Ok(Self::PriorityThresholdAdjusted {
550 session_id: SessionId::from_dto(session_id)?,
551 old_threshold,
552 new_threshold,
553 reason,
554 timestamp,
555 }),
556 DomainEventDto::StreamConfigUpdated {
557 session_id,
558 stream_id,
559 timestamp,
560 } => Ok(Self::StreamConfigUpdated {
561 session_id: SessionId::from_dto(session_id)?,
562 stream_id: StreamId::from_dto(stream_id)?,
563 timestamp,
564 }),
565 DomainEventDto::PerformanceMetricsRecorded {
566 session_id,
567 metrics,
568 timestamp,
569 } => Ok(Self::PerformanceMetricsRecorded {
570 session_id: SessionId::from_dto(session_id)?,
571 metrics: metrics.try_into().map_err(|_| {
572 DomainError::InvalidInput("Invalid performance metrics".to_string())
573 })?,
574 timestamp,
575 }),
576 DomainEventDto::SessionTimedOut {
577 session_id,
578 original_state,
579 timeout_duration,
580 timestamp,
581 } => {
582 let state = match original_state.as_str() {
584 "Initializing" => crate::domain::events::SessionState::Initializing,
585 "Active" => crate::domain::events::SessionState::Active,
586 "Closing" => crate::domain::events::SessionState::Closing,
587 "Completed" => crate::domain::events::SessionState::Completed,
588 "Failed" => crate::domain::events::SessionState::Failed,
589 _ => {
590 return Err(DomainError::InvalidInput(format!(
591 "Invalid session state: {}",
592 original_state
593 )));
594 }
595 };
596 Ok(Self::SessionTimedOut {
597 session_id: SessionId::from_dto(session_id)?,
598 original_state: state,
599 timeout_duration,
600 timestamp,
601 })
602 }
603 DomainEventDto::SessionTimeoutExtended {
604 session_id,
605 additional_seconds,
606 new_expires_at,
607 timestamp,
608 } => Ok(Self::SessionTimeoutExtended {
609 session_id: SessionId::from_dto(session_id)?,
610 additional_seconds,
611 new_expires_at,
612 timestamp,
613 }),
614 DomainEventDto::BackpressureReceived {
615 session_id,
616 stream_id,
617 signal,
618 timestamp,
619 } => {
620 let backpressure_signal = match signal.as_str() {
621 "Ok" => crate::domain::value_objects::BackpressureSignal::Ok,
622 "SlowDown" => crate::domain::value_objects::BackpressureSignal::SlowDown,
623 "Pause" => crate::domain::value_objects::BackpressureSignal::Pause,
624 _ => {
625 return Err(DomainError::InvalidInput(format!(
626 "Invalid backpressure signal: {}",
627 signal
628 )));
629 }
630 };
631 Ok(Self::BackpressureReceived {
632 session_id: SessionId::from_dto(session_id)?,
633 stream_id: stream_id.map(StreamId::from_dto).transpose()?,
634 signal: backpressure_signal,
635 timestamp,
636 })
637 }
638 DomainEventDto::StreamPaused {
639 session_id,
640 stream_id,
641 reason,
642 timestamp,
643 } => Ok(Self::StreamPaused {
644 session_id: SessionId::from_dto(session_id)?,
645 stream_id: StreamId::from_dto(stream_id)?,
646 reason,
647 timestamp,
648 }),
649 DomainEventDto::StreamResumed {
650 session_id,
651 stream_id,
652 paused_duration_ms,
653 timestamp,
654 } => Ok(Self::StreamResumed {
655 session_id: SessionId::from_dto(session_id)?,
656 stream_id: StreamId::from_dto(stream_id)?,
657 paused_duration_ms,
658 timestamp,
659 }),
660 DomainEventDto::CreditsUpdated {
661 session_id,
662 available_credits,
663 max_credits,
664 timestamp,
665 } => Ok(Self::CreditsUpdated {
666 session_id: SessionId::from_dto(session_id)?,
667 available_credits,
668 max_credits,
669 timestamp,
670 }),
671 }
672 }
673}
674
675impl From<PerformanceMetrics> for PerformanceMetricsDto {
676 fn from(metrics: PerformanceMetrics) -> Self {
677 Self {
678 frames_per_second: metrics.frames_per_second,
679 bytes_per_second: metrics.bytes_per_second,
680 average_frame_size: metrics.average_frame_size,
681 priority_distribution: metrics.priority_distribution,
682 latency_ms: metrics.latency_ms,
683 }
684 }
685}
686
687impl TryFrom<PerformanceMetricsDto> for PerformanceMetrics {
688 type Error = DomainError;
689
690 fn try_from(dto: PerformanceMetricsDto) -> Result<Self, Self::Error> {
691 Ok(Self {
692 frames_per_second: dto.frames_per_second,
693 bytes_per_second: dto.bytes_per_second,
694 average_frame_size: dto.average_frame_size,
695 priority_distribution: dto.priority_distribution,
696 latency_ms: dto.latency_ms,
697 })
698 }
699}
700
701impl From<EventId> for EventIdDto {
704 fn from(event_id: EventId) -> Self {
705 Self::new(event_id.inner())
706 }
707}
708
709impl From<EventIdDto> for EventId {
710 fn from(dto: EventIdDto) -> Self {
711 EventId::from_uuid(dto.uuid)
712 }
713}
714
715#[cfg(test)]
716mod tests {
717 use super::*;
718 use chrono::Utc;
719
720 #[test]
721 fn test_domain_event_dto_conversion() {
722 let session_id = SessionId::new();
723 let stream_id = StreamId::new();
724 let timestamp = Utc::now();
725
726 let domain_event = DomainEvent::StreamCreated {
727 session_id,
728 stream_id,
729 timestamp,
730 };
731
732 let dto = domain_event.clone().to_dto();
734
735 let converted = DomainEvent::from_dto(dto).unwrap();
737
738 assert_eq!(domain_event, converted);
739 }
740
741 #[test]
742 fn test_performance_metrics_dto_conversion() {
743 let metrics = PerformanceMetrics {
744 frames_per_second: 60.0,
745 bytes_per_second: 1024.0,
746 average_frame_size: 512.0,
747 priority_distribution: PriorityDistributionDto::default(),
748 latency_ms: Some(100),
749 };
750
751 let dto = PerformanceMetricsDto::from(metrics.clone());
752 let converted = PerformanceMetrics::try_from(dto).unwrap();
753
754 assert_eq!(metrics, converted);
755 }
756
757 #[test]
758 fn test_event_dto_serialization() {
759 let session_id = SessionId::new();
760 let event_dto = DomainEventDto::SessionActivated {
761 session_id: session_id.to_dto(),
762 timestamp: Utc::now(),
763 };
764
765 let serialized = serde_json::to_string(&event_dto).unwrap();
766 let deserialized: DomainEventDto = serde_json::from_str(&serialized).unwrap();
767
768 assert_eq!(event_dto, deserialized);
769 }
770
771 #[test]
772 fn test_event_id_dto_creation() {
773 let uuid = uuid::Uuid::new_v4();
774 let event_id_dto = EventIdDto::new(uuid);
775
776 assert_eq!(event_id_dto.uuid(), uuid);
777 }
778
779 #[test]
780 fn test_event_id_dto_generate() {
781 let event_id1 = EventIdDto::generate();
782 let event_id2 = EventIdDto::generate();
783
784 assert_ne!(event_id1, event_id2);
785 }
786
787 #[test]
788 fn test_event_id_dto_display() {
789 let uuid = uuid::Uuid::new_v4();
790 let event_id_dto = EventIdDto::new(uuid);
791
792 assert_eq!(event_id_dto.to_string(), uuid.to_string());
793 }
794
795 #[test]
796 fn test_event_id_dto_conversion() {
797 let event_id = EventId::new();
798 let dto = EventIdDto::from(event_id);
799 let converted_back = EventId::from(dto);
800
801 assert_eq!(event_id, converted_back);
802 }
803
804 #[test]
805 fn test_all_domain_event_dto_variants() {
806 let session_id = SessionId::new();
807 let stream_id = StreamId::new();
808 let timestamp = Utc::now();
809
810 let event1 = DomainEvent::SessionActivated {
812 session_id,
813 timestamp,
814 };
815 let dto1 = DomainEventDto::from(event1.clone());
816 let converted1 = DomainEvent::from_dto(dto1).unwrap();
817 assert_eq!(event1, converted1);
818
819 let event2 = DomainEvent::SessionClosed {
821 session_id,
822 timestamp,
823 };
824 let dto2 = DomainEventDto::from(event2.clone());
825 let converted2 = DomainEvent::from_dto(dto2).unwrap();
826 assert_eq!(event2, converted2);
827
828 let event3 = DomainEvent::SessionExpired {
830 session_id,
831 timestamp,
832 };
833 let dto3 = DomainEventDto::from(event3.clone());
834 let converted3 = DomainEvent::from_dto(dto3).unwrap();
835 assert_eq!(event3, converted3);
836
837 let event4 = DomainEvent::StreamCreated {
839 session_id,
840 stream_id,
841 timestamp,
842 };
843 let dto4 = DomainEventDto::from(event4.clone());
844 let converted4 = DomainEvent::from_dto(dto4).unwrap();
845 assert_eq!(event4, converted4);
846
847 let event5 = DomainEvent::StreamStarted {
849 session_id,
850 stream_id,
851 timestamp,
852 };
853 let dto5 = DomainEventDto::from(event5.clone());
854 let converted5 = DomainEvent::from_dto(dto5).unwrap();
855 assert_eq!(event5, converted5);
856
857 let event6 = DomainEvent::StreamCompleted {
859 session_id,
860 stream_id,
861 timestamp,
862 };
863 let dto6 = DomainEventDto::from(event6.clone());
864 let converted6 = DomainEvent::from_dto(dto6).unwrap();
865 assert_eq!(event6, converted6);
866 }
867
868 #[test]
869 fn test_stream_failed_event_conversion() {
870 let session_id = SessionId::new();
871 let stream_id = StreamId::new();
872 let timestamp = Utc::now();
873 let error = "Test error message".to_string();
874
875 let event = DomainEvent::StreamFailed {
876 session_id,
877 stream_id,
878 error: error.clone(),
879 timestamp,
880 };
881
882 let dto = DomainEventDto::from(event.clone());
883 let converted = DomainEvent::from_dto(dto).unwrap();
884
885 assert_eq!(event, converted);
886 }
887
888 #[test]
889 fn test_performance_metrics_with_none_latency() {
890 let metrics = PerformanceMetrics {
891 frames_per_second: 30.0,
892 bytes_per_second: 2048.0,
893 average_frame_size: 1024.0,
894 priority_distribution: PriorityDistributionDto::default(),
895 latency_ms: None,
896 };
897
898 let dto = PerformanceMetricsDto::from(metrics.clone());
899 let converted = PerformanceMetrics::try_from(dto).unwrap();
900
901 assert_eq!(metrics, converted);
902 assert_eq!(converted.latency_ms, None);
903 }
904
905 #[test]
906 fn test_complex_event_serialization() {
907 let session_id = SessionId::new();
908 let stream_id = StreamId::new();
909 let timestamp = Utc::now();
910
911 let event_dto = DomainEventDto::PatchFramesGenerated {
912 session_id: session_id.to_dto(),
913 stream_id: stream_id.to_dto(),
914 frame_count: 42,
915 total_bytes: 1024,
916 highest_priority: 100,
917 timestamp,
918 };
919
920 let serialized = serde_json::to_string(&event_dto).unwrap();
921 let deserialized: DomainEventDto = serde_json::from_str(&serialized).unwrap();
922
923 assert_eq!(event_dto, deserialized);
924 }
925
926 #[test]
927 fn test_performance_metrics_dto_fields() {
928 let dto = PerformanceMetricsDto {
929 frames_per_second: 60.0,
930 bytes_per_second: 4096.0,
931 average_frame_size: 256.0,
932 priority_distribution: PriorityDistributionDto::default(),
933 latency_ms: Some(50),
934 };
935
936 assert_eq!(dto.frames_per_second, 60.0);
937 assert_eq!(dto.bytes_per_second, 4096.0);
938 assert_eq!(dto.average_frame_size, 256.0);
939 assert_eq!(dto.latency_ms, Some(50));
940 }
941
942 #[test]
943 fn test_priority_threshold_adjusted_event() {
944 let session_id = SessionId::new();
945 let timestamp = Utc::now();
946
947 let event = DomainEvent::PriorityThresholdAdjusted {
948 session_id,
949 old_threshold: 50,
950 new_threshold: 75,
951 reason: "Performance optimization".to_string(),
952 timestamp,
953 };
954
955 let dto = DomainEventDto::from(event.clone());
956 let converted = DomainEvent::from_dto(dto).unwrap();
957
958 assert_eq!(event, converted);
959 }
960
961 #[test]
962 fn test_event_id_dto_hash() {
963 let uuid1 = uuid::Uuid::new_v4();
964 let uuid2 = uuid::Uuid::new_v4();
965
966 let event_id1 = EventIdDto::new(uuid1);
967 let event_id2 = EventIdDto::new(uuid2);
968 let event_id3 = EventIdDto::new(uuid1); use std::collections::HashSet;
971 let mut set = HashSet::new();
972 set.insert(event_id1.clone());
973 set.insert(event_id2);
974 set.insert(event_id3);
975
976 assert_eq!(set.len(), 2); }
978
979 #[test]
980 fn test_event_dto_clone() {
981 let session_id = SessionId::new();
982 let timestamp = Utc::now();
983
984 let original = DomainEventDto::SessionActivated {
985 session_id: session_id.to_dto(),
986 timestamp,
987 };
988
989 let cloned = original.clone();
990 assert_eq!(original, cloned);
991 }
992
993 #[test]
994 fn test_event_dto_debug() {
995 let session_id = SessionId::new();
996 let timestamp = Utc::now();
997
998 let event = DomainEventDto::SessionActivated {
999 session_id: session_id.to_dto(),
1000 timestamp,
1001 };
1002
1003 let debug_str = format!("{:?}", event);
1004 assert!(debug_str.contains("SessionActivated"));
1005 }
1006}