Coverage for app/backend/src/tests/test_conversations.py: 100%
1442 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-03 11:56 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-03 11:56 +0000
1from datetime import timedelta
3import grpc
4import pytest
5from google.protobuf import empty_pb2, wrappers_pb2
6from sqlalchemy import func, select
8from couchers.constants import MISSED_MESSAGES_DELAY
9from couchers.db import session_scope
10from couchers.jobs.handlers import send_message_notifications
11from couchers.jobs.worker import process_job
12from couchers.models import (
13 GroupChatRole,
14 GroupChatSubscription,
15 Notification,
16 NotificationDelivery,
17 NotificationDeliveryType,
18 NotificationTopicAction,
19 RateLimitAction,
20 RateLimitViolation,
21)
22from couchers.proto import api_pb2, conversations_pb2, notification_data_pb2, notifications_pb2
23from couchers.rate_limits.definitions import RATE_LIMIT_DEFINITIONS, RATE_LIMIT_HOURS
24from couchers.servicers import conversations
25from couchers.utils import Duration_from_timedelta, now, to_aware_datetime
26from tests.fixtures.db import generate_user, make_friends, make_user_block, make_user_invisible
27from tests.fixtures.misc import EmailCollector, Moderator, PushCollector, process_jobs
28from tests.fixtures.sessions import api_session, conversations_session, notifications_session
29from tests.fixtures.timewarp import Timewarp
32def test_list_group_chats(db, moderator):
33 user1, token1 = generate_user()
34 user2, token2 = generate_user()
35 user3, token3 = generate_user()
37 make_friends(user2, user1)
38 make_friends(user1, user3)
40 with conversations_session(token1) as c:
41 # no threads initially
42 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
43 assert len(res.group_chats) == 0
45 # create some group chats with messages
46 res = c.CreateGroupChat(
47 conversations_pb2.CreateGroupChatReq(
48 recipient_user_ids=[user2.id], title=wrappers_pb2.StringValue(value="Test title")
49 )
50 )
51 group_chat1_id = res.group_chat_id
52 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message 1"))
53 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message 2"))
54 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]))
55 group_chat2_id = res.group_chat_id
56 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test group message 1"))
57 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test group message 2"))
59 moderator.approve_group_chat(group_chat1_id)
60 moderator.approve_group_chat(group_chat2_id)
62 with conversations_session(token1) as c:
63 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
64 assert len(res.group_chats) == 2
65 assert res.no_more
67 with conversations_session(token2) as c:
68 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
69 assert len(res.group_chats) == 2
70 assert res.no_more
72 with conversations_session(token3) as c:
73 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
74 assert len(res.group_chats) == 1
75 assert res.no_more
77 # Test archive filtering: archive group_chat1 for user1
78 with conversations_session(token1) as c:
79 res = c.SetGroupChatArchiveStatus(
80 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=group_chat1_id, is_archived=True)
81 )
82 assert res.group_chat_id == group_chat1_id
83 assert res.is_archived
85 with conversations_session(token1) as c:
86 # Without filter, returns all chats
87 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
88 assert len(res.group_chats) == 2
90 # only_archived=False returns non-archived chats only
91 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=False))
92 assert len(res.group_chats) == 1
94 # only_archived=True returns archived chats only
95 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=True))
96 assert len(res.group_chats) == 1
98 # user2 should still see both as non-archived (archive is per-user)
99 with conversations_session(token2) as c:
100 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=False))
101 assert len(res.group_chats) == 2
103 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=True))
104 assert len(res.group_chats) == 0
107def test_list_empty_group_chats(db, moderator):
108 user1, token1 = generate_user()
109 user2, token2 = generate_user()
110 user3, token3 = generate_user()
112 make_friends(user1, user3)
113 make_friends(user2, user1)
114 make_friends(user2, user3)
116 with conversations_session(token1) as c:
117 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
118 assert len(res.group_chats) == 0
120 res1 = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id]))
121 res2 = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]))
123 moderator.approve_group_chat(res1.group_chat_id)
124 moderator.approve_group_chat(res2.group_chat_id)
126 with conversations_session(token1) as c:
127 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
128 assert len(res.group_chats) == 2
129 assert res.no_more
131 with conversations_session(token2) as c:
132 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
133 assert len(res.group_chats) == 2
134 assert res.no_more
136 res3 = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id]))
137 moderator.approve_group_chat(res3.group_chat_id)
139 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
140 assert len(res.group_chats) == 3
141 assert res.no_more
143 with conversations_session(token3) as c:
144 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
145 assert len(res.group_chats) == 2
146 assert res.no_more
149def test_list_group_chats_ordering(db, moderator):
150 # user is member of 5 group chats, order them correctly
151 user, token = generate_user()
152 user2, token2 = generate_user()
153 user3, token3 = generate_user()
154 user4, token4 = generate_user()
156 make_friends(user2, user)
157 make_friends(user2, user3)
158 make_friends(user2, user4)
159 make_friends(user3, user)
160 make_friends(user3, user4)
161 make_friends(user, user4)
163 chat_ids = []
165 with conversations_session(token2) as c:
166 res = c.CreateGroupChat(
167 conversations_pb2.CreateGroupChatReq(
168 recipient_user_ids=[user.id], title=wrappers_pb2.StringValue(value="Chat 0")
169 )
170 )
171 chat_ids.append(res.group_chat_id)
172 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message"))
173 res = c.CreateGroupChat(
174 conversations_pb2.CreateGroupChatReq(
175 recipient_user_ids=[user.id, user3.id], title=wrappers_pb2.StringValue(value="Chat 1")
176 )
177 )
178 chat_ids.append(res.group_chat_id)
179 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message"))
180 res = c.CreateGroupChat(
181 conversations_pb2.CreateGroupChatReq(
182 recipient_user_ids=[user.id, user3.id], title=wrappers_pb2.StringValue(value="Chat 2")
183 )
184 )
185 chat_ids.append(res.group_chat_id)
186 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message"))
188 with conversations_session(token3) as c:
189 res = c.CreateGroupChat(
190 conversations_pb2.CreateGroupChatReq(
191 recipient_user_ids=[user.id, user2.id, user4.id], title=wrappers_pb2.StringValue(value="Chat 3")
192 )
193 )
194 chat_ids.append(res.group_chat_id)
195 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message"))
197 for chat_id in chat_ids:
198 moderator.approve_group_chat(chat_id)
200 with conversations_session(token) as c:
201 res = c.CreateGroupChat(
202 conversations_pb2.CreateGroupChatReq(
203 recipient_user_ids=[user2.id, user3.id, user4.id], title=wrappers_pb2.StringValue(value="Chat 4")
204 )
205 )
206 moderator.approve_group_chat(res.group_chat_id)
207 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message"))
208 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
209 assert len(res.group_chats) == 5
210 assert res.group_chats[0].title == "Chat 4"
211 assert res.group_chats[1].title == "Chat 3"
212 assert res.group_chats[2].title == "Chat 2"
213 assert res.group_chats[3].title == "Chat 1"
214 assert res.group_chats[4].title == "Chat 0"
216 c.SendMessage(
217 conversations_pb2.SendMessageReq(group_chat_id=res.group_chats[3].group_chat_id, text="Test message 2a")
218 )
219 c.SendMessage(
220 conversations_pb2.SendMessageReq(group_chat_id=res.group_chats[2].group_chat_id, text="Test message 2b")
221 )
223 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
224 assert len(res.group_chats) == 5
225 assert res.group_chats[0].title == "Chat 2"
226 assert res.group_chats[0].latest_message.text.text == "Test message 2b"
227 assert res.group_chats[1].title == "Chat 1"
228 assert res.group_chats[1].latest_message.text.text == "Test message 2a"
229 assert res.group_chats[2].title == "Chat 4"
230 assert res.group_chats[3].title == "Chat 3"
231 assert res.group_chats[4].title == "Chat 0"
234def test_list_group_chats_ordering_after_left(db, moderator):
235 # user is member to 4 group chats, and has left one.
236 # The one user left has the most recent message, but user left before then,
237 # this should display as e.g. 3rd most recent depending on last message when they were in the chat
238 user, token = generate_user()
239 user2, token2 = generate_user()
240 user3, token3 = generate_user()
241 user4, token4 = generate_user()
243 make_friends(user2, user)
244 make_friends(user2, user3)
245 make_friends(user2, user4)
246 make_friends(user3, user)
247 make_friends(user3, user4)
248 make_friends(user, user4)
250 chat_ids = []
252 with conversations_session(token2) as c:
253 res = c.CreateGroupChat(
254 conversations_pb2.CreateGroupChatReq(
255 recipient_user_ids=[user.id], title=wrappers_pb2.StringValue(value="Chat 0")
256 )
257 )
258 chat_ids.append(res.group_chat_id)
259 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message"))
260 res = c.CreateGroupChat(
261 conversations_pb2.CreateGroupChatReq(
262 recipient_user_ids=[user.id, user3.id], title=wrappers_pb2.StringValue(value="Left Chat 1")
263 )
264 )
265 left_chat_id = res.group_chat_id
266 chat_ids.append(left_chat_id)
267 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=left_chat_id, text="Test message"))
268 res = c.CreateGroupChat(
269 conversations_pb2.CreateGroupChatReq(
270 recipient_user_ids=[user.id, user3.id], title=wrappers_pb2.StringValue(value="Chat 2")
271 )
272 )
273 chat2_id = res.group_chat_id
274 chat_ids.append(chat2_id)
275 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=chat2_id, text="Test message"))
277 with conversations_session(token3) as c:
278 res = c.CreateGroupChat(
279 conversations_pb2.CreateGroupChatReq(
280 recipient_user_ids=[user.id, user2.id, user4.id], title=wrappers_pb2.StringValue(value="Chat 3")
281 )
282 )
283 chat_ids.append(res.group_chat_id)
284 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message"))
286 for chat_id in chat_ids:
287 moderator.approve_group_chat(chat_id)
289 with conversations_session(token) as c:
290 res = c.CreateGroupChat(
291 conversations_pb2.CreateGroupChatReq(
292 recipient_user_ids=[user2.id, user3.id, user4.id], title=wrappers_pb2.StringValue(value="Chat 4")
293 )
294 )
295 moderator.approve_group_chat(res.group_chat_id)
296 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message"))
298 # leave chat
299 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=left_chat_id))
301 with conversations_session(token3) as c:
302 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=chat2_id, text="Test message"))
303 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=left_chat_id, text="Test message"))
305 with conversations_session(token2) as c:
306 # other user sends a message to that chat
307 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=left_chat_id, text="Another test message"))
308 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
309 assert len(res.group_chats) == 5
310 assert res.group_chats[0].title == "Left Chat 1"
311 assert res.group_chats[1].title == "Chat 2"
312 assert res.group_chats[2].title == "Chat 4"
313 assert res.group_chats[3].title == "Chat 3"
314 assert res.group_chats[4].title == "Chat 0"
316 with conversations_session(token) as c:
317 # we can't see the new message since we left before it was sent
318 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
319 assert len(res.group_chats) == 5
320 assert res.group_chats[0].title == "Chat 2"
321 assert res.group_chats[1].title == "Left Chat 1"
322 assert res.group_chats[2].title == "Chat 4"
323 assert res.group_chats[3].title == "Chat 3"
324 assert res.group_chats[4].title == "Chat 0"
327def test_get_group_chat_messages(db):
328 user1, token1 = generate_user()
329 user2, token2 = generate_user()
330 user3, token3 = generate_user()
332 make_friends(user1, user2)
334 with conversations_session(token1) as c:
335 # create some threads with messages
336 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id]))
337 group_chat_id = res.group_chat_id
338 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message 1"))
339 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message 2"))
341 res = c.GetGroupChatMessages(conversations_pb2.GetGroupChatMessagesReq(group_chat_id=group_chat_id))
342 # created + 2 normal
343 assert len(res.messages) == 3
344 assert res.no_more
346 assert res.messages[0].text.text == "Test message 2"
347 assert res.messages[1].text.text == "Test message 1"
348 assert res.messages[2].WhichOneof("content") == "chat_created"
350 # test that another user can't access the thread
351 with conversations_session(token3) as c:
352 res = c.GetGroupChatMessages(conversations_pb2.GetGroupChatMessagesReq(group_chat_id=group_chat_id))
353 assert len(res.messages) == 0
356def test_get_group_chat_messages_pagination(db, moderator):
357 user1, token1 = generate_user()
358 user2, token2 = generate_user()
359 make_friends(user1, user2)
361 with conversations_session(token1) as c:
362 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id]))
363 group_chat_id = res.group_chat_id
364 for i in range(30):
365 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text=str(i)))
367 moderator.approve_group_chat(group_chat_id)
369 with conversations_session(token2) as c:
370 res = c.GetGroupChatMessages(conversations_pb2.GetGroupChatMessagesReq(group_chat_id=group_chat_id))
371 # pagination
372 assert len(res.messages) == 20
373 assert res.messages[0].text.text == "29"
374 assert res.messages[19].text.text == "10"
375 assert not res.no_more
376 res = c.GetGroupChatMessages(
377 conversations_pb2.GetGroupChatMessagesReq(
378 group_chat_id=group_chat_id, last_message_id=res.messages[19].message_id
379 )
380 )
381 assert len(res.messages) == 11
382 assert res.messages[0].text.text == "9"
383 assert res.messages[9].text.text == "0"
384 assert res.no_more
387def test_get_group_chat_messages_joined_left(db, moderator):
388 user1, token1 = generate_user()
389 user2, token2 = generate_user()
390 user3, token3 = generate_user()
391 user4, token4 = generate_user()
392 make_friends(user1, user2)
393 make_friends(user1, user3)
394 make_friends(user1, user4)
396 with conversations_session(token1) as c:
397 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user4.id]))
398 group_chat_id = res.group_chat_id
399 moderator.approve_group_chat(group_chat_id)
401 for i in range(10):
402 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text=str(i)))
404 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id))
406 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="10"))
408 res = c.GetGroupChatMessages(conversations_pb2.GetGroupChatMessagesReq(group_chat_id=group_chat_id))
410 # created + 10 normal + invited + normal
411 assert len(res.messages) == 13
413 with conversations_session(token3) as c:
414 # can only see last message after invited
415 res = c.GetGroupChatMessages(conversations_pb2.GetGroupChatMessagesReq(group_chat_id=group_chat_id))
416 # joined + normal
417 assert len(res.messages) == 2
418 assert res.messages[0].text.text == "10"
420 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
422 with conversations_session(token1) as c:
423 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="11"))
424 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="12"))
426 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id))
428 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="13"))
429 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="14"))
431 with conversations_session(token3) as c:
432 # can only see last message after invited
433 res = c.GetGroupChatMessages(conversations_pb2.GetGroupChatMessagesReq(group_chat_id=group_chat_id))
434 # joined + normal + left + invite + 2 normal
435 assert len(res.messages) == 6
436 assert res.messages[0].text.text == "14"
437 assert res.messages[1].text.text == "13"
438 assert res.messages[2].WhichOneof("content") == "user_invited"
439 assert res.messages[3].WhichOneof("content") == "user_left"
440 assert res.messages[4].text.text == "10"
441 assert res.messages[5].WhichOneof("content") == "user_invited"
444def test_get_group_chat_info(db):
445 user1, token1 = generate_user()
446 user2, token2 = generate_user()
447 user3, token3 = generate_user()
449 make_friends(user1, user2)
450 make_friends(user3, user1)
452 with conversations_session(token1) as c:
453 # create some threads with messages
454 res = c.CreateGroupChat(
455 conversations_pb2.CreateGroupChatReq(
456 recipient_user_ids=[user2.id], title=wrappers_pb2.StringValue(value="Test title")
457 )
458 )
459 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message 1"))
460 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message 2"))
461 group_chat1_id = res.group_chat_id
462 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]))
463 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test group message 1"))
464 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test group message 2"))
465 group_chat2_id = res.group_chat_id
467 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat1_id))
468 assert res.title == "Test title"
469 assert user2.id in res.member_user_ids
470 assert user1.id in res.admin_user_ids
471 assert to_aware_datetime(res.created) <= now()
472 assert res.only_admins_invite
473 assert res.is_dm
474 assert not res.is_archived
476 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat2_id))
477 assert not res.title
478 assert user2.id in res.member_user_ids
479 assert user3.id in res.member_user_ids
480 assert user1.id in res.admin_user_ids
481 assert to_aware_datetime(res.created) <= now()
482 assert res.only_admins_invite
483 assert not res.is_dm
484 assert not res.is_archived
487def test_get_group_chat_info_denied(db):
488 user1, token1 = generate_user()
489 user2, token2 = generate_user()
490 user3, token3 = generate_user()
491 user4, token4 = generate_user()
493 make_friends(user1, user2)
494 make_friends(user3, user1)
496 with conversations_session(token1) as c:
497 # create a group chat with messages
498 res = c.CreateGroupChat(
499 conversations_pb2.CreateGroupChatReq(
500 recipient_user_ids=[user2.id, user3.id], title=wrappers_pb2.StringValue(value="Test title")
501 )
502 )
503 group_chat_id = res.group_chat_id
504 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 1"))
506 with conversations_session(token4) as c:
507 with pytest.raises(grpc.RpcError) as e:
508 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id))
509 assert e.value.code() == grpc.StatusCode.NOT_FOUND
512def test_get_group_chat_info_left(db, moderator):
513 user1, token1 = generate_user()
514 user2, token2 = generate_user()
515 user3, token3 = generate_user()
516 user4, token4 = generate_user()
518 make_friends(user1, user2)
519 make_friends(user3, user1)
520 make_friends(user1, user4)
522 with conversations_session(token1) as c:
523 # create a group chat with messages
524 res = c.CreateGroupChat(
525 conversations_pb2.CreateGroupChatReq(
526 recipient_user_ids=[user2.id, user3.id], title=wrappers_pb2.StringValue(value="Test title")
527 )
528 )
529 group_chat_id = res.group_chat_id
530 moderator.approve_group_chat(group_chat_id)
531 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 1"))
533 with conversations_session(token3) as c:
534 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
536 with conversations_session(token1) as c:
537 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user4.id))
539 with conversations_session(token3) as c:
540 # this user left when user4 wasn't a member,
541 # so the returned members should be user1, user2, and user3 only
542 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id))
543 assert len(res.member_user_ids) == 3
544 assert user1.id in res.member_user_ids
545 assert user2.id in res.member_user_ids
546 assert user3.id in res.member_user_ids
549def test_remove_group_chat_user(db):
550 # create 3 uses and connect them
551 user1, token1 = generate_user()
552 user2, token2 = generate_user()
553 user3, token3 = generate_user()
554 make_friends(user1, user2)
555 make_friends(user1, user3)
557 # using user token, create a Conversations API for testing
558 with conversations_session(token1) as c:
559 # create a group chat
560 res = c.CreateGroupChat(
561 conversations_pb2.CreateGroupChatReq(
562 recipient_user_ids=[user2.id, user3.id], title=wrappers_pb2.StringValue(value="Test title")
563 )
564 )
565 group_chat_id = res.group_chat_id
567 # remove a user from group
568 c.RemoveGroupChatUser(conversations_pb2.RemoveGroupChatUserReq(group_chat_id=group_chat_id, user_id=user2.id))
569 assert user3.id in res.member_user_ids # other users are still in the group
571 # can't remove the same user twice
572 with pytest.raises(grpc.RpcError) as e:
573 c.RemoveGroupChatUser(
574 conversations_pb2.RemoveGroupChatUserReq(group_chat_id=group_chat_id, user_id=user2.id)
575 )
576 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
579def test_edit_group_chat(db, moderator):
580 user1, token1 = generate_user()
581 user2, token2 = generate_user()
582 user3, token3 = generate_user()
583 make_friends(user1, user2)
585 with conversations_session(token1) as c:
586 # create some threads with messages
587 res = c.CreateGroupChat(
588 conversations_pb2.CreateGroupChatReq(
589 recipient_user_ids=[user2.id], title=wrappers_pb2.StringValue(value="Test title")
590 )
591 )
592 group_chat_id = res.group_chat_id
593 moderator.approve_group_chat(group_chat_id)
595 c.EditGroupChat(
596 conversations_pb2.EditGroupChatReq(
597 group_chat_id=group_chat_id,
598 title=wrappers_pb2.StringValue(value="Modified title"),
599 only_admins_invite=wrappers_pb2.BoolValue(value=False),
600 )
601 )
602 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id))
603 assert res.title == "Modified title"
604 assert not res.only_admins_invite
606 # make sure non-admin is not allowed to modify
607 with conversations_session(token2) as c:
608 with pytest.raises(grpc.RpcError) as e:
609 c.EditGroupChat(
610 conversations_pb2.EditGroupChatReq(
611 group_chat_id=group_chat_id,
612 title=wrappers_pb2.StringValue(value="Other title"),
613 only_admins_invite=wrappers_pb2.BoolValue(value=True),
614 )
615 )
616 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
618 # make sure non-recipient is not allowed to modify
619 with conversations_session(token3) as c:
620 with pytest.raises(grpc.RpcError) as e:
621 c.EditGroupChat(
622 conversations_pb2.EditGroupChatReq(
623 group_chat_id=group_chat_id,
624 title=wrappers_pb2.StringValue(value="Other title"),
625 only_admins_invite=wrappers_pb2.BoolValue(value=True),
626 )
627 )
628 assert e.value.code() == grpc.StatusCode.NOT_FOUND
631def test_make_remove_group_chat_admin(db, moderator):
632 user1, token1 = generate_user()
633 user2, token2 = generate_user()
634 user3, token3 = generate_user()
636 make_friends(user1, user2)
637 make_friends(user1, user3)
638 make_friends(user2, user3)
640 with conversations_session(token1) as c:
641 # create some threads with messages
642 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]))
643 group_chat_id = res.group_chat_id
644 moderator.approve_group_chat(group_chat_id)
646 # shouldn't be able to remove only admin
647 with pytest.raises(grpc.RpcError) as e:
648 c.RemoveGroupChatAdmin(
649 conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=group_chat_id, user_id=user1.id)
650 )
651 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
652 assert e.value.details() == "You can't remove the last admin."
654 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=group_chat_id, user_id=user2.id))
656 # shouldn't be able to make admin again
657 with pytest.raises(grpc.RpcError) as e:
658 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=group_chat_id, user_id=user2.id))
659 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
660 assert e.value.details() == "That user is already an admin."
662 with conversations_session(token2) as c:
663 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id))
664 assert user1.id in res.admin_user_ids
665 assert user2.id in res.admin_user_ids
667 with conversations_session(token1) as c:
668 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=group_chat_id, user_id=user2.id))
670 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id))
671 assert user1.id in res.admin_user_ids
672 assert user2.id not in res.admin_user_ids
674 with conversations_session(token2) as c:
675 # shouldn't be able to make admin if not admin
676 with pytest.raises(grpc.RpcError) as e:
677 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=group_chat_id, user_id=user3.id))
678 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
681def test_send_message(db, moderator: Moderator, push_collector: PushCollector, email_collector: EmailCollector):
682 user1, token1 = generate_user()
683 user2, token2 = generate_user()
684 user3, token3 = generate_user()
685 make_friends(user1, user2)
686 make_friends(user1, user3)
688 # Let user2 receive email notifications for every chat message
689 with notifications_session(token2) as n:
690 n.SetNotificationSettings(
691 notifications_pb2.SetNotificationSettingsReq(
692 preferences=[
693 notifications_pb2.SingleNotificationPreference(
694 topic=NotificationTopicAction.chat__message.topic,
695 action=NotificationTopicAction.chat__message.action,
696 delivery_method="email",
697 enabled=True,
698 )
699 ]
700 )
701 )
703 group_chat_title = "My group chat"
704 message1 = "Test message 1"
706 # Let user1 create a group chat with user2 and send a message
707 with conversations_session(token1) as c:
708 res = c.CreateGroupChat(
709 conversations_pb2.CreateGroupChatReq(
710 title=wrappers_pb2.StringValue(value=group_chat_title), recipient_user_ids=[user2.id]
711 )
712 )
713 group_chat_id = res.group_chat_id
714 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text=message1))
716 # Sender can already see group chat
717 res = c.GetGroupChatMessages(conversations_pb2.GetGroupChatMessagesReq(group_chat_id=group_chat_id))
718 assert res.messages[0].text.text == message1
719 assert to_aware_datetime(res.messages[0].time) <= now()
720 assert res.messages[0].author_user_id == user1.id
722 # user2 sees nothing until the group chat is approved
723 assert push_collector.count_for_user(user2.id) == 0
724 assert email_collector.count_for_recipient(user2.email) == 0
726 moderator.approve_group_chat(group_chat_id)
728 # user2 gets email and push notifications
729 push = push_collector.pop_for_user(user2.id, last=True)
730 assert push.topic_action == NotificationTopicAction.chat__message.display
731 assert user1.name in push.content.title
732 assert group_chat_title in push.content.title
733 assert push.content.ios_title == user1.name
734 assert push.content.ios_subtitle == group_chat_title
735 assert message1 in push.content.body
737 email = email_collector.pop_for_recipient(user2.email, last=True)
738 assert group_chat_title in email.subject
739 assert message1 in email.plain
740 assert message1 in email.html
742 # can't send message if not in chat
743 with conversations_session(token3) as c:
744 with pytest.raises(grpc.RpcError) as e:
745 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 2"))
746 assert e.value.code() == grpc.StatusCode.NOT_FOUND
748 make_user_block(user2, user1)
749 with conversations_session(token1) as c:
750 with pytest.raises(grpc.RpcError) as e:
751 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Message after block"))
752 assert e.value.details() == "You can't send a message in this chat."
755def test_send_direct_message(db, moderator: Moderator, push_collector: PushCollector, email_collector: EmailCollector):
756 user1, token1 = generate_user(complete_profile=True)
757 user2, token2 = generate_user(complete_profile=True)
759 make_friends(user1, user2)
761 # Let user2 receive email notifications for every chat message
762 with notifications_session(token2) as n:
763 n.SetNotificationSettings(
764 notifications_pb2.SetNotificationSettingsReq(
765 preferences=[
766 notifications_pb2.SingleNotificationPreference(
767 topic=NotificationTopicAction.chat__message.topic,
768 action=NotificationTopicAction.chat__message.action,
769 delivery_method="email",
770 enabled=True,
771 )
772 ]
773 )
774 )
776 # Let user1 send two DM's to user2
777 message1 = "Hello, user2!"
778 message2 = "One more message."
780 with conversations_session(token1) as c1:
781 # Send a DM from user1 to user2
782 res = c1.SendDirectMessage(conversations_pb2.SendDirectMessageReq(recipient_user_id=user2.id, text=message1))
783 moderator.approve_group_chat(res.group_chat_id)
785 c1.SendDirectMessage(conversations_pb2.SendDirectMessageReq(recipient_user_id=user2.id, text=message2))
787 # user2 should have received push and email notifications for both messages
788 push = push_collector.pop_for_user(user2.id, last=False)
789 assert push.topic_action == NotificationTopicAction.chat__message.display
790 assert push.content.title == user1.name
791 assert push.content.body == message1
793 email = email_collector.pop_for_recipient(user2.email, last=False)
794 assert user1.name in email.subject
795 assert message1 in email.plain
796 assert message1 in email.html
798 push = push_collector.pop_for_user(user2.id, last=True)
799 assert push.topic_action == NotificationTopicAction.chat__message.display
800 assert push.content.title == user1.name
801 assert push.content.body == message2
803 email = email_collector.pop_for_recipient(user2.email, last=True)
804 assert user1.name in email.subject
805 assert message2 in email.plain
806 assert message2 in email.html
808 with conversations_session(token2) as c2:
809 # Fetch the chat by ID returned from SendDirectMessage
810 chat = c2.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=res.group_chat_id))
812 assert chat.is_dm
813 group_chat_id = chat.group_chat_id
815 # Verify that the messages was received
816 messages = c2.GetGroupChatMessages(
817 conversations_pb2.GetGroupChatMessagesReq(group_chat_id=group_chat_id)
818 ).messages
820 assert len(messages) == 2
821 assert messages[0].text.text == message2
822 assert messages[1].text.text == message1
823 assert messages[0].author_user_id == user1.id
826def test_excessive_chat_initiations_are_reported(db, low_rate_limits, email_collector: EmailCollector):
827 """Test that excessive chat initiations are first reported in a warning email and finally lead blocking of further contacting other users."""
828 user, token = generate_user()
829 rate_limit_definition = RATE_LIMIT_DEFINITIONS[RateLimitAction.chat_initiation]
830 with conversations_session(token) as c:
831 # Test warning email
832 for _ in range(rate_limit_definition.warning_limit):
833 recipient_user, _ = generate_user()
834 _ = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[recipient_user.id]))
836 assert email_collector.count_for_reports() == 0
838 recipient_user, _ = generate_user()
839 _ = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[recipient_user.id]))
841 email = email_collector.pop_for_reports(last=True)
842 assert email.plain.startswith(
843 f"User {user.username} has sent {rate_limit_definition.warning_limit} chat initiations in the past {RATE_LIMIT_HOURS} hours."
844 )
846 # Test new chat initiations fail after exceeding CHAT_INITIATION_HARD_LIMIT
847 for _ in range(rate_limit_definition.hard_limit - rate_limit_definition.warning_limit - 1):
848 recipient_user, _ = generate_user()
849 _ = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[recipient_user.id]))
851 assert email_collector.count_for_reports() == 0
853 recipient_user, _ = generate_user()
854 with pytest.raises(grpc.RpcError) as exc_info:
855 _ = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[recipient_user.id]))
856 assert exc_info.value.code() == grpc.StatusCode.RESOURCE_EXHAUSTED
857 assert (
858 exc_info.value.details()
859 == "You have messaged a lot of users in the past 24 hours. To avoid spam, you can't contact any more users for now."
860 )
862 email = email_collector.pop_for_reports(last=True)
863 assert email.plain.startswith(
864 f"User {user.username} has sent {rate_limit_definition.hard_limit} chat initiations in the past {RATE_LIMIT_HOURS} hours."
865 )
866 assert "The user has been blocked from sending further chat initiations for now." in email.plain
868 with session_scope() as session:
869 assert session.execute(
870 select(RateLimitViolation.is_hard_limit)
871 .where(RateLimitViolation.user_id == user.id)
872 .order_by(RateLimitViolation.id)
873 ).scalars().all() == [False, True]
875 # still blocked once the hard violation is on record, without reporting again
876 recipient_user, _ = generate_user()
877 with pytest.raises(grpc.RpcError) as exc_info:
878 _ = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[recipient_user.id]))
879 assert exc_info.value.code() == grpc.StatusCode.RESOURCE_EXHAUSTED
880 assert email_collector.count_for_reports() == 0
883def test_send_direct_message_rate_limit(db, low_rate_limits, moderator, email_collector: EmailCollector):
884 """SendDirectMessage should enforce the chat_initiation rate limit when creating a new DM, but not when sending into an existing one."""
885 user, token = generate_user(complete_profile=True)
886 rate_limit_definition = RATE_LIMIT_DEFINITIONS[RateLimitAction.chat_initiation]
888 with conversations_session(token) as c:
889 for _ in range(rate_limit_definition.warning_limit):
890 recipient, _ = generate_user()
891 c.SendDirectMessage(conversations_pb2.SendDirectMessageReq(recipient_user_id=recipient.id, text="hi"))
893 assert email_collector.count_for_reports() == 0
895 recipient, _ = generate_user()
896 existing_dm_recipient_id = recipient.id
897 c.SendDirectMessage(
898 conversations_pb2.SendDirectMessageReq(recipient_user_id=existing_dm_recipient_id, text="hi")
899 )
901 email = email_collector.pop_for_reports(last=True)
902 assert email.plain.startswith(
903 f"User {user.username} has sent {rate_limit_definition.warning_limit} chat initiations in the past {RATE_LIMIT_HOURS} hours."
904 )
906 for _ in range(rate_limit_definition.hard_limit - rate_limit_definition.warning_limit - 1):
907 recipient, _ = generate_user()
908 c.SendDirectMessage(conversations_pb2.SendDirectMessageReq(recipient_user_id=recipient.id, text="hi"))
910 assert email_collector.count_for_reports() == 0
912 # follow-up into an existing DM must not count as a new initiation
913 c.SendDirectMessage(
914 conversations_pb2.SendDirectMessageReq(recipient_user_id=existing_dm_recipient_id, text="follow-up")
915 )
916 assert email_collector.count_for_reports() == 0
918 recipient, _ = generate_user()
919 with pytest.raises(grpc.RpcError) as exc_info:
920 c.SendDirectMessage(conversations_pb2.SendDirectMessageReq(recipient_user_id=recipient.id, text="hi"))
921 assert exc_info.value.code() == grpc.StatusCode.RESOURCE_EXHAUSTED
922 assert (
923 exc_info.value.details()
924 == "You have messaged a lot of users in the past 24 hours. To avoid spam, you can't contact any more users for now."
925 )
927 email = email_collector.pop_for_reports(last=True)
928 assert email.plain.startswith(
929 f"User {user.username} has sent {rate_limit_definition.hard_limit} chat initiations in the past {RATE_LIMIT_HOURS} hours."
930 )
931 assert "The user has been blocked from sending further chat initiations for now." in email.plain
934def test_leave_invite_to_group_chat(db, moderator):
935 user1, token1 = generate_user()
936 user2, token2 = generate_user()
937 user3, token3 = generate_user()
938 user4, token4 = generate_user()
939 user5, token5 = generate_user()
940 user6, token6 = generate_user(delete_user=True)
941 user7, token7 = generate_user()
942 user8, token8 = generate_user()
944 make_friends(user1, user2)
945 make_friends(user1, user3)
946 make_friends(user1, user5)
947 make_friends(user1, user7)
948 make_friends(user1, user8)
949 make_friends(user2, user3)
950 make_friends(user4, user3)
951 make_user_block(user1, user7)
952 make_user_block(user8, user1)
954 with conversations_session(token1) as c:
955 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user5.id]))
956 group_chat_id = res.group_chat_id
957 moderator.approve_group_chat(group_chat_id)
958 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 1"))
960 # other user not in chat
961 with conversations_session(token3) as c:
962 with pytest.raises(grpc.RpcError) as e:
963 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id))
964 assert e.value.code() == grpc.StatusCode.NOT_FOUND
965 with pytest.raises(grpc.RpcError) as e:
966 res = c.InviteToGroupChat(
967 conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user4.id)
968 )
969 assert e.value.code() == grpc.StatusCode.NOT_FOUND
970 with pytest.raises(grpc.RpcError) as e:
971 res = c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
972 assert e.value.code() == grpc.StatusCode.NOT_FOUND
974 with conversations_session(token2) as c:
975 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id))
976 assert user3.id not in res.member_user_ids
978 # only_admins_invite defaults to true so shouldn't be able to invite
979 with pytest.raises(grpc.RpcError) as e:
980 res = c.InviteToGroupChat(
981 conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id)
982 )
983 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
984 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
986 with conversations_session(token1) as c:
987 # invite invisible user fails
988 with pytest.raises(grpc.RpcError) as e:
989 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user6.id))
990 assert e.value.code() == grpc.StatusCode.NOT_FOUND
991 assert e.value.details() == "Couldn't find that user."
992 # invite fake user fails
993 with pytest.raises(grpc.RpcError) as e:
994 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=999))
995 assert e.value.code() == grpc.StatusCode.NOT_FOUND
996 assert e.value.details() == "Couldn't find that user."
997 # invite blocked user fails
998 with pytest.raises(grpc.RpcError) as e:
999 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user7.id))
1000 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1001 assert e.value.details() == "Couldn't find that user."
1002 # invite blocking user fails
1003 with pytest.raises(grpc.RpcError) as e:
1004 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user8.id))
1005 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1006 assert e.value.details() == "Couldn't find that user."
1008 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id))
1009 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id))
1010 assert user1.id in res.member_user_ids
1011 assert user5.id in res.member_user_ids
1012 assert user3.id in res.member_user_ids
1014 # test non-admin inviting
1015 c.EditGroupChat(
1016 conversations_pb2.EditGroupChatReq(
1017 group_chat_id=group_chat_id, only_admins_invite=wrappers_pb2.BoolValue(value=False)
1018 )
1019 )
1021 with conversations_session(token3) as c:
1022 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user2.id))
1023 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id))
1024 assert user2.id in res.member_user_ids
1027def test_group_chats_with_messages_before_join(db, moderator):
1028 """
1029 If user 1 and 2 have a group chat and send messages, then add user 3; user 3
1030 should still see the group chat!
1031 """
1032 user1, token1 = generate_user()
1033 user2, token2 = generate_user()
1034 user3, token3 = generate_user()
1035 user4, token4 = generate_user()
1037 make_friends(user1, user2)
1038 make_friends(user1, user3)
1039 make_friends(user2, user3)
1040 make_friends(user1, user4)
1042 with conversations_session(token1) as c:
1043 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user4.id]))
1044 group_chat_id = res.group_chat_id
1045 moderator.approve_group_chat(group_chat_id)
1046 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 1"))
1048 with conversations_session(token2) as c:
1049 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 2"))
1051 with conversations_session(token1) as c:
1052 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 3"))
1054 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id))
1056 with conversations_session(token3) as c:
1057 # should work
1058 c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id))
1060 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
1061 assert len(res.group_chats) == 1
1064def test_invite_to_dm(db):
1065 user1, token1 = generate_user()
1066 user2, token2 = generate_user()
1067 user3, token3 = generate_user()
1069 make_friends(user1, user2)
1070 make_friends(user1, user3)
1071 make_friends(user2, user3)
1073 with conversations_session(token1) as c:
1074 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id]))
1075 group_chat_id = res.group_chat_id
1076 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 1"))
1078 # dm, shou;dn't be able to invite someone else
1079 with pytest.raises(grpc.RpcError) as e:
1080 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id))
1081 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
1082 assert e.value.details() == "You can't invite other users to a direct message."
1085def test_sole_admin_leaves(db, moderator):
1086 user1, token1 = generate_user()
1087 user2, token2 = generate_user()
1088 user3, token3 = generate_user()
1090 make_friends(user1, user2)
1091 make_friends(user1, user3)
1092 make_friends(user2, user3)
1094 with conversations_session(token1) as c:
1095 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]))
1096 group_chat_id = res.group_chat_id
1097 moderator.approve_group_chat(group_chat_id)
1098 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 1"))
1100 # sole admin can't leave group chat
1101 with pytest.raises(grpc.RpcError) as e:
1102 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
1103 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
1104 assert e.value.details() == "The last admin can't leave a group chat."
1106 with conversations_session(token2) as c:
1107 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
1109 with conversations_session(token3) as c:
1110 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
1112 # sole admin can leave when last in chat
1113 with conversations_session(token1) as c:
1114 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
1117def test_search_messages(db, moderator):
1118 user1, token1 = generate_user()
1119 user2, token2 = generate_user()
1120 user3, token3 = generate_user()
1122 make_friends(user1, user2)
1123 make_friends(user1, user3)
1125 with conversations_session(token1) as c:
1126 # create some threads with messages
1127 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id]))
1128 gc1_id = res.group_chat_id
1129 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gc1_id, text="Test message 1"))
1130 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gc1_id, text="Test message 2"))
1131 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]))
1132 gc2_id = res.group_chat_id
1133 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gc2_id, text="Test group message 3"))
1134 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gc2_id, text="Test group message 4"))
1136 # Approve group chats so they appear in search results
1137 moderator.approve_group_chat(gc1_id)
1138 moderator.approve_group_chat(gc2_id)
1140 with conversations_session(token1) as c:
1141 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="message "))
1142 assert len(res.results) == 4
1143 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="group "))
1144 assert len(res.results) == 2
1145 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="message 5"))
1146 assert len(res.results) == 0
1148 # outside user doesn't get results
1149 with conversations_session(token3) as c:
1150 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="Test message"))
1151 assert len(res.results) == 0
1154def test_search_messages_left_joined(db, moderator):
1155 user1, token1 = generate_user()
1156 user2, token2 = generate_user()
1157 user3, token3 = generate_user()
1158 user4, token4 = generate_user()
1159 make_friends(user1, user2)
1160 make_friends(user1, user3)
1161 make_friends(user1, user4)
1163 with conversations_session(token1) as c:
1164 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user4.id]))
1165 group_chat_id = res.group_chat_id
1166 moderator.approve_group_chat(group_chat_id)
1167 for i in range(10):
1168 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message " + str(i)))
1170 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id))
1171 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 10"))
1172 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="Test message"))
1174 assert len(res.results) == 11
1176 with conversations_session(token3) as c:
1177 # can only see last message after invited
1178 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="Test message"))
1180 assert len(res.results) == 1
1181 assert res.results[0].message.text.text == "Test message 10"
1183 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
1185 with conversations_session(token1) as c:
1186 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 11"))
1187 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 12"))
1188 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id))
1189 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 13"))
1190 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 14"))
1192 with conversations_session(token3) as c:
1193 # can only see last message after invited
1194 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="Test message"))
1195 assert len(res.results) == 3
1196 assert res.results[0].message.text.text == "Test message 14"
1197 assert res.results[1].message.text.text == "Test message 13"
1198 assert res.results[2].message.text.text == "Test message 10"
1201def test_admin_behaviour(db, moderator):
1202 user1, token1 = generate_user()
1203 user2, token2 = generate_user()
1204 user3, token3 = generate_user()
1206 make_friends(user1, user2)
1207 make_friends(user1, user3)
1208 make_friends(user2, user3)
1210 with conversations_session(token1) as c:
1211 gcid = c.CreateGroupChat(
1212 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])
1213 ).group_chat_id
1214 moderator.approve_group_chat(gcid)
1215 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user2.id))
1216 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1217 assert len(res.admin_user_ids) == 2
1218 assert user1.id in res.admin_user_ids
1219 assert user2.id in res.admin_user_ids
1221 with conversations_session(token3) as c:
1222 with pytest.raises(grpc.RpcError) as e:
1223 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user3.id))
1224 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
1225 with pytest.raises(grpc.RpcError) as e:
1226 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user1.id))
1227 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
1228 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1229 assert len(res.admin_user_ids) == 2
1230 assert user1.id in res.admin_user_ids
1231 assert user2.id in res.admin_user_ids
1233 with conversations_session(token2) as c:
1234 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user3.id))
1235 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1236 assert len(res.admin_user_ids) == 3
1237 assert user1.id in res.admin_user_ids
1238 assert user2.id in res.admin_user_ids
1239 assert user3.id in res.admin_user_ids
1241 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user1.id))
1242 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1243 assert len(res.admin_user_ids) == 2
1244 assert user2.id in res.admin_user_ids
1245 assert user3.id in res.admin_user_ids
1247 with conversations_session(token1) as c:
1248 with pytest.raises(grpc.RpcError):
1249 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user1.id))
1250 with pytest.raises(grpc.RpcError):
1251 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user3.id))
1252 with pytest.raises(grpc.RpcError):
1253 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user2.id))
1254 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1255 assert len(res.admin_user_ids) == 2
1256 assert user2.id in res.admin_user_ids
1257 assert user3.id in res.admin_user_ids
1259 with conversations_session(token2) as c:
1260 # can demote self if there are other admins
1261 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user2.id))
1262 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1263 assert len(res.admin_user_ids) == 1
1264 assert user3.id in res.admin_user_ids
1266 with conversations_session(token3) as c:
1267 with pytest.raises(grpc.RpcError) as e:
1268 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user3.id))
1269 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
1270 assert e.value.details() == "You can't remove the last admin."
1271 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1272 assert len(res.admin_user_ids) == 1
1273 assert user3.id in res.admin_user_ids
1275 # last admin can't leave
1276 with pytest.raises(grpc.RpcError) as e:
1277 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=gcid))
1278 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
1279 assert e.value.details() == "The last admin can't leave a group chat."
1281 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user1.id))
1283 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=gcid))
1285 with conversations_session(token2) as c:
1286 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=gcid))
1288 # last participant must be admin but can leave to orphan chat
1289 with conversations_session(token1) as c:
1290 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=gcid))
1293def test_add_remove_admin_failures(db):
1294 user1, token1 = generate_user()
1295 user2, token2 = generate_user()
1296 user3, token3 = generate_user()
1297 user4, token4 = generate_user()
1298 user5, token5 = generate_user()
1300 make_friends(user1, user2)
1301 make_friends(user1, user3)
1302 make_friends(user1, user4)
1303 make_friends(user1, user5)
1305 with conversations_session(token1) as c:
1306 gcid = c.CreateGroupChat(
1307 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id, user4.id, user5.id])
1308 ).group_chat_id
1310 make_user_invisible(user3.id)
1311 make_user_block(user1, user4)
1312 make_user_block(user5, user1)
1314 # make non-existent user admin
1315 with pytest.raises(grpc.RpcError) as e:
1316 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=999))
1317 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1318 assert e.value.details() == "Couldn't find that user."
1320 # make invisible user admin
1321 with pytest.raises(grpc.RpcError) as e:
1322 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user3.id))
1323 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1324 assert e.value.details() == "Couldn't find that user."
1326 # make blocked user admin
1327 with pytest.raises(grpc.RpcError) as e:
1328 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user4.id))
1329 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1330 assert e.value.details() == "Couldn't find that user."
1332 # make blocking user admin
1333 with pytest.raises(grpc.RpcError) as e:
1334 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user5.id))
1335 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1336 assert e.value.details() == "Couldn't find that user."
1338 with session_scope() as session:
1339 subscriptions = (
1340 session.execute(
1341 select(GroupChatSubscription)
1342 .where(GroupChatSubscription.group_chat_id == gcid)
1343 .where(GroupChatSubscription.role == GroupChatRole.participant)
1344 )
1345 .scalars()
1346 .all()
1347 )
1349 for subscription in subscriptions:
1350 subscription.role = GroupChatRole.admin
1352 with conversations_session(token1) as c:
1353 # remove non-existent user admin
1354 with pytest.raises(grpc.RpcError) as e:
1355 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=999))
1356 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1357 assert e.value.details() == "Couldn't find that user."
1359 # remove invisible admin
1360 with pytest.raises(grpc.RpcError) as e:
1361 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user3.id))
1362 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1363 assert e.value.details() == "Couldn't find that user."
1365 # remove blocked admin
1366 with pytest.raises(grpc.RpcError) as e:
1367 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user4.id))
1368 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1369 assert e.value.details() == "Couldn't find that user."
1371 # remove blocking admin
1372 with pytest.raises(grpc.RpcError) as e:
1373 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user5.id))
1374 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1375 assert e.value.details() == "Couldn't find that user."
1378def test_last_seen(db, moderator):
1379 user1, token1 = generate_user()
1380 user2, token2 = generate_user()
1381 user3, token3 = generate_user()
1383 make_friends(user1, user2)
1384 make_friends(user1, user3)
1385 make_friends(user2, user3)
1387 with conversations_session(token3) as c:
1388 # this is just here to mess up any issues we get if we pretend there's only one group chat ever
1389 gcid_distraction = c.CreateGroupChat(
1390 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user1.id])
1391 ).group_chat_id
1392 moderator.approve_group_chat(gcid_distraction)
1394 with conversations_session(token1) as c:
1395 gcid = c.CreateGroupChat(
1396 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])
1397 ).group_chat_id
1398 moderator.approve_group_chat(gcid)
1400 message_ids = []
1402 for i in range(6):
1403 c.SendMessage(
1404 conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text=f"gibberish message... {i}")
1405 )
1406 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text=f"test message {i}"))
1407 c.SendMessage(
1408 conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text=f"gibberish message {i}")
1409 )
1411 message_ids.append(
1412 c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)).latest_message.message_id
1413 )
1415 # messages are automatically marked as seen when you send a new message
1416 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1417 assert res.unseen_message_count == 0
1419 with conversations_session(token2) as c:
1420 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1421 # created + 6 normal
1422 assert res.unseen_message_count == 7
1424 backward_offset = 3
1425 c.MarkLastSeenGroupChat(
1426 conversations_pb2.MarkLastSeenGroupChatReq(
1427 group_chat_id=gcid, last_seen_message_id=message_ids[-backward_offset - 1]
1428 )
1429 )
1431 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1432 assert res.unseen_message_count == backward_offset
1434 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="test message ..."))
1436 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1437 assert res.unseen_message_count == 0
1439 with conversations_session(token3) as c:
1440 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1441 # created + 7 normal
1442 assert res.unseen_message_count == 8
1444 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="test message ..."))
1446 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1447 assert res.unseen_message_count == 0
1450def test_mark_last_seen_clears_notifications(db, moderator):
1451 user1, token1 = generate_user()
1452 user2, token2 = generate_user()
1454 with conversations_session(token1) as c:
1455 gcid = c.CreateGroupChat(
1456 conversations_pb2.CreateGroupChatReq(
1457 recipient_user_ids=[user2.id], title=wrappers_pb2.StringValue(value="Test chat")
1458 )
1459 ).group_chat_id
1461 moderator.approve_group_chat(gcid)
1463 with conversations_session(token1) as c:
1464 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello"))
1466 process_jobs()
1468 def unseen_notification_count(user_id):
1469 with session_scope() as session:
1470 return session.execute(
1471 select(func.count())
1472 .select_from(Notification)
1473 .where(Notification.user_id == user_id)
1474 .where(Notification.key == str(gcid))
1475 .where(Notification.is_seen == False)
1476 ).scalar_one()
1478 assert unseen_notification_count(user2.id) > 0
1480 with conversations_session(token2) as c:
1481 c.MarkLastSeenGroupChat(conversations_pb2.MarkLastSeenGroupChatReq(group_chat_id=gcid, last_seen_message_id=1))
1483 assert unseen_notification_count(user2.id) == 0
1486def test_mark_last_seen_clears_missed_messages_notification(db, moderator, timewarp: Timewarp):
1487 """
1488 Regression test: chat__missed_messages is a summary keyed with "" rather than a chat id, so it
1489 needs to be marked seen separately from the per-chat chat__message notifications.
1490 """
1491 user1, token1 = generate_user()
1492 user2, token2 = generate_user()
1494 # this notification is email-only by default, and the in-app feed only shows push-enabled ones
1495 with notifications_session(token2) as n:
1496 n.SetNotificationSettings(
1497 notifications_pb2.SetNotificationSettingsReq(
1498 preferences=[
1499 notifications_pb2.SingleNotificationPreference(
1500 topic=NotificationTopicAction.chat__missed_messages.topic,
1501 action=NotificationTopicAction.chat__missed_messages.action,
1502 delivery_method="push",
1503 enabled=True,
1504 )
1505 ]
1506 )
1507 )
1509 with conversations_session(token1) as c:
1510 gcid = c.CreateGroupChat(
1511 conversations_pb2.CreateGroupChatReq(
1512 recipient_user_ids=[user2.id], title=wrappers_pb2.StringValue(value="Test chat")
1513 )
1514 ).group_chat_id
1516 moderator.approve_group_chat(gcid)
1518 with conversations_session(token1) as c:
1519 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello!"))
1521 # the job only picks up messages that have been unseen for five minutes
1522 timewarp.advance(MISSED_MESSAGES_DELAY)
1523 send_message_notifications(empty_pb2.Empty())
1524 process_jobs()
1526 def unseen_missed_messages():
1527 with notifications_session(token2) as n:
1528 res = n.ListNotifications(notifications_pb2.ListNotificationsReq(only_unread=True))
1529 return [
1530 notification
1531 for notification in res.notifications
1532 if notification.topic == NotificationTopicAction.chat__missed_messages.topic
1533 and notification.action == NotificationTopicAction.chat__missed_messages.action
1534 ]
1536 assert len(unseen_missed_messages()) == 1
1538 with conversations_session(token2) as c:
1539 c.MarkLastSeenGroupChat(conversations_pb2.MarkLastSeenGroupChatReq(group_chat_id=gcid, last_seen_message_id=1))
1541 assert unseen_missed_messages() == []
1544def test_one_dm_per_pair(db, moderator):
1545 user1, token1 = generate_user()
1546 user2, token2 = generate_user()
1547 user3, token3 = generate_user()
1549 make_friends(user1, user2)
1550 make_friends(user1, user3)
1551 make_friends(user2, user3)
1553 with conversations_session(token1) as c:
1554 # create DM with user 2
1555 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id]))
1556 assert res.is_dm
1557 dm_with_user2 = res.group_chat_id
1559 # create DM with user 3
1560 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id]))
1561 assert res.is_dm
1562 dm_with_user3 = res.group_chat_id
1564 # can't create another group chat with just user 2
1565 with pytest.raises(grpc.RpcError) as e:
1566 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id]))
1567 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
1569 # can't create another group chat with just user 3
1570 with pytest.raises(grpc.RpcError) as e:
1571 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id]))
1572 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
1574 # can create joined group chat
1575 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]))
1576 assert not res.is_dm
1578 # Approve the DMs so user2 can see them (otherwise they're SHADOWED and only visible to creator)
1579 moderator.approve_group_chat(dm_with_user2)
1581 with conversations_session(token2) as c:
1582 # can create DM with user 3
1583 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id]))
1584 assert res.is_dm
1586 # can't create another group chat with just user 1 (DM was approved, so user2 can see it)
1587 with pytest.raises(grpc.RpcError) as e:
1588 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user1.id]))
1589 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
1592def test_GetDirectMessage(db):
1593 user1, token1 = generate_user()
1594 user2, token2 = generate_user()
1595 user3, token3 = generate_user()
1597 make_friends(user1, user2)
1598 make_friends(user1, user3)
1599 make_friends(user2, user3)
1601 with conversations_session(token1) as c:
1602 # no group chat with user 2
1603 with pytest.raises(grpc.RpcError) as e:
1604 res = c.GetDirectMessage(conversations_pb2.GetDirectMessageReq(user_id=user2.id))
1605 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1607 # no group chat with nor user 3
1608 with pytest.raises(grpc.RpcError) as e:
1609 res = c.GetDirectMessage(conversations_pb2.GetDirectMessageReq(user_id=user3.id))
1610 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1612 # create DM with user 2
1613 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id]))
1614 assert res.is_dm
1615 gcid = res.group_chat_id
1617 # now should exist
1618 res = c.GetDirectMessage(conversations_pb2.GetDirectMessageReq(user_id=user2.id))
1619 assert res.group_chat_id == gcid
1620 assert not res.is_archived
1622 # create DM with user 3
1623 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id]))
1624 assert res.is_dm
1626 # can create joined group chat
1627 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]))
1628 assert not res.is_dm
1630 with conversations_session(token2) as c:
1631 # can create DM with user 3
1632 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id]))
1633 assert res.is_dm
1634 assert res.can_message
1635 gcid = res.group_chat_id
1637 # DM with 3 should exist, but can't message after being blocked
1638 make_user_block(user3, user2)
1639 res = c.GetDirectMessage(conversations_pb2.GetDirectMessageReq(user_id=user3.id))
1640 assert res.group_chat_id == gcid
1641 assert not res.can_message
1644def test_total_unseen(db, moderator):
1645 user1, token1 = generate_user()
1646 user2, token2 = generate_user()
1647 user3, token3 = generate_user()
1649 # distractions
1650 user4, token4 = generate_user()
1652 make_friends(user1, user2)
1653 make_friends(user1, user3)
1654 make_friends(user2, user3)
1656 # distractions
1657 make_friends(user1, user4)
1659 with conversations_session(token1) as c:
1660 # distractions
1661 gcid_distraction = c.CreateGroupChat(
1662 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user4.id])
1663 ).group_chat_id
1664 moderator.approve_group_chat(gcid_distraction)
1665 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text="distraction..."))
1667 gcid = c.CreateGroupChat(
1668 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])
1669 ).group_chat_id
1670 moderator.approve_group_chat(gcid)
1672 for i in range(6):
1673 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text=f"test message {i}"))
1675 # distractions
1676 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text="distraction..."))
1678 # messages are automatically marked as seen when you send a new message
1679 with api_session(token1) as api:
1680 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 0
1682 with api_session(token2) as api:
1683 # chat created + 6 normal messages
1684 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 7
1686 # now leave chat with user2
1687 with conversations_session(token2) as c:
1688 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=gcid))
1690 with api_session(token2) as api:
1691 # seen messages becomes 0 when leaving
1692 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 0
1694 with conversations_session(token1) as c:
1695 # distractions
1696 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text="distraction..."))
1698 # send more stuff without user 2
1699 for i in range(3):
1700 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text=f"test message {i}"))
1702 # distractions
1703 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text="distraction..."))
1705 with api_session(token2) as api:
1706 # seen messages becomes 0 when leaving
1707 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 0
1709 with conversations_session(token1) as c:
1710 # add user 2 back
1711 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=gcid, user_id=user2.id))
1713 # send more stuff with user 2
1714 for i in range(12):
1715 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text=f"test message {i}"))
1717 # distractions
1718 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text="distraction..."))
1720 with api_session(token2) as api:
1721 # joined + 12 normal
1722 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 13
1725def test_left_group_chat_unseen_count_excludes_unreachable_messages(db, moderator):
1726 """
1727 ListGroupChats and GetGroupChat used to count messages sent after the viewer left the chat, which
1728 they can never open. The viewer is removed rather than leaving, because leaving posts a message of
1729 your own, which marks everything up to it seen.
1730 """
1731 user1, token1 = generate_user()
1732 user2, token2 = generate_user()
1733 user3, token3 = generate_user()
1734 make_friends(user1, user2)
1735 make_friends(user2, user3)
1737 with conversations_session(token2) as c:
1738 group_chat_id = c.CreateGroupChat(
1739 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user1.id, user3.id])
1740 ).group_chat_id
1741 moderator.approve_group_chat(group_chat_id)
1742 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="hi"))
1743 c.RemoveGroupChatUser(conversations_pb2.RemoveGroupChatUserReq(group_chat_id=group_chat_id, user_id=user1.id))
1744 for _ in range(3):
1745 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="after you left"))
1747 # chat created, "hi", and the removal notice; the three messages sent afterwards are out of reach
1748 with api_session(token1) as api:
1749 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 3
1751 with conversations_session(token1) as c:
1752 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
1753 (listed,) = [gc for gc in res.group_chats if gc.group_chat_id == group_chat_id]
1754 assert listed.unseen_message_count == 3
1755 assert c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id)).unseen_message_count == 3
1758def test_left_group_chat_can_be_marked_seen(db, moderator):
1759 """
1760 MarkLastSeenGroupChat used to require a subscription with left == None, so a chat you were removed
1761 from kept contributing to the unread badge with no way to clear it.
1762 """
1763 user1, token1 = generate_user()
1764 user2, token2 = generate_user()
1765 user3, token3 = generate_user()
1766 make_friends(user1, user2)
1767 make_friends(user2, user3)
1769 with conversations_session(token2) as c:
1770 group_chat_id = c.CreateGroupChat(
1771 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user1.id, user3.id])
1772 ).group_chat_id
1773 moderator.approve_group_chat(group_chat_id)
1774 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="hi"))
1775 c.RemoveGroupChatUser(conversations_pb2.RemoveGroupChatUserReq(group_chat_id=group_chat_id, user_id=user1.id))
1776 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="after you left"))
1778 with api_session(token1) as api:
1779 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 3
1781 with conversations_session(token1) as c:
1782 latest_message_id = c.GetGroupChat(
1783 conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id)
1784 ).latest_message.message_id
1785 c.MarkLastSeenGroupChat(
1786 conversations_pb2.MarkLastSeenGroupChatReq(
1787 group_chat_id=group_chat_id, last_seen_message_id=latest_message_id
1788 )
1789 )
1791 assert c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id)).unseen_message_count == 0
1793 with pytest.raises(grpc.RpcError) as e:
1794 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="let me back in"))
1795 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1797 with api_session(token1) as api:
1798 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 0
1801def test_rejoined_group_chat_ping_reads_newest_subscription(db, moderator):
1802 """
1803 Rejoining a chat leaves the earlier subscription behind with its own last-seen state. Ping used to
1804 match on any of the viewer's subscriptions, so the stale one kept the badge up forever.
1805 """
1806 user1, token1 = generate_user()
1807 user2, token2 = generate_user()
1808 user3, token3 = generate_user()
1809 make_friends(user1, user2)
1810 make_friends(user2, user3)
1812 with conversations_session(token2) as c:
1813 group_chat_id = c.CreateGroupChat(
1814 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user1.id, user3.id])
1815 ).group_chat_id
1816 moderator.approve_group_chat(group_chat_id)
1817 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="hi"))
1818 # removed rather than leaving, so user1's first subscription is left behind with unread messages
1819 c.RemoveGroupChatUser(conversations_pb2.RemoveGroupChatUserReq(group_chat_id=group_chat_id, user_id=user1.id))
1820 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="while you were away"))
1821 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user1.id))
1822 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="welcome back"))
1824 # only the invite notice and "welcome back" are in reach of the newest subscription
1825 with api_session(token1) as api:
1826 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 2
1828 with conversations_session(token1) as c:
1829 latest_message_id = c.GetGroupChat(
1830 conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id)
1831 ).latest_message.message_id
1832 c.MarkLastSeenGroupChat(
1833 conversations_pb2.MarkLastSeenGroupChatReq(
1834 group_chat_id=group_chat_id, last_seen_message_id=latest_message_id
1835 )
1836 )
1838 with api_session(token1) as api:
1839 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 0
1842def test_rejoined_group_chat_only_unseen_matches_the_badge(db, moderator):
1843 """
1844 GetGroupChatMessages matched only_unseen against every one of the viewer's subscriptions, so the
1845 abandoned one served up its unread messages even though the badge no longer counts them.
1846 """
1847 user1, token1 = generate_user()
1848 user2, token2 = generate_user()
1849 user3, token3 = generate_user()
1850 make_friends(user1, user2)
1851 make_friends(user2, user3)
1853 with conversations_session(token2) as c:
1854 group_chat_id = c.CreateGroupChat(
1855 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user1.id, user3.id])
1856 ).group_chat_id
1857 moderator.approve_group_chat(group_chat_id)
1858 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="hi"))
1859 c.RemoveGroupChatUser(conversations_pb2.RemoveGroupChatUserReq(group_chat_id=group_chat_id, user_id=user1.id))
1860 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user1.id))
1861 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="welcome back"))
1863 with api_session(token1) as api:
1864 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 2
1866 with conversations_session(token1) as c:
1867 unseen = c.GetGroupChatMessages(
1868 conversations_pb2.GetGroupChatMessagesReq(group_chat_id=group_chat_id, only_unseen=True)
1869 ).messages
1870 assert [m.WhichOneof("content") for m in unseen] == ["text", "user_invited"]
1871 assert unseen[0].text.text == "welcome back"
1873 # the earlier stint is still readable, it just isn't unread
1874 all_messages = c.GetGroupChatMessages(
1875 conversations_pb2.GetGroupChatMessagesReq(group_chat_id=group_chat_id)
1876 ).messages
1877 assert [m.text.text for m in all_messages if m.WhichOneof("content") == "text"] == ["welcome back", "hi"]
1880def test_rejoined_group_chat_archived_filter_reads_newest_subscription(db, moderator):
1881 """
1882 ListGroupChats applied the archived filter before picking the newest subscription, so a chat
1883 archived before the viewer was removed stayed in their archived list off the abandoned
1884 subscription, where nothing could ever mark it seen.
1885 """
1886 user1, token1 = generate_user()
1887 user2, token2 = generate_user()
1888 user3, token3 = generate_user()
1889 make_friends(user1, user2)
1890 make_friends(user2, user3)
1892 with conversations_session(token2) as c:
1893 group_chat_id = c.CreateGroupChat(
1894 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user1.id, user3.id])
1895 ).group_chat_id
1896 moderator.approve_group_chat(group_chat_id)
1897 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="hi"))
1899 with conversations_session(token1) as c:
1900 c.SetGroupChatArchiveStatus(
1901 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=group_chat_id, is_archived=True)
1902 )
1904 with conversations_session(token2) as c:
1905 c.RemoveGroupChatUser(conversations_pb2.RemoveGroupChatUserReq(group_chat_id=group_chat_id, user_id=user1.id))
1906 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user1.id))
1908 with conversations_session(token1) as c:
1909 archived = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=True)).group_chats
1910 assert [gc.group_chat_id for gc in archived] == []
1912 (listed,) = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=False)).group_chats
1913 assert listed.group_chat_id == group_chat_id
1914 assert not listed.is_archived
1917def test_regression_ListGroupChats_pagination(db, moderator, monkeypatch):
1918 user1, token1 = generate_user()
1919 user2, token2 = generate_user()
1920 user3, token3 = generate_user()
1922 make_friends(user1, user2)
1923 make_friends(user1, user3)
1925 # two full pages and a partial one, which is the shape that lost chats, without paying for a
1926 # chat per row of a production-sized page
1927 monkeypatch.setattr(conversations, "DEFAULT_PAGINATION_LENGTH", 3)
1929 with conversations_session(token1) as c:
1930 # tuples of (group_chat_id, message_id)
1931 group_chat_and_message_ids = []
1932 for i in range(7):
1933 res1 = c.CreateGroupChat(
1934 conversations_pb2.CreateGroupChatReq(
1935 recipient_user_ids=[user2.id, user3.id], title=wrappers_pb2.StringValue(value=f"Chat {i}")
1936 )
1937 )
1939 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res1.group_chat_id, text=f"Test message {i}"))
1941 res2 = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=res1.group_chat_id))
1943 group_chat_and_message_ids.append((res2.group_chat_id, res2.latest_message.message_id))
1944 moderator.approve_group_chat(res1.group_chat_id)
1946 seen_group_chat_ids = []
1948 pages = 0
1949 last_message_id = 0
1950 more = True
1951 while more:
1952 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(last_message_id=last_message_id))
1953 last_message_id = res.last_message_id
1954 more = not res.no_more
1955 pages += 1
1957 seen_group_chat_ids.extend([chat.group_chat_id for chat in res.group_chats])
1959 assert pages == 3, "The chats didn't come back over several pages, so nothing was paginated"
1960 assert set(seen_group_chat_ids) == {x[0] for x in group_chat_and_message_ids}, "Not all group chats returned"
1963def test_muting(db, moderator):
1964 user1, token1 = generate_user()
1965 user2, token2 = generate_user()
1966 user3, token3 = generate_user()
1968 make_friends(user1, user2)
1969 make_friends(user1, user3)
1970 make_friends(user2, user3)
1972 with conversations_session(token3) as c:
1973 # this is just here to mess up any issues we get if we pretend there's only one group chat ever
1974 gcid_distraction = c.CreateGroupChat(
1975 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user1.id])
1976 ).group_chat_id
1977 moderator.approve_group_chat(gcid_distraction)
1979 with conversations_session(token1) as c:
1980 gcid = c.CreateGroupChat(
1981 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])
1982 ).group_chat_id
1983 moderator.approve_group_chat(gcid)
1985 with conversations_session(token2) as c:
1986 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1987 assert not res.mute_info.muted
1988 assert not res.mute_info.HasField("muted_until")
1990 c.MuteGroupChat(conversations_pb2.MuteGroupChatReq(group_chat_id=gcid, forever=True))
1991 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1992 assert res.mute_info.muted
1993 assert not res.mute_info.HasField("muted_until")
1995 c.MuteGroupChat(conversations_pb2.MuteGroupChatReq(group_chat_id=gcid, unmute=True))
1996 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1997 assert not res.mute_info.muted
1998 assert not res.mute_info.HasField("muted_until")
2000 c.MuteGroupChat(
2001 conversations_pb2.MuteGroupChatReq(
2002 group_chat_id=gcid, for_duration=Duration_from_timedelta(timedelta(hours=2))
2003 )
2004 )
2005 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
2006 assert res.mute_info.muted
2007 assert res.mute_info.HasField("muted_until")
2008 assert to_aware_datetime(res.mute_info.muted_until) >= now() + timedelta(hours=1, minutes=59)
2009 assert to_aware_datetime(res.mute_info.muted_until) <= now() + timedelta(hours=2, minutes=1)
2012def test_archiving(db, moderator):
2013 """Test SetGroupChatArchiveStatus RPC with GetGroupChat and ListGroupChats"""
2014 user1, token1 = generate_user()
2015 user2, token2 = generate_user()
2016 user3, token3 = generate_user()
2018 make_friends(user1, user2)
2019 make_friends(user1, user3)
2020 make_friends(user2, user3)
2022 # Create a distraction chat to ensure we're testing the right one
2023 with conversations_session(token3) as c:
2024 gcid_distraction = c.CreateGroupChat(
2025 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user1.id])
2026 ).group_chat_id
2027 moderator.approve_group_chat(gcid_distraction)
2029 with conversations_session(token1) as c:
2030 gcid = c.CreateGroupChat(
2031 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])
2032 ).group_chat_id
2033 moderator.approve_group_chat(gcid)
2035 # Test basic archive/unarchive functionality
2036 with conversations_session(token2) as c:
2037 # Initially not archived
2038 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
2039 assert not res.is_archived
2041 # Archive the chat
2042 archive_res = c.SetGroupChatArchiveStatus(
2043 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True)
2044 )
2045 assert archive_res.group_chat_id == gcid
2046 assert archive_res.is_archived
2048 # Verify archived via GetGroupChat
2049 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
2050 assert res.is_archived
2052 # Unarchive the chat
2053 archive_res = c.SetGroupChatArchiveStatus(
2054 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=False)
2055 )
2056 assert archive_res.group_chat_id == gcid
2057 assert not archive_res.is_archived
2059 # Verify unarchived via GetGroupChat
2060 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
2061 assert not res.is_archived
2064def test_archiving_per_user(db, moderator):
2065 """Test that archiving is per-user - one user archiving doesn't affect others"""
2066 user1, token1 = generate_user()
2067 user2, token2 = generate_user()
2069 make_friends(user1, user2)
2071 with conversations_session(token1) as c:
2072 gcid = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])).group_chat_id
2073 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello"))
2074 moderator.approve_group_chat(gcid)
2076 # User1 archives the chat
2077 with conversations_session(token1) as c:
2078 c.SetGroupChatArchiveStatus(
2079 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True)
2080 )
2081 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
2082 assert res.is_archived
2084 # User2 should NOT see it as archived
2085 with conversations_session(token2) as c:
2086 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
2087 assert not res.is_archived
2089 # User2 archives it too
2090 with conversations_session(token2) as c:
2091 c.SetGroupChatArchiveStatus(
2092 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True)
2093 )
2094 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
2095 assert res.is_archived
2097 # User1 unarchives - user2 should still see it as archived
2098 with conversations_session(token1) as c:
2099 c.SetGroupChatArchiveStatus(
2100 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=False)
2101 )
2102 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
2103 assert not res.is_archived
2105 with conversations_session(token2) as c:
2106 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
2107 assert res.is_archived
2110def test_archiving_with_list_group_chats(db, moderator):
2111 """Test archive filtering with ListGroupChats endpoint"""
2112 user1, token1 = generate_user()
2113 user2, token2 = generate_user()
2114 user3, token3 = generate_user()
2116 make_friends(user1, user2)
2117 make_friends(user1, user3)
2119 with conversations_session(token1) as c:
2120 # Create 3 chats
2121 gcid1 = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])).group_chat_id
2122 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid1, text="Chat 1"))
2123 moderator.approve_group_chat(gcid1)
2125 gcid2 = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id])).group_chat_id
2126 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid2, text="Chat 2"))
2127 moderator.approve_group_chat(gcid2)
2129 gcid3 = c.CreateGroupChat(
2130 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])
2131 ).group_chat_id
2132 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid3, text="Chat 3"))
2133 moderator.approve_group_chat(gcid3)
2135 with conversations_session(token1) as c:
2136 # Initially all 3 chats should be visible
2137 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
2138 assert len(res.group_chats) == 3
2140 # All should be non-archived
2141 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=False))
2142 assert len(res.group_chats) == 3
2144 # None should be archived
2145 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=True))
2146 assert len(res.group_chats) == 0
2148 # Archive chat 1
2149 c.SetGroupChatArchiveStatus(
2150 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid1, is_archived=True)
2151 )
2153 # Without filter, still see all 3
2154 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
2155 assert len(res.group_chats) == 3
2157 # Non-archived should show 2
2158 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=False))
2159 assert len(res.group_chats) == 2
2160 assert gcid1 not in [gc.group_chat_id for gc in res.group_chats]
2162 # Archived should show 1
2163 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=True))
2164 assert len(res.group_chats) == 1
2165 assert res.group_chats[0].group_chat_id == gcid1
2166 assert res.group_chats[0].is_archived
2168 # Archive chat 2 as well
2169 c.SetGroupChatArchiveStatus(
2170 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid2, is_archived=True)
2171 )
2173 # Non-archived should show 1
2174 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=False))
2175 assert len(res.group_chats) == 1
2176 assert res.group_chats[0].group_chat_id == gcid3
2178 # Archived should show 2
2179 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=True))
2180 assert len(res.group_chats) == 2
2181 archived_ids = {gc.group_chat_id for gc in res.group_chats}
2182 assert archived_ids == {gcid1, gcid2}
2184 # Unarchive chat 1
2185 c.SetGroupChatArchiveStatus(
2186 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid1, is_archived=False)
2187 )
2189 # Non-archived should show 2
2190 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=False))
2191 assert len(res.group_chats) == 2
2193 # Archived should show 1
2194 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=True))
2195 assert len(res.group_chats) == 1
2196 assert res.group_chats[0].group_chat_id == gcid2
2199def test_archiving_chat_not_found(db, moderator):
2200 """Test that archiving a non-existent or non-accessible chat fails"""
2201 user1, token1 = generate_user()
2202 user2, token2 = generate_user()
2203 user3, token3 = generate_user()
2205 make_friends(user1, user2)
2207 with conversations_session(token1) as c:
2208 gcid = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])).group_chat_id
2209 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello"))
2210 moderator.approve_group_chat(gcid)
2212 # User3 is not in the chat - should get NOT_FOUND
2213 with conversations_session(token3) as c:
2214 with pytest.raises(grpc.RpcError) as e:
2215 c.SetGroupChatArchiveStatus(
2216 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True)
2217 )
2218 assert e.value.code() == grpc.StatusCode.NOT_FOUND
2220 # Non-existent chat ID should fail
2221 with conversations_session(token1) as c:
2222 with pytest.raises(grpc.RpcError) as e:
2223 c.SetGroupChatArchiveStatus(
2224 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=99999, is_archived=True)
2225 )
2226 assert e.value.code() == grpc.StatusCode.NOT_FOUND
2229def test_archiving_after_leaving_chat(db, moderator):
2230 """Test that you can't archive a chat after leaving it"""
2231 user1, token1 = generate_user()
2232 user2, token2 = generate_user()
2233 user3, token3 = generate_user()
2235 make_friends(user1, user2)
2236 make_friends(user1, user3)
2237 make_friends(user2, user3)
2239 with conversations_session(token1) as c:
2240 gcid = c.CreateGroupChat(
2241 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])
2242 ).group_chat_id
2243 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello"))
2244 moderator.approve_group_chat(gcid)
2246 # User2 leaves the chat
2247 with conversations_session(token2) as c:
2248 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=gcid))
2250 # User2 should not be able to archive it now
2251 with conversations_session(token2) as c:
2252 with pytest.raises(grpc.RpcError) as e:
2253 c.SetGroupChatArchiveStatus(
2254 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True)
2255 )
2256 assert e.value.code() == grpc.StatusCode.NOT_FOUND
2259def test_archiving_dm(db, moderator):
2260 """Test that archiving works for DMs as well as group chats"""
2261 user1, token1 = generate_user()
2262 user2, token2 = generate_user()
2264 make_friends(user1, user2)
2266 with conversations_session(token1) as c:
2267 # Create a DM
2268 gcid = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])).group_chat_id
2269 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello"))
2270 moderator.approve_group_chat(gcid)
2272 # Verify it's a DM
2273 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
2274 assert res.is_dm
2275 assert not res.is_archived
2277 # Archive the DM
2278 c.SetGroupChatArchiveStatus(
2279 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True)
2280 )
2282 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
2283 assert res.is_archived
2285 # Unarchive it
2286 c.SetGroupChatArchiveStatus(
2287 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=False)
2288 )
2290 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
2291 assert not res.is_archived
2294def test_archiving_idempotent(db, moderator):
2295 """Test that archiving/unarchiving is idempotent"""
2296 user1, token1 = generate_user()
2297 user2, token2 = generate_user()
2299 make_friends(user1, user2)
2301 with conversations_session(token1) as c:
2302 gcid = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])).group_chat_id
2303 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello"))
2304 moderator.approve_group_chat(gcid)
2306 # Archive twice - should work without error
2307 c.SetGroupChatArchiveStatus(
2308 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True)
2309 )
2310 c.SetGroupChatArchiveStatus(
2311 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True)
2312 )
2313 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
2314 assert res.is_archived
2316 # Unarchive twice - should work without error
2317 c.SetGroupChatArchiveStatus(
2318 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=False)
2319 )
2320 c.SetGroupChatArchiveStatus(
2321 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=False)
2322 )
2323 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
2324 assert not res.is_archived
2327def test_chat_notifications(db, moderator):
2328 user1, token1 = generate_user()
2329 user2, token2 = generate_user()
2330 # notifs off
2331 user3, token3 = generate_user()
2332 user4, token4 = generate_user()
2333 user5, token5 = generate_user()
2334 user6, token6 = generate_user()
2336 make_friends(user1, user2)
2337 make_friends(user1, user3)
2338 make_friends(user1, user4)
2339 make_friends(user4, user5)
2340 make_friends(user4, user6)
2342 # have some of them enable/disable notifs
2343 topic_action = NotificationTopicAction.chat__message
2344 for token, enabled in [
2345 (token1, True),
2346 (token2, True),
2347 (token3, False),
2348 (token4, True),
2349 (token5, True),
2350 (token6, True),
2351 ]:
2352 with notifications_session(token) as notifications:
2353 notifications.SetNotificationSettings(
2354 notifications_pb2.SetNotificationSettingsReq(
2355 preferences=[
2356 notifications_pb2.SingleNotificationPreference(
2357 topic=topic_action.topic,
2358 action=topic_action.action,
2359 delivery_method=delivery_method,
2360 enabled=enabled,
2361 )
2362 for delivery_method in ["push", "email", "digest"]
2363 ],
2364 )
2365 )
2367 group_chat_id = None
2369 def send_msg(c, i):
2370 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text=f"Test message {i}"))
2372 with conversations_session(token1) as c:
2373 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id, user4.id]))
2374 group_chat_id = res.group_chat_id
2375 moderator.approve_group_chat(group_chat_id)
2376 c.EditGroupChat(
2377 conversations_pb2.EditGroupChatReq(
2378 group_chat_id=group_chat_id, only_admins_invite=wrappers_pb2.BoolValue(value=False)
2379 )
2380 )
2381 send_msg(c, i=1)
2382 send_msg(c, i=2)
2384 with conversations_session(token4) as c:
2385 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user5.id))
2386 send_msg(c, i=3)
2387 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user6.id))
2388 send_msg(c, i=4)
2389 send_msg(c, i=5)
2391 with conversations_session(token3) as c:
2392 send_msg(c, i=6)
2393 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
2395 with conversations_session(token2) as c:
2396 send_msg(c, i=7)
2397 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
2399 with conversations_session(token6) as c:
2400 send_msg(c, i=8)
2402 # go through all bg jobs
2403 while process_job():
2404 pass
2406 # now check notifs...
2407 expected_notifs = [
2408 (user1, "user1", [3, 4, 5, 6, 7, 8]),
2409 (user2, "user2", [1, 2, 3, 4, 5, 6]),
2410 (user3, "user3", []), # notifs off
2411 (user4, "user4", [1, 2, 6, 7, 8]),
2412 (user5, "user5", [3, 4, 5, 6, 7, 8]),
2413 (user6, "user6", [4, 5, 6, 7]),
2414 ]
2416 with session_scope() as session:
2417 for user, label, expected_msgs in expected_notifs:
2418 deliv = (
2419 session.execute(
2420 select(Notification.data)
2421 .join(NotificationDelivery, NotificationDelivery.notification_id == Notification.id)
2422 .where(Notification.user_id == user.id)
2423 .where(Notification.topic_action == topic_action)
2424 .where(NotificationDelivery.delivery_type == NotificationDeliveryType.push)
2425 .order_by(Notification.created)
2426 )
2427 .scalars()
2428 .all()
2429 )
2431 def parse_message_payload(data):
2432 return notification_data_pb2.ChatMessage.FromString(data).text
2434 contents = [parse_message_payload(d) for d in deliv]
2436 print(contents)
2438 assert [f"Test message {i}" for i in expected_msgs] == contents, f"Wrong messages for {label}"
2441def test_incomplete_profile(db):
2442 user1, token1 = generate_user(complete_profile=True)
2443 user2, token2 = generate_user(complete_profile=False)
2444 user3, token3 = generate_user(complete_profile=True)
2445 make_friends(user1, user2)
2446 make_friends(user1, user3)
2448 # user 1 can make a chat
2449 with conversations_session(token1) as c:
2450 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id]))
2451 group_chat_id = res.group_chat_id
2452 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 1"))
2453 res = c.GetGroupChatMessages(conversations_pb2.GetGroupChatMessagesReq(group_chat_id=group_chat_id))
2454 assert res.messages[0].text.text == "Test message 1"
2455 assert to_aware_datetime(res.messages[0].time) <= now()
2456 assert res.messages[0].author_user_id == user1.id
2458 # user 2 cannot
2459 with conversations_session(token2) as c:
2460 with pytest.raises(grpc.RpcError) as e:
2461 c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id]))
2462 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
2463 assert e.value.details() == "You have to complete your profile before you can send a message."