1mod homeserver_config;
17
18use std::{fmt, sync::Arc};
19
20use homeserver_config::*;
21use matrix_sdk_base::{store::StoreConfig, BaseClient};
22use ruma::{
23 api::{error::FromHttpResponseError, MatrixVersion},
24 OwnedServerName, ServerName,
25};
26use thiserror::Error;
27use tokio::sync::{broadcast, Mutex, OnceCell};
28use tracing::{debug, field::debug, instrument, Span};
29
30use super::{Client, ClientInner};
31#[cfg(feature = "experimental-oidc")]
32use crate::authentication::oidc::OidcCtx;
33#[cfg(feature = "e2e-encryption")]
34use crate::crypto::{CollectStrategy, TrustRequirement};
35#[cfg(feature = "e2e-encryption")]
36use crate::encryption::EncryptionSettings;
37#[cfg(not(target_arch = "wasm32"))]
38use crate::http_client::HttpSettings;
39use crate::{
40 authentication::AuthCtx, client::ClientServerCapabilities, config::RequestConfig,
41 error::RumaApiError, http_client::HttpClient, send_queue::SendQueueData,
42 sliding_sync::VersionBuilder as SlidingSyncVersionBuilder, HttpError, IdParseError,
43};
44
45#[must_use]
84#[derive(Clone, Debug)]
85pub struct ClientBuilder {
86 homeserver_cfg: Option<HomeserverConfig>,
87 sliding_sync_version_builder: SlidingSyncVersionBuilder,
88 http_cfg: Option<HttpConfig>,
89 store_config: BuilderStoreConfig,
90 request_config: RequestConfig,
91 respect_login_well_known: bool,
92 server_versions: Option<Box<[MatrixVersion]>>,
93 handle_refresh_tokens: bool,
94 base_client: Option<BaseClient>,
95 #[cfg(feature = "e2e-encryption")]
96 encryption_settings: EncryptionSettings,
97 #[cfg(feature = "e2e-encryption")]
98 room_key_recipient_strategy: CollectStrategy,
99 #[cfg(feature = "e2e-encryption")]
100 decryption_trust_requirement: TrustRequirement,
101 cross_process_store_locks_holder_name: String,
102}
103
104impl ClientBuilder {
105 const DEFAULT_CROSS_PROCESS_STORE_LOCKS_HOLDER_NAME: &str = "main";
106
107 pub(crate) fn new() -> Self {
108 Self {
109 homeserver_cfg: None,
110 sliding_sync_version_builder: SlidingSyncVersionBuilder::Native,
111 http_cfg: None,
112 store_config: BuilderStoreConfig::Custom(StoreConfig::new(
113 Self::DEFAULT_CROSS_PROCESS_STORE_LOCKS_HOLDER_NAME.to_owned(),
114 )),
115 request_config: Default::default(),
116 respect_login_well_known: true,
117 server_versions: None,
118 handle_refresh_tokens: false,
119 base_client: None,
120 #[cfg(feature = "e2e-encryption")]
121 encryption_settings: Default::default(),
122 #[cfg(feature = "e2e-encryption")]
123 room_key_recipient_strategy: Default::default(),
124 #[cfg(feature = "e2e-encryption")]
125 decryption_trust_requirement: TrustRequirement::Untrusted,
126 cross_process_store_locks_holder_name:
127 Self::DEFAULT_CROSS_PROCESS_STORE_LOCKS_HOLDER_NAME.to_owned(),
128 }
129 }
130
131 pub fn homeserver_url(mut self, url: impl AsRef<str>) -> Self {
138 self.homeserver_cfg = Some(HomeserverConfig::HomeserverUrl(url.as_ref().to_owned()));
139 self
140 }
141
142 pub fn server_name(mut self, server_name: &ServerName) -> Self {
152 self.homeserver_cfg = Some(HomeserverConfig::ServerName {
153 server: server_name.to_owned(),
154 protocol: UrlScheme::Https,
156 });
157 self
158 }
159
160 pub fn insecure_server_name_no_tls(mut self, server_name: &ServerName) -> Self {
169 self.homeserver_cfg = Some(HomeserverConfig::ServerName {
170 server: server_name.to_owned(),
171 protocol: UrlScheme::Http,
172 });
173 self
174 }
175
176 pub fn server_name_or_homeserver_url(mut self, server_name_or_url: impl AsRef<str>) -> Self {
187 self.homeserver_cfg = Some(HomeserverConfig::ServerNameOrHomeserverUrl(
188 server_name_or_url.as_ref().to_owned(),
189 ));
190 self
191 }
192
193 pub fn sliding_sync_version_builder(
195 mut self,
196 version_builder: SlidingSyncVersionBuilder,
197 ) -> Self {
198 self.sliding_sync_version_builder = version_builder;
199 self
200 }
201
202 #[cfg(feature = "sqlite")]
204 pub fn sqlite_store(
205 mut self,
206 path: impl AsRef<std::path::Path>,
207 passphrase: Option<&str>,
208 ) -> Self {
209 self.store_config = BuilderStoreConfig::Sqlite {
210 path: path.as_ref().to_owned(),
211 cache_path: None,
212 passphrase: passphrase.map(ToOwned::to_owned),
213 };
214 self
215 }
216
217 #[cfg(feature = "sqlite")]
220 pub fn sqlite_store_with_cache_path(
221 mut self,
222 path: impl AsRef<std::path::Path>,
223 cache_path: impl AsRef<std::path::Path>,
224 passphrase: Option<&str>,
225 ) -> Self {
226 self.store_config = BuilderStoreConfig::Sqlite {
227 path: path.as_ref().to_owned(),
228 cache_path: Some(cache_path.as_ref().to_owned()),
229 passphrase: passphrase.map(ToOwned::to_owned),
230 };
231 self
232 }
233
234 #[cfg(feature = "indexeddb")]
236 pub fn indexeddb_store(mut self, name: &str, passphrase: Option<&str>) -> Self {
237 self.store_config = BuilderStoreConfig::IndexedDb {
238 name: name.to_owned(),
239 passphrase: passphrase.map(ToOwned::to_owned),
240 };
241 self
242 }
243
244 pub fn store_config(mut self, store_config: StoreConfig) -> Self {
266 self.store_config = BuilderStoreConfig::Custom(store_config);
267 self
268 }
269
270 pub fn respect_login_well_known(mut self, value: bool) -> Self {
273 self.respect_login_well_known = value;
274 self
275 }
276
277 pub fn request_config(mut self, request_config: RequestConfig) -> Self {
279 self.request_config = request_config;
280 self
281 }
282
283 #[cfg(not(target_arch = "wasm32"))]
299 pub fn proxy(mut self, proxy: impl AsRef<str>) -> Self {
300 self.http_settings().proxy = Some(proxy.as_ref().to_owned());
301 self
302 }
303
304 #[cfg(not(target_arch = "wasm32"))]
306 pub fn disable_ssl_verification(mut self) -> Self {
307 self.http_settings().disable_ssl_verification = true;
308 self
309 }
310
311 #[cfg(not(target_arch = "wasm32"))]
313 pub fn user_agent(mut self, user_agent: impl AsRef<str>) -> Self {
314 self.http_settings().user_agent = Some(user_agent.as_ref().to_owned());
315 self
316 }
317
318 #[cfg(not(target_arch = "wasm32"))]
327 pub fn add_root_certificates(mut self, certificates: Vec<reqwest::Certificate>) -> Self {
328 self.http_settings().additional_root_certificates = certificates;
329 self
330 }
331
332 #[cfg(not(target_arch = "wasm32"))]
336 pub fn disable_built_in_root_certificates(mut self) -> Self {
337 self.http_settings().disable_built_in_root_certificates = true;
338 self
339 }
340
341 pub fn http_client(mut self, client: reqwest::Client) -> Self {
351 self.http_cfg = Some(HttpConfig::Custom(client));
352 self
353 }
354
355 pub fn server_versions(mut self, value: impl IntoIterator<Item = MatrixVersion>) -> Self {
360 self.server_versions = Some(value.into_iter().collect());
361 self
362 }
363
364 #[cfg(not(target_arch = "wasm32"))]
365 fn http_settings(&mut self) -> &mut HttpSettings {
366 self.http_cfg.get_or_insert_with(Default::default).settings()
367 }
368
369 pub fn handle_refresh_tokens(mut self) -> Self {
391 self.handle_refresh_tokens = true;
392 self
393 }
394
395 #[doc(hidden)]
397 pub fn base_client(mut self, base_client: BaseClient) -> Self {
398 self.base_client = Some(base_client);
399 self
400 }
401
402 #[cfg(feature = "e2e-encryption")]
405 pub fn with_encryption_settings(mut self, settings: EncryptionSettings) -> Self {
406 self.encryption_settings = settings;
407 self
408 }
409
410 #[cfg(feature = "e2e-encryption")]
413 pub fn with_room_key_recipient_strategy(mut self, strategy: CollectStrategy) -> Self {
414 self.room_key_recipient_strategy = strategy;
415 self
416 }
417
418 #[cfg(feature = "e2e-encryption")]
420 pub fn with_decryption_trust_requirement(
421 mut self,
422 trust_requirement: TrustRequirement,
423 ) -> Self {
424 self.decryption_trust_requirement = trust_requirement;
425 self
426 }
427
428 pub fn cross_process_store_locks_holder_name(mut self, holder_name: String) -> Self {
438 self.cross_process_store_locks_holder_name = holder_name;
439 self
440 }
441
442 #[instrument(skip_all, target = "matrix_sdk::client", fields(homeserver))]
455 pub async fn build(self) -> Result<Client, ClientBuildError> {
456 debug!("Starting to build the Client");
457
458 let homeserver_cfg = self.homeserver_cfg.ok_or(ClientBuildError::MissingHomeserver)?;
459 Span::current().record("homeserver", debug(&homeserver_cfg));
460
461 #[cfg_attr(target_arch = "wasm32", allow(clippy::infallible_destructuring_match))]
462 let inner_http_client = match self.http_cfg.unwrap_or_default() {
463 #[cfg(not(target_arch = "wasm32"))]
464 HttpConfig::Settings(mut settings) => {
465 settings.timeout = self.request_config.timeout;
466 settings.make_client()?
467 }
468 HttpConfig::Custom(c) => c,
469 };
470
471 let base_client = if let Some(base_client) = self.base_client {
472 base_client
473 } else {
474 #[allow(unused_mut)]
475 let mut client = BaseClient::with_store_config(
476 build_store_config(self.store_config, &self.cross_process_store_locks_holder_name)
477 .await?,
478 );
479
480 #[cfg(feature = "e2e-encryption")]
481 {
482 client.room_key_recipient_strategy = self.room_key_recipient_strategy;
483 client.decryption_trust_requirement = self.decryption_trust_requirement;
484 }
485
486 client
487 };
488
489 let http_client = HttpClient::new(inner_http_client.clone(), self.request_config);
490
491 #[allow(unused_variables)]
492 let HomeserverDiscoveryResult { server, homeserver, supported_versions } =
493 homeserver_cfg.discover(&http_client).await?;
494
495 let sliding_sync_version = {
496 let supported_versions = match supported_versions {
497 Some(versions) => Some(versions),
498 None if self.sliding_sync_version_builder.needs_get_supported_versions() => {
499 Some(get_supported_versions(&homeserver, &http_client).await?)
500 }
501 None => None,
502 };
503
504 let version = self.sliding_sync_version_builder.build(supported_versions.as_ref())?;
505
506 tracing::info!(?version, "selected sliding sync version");
507
508 version
509 };
510
511 #[cfg(feature = "experimental-oidc")]
512 let allow_insecure_oidc = homeserver.scheme() == "http";
513
514 let auth_ctx = Arc::new(AuthCtx {
515 handle_refresh_tokens: self.handle_refresh_tokens,
516 refresh_token_lock: Arc::new(Mutex::new(Ok(()))),
517 session_change_sender: broadcast::Sender::new(1),
518 auth_data: OnceCell::default(),
519 reload_session_callback: OnceCell::default(),
520 save_session_callback: OnceCell::default(),
521 #[cfg(feature = "experimental-oidc")]
522 oidc: OidcCtx::new(allow_insecure_oidc),
523 });
524
525 let send_queue = Arc::new(SendQueueData::new(true));
527
528 let server_capabilities = ClientServerCapabilities {
529 server_versions: self.server_versions,
530 unstable_features: None,
531 };
532
533 let event_cache = OnceCell::new();
534 let inner = ClientInner::new(
535 auth_ctx,
536 server,
537 homeserver,
538 sliding_sync_version,
539 http_client,
540 base_client,
541 server_capabilities,
542 self.respect_login_well_known,
543 event_cache,
544 send_queue,
545 #[cfg(feature = "e2e-encryption")]
546 self.encryption_settings,
547 self.cross_process_store_locks_holder_name,
548 )
549 .await;
550
551 debug!("Done building the Client");
552
553 Ok(Client { inner })
554 }
555}
556
557pub fn sanitize_server_name(s: &str) -> crate::Result<OwnedServerName, IdParseError> {
561 ServerName::parse(
562 s.trim().trim_start_matches("http://").trim_start_matches("https://").trim_end_matches('/'),
563 )
564}
565
566#[allow(clippy::unused_async, unused)] async fn build_store_config(
568 builder_config: BuilderStoreConfig,
569 cross_process_store_locks_holder_name: &str,
570) -> Result<StoreConfig, ClientBuildError> {
571 #[allow(clippy::infallible_destructuring_match)]
572 let store_config = match builder_config {
573 #[cfg(feature = "sqlite")]
574 BuilderStoreConfig::Sqlite { path, cache_path, passphrase } => {
575 let store_config = StoreConfig::new(cross_process_store_locks_holder_name.to_owned())
576 .state_store(
577 matrix_sdk_sqlite::SqliteStateStore::open(&path, passphrase.as_deref()).await?,
578 )
579 .event_cache_store(
580 matrix_sdk_sqlite::SqliteEventCacheStore::open(
581 cache_path.as_ref().unwrap_or(&path),
582 passphrase.as_deref(),
583 )
584 .await?,
585 );
586
587 #[cfg(feature = "e2e-encryption")]
588 let store_config = store_config.crypto_store(
589 matrix_sdk_sqlite::SqliteCryptoStore::open(&path, passphrase.as_deref()).await?,
590 );
591
592 store_config
593 }
594
595 #[cfg(feature = "indexeddb")]
596 BuilderStoreConfig::IndexedDb { name, passphrase } => {
597 build_indexeddb_store_config(
598 &name,
599 passphrase.as_deref(),
600 cross_process_store_locks_holder_name,
601 )
602 .await?
603 }
604
605 BuilderStoreConfig::Custom(config) => config,
606 };
607 Ok(store_config)
608}
609
610#[cfg(all(target_arch = "wasm32", feature = "indexeddb"))]
613async fn build_indexeddb_store_config(
614 name: &str,
615 passphrase: Option<&str>,
616 cross_process_store_locks_holder_name: &str,
617) -> Result<StoreConfig, ClientBuildError> {
618 let cross_process_store_locks_holder_name = cross_process_store_locks_holder_name.to_owned();
619
620 #[cfg(feature = "e2e-encryption")]
621 let store_config = {
622 let (state_store, crypto_store) =
623 matrix_sdk_indexeddb::open_stores_with_name(name, passphrase).await?;
624 StoreConfig::new(cross_process_store_locks_holder_name)
625 .state_store(state_store)
626 .crypto_store(crypto_store)
627 };
628
629 #[cfg(not(feature = "e2e-encryption"))]
630 let store_config = {
631 let state_store = matrix_sdk_indexeddb::open_state_store(name, passphrase).await?;
632 StoreConfig::new(cross_process_store_locks_holder_name).state_store(state_store)
633 };
634
635 let store_config = {
636 tracing::warn!("The IndexedDB backend does not implement an event cache store, falling back to the in-memory event cache store…");
637 store_config.event_cache_store(matrix_sdk_base::event_cache::store::MemoryStore::new())
638 };
639
640 Ok(store_config)
641}
642
643#[cfg(all(not(target_arch = "wasm32"), feature = "indexeddb"))]
644#[allow(clippy::unused_async)]
645async fn build_indexeddb_store_config(
646 _name: &str,
647 _passphrase: Option<&str>,
648 _event_cache_store_lock_holder_name: &str,
649) -> Result<StoreConfig, ClientBuildError> {
650 panic!("the IndexedDB is only available on the 'wasm32' arch")
651}
652
653#[derive(Clone, Debug)]
654enum HttpConfig {
655 #[cfg(not(target_arch = "wasm32"))]
656 Settings(HttpSettings),
657 Custom(reqwest::Client),
658}
659
660#[cfg(not(target_arch = "wasm32"))]
661impl HttpConfig {
662 fn settings(&mut self) -> &mut HttpSettings {
663 match self {
664 Self::Settings(s) => s,
665 Self::Custom(_) => {
666 *self = Self::default();
667 match self {
668 Self::Settings(s) => s,
669 Self::Custom(_) => unreachable!(),
670 }
671 }
672 }
673 }
674}
675
676impl Default for HttpConfig {
677 fn default() -> Self {
678 #[cfg(not(target_arch = "wasm32"))]
679 return Self::Settings(HttpSettings::default());
680
681 #[cfg(target_arch = "wasm32")]
682 return Self::Custom(reqwest::Client::new());
683 }
684}
685
686#[derive(Clone)]
687enum BuilderStoreConfig {
688 #[cfg(feature = "sqlite")]
689 Sqlite {
690 path: std::path::PathBuf,
691 cache_path: Option<std::path::PathBuf>,
692 passphrase: Option<String>,
693 },
694 #[cfg(feature = "indexeddb")]
695 IndexedDb {
696 name: String,
697 passphrase: Option<String>,
698 },
699 Custom(StoreConfig),
700}
701
702#[cfg(not(tarpaulin_include))]
703impl fmt::Debug for BuilderStoreConfig {
704 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
705 #[allow(clippy::infallible_destructuring_match)]
706 match self {
707 #[cfg(feature = "sqlite")]
708 Self::Sqlite { path, .. } => {
709 f.debug_struct("Sqlite").field("path", path).finish_non_exhaustive()
710 }
711 #[cfg(feature = "indexeddb")]
712 Self::IndexedDb { name, .. } => {
713 f.debug_struct("IndexedDb").field("name", name).finish_non_exhaustive()
714 }
715 Self::Custom(store_config) => f.debug_tuple("Custom").field(store_config).finish(),
716 }
717 }
718}
719
720#[derive(Debug, Error)]
722pub enum ClientBuildError {
723 #[error("no homeserver or user ID was configured")]
725 MissingHomeserver,
726
727 #[error("The supplied server name is invalid")]
729 InvalidServerName,
730
731 #[error("Error looking up the .well-known endpoint on auto-discovery")]
733 AutoDiscovery(FromHttpResponseError<RumaApiError>),
734
735 #[error(transparent)]
737 SlidingSyncVersion(#[from] crate::sliding_sync::VersionBuilderError),
738
739 #[error(transparent)]
741 Url(#[from] url::ParseError),
742
743 #[error(transparent)]
745 Http(#[from] HttpError),
746
747 #[cfg(feature = "indexeddb")]
749 #[error(transparent)]
750 IndexeddbStore(#[from] matrix_sdk_indexeddb::OpenStoreError),
751
752 #[cfg(feature = "sqlite")]
754 #[error(transparent)]
755 SqliteStore(#[from] matrix_sdk_sqlite::OpenStoreError),
756}
757
758#[cfg(all(test, not(target_arch = "wasm32")))]
760pub(crate) mod tests {
761 use assert_matches::assert_matches;
762 use matrix_sdk_test::{async_test, test_json};
763 use serde_json::{json_internal, Value as JsonValue};
764 use wiremock::{
765 matchers::{method, path},
766 Mock, MockServer, ResponseTemplate,
767 };
768
769 use super::*;
770 use crate::sliding_sync::Version as SlidingSyncVersion;
771
772 #[test]
773 fn test_sanitize_server_name() {
774 assert_eq!(sanitize_server_name("matrix.org").unwrap().as_str(), "matrix.org");
775 assert_eq!(sanitize_server_name("https://matrix.org").unwrap().as_str(), "matrix.org");
776 assert_eq!(sanitize_server_name("http://matrix.org").unwrap().as_str(), "matrix.org");
777 assert_eq!(
778 sanitize_server_name("https://matrix.server.org").unwrap().as_str(),
779 "matrix.server.org"
780 );
781 assert_eq!(
782 sanitize_server_name("https://matrix.server.org/").unwrap().as_str(),
783 "matrix.server.org"
784 );
785 assert_eq!(
786 sanitize_server_name(" https://matrix.server.org// ").unwrap().as_str(),
787 "matrix.server.org"
788 );
789 assert_matches!(sanitize_server_name("https://matrix.server.org/something"), Err(_))
790 }
791
792 #[async_test]
799 async fn test_discovery_invalid_server() {
800 let mut builder = ClientBuilder::new();
802
803 builder = builder.server_name_or_homeserver_url("⚠️ This won't work 🚫");
805 let error = builder.build().await.unwrap_err();
806
807 assert_matches!(error, ClientBuildError::InvalidServerName);
809 }
810
811 #[async_test]
812 async fn test_discovery_no_server() {
813 let mut builder = ClientBuilder::new();
815
816 builder = builder.server_name_or_homeserver_url("localhost:3456");
818 let error = builder.build().await.unwrap_err();
819
820 println!("{error}");
822 assert_matches!(error, ClientBuildError::Http(_));
823 }
824
825 #[async_test]
826 async fn test_discovery_web_server() {
827 let server = MockServer::start().await;
830 let mut builder = ClientBuilder::new();
831
832 builder = builder.server_name_or_homeserver_url(server.uri());
834 let error = builder.build().await.unwrap_err();
835
836 assert_matches!(error, ClientBuildError::AutoDiscovery(FromHttpResponseError::Server(_)));
838 }
839
840 #[async_test]
841 async fn test_discovery_direct_legacy() {
842 let homeserver = make_mock_homeserver().await;
844 let mut builder = ClientBuilder::new();
845
846 builder = builder.server_name_or_homeserver_url(homeserver.uri());
848 let _client = builder.build().await.unwrap();
849
850 assert!(_client.sliding_sync_version().is_native());
852 }
853
854 #[async_test]
855 async fn test_discovery_well_known_parse_error() {
856 let server = MockServer::start().await;
858 let homeserver = make_mock_homeserver().await;
859 let mut builder = ClientBuilder::new();
860
861 let well_known = make_well_known_json(&homeserver.uri());
862 let bad_json = well_known.to_string().replace(',', "");
863 Mock::given(method("GET"))
864 .and(path("/.well-known/matrix/client"))
865 .respond_with(ResponseTemplate::new(200).set_body_json(bad_json))
866 .mount(&server)
867 .await;
868
869 builder = builder.server_name_or_homeserver_url(server.uri());
871 let error = builder.build().await.unwrap_err();
872
873 assert_matches!(
875 error,
876 ClientBuildError::AutoDiscovery(FromHttpResponseError::Deserialization(_))
877 );
878 }
879
880 #[async_test]
881 async fn test_discovery_well_known_legacy() {
882 let server = MockServer::start().await;
885 let homeserver = make_mock_homeserver().await;
886 let mut builder = ClientBuilder::new();
887
888 Mock::given(method("GET"))
889 .and(path("/.well-known/matrix/client"))
890 .respond_with(
891 ResponseTemplate::new(200).set_body_json(make_well_known_json(&homeserver.uri())),
892 )
893 .mount(&server)
894 .await;
895
896 builder = builder.server_name_or_homeserver_url(server.uri());
898 let client = builder.build().await.unwrap();
899
900 assert!(client.sliding_sync_version().is_native());
903 }
904
905 #[async_test]
906 async fn test_sliding_sync_discover_native() {
907 let homeserver = make_mock_homeserver().await;
909 let mut builder = ClientBuilder::new();
910
911 builder = builder
914 .server_name_or_homeserver_url(homeserver.uri())
915 .sliding_sync_version_builder(SlidingSyncVersionBuilder::DiscoverNative);
916
917 let client = builder.build().await.unwrap();
918
919 assert_matches!(client.sliding_sync_version(), SlidingSyncVersion::Native);
921 }
922
923 #[async_test]
924 #[cfg(feature = "e2e-encryption")]
925 async fn test_set_up_decryption_trust_requirement_cross_signed() {
926 let homeserver = make_mock_homeserver().await;
927 let builder = ClientBuilder::new()
928 .server_name_or_homeserver_url(homeserver.uri())
929 .with_decryption_trust_requirement(TrustRequirement::CrossSigned);
930
931 let client = builder.build().await.unwrap();
932 assert_matches!(
933 client.base_client().decryption_trust_requirement,
934 TrustRequirement::CrossSigned
935 );
936 }
937
938 #[async_test]
939 #[cfg(feature = "e2e-encryption")]
940 async fn test_set_up_decryption_trust_requirement_untrusted() {
941 let homeserver = make_mock_homeserver().await;
942
943 let builder = ClientBuilder::new()
944 .server_name_or_homeserver_url(homeserver.uri())
945 .with_decryption_trust_requirement(TrustRequirement::Untrusted);
946
947 let client = builder.build().await.unwrap();
948 assert_matches!(
949 client.base_client().decryption_trust_requirement,
950 TrustRequirement::Untrusted
951 );
952 }
953
954 async fn make_mock_homeserver() -> MockServer {
957 let homeserver = MockServer::start().await;
958 Mock::given(method("GET"))
959 .and(path("/_matrix/client/versions"))
960 .respond_with(ResponseTemplate::new(200).set_body_json(&*test_json::VERSIONS))
961 .mount(&homeserver)
962 .await;
963 Mock::given(method("GET"))
964 .and(path("/_matrix/client/r0/login"))
965 .respond_with(ResponseTemplate::new(200).set_body_json(&*test_json::LOGIN_TYPES))
966 .mount(&homeserver)
967 .await;
968 homeserver
969 }
970
971 fn make_well_known_json(homeserver_url: &str) -> JsonValue {
972 ::serde_json::Value::Object({
973 let mut object = ::serde_json::Map::new();
974 let _ = object.insert(
975 "m.homeserver".into(),
976 json_internal!({
977 "base_url": homeserver_url
978 }),
979 );
980
981 object
982 })
983 }
984
985 #[async_test]
986 async fn test_cross_process_store_locks_holder_name() {
987 {
988 let homeserver = make_mock_homeserver().await;
989 let client =
990 ClientBuilder::new().homeserver_url(homeserver.uri()).build().await.unwrap();
991
992 assert_eq!(client.cross_process_store_locks_holder_name(), "main");
993 }
994
995 {
996 let homeserver = make_mock_homeserver().await;
997 let client = ClientBuilder::new()
998 .homeserver_url(homeserver.uri())
999 .cross_process_store_locks_holder_name("foo".to_owned())
1000 .build()
1001 .await
1002 .unwrap();
1003
1004 assert_eq!(client.cross_process_store_locks_holder_name(), "foo");
1005 }
1006 }
1007}