Coverage for app/backend/src/tests/test_conversations.py: 100%
1276 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-14 11:44 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-14 11:44 +0000
1from datetime import timedelta
3import grpc
4import pytest
5from google.protobuf import wrappers_pb2
6from sqlalchemy import func, select
8from couchers.db import session_scope
9from couchers.jobs.worker import process_job
10from couchers.models import (
11 GroupChatRole,
12 GroupChatSubscription,
13 Notification,
14 NotificationDelivery,
15 NotificationDeliveryType,
16 NotificationTopicAction,
17 RateLimitAction,
18)
19from couchers.proto import api_pb2, conversations_pb2, notification_data_pb2, notifications_pb2
20from couchers.rate_limits.definitions import RATE_LIMIT_DEFINITIONS, RATE_LIMIT_HOURS
21from couchers.utils import Duration_from_timedelta, now, to_aware_datetime
22from tests.fixtures.db import generate_user, make_friends, make_user_block, make_user_invisible
23from tests.fixtures.misc import EmailCollector, PushCollector, process_jobs
24from tests.fixtures.sessions import api_session, conversations_session, notifications_session
27@pytest.fixture(autouse=True)
28def _(testconfig):
29 pass
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):
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 with conversations_session(token1) as c:
689 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id]))
690 group_chat_id = res.group_chat_id
691 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 1"))
692 res = c.GetGroupChatMessages(conversations_pb2.GetGroupChatMessagesReq(group_chat_id=group_chat_id))
693 assert res.messages[0].text.text == "Test message 1"
694 assert to_aware_datetime(res.messages[0].time) <= now()
695 assert res.messages[0].author_user_id == user1.id
697 # can't send message if not in chat
698 with conversations_session(token3) as c:
699 with pytest.raises(grpc.RpcError) as e:
700 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 2"))
701 assert e.value.code() == grpc.StatusCode.NOT_FOUND
703 make_user_block(user2, user1)
704 with conversations_session(token1) as c:
705 with pytest.raises(grpc.RpcError) as e:
706 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Message after block"))
707 assert e.value.details() == "You can't send a message in this chat."
710def test_send_direct_message(db, moderator, push_collector: PushCollector):
711 user1, token1 = generate_user(complete_profile=True)
712 user2, token2 = generate_user(complete_profile=True)
714 make_friends(user1, user2)
716 message1 = "Hello, user2!"
717 message2 = "One more message."
719 with conversations_session(token1) as c1:
720 # Send a DM from user1 to user2
721 res = c1.SendDirectMessage(conversations_pb2.SendDirectMessageReq(recipient_user_id=user2.id, text=message1))
722 moderator.approve_group_chat(res.group_chat_id)
724 c1.SendDirectMessage(conversations_pb2.SendDirectMessageReq(recipient_user_id=user2.id, text=message2))
726 process_jobs()
728 push = push_collector.pop_for_user(user2.id, last=False)
729 assert push.topic_action == NotificationTopicAction.chat__message.display
730 assert push.content.title == user1.name
731 assert push.content.body == message1
733 push = push_collector.pop_for_user(user2.id, last=True)
734 assert push.topic_action == NotificationTopicAction.chat__message.display
735 assert push.content.title == user1.name
736 assert push.content.body == message2
738 with conversations_session(token2) as c2:
739 # Fetch the chat by ID returned from SendDirectMessage
740 chat = c2.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=res.group_chat_id))
742 assert chat.is_dm
743 group_chat_id = chat.group_chat_id
745 # Verify that the messages was received
746 messages = c2.GetGroupChatMessages(
747 conversations_pb2.GetGroupChatMessagesReq(group_chat_id=group_chat_id)
748 ).messages
750 assert len(messages) == 2
751 assert messages[0].text.text == message2
752 assert messages[1].text.text == message1
753 assert messages[0].author_user_id == user1.id
756def test_excessive_chat_initiations_are_reported(db, email_collector: EmailCollector):
757 """Test that excessive chat initiations are first reported in a warning email and finally lead blocking of further contacting other users."""
758 user, token = generate_user()
759 rate_limit_definition = RATE_LIMIT_DEFINITIONS[RateLimitAction.chat_initiation]
760 with conversations_session(token) as c:
761 # Test warning email
762 for _ in range(rate_limit_definition.warning_limit):
763 recipient_user, _ = generate_user()
764 _ = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[recipient_user.id]))
766 assert email_collector.count_for_reports() == 0
768 recipient_user, _ = generate_user()
769 _ = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[recipient_user.id]))
771 email = email_collector.pop_for_reports(last=True)
772 assert email.plain.startswith(
773 f"User {user.username} has sent {rate_limit_definition.warning_limit} chat initiations in the past {RATE_LIMIT_HOURS} hours."
774 )
776 # Test new chat initiations fail after exceeding CHAT_INITIATION_HARD_LIMIT
777 for _ in range(rate_limit_definition.hard_limit - rate_limit_definition.warning_limit - 1):
778 recipient_user, _ = generate_user()
779 _ = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[recipient_user.id]))
781 assert email_collector.count_for_reports() == 0
783 recipient_user, _ = generate_user()
784 with pytest.raises(grpc.RpcError) as exc_info:
785 _ = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[recipient_user.id]))
786 assert exc_info.value.code() == grpc.StatusCode.RESOURCE_EXHAUSTED
787 assert (
788 exc_info.value.details()
789 == "You have messaged a lot of users in the past 24 hours. To avoid spam, you can't contact any more users for now."
790 )
792 email = email_collector.pop_for_reports(last=True)
793 assert email.plain.startswith(
794 f"User {user.username} has sent {rate_limit_definition.hard_limit} chat initiations in the past {RATE_LIMIT_HOURS} hours."
795 )
796 assert "The user has been blocked from sending further chat initiations for now." in email.plain
799def test_send_direct_message_rate_limit(db, moderator, email_collector: EmailCollector):
800 """SendDirectMessage should enforce the chat_initiation rate limit when creating a new DM, but not when sending into an existing one."""
801 user, token = generate_user(complete_profile=True)
802 rate_limit_definition = RATE_LIMIT_DEFINITIONS[RateLimitAction.chat_initiation]
804 with conversations_session(token) as c:
805 for _ in range(rate_limit_definition.warning_limit):
806 recipient, _ = generate_user()
807 c.SendDirectMessage(conversations_pb2.SendDirectMessageReq(recipient_user_id=recipient.id, text="hi"))
809 assert email_collector.count_for_reports() == 0
811 recipient, _ = generate_user()
812 existing_dm_recipient_id = recipient.id
813 c.SendDirectMessage(
814 conversations_pb2.SendDirectMessageReq(recipient_user_id=existing_dm_recipient_id, text="hi")
815 )
817 email = email_collector.pop_for_reports(last=True)
818 assert email.plain.startswith(
819 f"User {user.username} has sent {rate_limit_definition.warning_limit} chat initiations in the past {RATE_LIMIT_HOURS} hours."
820 )
822 for _ in range(rate_limit_definition.hard_limit - rate_limit_definition.warning_limit - 1):
823 recipient, _ = generate_user()
824 c.SendDirectMessage(conversations_pb2.SendDirectMessageReq(recipient_user_id=recipient.id, text="hi"))
826 assert email_collector.count_for_reports() == 0
828 # follow-up into an existing DM must not count as a new initiation
829 c.SendDirectMessage(
830 conversations_pb2.SendDirectMessageReq(recipient_user_id=existing_dm_recipient_id, text="follow-up")
831 )
832 assert email_collector.count_for_reports() == 0
834 recipient, _ = generate_user()
835 with pytest.raises(grpc.RpcError) as exc_info:
836 c.SendDirectMessage(conversations_pb2.SendDirectMessageReq(recipient_user_id=recipient.id, text="hi"))
837 assert exc_info.value.code() == grpc.StatusCode.RESOURCE_EXHAUSTED
838 assert (
839 exc_info.value.details()
840 == "You have messaged a lot of users in the past 24 hours. To avoid spam, you can't contact any more users for now."
841 )
843 email = email_collector.pop_for_reports(last=True)
844 assert email.plain.startswith(
845 f"User {user.username} has sent {rate_limit_definition.hard_limit} chat initiations in the past {RATE_LIMIT_HOURS} hours."
846 )
847 assert "The user has been blocked from sending further chat initiations for now." in email.plain
850def test_leave_invite_to_group_chat(db, moderator):
851 user1, token1 = generate_user()
852 user2, token2 = generate_user()
853 user3, token3 = generate_user()
854 user4, token4 = generate_user()
855 user5, token5 = generate_user()
856 user6, token6 = generate_user(delete_user=True)
857 user7, token7 = generate_user()
858 user8, token8 = generate_user()
860 make_friends(user1, user2)
861 make_friends(user1, user3)
862 make_friends(user1, user5)
863 make_friends(user1, user7)
864 make_friends(user1, user8)
865 make_friends(user2, user3)
866 make_friends(user4, user3)
867 make_user_block(user1, user7)
868 make_user_block(user8, user1)
870 with conversations_session(token1) as c:
871 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user5.id]))
872 group_chat_id = res.group_chat_id
873 moderator.approve_group_chat(group_chat_id)
874 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 1"))
876 # other user not in chat
877 with conversations_session(token3) as c:
878 with pytest.raises(grpc.RpcError) as e:
879 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id))
880 assert e.value.code() == grpc.StatusCode.NOT_FOUND
881 with pytest.raises(grpc.RpcError) as e:
882 res = c.InviteToGroupChat(
883 conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user4.id)
884 )
885 assert e.value.code() == grpc.StatusCode.NOT_FOUND
886 with pytest.raises(grpc.RpcError) as e:
887 res = c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
888 assert e.value.code() == grpc.StatusCode.NOT_FOUND
890 with conversations_session(token2) as c:
891 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id))
892 assert user3.id not in res.member_user_ids
894 # only_admins_invite defaults to true so shouldn't be able to invite
895 with pytest.raises(grpc.RpcError) as e:
896 res = c.InviteToGroupChat(
897 conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id)
898 )
899 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
900 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
902 with conversations_session(token1) as c:
903 # invite invisible user fails
904 with pytest.raises(grpc.RpcError) as e:
905 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user6.id))
906 assert e.value.code() == grpc.StatusCode.NOT_FOUND
907 assert e.value.details() == "Couldn't find that user."
908 # invite fake user fails
909 with pytest.raises(grpc.RpcError) as e:
910 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=999))
911 assert e.value.code() == grpc.StatusCode.NOT_FOUND
912 assert e.value.details() == "Couldn't find that user."
913 # invite blocked user fails
914 with pytest.raises(grpc.RpcError) as e:
915 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user7.id))
916 assert e.value.code() == grpc.StatusCode.NOT_FOUND
917 assert e.value.details() == "Couldn't find that user."
918 # invite blocking user fails
919 with pytest.raises(grpc.RpcError) as e:
920 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user8.id))
921 assert e.value.code() == grpc.StatusCode.NOT_FOUND
922 assert e.value.details() == "Couldn't find that user."
924 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id))
925 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id))
926 assert user1.id in res.member_user_ids
927 assert user5.id in res.member_user_ids
928 assert user3.id in res.member_user_ids
930 # test non-admin inviting
931 c.EditGroupChat(
932 conversations_pb2.EditGroupChatReq(
933 group_chat_id=group_chat_id, only_admins_invite=wrappers_pb2.BoolValue(value=False)
934 )
935 )
937 with conversations_session(token3) as c:
938 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user2.id))
939 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id))
940 assert user2.id in res.member_user_ids
943def test_group_chats_with_messages_before_join(db, moderator):
944 """
945 If user 1 and 2 have a group chat and send messages, then add user 3; user 3
946 should still see the group chat!
947 """
948 user1, token1 = generate_user()
949 user2, token2 = generate_user()
950 user3, token3 = generate_user()
951 user4, token4 = generate_user()
953 make_friends(user1, user2)
954 make_friends(user1, user3)
955 make_friends(user2, user3)
956 make_friends(user1, user4)
958 with conversations_session(token1) as c:
959 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user4.id]))
960 group_chat_id = res.group_chat_id
961 moderator.approve_group_chat(group_chat_id)
962 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 1"))
964 with conversations_session(token2) as c:
965 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 2"))
967 with conversations_session(token1) as c:
968 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 3"))
970 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id))
972 with conversations_session(token3) as c:
973 # should work
974 c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id))
976 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
977 assert len(res.group_chats) == 1
980def test_invite_to_dm(db):
981 user1, token1 = generate_user()
982 user2, token2 = generate_user()
983 user3, token3 = generate_user()
985 make_friends(user1, user2)
986 make_friends(user1, user3)
987 make_friends(user2, user3)
989 with conversations_session(token1) as c:
990 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id]))
991 group_chat_id = res.group_chat_id
992 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 1"))
994 # dm, shou;dn't be able to invite someone else
995 with pytest.raises(grpc.RpcError) as e:
996 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id))
997 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
998 assert e.value.details() == "You can't invite other users to a direct message."
1001def test_sole_admin_leaves(db, moderator):
1002 user1, token1 = generate_user()
1003 user2, token2 = generate_user()
1004 user3, token3 = generate_user()
1006 make_friends(user1, user2)
1007 make_friends(user1, user3)
1008 make_friends(user2, user3)
1010 with conversations_session(token1) as c:
1011 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]))
1012 group_chat_id = res.group_chat_id
1013 moderator.approve_group_chat(group_chat_id)
1014 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 1"))
1016 # sole admin can't leave group chat
1017 with pytest.raises(grpc.RpcError) as e:
1018 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
1019 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
1020 assert e.value.details() == "The last admin can't leave a group chat."
1022 with conversations_session(token2) as c:
1023 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
1025 with conversations_session(token3) as c:
1026 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
1028 # sole admin can leave when last in chat
1029 with conversations_session(token1) as c:
1030 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
1033def test_search_messages(db, moderator):
1034 user1, token1 = generate_user()
1035 user2, token2 = generate_user()
1036 user3, token3 = generate_user()
1038 make_friends(user1, user2)
1039 make_friends(user1, user3)
1041 with conversations_session(token1) as c:
1042 # create some threads with messages
1043 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id]))
1044 gc1_id = res.group_chat_id
1045 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gc1_id, text="Test message 1"))
1046 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gc1_id, text="Test message 2"))
1047 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]))
1048 gc2_id = res.group_chat_id
1049 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gc2_id, text="Test group message 3"))
1050 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gc2_id, text="Test group message 4"))
1052 # Approve group chats so they appear in search results
1053 moderator.approve_group_chat(gc1_id)
1054 moderator.approve_group_chat(gc2_id)
1056 with conversations_session(token1) as c:
1057 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="message "))
1058 assert len(res.results) == 4
1059 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="group "))
1060 assert len(res.results) == 2
1061 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="message 5"))
1062 assert len(res.results) == 0
1064 # outside user doesn't get results
1065 with conversations_session(token3) as c:
1066 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="Test message"))
1067 assert len(res.results) == 0
1070def test_search_messages_left_joined(db, moderator):
1071 user1, token1 = generate_user()
1072 user2, token2 = generate_user()
1073 user3, token3 = generate_user()
1074 user4, token4 = generate_user()
1075 make_friends(user1, user2)
1076 make_friends(user1, user3)
1077 make_friends(user1, user4)
1079 with conversations_session(token1) as c:
1080 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user4.id]))
1081 group_chat_id = res.group_chat_id
1082 moderator.approve_group_chat(group_chat_id)
1083 for i in range(10):
1084 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message " + str(i)))
1086 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id))
1087 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 10"))
1088 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="Test message"))
1090 assert len(res.results) == 11
1092 with conversations_session(token3) as c:
1093 # can only see last message after invited
1094 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="Test message"))
1096 assert len(res.results) == 1
1097 assert res.results[0].message.text.text == "Test message 10"
1099 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
1101 with conversations_session(token1) as c:
1102 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 11"))
1103 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 12"))
1104 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id))
1105 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 13"))
1106 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 14"))
1108 with conversations_session(token3) as c:
1109 # can only see last message after invited
1110 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="Test message"))
1111 assert len(res.results) == 3
1112 assert res.results[0].message.text.text == "Test message 14"
1113 assert res.results[1].message.text.text == "Test message 13"
1114 assert res.results[2].message.text.text == "Test message 10"
1117def test_admin_behaviour(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)
1124 make_friends(user2, user3)
1126 with conversations_session(token1) as c:
1127 gcid = c.CreateGroupChat(
1128 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])
1129 ).group_chat_id
1130 moderator.approve_group_chat(gcid)
1131 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user2.id))
1132 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1133 assert len(res.admin_user_ids) == 2
1134 assert user1.id in res.admin_user_ids
1135 assert user2.id in res.admin_user_ids
1137 with conversations_session(token3) as c:
1138 with pytest.raises(grpc.RpcError) as e:
1139 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user3.id))
1140 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
1141 with pytest.raises(grpc.RpcError) as e:
1142 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user1.id))
1143 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
1144 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1145 assert len(res.admin_user_ids) == 2
1146 assert user1.id in res.admin_user_ids
1147 assert user2.id in res.admin_user_ids
1149 with conversations_session(token2) as c:
1150 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user3.id))
1151 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1152 assert len(res.admin_user_ids) == 3
1153 assert user1.id in res.admin_user_ids
1154 assert user2.id in res.admin_user_ids
1155 assert user3.id in res.admin_user_ids
1157 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user1.id))
1158 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1159 assert len(res.admin_user_ids) == 2
1160 assert user2.id in res.admin_user_ids
1161 assert user3.id in res.admin_user_ids
1163 with conversations_session(token1) as c:
1164 with pytest.raises(grpc.RpcError):
1165 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user1.id))
1166 with pytest.raises(grpc.RpcError):
1167 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user3.id))
1168 with pytest.raises(grpc.RpcError):
1169 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user2.id))
1170 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1171 assert len(res.admin_user_ids) == 2
1172 assert user2.id in res.admin_user_ids
1173 assert user3.id in res.admin_user_ids
1175 with conversations_session(token2) as c:
1176 # can demote self if there are other admins
1177 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user2.id))
1178 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1179 assert len(res.admin_user_ids) == 1
1180 assert user3.id in res.admin_user_ids
1182 with conversations_session(token3) as c:
1183 with pytest.raises(grpc.RpcError) as e:
1184 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user3.id))
1185 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
1186 assert e.value.details() == "You can't remove the last admin."
1187 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1188 assert len(res.admin_user_ids) == 1
1189 assert user3.id in res.admin_user_ids
1191 # last admin can't leave
1192 with pytest.raises(grpc.RpcError) as e:
1193 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=gcid))
1194 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
1195 assert e.value.details() == "The last admin can't leave a group chat."
1197 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user1.id))
1199 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=gcid))
1201 with conversations_session(token2) as c:
1202 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=gcid))
1204 # last participant must be admin but can leave to orphan chat
1205 with conversations_session(token1) as c:
1206 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=gcid))
1209def test_add_remove_admin_failures(db):
1210 user1, token1 = generate_user()
1211 user2, token2 = generate_user()
1212 user3, token3 = generate_user()
1213 user4, token4 = generate_user()
1214 user5, token5 = generate_user()
1216 make_friends(user1, user2)
1217 make_friends(user1, user3)
1218 make_friends(user1, user4)
1219 make_friends(user1, user5)
1221 with conversations_session(token1) as c:
1222 gcid = c.CreateGroupChat(
1223 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id, user4.id, user5.id])
1224 ).group_chat_id
1226 make_user_invisible(user3.id)
1227 make_user_block(user1, user4)
1228 make_user_block(user5, user1)
1230 # make non-existent user admin
1231 with pytest.raises(grpc.RpcError) as e:
1232 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=999))
1233 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1234 assert e.value.details() == "Couldn't find that user."
1236 # make invisible user admin
1237 with pytest.raises(grpc.RpcError) as e:
1238 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user3.id))
1239 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1240 assert e.value.details() == "Couldn't find that user."
1242 # make blocked user admin
1243 with pytest.raises(grpc.RpcError) as e:
1244 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user4.id))
1245 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1246 assert e.value.details() == "Couldn't find that user."
1248 # make blocking user admin
1249 with pytest.raises(grpc.RpcError) as e:
1250 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user5.id))
1251 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1252 assert e.value.details() == "Couldn't find that user."
1254 with session_scope() as session:
1255 subscriptions = (
1256 session.execute(
1257 select(GroupChatSubscription)
1258 .where(GroupChatSubscription.group_chat_id == gcid)
1259 .where(GroupChatSubscription.role == GroupChatRole.participant)
1260 )
1261 .scalars()
1262 .all()
1263 )
1265 for subscription in subscriptions:
1266 subscription.role = GroupChatRole.admin
1268 with conversations_session(token1) as c:
1269 # remove non-existent user admin
1270 with pytest.raises(grpc.RpcError) as e:
1271 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=999))
1272 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1273 assert e.value.details() == "Couldn't find that user."
1275 # remove invisible admin
1276 with pytest.raises(grpc.RpcError) as e:
1277 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user3.id))
1278 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1279 assert e.value.details() == "Couldn't find that user."
1281 # remove blocked admin
1282 with pytest.raises(grpc.RpcError) as e:
1283 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user4.id))
1284 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1285 assert e.value.details() == "Couldn't find that user."
1287 # remove blocking admin
1288 with pytest.raises(grpc.RpcError) as e:
1289 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user5.id))
1290 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1291 assert e.value.details() == "Couldn't find that user."
1294def test_last_seen(db, moderator):
1295 user1, token1 = generate_user()
1296 user2, token2 = generate_user()
1297 user3, token3 = generate_user()
1299 make_friends(user1, user2)
1300 make_friends(user1, user3)
1301 make_friends(user2, user3)
1303 with conversations_session(token3) as c:
1304 # this is just here to mess up any issues we get if we pretend there's only one group chat ever
1305 gcid_distraction = c.CreateGroupChat(
1306 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user1.id])
1307 ).group_chat_id
1308 moderator.approve_group_chat(gcid_distraction)
1310 with conversations_session(token1) as c:
1311 gcid = c.CreateGroupChat(
1312 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])
1313 ).group_chat_id
1314 moderator.approve_group_chat(gcid)
1316 message_ids = []
1318 for i in range(6):
1319 c.SendMessage(
1320 conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text=f"gibberish message... {i}")
1321 )
1322 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text=f"test message {i}"))
1323 c.SendMessage(
1324 conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text=f"gibberish message {i}")
1325 )
1327 message_ids.append(
1328 c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)).latest_message.message_id
1329 )
1331 # messages are automatically marked as seen when you send a new message
1332 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1333 assert res.unseen_message_count == 0
1335 with conversations_session(token2) as c:
1336 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1337 # created + 6 normal
1338 assert res.unseen_message_count == 7
1340 backward_offset = 3
1341 c.MarkLastSeenGroupChat(
1342 conversations_pb2.MarkLastSeenGroupChatReq(
1343 group_chat_id=gcid, last_seen_message_id=message_ids[-backward_offset - 1]
1344 )
1345 )
1347 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1348 assert res.unseen_message_count == backward_offset
1350 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="test message ..."))
1352 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1353 assert res.unseen_message_count == 0
1355 with conversations_session(token3) as c:
1356 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1357 # created + 7 normal
1358 assert res.unseen_message_count == 8
1360 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="test message ..."))
1362 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1363 assert res.unseen_message_count == 0
1366def test_mark_last_seen_clears_notifications(db, moderator):
1367 user1, token1 = generate_user()
1368 user2, token2 = generate_user()
1370 with conversations_session(token1) as c:
1371 gcid = c.CreateGroupChat(
1372 conversations_pb2.CreateGroupChatReq(
1373 recipient_user_ids=[user2.id], title=wrappers_pb2.StringValue(value="Test chat")
1374 )
1375 ).group_chat_id
1377 moderator.approve_group_chat(gcid)
1379 with conversations_session(token1) as c:
1380 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello"))
1382 process_jobs()
1384 def unseen_notification_count(user_id):
1385 with session_scope() as session:
1386 return session.execute(
1387 select(func.count())
1388 .select_from(Notification)
1389 .where(Notification.user_id == user_id)
1390 .where(Notification.key == str(gcid))
1391 .where(Notification.is_seen == False)
1392 ).scalar_one()
1394 assert unseen_notification_count(user2.id) > 0
1396 with conversations_session(token2) as c:
1397 c.MarkLastSeenGroupChat(conversations_pb2.MarkLastSeenGroupChatReq(group_chat_id=gcid, last_seen_message_id=1))
1399 assert unseen_notification_count(user2.id) == 0
1402def test_one_dm_per_pair(db, moderator):
1403 user1, token1 = generate_user()
1404 user2, token2 = generate_user()
1405 user3, token3 = generate_user()
1407 make_friends(user1, user2)
1408 make_friends(user1, user3)
1409 make_friends(user2, user3)
1411 with conversations_session(token1) as c:
1412 # create DM with user 2
1413 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id]))
1414 assert res.is_dm
1415 dm_with_user2 = res.group_chat_id
1417 # create DM with user 3
1418 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id]))
1419 assert res.is_dm
1420 dm_with_user3 = res.group_chat_id
1422 # can't create another group chat with just user 2
1423 with pytest.raises(grpc.RpcError) as e:
1424 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id]))
1425 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
1427 # can't create another group chat with just user 3
1428 with pytest.raises(grpc.RpcError) as e:
1429 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id]))
1430 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
1432 # can create joined group chat
1433 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]))
1434 assert not res.is_dm
1436 # Approve the DMs so user2 can see them (otherwise they're SHADOWED and only visible to creator)
1437 moderator.approve_group_chat(dm_with_user2)
1439 with conversations_session(token2) as c:
1440 # can create DM with user 3
1441 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id]))
1442 assert res.is_dm
1444 # can't create another group chat with just user 1 (DM was approved, so user2 can see it)
1445 with pytest.raises(grpc.RpcError) as e:
1446 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user1.id]))
1447 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
1450def test_GetDirectMessage(db):
1451 user1, token1 = generate_user()
1452 user2, token2 = generate_user()
1453 user3, token3 = generate_user()
1455 make_friends(user1, user2)
1456 make_friends(user1, user3)
1457 make_friends(user2, user3)
1459 with conversations_session(token1) as c:
1460 # no group chat with user 2
1461 with pytest.raises(grpc.RpcError) as e:
1462 res = c.GetDirectMessage(conversations_pb2.GetDirectMessageReq(user_id=user2.id))
1463 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1465 # no group chat with nor user 3
1466 with pytest.raises(grpc.RpcError) as e:
1467 res = c.GetDirectMessage(conversations_pb2.GetDirectMessageReq(user_id=user3.id))
1468 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1470 # create DM with user 2
1471 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id]))
1472 assert res.is_dm
1473 gcid = res.group_chat_id
1475 # now should exist
1476 res = c.GetDirectMessage(conversations_pb2.GetDirectMessageReq(user_id=user2.id))
1477 assert res.group_chat_id == gcid
1478 assert not res.is_archived
1480 # create DM with user 3
1481 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id]))
1482 assert res.is_dm
1484 # can create joined group chat
1485 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]))
1486 assert not res.is_dm
1488 with conversations_session(token2) as c:
1489 # can create DM with user 3
1490 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id]))
1491 assert res.is_dm
1492 assert res.can_message
1493 gcid = res.group_chat_id
1495 # DM with 3 should exist, but can't message after being blocked
1496 make_user_block(user3, user2)
1497 res = c.GetDirectMessage(conversations_pb2.GetDirectMessageReq(user_id=user3.id))
1498 assert res.group_chat_id == gcid
1499 assert not res.can_message
1502def test_total_unseen(db, moderator):
1503 user1, token1 = generate_user()
1504 user2, token2 = generate_user()
1505 user3, token3 = generate_user()
1507 # distractions
1508 user4, token4 = generate_user()
1510 make_friends(user1, user2)
1511 make_friends(user1, user3)
1512 make_friends(user2, user3)
1514 # distractions
1515 make_friends(user1, user4)
1517 with conversations_session(token1) as c:
1518 # distractions
1519 gcid_distraction = c.CreateGroupChat(
1520 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user4.id])
1521 ).group_chat_id
1522 moderator.approve_group_chat(gcid_distraction)
1523 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text="distraction..."))
1525 gcid = c.CreateGroupChat(
1526 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])
1527 ).group_chat_id
1528 moderator.approve_group_chat(gcid)
1530 for i in range(6):
1531 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text=f"test message {i}"))
1533 # distractions
1534 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text="distraction..."))
1536 # messages are automatically marked as seen when you send a new message
1537 with api_session(token1) as api:
1538 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 0
1540 with api_session(token2) as api:
1541 # chat created + 6 normal messages
1542 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 7
1544 # now leave chat with user2
1545 with conversations_session(token2) as c:
1546 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=gcid))
1548 with api_session(token2) as api:
1549 # seen messages becomes 0 when leaving
1550 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 0
1552 with conversations_session(token1) as c:
1553 # distractions
1554 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text="distraction..."))
1556 # send more stuff without user 2
1557 for i in range(3):
1558 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text=f"test message {i}"))
1560 # distractions
1561 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text="distraction..."))
1563 with api_session(token2) as api:
1564 # seen messages becomes 0 when leaving
1565 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 0
1567 with conversations_session(token1) as c:
1568 # add user 2 back
1569 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=gcid, user_id=user2.id))
1571 # send more stuff with user 2
1572 for i in range(12):
1573 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text=f"test message {i}"))
1575 # distractions
1576 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text="distraction..."))
1578 with api_session(token2) as api:
1579 # joined + 12 normal
1580 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 13
1583def test_regression_ListGroupChats_pagination(db, moderator):
1584 user1, token1 = generate_user()
1585 user2, token2 = generate_user()
1586 user3, token3 = generate_user()
1588 make_friends(user1, user2)
1589 make_friends(user1, user3)
1591 with conversations_session(token1) as c:
1592 # tuples of (group_chat_id, message_id)
1593 group_chat_and_message_ids = []
1594 for i in range(50):
1595 res1 = c.CreateGroupChat(
1596 conversations_pb2.CreateGroupChatReq(
1597 recipient_user_ids=[user2.id, user3.id], title=wrappers_pb2.StringValue(value=f"Chat {i}")
1598 )
1599 )
1601 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res1.group_chat_id, text=f"Test message {i}"))
1603 res2 = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=res1.group_chat_id))
1605 group_chat_and_message_ids.append((res2.group_chat_id, res2.latest_message.message_id))
1606 moderator.approve_group_chat(res1.group_chat_id)
1608 seen_group_chat_ids = []
1610 last_message_id = 0
1611 more = True
1612 while more:
1613 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(last_message_id=last_message_id))
1614 last_message_id = res.last_message_id
1615 more = not res.no_more
1617 seen_group_chat_ids.extend([chat.group_chat_id for chat in res.group_chats])
1619 assert set(seen_group_chat_ids) == {x[0] for x in group_chat_and_message_ids}, "Not all group chats returned"
1622def test_muting(db, moderator):
1623 user1, token1 = generate_user()
1624 user2, token2 = generate_user()
1625 user3, token3 = generate_user()
1627 make_friends(user1, user2)
1628 make_friends(user1, user3)
1629 make_friends(user2, user3)
1631 with conversations_session(token3) as c:
1632 # this is just here to mess up any issues we get if we pretend there's only one group chat ever
1633 gcid_distraction = c.CreateGroupChat(
1634 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user1.id])
1635 ).group_chat_id
1636 moderator.approve_group_chat(gcid_distraction)
1638 with conversations_session(token1) as c:
1639 gcid = c.CreateGroupChat(
1640 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])
1641 ).group_chat_id
1642 moderator.approve_group_chat(gcid)
1644 with conversations_session(token2) as c:
1645 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1646 assert not res.mute_info.muted
1647 assert not res.mute_info.HasField("muted_until")
1649 c.MuteGroupChat(conversations_pb2.MuteGroupChatReq(group_chat_id=gcid, forever=True))
1650 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1651 assert res.mute_info.muted
1652 assert not res.mute_info.HasField("muted_until")
1654 c.MuteGroupChat(conversations_pb2.MuteGroupChatReq(group_chat_id=gcid, unmute=True))
1655 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1656 assert not res.mute_info.muted
1657 assert not res.mute_info.HasField("muted_until")
1659 c.MuteGroupChat(
1660 conversations_pb2.MuteGroupChatReq(
1661 group_chat_id=gcid, for_duration=Duration_from_timedelta(timedelta(hours=2))
1662 )
1663 )
1664 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1665 assert res.mute_info.muted
1666 assert res.mute_info.HasField("muted_until")
1667 assert to_aware_datetime(res.mute_info.muted_until) >= now() + timedelta(hours=1, minutes=59)
1668 assert to_aware_datetime(res.mute_info.muted_until) <= now() + timedelta(hours=2, minutes=1)
1671def test_archiving(db, moderator):
1672 """Test SetGroupChatArchiveStatus RPC with GetGroupChat and ListGroupChats"""
1673 user1, token1 = generate_user()
1674 user2, token2 = generate_user()
1675 user3, token3 = generate_user()
1677 make_friends(user1, user2)
1678 make_friends(user1, user3)
1679 make_friends(user2, user3)
1681 # Create a distraction chat to ensure we're testing the right one
1682 with conversations_session(token3) as c:
1683 gcid_distraction = c.CreateGroupChat(
1684 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user1.id])
1685 ).group_chat_id
1686 moderator.approve_group_chat(gcid_distraction)
1688 with conversations_session(token1) as c:
1689 gcid = c.CreateGroupChat(
1690 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])
1691 ).group_chat_id
1692 moderator.approve_group_chat(gcid)
1694 # Test basic archive/unarchive functionality
1695 with conversations_session(token2) as c:
1696 # Initially not archived
1697 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1698 assert not res.is_archived
1700 # Archive the chat
1701 archive_res = c.SetGroupChatArchiveStatus(
1702 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True)
1703 )
1704 assert archive_res.group_chat_id == gcid
1705 assert archive_res.is_archived
1707 # Verify archived via GetGroupChat
1708 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1709 assert res.is_archived
1711 # Unarchive the chat
1712 archive_res = c.SetGroupChatArchiveStatus(
1713 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=False)
1714 )
1715 assert archive_res.group_chat_id == gcid
1716 assert not archive_res.is_archived
1718 # Verify unarchived via GetGroupChat
1719 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1720 assert not res.is_archived
1723def test_archiving_per_user(db, moderator):
1724 """Test that archiving is per-user - one user archiving doesn't affect others"""
1725 user1, token1 = generate_user()
1726 user2, token2 = generate_user()
1728 make_friends(user1, user2)
1730 with conversations_session(token1) as c:
1731 gcid = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])).group_chat_id
1732 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello"))
1733 moderator.approve_group_chat(gcid)
1735 # User1 archives the chat
1736 with conversations_session(token1) as c:
1737 c.SetGroupChatArchiveStatus(
1738 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True)
1739 )
1740 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1741 assert res.is_archived
1743 # User2 should NOT see it as archived
1744 with conversations_session(token2) as c:
1745 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1746 assert not res.is_archived
1748 # User2 archives it too
1749 with conversations_session(token2) as c:
1750 c.SetGroupChatArchiveStatus(
1751 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True)
1752 )
1753 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1754 assert res.is_archived
1756 # User1 unarchives - user2 should still see it as archived
1757 with conversations_session(token1) as c:
1758 c.SetGroupChatArchiveStatus(
1759 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=False)
1760 )
1761 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1762 assert not res.is_archived
1764 with conversations_session(token2) as c:
1765 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1766 assert res.is_archived
1769def test_archiving_with_list_group_chats(db, moderator):
1770 """Test archive filtering with ListGroupChats endpoint"""
1771 user1, token1 = generate_user()
1772 user2, token2 = generate_user()
1773 user3, token3 = generate_user()
1775 make_friends(user1, user2)
1776 make_friends(user1, user3)
1778 with conversations_session(token1) as c:
1779 # Create 3 chats
1780 gcid1 = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])).group_chat_id
1781 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid1, text="Chat 1"))
1782 moderator.approve_group_chat(gcid1)
1784 gcid2 = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id])).group_chat_id
1785 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid2, text="Chat 2"))
1786 moderator.approve_group_chat(gcid2)
1788 gcid3 = c.CreateGroupChat(
1789 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])
1790 ).group_chat_id
1791 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid3, text="Chat 3"))
1792 moderator.approve_group_chat(gcid3)
1794 with conversations_session(token1) as c:
1795 # Initially all 3 chats should be visible
1796 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
1797 assert len(res.group_chats) == 3
1799 # All should be non-archived
1800 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=False))
1801 assert len(res.group_chats) == 3
1803 # None should be archived
1804 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=True))
1805 assert len(res.group_chats) == 0
1807 # Archive chat 1
1808 c.SetGroupChatArchiveStatus(
1809 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid1, is_archived=True)
1810 )
1812 # Without filter, still see all 3
1813 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq())
1814 assert len(res.group_chats) == 3
1816 # Non-archived should show 2
1817 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=False))
1818 assert len(res.group_chats) == 2
1819 assert gcid1 not in [gc.group_chat_id for gc in res.group_chats]
1821 # Archived should show 1
1822 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=True))
1823 assert len(res.group_chats) == 1
1824 assert res.group_chats[0].group_chat_id == gcid1
1825 assert res.group_chats[0].is_archived
1827 # Archive chat 2 as well
1828 c.SetGroupChatArchiveStatus(
1829 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid2, is_archived=True)
1830 )
1832 # Non-archived should show 1
1833 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=False))
1834 assert len(res.group_chats) == 1
1835 assert res.group_chats[0].group_chat_id == gcid3
1837 # Archived should show 2
1838 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=True))
1839 assert len(res.group_chats) == 2
1840 archived_ids = {gc.group_chat_id for gc in res.group_chats}
1841 assert archived_ids == {gcid1, gcid2}
1843 # Unarchive chat 1
1844 c.SetGroupChatArchiveStatus(
1845 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid1, is_archived=False)
1846 )
1848 # Non-archived should show 2
1849 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=False))
1850 assert len(res.group_chats) == 2
1852 # Archived should show 1
1853 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=True))
1854 assert len(res.group_chats) == 1
1855 assert res.group_chats[0].group_chat_id == gcid2
1858def test_archiving_chat_not_found(db, moderator):
1859 """Test that archiving a non-existent or non-accessible chat fails"""
1860 user1, token1 = generate_user()
1861 user2, token2 = generate_user()
1862 user3, token3 = generate_user()
1864 make_friends(user1, user2)
1866 with conversations_session(token1) as c:
1867 gcid = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])).group_chat_id
1868 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello"))
1869 moderator.approve_group_chat(gcid)
1871 # User3 is not in the chat - should get NOT_FOUND
1872 with conversations_session(token3) as c:
1873 with pytest.raises(grpc.RpcError) as e:
1874 c.SetGroupChatArchiveStatus(
1875 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True)
1876 )
1877 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1879 # Non-existent chat ID should fail
1880 with conversations_session(token1) as c:
1881 with pytest.raises(grpc.RpcError) as e:
1882 c.SetGroupChatArchiveStatus(
1883 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=99999, is_archived=True)
1884 )
1885 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1888def test_archiving_after_leaving_chat(db, moderator):
1889 """Test that you can't archive a chat after leaving it"""
1890 user1, token1 = generate_user()
1891 user2, token2 = generate_user()
1892 user3, token3 = generate_user()
1894 make_friends(user1, user2)
1895 make_friends(user1, user3)
1896 make_friends(user2, user3)
1898 with conversations_session(token1) as c:
1899 gcid = c.CreateGroupChat(
1900 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])
1901 ).group_chat_id
1902 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello"))
1903 moderator.approve_group_chat(gcid)
1905 # User2 leaves the chat
1906 with conversations_session(token2) as c:
1907 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=gcid))
1909 # User2 should not be able to archive it now
1910 with conversations_session(token2) as c:
1911 with pytest.raises(grpc.RpcError) as e:
1912 c.SetGroupChatArchiveStatus(
1913 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True)
1914 )
1915 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1918def test_archiving_dm(db, moderator):
1919 """Test that archiving works for DMs as well as group chats"""
1920 user1, token1 = generate_user()
1921 user2, token2 = generate_user()
1923 make_friends(user1, user2)
1925 with conversations_session(token1) as c:
1926 # Create a DM
1927 gcid = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])).group_chat_id
1928 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello"))
1929 moderator.approve_group_chat(gcid)
1931 # Verify it's a DM
1932 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1933 assert res.is_dm
1934 assert not res.is_archived
1936 # Archive the DM
1937 c.SetGroupChatArchiveStatus(
1938 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True)
1939 )
1941 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1942 assert res.is_archived
1944 # Unarchive it
1945 c.SetGroupChatArchiveStatus(
1946 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=False)
1947 )
1949 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1950 assert not res.is_archived
1953def test_archiving_idempotent(db, moderator):
1954 """Test that archiving/unarchiving is idempotent"""
1955 user1, token1 = generate_user()
1956 user2, token2 = generate_user()
1958 make_friends(user1, user2)
1960 with conversations_session(token1) as c:
1961 gcid = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])).group_chat_id
1962 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello"))
1963 moderator.approve_group_chat(gcid)
1965 # Archive twice - should work without error
1966 c.SetGroupChatArchiveStatus(
1967 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True)
1968 )
1969 c.SetGroupChatArchiveStatus(
1970 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True)
1971 )
1972 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1973 assert res.is_archived
1975 # Unarchive twice - should work without error
1976 c.SetGroupChatArchiveStatus(
1977 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=False)
1978 )
1979 c.SetGroupChatArchiveStatus(
1980 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=False)
1981 )
1982 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid))
1983 assert not res.is_archived
1986def test_chat_notifications(db, moderator):
1987 user1, token1 = generate_user()
1988 user2, token2 = generate_user()
1989 # notifs off
1990 user3, token3 = generate_user()
1991 user4, token4 = generate_user()
1992 user5, token5 = generate_user()
1993 user6, token6 = generate_user()
1995 make_friends(user1, user2)
1996 make_friends(user1, user3)
1997 make_friends(user1, user4)
1998 make_friends(user4, user5)
1999 make_friends(user4, user6)
2001 # have some of them enable/disable notifs
2002 topic_action = NotificationTopicAction.chat__message
2003 for token, enabled in [
2004 (token1, True),
2005 (token2, True),
2006 (token3, False),
2007 (token4, True),
2008 (token5, True),
2009 (token6, True),
2010 ]:
2011 with notifications_session(token) as notifications:
2012 notifications.SetNotificationSettings(
2013 notifications_pb2.SetNotificationSettingsReq(
2014 preferences=[
2015 notifications_pb2.SingleNotificationPreference(
2016 topic=topic_action.topic,
2017 action=topic_action.action,
2018 delivery_method=delivery_method,
2019 enabled=enabled,
2020 )
2021 for delivery_method in ["push", "email", "digest"]
2022 ],
2023 )
2024 )
2026 group_chat_id = None
2028 def send_msg(c, i):
2029 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text=f"Test message {i}"))
2031 with conversations_session(token1) as c:
2032 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id, user4.id]))
2033 group_chat_id = res.group_chat_id
2034 moderator.approve_group_chat(group_chat_id)
2035 c.EditGroupChat(
2036 conversations_pb2.EditGroupChatReq(
2037 group_chat_id=group_chat_id, only_admins_invite=wrappers_pb2.BoolValue(value=False)
2038 )
2039 )
2040 send_msg(c, i=1)
2041 send_msg(c, i=2)
2043 with conversations_session(token4) as c:
2044 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user5.id))
2045 send_msg(c, i=3)
2046 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user6.id))
2047 send_msg(c, i=4)
2048 send_msg(c, i=5)
2050 with conversations_session(token3) as c:
2051 send_msg(c, i=6)
2052 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
2054 with conversations_session(token2) as c:
2055 send_msg(c, i=7)
2056 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id))
2058 with conversations_session(token6) as c:
2059 send_msg(c, i=8)
2061 # go through all bg jobs
2062 while process_job():
2063 pass
2065 # now check notifs...
2066 expected_notifs = [
2067 (user1, "user1", [3, 4, 5, 6, 7, 8]),
2068 (user2, "user2", [1, 2, 3, 4, 5, 6]),
2069 (user3, "user3", []), # notifs off
2070 (user4, "user4", [1, 2, 6, 7, 8]),
2071 (user5, "user5", [3, 4, 5, 6, 7, 8]),
2072 (user6, "user6", [4, 5, 6, 7]),
2073 ]
2075 with session_scope() as session:
2076 for user, label, expected_msgs in expected_notifs:
2077 deliv = (
2078 session.execute(
2079 select(Notification.data)
2080 .join(NotificationDelivery, NotificationDelivery.notification_id == Notification.id)
2081 .where(Notification.user_id == user.id)
2082 .where(Notification.topic_action == topic_action)
2083 .where(NotificationDelivery.delivery_type == NotificationDeliveryType.push)
2084 .order_by(Notification.created)
2085 )
2086 .scalars()
2087 .all()
2088 )
2090 def parse_message_payload(data):
2091 return notification_data_pb2.ChatMessage.FromString(data).text
2093 contents = [parse_message_payload(d) for d in deliv]
2095 print(contents)
2097 assert [f"Test message {i}" for i in expected_msgs] == contents, f"Wrong messages for {label}"
2100def test_incomplete_profile(db):
2101 user1, token1 = generate_user(complete_profile=True)
2102 user2, token2 = generate_user(complete_profile=False)
2103 user3, token3 = generate_user(complete_profile=True)
2104 make_friends(user1, user2)
2105 make_friends(user1, user3)
2107 # user 1 can make a chat
2108 with conversations_session(token1) as c:
2109 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id]))
2110 group_chat_id = res.group_chat_id
2111 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 1"))
2112 res = c.GetGroupChatMessages(conversations_pb2.GetGroupChatMessagesReq(group_chat_id=group_chat_id))
2113 assert res.messages[0].text.text == "Test message 1"
2114 assert to_aware_datetime(res.messages[0].time) <= now()
2115 assert res.messages[0].author_user_id == user1.id
2117 # user 2 cannot
2118 with conversations_session(token2) as c:
2119 with pytest.raises(grpc.RpcError) as e:
2120 c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id]))
2121 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
2122 assert e.value.details() == "You have to complete your profile before you can send a message."