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