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