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 } => {
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(&alice, "my_group").await?;
142
143 chat_id
144 .set_visibility(&alice, ChatVisibility::Pinned)
145 .await?;
146 wait_for_chatlist_and_specific_item(&alice, chat_id).await;
147
148 chat_id
149 .set_visibility(&alice, ChatVisibility::Archived)
150 .await?;
151 wait_for_chatlist_and_specific_item(&alice, chat_id).await;
152
153 chat_id
154 .set_visibility(&alice, ChatVisibility::Normal)
155 .await?;
156 wait_for_chatlist_and_specific_item(&alice, chat_id).await;
157
158 Ok(())
159 }
160
161 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
163 async fn test_archived_counter_increases_for_muted_chats() -> Result<()> {
164 let mut tcm = TestContextManager::new();
165 let alice = tcm.alice().await;
166 let bob = tcm.bob().await;
167
168 let chat = alice.create_chat(&bob).await;
169 let sent_msg = alice.send_text(chat.id, "moin").await;
170 bob.recv_msg(&sent_msg).await;
171
172 let bob_chat = bob.create_chat(&alice).await;
173 bob_chat
174 .id
175 .set_visibility(&bob, ChatVisibility::Archived)
176 .await?;
177 set_muted(&bob, bob_chat.id, MuteDuration::Forever).await?;
178
179 bob.evtracker.clear_events();
180
181 let sent_msg = alice.send_text(chat.id, "moin2").await;
182 bob.recv_msg(&sent_msg).await;
183
184 bob.evtracker
185 .get_matching(|evt| match evt {
186 EventType::ChatlistItemChanged {
187 chat_id: Some(chat_id),
188 } => chat_id.is_archived_link(),
189 _ => false,
190 })
191 .await;
192
193 Ok(())
194 }
195
196 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
198 async fn test_archived_counter_update_on_mark_noticed() -> Result<()> {
199 let mut tcm = TestContextManager::new();
200 let alice = tcm.alice().await;
201 let bob = tcm.bob().await;
202 let chat = alice.create_chat(&bob).await;
203 let sent_msg = alice.send_text(chat.id, "moin").await;
204 bob.recv_msg(&sent_msg).await;
205 let bob_chat = bob.create_chat(&alice).await;
206 bob_chat
207 .id
208 .set_visibility(&bob, ChatVisibility::Archived)
209 .await?;
210 set_muted(&bob, bob_chat.id, MuteDuration::Forever).await?;
211 let sent_msg = alice.send_text(chat.id, "moin2").await;
212 bob.recv_msg(&sent_msg).await;
213
214 bob.evtracker.clear_events();
215 chat::marknoticed_chat(&bob, DC_CHAT_ID_ARCHIVED_LINK).await?;
216 wait_for_chatlist_specific_item(&bob, DC_CHAT_ID_ARCHIVED_LINK).await;
217
218 Ok(())
219 }
220
221 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
223 async fn test_contact_name_update() -> Result<()> {
224 let mut tcm = TestContextManager::new();
225 let alice = tcm.alice().await;
226 let bob = tcm.bob().await;
227 let alice_to_bob_chat = alice.create_chat(&bob).await;
228 let sent_msg = alice.send_text(alice_to_bob_chat.id, "hello").await;
229 bob.recv_msg(&sent_msg).await;
230
231 bob.evtracker.clear_events();
232 alice.set_config(Config::Displayname, Some("Alice")).await?;
234 let sent_msg = alice
235 .send_text(alice_to_bob_chat.id, "hello, I set a displayname")
236 .await;
237 bob.recv_msg(&sent_msg).await;
238 let alice_on_bob = bob.add_or_lookup_contact(&alice).await;
239 assert!(alice_on_bob.get_display_name() == "Alice");
240
241 wait_for_chatlist_all_items(&bob).await;
242
243 bob.evtracker.clear_events();
244 alice_on_bob.id.set_name(&bob, "Alice2").await?;
246 assert!(bob.add_or_lookup_contact(&alice).await.get_display_name() == "Alice2");
247
248 wait_for_chatlist_all_items(&bob).await;
249
250 Ok(())
251 }
252
253 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
255 async fn test_contact_changed_avatar() -> Result<()> {
256 let mut tcm = TestContextManager::new();
257 let alice = tcm.alice().await;
258 let bob = tcm.bob().await;
259 let alice_to_bob_chat = alice.create_chat(&bob).await;
260 let sent_msg = alice.send_text(alice_to_bob_chat.id, "hello").await;
261 bob.recv_msg(&sent_msg).await;
262
263 bob.evtracker.clear_events();
264 let file = alice.dir.path().join("avatar.png");
266 let bytes = include_bytes!("../../test-data/image/avatar64x64.png");
267 tokio::fs::write(&file, bytes).await?;
268 alice
269 .set_config(Config::Selfavatar, Some(file.to_str().unwrap()))
270 .await?;
271 let sent_msg = alice
272 .send_text(alice_to_bob_chat.id, "hello, I have a new avatar")
273 .await;
274 bob.recv_msg(&sent_msg).await;
275 let alice_on_bob = bob.add_or_lookup_contact(&alice).await;
276 assert!(alice_on_bob.get_profile_image(&bob).await?.is_some());
277
278 wait_for_chatlist_specific_item(&bob, bob.create_chat(&alice).await.id).await;
279 Ok(())
280 }
281
282 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
284 async fn test_delete_chat() -> Result<()> {
285 let mut tcm = TestContextManager::new();
286 let alice = tcm.alice().await;
287 let chat = create_group(&alice, "My Group").await?;
288
289 alice.evtracker.clear_events();
290 chat.delete(&alice).await?;
291 wait_for_chatlist(&alice).await;
292 Ok(())
293 }
294
295 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
297 async fn test_create_group() -> Result<()> {
298 let mut tcm = TestContextManager::new();
299 let alice = tcm.alice().await;
300 alice.evtracker.clear_events();
301 let chat = create_group(&alice, "My Group").await?;
302 wait_for_chatlist_and_specific_item(&alice, chat).await;
303 Ok(())
304 }
305
306 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
308 async fn test_create_broadcast() -> Result<()> {
309 let mut tcm = TestContextManager::new();
310 let alice = tcm.alice().await;
311 alice.evtracker.clear_events();
312 create_broadcast(&alice, "Channel".to_string()).await?;
313 wait_for_chatlist(&alice).await;
314 Ok(())
315 }
316
317 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
319 async fn test_mute_chat() -> Result<()> {
320 let mut tcm = TestContextManager::new();
321 let alice = tcm.alice().await;
322 let chat = create_group(&alice, "My Group").await?;
323
324 alice.evtracker.clear_events();
325 chat::set_muted(&alice, chat, MuteDuration::Forever).await?;
326 wait_for_chatlist_specific_item(&alice, chat).await;
327
328 alice.evtracker.clear_events();
329 chat::set_muted(&alice, chat, MuteDuration::NotMuted).await?;
330 wait_for_chatlist_specific_item(&alice, chat).await;
331
332 Ok(())
333 }
334
335 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
337 #[ignore = "does not work yet"]
338 async fn test_mute_chat_expired() -> Result<()> {
339 let mut tcm = TestContextManager::new();
340 let alice = tcm.alice().await;
341 let chat = create_group(&alice, "My Group").await?;
342
343 let mute_duration = MuteDuration::Until(
344 std::time::SystemTime::now()
345 .checked_add(Duration::from_secs(2))
346 .unwrap(),
347 );
348 chat::set_muted(&alice, chat, mute_duration).await?;
349 alice.evtracker.clear_events();
350 SystemTime::shift(Duration::from_secs(3));
351 wait_for_chatlist_specific_item(&alice, chat).await;
352
353 Ok(())
354 }
355
356 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
358 async fn test_change_chat_name() -> Result<()> {
359 let mut tcm = TestContextManager::new();
360 let alice = tcm.alice().await;
361 let chat = create_group(&alice, "My Group").await?;
362
363 alice.evtracker.clear_events();
364 chat::set_chat_name(&alice, chat, "New Name").await?;
365 wait_for_chatlist_specific_item(&alice, chat).await;
366
367 Ok(())
368 }
369
370 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
372 async fn test_change_chat_profile_image() -> Result<()> {
373 let mut tcm = TestContextManager::new();
374 let alice = tcm.alice().await;
375 let chat = create_group(&alice, "My Group").await?;
376
377 alice.evtracker.clear_events();
378 let file = alice.dir.path().join("avatar.png");
379 let bytes = include_bytes!("../../test-data/image/avatar64x64.png");
380 tokio::fs::write(&file, bytes).await?;
381 chat::set_chat_profile_image(&alice, chat, file.to_str().unwrap()).await?;
382 wait_for_chatlist_specific_item(&alice, chat).await;
383
384 Ok(())
385 }
386
387 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
389 async fn test_receiving_group_and_group_changes() -> Result<()> {
390 let mut tcm = TestContextManager::new();
391 let alice = tcm.alice().await;
392 let bob = tcm.bob().await;
393 let chat = alice.create_group_with_members("My Group", &[&bob]).await;
394
395 let sent_msg = alice.send_text(chat, "Hello").await;
396 let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
397 wait_for_chatlist_specific_item(&bob, chat_id_for_bob).await;
398 chat_id_for_bob.accept(&bob).await?;
399
400 bob.evtracker.clear_events();
401 chat::set_chat_name(&alice, chat, "New Name").await?;
402 let sent_msg = alice.send_text(chat, "Hello").await;
403 bob.recv_msg(&sent_msg).await;
404 wait_for_chatlist_specific_item(&bob, chat_id_for_bob).await;
405
406 Ok(())
407 }
408
409 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
411 async fn test_accept_contact_request() -> Result<()> {
412 let mut tcm = TestContextManager::new();
413 let alice = tcm.alice().await;
414 let bob = tcm.bob().await;
415 let chat = alice.create_group_with_members("My Group", &[&bob]).await;
416 let sent_msg = alice.send_text(chat, "Hello").await;
417 let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
418
419 bob.evtracker.clear_events();
420 chat_id_for_bob.accept(&bob).await?;
421 wait_for_chatlist_specific_item(&bob, chat_id_for_bob).await;
422
423 Ok(())
424 }
425
426 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
428 async fn test_block_contact_request() -> Result<()> {
429 let mut tcm = TestContextManager::new();
430 let alice = tcm.alice().await;
431 let bob = tcm.bob().await;
432 let chat = alice.create_group_with_members("My Group", &[&bob]).await;
433 let sent_msg = alice.send_text(chat, "Hello").await;
434 let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
435
436 bob.evtracker.clear_events();
437 chat_id_for_bob.block(&bob).await?;
438 wait_for_chatlist(&bob).await;
439
440 Ok(())
441 }
442
443 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
445 async fn test_delete_message() -> Result<()> {
446 let mut tcm = TestContextManager::new();
447 let alice = tcm.alice().await;
448 let chat = create_group(&alice, "My Group").await?;
449 let message = chat::send_text_msg(&alice, chat, "Hello World".to_owned()).await?;
450
451 alice.evtracker.clear_events();
452 message::delete_msgs(&alice, &[message]).await?;
453 wait_for_chatlist_specific_item(&alice, chat).await;
454
455 Ok(())
456 }
457
458 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
460 async fn test_msgs_noticed_on_chat() -> Result<()> {
461 let mut tcm = TestContextManager::new();
462 let alice = tcm.alice().await;
463 let bob = tcm.bob().await;
464
465 let chat = alice.create_group_with_members("My Group", &[&bob]).await;
466 let sent_msg = alice.send_text(chat, "Hello").await;
467 let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
468 chat_id_for_bob.accept(&bob).await?;
469
470 let sent_msg = alice.send_text(chat, "New Message").await;
471 let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
472 assert!(chat_id_for_bob.get_fresh_msg_cnt(&bob).await? >= 1);
473
474 bob.evtracker.clear_events();
475 chat::marknoticed_chat(&bob, chat_id_for_bob).await?;
476 wait_for_chatlist_specific_item(&bob, chat_id_for_bob).await;
477
478 Ok(())
479 }
480
481 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
483 async fn test_unblock_contact() -> Result<()> {
484 let mut tcm = TestContextManager::new();
485 let alice = tcm.alice().await;
486 let contact_id = Contact::create(&alice, "example", "example@example.com").await?;
487 let _ = ChatId::create_for_contact(&alice, contact_id).await;
488
489 alice.evtracker.clear_events();
490 Contact::block(&alice, contact_id).await?;
491 wait_for_chatlist(&alice).await;
492
493 alice.evtracker.clear_events();
494 Contact::unblock(&alice, contact_id).await?;
495 wait_for_chatlist(&alice).await;
496
497 Ok(())
498 }
499
500 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
503 async fn test_update_after_ephemeral_messages() -> Result<()> {
504 let mut tcm = TestContextManager::new();
505 let alice = tcm.alice().await;
506 let chat = create_group(&alice, "My Group").await?;
507 chat.set_ephemeral_timer(&alice, crate::ephemeral::Timer::Enabled { duration: 60 })
508 .await?;
509 alice
510 .evtracker
511 .get_matching(|evt| matches!(evt, EventType::ChatEphemeralTimerModified { .. }))
512 .await;
513
514 let _ = chat::send_text_msg(&alice, chat, "Hello".to_owned()).await?;
515 wait_for_chatlist_and_specific_item(&alice, chat).await;
516
517 SystemTime::shift(Duration::from_secs(70));
518 crate::ephemeral::delete_expired_messages(&alice, crate::tools::time()).await?;
519 wait_for_chatlist_and_specific_item(&alice, chat).await;
520
521 Ok(())
522 }
523
524 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
526 async fn test_adhoc_group() -> Result<()> {
527 let alice = TestContext::new_alice().await;
528 let mime = br#"Subject: First thread
529Message-ID: first@example.org
530To: Alice <alice@example.org>, Bob <bob@example.net>
531From: Claire <claire@example.org>
532Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
533
534First thread."#;
535
536 alice.evtracker.clear_events();
537 receive_imf(&alice, mime, false).await?;
538 wait_for_chatlist(&alice).await;
539
540 Ok(())
541 }
542
543 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
545 async fn test_secure_join_group() -> Result<()> {
546 let mut tcm = TestContextManager::new();
547 let alice = tcm.alice().await;
548 let bob = tcm.bob().await;
549
550 let alice_chatid = chat::create_group(&alice.ctx, "the chat").await?;
551
552 let qr = get_securejoin_qr(&alice.ctx, Some(alice_chatid)).await?;
554
555 bob.evtracker.clear_events();
557 let bob_chatid = join_securejoin(&bob.ctx, &qr).await?;
558 wait_for_chatlist(&bob).await;
559
560 let sent = bob.pop_sent_msg().await;
561
562 alice.evtracker.clear_events();
564 alice.recv_msg_trash(&sent).await;
565
566 let sent = alice.pop_sent_msg().await;
567
568 bob.evtracker.clear_events();
570 bob.recv_msg_trash(&sent).await;
571 wait_for_chatlist_and_specific_item(&bob, bob_chatid).await;
572
573 let sent = bob.pop_sent_msg().await;
574
575 alice.evtracker.clear_events();
577 alice.recv_msg_trash(&sent).await;
578 wait_for_chatlist_and_specific_item(&alice, alice_chatid).await;
579
580 let sent = alice.pop_sent_msg().await;
581
582 bob.evtracker.clear_events();
584 bob.recv_msg(&sent).await;
585 wait_for_chatlist_and_specific_item(&bob, bob_chatid).await;
586
587 Ok(())
588 }
589
590 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
594 async fn test_resend_message() -> Result<()> {
595 let mut tcm = TestContextManager::new();
596 let alice = tcm.alice().await;
597 let chat = create_group(&alice, "My Group").await?;
598
599 let msg_id = chat::send_text_msg(&alice, chat, "Hello".to_owned()).await?;
600 let _ = alice.pop_sent_msg().await;
601
602 let message = Message::load_from_db(&alice, msg_id).await?;
603 assert_eq!(message.get_state(), MessageState::OutDelivered);
604
605 alice.evtracker.clear_events();
606 chat::resend_msgs(&alice, &[msg_id]).await?;
607 wait_for_chatlist_specific_item(&alice, chat).await;
608
609 Ok(())
610 }
611
612 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
614 async fn test_reaction() -> Result<()> {
615 let mut tcm = TestContextManager::new();
616 let alice = tcm.alice().await;
617 let chat = create_group(&alice, "My Group").await?;
618 let msg_id = chat::send_text_msg(&alice, chat, "Hello".to_owned()).await?;
619 let _ = alice.pop_sent_msg().await;
620
621 alice.evtracker.clear_events();
622 reaction::send_reaction(&alice, msg_id, "👍").await?;
623 let _ = alice.pop_sent_msg().await;
624 wait_for_chatlist_specific_item(&alice, chat).await;
625
626 Ok(())
627 }
628}