Coverage for app/backend/src/tests/test_conversations.py: 100%

1299 statements  

« prev     ^ index     » next       coverage.py v7.14.2, created at 2026-06-21 09:29 +0000

1from datetime import timedelta 

2 

3import grpc 

4import pytest 

5from google.protobuf import wrappers_pb2 

6from sqlalchemy import func, select 

7 

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, Moderator, PushCollector, process_jobs 

24from tests.fixtures.sessions import api_session, conversations_session, notifications_session 

25 

26 

27@pytest.fixture(autouse=True) 

28def _(testconfig): 

29 pass 

30 

31 

32def test_list_group_chats(db, moderator): 

33 user1, token1 = generate_user() 

34 user2, token2 = generate_user() 

35 user3, token3 = generate_user() 

36 

37 make_friends(user2, user1) 

38 make_friends(user1, user3) 

39 

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 

44 

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")) 

58 

59 moderator.approve_group_chat(group_chat1_id) 

60 moderator.approve_group_chat(group_chat2_id) 

61 

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 

66 

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 

71 

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 

76 

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 

84 

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 

89 

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 

93 

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 

97 

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 

102 

103 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=True)) 

104 assert len(res.group_chats) == 0 

105 

106 

107def test_list_empty_group_chats(db, moderator): 

108 user1, token1 = generate_user() 

109 user2, token2 = generate_user() 

110 user3, token3 = generate_user() 

111 

112 make_friends(user1, user3) 

113 make_friends(user2, user1) 

114 make_friends(user2, user3) 

115 

116 with conversations_session(token1) as c: 

117 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq()) 

118 assert len(res.group_chats) == 0 

119 

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])) 

122 

123 moderator.approve_group_chat(res1.group_chat_id) 

124 moderator.approve_group_chat(res2.group_chat_id) 

125 

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 

130 

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 

135 

136 res3 = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id])) 

137 moderator.approve_group_chat(res3.group_chat_id) 

138 

139 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq()) 

140 assert len(res.group_chats) == 3 

141 assert res.no_more 

142 

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 

147 

148 

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() 

155 

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) 

162 

163 chat_ids = [] 

164 

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")) 

187 

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")) 

196 

197 for chat_id in chat_ids: 

198 moderator.approve_group_chat(chat_id) 

199 

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" 

215 

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 ) 

222 

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" 

232 

233 

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() 

242 

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) 

249 

250 chat_ids = [] 

251 

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")) 

276 

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")) 

285 

286 for chat_id in chat_ids: 

287 moderator.approve_group_chat(chat_id) 

288 

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")) 

297 

298 # leave chat 

299 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=left_chat_id)) 

300 

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")) 

304 

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" 

315 

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" 

325 

326 

327def test_get_group_chat_messages(db): 

328 user1, token1 = generate_user() 

329 user2, token2 = generate_user() 

330 user3, token3 = generate_user() 

331 

332 make_friends(user1, user2) 

333 

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")) 

340 

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 

345 

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" 

349 

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 

354 

355 

356def test_get_group_chat_messages_pagination(db, moderator): 

357 user1, token1 = generate_user() 

358 user2, token2 = generate_user() 

359 make_friends(user1, user2) 

360 

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))) 

366 

367 moderator.approve_group_chat(group_chat_id) 

368 

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 

385 

386 

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) 

395 

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) 

400 

401 for i in range(10): 

402 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text=str(i))) 

403 

404 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id)) 

405 

406 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="10")) 

407 

408 res = c.GetGroupChatMessages(conversations_pb2.GetGroupChatMessagesReq(group_chat_id=group_chat_id)) 

409 

410 # created + 10 normal + invited + normal 

411 assert len(res.messages) == 13 

412 

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" 

419 

420 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id)) 

421 

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")) 

425 

426 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id)) 

427 

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")) 

430 

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" 

442 

443 

444def test_get_group_chat_info(db): 

445 user1, token1 = generate_user() 

446 user2, token2 = generate_user() 

447 user3, token3 = generate_user() 

448 

449 make_friends(user1, user2) 

450 make_friends(user3, user1) 

451 

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 

466 

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 

475 

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 

485 

486 

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() 

492 

493 make_friends(user1, user2) 

494 make_friends(user3, user1) 

495 

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")) 

505 

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 

510 

511 

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() 

517 

518 make_friends(user1, user2) 

519 make_friends(user3, user1) 

520 make_friends(user1, user4) 

521 

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")) 

532 

533 with conversations_session(token3) as c: 

534 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id)) 

535 

536 with conversations_session(token1) as c: 

537 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user4.id)) 

538 

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 

547 

548 

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) 

556 

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 

566 

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 

570 

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 

577 

578 

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) 

584 

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) 

594 

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 

605 

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 

617 

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 

629 

630 

631def test_make_remove_group_chat_admin(db, moderator): 

632 user1, token1 = generate_user() 

633 user2, token2 = generate_user() 

634 user3, token3 = generate_user() 

635 

636 make_friends(user1, user2) 

637 make_friends(user1, user3) 

638 make_friends(user2, user3) 

639 

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) 

645 

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." 

653 

654 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=group_chat_id, user_id=user2.id)) 

655 

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." 

661 

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 

666 

667 with conversations_session(token1) as c: 

668 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=group_chat_id, user_id=user2.id)) 

669 

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 

673 

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 

679 

680 

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) 

687 

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 ) 

702 

703 group_chat_title = "My group chat" 

704 message1 = "Test message 1" 

705 

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)) 

715 

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 

721 

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 

725 

726 moderator.approve_group_chat(group_chat_id) 

727 

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 message1 in push.content.body 

732 

733 email = email_collector.pop_for_recipient(user2.email, last=True) 

734 assert group_chat_title in email.subject 

735 assert message1 in email.plain 

736 assert message1 in email.html 

737 

738 # can't send message if not in chat 

739 with conversations_session(token3) as c: 

740 with pytest.raises(grpc.RpcError) as e: 

741 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 2")) 

742 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

743 

744 make_user_block(user2, user1) 

745 with conversations_session(token1) as c: 

746 with pytest.raises(grpc.RpcError) as e: 

747 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Message after block")) 

748 assert e.value.details() == "You can't send a message in this chat." 

749 

750 

751def test_send_direct_message(db, moderator: Moderator, push_collector: PushCollector, email_collector: EmailCollector): 

752 user1, token1 = generate_user(complete_profile=True) 

753 user2, token2 = generate_user(complete_profile=True) 

754 

755 make_friends(user1, user2) 

756 

757 # Let user2 receive email notifications for every chat message 

758 with notifications_session(token2) as n: 

759 n.SetNotificationSettings( 

760 notifications_pb2.SetNotificationSettingsReq( 

761 preferences=[ 

762 notifications_pb2.SingleNotificationPreference( 

763 topic=NotificationTopicAction.chat__message.topic, 

764 action=NotificationTopicAction.chat__message.action, 

765 delivery_method="email", 

766 enabled=True, 

767 ) 

768 ] 

769 ) 

770 ) 

771 

772 # Let user1 send two DM's to user2 

773 message1 = "Hello, user2!" 

774 message2 = "One more message." 

775 

776 with conversations_session(token1) as c1: 

777 # Send a DM from user1 to user2 

778 res = c1.SendDirectMessage(conversations_pb2.SendDirectMessageReq(recipient_user_id=user2.id, text=message1)) 

779 moderator.approve_group_chat(res.group_chat_id) 

780 

781 c1.SendDirectMessage(conversations_pb2.SendDirectMessageReq(recipient_user_id=user2.id, text=message2)) 

782 

783 # user2 should have received push and email notifications for both messages 

784 push = push_collector.pop_for_user(user2.id, last=False) 

785 assert push.topic_action == NotificationTopicAction.chat__message.display 

786 assert push.content.title == user1.name 

787 assert push.content.body == message1 

788 

789 email = email_collector.pop_for_recipient(user2.email, last=False) 

790 assert user1.name in email.subject 

791 assert message1 in email.plain 

792 assert message1 in email.html 

793 

794 push = push_collector.pop_for_user(user2.id, last=True) 

795 assert push.topic_action == NotificationTopicAction.chat__message.display 

796 assert push.content.title == user1.name 

797 assert push.content.body == message2 

798 

799 email = email_collector.pop_for_recipient(user2.email, last=True) 

800 assert user1.name in email.subject 

801 assert message2 in email.plain 

802 assert message2 in email.html 

803 

804 with conversations_session(token2) as c2: 

805 # Fetch the chat by ID returned from SendDirectMessage 

806 chat = c2.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=res.group_chat_id)) 

807 

808 assert chat.is_dm 

809 group_chat_id = chat.group_chat_id 

810 

811 # Verify that the messages was received 

812 messages = c2.GetGroupChatMessages( 

813 conversations_pb2.GetGroupChatMessagesReq(group_chat_id=group_chat_id) 

814 ).messages 

815 

816 assert len(messages) == 2 

817 assert messages[0].text.text == message2 

818 assert messages[1].text.text == message1 

819 assert messages[0].author_user_id == user1.id 

820 

821 

822def test_excessive_chat_initiations_are_reported(db, email_collector: EmailCollector): 

823 """Test that excessive chat initiations are first reported in a warning email and finally lead blocking of further contacting other users.""" 

824 user, token = generate_user() 

825 rate_limit_definition = RATE_LIMIT_DEFINITIONS[RateLimitAction.chat_initiation] 

826 with conversations_session(token) as c: 

827 # Test warning email 

828 for _ in range(rate_limit_definition.warning_limit): 

829 recipient_user, _ = generate_user() 

830 _ = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[recipient_user.id])) 

831 

832 assert email_collector.count_for_reports() == 0 

833 

834 recipient_user, _ = generate_user() 

835 _ = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[recipient_user.id])) 

836 

837 email = email_collector.pop_for_reports(last=True) 

838 assert email.plain.startswith( 

839 f"User {user.username} has sent {rate_limit_definition.warning_limit} chat initiations in the past {RATE_LIMIT_HOURS} hours." 

840 ) 

841 

842 # Test new chat initiations fail after exceeding CHAT_INITIATION_HARD_LIMIT 

843 for _ in range(rate_limit_definition.hard_limit - rate_limit_definition.warning_limit - 1): 

844 recipient_user, _ = generate_user() 

845 _ = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[recipient_user.id])) 

846 

847 assert email_collector.count_for_reports() == 0 

848 

849 recipient_user, _ = generate_user() 

850 with pytest.raises(grpc.RpcError) as exc_info: 

851 _ = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[recipient_user.id])) 

852 assert exc_info.value.code() == grpc.StatusCode.RESOURCE_EXHAUSTED 

853 assert ( 

854 exc_info.value.details() 

855 == "You have messaged a lot of users in the past 24 hours. To avoid spam, you can't contact any more users for now." 

856 ) 

857 

858 email = email_collector.pop_for_reports(last=True) 

859 assert email.plain.startswith( 

860 f"User {user.username} has sent {rate_limit_definition.hard_limit} chat initiations in the past {RATE_LIMIT_HOURS} hours." 

861 ) 

862 assert "The user has been blocked from sending further chat initiations for now." in email.plain 

863 

864 

865def test_send_direct_message_rate_limit(db, moderator, email_collector: EmailCollector): 

866 """SendDirectMessage should enforce the chat_initiation rate limit when creating a new DM, but not when sending into an existing one.""" 

867 user, token = generate_user(complete_profile=True) 

868 rate_limit_definition = RATE_LIMIT_DEFINITIONS[RateLimitAction.chat_initiation] 

869 

870 with conversations_session(token) as c: 

871 for _ in range(rate_limit_definition.warning_limit): 

872 recipient, _ = generate_user() 

873 c.SendDirectMessage(conversations_pb2.SendDirectMessageReq(recipient_user_id=recipient.id, text="hi")) 

874 

875 assert email_collector.count_for_reports() == 0 

876 

877 recipient, _ = generate_user() 

878 existing_dm_recipient_id = recipient.id 

879 c.SendDirectMessage( 

880 conversations_pb2.SendDirectMessageReq(recipient_user_id=existing_dm_recipient_id, text="hi") 

881 ) 

882 

883 email = email_collector.pop_for_reports(last=True) 

884 assert email.plain.startswith( 

885 f"User {user.username} has sent {rate_limit_definition.warning_limit} chat initiations in the past {RATE_LIMIT_HOURS} hours." 

886 ) 

887 

888 for _ in range(rate_limit_definition.hard_limit - rate_limit_definition.warning_limit - 1): 

889 recipient, _ = generate_user() 

890 c.SendDirectMessage(conversations_pb2.SendDirectMessageReq(recipient_user_id=recipient.id, text="hi")) 

891 

892 assert email_collector.count_for_reports() == 0 

893 

894 # follow-up into an existing DM must not count as a new initiation 

895 c.SendDirectMessage( 

896 conversations_pb2.SendDirectMessageReq(recipient_user_id=existing_dm_recipient_id, text="follow-up") 

897 ) 

898 assert email_collector.count_for_reports() == 0 

899 

900 recipient, _ = generate_user() 

901 with pytest.raises(grpc.RpcError) as exc_info: 

902 c.SendDirectMessage(conversations_pb2.SendDirectMessageReq(recipient_user_id=recipient.id, text="hi")) 

903 assert exc_info.value.code() == grpc.StatusCode.RESOURCE_EXHAUSTED 

904 assert ( 

905 exc_info.value.details() 

906 == "You have messaged a lot of users in the past 24 hours. To avoid spam, you can't contact any more users for now." 

907 ) 

908 

909 email = email_collector.pop_for_reports(last=True) 

910 assert email.plain.startswith( 

911 f"User {user.username} has sent {rate_limit_definition.hard_limit} chat initiations in the past {RATE_LIMIT_HOURS} hours." 

912 ) 

913 assert "The user has been blocked from sending further chat initiations for now." in email.plain 

914 

915 

916def test_leave_invite_to_group_chat(db, moderator): 

917 user1, token1 = generate_user() 

918 user2, token2 = generate_user() 

919 user3, token3 = generate_user() 

920 user4, token4 = generate_user() 

921 user5, token5 = generate_user() 

922 user6, token6 = generate_user(delete_user=True) 

923 user7, token7 = generate_user() 

924 user8, token8 = generate_user() 

925 

926 make_friends(user1, user2) 

927 make_friends(user1, user3) 

928 make_friends(user1, user5) 

929 make_friends(user1, user7) 

930 make_friends(user1, user8) 

931 make_friends(user2, user3) 

932 make_friends(user4, user3) 

933 make_user_block(user1, user7) 

934 make_user_block(user8, user1) 

935 

936 with conversations_session(token1) as c: 

937 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user5.id])) 

938 group_chat_id = res.group_chat_id 

939 moderator.approve_group_chat(group_chat_id) 

940 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 1")) 

941 

942 # other user not in chat 

943 with conversations_session(token3) as c: 

944 with pytest.raises(grpc.RpcError) as e: 

945 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id)) 

946 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

947 with pytest.raises(grpc.RpcError) as e: 

948 res = c.InviteToGroupChat( 

949 conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user4.id) 

950 ) 

951 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

952 with pytest.raises(grpc.RpcError) as e: 

953 res = c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id)) 

954 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

955 

956 with conversations_session(token2) as c: 

957 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id)) 

958 assert user3.id not in res.member_user_ids 

959 

960 # only_admins_invite defaults to true so shouldn't be able to invite 

961 with pytest.raises(grpc.RpcError) as e: 

962 res = c.InviteToGroupChat( 

963 conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id) 

964 ) 

965 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED 

966 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id)) 

967 

968 with conversations_session(token1) as c: 

969 # invite invisible user fails 

970 with pytest.raises(grpc.RpcError) as e: 

971 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user6.id)) 

972 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

973 assert e.value.details() == "Couldn't find that user." 

974 # invite fake user fails 

975 with pytest.raises(grpc.RpcError) as e: 

976 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=999)) 

977 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

978 assert e.value.details() == "Couldn't find that user." 

979 # invite blocked user fails 

980 with pytest.raises(grpc.RpcError) as e: 

981 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user7.id)) 

982 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

983 assert e.value.details() == "Couldn't find that user." 

984 # invite blocking user fails 

985 with pytest.raises(grpc.RpcError) as e: 

986 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user8.id)) 

987 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

988 assert e.value.details() == "Couldn't find that user." 

989 

990 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id)) 

991 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id)) 

992 assert user1.id in res.member_user_ids 

993 assert user5.id in res.member_user_ids 

994 assert user3.id in res.member_user_ids 

995 

996 # test non-admin inviting 

997 c.EditGroupChat( 

998 conversations_pb2.EditGroupChatReq( 

999 group_chat_id=group_chat_id, only_admins_invite=wrappers_pb2.BoolValue(value=False) 

1000 ) 

1001 ) 

1002 

1003 with conversations_session(token3) as c: 

1004 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user2.id)) 

1005 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id)) 

1006 assert user2.id in res.member_user_ids 

1007 

1008 

1009def test_group_chats_with_messages_before_join(db, moderator): 

1010 """ 

1011 If user 1 and 2 have a group chat and send messages, then add user 3; user 3 

1012 should still see the group chat! 

1013 """ 

1014 user1, token1 = generate_user() 

1015 user2, token2 = generate_user() 

1016 user3, token3 = generate_user() 

1017 user4, token4 = generate_user() 

1018 

1019 make_friends(user1, user2) 

1020 make_friends(user1, user3) 

1021 make_friends(user2, user3) 

1022 make_friends(user1, user4) 

1023 

1024 with conversations_session(token1) as c: 

1025 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user4.id])) 

1026 group_chat_id = res.group_chat_id 

1027 moderator.approve_group_chat(group_chat_id) 

1028 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 1")) 

1029 

1030 with conversations_session(token2) as c: 

1031 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 2")) 

1032 

1033 with conversations_session(token1) as c: 

1034 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 3")) 

1035 

1036 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id)) 

1037 

1038 with conversations_session(token3) as c: 

1039 # should work 

1040 c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat_id)) 

1041 

1042 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq()) 

1043 assert len(res.group_chats) == 1 

1044 

1045 

1046def test_invite_to_dm(db): 

1047 user1, token1 = generate_user() 

1048 user2, token2 = generate_user() 

1049 user3, token3 = generate_user() 

1050 

1051 make_friends(user1, user2) 

1052 make_friends(user1, user3) 

1053 make_friends(user2, user3) 

1054 

1055 with conversations_session(token1) as c: 

1056 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])) 

1057 group_chat_id = res.group_chat_id 

1058 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 1")) 

1059 

1060 # dm, shou;dn't be able to invite someone else 

1061 with pytest.raises(grpc.RpcError) as e: 

1062 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id)) 

1063 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

1064 assert e.value.details() == "You can't invite other users to a direct message." 

1065 

1066 

1067def test_sole_admin_leaves(db, moderator): 

1068 user1, token1 = generate_user() 

1069 user2, token2 = generate_user() 

1070 user3, token3 = generate_user() 

1071 

1072 make_friends(user1, user2) 

1073 make_friends(user1, user3) 

1074 make_friends(user2, user3) 

1075 

1076 with conversations_session(token1) as c: 

1077 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])) 

1078 group_chat_id = res.group_chat_id 

1079 moderator.approve_group_chat(group_chat_id) 

1080 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 1")) 

1081 

1082 # sole admin can't leave group chat 

1083 with pytest.raises(grpc.RpcError) as e: 

1084 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id)) 

1085 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

1086 assert e.value.details() == "The last admin can't leave a group chat." 

1087 

1088 with conversations_session(token2) as c: 

1089 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id)) 

1090 

1091 with conversations_session(token3) as c: 

1092 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id)) 

1093 

1094 # sole admin can leave when last in chat 

1095 with conversations_session(token1) as c: 

1096 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id)) 

1097 

1098 

1099def test_search_messages(db, moderator): 

1100 user1, token1 = generate_user() 

1101 user2, token2 = generate_user() 

1102 user3, token3 = generate_user() 

1103 

1104 make_friends(user1, user2) 

1105 make_friends(user1, user3) 

1106 

1107 with conversations_session(token1) as c: 

1108 # create some threads with messages 

1109 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])) 

1110 gc1_id = res.group_chat_id 

1111 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gc1_id, text="Test message 1")) 

1112 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gc1_id, text="Test message 2")) 

1113 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])) 

1114 gc2_id = res.group_chat_id 

1115 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gc2_id, text="Test group message 3")) 

1116 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gc2_id, text="Test group message 4")) 

1117 

1118 # Approve group chats so they appear in search results 

1119 moderator.approve_group_chat(gc1_id) 

1120 moderator.approve_group_chat(gc2_id) 

1121 

1122 with conversations_session(token1) as c: 

1123 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="message ")) 

1124 assert len(res.results) == 4 

1125 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="group ")) 

1126 assert len(res.results) == 2 

1127 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="message 5")) 

1128 assert len(res.results) == 0 

1129 

1130 # outside user doesn't get results 

1131 with conversations_session(token3) as c: 

1132 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="Test message")) 

1133 assert len(res.results) == 0 

1134 

1135 

1136def test_search_messages_left_joined(db, moderator): 

1137 user1, token1 = generate_user() 

1138 user2, token2 = generate_user() 

1139 user3, token3 = generate_user() 

1140 user4, token4 = generate_user() 

1141 make_friends(user1, user2) 

1142 make_friends(user1, user3) 

1143 make_friends(user1, user4) 

1144 

1145 with conversations_session(token1) as c: 

1146 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user4.id])) 

1147 group_chat_id = res.group_chat_id 

1148 moderator.approve_group_chat(group_chat_id) 

1149 for i in range(10): 

1150 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message " + str(i))) 

1151 

1152 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user3.id)) 

1153 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 10")) 

1154 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="Test message")) 

1155 

1156 assert len(res.results) == 11 

1157 

1158 with conversations_session(token3) as c: 

1159 # can only see last message after invited 

1160 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="Test message")) 

1161 

1162 assert len(res.results) == 1 

1163 assert res.results[0].message.text.text == "Test message 10" 

1164 

1165 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id)) 

1166 

1167 with conversations_session(token1) as c: 

1168 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 11")) 

1169 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 12")) 

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 13")) 

1172 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 14")) 

1173 

1174 with conversations_session(token3) as c: 

1175 # can only see last message after invited 

1176 res = c.SearchMessages(conversations_pb2.SearchMessagesReq(query="Test message")) 

1177 assert len(res.results) == 3 

1178 assert res.results[0].message.text.text == "Test message 14" 

1179 assert res.results[1].message.text.text == "Test message 13" 

1180 assert res.results[2].message.text.text == "Test message 10" 

1181 

1182 

1183def test_admin_behaviour(db, moderator): 

1184 user1, token1 = generate_user() 

1185 user2, token2 = generate_user() 

1186 user3, token3 = generate_user() 

1187 

1188 make_friends(user1, user2) 

1189 make_friends(user1, user3) 

1190 make_friends(user2, user3) 

1191 

1192 with conversations_session(token1) as c: 

1193 gcid = c.CreateGroupChat( 

1194 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]) 

1195 ).group_chat_id 

1196 moderator.approve_group_chat(gcid) 

1197 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user2.id)) 

1198 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1199 assert len(res.admin_user_ids) == 2 

1200 assert user1.id in res.admin_user_ids 

1201 assert user2.id in res.admin_user_ids 

1202 

1203 with conversations_session(token3) as c: 

1204 with pytest.raises(grpc.RpcError) as e: 

1205 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user3.id)) 

1206 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED 

1207 with pytest.raises(grpc.RpcError) as e: 

1208 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user1.id)) 

1209 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED 

1210 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1211 assert len(res.admin_user_ids) == 2 

1212 assert user1.id in res.admin_user_ids 

1213 assert user2.id in res.admin_user_ids 

1214 

1215 with conversations_session(token2) as c: 

1216 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user3.id)) 

1217 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1218 assert len(res.admin_user_ids) == 3 

1219 assert user1.id in res.admin_user_ids 

1220 assert user2.id in res.admin_user_ids 

1221 assert user3.id in res.admin_user_ids 

1222 

1223 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user1.id)) 

1224 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1225 assert len(res.admin_user_ids) == 2 

1226 assert user2.id in res.admin_user_ids 

1227 assert user3.id in res.admin_user_ids 

1228 

1229 with conversations_session(token1) as c: 

1230 with pytest.raises(grpc.RpcError): 

1231 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user1.id)) 

1232 with pytest.raises(grpc.RpcError): 

1233 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user3.id)) 

1234 with pytest.raises(grpc.RpcError): 

1235 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user2.id)) 

1236 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1237 assert len(res.admin_user_ids) == 2 

1238 assert user2.id in res.admin_user_ids 

1239 assert user3.id in res.admin_user_ids 

1240 

1241 with conversations_session(token2) as c: 

1242 # can demote self if there are other admins 

1243 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user2.id)) 

1244 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1245 assert len(res.admin_user_ids) == 1 

1246 assert user3.id in res.admin_user_ids 

1247 

1248 with conversations_session(token3) as c: 

1249 with pytest.raises(grpc.RpcError) as e: 

1250 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user3.id)) 

1251 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

1252 assert e.value.details() == "You can't remove the last admin." 

1253 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1254 assert len(res.admin_user_ids) == 1 

1255 assert user3.id in res.admin_user_ids 

1256 

1257 # last admin can't leave 

1258 with pytest.raises(grpc.RpcError) as e: 

1259 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=gcid)) 

1260 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

1261 assert e.value.details() == "The last admin can't leave a group chat." 

1262 

1263 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user1.id)) 

1264 

1265 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=gcid)) 

1266 

1267 with conversations_session(token2) as c: 

1268 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=gcid)) 

1269 

1270 # last participant must be admin but can leave to orphan chat 

1271 with conversations_session(token1) as c: 

1272 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=gcid)) 

1273 

1274 

1275def test_add_remove_admin_failures(db): 

1276 user1, token1 = generate_user() 

1277 user2, token2 = generate_user() 

1278 user3, token3 = generate_user() 

1279 user4, token4 = generate_user() 

1280 user5, token5 = generate_user() 

1281 

1282 make_friends(user1, user2) 

1283 make_friends(user1, user3) 

1284 make_friends(user1, user4) 

1285 make_friends(user1, user5) 

1286 

1287 with conversations_session(token1) as c: 

1288 gcid = c.CreateGroupChat( 

1289 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id, user4.id, user5.id]) 

1290 ).group_chat_id 

1291 

1292 make_user_invisible(user3.id) 

1293 make_user_block(user1, user4) 

1294 make_user_block(user5, user1) 

1295 

1296 # make non-existent user admin 

1297 with pytest.raises(grpc.RpcError) as e: 

1298 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=999)) 

1299 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

1300 assert e.value.details() == "Couldn't find that user." 

1301 

1302 # make invisible user admin 

1303 with pytest.raises(grpc.RpcError) as e: 

1304 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user3.id)) 

1305 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

1306 assert e.value.details() == "Couldn't find that user." 

1307 

1308 # make blocked user admin 

1309 with pytest.raises(grpc.RpcError) as e: 

1310 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user4.id)) 

1311 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

1312 assert e.value.details() == "Couldn't find that user." 

1313 

1314 # make blocking user admin 

1315 with pytest.raises(grpc.RpcError) as e: 

1316 c.MakeGroupChatAdmin(conversations_pb2.MakeGroupChatAdminReq(group_chat_id=gcid, user_id=user5.id)) 

1317 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

1318 assert e.value.details() == "Couldn't find that user." 

1319 

1320 with session_scope() as session: 

1321 subscriptions = ( 

1322 session.execute( 

1323 select(GroupChatSubscription) 

1324 .where(GroupChatSubscription.group_chat_id == gcid) 

1325 .where(GroupChatSubscription.role == GroupChatRole.participant) 

1326 ) 

1327 .scalars() 

1328 .all() 

1329 ) 

1330 

1331 for subscription in subscriptions: 

1332 subscription.role = GroupChatRole.admin 

1333 

1334 with conversations_session(token1) as c: 

1335 # remove non-existent user admin 

1336 with pytest.raises(grpc.RpcError) as e: 

1337 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=999)) 

1338 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

1339 assert e.value.details() == "Couldn't find that user." 

1340 

1341 # remove invisible admin 

1342 with pytest.raises(grpc.RpcError) as e: 

1343 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user3.id)) 

1344 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

1345 assert e.value.details() == "Couldn't find that user." 

1346 

1347 # remove blocked admin 

1348 with pytest.raises(grpc.RpcError) as e: 

1349 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user4.id)) 

1350 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

1351 assert e.value.details() == "Couldn't find that user." 

1352 

1353 # remove blocking admin 

1354 with pytest.raises(grpc.RpcError) as e: 

1355 c.RemoveGroupChatAdmin(conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=gcid, user_id=user5.id)) 

1356 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

1357 assert e.value.details() == "Couldn't find that user." 

1358 

1359 

1360def test_last_seen(db, moderator): 

1361 user1, token1 = generate_user() 

1362 user2, token2 = generate_user() 

1363 user3, token3 = generate_user() 

1364 

1365 make_friends(user1, user2) 

1366 make_friends(user1, user3) 

1367 make_friends(user2, user3) 

1368 

1369 with conversations_session(token3) as c: 

1370 # this is just here to mess up any issues we get if we pretend there's only one group chat ever 

1371 gcid_distraction = c.CreateGroupChat( 

1372 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user1.id]) 

1373 ).group_chat_id 

1374 moderator.approve_group_chat(gcid_distraction) 

1375 

1376 with conversations_session(token1) as c: 

1377 gcid = c.CreateGroupChat( 

1378 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]) 

1379 ).group_chat_id 

1380 moderator.approve_group_chat(gcid) 

1381 

1382 message_ids = [] 

1383 

1384 for i in range(6): 

1385 c.SendMessage( 

1386 conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text=f"gibberish message... {i}") 

1387 ) 

1388 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text=f"test message {i}")) 

1389 c.SendMessage( 

1390 conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text=f"gibberish message {i}") 

1391 ) 

1392 

1393 message_ids.append( 

1394 c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)).latest_message.message_id 

1395 ) 

1396 

1397 # messages are automatically marked as seen when you send a new message 

1398 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1399 assert res.unseen_message_count == 0 

1400 

1401 with conversations_session(token2) as c: 

1402 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1403 # created + 6 normal 

1404 assert res.unseen_message_count == 7 

1405 

1406 backward_offset = 3 

1407 c.MarkLastSeenGroupChat( 

1408 conversations_pb2.MarkLastSeenGroupChatReq( 

1409 group_chat_id=gcid, last_seen_message_id=message_ids[-backward_offset - 1] 

1410 ) 

1411 ) 

1412 

1413 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1414 assert res.unseen_message_count == backward_offset 

1415 

1416 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="test message ...")) 

1417 

1418 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1419 assert res.unseen_message_count == 0 

1420 

1421 with conversations_session(token3) as c: 

1422 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1423 # created + 7 normal 

1424 assert res.unseen_message_count == 8 

1425 

1426 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="test message ...")) 

1427 

1428 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1429 assert res.unseen_message_count == 0 

1430 

1431 

1432def test_mark_last_seen_clears_notifications(db, moderator): 

1433 user1, token1 = generate_user() 

1434 user2, token2 = generate_user() 

1435 

1436 with conversations_session(token1) as c: 

1437 gcid = c.CreateGroupChat( 

1438 conversations_pb2.CreateGroupChatReq( 

1439 recipient_user_ids=[user2.id], title=wrappers_pb2.StringValue(value="Test chat") 

1440 ) 

1441 ).group_chat_id 

1442 

1443 moderator.approve_group_chat(gcid) 

1444 

1445 with conversations_session(token1) as c: 

1446 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello")) 

1447 

1448 process_jobs() 

1449 

1450 def unseen_notification_count(user_id): 

1451 with session_scope() as session: 

1452 return session.execute( 

1453 select(func.count()) 

1454 .select_from(Notification) 

1455 .where(Notification.user_id == user_id) 

1456 .where(Notification.key == str(gcid)) 

1457 .where(Notification.is_seen == False) 

1458 ).scalar_one() 

1459 

1460 assert unseen_notification_count(user2.id) > 0 

1461 

1462 with conversations_session(token2) as c: 

1463 c.MarkLastSeenGroupChat(conversations_pb2.MarkLastSeenGroupChatReq(group_chat_id=gcid, last_seen_message_id=1)) 

1464 

1465 assert unseen_notification_count(user2.id) == 0 

1466 

1467 

1468def test_one_dm_per_pair(db, moderator): 

1469 user1, token1 = generate_user() 

1470 user2, token2 = generate_user() 

1471 user3, token3 = generate_user() 

1472 

1473 make_friends(user1, user2) 

1474 make_friends(user1, user3) 

1475 make_friends(user2, user3) 

1476 

1477 with conversations_session(token1) as c: 

1478 # create DM with user 2 

1479 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])) 

1480 assert res.is_dm 

1481 dm_with_user2 = res.group_chat_id 

1482 

1483 # create DM with user 3 

1484 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id])) 

1485 assert res.is_dm 

1486 dm_with_user3 = res.group_chat_id 

1487 

1488 # can't create another group chat with just user 2 

1489 with pytest.raises(grpc.RpcError) as e: 

1490 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])) 

1491 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

1492 

1493 # can't create another group chat with just user 3 

1494 with pytest.raises(grpc.RpcError) as e: 

1495 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id])) 

1496 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

1497 

1498 # can create joined group chat 

1499 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])) 

1500 assert not res.is_dm 

1501 

1502 # Approve the DMs so user2 can see them (otherwise they're SHADOWED and only visible to creator) 

1503 moderator.approve_group_chat(dm_with_user2) 

1504 

1505 with conversations_session(token2) as c: 

1506 # can create DM with user 3 

1507 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id])) 

1508 assert res.is_dm 

1509 

1510 # can't create another group chat with just user 1 (DM was approved, so user2 can see it) 

1511 with pytest.raises(grpc.RpcError) as e: 

1512 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user1.id])) 

1513 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

1514 

1515 

1516def test_GetDirectMessage(db): 

1517 user1, token1 = generate_user() 

1518 user2, token2 = generate_user() 

1519 user3, token3 = generate_user() 

1520 

1521 make_friends(user1, user2) 

1522 make_friends(user1, user3) 

1523 make_friends(user2, user3) 

1524 

1525 with conversations_session(token1) as c: 

1526 # no group chat with user 2 

1527 with pytest.raises(grpc.RpcError) as e: 

1528 res = c.GetDirectMessage(conversations_pb2.GetDirectMessageReq(user_id=user2.id)) 

1529 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

1530 

1531 # no group chat with nor user 3 

1532 with pytest.raises(grpc.RpcError) as e: 

1533 res = c.GetDirectMessage(conversations_pb2.GetDirectMessageReq(user_id=user3.id)) 

1534 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

1535 

1536 # create DM with user 2 

1537 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])) 

1538 assert res.is_dm 

1539 gcid = res.group_chat_id 

1540 

1541 # now should exist 

1542 res = c.GetDirectMessage(conversations_pb2.GetDirectMessageReq(user_id=user2.id)) 

1543 assert res.group_chat_id == gcid 

1544 assert not res.is_archived 

1545 

1546 # create DM with user 3 

1547 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id])) 

1548 assert res.is_dm 

1549 

1550 # can create joined group chat 

1551 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])) 

1552 assert not res.is_dm 

1553 

1554 with conversations_session(token2) as c: 

1555 # can create DM with user 3 

1556 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id])) 

1557 assert res.is_dm 

1558 assert res.can_message 

1559 gcid = res.group_chat_id 

1560 

1561 # DM with 3 should exist, but can't message after being blocked 

1562 make_user_block(user3, user2) 

1563 res = c.GetDirectMessage(conversations_pb2.GetDirectMessageReq(user_id=user3.id)) 

1564 assert res.group_chat_id == gcid 

1565 assert not res.can_message 

1566 

1567 

1568def test_total_unseen(db, moderator): 

1569 user1, token1 = generate_user() 

1570 user2, token2 = generate_user() 

1571 user3, token3 = generate_user() 

1572 

1573 # distractions 

1574 user4, token4 = generate_user() 

1575 

1576 make_friends(user1, user2) 

1577 make_friends(user1, user3) 

1578 make_friends(user2, user3) 

1579 

1580 # distractions 

1581 make_friends(user1, user4) 

1582 

1583 with conversations_session(token1) as c: 

1584 # distractions 

1585 gcid_distraction = c.CreateGroupChat( 

1586 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user4.id]) 

1587 ).group_chat_id 

1588 moderator.approve_group_chat(gcid_distraction) 

1589 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text="distraction...")) 

1590 

1591 gcid = c.CreateGroupChat( 

1592 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]) 

1593 ).group_chat_id 

1594 moderator.approve_group_chat(gcid) 

1595 

1596 for i in range(6): 

1597 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text=f"test message {i}")) 

1598 

1599 # distractions 

1600 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text="distraction...")) 

1601 

1602 # messages are automatically marked as seen when you send a new message 

1603 with api_session(token1) as api: 

1604 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 0 

1605 

1606 with api_session(token2) as api: 

1607 # chat created + 6 normal messages 

1608 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 7 

1609 

1610 # now leave chat with user2 

1611 with conversations_session(token2) as c: 

1612 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=gcid)) 

1613 

1614 with api_session(token2) as api: 

1615 # seen messages becomes 0 when leaving 

1616 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 0 

1617 

1618 with conversations_session(token1) as c: 

1619 # distractions 

1620 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text="distraction...")) 

1621 

1622 # send more stuff without user 2 

1623 for i in range(3): 

1624 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text=f"test message {i}")) 

1625 

1626 # distractions 

1627 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text="distraction...")) 

1628 

1629 with api_session(token2) as api: 

1630 # seen messages becomes 0 when leaving 

1631 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 0 

1632 

1633 with conversations_session(token1) as c: 

1634 # add user 2 back 

1635 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=gcid, user_id=user2.id)) 

1636 

1637 # send more stuff with user 2 

1638 for i in range(12): 

1639 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text=f"test message {i}")) 

1640 

1641 # distractions 

1642 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid_distraction, text="distraction...")) 

1643 

1644 with api_session(token2) as api: 

1645 # joined + 12 normal 

1646 assert api.Ping(api_pb2.PingReq()).unseen_message_count == 13 

1647 

1648 

1649def test_regression_ListGroupChats_pagination(db, moderator): 

1650 user1, token1 = generate_user() 

1651 user2, token2 = generate_user() 

1652 user3, token3 = generate_user() 

1653 

1654 make_friends(user1, user2) 

1655 make_friends(user1, user3) 

1656 

1657 with conversations_session(token1) as c: 

1658 # tuples of (group_chat_id, message_id) 

1659 group_chat_and_message_ids = [] 

1660 for i in range(50): 

1661 res1 = c.CreateGroupChat( 

1662 conversations_pb2.CreateGroupChatReq( 

1663 recipient_user_ids=[user2.id, user3.id], title=wrappers_pb2.StringValue(value=f"Chat {i}") 

1664 ) 

1665 ) 

1666 

1667 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res1.group_chat_id, text=f"Test message {i}")) 

1668 

1669 res2 = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=res1.group_chat_id)) 

1670 

1671 group_chat_and_message_ids.append((res2.group_chat_id, res2.latest_message.message_id)) 

1672 moderator.approve_group_chat(res1.group_chat_id) 

1673 

1674 seen_group_chat_ids = [] 

1675 

1676 last_message_id = 0 

1677 more = True 

1678 while more: 

1679 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(last_message_id=last_message_id)) 

1680 last_message_id = res.last_message_id 

1681 more = not res.no_more 

1682 

1683 seen_group_chat_ids.extend([chat.group_chat_id for chat in res.group_chats]) 

1684 

1685 assert set(seen_group_chat_ids) == {x[0] for x in group_chat_and_message_ids}, "Not all group chats returned" 

1686 

1687 

1688def test_muting(db, moderator): 

1689 user1, token1 = generate_user() 

1690 user2, token2 = generate_user() 

1691 user3, token3 = generate_user() 

1692 

1693 make_friends(user1, user2) 

1694 make_friends(user1, user3) 

1695 make_friends(user2, user3) 

1696 

1697 with conversations_session(token3) as c: 

1698 # this is just here to mess up any issues we get if we pretend there's only one group chat ever 

1699 gcid_distraction = c.CreateGroupChat( 

1700 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user1.id]) 

1701 ).group_chat_id 

1702 moderator.approve_group_chat(gcid_distraction) 

1703 

1704 with conversations_session(token1) as c: 

1705 gcid = c.CreateGroupChat( 

1706 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]) 

1707 ).group_chat_id 

1708 moderator.approve_group_chat(gcid) 

1709 

1710 with conversations_session(token2) as c: 

1711 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1712 assert not res.mute_info.muted 

1713 assert not res.mute_info.HasField("muted_until") 

1714 

1715 c.MuteGroupChat(conversations_pb2.MuteGroupChatReq(group_chat_id=gcid, forever=True)) 

1716 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1717 assert res.mute_info.muted 

1718 assert not res.mute_info.HasField("muted_until") 

1719 

1720 c.MuteGroupChat(conversations_pb2.MuteGroupChatReq(group_chat_id=gcid, unmute=True)) 

1721 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1722 assert not res.mute_info.muted 

1723 assert not res.mute_info.HasField("muted_until") 

1724 

1725 c.MuteGroupChat( 

1726 conversations_pb2.MuteGroupChatReq( 

1727 group_chat_id=gcid, for_duration=Duration_from_timedelta(timedelta(hours=2)) 

1728 ) 

1729 ) 

1730 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1731 assert res.mute_info.muted 

1732 assert res.mute_info.HasField("muted_until") 

1733 assert to_aware_datetime(res.mute_info.muted_until) >= now() + timedelta(hours=1, minutes=59) 

1734 assert to_aware_datetime(res.mute_info.muted_until) <= now() + timedelta(hours=2, minutes=1) 

1735 

1736 

1737def test_archiving(db, moderator): 

1738 """Test SetGroupChatArchiveStatus RPC with GetGroupChat and ListGroupChats""" 

1739 user1, token1 = generate_user() 

1740 user2, token2 = generate_user() 

1741 user3, token3 = generate_user() 

1742 

1743 make_friends(user1, user2) 

1744 make_friends(user1, user3) 

1745 make_friends(user2, user3) 

1746 

1747 # Create a distraction chat to ensure we're testing the right one 

1748 with conversations_session(token3) as c: 

1749 gcid_distraction = c.CreateGroupChat( 

1750 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user1.id]) 

1751 ).group_chat_id 

1752 moderator.approve_group_chat(gcid_distraction) 

1753 

1754 with conversations_session(token1) as c: 

1755 gcid = c.CreateGroupChat( 

1756 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]) 

1757 ).group_chat_id 

1758 moderator.approve_group_chat(gcid) 

1759 

1760 # Test basic archive/unarchive functionality 

1761 with conversations_session(token2) as c: 

1762 # Initially not archived 

1763 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1764 assert not res.is_archived 

1765 

1766 # Archive the chat 

1767 archive_res = c.SetGroupChatArchiveStatus( 

1768 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True) 

1769 ) 

1770 assert archive_res.group_chat_id == gcid 

1771 assert archive_res.is_archived 

1772 

1773 # Verify archived via GetGroupChat 

1774 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1775 assert res.is_archived 

1776 

1777 # Unarchive the chat 

1778 archive_res = c.SetGroupChatArchiveStatus( 

1779 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=False) 

1780 ) 

1781 assert archive_res.group_chat_id == gcid 

1782 assert not archive_res.is_archived 

1783 

1784 # Verify unarchived via GetGroupChat 

1785 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1786 assert not res.is_archived 

1787 

1788 

1789def test_archiving_per_user(db, moderator): 

1790 """Test that archiving is per-user - one user archiving doesn't affect others""" 

1791 user1, token1 = generate_user() 

1792 user2, token2 = generate_user() 

1793 

1794 make_friends(user1, user2) 

1795 

1796 with conversations_session(token1) as c: 

1797 gcid = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])).group_chat_id 

1798 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello")) 

1799 moderator.approve_group_chat(gcid) 

1800 

1801 # User1 archives the chat 

1802 with conversations_session(token1) as c: 

1803 c.SetGroupChatArchiveStatus( 

1804 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True) 

1805 ) 

1806 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1807 assert res.is_archived 

1808 

1809 # User2 should NOT see it as archived 

1810 with conversations_session(token2) as c: 

1811 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1812 assert not res.is_archived 

1813 

1814 # User2 archives it too 

1815 with conversations_session(token2) as c: 

1816 c.SetGroupChatArchiveStatus( 

1817 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True) 

1818 ) 

1819 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1820 assert res.is_archived 

1821 

1822 # User1 unarchives - user2 should still see it as archived 

1823 with conversations_session(token1) as c: 

1824 c.SetGroupChatArchiveStatus( 

1825 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=False) 

1826 ) 

1827 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1828 assert not res.is_archived 

1829 

1830 with conversations_session(token2) as c: 

1831 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1832 assert res.is_archived 

1833 

1834 

1835def test_archiving_with_list_group_chats(db, moderator): 

1836 """Test archive filtering with ListGroupChats endpoint""" 

1837 user1, token1 = generate_user() 

1838 user2, token2 = generate_user() 

1839 user3, token3 = generate_user() 

1840 

1841 make_friends(user1, user2) 

1842 make_friends(user1, user3) 

1843 

1844 with conversations_session(token1) as c: 

1845 # Create 3 chats 

1846 gcid1 = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])).group_chat_id 

1847 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid1, text="Chat 1")) 

1848 moderator.approve_group_chat(gcid1) 

1849 

1850 gcid2 = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id])).group_chat_id 

1851 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid2, text="Chat 2")) 

1852 moderator.approve_group_chat(gcid2) 

1853 

1854 gcid3 = c.CreateGroupChat( 

1855 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]) 

1856 ).group_chat_id 

1857 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid3, text="Chat 3")) 

1858 moderator.approve_group_chat(gcid3) 

1859 

1860 with conversations_session(token1) as c: 

1861 # Initially all 3 chats should be visible 

1862 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq()) 

1863 assert len(res.group_chats) == 3 

1864 

1865 # All should be non-archived 

1866 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=False)) 

1867 assert len(res.group_chats) == 3 

1868 

1869 # None should be archived 

1870 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=True)) 

1871 assert len(res.group_chats) == 0 

1872 

1873 # Archive chat 1 

1874 c.SetGroupChatArchiveStatus( 

1875 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid1, is_archived=True) 

1876 ) 

1877 

1878 # Without filter, still see all 3 

1879 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq()) 

1880 assert len(res.group_chats) == 3 

1881 

1882 # Non-archived should show 2 

1883 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=False)) 

1884 assert len(res.group_chats) == 2 

1885 assert gcid1 not in [gc.group_chat_id for gc in res.group_chats] 

1886 

1887 # Archived should show 1 

1888 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=True)) 

1889 assert len(res.group_chats) == 1 

1890 assert res.group_chats[0].group_chat_id == gcid1 

1891 assert res.group_chats[0].is_archived 

1892 

1893 # Archive chat 2 as well 

1894 c.SetGroupChatArchiveStatus( 

1895 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid2, is_archived=True) 

1896 ) 

1897 

1898 # Non-archived should show 1 

1899 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=False)) 

1900 assert len(res.group_chats) == 1 

1901 assert res.group_chats[0].group_chat_id == gcid3 

1902 

1903 # Archived should show 2 

1904 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=True)) 

1905 assert len(res.group_chats) == 2 

1906 archived_ids = {gc.group_chat_id for gc in res.group_chats} 

1907 assert archived_ids == {gcid1, gcid2} 

1908 

1909 # Unarchive chat 1 

1910 c.SetGroupChatArchiveStatus( 

1911 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid1, is_archived=False) 

1912 ) 

1913 

1914 # Non-archived should show 2 

1915 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=False)) 

1916 assert len(res.group_chats) == 2 

1917 

1918 # Archived should show 1 

1919 res = c.ListGroupChats(conversations_pb2.ListGroupChatsReq(only_archived=True)) 

1920 assert len(res.group_chats) == 1 

1921 assert res.group_chats[0].group_chat_id == gcid2 

1922 

1923 

1924def test_archiving_chat_not_found(db, moderator): 

1925 """Test that archiving a non-existent or non-accessible chat fails""" 

1926 user1, token1 = generate_user() 

1927 user2, token2 = generate_user() 

1928 user3, token3 = generate_user() 

1929 

1930 make_friends(user1, user2) 

1931 

1932 with conversations_session(token1) as c: 

1933 gcid = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])).group_chat_id 

1934 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello")) 

1935 moderator.approve_group_chat(gcid) 

1936 

1937 # User3 is not in the chat - should get NOT_FOUND 

1938 with conversations_session(token3) as c: 

1939 with pytest.raises(grpc.RpcError) as e: 

1940 c.SetGroupChatArchiveStatus( 

1941 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True) 

1942 ) 

1943 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

1944 

1945 # Non-existent chat ID should fail 

1946 with conversations_session(token1) as c: 

1947 with pytest.raises(grpc.RpcError) as e: 

1948 c.SetGroupChatArchiveStatus( 

1949 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=99999, is_archived=True) 

1950 ) 

1951 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

1952 

1953 

1954def test_archiving_after_leaving_chat(db, moderator): 

1955 """Test that you can't archive a chat after leaving it""" 

1956 user1, token1 = generate_user() 

1957 user2, token2 = generate_user() 

1958 user3, token3 = generate_user() 

1959 

1960 make_friends(user1, user2) 

1961 make_friends(user1, user3) 

1962 make_friends(user2, user3) 

1963 

1964 with conversations_session(token1) as c: 

1965 gcid = c.CreateGroupChat( 

1966 conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id]) 

1967 ).group_chat_id 

1968 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello")) 

1969 moderator.approve_group_chat(gcid) 

1970 

1971 # User2 leaves the chat 

1972 with conversations_session(token2) as c: 

1973 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=gcid)) 

1974 

1975 # User2 should not be able to archive it now 

1976 with conversations_session(token2) as c: 

1977 with pytest.raises(grpc.RpcError) as e: 

1978 c.SetGroupChatArchiveStatus( 

1979 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True) 

1980 ) 

1981 assert e.value.code() == grpc.StatusCode.NOT_FOUND 

1982 

1983 

1984def test_archiving_dm(db, moderator): 

1985 """Test that archiving works for DMs as well as group chats""" 

1986 user1, token1 = generate_user() 

1987 user2, token2 = generate_user() 

1988 

1989 make_friends(user1, user2) 

1990 

1991 with conversations_session(token1) as c: 

1992 # Create a DM 

1993 gcid = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])).group_chat_id 

1994 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello")) 

1995 moderator.approve_group_chat(gcid) 

1996 

1997 # Verify it's a DM 

1998 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

1999 assert res.is_dm 

2000 assert not res.is_archived 

2001 

2002 # Archive the DM 

2003 c.SetGroupChatArchiveStatus( 

2004 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True) 

2005 ) 

2006 

2007 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

2008 assert res.is_archived 

2009 

2010 # Unarchive it 

2011 c.SetGroupChatArchiveStatus( 

2012 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=False) 

2013 ) 

2014 

2015 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

2016 assert not res.is_archived 

2017 

2018 

2019def test_archiving_idempotent(db, moderator): 

2020 """Test that archiving/unarchiving is idempotent""" 

2021 user1, token1 = generate_user() 

2022 user2, token2 = generate_user() 

2023 

2024 make_friends(user1, user2) 

2025 

2026 with conversations_session(token1) as c: 

2027 gcid = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])).group_chat_id 

2028 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=gcid, text="Hello")) 

2029 moderator.approve_group_chat(gcid) 

2030 

2031 # Archive twice - should work without error 

2032 c.SetGroupChatArchiveStatus( 

2033 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True) 

2034 ) 

2035 c.SetGroupChatArchiveStatus( 

2036 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=True) 

2037 ) 

2038 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

2039 assert res.is_archived 

2040 

2041 # Unarchive twice - should work without error 

2042 c.SetGroupChatArchiveStatus( 

2043 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=False) 

2044 ) 

2045 c.SetGroupChatArchiveStatus( 

2046 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=gcid, is_archived=False) 

2047 ) 

2048 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=gcid)) 

2049 assert not res.is_archived 

2050 

2051 

2052def test_chat_notifications(db, moderator): 

2053 user1, token1 = generate_user() 

2054 user2, token2 = generate_user() 

2055 # notifs off 

2056 user3, token3 = generate_user() 

2057 user4, token4 = generate_user() 

2058 user5, token5 = generate_user() 

2059 user6, token6 = generate_user() 

2060 

2061 make_friends(user1, user2) 

2062 make_friends(user1, user3) 

2063 make_friends(user1, user4) 

2064 make_friends(user4, user5) 

2065 make_friends(user4, user6) 

2066 

2067 # have some of them enable/disable notifs 

2068 topic_action = NotificationTopicAction.chat__message 

2069 for token, enabled in [ 

2070 (token1, True), 

2071 (token2, True), 

2072 (token3, False), 

2073 (token4, True), 

2074 (token5, True), 

2075 (token6, True), 

2076 ]: 

2077 with notifications_session(token) as notifications: 

2078 notifications.SetNotificationSettings( 

2079 notifications_pb2.SetNotificationSettingsReq( 

2080 preferences=[ 

2081 notifications_pb2.SingleNotificationPreference( 

2082 topic=topic_action.topic, 

2083 action=topic_action.action, 

2084 delivery_method=delivery_method, 

2085 enabled=enabled, 

2086 ) 

2087 for delivery_method in ["push", "email", "digest"] 

2088 ], 

2089 ) 

2090 ) 

2091 

2092 group_chat_id = None 

2093 

2094 def send_msg(c, i): 

2095 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text=f"Test message {i}")) 

2096 

2097 with conversations_session(token1) as c: 

2098 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id, user4.id])) 

2099 group_chat_id = res.group_chat_id 

2100 moderator.approve_group_chat(group_chat_id) 

2101 c.EditGroupChat( 

2102 conversations_pb2.EditGroupChatReq( 

2103 group_chat_id=group_chat_id, only_admins_invite=wrappers_pb2.BoolValue(value=False) 

2104 ) 

2105 ) 

2106 send_msg(c, i=1) 

2107 send_msg(c, i=2) 

2108 

2109 with conversations_session(token4) as c: 

2110 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user5.id)) 

2111 send_msg(c, i=3) 

2112 c.InviteToGroupChat(conversations_pb2.InviteToGroupChatReq(group_chat_id=group_chat_id, user_id=user6.id)) 

2113 send_msg(c, i=4) 

2114 send_msg(c, i=5) 

2115 

2116 with conversations_session(token3) as c: 

2117 send_msg(c, i=6) 

2118 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id)) 

2119 

2120 with conversations_session(token2) as c: 

2121 send_msg(c, i=7) 

2122 c.LeaveGroupChat(conversations_pb2.LeaveGroupChatReq(group_chat_id=group_chat_id)) 

2123 

2124 with conversations_session(token6) as c: 

2125 send_msg(c, i=8) 

2126 

2127 # go through all bg jobs 

2128 while process_job(): 

2129 pass 

2130 

2131 # now check notifs... 

2132 expected_notifs = [ 

2133 (user1, "user1", [3, 4, 5, 6, 7, 8]), 

2134 (user2, "user2", [1, 2, 3, 4, 5, 6]), 

2135 (user3, "user3", []), # notifs off 

2136 (user4, "user4", [1, 2, 6, 7, 8]), 

2137 (user5, "user5", [3, 4, 5, 6, 7, 8]), 

2138 (user6, "user6", [4, 5, 6, 7]), 

2139 ] 

2140 

2141 with session_scope() as session: 

2142 for user, label, expected_msgs in expected_notifs: 

2143 deliv = ( 

2144 session.execute( 

2145 select(Notification.data) 

2146 .join(NotificationDelivery, NotificationDelivery.notification_id == Notification.id) 

2147 .where(Notification.user_id == user.id) 

2148 .where(Notification.topic_action == topic_action) 

2149 .where(NotificationDelivery.delivery_type == NotificationDeliveryType.push) 

2150 .order_by(Notification.created) 

2151 ) 

2152 .scalars() 

2153 .all() 

2154 ) 

2155 

2156 def parse_message_payload(data): 

2157 return notification_data_pb2.ChatMessage.FromString(data).text 

2158 

2159 contents = [parse_message_payload(d) for d in deliv] 

2160 

2161 print(contents) 

2162 

2163 assert [f"Test message {i}" for i in expected_msgs] == contents, f"Wrong messages for {label}" 

2164 

2165 

2166def test_incomplete_profile(db): 

2167 user1, token1 = generate_user(complete_profile=True) 

2168 user2, token2 = generate_user(complete_profile=False) 

2169 user3, token3 = generate_user(complete_profile=True) 

2170 make_friends(user1, user2) 

2171 make_friends(user1, user3) 

2172 

2173 # user 1 can make a chat 

2174 with conversations_session(token1) as c: 

2175 res = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])) 

2176 group_chat_id = res.group_chat_id 

2177 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 1")) 

2178 res = c.GetGroupChatMessages(conversations_pb2.GetGroupChatMessagesReq(group_chat_id=group_chat_id)) 

2179 assert res.messages[0].text.text == "Test message 1" 

2180 assert to_aware_datetime(res.messages[0].time) <= now() 

2181 assert res.messages[0].author_user_id == user1.id 

2182 

2183 # user 2 cannot 

2184 with conversations_session(token2) as c: 

2185 with pytest.raises(grpc.RpcError) as e: 

2186 c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user3.id])) 

2187 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

2188 assert e.value.details() == "You have to complete your profile before you can send a message."