1use std::marker::PhantomData;
5
6use slim_auth::traits::{TokenProvider, Verifier};
7use slim_datapath::api::{NameId, ProtoName};
8
9use crate::{
10 Direction, SlimChannelSender,
11 common::{AppChannelSender, SessionMessage},
12 errors::SessionError,
13 session_config::SessionConfig,
14 session_controller::SessionController,
15 session_moderator::SessionModerator,
16 session_participant::SessionParticipant,
17 session_settings::SessionSettings,
18 subscription_manager::{SubscriptionManager, SubscriptionOps},
19 traits::MessageHandler,
20};
21
22pub struct NotReady;
24pub struct Ready;
25pub struct ForController;
27pub struct ForParticipant;
28pub struct ForModerator;
29
30pub struct SessionBuilder<P, V, Target, State = NotReady, M = SubscriptionManager>
112where
113 P: TokenProvider + Send + Sync + Clone + 'static,
114 V: Verifier + Send + Sync + Clone + 'static,
115 M: SubscriptionOps,
116{
117 id: Option<u32>,
118 source: Option<ProtoName>,
119 destination: Option<ProtoName>,
120 control: Option<ProtoName>,
121 config: Option<SessionConfig>,
122 identity_provider: Option<P>,
123 identity_verifier: Option<V>,
124 slim_tx: Option<SlimChannelSender>,
125 app_tx: Option<AppChannelSender>,
126 tx_to_session_layer: Option<tokio::sync::mpsc::Sender<Result<SessionMessage, SessionError>>>,
127 graceful_shutdown_timeout: Option<std::time::Duration>,
128 direction: Direction,
129 subscription_manager: Option<M>,
130 service_id: Option<String>,
131 _target: PhantomData<Target>,
132 _state: PhantomData<State>,
133}
134
135impl<P, V, Target, M> SessionBuilder<P, V, Target, NotReady, M>
137where
138 P: TokenProvider + Send + Sync + Clone + 'static,
139 V: Verifier + Send + Sync + Clone + 'static,
140 M: SubscriptionOps,
141{
142 fn new() -> Self {
143 Self {
144 id: None,
145 source: None,
146 destination: None,
147 control: None,
148 config: None,
149 identity_provider: None,
150 identity_verifier: None,
151 slim_tx: None,
152 app_tx: None,
153 tx_to_session_layer: None,
154 graceful_shutdown_timeout: None,
155 direction: Direction::Bidirectional,
156 subscription_manager: None,
157 service_id: None,
158 _target: PhantomData,
159 _state: PhantomData,
160 }
161 }
162
163 pub fn with_id(mut self, id: u32) -> Self {
164 self.id = Some(id);
165 self
166 }
167
168 pub fn with_source(mut self, source: ProtoName) -> Self {
169 self.source = Some(source);
170 self
171 }
172
173 pub fn with_destination(mut self, destination: ProtoName) -> Self {
174 self.destination = Some(destination);
175 self
176 }
177
178 pub fn with_config(mut self, config: SessionConfig) -> Self {
179 self.config = Some(config);
180 self
181 }
182
183 pub fn with_identity_provider(mut self, identity_provider: P) -> Self {
184 self.identity_provider = Some(identity_provider);
185 self
186 }
187
188 pub fn with_identity_verifier(mut self, identity_verifier: V) -> Self {
189 self.identity_verifier = Some(identity_verifier);
190 self
191 }
192
193 pub fn with_slim_tx(mut self, slim_tx: SlimChannelSender) -> Self {
194 self.slim_tx = Some(slim_tx);
195 self
196 }
197
198 pub fn with_app_tx(mut self, app_tx: AppChannelSender) -> Self {
199 self.app_tx = Some(app_tx);
200 self
201 }
202
203 #[cfg(test)]
204 fn with_test_channels(mut self) -> Self {
205 let (slim_tx, _) = tokio::sync::mpsc::channel(10);
206 let (app_tx, _) = tokio::sync::mpsc::unbounded_channel();
207 self.slim_tx = Some(slim_tx);
208 self.app_tx = Some(app_tx);
209 self
210 }
211
212 pub fn with_tx_to_session_layer(
213 mut self,
214 tx_to_session_layer: tokio::sync::mpsc::Sender<Result<SessionMessage, SessionError>>,
215 ) -> Self {
216 self.tx_to_session_layer = Some(tx_to_session_layer);
217 self
218 }
219
220 pub fn with_graceful_shutdown_timeout(mut self, timeout: std::time::Duration) -> Self {
221 self.graceful_shutdown_timeout = Some(timeout);
222 self
223 }
224
225 pub fn with_direction(mut self, direction: Direction) -> Self {
226 self.direction = direction;
227 self
228 }
229
230 pub fn with_subscription_manager<N: SubscriptionOps>(
234 self,
235 manager: N,
236 ) -> SessionBuilder<P, V, Target, NotReady, N> {
237 SessionBuilder {
238 id: self.id,
239 source: self.source,
240 destination: self.destination,
241 control: self.control,
242 config: self.config,
243 identity_provider: self.identity_provider,
244 identity_verifier: self.identity_verifier,
245 slim_tx: self.slim_tx,
246 app_tx: self.app_tx,
247 tx_to_session_layer: self.tx_to_session_layer,
248 graceful_shutdown_timeout: self.graceful_shutdown_timeout,
249 direction: self.direction,
250 subscription_manager: Some(manager),
251 service_id: self.service_id,
252 _target: PhantomData,
253 _state: PhantomData,
254 }
255 }
256
257 pub fn with_service_id(mut self, service_id: String) -> Self {
258 self.service_id = Some(service_id);
259 self
260 }
261
262 pub fn ready(self) -> Result<SessionBuilder<P, V, Target, Ready, M>, SessionError> {
263 if self.id.is_none()
265 || self.source.is_none()
266 || self.destination.is_none()
267 || self.config.is_none()
268 || self.identity_provider.is_none()
269 || self.identity_verifier.is_none()
270 || self.slim_tx.is_none()
271 || self.app_tx.is_none()
272 || self.tx_to_session_layer.is_none()
273 {
274 return Err(SessionError::SessionBuilderIncomplete);
275 }
276
277 let config = self.config.as_ref().unwrap();
279 let destination = self.destination.as_ref().unwrap();
280 let (final_destination, control) = match config.session_type {
281 slim_datapath::api::ProtoSessionType::PointToPoint => {
282 (destination.clone(), destination.clone())
284 }
285 slim_datapath::api::ProtoSessionType::Multicast => {
286 let data_destination = destination.clone().with_id(NameId::DATA_CHANNEL_ID);
288 let control_destination = destination.clone().with_id(NameId::CONTROL_CHANNEL_ID);
289 (data_destination, control_destination)
290 }
291 _ => {
292 return Err(SessionError::SessionBuilderIncomplete);
294 }
295 };
296
297 Ok(SessionBuilder {
298 id: self.id,
299 source: self.source,
300 destination: Some(final_destination),
301 control: Some(control),
302 config: self.config,
303 identity_provider: self.identity_provider,
304 identity_verifier: self.identity_verifier,
305 slim_tx: self.slim_tx,
306 app_tx: self.app_tx,
307 tx_to_session_layer: self.tx_to_session_layer,
308 graceful_shutdown_timeout: self.graceful_shutdown_timeout,
309 direction: self.direction,
310 subscription_manager: self.subscription_manager,
311 service_id: self.service_id,
312 _target: PhantomData,
313 _state: PhantomData,
314 })
315 }
316}
317
318impl<P, V, M> SessionBuilder<P, V, ForController, NotReady, M>
320where
321 P: TokenProvider + Send + Sync + Clone + 'static,
322 V: Verifier + Send + Sync + Clone + 'static,
323 M: SubscriptionOps,
324{
325 pub fn for_controller() -> Self {
327 Self::new()
328 }
329}
330
331impl<P, V, M> SessionBuilder<P, V, ForParticipant, NotReady, M>
332where
333 P: TokenProvider + Send + Sync + Clone + 'static,
334 V: Verifier + Send + Sync + Clone + 'static,
335 M: SubscriptionOps,
336{
337 pub fn for_participant() -> Self {
339 Self::new()
340 }
341}
342
343impl<P, V, M> SessionBuilder<P, V, ForModerator, NotReady, M>
344where
345 P: TokenProvider + Send + Sync + Clone + 'static,
346 V: Verifier + Send + Sync + Clone + 'static,
347 M: SubscriptionOps,
348{
349 pub fn for_moderator() -> Self {
351 Self::new()
352 }
353}
354
355impl<P, V, M> SessionBuilder<P, V, ForController, Ready, M>
357where
358 P: TokenProvider + Send + Sync + Clone + 'static,
359 V: Verifier + Send + Sync + Clone + 'static,
360 M: SubscriptionOps,
361{
362 pub fn build(self) -> Result<SessionController, SessionError> {
367 let id = self.id.unwrap();
368 let source = self.source.clone().unwrap();
369 let destination = self.destination.clone().unwrap();
370 let config = self.config.clone().unwrap();
371
372 let role = if config.initiator {
373 "Moderator"
374 } else {
375 "Participant"
376 };
377 tracing::debug!(%role, "Building SessionController");
378
379 let session_controller = if config.initiator {
380 let (inner, tx, rx, settings) = self.build_session_stack(SessionModerator::new)?;
381 SessionController::from_parts(
382 id,
383 source,
384 destination,
385 config.clone(),
386 settings,
387 tx,
388 rx,
389 inner,
390 )
391 } else {
392 let (inner, tx, rx, settings) = self.build_session_stack(SessionParticipant::new)?;
393 SessionController::from_parts(id, source, destination, config, settings, tx, rx, inner)
394 };
395
396 Ok(session_controller)
397 }
398
399 fn build_session_stack<W>(
402 self,
403 wrapper_constructor: impl FnOnce(crate::session::Session, SessionSettings<P, V, M>) -> W,
404 ) -> Result<
405 (
406 W,
407 tokio::sync::mpsc::Sender<SessionMessage>,
408 tokio::sync::mpsc::Receiver<SessionMessage>,
409 SessionSettings<P, V, M>,
410 ),
411 SessionError,
412 >
413 where
414 W: MessageHandler,
415 {
416 let (tx_session, rx_session) = tokio::sync::mpsc::channel(256);
417
418 let inner = crate::session::Session::new(
420 self.id.unwrap(),
421 self.config.clone().unwrap(),
422 &self.source.clone().unwrap(),
423 tx_session.clone(),
424 self.direction,
425 );
426
427 let slim_tx = self.slim_tx.unwrap();
428 let app_tx = self.app_tx.unwrap();
429 let subscription_manager = self
430 .subscription_manager
431 .or_else(|| M::from_slim_tx(&slim_tx))
432 .expect("subscription_manager must be provided or M must implement from_slim_tx");
433 let settings = SessionSettings {
434 id: self.id.unwrap(),
435 source: self.source.unwrap(),
436 destination: self.destination.unwrap(),
437 control: self.control.unwrap(),
438 config: self.config.unwrap(),
439 direction: self.direction,
440 slim_tx,
441 app_tx,
442 tx_session: tx_session.clone(),
443 tx_to_session_layer: self.tx_to_session_layer.unwrap(),
444 identity_provider: self.identity_provider.unwrap(),
445 identity_verifier: self.identity_verifier.unwrap(),
446 graceful_shutdown_timeout: self.graceful_shutdown_timeout,
447 subscription_manager,
448 service_id: self.service_id.unwrap_or_default(),
449 };
450
451 let wrapper = wrapper_constructor(inner, settings.clone());
452
453 Ok((wrapper, tx_session, rx_session, settings))
454 }
455}
456
457#[cfg(test)]
458mod tests {
459 use super::*;
460 use crate::{
461 SessionError,
462 session_config::MlsSettings,
463 test_utils::{MockTokenProvider, MockVerifier},
464 };
465 use slim_datapath::api::ProtoSessionType;
466 use std::collections::HashMap;
467 use tokio::sync::mpsc;
468
469 fn create_test_config(initiator: bool) -> SessionConfig {
470 SessionConfig {
471 session_type: ProtoSessionType::PointToPoint,
472 max_retries: Some(3),
473 interval: Some(std::time::Duration::from_secs(1)),
474 mls_settings: None,
475 initiator,
476 metadata: HashMap::new(),
477 }
478 }
479
480 fn create_test_name(prefix: &str) -> ProtoName {
481 ProtoName::from_strings([prefix, "test", "name"]).with_id(1)
482 }
483
484 fn create_test_channels() -> (SlimChannelSender, AppChannelSender) {
485 let (slim_tx, _) = mpsc::channel(10);
486 let (app_tx, _) = mpsc::unbounded_channel();
487 (slim_tx, app_tx)
488 }
489
490 #[test]
491 fn test_builder_for_controller_creation() {
492 let builder =
493 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller();
494 assert!(builder.id.is_none());
495 assert!(builder.source.is_none());
496 assert!(builder.destination.is_none());
497 }
498
499 #[test]
500 fn test_builder_for_participant_creation() {
501 let builder =
502 SessionBuilder::<MockTokenProvider, MockVerifier, ForParticipant, NotReady>::for_participant();
503 assert!(builder.id.is_none());
504 assert!(builder.source.is_none());
505 assert!(builder.destination.is_none());
506 }
507
508 #[test]
509 fn test_builder_for_moderator_creation() {
510 let builder =
511 SessionBuilder::<MockTokenProvider, MockVerifier, ForModerator, NotReady>::for_moderator();
512 assert!(builder.id.is_none());
513 assert!(builder.source.is_none());
514 assert!(builder.destination.is_none());
515 }
516
517 #[test]
518 fn test_builder_with_id() {
519 let builder =
520 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
521 .with_id(42);
522 assert_eq!(builder.id, Some(42));
523 }
524
525 #[test]
526 fn test_builder_with_source() {
527 let source = create_test_name("source");
528 let builder =
529 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
530 .with_source(source.clone());
531 assert_eq!(builder.source, Some(source));
532 }
533
534 #[test]
535 fn test_builder_with_destination() {
536 let destination = create_test_name("dest");
537 let builder =
538 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
539 .with_destination(destination.clone());
540 assert_eq!(builder.destination, Some(destination));
541 }
542
543 #[test]
544 fn test_builder_with_config() {
545 let config = create_test_config(true);
546 let builder =
547 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
548 .with_config(config.clone());
549 assert!(builder.config.is_some());
550 assert!(builder.config.unwrap().initiator);
551 }
552
553 #[test]
554 fn test_builder_with_identity_provider() {
555 let provider = MockTokenProvider;
556 let builder =
557 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
558 .with_identity_provider(provider);
559 assert!(builder.identity_provider.is_some());
560 }
561
562 #[test]
563 fn test_builder_with_identity_verifier() {
564 let verifier = MockVerifier;
565 let builder =
566 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
567 .with_identity_verifier(verifier);
568 assert!(builder.identity_verifier.is_some());
569 }
570
571 #[test]
572 fn test_builder_with_slim_tx_and_app_tx() {
573 let (slim_tx, app_tx) = create_test_channels();
574 let builder =
575 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
576 .with_slim_tx(slim_tx)
577 .with_app_tx(app_tx);
578 assert!(builder.slim_tx.is_some());
579 assert!(builder.app_tx.is_some());
580 }
581
582 #[test]
583 fn test_builder_with_tx_to_session_layer() {
584 let (tx, _) = mpsc::channel(10);
585 let builder =
586 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
587 .with_tx_to_session_layer(tx);
588 assert!(builder.tx_to_session_layer.is_some());
589 }
590
591 #[test]
592 fn test_builder_ready_with_all_fields() {
593 let (tx_to_session, _) = mpsc::channel(10);
594 let builder =
595 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
596 .with_id(1)
597 .with_source(create_test_name("source"))
598 .with_destination(create_test_name("dest"))
599 .with_config(create_test_config(true))
600 .with_identity_provider(MockTokenProvider)
601 .with_identity_verifier(MockVerifier)
602 .with_test_channels()
603 .with_tx_to_session_layer(tx_to_session);
604
605 let ready_result = builder.ready();
606 assert!(ready_result.is_ok());
607 }
608
609 #[test]
610 fn test_builder_ready_missing_id() {
611 let (tx_to_session, _) = mpsc::channel(10);
612 let builder =
613 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
614 .with_source(create_test_name("source"))
615 .with_destination(create_test_name("dest"))
616 .with_config(create_test_config(true))
617 .with_identity_provider(MockTokenProvider)
618 .with_identity_verifier(MockVerifier)
619 .with_test_channels()
620 .with_tx_to_session_layer(tx_to_session);
621
622 let ready_result = builder.ready();
623 assert!(ready_result.is_err_and(|e| matches!(e, SessionError::SessionBuilderIncomplete)));
624 }
625
626 #[test]
627 fn test_builder_ready_missing_source() {
628 let (tx_to_session, _) = mpsc::channel(10);
629 let builder =
630 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
631 .with_id(1)
632 .with_destination(create_test_name("dest"))
633 .with_config(create_test_config(true))
634 .with_identity_provider(MockTokenProvider)
635 .with_identity_verifier(MockVerifier)
636 .with_test_channels()
637 .with_tx_to_session_layer(tx_to_session);
638 let ready_result = builder.ready();
639 assert!(ready_result.is_err());
640 }
641
642 #[test]
643 fn test_builder_ready_missing_destination() {
644 let (tx_to_session, _) = mpsc::channel(10);
645 let builder =
646 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
647 .with_id(1)
648 .with_source(create_test_name("source"))
649 .with_config(create_test_config(true))
650 .with_identity_provider(MockTokenProvider)
651 .with_identity_verifier(MockVerifier)
652 .with_test_channels()
653 .with_tx_to_session_layer(tx_to_session);
654
655 let ready_result = builder.ready();
656 assert!(ready_result.is_err());
657 }
658
659 #[test]
660 fn test_builder_ready_missing_config() {
661 let (tx_to_session, _) = mpsc::channel(10);
662 let builder =
663 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
664 .with_id(1)
665 .with_source(create_test_name("source"))
666 .with_destination(create_test_name("dest"))
667 .with_identity_provider(MockTokenProvider)
668 .with_identity_verifier(MockVerifier)
669 .with_test_channels()
670 .with_tx_to_session_layer(tx_to_session);
671
672 let ready_result = builder.ready();
673 assert!(ready_result.is_err());
674 }
675
676 #[test]
677 fn test_builder_ready_missing_identity_provider() {
678 let (tx_to_session, _) = mpsc::channel(10);
679 let builder =
680 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
681 .with_id(1)
682 .with_source(create_test_name("source"))
683 .with_destination(create_test_name("dest"))
684 .with_config(create_test_config(true))
685 .with_identity_verifier(MockVerifier)
686 .with_test_channels()
687 .with_tx_to_session_layer(tx_to_session);
688
689 let ready_result = builder.ready();
690 assert!(ready_result.is_err());
691 }
692
693 #[test]
694 fn test_builder_ready_missing_identity_verifier() {
695 let (tx_to_session, _) = mpsc::channel(10);
696 let builder =
697 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
698 .with_id(1)
699 .with_source(create_test_name("source"))
700 .with_destination(create_test_name("dest"))
701 .with_config(create_test_config(true))
702 .with_identity_provider(MockTokenProvider)
703 .with_test_channels()
704 .with_tx_to_session_layer(tx_to_session);
705
706 let ready_result = builder.ready();
707 assert!(ready_result.is_err());
708 }
709
710 #[test]
711 fn test_builder_ready_missing_tx() {
712 let (tx_to_session, _) = mpsc::channel(10);
713 let builder =
714 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
715 .with_id(1)
716 .with_source(create_test_name("source"))
717 .with_destination(create_test_name("dest"))
718 .with_config(create_test_config(true))
719 .with_identity_provider(MockTokenProvider)
720 .with_identity_verifier(MockVerifier)
721 .with_tx_to_session_layer(tx_to_session);
722
723 let ready_result = builder.ready();
724 assert!(ready_result.is_err());
725 }
726
727 #[test]
728 fn test_builder_ready_missing_tx_to_session_layer() {
729 let builder =
730 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
731 .with_id(1)
732 .with_source(create_test_name("source"))
733 .with_destination(create_test_name("dest"))
734 .with_config(create_test_config(true))
735 .with_identity_provider(MockTokenProvider)
736 .with_identity_verifier(MockVerifier)
737 .with_test_channels();
738
739 let ready_result = builder.ready();
740 assert!(ready_result.is_err());
741 }
742
743 #[test]
744 fn test_builder_chaining() {
745 let (tx_to_session, _) = mpsc::channel(10);
746 let builder =
747 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
748 .with_id(123)
749 .with_source(create_test_name("src"))
750 .with_destination(create_test_name("dst"))
751 .with_config(create_test_config(false))
752 .with_identity_provider(MockTokenProvider)
753 .with_identity_verifier(MockVerifier)
754 .with_test_channels()
755 .with_tx_to_session_layer(tx_to_session);
756
757 assert_eq!(builder.id, Some(123));
758 assert!(builder.source.is_some());
759 assert!(builder.destination.is_some());
760 assert!(builder.config.is_some());
761 assert!(builder.identity_provider.is_some());
762 assert!(builder.identity_verifier.is_some());
763 assert!(builder.slim_tx.is_some());
764 assert!(builder.app_tx.is_some());
765 assert!(builder.tx_to_session_layer.is_some());
766 }
767
768 #[test]
769 fn test_builder_ready_state_transition() {
770 let (tx_to_session, _) = mpsc::channel(10);
771 let builder =
772 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
773 .with_id(1)
774 .with_source(create_test_name("source"))
775 .with_destination(create_test_name("dest"))
776 .with_config(create_test_config(true))
777 .with_identity_provider(MockTokenProvider)
778 .with_identity_verifier(MockVerifier)
779 .with_test_channels()
780 .with_tx_to_session_layer(tx_to_session);
781
782 let ready_builder = builder.ready().unwrap();
783
784 assert_eq!(ready_builder.id, Some(1));
786 assert!(ready_builder.source.is_some());
787 assert!(ready_builder.destination.is_some());
788 assert!(ready_builder.config.is_some());
789 assert!(ready_builder.identity_provider.is_some());
790 assert!(ready_builder.identity_verifier.is_some());
791 assert!(ready_builder.slim_tx.is_some());
792 assert!(ready_builder.app_tx.is_some());
793 assert!(ready_builder.tx_to_session_layer.is_some());
794 }
795
796 #[test]
797 fn test_builder_different_target_types() {
798 let _controller_builder = SessionBuilder::<
800 MockTokenProvider,
801 MockVerifier,
802 ForController,
803 NotReady,
804 >::for_controller();
805 let _participant_builder = SessionBuilder::<
806 MockTokenProvider,
807 MockVerifier,
808 ForParticipant,
809 NotReady,
810 >::for_participant();
811 let _moderator_builder = SessionBuilder::<
812 MockTokenProvider,
813 MockVerifier,
814 ForModerator,
815 NotReady,
816 >::for_moderator();
817 }
818
819 #[test]
820 fn test_builder_with_different_config_types() {
821 let config_initiator = create_test_config(true);
822 let config_participant = create_test_config(false);
823
824 let builder1 =
825 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
826 .with_config(config_initiator);
827 assert!(builder1.config.unwrap().initiator);
828
829 let builder2 =
830 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
831 .with_config(config_participant);
832 assert!(!builder2.config.unwrap().initiator);
833 }
834
835 #[test]
836 fn test_builder_with_multicast_session_config() {
837 let mut config = create_test_config(true);
838 config.session_type = ProtoSessionType::Multicast;
839
840 let builder =
841 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
842 .with_config(config);
843
844 assert_eq!(
845 builder.config.unwrap().session_type,
846 ProtoSessionType::Multicast
847 );
848 }
849
850 #[test]
851 fn test_builder_with_mls_enabled() {
852 let mut config = create_test_config(true);
853 config.mls_settings = Some(MlsSettings {
854 header_integrity_validation_percent: 100,
855 });
856
857 let builder =
858 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
859 .with_config(config);
860
861 assert!(builder.config.unwrap().mls_settings.is_some());
862 }
863
864 #[test]
865 fn test_builder_with_custom_retry_settings() {
866 let mut config = create_test_config(true);
867 config.max_retries = Some(10);
868 config.interval = Some(std::time::Duration::from_secs(5));
869
870 let builder =
871 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
872 .with_config(config.clone());
873
874 let stored_config = builder.config.unwrap();
875 assert_eq!(stored_config.max_retries, Some(10));
876 assert_eq!(
877 stored_config.interval,
878 Some(std::time::Duration::from_secs(5))
879 );
880 }
881
882 #[test]
883 fn test_builder_with_metadata() {
884 let mut config = create_test_config(true);
885 let mut metadata = HashMap::new();
886 metadata.insert("key1".to_string(), "value1".to_string());
887 metadata.insert("key2".to_string(), "value2".to_string());
888 config.metadata = metadata.clone();
889
890 let builder =
891 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
892 .with_config(config);
893
894 let stored_config = builder.config.unwrap();
895 assert_eq!(
896 stored_config.metadata.get("key1"),
897 Some(&"value1".to_string())
898 );
899 assert_eq!(
900 stored_config.metadata.get("key2"),
901 Some(&"value2".to_string())
902 );
903 }
904
905 #[test]
906 fn test_builder_overwrites_previous_values() {
907 let builder =
909 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
910 .with_id(1)
911 .with_id(2)
912 .with_id(3);
913
914 assert_eq!(builder.id, Some(3));
915
916 let source1 = create_test_name("first");
917 let source2 = create_test_name("second");
918 let builder = SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
919 .with_source(source1)
920 .with_source(source2.clone());
921
922 assert_eq!(builder.source, Some(source2));
923 }
924
925 #[test]
926 fn test_builder_partial_configuration() {
927 let builder =
930 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
931 .with_id(42);
932
933 assert_eq!(builder.id, Some(42));
934 assert!(builder.source.is_none());
935
936 let dest = create_test_name("dest");
938 let (tx_to_session, _) = mpsc::channel(10);
939 let builder = builder
940 .with_source(create_test_name("source"))
941 .with_destination(dest.clone())
942 .with_config(create_test_config(true))
943 .with_identity_provider(MockTokenProvider)
944 .with_identity_verifier(MockVerifier)
945 .with_test_channels()
946 .with_tx_to_session_layer(tx_to_session);
947
948 assert!(builder.ready().is_ok());
949 }
950
951 #[test]
952 fn test_builder_ready_validation_comprehensive() {
953 let (tx_to_session, _) = mpsc::channel(10);
955
956 let mut builder =
958 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller();
959
960 builder.id = Some(1);
961 builder.source = Some(create_test_name("source"));
962 builder.destination = Some(create_test_name("dest"));
963 builder.control = Some(create_test_name("dest"));
964 builder.config = Some(create_test_config(true));
965 builder.identity_provider = Some(MockTokenProvider);
966 builder.identity_verifier = Some(MockVerifier);
967 let (slim_tx, app_tx) = create_test_channels();
968 builder.slim_tx = Some(slim_tx);
969 builder.app_tx = Some(app_tx);
970 builder.tx_to_session_layer = Some(tx_to_session);
971
972 assert!(builder.ready().is_ok());
974 }
975
976 #[test]
977 fn test_builder_with_empty_metadata() {
978 let config = create_test_config(true);
979 assert!(config.metadata.is_empty());
980
981 let builder =
982 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
983 .with_config(config);
984
985 assert!(builder.config.unwrap().metadata.is_empty());
986 }
987
988 #[test]
989 fn test_builder_with_none_retries() {
990 let mut config = create_test_config(true);
991 config.max_retries = None;
992 config.interval = None;
993
994 let builder =
995 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
996 .with_config(config);
997
998 let stored = builder.config.unwrap();
999 assert_eq!(stored.max_retries, None);
1000 assert_eq!(stored.interval, None);
1001 }
1002
1003 #[test]
1004 fn test_builder_with_zero_duration() {
1005 let mut config = create_test_config(true);
1006 config.interval = Some(std::time::Duration::from_secs(0));
1007
1008 let builder =
1009 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
1010 .with_config(config);
1011
1012 assert_eq!(
1013 builder.config.unwrap().interval,
1014 Some(std::time::Duration::from_secs(0))
1015 );
1016 }
1017
1018 #[test]
1019 fn test_builder_with_large_id() {
1020 let builder =
1021 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
1022 .with_id(u32::MAX);
1023
1024 assert_eq!(builder.id, Some(u32::MAX));
1025 }
1026
1027 #[test]
1028 fn test_builder_type_states() {
1029 let not_ready =
1031 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller();
1032
1033 let not_ready = not_ready.with_id(1);
1035 assert_eq!(not_ready.id, Some(1));
1036
1037 assert_eq!(
1039 std::mem::size_of::<
1040 SessionBuilder<MockTokenProvider, MockVerifier, ForController, NotReady>,
1041 >(),
1042 std::mem::size_of::<
1043 SessionBuilder<MockTokenProvider, MockVerifier, ForController, Ready>,
1044 >()
1045 );
1046 }
1047
1048 #[test]
1049 fn test_builder_clone_safe_types() {
1050 let provider1 = MockTokenProvider;
1052 let provider2 = provider1.clone();
1053
1054 let builder1 =
1055 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
1056 .with_identity_provider(provider1);
1057
1058 let builder2 =
1059 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
1060 .with_identity_provider(provider2);
1061
1062 assert!(builder1.identity_provider.is_some());
1063 assert!(builder2.identity_provider.is_some());
1064 }
1065
1066 #[test]
1067 fn test_builder_error_message_content() {
1068 let builder =
1069 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
1070 .with_id(1);
1071
1072 let res = builder.ready();
1073 assert!(res.is_err_and(|e| matches!(e, SessionError::SessionBuilderIncomplete)));
1074 }
1075
1076 #[test]
1077 fn test_builder_with_all_session_types() {
1078 let config_p2p = SessionConfig {
1080 session_type: ProtoSessionType::PointToPoint,
1081 max_retries: None,
1082 interval: None,
1083 mls_settings: None,
1084 initiator: true,
1085 metadata: HashMap::new(),
1086 };
1087
1088 let builder_p2p =
1089 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
1090 .with_config(config_p2p);
1091 assert_eq!(
1092 builder_p2p.config.unwrap().session_type,
1093 ProtoSessionType::PointToPoint
1094 );
1095
1096 let config_multicast = SessionConfig {
1098 session_type: ProtoSessionType::Multicast,
1099 max_retries: None,
1100 interval: None,
1101 mls_settings: None,
1102 initiator: true,
1103 metadata: HashMap::new(),
1104 };
1105
1106 let builder_multicast = SessionBuilder::<
1107 MockTokenProvider,
1108 MockVerifier,
1109 ForController,
1110 NotReady,
1111 >::for_controller()
1112 .with_config(config_multicast);
1113 assert_eq!(
1114 builder_multicast.config.unwrap().session_type,
1115 ProtoSessionType::Multicast
1116 );
1117
1118 let config_unspecified = SessionConfig {
1120 session_type: ProtoSessionType::Unspecified,
1121 max_retries: None,
1122 interval: None,
1123 mls_settings: None,
1124 initiator: false,
1125 metadata: HashMap::new(),
1126 };
1127
1128 let builder_unspecified = SessionBuilder::<
1129 MockTokenProvider,
1130 MockVerifier,
1131 ForController,
1132 NotReady,
1133 >::for_controller()
1134 .with_config(config_unspecified);
1135 assert_eq!(
1136 builder_unspecified.config.unwrap().session_type,
1137 ProtoSessionType::Unspecified
1138 );
1139 }
1140
1141 #[tokio::test]
1144 async fn test_builder_build_as_participant() {
1145 let (tx_to_session, _rx_from_session) = mpsc::channel(10);
1146 let config = create_test_config(false); let dest = create_test_name("moderator");
1149 let builder =
1150 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
1151 .with_id(1)
1152 .with_source(create_test_name("participant"))
1153 .with_destination(dest.clone())
1154 .with_config(config)
1155 .with_identity_provider(MockTokenProvider)
1156 .with_identity_verifier(MockVerifier)
1157 .with_test_channels()
1158 .with_tx_to_session_layer(tx_to_session);
1159
1160 let controller = builder.ready().unwrap().build();
1161
1162 assert!(controller.is_ok());
1164 let controller = controller.unwrap();
1165 assert_eq!(controller.id(), 1);
1166 assert!(!controller.is_initiator());
1167 }
1168
1169 #[tokio::test]
1170 async fn test_builder_build_as_moderator_p2p() {
1171 let (tx_to_session, _rx_from_session) = mpsc::channel(10);
1172 let (slim_tx, mut slim_rx) = mpsc::channel(10);
1173 let (app_tx, _app_rx) = mpsc::unbounded_channel();
1174
1175 let mut config = create_test_config(true); config.session_type = ProtoSessionType::PointToPoint;
1177
1178 let dest = create_test_name("participant");
1179 let builder =
1180 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
1181 .with_id(2)
1182 .with_source(create_test_name("moderator"))
1183 .with_destination(dest.clone())
1184 .with_config(config)
1185 .with_identity_provider(MockTokenProvider)
1186 .with_identity_verifier(MockVerifier)
1187 .with_slim_tx(slim_tx)
1188 .with_app_tx(app_tx)
1189 .with_tx_to_session_layer(tx_to_session);
1190
1191 let controller = builder.ready().unwrap().build();
1192
1193 assert!(controller.is_ok());
1194 let controller = controller.unwrap();
1195 assert_eq!(controller.id(), 2);
1196 assert!(controller.is_initiator());
1197 assert_eq!(controller.session_type(), ProtoSessionType::PointToPoint);
1198
1199 tokio::time::timeout(std::time::Duration::from_millis(100), slim_rx.recv())
1202 .await
1203 .ok();
1204 }
1205
1206 #[tokio::test]
1207 async fn test_builder_build_as_moderator_multicast() {
1208 let (tx_to_session, _rx_from_session) = mpsc::channel(10);
1209 let (slim_tx, mut slim_rx) = mpsc::channel(10);
1210 let (app_tx, _app_rx) = mpsc::unbounded_channel();
1211
1212 let mut config = create_test_config(true); config.session_type = ProtoSessionType::Multicast;
1214
1215 let dest = create_test_name("group");
1216 let data_channel = dest.with_id(NameId::DATA_CHANNEL_ID);
1217 let builder =
1218 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
1219 .with_id(3)
1220 .with_source(create_test_name("moderator"))
1221 .with_destination(data_channel) .with_config(config)
1223 .with_identity_provider(MockTokenProvider)
1224 .with_identity_verifier(MockVerifier)
1225 .with_slim_tx(slim_tx)
1226 .with_app_tx(app_tx)
1227 .with_tx_to_session_layer(tx_to_session);
1228
1229 let controller = builder.ready().unwrap().build();
1230
1231 assert!(controller.is_ok());
1232 let controller = controller.unwrap();
1233 assert_eq!(controller.id(), 3);
1234 assert!(controller.is_initiator());
1235 assert_eq!(controller.session_type(), ProtoSessionType::Multicast);
1236
1237 let result =
1240 tokio::time::timeout(std::time::Duration::from_millis(50), slim_rx.recv()).await;
1241 assert!(result.is_err() || result.unwrap().is_none());
1243 }
1244
1245 #[tokio::test]
1246 async fn test_builder_build_with_mls_disabled() {
1247 let (tx_to_session, _) = mpsc::channel(10);
1248 let mut config = create_test_config(false);
1249 config.mls_settings = None;
1250
1251 let dest = create_test_name("dest");
1252 let builder =
1253 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
1254 .with_id(4)
1255 .with_source(create_test_name("source"))
1256 .with_destination(dest.clone())
1257 .with_config(config)
1258 .with_identity_provider(MockTokenProvider)
1259 .with_identity_verifier(MockVerifier)
1260 .with_test_channels()
1261 .with_tx_to_session_layer(tx_to_session);
1262
1263 let controller = builder.ready().unwrap().build();
1264 assert!(controller.is_ok());
1265 }
1266
1267 #[tokio::test]
1268 async fn test_builder_build_with_custom_retry_settings() {
1269 let (tx_to_session, _) = mpsc::channel(10);
1270 let mut config = create_test_config(true);
1271 config.max_retries = Some(5);
1272 config.interval = Some(std::time::Duration::from_millis(500));
1273
1274 let dest = create_test_name("dest");
1275 let builder =
1276 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
1277 .with_id(5)
1278 .with_source(create_test_name("source"))
1279 .with_destination(dest.clone())
1280 .with_config(config.clone())
1281 .with_identity_provider(MockTokenProvider)
1282 .with_identity_verifier(MockVerifier)
1283 .with_test_channels()
1284 .with_tx_to_session_layer(tx_to_session);
1285
1286 let controller = builder.ready().unwrap().build();
1287 assert!(controller.is_ok());
1288
1289 let controller = controller.unwrap();
1290 let retrieved_config = controller.session_config();
1291 assert_eq!(retrieved_config.max_retries, Some(5));
1292 assert_eq!(
1293 retrieved_config.interval,
1294 Some(std::time::Duration::from_millis(500))
1295 );
1296 }
1297
1298 #[tokio::test]
1299 async fn test_builder_build_with_metadata() {
1300 let (tx_to_session, _) = mpsc::channel(10);
1301 let mut config = create_test_config(false);
1302 let mut metadata = HashMap::new();
1303 metadata.insert("app_name".to_string(), "test_app".to_string());
1304 metadata.insert("version".to_string(), "1.0".to_string());
1305 config.metadata = metadata.clone();
1306
1307 let dest = create_test_name("dest");
1308 let builder =
1309 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
1310 .with_id(6)
1311 .with_source(create_test_name("source"))
1312 .with_destination(dest.clone())
1313 .with_config(config)
1314 .with_identity_provider(MockTokenProvider)
1315 .with_identity_verifier(MockVerifier)
1316 .with_test_channels()
1317 .with_tx_to_session_layer(tx_to_session);
1318
1319 let controller = builder.ready().unwrap().build();
1320 assert!(controller.is_ok());
1321
1322 let controller = controller.unwrap();
1323 let retrieved_metadata = controller.metadata();
1324 assert_eq!(
1325 retrieved_metadata.get("app_name"),
1326 Some(&"test_app".to_string())
1327 );
1328 assert_eq!(retrieved_metadata.get("version"), Some(&"1.0".to_string()));
1329 }
1330
1331 #[tokio::test]
1332 async fn test_builder_build_verifies_session_source_and_destination() {
1333 let (tx_to_session, _) = mpsc::channel(10);
1334 let source = create_test_name("my_source");
1335 let destination = create_test_name("my_dest");
1336
1337 let builder =
1338 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
1339 .with_id(7)
1340 .with_source(source.clone())
1341 .with_destination(destination.clone())
1342 .with_config(create_test_config(false))
1343 .with_identity_provider(MockTokenProvider)
1344 .with_identity_verifier(MockVerifier)
1345 .with_test_channels()
1346 .with_tx_to_session_layer(tx_to_session);
1347
1348 let controller = builder.ready().unwrap().build();
1349 assert!(controller.is_ok());
1350
1351 let controller = controller.unwrap();
1352 assert_eq!(controller.source(), &source);
1353 assert_eq!(controller.dst(), &destination);
1354 }
1355
1356 #[tokio::test]
1357 async fn test_builder_build_multiple_sessions_different_ids() {
1358 let (tx_to_session1, _) = mpsc::channel(10);
1359 let (tx_to_session2, _) = mpsc::channel(10);
1360
1361 let dest1 = create_test_name("dest1");
1362 let builder1 =
1363 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
1364 .with_id(100)
1365 .with_source(create_test_name("source1"))
1366 .with_destination(dest1.clone())
1367 .with_config(create_test_config(false))
1368 .with_identity_provider(MockTokenProvider)
1369 .with_identity_verifier(MockVerifier)
1370 .with_test_channels()
1371 .with_tx_to_session_layer(tx_to_session1);
1372
1373 let dest2 = create_test_name("dest2");
1374 let builder2 =
1375 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
1376 .with_id(200)
1377 .with_source(create_test_name("source2"))
1378 .with_destination(dest2.clone())
1379 .with_config(create_test_config(true))
1380 .with_identity_provider(MockTokenProvider)
1381 .with_identity_verifier(MockVerifier)
1382 .with_test_channels()
1383 .with_tx_to_session_layer(tx_to_session2);
1384
1385 let controller1 = builder1.ready().unwrap().build();
1386 let controller2 = builder2.ready().unwrap().build();
1387
1388 assert!(controller1.is_ok());
1389 assert!(controller2.is_ok());
1390
1391 let controller1 = controller1.unwrap();
1392 let controller2 = controller2.unwrap();
1393
1394 assert_eq!(controller1.id(), 100);
1395 assert_eq!(controller2.id(), 200);
1396 assert_ne!(controller1.id(), controller2.id());
1397 }
1398
1399 #[tokio::test]
1400 async fn test_builder_build_with_unspecified_session_type() {
1401 let (tx_to_session, _) = mpsc::channel(10);
1402 let mut config = create_test_config(false);
1403 config.session_type = ProtoSessionType::Unspecified;
1404
1405 let dest = create_test_name("dest");
1406 let builder =
1407 SessionBuilder::<MockTokenProvider, MockVerifier, ForController, NotReady>::for_controller()
1408 .with_id(8)
1409 .with_source(create_test_name("source"))
1410 .with_destination(dest.clone())
1411 .with_config(config)
1412 .with_identity_provider(MockTokenProvider)
1413 .with_identity_verifier(MockVerifier)
1414 .with_test_channels()
1415 .with_tx_to_session_layer(tx_to_session);
1416
1417 let ready_result = builder.ready();
1419 assert!(ready_result.is_err());
1420 assert!(ready_result.is_err_and(|e| matches!(e, SessionError::SessionBuilderIncomplete)));
1421 }
1422
1423 #[test]
1424 fn test_builder_multicast_forces_channel_ids() {
1425 let (tx_to_session, _) = mpsc::channel(10);
1426 let mut config = create_test_config(true);
1427 config.session_type = ProtoSessionType::Multicast;
1428
1429 let dest = create_test_name("group").with_id(999);
1431
1432 let ready_builder = SessionBuilder::<
1433 MockTokenProvider,
1434 MockVerifier,
1435 ForController,
1436 NotReady,
1437 >::for_controller()
1438 .with_id(1)
1439 .with_source(create_test_name("source"))
1440 .with_destination(dest)
1441 .with_config(config)
1442 .with_identity_provider(MockTokenProvider)
1443 .with_identity_verifier(MockVerifier)
1444 .with_test_channels()
1445 .with_tx_to_session_layer(tx_to_session)
1446 .ready()
1447 .unwrap();
1448
1449 assert_eq!(
1451 ready_builder.destination.unwrap().id(),
1452 NameId::DATA_CHANNEL_ID
1453 );
1454 assert_eq!(
1455 ready_builder.control.unwrap().id(),
1456 NameId::CONTROL_CHANNEL_ID
1457 );
1458 }
1459
1460 #[test]
1461 fn test_builder_p2p_control_equals_destination() {
1462 let (tx_to_session, _) = mpsc::channel(10);
1463 let config = create_test_config(true);
1464
1465 let dest = create_test_name("remote").with_id(42);
1466
1467 let ready_builder = SessionBuilder::<
1468 MockTokenProvider,
1469 MockVerifier,
1470 ForController,
1471 NotReady,
1472 >::for_controller()
1473 .with_id(1)
1474 .with_source(create_test_name("source"))
1475 .with_destination(dest.clone())
1476 .with_config(config)
1477 .with_identity_provider(MockTokenProvider)
1478 .with_identity_verifier(MockVerifier)
1479 .with_test_channels()
1480 .with_tx_to_session_layer(tx_to_session)
1481 .ready()
1482 .unwrap();
1483
1484 assert_eq!(ready_builder.destination.unwrap(), dest);
1486 assert_eq!(ready_builder.control.unwrap(), dest);
1487 }
1488}