From f308e739cd25b4fee0e606d56e0dba20aab7d780 Mon Sep 17 00:00:00 2001 From: Hocuri Date: Thu, 17 Sep 2026 18:18:22 +0200 Subject: [PATCH] Start adding some tests --- .../src/deltachat_rpc_client/account.py | 12 ++++ .../src/deltachat_rpc_client/pytestplugin.py | 3 +- .../tests/test_multitransport.py | 8 ++- src/autorelay.rs | 2 +- src/autorelay/autorelay_tests.rs | 65 +++++++++++++++++++ 5 files changed, 87 insertions(+), 3 deletions(-) diff --git a/deltachat-rpc-client/src/deltachat_rpc_client/account.py b/deltachat-rpc-client/src/deltachat_rpc_client/account.py index e4c20659f..ddfaef4af 100644 --- a/deltachat-rpc-client/src/deltachat_rpc_client/account.py +++ b/deltachat-rpc-client/src/deltachat_rpc_client/account.py @@ -139,6 +139,18 @@ class Account: """Add a new transport using a QR code.""" yield self._rpc.add_transport_from_qr.future(self.id, qr) + @futuremethod + def init_transports(self, qr: Optional[str] = None): + """Automatically adds up to three transports. + + If the user just scanned a QR code of type `Account`, `Login`, + `AskVerifyContact`, `AskVerifyGroup`, or `AskJoinBroadcast`, + then UI implementations should pass it as the `qr` parameter. + The host(s) from the QR code will then also be considered + for creating an account there. + """ + yield self._rpc.init_transports.future(self.id, qr) + def delete_transport(self, addr: str): """Delete a transport.""" self._rpc.delete_transport(self.id, addr) diff --git a/deltachat-rpc-client/src/deltachat_rpc_client/pytestplugin.py b/deltachat-rpc-client/src/deltachat_rpc_client/pytestplugin.py index a901cbd5a..6e1af9857 100644 --- a/deltachat-rpc-client/src/deltachat_rpc_client/pytestplugin.py +++ b/deltachat-rpc-client/src/deltachat_rpc_client/pytestplugin.py @@ -92,9 +92,10 @@ class RPCAccountFactory: """Create a new configured account.""" account = self.get_unconfigured_account() qr = self.get_account_qr() - yield account.add_transport_from_qr.future(qr) + yield account.init_transports.future(qr) assert account.is_configured() + assert len(account.list_transports()) == 1 return account def new_configured_bot(self) -> Bot: diff --git a/deltachat-rpc-client/tests/test_multitransport.py b/deltachat-rpc-client/tests/test_multitransport.py index e0e04eb2b..c0b45ff51 100644 --- a/deltachat-rpc-client/tests/test_multitransport.py +++ b/deltachat-rpc-client/tests/test_multitransport.py @@ -26,12 +26,18 @@ def wait_for_imap_message(imap): def test_add_second_address(acf) -> None: account = acf.new_configured_account() - assert len(account.list_transports()) == 1 + assert len(account.list_transports()) == 1 qr = acf.get_account_qr() account.add_transport_from_qr(qr) assert len(account.list_transports()) == 2 + # init_transports() only works on an unconfigured profile: + with pytest.raises(JsonRpcError): + account.init_transports(qr) + with pytest.raises(JsonRpcError): + account.init_transports() + account.add_transport_from_qr(qr) assert len(account.list_transports()) == 3 diff --git a/src/autorelay.rs b/src/autorelay.rs index 5de55e800..8356adfe9 100644 --- a/src/autorelay.rs +++ b/src/autorelay.rs @@ -60,7 +60,7 @@ pub(crate) async fn init_transports_inner( default_relays.shuffle(&mut rng()); let (relays_sender, relays_receiver) = async_channel::unbounded::(); - let relays_from_qr: BTreeSet<_> = addrs_from_qr + let relays_from_qr: BTreeSet = addrs_from_qr .into_iter() .filter_map(|addr| EmailAddress::new(&addr).ok()) .map(|email| email.domain) diff --git a/src/autorelay/autorelay_tests.rs b/src/autorelay/autorelay_tests.rs index 2e796d8ca..bf365f687 100644 --- a/src/autorelay/autorelay_tests.rs +++ b/src/autorelay/autorelay_tests.rs @@ -4,6 +4,71 @@ use super::*; use crate::test_utils::TestContext; use crate::tools::SystemTime; +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_init_transports_basic() -> Result<()> { + let t = &TestContext::new().await; + assert!(t.list_transports().await?.is_empty()); + + let skip_network = true; + init_transports_inner(t, vec![], skip_network).await?; + + // Wait until the tasks adding transports are finished: + let _ = t.background_task_lock.write().await; + + let relays = get_configured_relays(t).await; + assert_eq!(relays.len(), NUM_TRANSPORTS_TARGET); + for relay in &relays { + assert!(DEFAULT_RELAY_CANDIDATES.contains(&relay.as_ref())); + } + + assert_eq!(t.get_config_bool(Config::Autorelay).await?, true); + assert_eq!(t.get_config_bool(Config::AutorelayFinished).await?, true); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_init_transports_use_extra_addrs() -> Result<()> { + let t = &TestContext::new().await; + + let skip_network = true; + init_transports_inner( + t, + vec![ + "alice@example.org".to_string(), + "bob@example.org".to_string(), + "bob@nine.testrun.org".to_string(), + ], + skip_network, + ) + .await?; + + // Wait until the tasks adding transports are finished: + let _ = t.background_task_lock.write().await; + + let relays = get_configured_relays(t).await; + assert_eq!(relays.len(), NUM_TRANSPORTS_TARGET); + assert!(relays.contains(&"example.org".to_string())); + assert!(relays.contains(&"nine.testrun.org".to_string())); + + Ok(()) +} + +async fn get_configured_relays(t: &TestContext) -> Vec { + let transports = t.list_transports().await.unwrap(); + let mut relays: Vec<_> = transports + .iter() + .map(|t| t.addr.split_once('@').unwrap().1) + .collect(); + + // Check that every relay is used only once: + relays.sort(); + relays.dedup(); + assert_eq!(relays.len(), transports.len()); + + relays.into_iter().map(|s| s.to_string()).collect() +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_load_relay_candidates_single() -> Result<()> { let t = &TestContext::new_alice().await;