1use crate::tests::TestServer;
2use gpui::{executor::Deterministic, TestAppContext};
3use notifications::NotificationEvent;
4use parking_lot::Mutex;
5use rpc::{proto, Notification};
6use std::sync::Arc;
7
8#[gpui::test]
9async fn test_notifications(
10 deterministic: Arc<Deterministic>,
11 cx_a: &mut TestAppContext,
12 cx_b: &mut TestAppContext,
13) {
14 deterministic.forbid_parking();
15 let mut server = TestServer::start(&deterministic).await;
16 let client_a = server.create_client(cx_a, "user_a").await;
17 let client_b = server.create_client(cx_b, "user_b").await;
18
19 let notification_events_a = Arc::new(Mutex::new(Vec::new()));
20 let notification_events_b = Arc::new(Mutex::new(Vec::new()));
21 client_a.notification_store().update(cx_a, |_, cx| {
22 let events = notification_events_a.clone();
23 cx.subscribe(&cx.handle(), move |_, _, event, _| {
24 events.lock().push(event.clone());
25 })
26 .detach()
27 });
28 client_b.notification_store().update(cx_b, |_, cx| {
29 let events = notification_events_b.clone();
30 cx.subscribe(&cx.handle(), move |_, _, event, _| {
31 events.lock().push(event.clone());
32 })
33 .detach()
34 });
35
36 // Client A sends a contact request to client B.
37 client_a
38 .user_store()
39 .update(cx_a, |store, cx| store.request_contact(client_b.id(), cx))
40 .await
41 .unwrap();
42
43 // Client B receives a contact request notification and responds to the
44 // request, accepting it.
45 deterministic.run_until_parked();
46 client_b.notification_store().update(cx_b, |store, cx| {
47 assert_eq!(store.notification_count(), 1);
48 assert_eq!(store.unread_notification_count(), 1);
49
50 let entry = store.notification_at(0).unwrap();
51 assert_eq!(
52 entry.notification,
53 Notification::ContactRequest {
54 sender_id: client_a.id()
55 }
56 );
57 assert!(!entry.is_read);
58 assert_eq!(
59 ¬ification_events_b.lock()[0..],
60 &[
61 NotificationEvent::NewNotification {
62 entry: entry.clone(),
63 },
64 NotificationEvent::NotificationsUpdated {
65 old_range: 0..0,
66 new_count: 1
67 }
68 ]
69 );
70
71 store.respond_to_notification(entry.notification.clone(), true, cx);
72 });
73
74 // Client B sees the notification is now read, and that they responded.
75 deterministic.run_until_parked();
76 client_b.notification_store().read_with(cx_b, |store, _| {
77 assert_eq!(store.notification_count(), 1);
78 assert_eq!(store.unread_notification_count(), 0);
79
80 let entry = store.notification_at(0).unwrap();
81 assert!(entry.is_read);
82 assert_eq!(entry.response, Some(true));
83 assert_eq!(
84 ¬ification_events_b.lock()[2..],
85 &[
86 NotificationEvent::NotificationRead {
87 entry: entry.clone(),
88 },
89 NotificationEvent::NotificationsUpdated {
90 old_range: 0..1,
91 new_count: 1
92 }
93 ]
94 );
95 });
96
97 // Client A receives a notification that client B accepted their request.
98 client_a.notification_store().read_with(cx_a, |store, _| {
99 assert_eq!(store.notification_count(), 1);
100 assert_eq!(store.unread_notification_count(), 1);
101
102 let entry = store.notification_at(0).unwrap();
103 assert_eq!(
104 entry.notification,
105 Notification::ContactRequestAccepted {
106 responder_id: client_b.id()
107 }
108 );
109 assert!(!entry.is_read);
110 });
111
112 // Client A creates a channel and invites client B to be a member.
113 let channel_id = client_a
114 .channel_store()
115 .update(cx_a, |store, cx| {
116 store.create_channel("the-channel", None, cx)
117 })
118 .await
119 .unwrap();
120 client_a
121 .channel_store()
122 .update(cx_a, |store, cx| {
123 store.invite_member(channel_id, client_b.id(), proto::ChannelRole::Member, cx)
124 })
125 .await
126 .unwrap();
127
128 // Client B receives a channel invitation notification and responds to the
129 // invitation, accepting it.
130 deterministic.run_until_parked();
131 client_b.notification_store().update(cx_b, |store, cx| {
132 assert_eq!(store.notification_count(), 2);
133 assert_eq!(store.unread_notification_count(), 1);
134
135 let entry = store.notification_at(0).unwrap();
136 assert_eq!(
137 entry.notification,
138 Notification::ChannelInvitation {
139 channel_id,
140 channel_name: "the-channel".to_string(),
141 inviter_id: client_a.id()
142 }
143 );
144 assert!(!entry.is_read);
145
146 store.respond_to_notification(entry.notification.clone(), true, cx);
147 });
148
149 // Client B sees the notification is now read, and that they responded.
150 deterministic.run_until_parked();
151 client_b.notification_store().read_with(cx_b, |store, _| {
152 assert_eq!(store.notification_count(), 2);
153 assert_eq!(store.unread_notification_count(), 0);
154
155 let entry = store.notification_at(0).unwrap();
156 assert!(entry.is_read);
157 assert_eq!(entry.response, Some(true));
158 });
159}