mirror of
https://github.com/chatmail/core.git
synced 2026-09-22 13:01:21 +03:00
feat!: remove a relay immediately instead of unpublishing it
Removing a relay now takes effect immediately: - the profile stops fetching and advertising it, - secondary devices immediately apply the removal through the transport sync, Upgrading removes unpublished relays and triggers keyupdates. BREAKING CHANGE: set_transport_unpublished() is removed: UIs call delete_transport() when the user removes a relay. BREAKING CHANGE: list_transports_ex() and the TransportListEntry type are removed: use list_transports(). BREAKING CHANGE: delete_transport() no longer refuses to remove the primary transport: it refuses only to remove the last one and re-elects the sending transport as needed. BREAKING CHANGE: TransportsModified is now also emitted on the device modifying the transports, not only on devices applying the synced change. Deprecated: DC_STR_PHASING_OUT
This commit is contained in:
@@ -2623,6 +2623,25 @@ UPDATE msgs SET state=24 WHERE state=18; -- Change OutPreparing to OutFailed.
|
||||
.await?;
|
||||
}
|
||||
|
||||
inc_and_check(&mut migration_version, 165)?;
|
||||
if dbversion < migration_version {
|
||||
// Remove any unpublished relays and cause keyupdates.
|
||||
sql.execute_migration(
|
||||
"INSERT INTO removed_transports (addr, remove_timestamp)
|
||||
SELECT addr, MAX(add_timestamp, unixepoch()) FROM transports
|
||||
WHERE is_published=0
|
||||
AND addr!=(SELECT value FROM config WHERE keyname='configured_addr')
|
||||
ON CONFLICT (addr) DO UPDATE SET
|
||||
remove_timestamp=MAX(excluded.remove_timestamp, remove_timestamp);
|
||||
DELETE FROM transports
|
||||
WHERE is_published=0
|
||||
AND addr!=(SELECT value FROM config WHERE keyname='configured_addr');
|
||||
DELETE FROM config WHERE keyname='keyupdate_baseline' AND changes()>0",
|
||||
migration_version,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let new_version = sql
|
||||
.get_raw_config_int(VERSION_CFG)
|
||||
.await?
|
||||
|
||||
@@ -35,17 +35,8 @@ async fn test_keyupdate_baseline_migration() -> Result<()> {
|
||||
let configured = STOP_MIGRATIONS_AT
|
||||
.scope(163, async move { TestContext::new_alice().await })
|
||||
.await;
|
||||
// An address sorting before the primary pins the seed's ORDER BY,
|
||||
// an unpublished transport pins its filter.
|
||||
// An address sorting before the existing one pins the seed's ORDER BY.
|
||||
add_pseudo_transport(&configured, "aa@example.org").await?;
|
||||
add_pseudo_transport(&configured, "unpublished@example.org").await?;
|
||||
configured
|
||||
.sql
|
||||
.execute(
|
||||
"UPDATE transports SET is_published=0 WHERE addr='unpublished@example.org'",
|
||||
(),
|
||||
)
|
||||
.await?;
|
||||
configured.sql.run_migrations(&configured).await?;
|
||||
let relays = configured.get_config(Config::KeyupdateBaseline).await?;
|
||||
assert_eq!(relays.as_deref(), Some("aa@example.org alice@example.org"));
|
||||
@@ -53,6 +44,46 @@ async fn test_keyupdate_baseline_migration() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests that upgrading removes unpublished transports.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_unpublished_transport_migration_165() -> Result<()> {
|
||||
let t = STOP_MIGRATIONS_AT
|
||||
.scope(163, async move { TestContext::new_alice().await })
|
||||
.await;
|
||||
let skewed = tools::time() + 3600;
|
||||
t.sql
|
||||
.execute(
|
||||
"INSERT INTO transports (addr, entered_param, configured_param, is_published, add_timestamp)
|
||||
VALUES ('unpublished@example.org', '', '', 0, ?)",
|
||||
(skewed,),
|
||||
)
|
||||
.await?;
|
||||
|
||||
STOP_MIGRATIONS_AT
|
||||
.scope(165, async { t.sql.run_migrations(&t).await })
|
||||
.await?;
|
||||
|
||||
assert!(
|
||||
!t.sql
|
||||
.exists(
|
||||
"SELECT COUNT(*) FROM transports WHERE addr='unpublished@example.org'",
|
||||
(),
|
||||
)
|
||||
.await?
|
||||
);
|
||||
assert_eq!(
|
||||
t.sql
|
||||
.query_get_value(
|
||||
"SELECT remove_timestamp FROM removed_transports WHERE addr='unpublished@example.org'",
|
||||
(),
|
||||
)
|
||||
.await?,
|
||||
Some(skewed)
|
||||
);
|
||||
assert_eq!(t.get_config(Config::KeyupdateBaseline).await?, None);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_key_contacts_migration_autocrypt() -> Result<()> {
|
||||
let t = STOP_MIGRATIONS_AT
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use super::*;
|
||||
use crate::message::Message;
|
||||
use crate::tools::SystemTime;
|
||||
use crate::{EventType, test_utils::TestContext};
|
||||
|
||||
#[test]
|
||||
@@ -370,114 +369,3 @@ async fn test_incremental_vacuum() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_remove_unused_hidden_transports() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
let now = time();
|
||||
|
||||
let transport_id = t
|
||||
.sql
|
||||
.transaction(|t| {
|
||||
// published transport
|
||||
t.execute(
|
||||
"INSERT INTO transports
|
||||
(addr,
|
||||
entered_param,
|
||||
configured_param,
|
||||
add_timestamp)
|
||||
VALUES (?, ?, ?, ?)",
|
||||
("1", "", "", now),
|
||||
)?;
|
||||
|
||||
// not published transport
|
||||
let transport_id = t.query_row::<u32, _, _>(
|
||||
"INSERT INTO transports
|
||||
(addr,
|
||||
entered_param,
|
||||
configured_param,
|
||||
is_published,
|
||||
add_timestamp)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
RETURNING id",
|
||||
("2", "", "", 0, now),
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
|
||||
// this transport has add_timestamp in the future,
|
||||
// and should not be removed at all, since we only shift time
|
||||
// by 90 days and one second.
|
||||
t.execute(
|
||||
"INSERT INTO transports
|
||||
(addr,
|
||||
entered_param,
|
||||
configured_param,
|
||||
is_published,
|
||||
add_timestamp)
|
||||
VALUES (?, ?, ?, ?, ?)",
|
||||
("3", "", "", 0, now + 60),
|
||||
)?;
|
||||
Ok(transport_id)
|
||||
})
|
||||
.await?;
|
||||
|
||||
async fn assert_transport_removed(
|
||||
context: &Context,
|
||||
hidden_transport_id: u32,
|
||||
should_be_removed: bool,
|
||||
) -> Result<()> {
|
||||
let transports_count: u32 = context
|
||||
.sql
|
||||
.query_get_value("SELECT count(*) FROM transports", ())
|
||||
.await?
|
||||
.unwrap();
|
||||
assert_eq!(transports_count, if should_be_removed { 2 } else { 3 });
|
||||
let hidden_transport_present: bool = context
|
||||
.sql
|
||||
.query_get_value(
|
||||
"SELECT count(*) FROM transports WHERE id=?",
|
||||
(hidden_transport_id,),
|
||||
)
|
||||
.await?
|
||||
.unwrap();
|
||||
assert_eq!(hidden_transport_present, !should_be_removed);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
t.sql
|
||||
.query_get_value::<i64>(
|
||||
"SELECT last_rcvd_timestamp FROM transports WHERE id=?",
|
||||
(transport_id,)
|
||||
)
|
||||
.await?
|
||||
.unwrap(),
|
||||
0
|
||||
);
|
||||
update_transport_last_rcvd_timestamp(&t, transport_id).await?;
|
||||
// last_rcvd_timestamp should update
|
||||
assert!(
|
||||
t.sql
|
||||
.query_get_value::<i64>(
|
||||
"SELECT last_rcvd_timestamp FROM transports WHERE id=?",
|
||||
(transport_id,)
|
||||
)
|
||||
.await?
|
||||
.unwrap()
|
||||
>= now
|
||||
);
|
||||
|
||||
assert_transport_removed(&t, transport_id, false).await?;
|
||||
remove_unused_hidden_transports(&t).await?;
|
||||
// it was recently used, so nothing is deleted
|
||||
assert_transport_removed(&t, transport_id, false).await?;
|
||||
|
||||
SystemTime::shift(Duration::from_secs(
|
||||
(UNPUBLISHED_TRANSPORT_KEEP_TIME + 1).try_into()?,
|
||||
));
|
||||
|
||||
remove_unused_hidden_transports(&t).await?;
|
||||
assert_transport_removed(&t, transport_id, true).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user