1use std::sync::Arc;
5
6use crate::client_config::ClientConfig;
7use crate::errors::SlimError;
8use crate::get_runtime;
9use crate::identity_config::{IdentityProviderConfig, IdentityVerifierConfig};
10use crate::server_config::ServerConfig;
11use slim_auth::auth_provider::{AuthProvider, AuthVerifier};
12use slim_auth::traits::{TokenProvider, Verifier};
13use slim_config::auth::identity::{
14 IdentityProviderConfig as CoreIdentityProviderConfig,
15 IdentityVerifierConfig as CoreIdentityVerifierConfig,
16};
17use slim_config::component::Component;
18use slim_config::component::id::{ID, Kind};
19use slim_config::grpc::client::ClientConfig as CoreClientConfig;
20use slim_config::grpc::server::ServerConfig as CoreServerConfig;
21use slim_controller::config::Config as CoreControllerConfig;
22use slim_datapath::messages::Name as SlimName;
23use slim_service::{
24 KIND, Service as SlimService, ServiceConfiguration as SlimServiceConfiguration,
25};
26use tokio::task::JoinHandle;
27
28use crate::name::Name;
29
30#[derive(Clone, Default, uniffi::Record)]
32pub struct DataplaneConfig {
33 pub servers: Vec<ServerConfig>,
35 pub clients: Vec<ClientConfig>,
37}
38
39impl From<DataplaneConfig> for CoreControllerConfig {
40 fn from(config: DataplaneConfig) -> Self {
41 let mut core_config = CoreControllerConfig::new();
42 core_config.servers = config
43 .servers
44 .into_iter()
45 .map(|s| {
46 let core: CoreServerConfig = s.into();
47 core
48 })
49 .collect();
50 core_config.clients = config
51 .clients
52 .into_iter()
53 .map(|c| {
54 let core: CoreClientConfig = c.into();
55 core
56 })
57 .collect();
58 core_config.token_provider = CoreIdentityProviderConfig::None;
59 core_config.token_verifier = CoreIdentityVerifierConfig::None;
60 core_config
61 }
62}
63
64impl From<CoreControllerConfig> for DataplaneConfig {
65 fn from(config: CoreControllerConfig) -> Self {
66 Self {
67 servers: config.servers.into_iter().map(|s| s.into()).collect(),
68 clients: config.clients.into_iter().map(|c| c.into()).collect(),
69 }
70 }
71}
72
73#[derive(Clone, Default, uniffi::Record)]
75pub struct ServiceConfig {
76 pub node_id: Option<String>,
78
79 pub group_name: Option<String>,
81
82 pub dataplane: DataplaneConfig,
84}
85
86impl ServiceConfig {
87 pub fn new() -> Self {
88 Self {
89 node_id: None,
90 group_name: None,
91 dataplane: DataplaneConfig::default(),
92 }
93 }
94}
95
96impl From<ServiceConfig> for SlimServiceConfiguration {
97 fn from(config: ServiceConfig) -> Self {
98 let mut core_config = SlimServiceConfiguration::new();
99 core_config.node_id = config.node_id;
100 core_config.group_name = config.group_name;
101 core_config.dataplane = config.dataplane.into();
102 core_config
103 }
104}
105
106impl From<SlimServiceConfiguration> for ServiceConfig {
107 fn from(config: SlimServiceConfiguration) -> Self {
108 Self {
109 node_id: config.node_id,
110 group_name: config.group_name,
111 dataplane: config.dataplane.into(),
112 }
113 }
114}
115
116impl From<&SlimServiceConfiguration> for ServiceConfig {
117 fn from(config: &SlimServiceConfiguration) -> Self {
118 Self {
119 node_id: config.node_id.clone(),
120 group_name: config.group_name.clone(),
121 dataplane: config.dataplane.clone().into(),
122 }
123 }
124}
125
126#[derive(uniffi::Object)]
128pub struct Service {
129 pub(crate) inner: Arc<SlimService>,
130}
131
132impl Service {
133 pub fn inner(&self) -> Arc<SlimService> {
135 self.inner.clone()
136 }
137}
138
139impl From<SlimService> for Service {
141 fn from(service: SlimService) -> Self {
142 Service {
143 inner: Arc::new(service),
144 }
145 }
146}
147
148#[uniffi::export]
149impl Service {
150 #[uniffi::constructor]
152 pub fn new(name: String) -> Self {
153 let kind = Kind::new(KIND).expect("Invalid service kind");
154 let id = ID::new_with_name(kind, &name).expect("Invalid service name");
155 let service = SlimService::new(id);
156 Service {
157 inner: Arc::new(service),
158 }
159 }
160
161 #[uniffi::constructor]
163 pub fn new_with_config(name: String, config: ServiceConfig) -> Self {
164 let kind = Kind::new(KIND).expect("Invalid service kind");
165 let id = ID::new_with_name(kind, &name).expect("Invalid service name");
166 let core_config: SlimServiceConfiguration = config.into();
167 let service = SlimService::new_with_config(id, core_config);
168 Service {
169 inner: Arc::new(service),
170 }
171 }
172
173 pub fn config(&self) -> ServiceConfig {
175 self.inner.config().clone().into()
176 }
177
178 pub fn get_name(&self) -> String {
180 self.inner.identifier().to_string()
181 }
182
183 pub async fn run_async(&self) -> Result<(), SlimError> {
185 self.inner.run().await?;
186 Ok(())
187 }
188
189 pub fn run(&self) -> Result<(), SlimError> {
191 crate::config::get_runtime().block_on(self.run_async())
192 }
193
194 pub async fn shutdown_async(&self) -> Result<(), SlimError> {
196 self.inner.shutdown().await?;
197 Ok(())
198 }
199
200 pub fn shutdown(&self) -> Result<(), SlimError> {
202 crate::config::get_runtime().block_on(self.shutdown_async())
203 }
204
205 pub async fn run_server_async(&self, config: ServerConfig) -> Result<(), SlimError> {
207 let runtime = get_runtime();
209 let core_config: slim_config::grpc::server::ServerConfig = config.into();
210 let inner = self.inner.clone();
211
212 let handle: JoinHandle<Result<(), SlimError>> = runtime.handle().spawn(async move {
214 inner.run_server(&core_config).await?;
215 Ok(())
216 });
217
218 handle.await.map_err(|e| SlimError::ServiceError {
219 message: format!("Failed to join server task: {}", e),
220 })?
221 }
222
223 pub fn run_server(&self, config: ServerConfig) -> Result<(), SlimError> {
225 crate::config::get_runtime().block_on(self.run_server_async(config))
226 }
227
228 pub fn stop_server(&self, endpoint: String) -> Result<(), SlimError> {
230 self.inner.stop_server(&endpoint)?;
231 Ok(())
232 }
233
234 pub async fn connect_async(&self, config: ClientConfig) -> Result<u64, SlimError> {
236 let core_config: slim_config::grpc::client::ClientConfig = config.into();
237 let inner = self.inner.clone();
238 let runtime = get_runtime();
239
240 let handle = runtime.spawn(async move { inner.connect(&core_config).await });
242
243 let result = handle.await.map_err(|e| SlimError::InternalError {
244 message: format!("Failed to join connect task: {}", e),
245 })?;
246
247 Ok(result?)
248 }
249
250 pub fn connect(&self, config: ClientConfig) -> Result<u64, SlimError> {
252 crate::config::get_runtime().block_on(self.connect_async(config))
253 }
254
255 pub fn disconnect(&self, conn_id: u64) -> Result<(), SlimError> {
257 self.inner.disconnect(conn_id)?;
258 Ok(())
259 }
260
261 pub fn get_connection_id(&self, endpoint: String) -> Option<u64> {
263 self.inner.get_connection_id(&endpoint)
264 }
265
266 pub async fn create_app_async(
280 &self,
281 base_name: Arc<Name>,
282 identity_provider_config: IdentityProviderConfig,
283 identity_verifier_config: IdentityVerifierConfig,
284 ) -> Result<Arc<crate::app::App>, SlimError> {
285 let slim_name: SlimName = base_name.as_ref().into();
286 create_app_async_internal(
287 slim_name,
288 identity_provider_config,
289 identity_verifier_config,
290 self.inner.clone(),
291 crate::app::Direction::Bidirectional,
292 )
293 .await
294 .map(Arc::new)
295 }
296
297 pub async fn create_app_with_direction_async(
313 &self,
314 name: Arc<Name>,
315 identity_provider_config: IdentityProviderConfig,
316 identity_verifier_config: IdentityVerifierConfig,
317 direction: crate::app::Direction,
318 ) -> Result<Arc<crate::app::App>, SlimError> {
319 let slim_name: SlimName = name.as_ref().into();
320 create_app_async_internal(
321 slim_name,
322 identity_provider_config,
323 identity_verifier_config,
324 self.inner.clone(),
325 direction,
326 )
327 .await
328 .map(Arc::new)
329 }
330
331 pub async fn create_app_with_secret_async(
344 &self,
345 name: Arc<Name>,
346 secret: String,
347 ) -> Result<Arc<crate::app::App>, SlimError> {
348 let identity_provider_config = IdentityProviderConfig::SharedSecret {
349 id: name.to_string(),
350 data: secret.clone(),
351 };
352 let identity_verifier_config = IdentityVerifierConfig::SharedSecret {
353 id: name.to_string(),
354 data: secret,
355 };
356
357 self.create_app_async(name, identity_provider_config, identity_verifier_config)
358 .await
359 }
360
361 #[uniffi::method]
375 pub fn create_app(
376 &self,
377 base_name: Arc<Name>,
378 identity_provider_config: IdentityProviderConfig,
379 identity_verifier_config: IdentityVerifierConfig,
380 ) -> Result<Arc<crate::app::App>, SlimError> {
381 get_runtime().block_on(self.create_app_async(
382 base_name,
383 identity_provider_config,
384 identity_verifier_config,
385 ))
386 }
387
388 pub fn create_app_with_direction(
404 &self,
405 base_name: Arc<Name>,
406 identity_provider_config: IdentityProviderConfig,
407 identity_verifier_config: IdentityVerifierConfig,
408 direction: crate::app::Direction,
409 ) -> Result<Arc<crate::app::App>, SlimError> {
410 get_runtime().block_on(self.create_app_with_direction_async(
411 base_name,
412 identity_provider_config,
413 identity_verifier_config,
414 direction,
415 ))
416 }
417
418 #[uniffi::method]
431 pub fn create_app_with_secret(
432 &self,
433 name: Arc<Name>,
434 secret: String,
435 ) -> Result<Arc<crate::app::App>, SlimError> {
436 get_runtime().block_on(self.create_app_with_secret_async(name, secret))
437 }
438}
439
440pub(crate) async fn create_app_async_internal(
454 base_name: SlimName,
455 identity_provider_config: IdentityProviderConfig,
456 identity_verifier_config: IdentityVerifierConfig,
457 service: Arc<SlimService>,
458 direction: crate::app::Direction,
459) -> Result<crate::app::App, SlimError> {
460 let mut identity_provider: AuthProvider = identity_provider_config.try_into()?;
462 let mut identity_verifier: AuthVerifier = identity_verifier_config.try_into()?;
463
464 identity_provider.initialize().await?;
466
467 identity_verifier.initialize().await?;
469
470 let _identity_token = identity_provider.get_token()?;
472
473 let (app, rx) = service.create_app_with_direction(
475 &base_name,
476 identity_provider,
477 identity_verifier,
478 direction.into(),
479 )?;
480
481 Ok(crate::app::App::from_parts(
482 Arc::new(app),
483 Arc::new(tokio::sync::RwLock::new(rx)),
484 service,
485 ))
486}
487
488#[uniffi::export]
490pub fn new_service_configuration() -> ServiceConfig {
491 ServiceConfig::new()
492}
493
494#[uniffi::export]
496pub fn new_dataplane_config() -> DataplaneConfig {
497 DataplaneConfig::default()
498}
499
500#[uniffi::export]
502pub fn create_service(name: String) -> Result<Arc<Service>, SlimError> {
503 Ok(Arc::new(Service::new(name)))
504}
505
506#[uniffi::export]
508pub fn create_service_with_config(
509 name: String,
510 config: ServiceConfig,
511) -> Result<Arc<Service>, SlimError> {
512 Ok(Arc::new(Service::new_with_config(name, config)))
513}
514
515#[cfg(test)]
516mod tests {
517 use super::*;
518 use slim_datapath::messages::Name as SlimName;
519 use slim_testing::utils::TEST_VALID_SECRET;
520
521 use crate::app::App;
522 use crate::config::get_global_service;
523 use crate::identity_config::{IdentityProviderConfig, IdentityVerifierConfig};
524 use crate::name::Name;
525
526 fn create_test_auth() -> (IdentityProviderConfig, IdentityVerifierConfig) {
528 let provider = IdentityProviderConfig::SharedSecret {
529 id: "test-service".to_string(),
530 data: TEST_VALID_SECRET.to_string(),
531 };
532 let verifier = IdentityVerifierConfig::SharedSecret {
533 id: "test-service".to_string(),
534 data: TEST_VALID_SECRET.to_string(),
535 };
536 (provider, verifier)
537 }
538
539 fn create_test_name() -> SlimName {
541 SlimName::from_strings(["org", "namespace", "test-app"])
542 }
543
544 #[test]
549 fn test_dataplane_config_default() {
550 let config = DataplaneConfig::default();
551 assert!(config.servers.is_empty());
552 assert!(config.clients.is_empty());
553 }
554
555 #[test]
556 fn test_dataplane_config_to_core_conversion() {
557 let server_config = ServerConfig::default();
558 let client_config = ClientConfig::default();
559
560 let config = DataplaneConfig {
561 servers: vec![server_config],
562 clients: vec![client_config],
563 };
564
565 let core_config: CoreControllerConfig = config.clone().into();
566 assert_eq!(core_config.servers.len(), 1);
567 assert_eq!(core_config.clients.len(), 1);
568
569 assert!(matches!(
571 core_config.token_provider,
572 CoreIdentityProviderConfig::None
573 ));
574 assert!(matches!(
575 core_config.token_verifier,
576 CoreIdentityVerifierConfig::None
577 ));
578 }
579
580 #[test]
581 fn test_dataplane_config_from_core_conversion() {
582 let mut core_config = CoreControllerConfig::new();
583 core_config.servers = vec![CoreServerConfig::default()];
584 core_config.clients = vec![CoreClientConfig::default()];
585
586 let config: DataplaneConfig = core_config.into();
587 assert_eq!(config.servers.len(), 1);
588 assert_eq!(config.clients.len(), 1);
589 }
590
591 #[test]
592 fn test_dataplane_config_roundtrip() {
593 let original = DataplaneConfig {
594 servers: vec![ServerConfig::default()],
595 clients: vec![ClientConfig::default()],
596 };
597
598 let core: CoreControllerConfig = original.clone().into();
599 let roundtrip: DataplaneConfig = core.into();
600
601 assert_eq!(original.servers.len(), roundtrip.servers.len());
602 assert_eq!(original.clients.len(), roundtrip.clients.len());
603 }
604
605 #[test]
606 fn test_service_configuration_new() {
607 let config = ServiceConfig::new();
608 assert!(config.node_id.is_none());
609 assert!(config.group_name.is_none());
610 assert!(config.dataplane.servers.is_empty());
611 assert!(config.dataplane.clients.is_empty());
612 }
613
614 #[test]
615 fn test_service_configuration_default() {
616 let config = ServiceConfig::default();
617 assert!(config.node_id.is_none());
618 assert!(config.group_name.is_none());
619 }
620
621 #[test]
622 fn test_service_configuration_with_values() {
623 let config = ServiceConfig {
624 node_id: Some("node-123".to_string()),
625 group_name: Some("test-group".to_string()),
626 dataplane: DataplaneConfig::default(),
627 };
628
629 assert_eq!(config.node_id.as_deref(), Some("node-123"));
630 assert_eq!(config.group_name.as_deref(), Some("test-group"));
631 }
632
633 #[test]
634 fn test_service_configuration_to_core_conversion() {
635 let config = ServiceConfig {
636 node_id: Some("node-456".to_string()),
637 group_name: Some("group-abc".to_string()),
638 dataplane: DataplaneConfig::default(),
639 };
640
641 let core_config: SlimServiceConfiguration = config.clone().into();
642 assert_eq!(core_config.node_id.as_deref(), Some("node-456"));
643 assert_eq!(core_config.group_name.as_deref(), Some("group-abc"));
644 }
645
646 #[test]
647 fn test_service_configuration_from_core_conversion() {
648 let mut core_config = SlimServiceConfiguration::new();
649 core_config.node_id = Some("core-node".to_string());
650 core_config.group_name = Some("core-group".to_string());
651
652 let config: ServiceConfig = core_config.into();
653 assert_eq!(config.node_id.as_deref(), Some("core-node"));
654 assert_eq!(config.group_name.as_deref(), Some("core-group"));
655 }
656
657 #[test]
658 fn test_service_configuration_roundtrip() {
659 let original = ServiceConfig {
660 node_id: Some("roundtrip-node".to_string()),
661 group_name: Some("roundtrip-group".to_string()),
662 dataplane: DataplaneConfig::default(),
663 };
664
665 let core: SlimServiceConfiguration = original.clone().into();
666 let roundtrip: ServiceConfig = core.into();
667
668 assert_eq!(original.node_id, roundtrip.node_id);
669 assert_eq!(original.group_name, roundtrip.group_name);
670 }
671
672 #[test]
677 fn test_service_new() {
678 let service = Service::new("test-service".to_string());
679 let name = service.get_name();
680 assert!(name.contains("test-service"));
681 }
682
683 #[test]
684 fn test_service_new_with_config() {
685 let config = ServiceConfig {
686 node_id: Some("test-node".to_string()),
687 group_name: Some("test-group".to_string()),
688 dataplane: DataplaneConfig::default(),
689 };
690
691 let service = Service::new_with_config("configured-service".to_string(), config.clone());
692 let retrieved_config = service.config();
693
694 assert_eq!(retrieved_config.node_id, config.node_id);
695 assert_eq!(retrieved_config.group_name, config.group_name);
696 }
697
698 #[test]
699 fn test_service_inner_clone() {
700 let service = Service::new("inner-test".to_string());
701 let inner1 = service.inner();
702 let inner2 = service.inner();
703
704 assert!(Arc::ptr_eq(&inner1, &inner2));
706 }
707
708 #[test]
709 fn test_service_from_slim_service() {
710 let kind = Kind::new(KIND).expect("Invalid service kind");
711 let id = ID::new_with_name(kind, "from-test").expect("Invalid service name");
712 let slim_service = SlimService::new(id);
713
714 let service: Service = slim_service.into();
715 assert!(Arc::strong_count(&service.inner) >= 1);
717 }
718
719 #[test]
724 fn test_global_service_singleton() {
725 let service1 = get_global_service();
726 let service2 = get_global_service();
727
728 assert!(Arc::ptr_eq(&service1, &service2));
730
731 assert!(Arc::ptr_eq(&service1.inner, &service2.inner));
733 }
734
735 #[test]
736 fn test_get_global_service_function() {
737 let service1 = get_global_service();
738 let service2 = get_global_service();
739
740 assert!(Arc::ptr_eq(&service1, &service2));
741 }
742
743 #[tokio::test]
744 async fn test_service_arc_sharing() {
745 let base_name = create_test_name();
746 let (provider, verifier) = create_test_auth();
747
748 let global_service = get_global_service();
750 let ptr1 = Arc::as_ptr(&global_service.inner) as usize;
751
752 let _global_adapter1 =
754 App::new_async(base_name.clone(), provider.clone(), verifier.clone())
755 .await
756 .unwrap();
757
758 let _global_adapter2 = App::new_async(base_name, provider, verifier).await.unwrap();
760
761 let global_service2 = get_global_service();
762 let ptr2 = Arc::as_ptr(&global_service2.inner) as usize;
763
764 assert_eq!(ptr1, ptr2);
766 }
767
768 #[test]
769 fn test_global_service_name() {
770 let name = get_global_service().get_name();
771 assert!(!name.is_empty());
772 assert!(name.contains("global-bindings-service"));
773 }
774
775 #[tokio::test]
780 async fn test_service_shutdown_without_run() {
781 let service = Service::new("shutdown-test".to_string());
782 let result = service.shutdown_async().await;
784 assert!(result.is_ok() || result.is_err());
786 }
787
788 #[test]
793 fn test_stop_nonexistent_server() {
794 let service = Service::new("stop-test".to_string());
795 let result = service.stop_server("127.0.0.1:99999".to_string());
796 assert!(result.is_err());
798 }
799
800 #[test]
801 fn test_disconnect_invalid_connection() {
802 let service = Service::new("disconnect-test".to_string());
803 let result = service.disconnect(999999);
804 assert!(result.is_err());
806 }
807
808 #[tokio::test]
809 async fn test_get_connection_id_nonexistent() {
810 let service = Service::new("conn-id-test".to_string());
811 let conn_id = service.get_connection_id("nonexistent-endpoint".to_string());
812 assert!(conn_id.is_none());
813 }
814
815 #[tokio::test]
820 async fn test_create_adapter_async() {
821 let service = Service::new("adapter-test".to_string());
822 let base_name = Arc::new(Name::new(
823 "org".to_string(),
824 "namespace".to_string(),
825 "adapter-app".to_string(),
826 ));
827 let (provider, verifier) = create_test_auth();
828
829 let result = service
830 .create_app_async(base_name, provider, verifier)
831 .await;
832
833 assert!(result.is_ok(), "Should create adapter successfully");
834 let adapter = result.unwrap();
835 assert!(adapter.id() > 0, "Adapter should have non-zero ID");
836 }
837
838 #[tokio::test]
839 async fn test_create_adapter_with_different_names() {
840 let service = Service::new("multi-adapter-test".to_string());
841 let (provider, verifier) = create_test_auth();
842
843 let names = vec![
844 ("org1", "ns1", "app1"),
845 ("org2", "ns2", "app2"),
846 ("org3", "ns3", "app3"),
847 ];
848
849 for (org, ns, app) in names {
850 let base_name = Arc::new(Name::new(org.to_string(), ns.to_string(), app.to_string()));
851
852 let result = service
853 .create_app_async(base_name, provider.clone(), verifier.clone())
854 .await;
855
856 assert!(
857 result.is_ok(),
858 "Should create adapter for {}/{}/{}",
859 org,
860 ns,
861 app
862 );
863 }
864 }
865
866 #[tokio::test]
867 async fn test_create_adapter_unique_ids() {
868 let service = Service::new("unique-ids-test".to_string());
870 let base_name = Arc::new(Name::new(
871 "org".to_string(),
872 "namespace".to_string(),
873 "unique-app".to_string(),
874 ));
875 let (provider, verifier) = create_test_auth();
876
877 let adapter1 = service
878 .create_app_async(base_name.clone(), provider.clone(), verifier.clone())
879 .await
880 .expect("Failed to create first adapter");
881
882 let adapter2 = service
883 .create_app_async(base_name, provider, verifier)
884 .await
885 .expect("Failed to create second adapter");
886
887 assert_ne!(adapter1.id(), adapter2.id());
890
891 assert!(adapter1.id() > 0);
893 assert!(adapter2.id() > 0);
894 }
895
896 #[tokio::test]
897 async fn test_create_app_with_secret_async() {
898 let service = Service::new("secret-app-test".to_string());
899 let base_name = Arc::new(Name::new(
900 "org".to_string(),
901 "namespace".to_string(),
902 "secret-app".to_string(),
903 ));
904 let secret = TEST_VALID_SECRET.to_string();
905
906 let result = service
907 .create_app_with_secret_async(base_name, secret)
908 .await;
909
910 assert!(result.is_ok(), "Should create app with secret successfully");
911 let app = result.unwrap();
912 assert!(app.id() > 0, "App should have non-zero ID");
913 }
914
915 #[tokio::test]
916 async fn test_create_app_with_secret_multiple() {
917 let service = Service::new("multi-secret-app-test".to_string());
918 let secret = TEST_VALID_SECRET.to_string();
919
920 let names = vec![
921 ("org1", "ns1", "secret-app1"),
922 ("org2", "ns2", "secret-app2"),
923 ("org3", "ns3", "secret-app3"),
924 ];
925
926 for (org, ns, app) in names {
927 let base_name = Arc::new(Name::new(org.to_string(), ns.to_string(), app.to_string()));
928
929 let result = service
930 .create_app_with_secret_async(base_name, secret.clone())
931 .await;
932
933 assert!(
934 result.is_ok(),
935 "Should create secret app for {}/{}/{}",
936 org,
937 ns,
938 app
939 );
940 }
941 }
942
943 #[test]
944 fn test_create_app_blocking() {
945 let service = Service::new("blocking-app-test".to_string());
946 let base_name = Arc::new(Name::new(
947 "org".to_string(),
948 "namespace".to_string(),
949 "blocking-app".to_string(),
950 ));
951 let (provider, verifier) = create_test_auth();
952
953 let result = service.create_app(base_name, provider, verifier);
954
955 assert!(result.is_ok(), "Should create app in blocking mode");
956 let app = result.unwrap();
957 assert!(app.id() > 0, "App should have non-zero ID");
958 }
959
960 #[test]
961 fn test_create_app_with_secret_blocking() {
962 let service = Service::new("blocking-secret-app-test".to_string());
963 let base_name = Arc::new(Name::new(
964 "org".to_string(),
965 "namespace".to_string(),
966 "blocking-secret-app".to_string(),
967 ));
968 let secret = TEST_VALID_SECRET.to_string();
969
970 let result = service.create_app_with_secret(base_name, secret);
971
972 assert!(result.is_ok(), "Should create secret app in blocking mode");
973 let app = result.unwrap();
974 assert!(app.id() > 0, "App should have non-zero ID");
975 }
976
977 #[tokio::test]
978 async fn test_create_app_async_internal_directly() {
979 let service = Service::new("internal-test".to_string());
980 let base_name = create_test_name();
981 let (provider, verifier) = create_test_auth();
982
983 let result = create_app_async_internal(
984 base_name,
985 provider,
986 verifier,
987 service.inner.clone(),
988 crate::app::Direction::Bidirectional,
989 )
990 .await;
991
992 assert!(result.is_ok(), "Should create app via internal function");
993 let app = result.unwrap();
994 assert!(app.id() > 0, "App should have non-zero ID");
995 }
996
997 #[tokio::test]
998 async fn test_create_app_with_same_secret_different_ids() {
999 let service = Service::new("same-secret-test".to_string());
1001 let secret = TEST_VALID_SECRET.to_string();
1002 let base_name = Arc::new(Name::new(
1003 "org".to_string(),
1004 "namespace".to_string(),
1005 "same-secret-app".to_string(),
1006 ));
1007
1008 let app1 = service
1009 .create_app_with_secret_async(base_name.clone(), secret.clone())
1010 .await
1011 .expect("Failed to create first app");
1012
1013 let app2 = service
1014 .create_app_with_secret_async(base_name, secret)
1015 .await
1016 .expect("Failed to create second app");
1017
1018 assert_ne!(app1.id(), app2.id());
1020 assert!(app1.id() > 0);
1021 assert!(app2.id() > 0);
1022 }
1023
1024 #[test]
1029 fn test_new_service_configuration() {
1030 let config = new_service_configuration();
1031 assert!(config.node_id.is_none());
1032 assert!(config.group_name.is_none());
1033 }
1034
1035 #[test]
1036 fn test_new_dataplane_config() {
1037 let config = new_dataplane_config();
1038 assert!(config.servers.is_empty());
1039 assert!(config.clients.is_empty());
1040 }
1041
1042 #[test]
1043 fn test_create_service() {
1044 let result = create_service("factory-test".to_string());
1045 assert!(result.is_ok());
1046 }
1047
1048 #[test]
1049 fn test_create_service_with_config() {
1050 let config = ServiceConfig {
1051 node_id: Some("factory-node".to_string()),
1052 group_name: Some("factory-group".to_string()),
1053 dataplane: DataplaneConfig::default(),
1054 };
1055
1056 let result = create_service_with_config("factory-configured-test".to_string(), config);
1057 assert!(result.is_ok());
1058 }
1059
1060 #[test]
1065 fn test_global_stop_server_convenience() {
1066 let result = get_global_service().stop_server("127.0.0.1:88888".to_string());
1067 assert!(result.is_err());
1069 }
1070
1071 #[test]
1072 fn test_global_disconnect_convenience() {
1073 let result = get_global_service().disconnect(888888);
1074 assert!(result.is_err());
1076 }
1077
1078 #[test]
1079 fn test_global_get_connection_id_convenience() {
1080 let conn_id = get_global_service().get_connection_id("nonexistent-global".to_string());
1081 assert!(conn_id.is_none());
1082 }
1083
1084 #[tokio::test]
1089 async fn test_service_operations_on_uninitialized_service() {
1090 let service = Service::new("uninitialized-test".to_string());
1092
1093 let result = service.stop_server("127.0.0.1:11111".to_string());
1095 assert!(result.is_err());
1096
1097 let result = service.disconnect(11111);
1099 assert!(result.is_err());
1100
1101 let conn_id = service.get_connection_id("fake-endpoint".to_string());
1103 assert!(conn_id.is_none());
1104 }
1105
1106 #[test]
1111 fn test_service_with_multiple_configs() {
1112 let server_config = ServerConfig::default();
1113 let client_config = ClientConfig::default();
1114
1115 let dataplane = DataplaneConfig {
1116 servers: vec![server_config.clone(), server_config],
1117 clients: vec![client_config.clone(), client_config],
1118 };
1119
1120 let service_config = ServiceConfig {
1121 node_id: Some("multi-config-node".to_string()),
1122 group_name: Some("multi-config-group".to_string()),
1123 dataplane,
1124 };
1125
1126 let service = Service::new_with_config("multi-config-service".to_string(), service_config);
1127 let retrieved = service.config();
1128
1129 assert_eq!(retrieved.dataplane.servers.len(), 2);
1130 assert_eq!(retrieved.dataplane.clients.len(), 2);
1131 }
1132
1133 #[test]
1134 fn test_service_config_mutation_isolation() {
1135 let config = ServiceConfig {
1136 node_id: Some("original-node".to_string()),
1137 group_name: Some("original-group".to_string()),
1138 dataplane: DataplaneConfig::default(),
1139 };
1140
1141 let service = Service::new_with_config("isolation-test".to_string(), config.clone());
1142
1143 let retrieved = service.config();
1145 assert_eq!(retrieved.node_id, config.node_id);
1146
1147 let mut modified_config = config;
1149 modified_config.node_id = Some("modified-node".to_string());
1150
1151 let retrieved2 = service.config();
1153 assert_eq!(retrieved2.node_id.as_deref(), Some("original-node"));
1154 }
1155
1156 #[tokio::test]
1161 async fn test_run_server_async_runtime_context() {
1162 let service = Service::new("run-server-runtime-test".to_string());
1163 let config = ServerConfig::default();
1164
1165 let result = service.run_server_async(config).await;
1167 let _ = result; }
1170
1171 #[tokio::test]
1172 async fn test_connect_async_runtime_context() {
1173 let service = Service::new("connect-runtime-test".to_string());
1174 let config = ClientConfig::default();
1175
1176 let result = service.connect_async(config).await;
1178 let _ = result; }
1181
1182 #[test]
1183 fn test_run_server_blocking() {
1184 let service = Service::new("run-server-blocking-test".to_string());
1185 let config = ServerConfig::default();
1186
1187 let result = service.run_server(config);
1189 let _ = result;
1191 }
1192
1193 #[test]
1194 fn test_connect_blocking() {
1195 let service = Service::new("connect-blocking-test".to_string());
1196 let config = ClientConfig::default();
1197
1198 let result = service.connect(config);
1200 let _ = result;
1202 }
1203
1204 #[tokio::test]
1209 async fn test_multiple_apps_on_same_service() {
1210 let service = Service::new("multi-app-service".to_string());
1211 let secret = TEST_VALID_SECRET.to_string();
1212
1213 let name1 = Arc::new(Name::new(
1214 "org".to_string(),
1215 "ns".to_string(),
1216 "app1".to_string(),
1217 ));
1218 let name2 = Arc::new(Name::new(
1219 "org".to_string(),
1220 "ns".to_string(),
1221 "app2".to_string(),
1222 ));
1223 let name3 = Arc::new(Name::new(
1224 "org".to_string(),
1225 "ns".to_string(),
1226 "app3".to_string(),
1227 ));
1228
1229 let app1 = service
1230 .create_app_with_secret_async(name1, secret.clone())
1231 .await
1232 .expect("Failed to create app1");
1233
1234 let app2 = service
1235 .create_app_with_secret_async(name2, secret.clone())
1236 .await
1237 .expect("Failed to create app2");
1238
1239 let app3 = service
1240 .create_app_with_secret_async(name3, secret)
1241 .await
1242 .expect("Failed to create app3");
1243
1244 assert_ne!(app1.id(), app2.id());
1246 assert_ne!(app1.id(), app3.id());
1247 assert_ne!(app2.id(), app3.id());
1248
1249 assert!(app1.id() > 0);
1251 assert!(app2.id() > 0);
1252 assert!(app3.id() > 0);
1253 }
1254
1255 #[test]
1256 fn test_service_run_blocking() {
1257 let service = Service::new("run-blocking-test".to_string());
1258 let result = service.run();
1260 let _ = result;
1262 }
1263
1264 #[test]
1265 fn test_service_shutdown_blocking() {
1266 let service = Service::new("shutdown-blocking-test".to_string());
1267 let result = service.shutdown();
1269 let _ = result;
1271 }
1272
1273 #[tokio::test]
1274 async fn test_run_async() {
1275 let service = Service::new("run-async-test".to_string());
1276 let result = service.run_async().await;
1278 let _ = result;
1280 }
1281}