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 let addr = alice_on_bob.get_addr();
251 Contact::create(&bob, "Alice2", addr).await?;
252 assert!(bob.add_or_lookup_contact(&alice).await.get_display_name() == "Alice2");
253
254 wait_for_chatlist_all_items(&bob).await;
255
256 Ok(())
257 }
258
259 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
261 async fn test_contact_changed_avatar() -> Result<()> {
262 let mut tcm = TestContextManager::new();
263 let alice = tcm.alice().await;
264 let bob = tcm.bob().await;
265 let alice_to_bob_chat = alice.create_chat(&bob).await;
266 let sent_msg = alice.send_text(alice_to_bob_chat.id, "hello").await;
267 bob.recv_msg(&sent_msg).await;
268
269 bob.evtracker.clear_events();
270 let file = alice.dir.path().join("avatar.png");
272 let bytes = include_bytes!("../../test-data/image/avatar64x64.png");
273 tokio::fs::write(&file, bytes).await?;
274 alice
275 .set_config(Config::Selfavatar, Some(file.to_str().unwrap()))
276 .await?;
277 let sent_msg = alice
278 .send_text(alice_to_bob_chat.id, "hello, I have a new avatar")
279 .await;
280 bob.recv_msg(&sent_msg).await;
281 let alice_on_bob = bob.add_or_lookup_contact(&alice).await;
282 assert!(alice_on_bob.get_profile_image(&bob).await?.is_some());
283
284 wait_for_chatlist_specific_item(&bob, bob.create_chat(&alice).await.id).await;
285 Ok(())
286 }
287
288 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
290 async fn test_delete_chat() -> Result<()> {
291 let mut tcm = TestContextManager::new();
292 let alice = tcm.alice().await;
293 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
294
295 alice.evtracker.clear_events();
296 chat.delete(&alice).await?;
297 wait_for_chatlist(&alice).await;
298 Ok(())
299 }
300
301 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
303 async fn test_create_group_chat() -> Result<()> {
304 let mut tcm = TestContextManager::new();
305 let alice = tcm.alice().await;
306 alice.evtracker.clear_events();
307 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
308 wait_for_chatlist_and_specific_item(&alice, chat).await;
309 Ok(())
310 }
311
312 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
314 async fn test_create_broadcastlist() -> Result<()> {
315 let mut tcm = TestContextManager::new();
316 let alice = tcm.alice().await;
317 alice.evtracker.clear_events();
318 create_broadcast_list(&alice).await?;
319 wait_for_chatlist(&alice).await;
320 Ok(())
321 }
322
323 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
325 async fn test_mute_chat() -> Result<()> {
326 let mut tcm = TestContextManager::new();
327 let alice = tcm.alice().await;
328 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
329
330 alice.evtracker.clear_events();
331 chat::set_muted(&alice, chat, MuteDuration::Forever).await?;
332 wait_for_chatlist_specific_item(&alice, chat).await;
333
334 alice.evtracker.clear_events();
335 chat::set_muted(&alice, chat, MuteDuration::NotMuted).await?;
336 wait_for_chatlist_specific_item(&alice, chat).await;
337
338 Ok(())
339 }
340
341 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
343 #[ignore = "does not work yet"]
344 async fn test_mute_chat_expired() -> Result<()> {
345 let mut tcm = TestContextManager::new();
346 let alice = tcm.alice().await;
347 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
348
349 let mute_duration = MuteDuration::Until(
350 std::time::SystemTime::now()
351 .checked_add(Duration::from_secs(2))
352 .unwrap(),
353 );
354 chat::set_muted(&alice, chat, mute_duration).await?;
355 alice.evtracker.clear_events();
356 SystemTime::shift(Duration::from_secs(3));
357 wait_for_chatlist_specific_item(&alice, chat).await;
358
359 Ok(())
360 }
361
362 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
364 async fn test_change_chat_name() -> Result<()> {
365 let mut tcm = TestContextManager::new();
366 let alice = tcm.alice().await;
367 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
368
369 alice.evtracker.clear_events();
370 chat::set_chat_name(&alice, chat, "New Name").await?;
371 wait_for_chatlist_specific_item(&alice, chat).await;
372
373 Ok(())
374 }
375
376 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
378 async fn test_change_chat_profile_image() -> Result<()> {
379 let mut tcm = TestContextManager::new();
380 let alice = tcm.alice().await;
381 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
382
383 alice.evtracker.clear_events();
384 let file = alice.dir.path().join("avatar.png");
385 let bytes = include_bytes!("../../test-data/image/avatar64x64.png");
386 tokio::fs::write(&file, bytes).await?;
387 chat::set_chat_profile_image(&alice, chat, file.to_str().unwrap()).await?;
388 wait_for_chatlist_specific_item(&alice, chat).await;
389
390 Ok(())
391 }
392
393 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
395 async fn test_receiving_group_and_group_changes() -> Result<()> {
396 let mut tcm = TestContextManager::new();
397 let alice = tcm.alice().await;
398 let bob = tcm.bob().await;
399 let chat = alice
400 .create_group_with_members(ProtectionStatus::Unprotected, "My Group", &[&bob])
401 .await;
402
403 let sent_msg = alice.send_text(chat, "Hello").await;
404 let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
405 wait_for_chatlist_specific_item(&bob, chat_id_for_bob).await;
406 chat_id_for_bob.accept(&bob).await?;
407
408 bob.evtracker.clear_events();
409 chat::set_chat_name(&alice, chat, "New Name").await?;
410 let sent_msg = alice.send_text(chat, "Hello").await;
411 bob.recv_msg(&sent_msg).await;
412 wait_for_chatlist_specific_item(&bob, chat_id_for_bob).await;
413
414 Ok(())
415 }
416
417 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
419 async fn test_accept_contact_request() -> Result<()> {
420 let mut tcm = TestContextManager::new();
421 let alice = tcm.alice().await;
422 let bob = tcm.bob().await;
423 let chat = alice
424 .create_group_with_members(ProtectionStatus::Unprotected, "My Group", &[&bob])
425 .await;
426 let sent_msg = alice.send_text(chat, "Hello").await;
427 let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
428
429 bob.evtracker.clear_events();
430 chat_id_for_bob.accept(&bob).await?;
431 wait_for_chatlist_specific_item(&bob, chat_id_for_bob).await;
432
433 Ok(())
434 }
435
436 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
438 async fn test_block_contact_request() -> Result<()> {
439 let mut tcm = TestContextManager::new();
440 let alice = tcm.alice().await;
441 let bob = tcm.bob().await;
442 let chat = alice
443 .create_group_with_members(ProtectionStatus::Unprotected, "My Group", &[&bob])
444 .await;
445 let sent_msg = alice.send_text(chat, "Hello").await;
446 let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
447
448 bob.evtracker.clear_events();
449 chat_id_for_bob.block(&bob).await?;
450 wait_for_chatlist(&bob).await;
451
452 Ok(())
453 }
454
455 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
457 async fn test_delete_message() -> Result<()> {
458 let mut tcm = TestContextManager::new();
459 let alice = tcm.alice().await;
460 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
461 let message = chat::send_text_msg(&alice, chat, "Hello World".to_owned()).await?;
462
463 alice.evtracker.clear_events();
464 message::delete_msgs(&alice, &[message]).await?;
465 wait_for_chatlist_specific_item(&alice, chat).await;
466
467 Ok(())
468 }
469
470 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
472 async fn test_msgs_noticed_on_chat() -> Result<()> {
473 let mut tcm = TestContextManager::new();
474 let alice = tcm.alice().await;
475 let bob = tcm.bob().await;
476
477 let chat = alice
478 .create_group_with_members(ProtectionStatus::Unprotected, "My Group", &[&bob])
479 .await;
480 let sent_msg = alice.send_text(chat, "Hello").await;
481 let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
482 chat_id_for_bob.accept(&bob).await?;
483
484 let sent_msg = alice.send_text(chat, "New Message").await;
485 let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
486 assert!(chat_id_for_bob.get_fresh_msg_cnt(&bob).await? >= 1);
487
488 bob.evtracker.clear_events();
489 chat::marknoticed_chat(&bob, chat_id_for_bob).await?;
490 wait_for_chatlist_specific_item(&bob, chat_id_for_bob).await;
491
492 Ok(())
493 }
494
495 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
497 async fn test_unblock_contact() -> Result<()> {
498 let mut tcm = TestContextManager::new();
499 let alice = tcm.alice().await;
500 let contact_id = Contact::create(&alice, "example", "example@example.com").await?;
501 let _ = ChatId::create_for_contact(&alice, contact_id).await;
502
503 alice.evtracker.clear_events();
504 Contact::block(&alice, contact_id).await?;
505 wait_for_chatlist(&alice).await;
506
507 alice.evtracker.clear_events();
508 Contact::unblock(&alice, contact_id).await?;
509 wait_for_chatlist(&alice).await;
510
511 Ok(())
512 }
513
514 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
517 async fn test_update_after_ephemeral_messages() -> Result<()> {
518 let mut tcm = TestContextManager::new();
519 let alice = tcm.alice().await;
520 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
521 chat.set_ephemeral_timer(&alice, crate::ephemeral::Timer::Enabled { duration: 60 })
522 .await?;
523 alice
524 .evtracker
525 .get_matching(|evt| matches!(evt, EventType::ChatEphemeralTimerModified { .. }))
526 .await;
527
528 let _ = chat::send_text_msg(&alice, chat, "Hello".to_owned()).await?;
529 wait_for_chatlist_and_specific_item(&alice, chat).await;
530
531 SystemTime::shift(Duration::from_secs(70));
532 crate::ephemeral::delete_expired_messages(&alice, crate::tools::time()).await?;
533 wait_for_chatlist_and_specific_item(&alice, chat).await;
534
535 Ok(())
536 }
537
538 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
540 async fn test_adhoc_group() -> Result<()> {
541 let alice = TestContext::new_alice().await;
542 let mime = br#"Subject: First thread
543Message-ID: first@example.org
544To: Alice <alice@example.org>, Bob <bob@example.net>
545From: Claire <claire@example.org>
546Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
547
548First thread."#;
549
550 alice.evtracker.clear_events();
551 receive_imf(&alice, mime, false).await?;
552 wait_for_chatlist(&alice).await;
553
554 Ok(())
555 }
556
557 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
559 async fn test_secure_join_group() -> Result<()> {
560 let mut tcm = TestContextManager::new();
561 let alice = tcm.alice().await;
562 let bob = tcm.bob().await;
563
564 let alice_chatid =
565 chat::create_group_chat(&alice.ctx, ProtectionStatus::Protected, "the chat").await?;
566
567 let qr = get_securejoin_qr(&alice.ctx, Some(alice_chatid)).await?;
569
570 bob.evtracker.clear_events();
572 let bob_chatid = join_securejoin(&bob.ctx, &qr).await?;
573 wait_for_chatlist(&bob).await;
574
575 let sent = bob.pop_sent_msg().await;
576
577 alice.evtracker.clear_events();
579 alice.recv_msg_trash(&sent).await;
580
581 let sent = alice.pop_sent_msg().await;
582
583 bob.evtracker.clear_events();
585 bob.recv_msg_trash(&sent).await;
586 wait_for_chatlist_and_specific_item(&bob, bob_chatid).await;
587
588 let sent = bob.pop_sent_msg().await;
589
590 alice.evtracker.clear_events();
592 alice.recv_msg_trash(&sent).await;
593 wait_for_chatlist_and_specific_item(&alice, alice_chatid).await;
594
595 let sent = alice.pop_sent_msg().await;
596
597 bob.evtracker.clear_events();
599 bob.recv_msg(&sent).await;
600 wait_for_chatlist_and_specific_item(&bob, bob_chatid).await;
601
602 Ok(())
603 }
604
605 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
609 async fn test_resend_message() -> Result<()> {
610 let mut tcm = TestContextManager::new();
611 let alice = tcm.alice().await;
612 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
613
614 let msg_id = chat::send_text_msg(&alice, chat, "Hello".to_owned()).await?;
615 let _ = alice.pop_sent_msg().await;
616
617 let message = Message::load_from_db(&alice, msg_id).await?;
618 assert_eq!(message.get_state(), MessageState::OutDelivered);
619
620 alice.evtracker.clear_events();
621 chat::resend_msgs(&alice, &[msg_id]).await?;
622 wait_for_chatlist_specific_item(&alice, chat).await;
623
624 Ok(())
625 }
626
627 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
629 async fn test_reaction() -> Result<()> {
630 let mut tcm = TestContextManager::new();
631 let alice = tcm.alice().await;
632 let chat = create_group_chat(&alice, ProtectionStatus::Protected, "My Group").await?;
633 let msg_id = chat::send_text_msg(&alice, chat, "Hello".to_owned()).await?;
634 let _ = alice.pop_sent_msg().await;
635
636 alice.evtracker.clear_events();
637 reaction::send_reaction(&alice, msg_id, "👍").await?;
638 let _ = alice.pop_sent_msg().await;
639 wait_for_chatlist_specific_item(&alice, chat).await;
640
641 Ok(())
642 }
643}