fix: Only emit TransportsModified if transports are really modified

Otherwise it's not possible to write tests reliably because sync messages may be executed multiple
times if they arrive from different transports. This should fix flaky
`test_transport_synchronization`.

Also always emit `TransportsModified` if the primary transport is changed by a sync message, even if
it doesn't contain `SyncData::Transports`.

Also don't decrease `add_timestamp` in `save_transport()` if nothing else changes, this doesn't make
sense.
This commit is contained in:
iequidoo
2026-01-10 23:07:16 -03:00
committed by iequidoo
parent ed300b6f97
commit fe3b1ea16d
3 changed files with 25 additions and 13 deletions

View File

@@ -417,12 +417,12 @@ pub enum EventType {
chat_id: ChatId, chat_id: ChatId,
}, },
/// One or more transports has changed. /// One or more transports has changed or another transport is primary now.
/// ///
/// UI should update the list. /// UI should update the list.
/// ///
/// This event is emitted when transport /// This event is emitted when a transport
/// synchronization messages arrives, /// synchronization message modifies transports,
/// but not when the UI modifies the transport list by itself. /// but not when the UI modifies the transport list by itself.
TransportsModified, TransportsModified,

View File

@@ -832,7 +832,9 @@ pub(crate) async fn receive_imf_inner(
let transport_changed = if transport_exists { let transport_changed = if transport_exists {
transaction.execute( transaction.execute(
"UPDATE config SET value=? WHERE keyname='configured_addr'", "
UPDATE config SET value=? WHERE keyname='configured_addr' AND value!=?1
",
(from_addr,), (from_addr,),
)? > 0 )? > 0
} else { } else {
@@ -848,6 +850,7 @@ pub(crate) async fn receive_imf_inner(
if transport_changed { if transport_changed {
info!(context, "Primary transport changed to {from_addr:?}."); info!(context, "Primary transport changed to {from_addr:?}.");
context.sql.uncache_raw_config("configured_addr").await; context.sql.uncache_raw_config("configured_addr").await;
context.emit_event(EventType::TransportsModified);
} }
} else { } else {
warn!(context, "Sync items are not encrypted."); warn!(context, "Sync items are not encrypted.");

View File

@@ -621,16 +621,17 @@ impl From<ConfiguredLoginParam> for ConfiguredLoginParamJson {
} }
/// Saves transport to the database. /// Saves transport to the database.
/// Returns whether transports are modified.
pub(crate) async fn save_transport( pub(crate) async fn save_transport(
context: &Context, context: &Context,
entered_param: &EnteredLoginParam, entered_param: &EnteredLoginParam,
configured: &ConfiguredLoginParamJson, configured: &ConfiguredLoginParamJson,
add_timestamp: i64, add_timestamp: i64,
) -> Result<()> { ) -> Result<bool> {
let addr = addr_normalize(&configured.addr); let addr = addr_normalize(&configured.addr);
let configured_addr = context.get_config(Config::ConfiguredAddr).await?; let configured_addr = context.get_config(Config::ConfiguredAddr).await?;
context let mut modified = context
.sql .sql
.execute( .execute(
"INSERT INTO transports (addr, entered_param, configured_param, add_timestamp) "INSERT INTO transports (addr, entered_param, configured_param, add_timestamp)
@@ -638,7 +639,10 @@ pub(crate) async fn save_transport(
ON CONFLICT (addr) ON CONFLICT (addr)
DO UPDATE SET entered_param=excluded.entered_param, DO UPDATE SET entered_param=excluded.entered_param,
configured_param=excluded.configured_param, configured_param=excluded.configured_param,
add_timestamp=excluded.add_timestamp", add_timestamp=excluded.add_timestamp
WHERE entered_param != excluded.entered_param
OR configured_param != excluded.configured_param
OR add_timestamp < excluded.add_timestamp",
( (
&addr, &addr,
serde_json::to_string(entered_param)?, serde_json::to_string(entered_param)?,
@@ -646,7 +650,8 @@ pub(crate) async fn save_transport(
add_timestamp, add_timestamp,
), ),
) )
.await?; .await?
> 0;
if configured_addr.is_none() { if configured_addr.is_none() {
// If there is no transport yet, set the new transport as the primary one // If there is no transport yet, set the new transport as the primary one
@@ -654,8 +659,9 @@ pub(crate) async fn save_transport(
.sql .sql
.set_raw_config(Config::ConfiguredAddr.as_ref(), Some(&addr)) .set_raw_config(Config::ConfiguredAddr.as_ref(), Some(&addr))
.await?; .await?;
modified = true;
} }
Ok(()) Ok(modified)
} }
/// Sends a sync message to synchronize transports across devices. /// Sends a sync message to synchronize transports across devices.
@@ -721,24 +727,25 @@ pub(crate) async fn sync_transports(
transports: &[TransportData], transports: &[TransportData],
removed_transports: &[RemovedTransportData], removed_transports: &[RemovedTransportData],
) -> Result<()> { ) -> Result<()> {
let mut modified = false;
for TransportData { for TransportData {
configured, configured,
entered, entered,
timestamp, timestamp,
} in transports } in transports
{ {
save_transport(context, entered, configured, *timestamp).await?; modified |= save_transport(context, entered, configured, *timestamp).await?;
} }
context context
.sql .sql
.transaction(|transaction| { .transaction(|transaction| {
for RemovedTransportData { addr, timestamp } in removed_transports { for RemovedTransportData { addr, timestamp } in removed_transports {
transaction.execute( modified |= transaction.execute(
"DELETE FROM transports "DELETE FROM transports
WHERE addr=? AND add_timestamp<=?", WHERE addr=? AND add_timestamp<=?",
(addr, timestamp), (addr, timestamp),
)?; )? > 0;
transaction.execute( transaction.execute(
"INSERT INTO removed_transports (addr, remove_timestamp) "INSERT INTO removed_transports (addr, remove_timestamp)
VALUES (?, ?) VALUES (?, ?)
@@ -752,7 +759,9 @@ pub(crate) async fn sync_transports(
}) })
.await?; .await?;
if modified {
context.emit_event(EventType::TransportsModified); context.emit_event(EventType::TransportsModified);
}
Ok(()) Ok(())
} }