1use crate::{chat::ChatId, contact::ContactId, context::Context, EventType};
2
3pub(crate) fn emit_chatlist_changed(context: &Context) {
5 context.emit_event(EventType::ChatlistChanged);
6}
7
8pub(crate) fn emit_chatlist_item_changed(context: &Context, chat_id: ChatId) {
10 context.emit_event(EventType::ChatlistItemChanged {
11 chat_id: Some(chat_id),
12 });
13}
14
15fn emit_unknown_chatlist_items_changed(context: &Context) {
21 context.emit_event(EventType::ChatlistItemChanged { chat_id: None });
22}
23
24pub(crate) async fn emit_chatlist_item_changed_for_contact_chat(
27 context: &Context,
28 contact_id: ContactId,
29) {
30 match ChatId::lookup_by_contact(context, contact_id).await {
31 Ok(Some(chat_id)) => self::emit_chatlist_item_changed(context, chat_id),
32 Ok(None) => {}
33 Err(error) => context.emit_event(EventType::Error(format!(
34 "failed to find chat id for contact for chatlist event: {error:?}"
35 ))),
36 }
37}
38
39pub(crate) fn emit_chatlist_items_changed_for_contact(context: &Context, _contact_id: ContactId) {
46 emit_unknown_chatlist_items_changed(context)
49 }
53
54#[cfg(test)]
59mod test_chatlist_events {
60
61 use std::{
62 sync::atomic::{AtomicBool, Ordering},
63 time::Duration,
64 };
65
66 use crate::{
67 chat::{
68 self, create_broadcast_list, create_group_chat, set_muted, ChatId, ChatVisibility,
69 MuteDuration, ProtectionStatus,
70 },
71 config::Config,
72 constants::*,
73 contact::Contact,
74 message::{self, Message, MessageState},
75 reaction,
76 receive_imf::receive_imf,
77 securejoin::{get_securejoin_qr, join_securejoin},
78 test_utils::{TestContext, TestContextManager},
79 EventType,
80 };
81
82 use crate::tools::SystemTime;
83 use anyhow::Result;
84
85 async fn wait_for_chatlist_and_specific_item(context: &TestContext, chat_id: ChatId) {
86 let first_event_is_item = AtomicBool::new(false);
87 context
88 .evtracker
89 .get_matching(|evt| match evt {
90 EventType::ChatlistItemChanged {
91 chat_id: Some(ev_chat_id),
92 } => {
93 if ev_chat_id == &chat_id {
94 first_event_is_item.store(true, Ordering::Relaxed);
95 true
96 } else {
97 false
98 }
99 }
100 EventType::ChatlistChanged => true,
101 _ => false,
102 })
103 .await;
104 if first_event_is_item.load(Ordering::Relaxed) {
105 wait_for_chatlist(context).await;
106 } else {
107 wait_for_chatlist_specific_item(context, chat_id).await;
108 }
109 }
110
111 async fn wait_for_chatlist_specific_item(context: &TestContext, chat_id: ChatId) {
112 context
113 .evtracker
114 .get_matching(|evt| match evt {
115 EventType::ChatlistItemChanged {
116 chat_id: Some(ev_chat_id),
117 } => ev_chat_id == &chat_id,
118 _ => false,
119 })
120 .await;
121 }
122
123 async fn wait_for_chatlist_all_items(context: &TestContext) {
124 context
125 .evtracker
126 .get_matching(|evt| matches!(evt, EventType::ChatlistItemChanged { chat_id: None }))
127 .await;
128 }
129
130 async fn wait_for_chatlist(context: &TestContext) {
131 context
132 .evtracker
133 .get_matching(|evt| matches!(evt, EventType::ChatlistChanged))
134 .await;
135 }
136
137 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
138 async fn test_change_chat_visibility() -> Result<()> {
139 let mut tcm = TestContextManager::new();
140 let alice = tcm.alice().await;
141 let chat_id = create_group_chat(
142 &alice,
143 crate::chat::ProtectionStatus::Unprotected,
144 "my_group",
145 )
146 .await?;
147
148 chat_id
149 .set_visibility(&alice, ChatVisibility::Pinned)
150 .await?;
151 wait_for_chatlist_and_specific_item(&alice, chat_id).await;
152
153 chat_id
154 .set_visibility(&alice, ChatVisibility::Archived)
155 .await?;
156 wait_for_chatlist_and_specific_item(&alice, chat_id).await;
157
158 chat_id
159 .set_visibility(&alice, ChatVisibility::Normal)
160 .await?;
161 wait_for_chatlist_and_specific_item(&alice, chat_id).await;
162
163 Ok(())
164 }
165
166 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
168 async fn test_archived_counter_increases_for_muted_chats() -> Result<()> {
169 let mut tcm = TestContextManager::new();
170 let alice = tcm.alice().await;
171 let bob = tcm.bob().await;
172
173 let chat = alice.create_chat(&bob).await;
174 let sent_msg = alice.send_text(chat.id, "moin").await;
175 bob.recv_msg(&sent_msg).await;
176
177 let bob_chat = bob.create_chat(&alice).await;
178 bob_chat
179 .id
180 .set_visibility(&bob, ChatVisibility::Archived)
181 .await?;
182 set_muted(&bob, bob_chat.id, MuteDuration::Forever).await?;
183
184 bob.evtracker.clear_events();
185
186 let sent_msg = alice.send_text(chat.id, "moin2").await;
187 bob.recv_msg(&sent_msg).await;
188
189 bob.evtracker
190 .get_matching(|evt| match evt {
191 EventType::ChatlistItemChanged {
192 chat_id: Some(chat_id),
193 } => chat_id.is_archived_link(),
194 _ => false,
195 })
196 .await;
197
198 Ok(())
199 }
200
201 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
203 async fn test_archived_counter_update_on_mark_noticed() -> Result<()> {
204 let mut tcm = TestContextManager::new();
205 let alice = tcm.alice().await;
206 let bob = tcm.bob().await;
207 let chat = alice.create_chat(&bob).await;
208 let sent_msg = alice.send_text(chat.id, "moin").await;
209 bob.recv_msg(&sent_msg).await;
210 let bob_chat = bob.create_chat(&alice).await;
211 bob_chat
212 .id
213 .set_visibility(&bob, ChatVisibility::Archived)
214 .await?;
215 set_muted(&bob, bob_chat.id, MuteDuration::Forever).await?;
216 let sent_msg = alice.send_text(chat.id, "moin2").await;
217 bob.recv_msg(&sent_msg).await;
218
219 bob.evtracker.clear_events();
220 chat::marknoticed_chat(&bob, DC_CHAT_ID_ARCHIVED_LINK).await?;
221 wait_for_chatlist_specific_item(&bob, DC_CHAT_ID_ARCHIVED_LINK).await;
222
223 Ok(())
224 }
225
226 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
228 async fn test_contact_name_update() -> Result<()> {
229 let mut tcm = TestContextManager::new();
230 let alice = tcm.alice().await;
231 let bob = tcm.bob().await;
232 let alice_to_bob_chat = alice.create_chat(&bob).await;
233 let sent_msg = alice.send_text(alice_to_bob_chat.id, "hello").await;
234 bob.recv_msg(&sent_msg).await;
235
236 bob.evtracker.clear_events();
237 alice.set_config(Config::Displayname, Some("Alice")).await?;
239 let sent_msg = alice
240 .send_text(alice_to_bob_chat.id, "hello, I set a displayname")
241 .await;
242 bob.recv_msg(&sent_msg).await;
243 let alice_on_bob = bob.add_or_lookup_contact(&alice).await;
244 assert!(alice_on_bob.get_display_name() == "Alice");
245
246 wait_for_chatlist_all_items(&bob).await;
247
248 bob.evtracker.clear_events();
249 alice_on_bob.id.set_name(&bob, "Alice2").await?;
251 assert!(bob.add_or_lookup_contact(&alice).await.get_display_name() == "Alice2");
252
253 wait_for_chatlist_all_items(&bob).await;
254
255 Ok(())
256 }
257
258 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
260 async fn test_contact_changed_avatar() -> Result<()> {
261 let mut tcm = TestContextManager::new();
262 let alice = tcm.alice().await;
263 let bob = tcm.bob().await;
264 let alice_to_bob_chat = alice.create_chat(&bob).await;
265 let sent_msg = alice.send_text(alice_to_bob_chat.id, "hello").await;
266 bob.recv_msg(&sent_msg).await;
267
268 bob.evtracker.clear_events();
269 let file = alice.dir.path().join("avatar.png");
271 let bytes = include_bytes!("../../test-data/image/avatar64x64.png");
272 tokio::fs::write(&file, bytes).await?;
273 alice
274 .set_config(Config::Selfavatar, Some(file.to_str().unwrap()))
275 .await?;
276 let sent_msg = alice
277 .send_text(alice_to_bob_chat.id, "hello, I have a new avatar")
278 .await;
279 bob.recv_msg(&sent_msg).await;
280 let alice_on_bob = bob.add_or_lookup_contact(&alice).await;
281 assert!(alice_on_bob.get_profile_image(&bob).await?.is_some());
282
283 wait_for_chatlist_specific_item(&bob, bob.create_chat(&alice).await.id).await;
284 Ok(())
285 }
286
287 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
289 async fn test_delete_chat() -> Result<()> {
290 let mut tcm = TestContextManager::new();
291 let alice = tcm.alice().await;
292 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
293
294 alice.evtracker.clear_events();
295 chat.delete(&alice).await?;
296 wait_for_chatlist(&alice).await;
297 Ok(())
298 }
299
300 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
302 async fn test_create_group_chat() -> Result<()> {
303 let mut tcm = TestContextManager::new();
304 let alice = tcm.alice().await;
305 alice.evtracker.clear_events();
306 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
307 wait_for_chatlist_and_specific_item(&alice, chat).await;
308 Ok(())
309 }
310
311 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
313 async fn test_create_broadcastlist() -> Result<()> {
314 let mut tcm = TestContextManager::new();
315 let alice = tcm.alice().await;
316 alice.evtracker.clear_events();
317 create_broadcast_list(&alice).await?;
318 wait_for_chatlist(&alice).await;
319 Ok(())
320 }
321
322 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
324 async fn test_mute_chat() -> Result<()> {
325 let mut tcm = TestContextManager::new();
326 let alice = tcm.alice().await;
327 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
328
329 alice.evtracker.clear_events();
330 chat::set_muted(&alice, chat, MuteDuration::Forever).await?;
331 wait_for_chatlist_specific_item(&alice, chat).await;
332
333 alice.evtracker.clear_events();
334 chat::set_muted(&alice, chat, MuteDuration::NotMuted).await?;
335 wait_for_chatlist_specific_item(&alice, chat).await;
336
337 Ok(())
338 }
339
340 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
342 #[ignore = "does not work yet"]
343 async fn test_mute_chat_expired() -> Result<()> {
344 let mut tcm = TestContextManager::new();
345 let alice = tcm.alice().await;
346 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
347
348 let mute_duration = MuteDuration::Until(
349 std::time::SystemTime::now()
350 .checked_add(Duration::from_secs(2))
351 .unwrap(),
352 );
353 chat::set_muted(&alice, chat, mute_duration).await?;
354 alice.evtracker.clear_events();
355 SystemTime::shift(Duration::from_secs(3));
356 wait_for_chatlist_specific_item(&alice, chat).await;
357
358 Ok(())
359 }
360
361 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
363 async fn test_change_chat_name() -> Result<()> {
364 let mut tcm = TestContextManager::new();
365 let alice = tcm.alice().await;
366 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
367
368 alice.evtracker.clear_events();
369 chat::set_chat_name(&alice, chat, "New Name").await?;
370 wait_for_chatlist_specific_item(&alice, chat).await;
371
372 Ok(())
373 }
374
375 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
377 async fn test_change_chat_profile_image() -> Result<()> {
378 let mut tcm = TestContextManager::new();
379 let alice = tcm.alice().await;
380 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
381
382 alice.evtracker.clear_events();
383 let file = alice.dir.path().join("avatar.png");
384 let bytes = include_bytes!("../../test-data/image/avatar64x64.png");
385 tokio::fs::write(&file, bytes).await?;
386 chat::set_chat_profile_image(&alice, chat, file.to_str().unwrap()).await?;
387 wait_for_chatlist_specific_item(&alice, chat).await;
388
389 Ok(())
390 }
391
392 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
394 async fn test_receiving_group_and_group_changes() -> Result<()> {
395 let mut tcm = TestContextManager::new();
396 let alice = tcm.alice().await;
397 let bob = tcm.bob().await;
398 let chat = alice
399 .create_group_with_members(ProtectionStatus::Unprotected, "My Group", &[&bob])
400 .await;
401
402 let sent_msg = alice.send_text(chat, "Hello").await;
403 let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
404 wait_for_chatlist_specific_item(&bob, chat_id_for_bob).await;
405 chat_id_for_bob.accept(&bob).await?;
406
407 bob.evtracker.clear_events();
408 chat::set_chat_name(&alice, chat, "New Name").await?;
409 let sent_msg = alice.send_text(chat, "Hello").await;
410 bob.recv_msg(&sent_msg).await;
411 wait_for_chatlist_specific_item(&bob, chat_id_for_bob).await;
412
413 Ok(())
414 }
415
416 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
418 async fn test_accept_contact_request() -> Result<()> {
419 let mut tcm = TestContextManager::new();
420 let alice = tcm.alice().await;
421 let bob = tcm.bob().await;
422 let chat = alice
423 .create_group_with_members(ProtectionStatus::Unprotected, "My Group", &[&bob])
424 .await;
425 let sent_msg = alice.send_text(chat, "Hello").await;
426 let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
427
428 bob.evtracker.clear_events();
429 chat_id_for_bob.accept(&bob).await?;
430 wait_for_chatlist_specific_item(&bob, chat_id_for_bob).await;
431
432 Ok(())
433 }
434
435 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
437 async fn test_block_contact_request() -> Result<()> {
438 let mut tcm = TestContextManager::new();
439 let alice = tcm.alice().await;
440 let bob = tcm.bob().await;
441 let chat = alice
442 .create_group_with_members(ProtectionStatus::Unprotected, "My Group", &[&bob])
443 .await;
444 let sent_msg = alice.send_text(chat, "Hello").await;
445 let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
446
447 bob.evtracker.clear_events();
448 chat_id_for_bob.block(&bob).await?;
449 wait_for_chatlist(&bob).await;
450
451 Ok(())
452 }
453
454 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
456 async fn test_delete_message() -> Result<()> {
457 let mut tcm = TestContextManager::new();
458 let alice = tcm.alice().await;
459 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
460 let message = chat::send_text_msg(&alice, chat, "Hello World".to_owned()).await?;
461
462 alice.evtracker.clear_events();
463 message::delete_msgs(&alice, &[message]).await?;
464 wait_for_chatlist_specific_item(&alice, chat).await;
465
466 Ok(())
467 }
468
469 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
471 async fn test_msgs_noticed_on_chat() -> Result<()> {
472 let mut tcm = TestContextManager::new();
473 let alice = tcm.alice().await;
474 let bob = tcm.bob().await;
475
476 let chat = alice
477 .create_group_with_members(ProtectionStatus::Unprotected, "My Group", &[&bob])
478 .await;
479 let sent_msg = alice.send_text(chat, "Hello").await;
480 let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
481 chat_id_for_bob.accept(&bob).await?;
482
483 let sent_msg = alice.send_text(chat, "New Message").await;
484 let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
485 assert!(chat_id_for_bob.get_fresh_msg_cnt(&bob).await? >= 1);
486
487 bob.evtracker.clear_events();
488 chat::marknoticed_chat(&bob, chat_id_for_bob).await?;
489 wait_for_chatlist_specific_item(&bob, chat_id_for_bob).await;
490
491 Ok(())
492 }
493
494 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
496 async fn test_unblock_contact() -> Result<()> {
497 let mut tcm = TestContextManager::new();
498 let alice = tcm.alice().await;
499 let contact_id = Contact::create(&alice, "example", "example@example.com").await?;
500 let _ = ChatId::create_for_contact(&alice, contact_id).await;
501
502 alice.evtracker.clear_events();
503 Contact::block(&alice, contact_id).await?;
504 wait_for_chatlist(&alice).await;
505
506 alice.evtracker.clear_events();
507 Contact::unblock(&alice, contact_id).await?;
508 wait_for_chatlist(&alice).await;
509
510 Ok(())
511 }
512
513 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
516 async fn test_update_after_ephemeral_messages() -> Result<()> {
517 let mut tcm = TestContextManager::new();
518 let alice = tcm.alice().await;
519 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
520 chat.set_ephemeral_timer(&alice, crate::ephemeral::Timer::Enabled { duration: 60 })
521 .await?;
522 alice
523 .evtracker
524 .get_matching(|evt| matches!(evt, EventType::ChatEphemeralTimerModified { .. }))
525 .await;
526
527 let _ = chat::send_text_msg(&alice, chat, "Hello".to_owned()).await?;
528 wait_for_chatlist_and_specific_item(&alice, chat).await;
529
530 SystemTime::shift(Duration::from_secs(70));
531 crate::ephemeral::delete_expired_messages(&alice, crate::tools::time()).await?;
532 wait_for_chatlist_and_specific_item(&alice, chat).await;
533
534 Ok(())
535 }
536
537 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
539 async fn test_adhoc_group() -> Result<()> {
540 let alice = TestContext::new_alice().await;
541 let mime = br#"Subject: First thread
542Message-ID: first@example.org
543To: Alice <alice@example.org>, Bob <bob@example.net>
544From: Claire <claire@example.org>
545Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
546
547First thread."#;
548
549 alice.evtracker.clear_events();
550 receive_imf(&alice, mime, false).await?;
551 wait_for_chatlist(&alice).await;
552
553 Ok(())
554 }
555
556 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
558 async fn test_secure_join_group() -> Result<()> {
559 let mut tcm = TestContextManager::new();
560 let alice = tcm.alice().await;
561 let bob = tcm.bob().await;
562
563 let alice_chatid =
564 chat::create_group_chat(&alice.ctx, ProtectionStatus::Protected, "the chat").await?;
565
566 let qr = get_securejoin_qr(&alice.ctx, Some(alice_chatid)).await?;
568
569 bob.evtracker.clear_events();
571 let bob_chatid = join_securejoin(&bob.ctx, &qr).await?;
572 wait_for_chatlist(&bob).await;
573
574 let sent = bob.pop_sent_msg().await;
575
576 alice.evtracker.clear_events();
578 alice.recv_msg_trash(&sent).await;
579
580 let sent = alice.pop_sent_msg().await;
581
582 bob.evtracker.clear_events();
584 bob.recv_msg_trash(&sent).await;
585 wait_for_chatlist_and_specific_item(&bob, bob_chatid).await;
586
587 let sent = bob.pop_sent_msg().await;
588
589 alice.evtracker.clear_events();
591 alice.recv_msg_trash(&sent).await;
592 wait_for_chatlist_and_specific_item(&alice, alice_chatid).await;
593
594 let sent = alice.pop_sent_msg().await;
595
596 bob.evtracker.clear_events();
598 bob.recv_msg(&sent).await;
599 wait_for_chatlist_and_specific_item(&bob, bob_chatid).await;
600
601 Ok(())
602 }
603
604 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
608 async fn test_resend_message() -> Result<()> {
609 let mut tcm = TestContextManager::new();
610 let alice = tcm.alice().await;
611 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
612
613 let msg_id = chat::send_text_msg(&alice, chat, "Hello".to_owned()).await?;
614 let _ = alice.pop_sent_msg().await;
615
616 let message = Message::load_from_db(&alice, msg_id).await?;
617 assert_eq!(message.get_state(), MessageState::OutDelivered);
618
619 alice.evtracker.clear_events();
620 chat::resend_msgs(&alice, &[msg_id]).await?;
621 wait_for_chatlist_specific_item(&alice, chat).await;
622
623 Ok(())
624 }
625
626 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
628 async fn test_reaction() -> Result<()> {
629 let mut tcm = TestContextManager::new();
630 let alice = tcm.alice().await;
631 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
632 let msg_id = chat::send_text_msg(&alice, chat, "Hello".to_owned()).await?;
633 let _ = alice.pop_sent_msg().await;
634
635 alice.evtracker.clear_events();
636 reaction::send_reaction(&alice, msg_id, "👍").await?;
637 let _ = alice.pop_sent_msg().await;
638 wait_for_chatlist_specific_item(&alice, chat).await;
639
640 Ok(())
641 }
642}