@@ -2,6 +2,7 @@ use super::*;
2
2
use crate :: chatlist:: get_archived_cnt;
3
3
use crate :: constants:: { DC_GCL_ARCHIVED_ONLY , DC_GCL_NO_SPECIALS } ;
4
4
use crate :: headerdef:: HeaderDef ;
5
+ use crate :: imex:: { has_backup, imex, ImexMode } ;
5
6
use crate :: message:: { delete_msgs, MessengerMessage } ;
6
7
use crate :: receive_imf:: receive_imf;
7
8
use crate :: test_utils:: { sync, TestContext , TestContextManager , TimeShiftFalsePositiveNote } ;
@@ -3365,3 +3366,160 @@ async fn unpromoted_group_no_tombstones() -> Result<()> {
3365
3366
3366
3367
Ok ( ( ) )
3367
3368
}
3369
+
3370
+ /// Test that past members expire after 60 days.
3371
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
3372
+ async fn test_expire_past_members_after_60_days ( ) -> Result < ( ) > {
3373
+ let mut tcm = TestContextManager :: new ( ) ;
3374
+
3375
+ let alice = & tcm. alice ( ) . await ;
3376
+ let fiona_addr = "fiona@example.net" ;
3377
+ let alice_fiona_contact_id = Contact :: create ( alice, "Fiona" , fiona_addr) . await ?;
3378
+
3379
+ let alice_chat_id =
3380
+ create_group_chat ( alice, ProtectionStatus :: Unprotected , "Group chat" ) . await ?;
3381
+ add_contact_to_chat ( alice, alice_chat_id, alice_fiona_contact_id) . await ?;
3382
+ alice
3383
+ . send_text ( alice_chat_id, "Hi! I created a group." )
3384
+ . await ;
3385
+ remove_contact_from_chat ( alice, alice_chat_id, alice_fiona_contact_id) . await ?;
3386
+ assert_eq ! ( get_past_chat_contacts( alice, alice_chat_id) . await ?. len( ) , 1 ) ;
3387
+
3388
+ SystemTime :: shift ( Duration :: from_secs ( 60 * 24 * 60 * 60 + 1 ) ) ;
3389
+ assert_eq ! ( get_past_chat_contacts( alice, alice_chat_id) . await ?. len( ) , 0 ) ;
3390
+
3391
+ let bob = & tcm. bob ( ) . await ;
3392
+ let bob_addr = bob. get_config ( Config :: Addr ) . await ?. unwrap ( ) ;
3393
+ let alice_bob_contact_id = Contact :: create ( alice, "Bob" , & bob_addr) . await ?;
3394
+ add_contact_to_chat ( alice, alice_chat_id, alice_bob_contact_id) . await ?;
3395
+
3396
+ let add_message = alice. pop_sent_msg ( ) . await ;
3397
+ assert_eq ! ( add_message. payload. contains( fiona_addr) , false ) ;
3398
+ let bob_add_message = bob. recv_msg ( & add_message) . await ;
3399
+ let bob_chat_id = bob_add_message. chat_id ;
3400
+ assert_eq ! ( get_chat_contacts( bob, bob_chat_id) . await ?. len( ) , 2 ) ;
3401
+ assert_eq ! ( get_past_chat_contacts( bob, bob_chat_id) . await ?. len( ) , 0 ) ;
3402
+
3403
+ Ok ( ( ) )
3404
+ }
3405
+
3406
+ /// Test the case when Alice restores a backup older than 60 days
3407
+ /// with outdated member list.
3408
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
3409
+ async fn test_restore_backup_after_60_days ( ) -> Result < ( ) > {
3410
+ let backup_dir = tempfile:: tempdir ( ) ?;
3411
+
3412
+ let mut tcm = TestContextManager :: new ( ) ;
3413
+
3414
+ let alice = & tcm. alice ( ) . await ;
3415
+ let bob = & tcm. bob ( ) . await ;
3416
+ let fiona = & tcm. fiona ( ) . await ;
3417
+
3418
+ let bob_addr = bob. get_config ( Config :: Addr ) . await ?. unwrap ( ) ;
3419
+ let alice_bob_contact_id = Contact :: create ( alice, "Bob" , & bob_addr) . await ?;
3420
+
3421
+ let charlie_addr = "charlie@example.com" ;
3422
+ let alice_charlie_contact_id = Contact :: create ( alice, "Charlie" , charlie_addr) . await ?;
3423
+
3424
+ let alice_chat_id =
3425
+ create_group_chat ( alice, ProtectionStatus :: Unprotected , "Group chat" ) . await ?;
3426
+ add_contact_to_chat ( alice, alice_chat_id, alice_bob_contact_id) . await ?;
3427
+ add_contact_to_chat ( alice, alice_chat_id, alice_charlie_contact_id) . await ?;
3428
+
3429
+ let alice_sent_promote = alice
3430
+ . send_text ( alice_chat_id, "Hi! I created a group." )
3431
+ . await ;
3432
+ let bob_rcvd_promote = bob. recv_msg ( & alice_sent_promote) . await ;
3433
+ let bob_chat_id = bob_rcvd_promote. chat_id ;
3434
+ bob_chat_id. accept ( bob) . await ?;
3435
+
3436
+ // Alice exports a backup.
3437
+ imex ( alice, ImexMode :: ExportBackup , backup_dir. path ( ) , None ) . await ?;
3438
+
3439
+ remove_contact_from_chat ( alice, alice_chat_id, alice_charlie_contact_id) . await ?;
3440
+ assert_eq ! ( get_chat_contacts( alice, alice_chat_id) . await ?. len( ) , 2 ) ;
3441
+ assert_eq ! ( get_past_chat_contacts( alice, alice_chat_id) . await ?. len( ) , 1 ) ;
3442
+
3443
+ let remove_message = alice. pop_sent_msg ( ) . await ;
3444
+ assert_eq ! ( remove_message. payload. contains( charlie_addr) , true ) ;
3445
+ bob. recv_msg ( & remove_message) . await ;
3446
+
3447
+ // 60 days pass.
3448
+ SystemTime :: shift ( Duration :: from_secs ( 60 * 24 * 60 * 60 + 1 ) ) ;
3449
+
3450
+ assert_eq ! ( get_past_chat_contacts( alice, alice_chat_id) . await ?. len( ) , 0 ) ;
3451
+
3452
+ // Bob adds Fiona to the chat.
3453
+ let fiona_addr = fiona. get_config ( Config :: Addr ) . await ?. unwrap ( ) ;
3454
+ let bob_fiona_contact_id = Contact :: create ( bob, "Fiona" , & fiona_addr) . await ?;
3455
+ add_contact_to_chat ( bob, bob_chat_id, bob_fiona_contact_id) . await ?;
3456
+
3457
+ let add_message = bob. pop_sent_msg ( ) . await ;
3458
+ alice. recv_msg ( & add_message) . await ;
3459
+ let fiona_add_message = fiona. recv_msg ( & add_message) . await ;
3460
+ let fiona_chat_id = fiona_add_message. chat_id ;
3461
+ fiona_chat_id. accept ( fiona) . await ?;
3462
+
3463
+ // Fiona does not learn about Charlie,
3464
+ // even from `Chat-Group-Past-Members`, because tombstone has expired.
3465
+ assert_eq ! ( get_chat_contacts( fiona, fiona_chat_id) . await ?. len( ) , 3 ) ;
3466
+ assert_eq ! ( get_past_chat_contacts( fiona, fiona_chat_id) . await ?. len( ) , 0 ) ;
3467
+
3468
+ // Fiona sends a message
3469
+ // so chat is not stale for Bob again.
3470
+ // Alice also receives the message,
3471
+ // but will import a backup immediately afterwards,
3472
+ // so it does not matter.
3473
+ let fiona_sent_message = fiona. send_text ( fiona_chat_id, "Hi!" ) . await ;
3474
+ alice. recv_msg ( & fiona_sent_message) . await ;
3475
+ bob. recv_msg ( & fiona_sent_message) . await ;
3476
+
3477
+ tcm. section ( "Alice imports old backup" ) ;
3478
+ let alice = & tcm. unconfigured ( ) . await ;
3479
+ let backup = has_backup ( alice, backup_dir. path ( ) ) . await ?;
3480
+ imex ( alice, ImexMode :: ImportBackup , backup. as_ref ( ) , None ) . await ?;
3481
+
3482
+ // Alice thinks Charlie is in the chat, but does not know about Fiona.
3483
+ assert_eq ! ( get_chat_contacts( alice, alice_chat_id) . await ?. len( ) , 3 ) ;
3484
+ assert_eq ! ( get_past_chat_contacts( alice, alice_chat_id) . await ?. len( ) , 0 ) ;
3485
+
3486
+ assert_eq ! ( get_chat_contacts( bob, bob_chat_id) . await ?. len( ) , 3 ) ;
3487
+ assert_eq ! ( get_past_chat_contacts( bob, bob_chat_id) . await ?. len( ) , 0 ) ;
3488
+
3489
+ assert_eq ! ( get_chat_contacts( fiona, fiona_chat_id) . await ?. len( ) , 3 ) ;
3490
+ assert_eq ! ( get_past_chat_contacts( fiona, fiona_chat_id) . await ?. len( ) , 0 ) ;
3491
+
3492
+ // Bob sends a text message to the chat, without a tombstone for Charlie.
3493
+ // Alice learns about Fiona.
3494
+ let bob_sent_text = bob. send_text ( bob_chat_id, "Message." ) . await ;
3495
+
3496
+ tcm. section ( "Alice sends a message to stale chat" ) ;
3497
+ let alice_sent_text = alice
3498
+ . send_text ( alice_chat_id, "Hi! I just restored a backup." )
3499
+ . await ;
3500
+
3501
+ tcm. section ( "Alice sent a message to stale chat" ) ;
3502
+ alice. recv_msg ( & bob_sent_text) . await ;
3503
+ fiona. recv_msg ( & bob_sent_text) . await ;
3504
+
3505
+ bob. recv_msg ( & alice_sent_text) . await ;
3506
+ fiona. recv_msg ( & alice_sent_text) . await ;
3507
+
3508
+ // Alice should have learned about Charlie not being part of the group
3509
+ // by receiving Bob's message.
3510
+ assert_eq ! ( get_chat_contacts( alice, alice_chat_id) . await ?. len( ) , 3 ) ;
3511
+ assert ! ( !is_contact_in_chat( alice, alice_chat_id, alice_charlie_contact_id) . await ?) ;
3512
+ assert_eq ! ( get_past_chat_contacts( alice, alice_chat_id) . await ?. len( ) , 0 ) ;
3513
+
3514
+ // This should not add or restore Charlie for Bob and Fiona,
3515
+ // Charlie is not part of the chat.
3516
+ assert_eq ! ( get_chat_contacts( bob, bob_chat_id) . await ?. len( ) , 3 ) ;
3517
+ assert_eq ! ( get_past_chat_contacts( bob, bob_chat_id) . await ?. len( ) , 0 ) ;
3518
+ let bob_charlie_contact_id = Contact :: create ( bob, "Charlie" , charlie_addr) . await ?;
3519
+ assert ! ( !is_contact_in_chat( bob, bob_chat_id, bob_charlie_contact_id) . await ?) ;
3520
+
3521
+ assert_eq ! ( get_chat_contacts( fiona, fiona_chat_id) . await ?. len( ) , 3 ) ;
3522
+ assert_eq ! ( get_past_chat_contacts( fiona, fiona_chat_id) . await ?. len( ) , 0 ) ;
3523
+
3524
+ Ok ( ( ) )
3525
+ }
0 commit comments