From 4b2dfefc69540f9e3180843709eded29bfc6dbe9 Mon Sep 17 00:00:00 2001 From: holger krekel Date: Mon, 24 Aug 2026 16:37:43 +0200 Subject: [PATCH] fix: reliably complete configuration with progress=1000 or progress=0 this is meant to help configuration event consumers (Python, UIs) to not hang waiting for configuration outcomes. One test case is added that fails on main. --- src/configure.rs | 56 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/src/configure.rs b/src/configure.rs index faca48b837..a9586b6d36 100644 --- a/src/configure.rs +++ b/src/configure.rs @@ -121,6 +121,23 @@ impl Context { } pub(crate) async fn add_transport_inner(&self, param: &mut EnteredLoginParam) -> Result<()> { + match self.add_transport_unreported(param).await { + Ok(()) => { + progress!(self, 1000); + Ok(()) + } + Err(err) => { + // We are using Anyhow's .context() and to show the + // inner error, too, we need the {:#}: + let error_msg = stock_str::configuration_failed(self, &format!("{err:#}")); + progress!(self, 0, Some(error_msg.clone())); + bail!(error_msg); + } + } + } + + /// Adds a transport without reporting the outcome. + async fn add_transport_unreported(&self, param: &mut EnteredLoginParam) -> Result<()> { ensure!( !self.scheduler.is_running().await, "cannot configure, already running" @@ -138,19 +155,9 @@ impl Context { .await; self.free_ongoing().await; + res?; - if let Err(err) = res.as_ref() { - // We are using Anyhow's .context() and to show the - // inner error, too, we need the {:#}: - let error_msg = stock_str::configuration_failed(self, &format!("{err:#}")); - progress!(self, 0, Some(error_msg.clone())); - bail!(error_msg); - } else { - param.save_legacy(self).await?; - progress!(self, 1000); - } - - res + param.save_legacy(self).await } /// Adds a new email account as a transport @@ -745,6 +752,7 @@ mod tests { use crate::tools::SystemTime; use super::*; + use crate::automatic_relay_management::login_param_from_host; use crate::config::Config; use crate::login_param::EnteredImapLoginParam; use crate::sql::update_transport_last_rcvd_timestamp; @@ -763,6 +771,30 @@ mod tests { t.assert_warns_or_errors(&["DNS resolution"]).await; } + /// Tests that a configuration failing + /// before the first login attempt is still reported as a failure. + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_early_configure_failure_is_reported() -> Result<()> { + let t = TestContext::new().await; + let mut param = login_param_from_host("example.org"); + + // An ongoing process, e.g. a backup import, + // makes configuration fail without ever contacting a relay. + let _ongoing = t.alloc_ongoing().await?; + assert!(t.add_or_update_transport(&mut param).await.is_err()); + + let event = t + .evtracker + .get_matching(|evt| matches!(evt, EventType::ConfigureProgress { .. })) + .await; + assert!(matches!( + event, + EventType::ConfigureProgress { progress: 0, .. } + )); + + Ok(()) + } + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_get_configured_param() -> Result<()> { let t = &TestContext::new().await;