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

1322 statements  

« prev     ^ index     » next       coverage.py v7.15.3, created at 2026-08-04 22:32 +0000

1from datetime import timedelta 

2from unittest.mock import patch 

3 

4import grpc 

5import pytest 

6from google.protobuf import empty_pb2, wrappers_pb2 

7from sqlalchemy import func, select 

8 

9from couchers.db import session_scope 

10from couchers.jobs.handlers import send_message_notifications 

11from couchers.jobs.worker import process_job 

12from couchers.models import ( 

13 GroupChatRole, 

14 GroupChatSubscription, 

15 Notification, 

16 NotificationDelivery, 

17 NotificationDeliveryType, 

18 NotificationTopicAction, 

19 RateLimitAction, 

20) 

21from couchers.proto import api_pb2, conversations_pb2, notification_data_pb2, notifications_pb2 

22from couchers.rate_limits.definitions import RATE_LIMIT_DEFINITIONS, RATE_LIMIT_HOURS 

23from couchers.utils import Duration_from_timedelta, now, to_aware_datetime 

24from tests.fixtures.db import generate_user, make_friends, make_user_block, make_user_invisible 

25from tests.fixtures.misc import EmailCollector, Moderator, PushCollector, now_5_min_in_future, process_jobs 

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

27 

28 

29@pytest.fixture(autouse=True) 

30def _(testconfig): 

31 pass 

32 

33 

34def test_list_group_chats(db, moderator): 

35 user1, token1 = generate_user() 

36 user2, token2 = generate_user() 

37 user3, token3 = generate_user() 

38 

39 make_friends(user2, user1) 

40 make_friends(user1, user3) 

41 

42 with conversations_session(token1) as c: 

43 # no threads initially 

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

45 assert len(res.group_chats) == 0 

46 

47 # create some group chats with messages 

48 res = c.CreateGroupChat( 

49 conversations_pb2.CreateGroupChatReq( 

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

51 ) 

52 ) 

53 group_chat1_id = res.group_chat_id 

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

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

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

57 group_chat2_id = res.group_chat_id 

58 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test group message 1")) 

59 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test group message 2")) 

60 

61 moderator.approve_group_chat(group_chat1_id) 

62 moderator.approve_group_chat(group_chat2_id) 

63 

64 with conversations_session(token1) as c: 

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

66 assert len(res.group_chats) == 2 

67 assert res.no_more 

68 

69 with conversations_session(token2) as c: 

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

71 assert len(res.group_chats) == 2 

72 assert res.no_more 

73 

74 with conversations_session(token3) as c: 

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

76 assert len(res.group_chats) == 1 

77 assert res.no_more 

78 

79 # Test archive filtering: archive group_chat1 for user1 

80 with conversations_session(token1) as c: 

81 res = c.SetGroupChatArchiveStatus( 

82 conversations_pb2.SetGroupChatArchiveStatusReq(group_chat_id=group_chat1_id, is_archived=True) 

83 ) 

84 assert res.group_chat_id == group_chat1_id 

85 assert res.is_archived 

86 

87 with conversations_session(token1) as c: 

88 # Without filter, returns all chats 

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

90 assert len(res.group_chats) == 2 

91 

92 # only_archived=False returns non-archived chats only 

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

94 assert len(res.group_chats) == 1 

95 

96 # only_archived=True returns archived chats only 

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

98 assert len(res.group_chats) == 1 

99 

100 # user2 should still see both as non-archived (archive is per-user) 

101 with conversations_session(token2) as c: 

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

103 assert len(res.group_chats) == 2 

104 

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

106 assert len(res.group_chats) == 0 

107 

108 

109def test_list_empty_group_chats(db, moderator): 

110 user1, token1 = generate_user() 

111 user2, token2 = generate_user() 

112 user3, token3 = generate_user() 

113 

114 make_friends(user1, user3) 

115 make_friends(user2, user1) 

116 make_friends(user2, user3) 

117 

118 with conversations_session(token1) as c: 

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

120 assert len(res.group_chats) == 0 

121 

122 res1 = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id])) 

123 res2 = c.CreateGroupChat(conversations_pb2.CreateGroupChatReq(recipient_user_ids=[user2.id, user3.id])) 

124 

125 moderator.approve_group_chat(res1.group_chat_id) 

126 moderator.approve_group_chat(res2.group_chat_id) 

127 

128 with conversations_session(token1) as c: 

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

130 assert len(res.group_chats) == 2 

131 assert res.no_more 

132 

133 with conversations_session(token2) as c: 

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

135 assert len(res.group_chats) == 2 

136 assert res.no_more 

137 

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

139 moderator.approve_group_chat(res3.group_chat_id) 

140 

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

142 assert len(res.group_chats) == 3 

143 assert res.no_more 

144 

145 with conversations_session(token3) as c: 

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

147 assert len(res.group_chats) == 2 

148 assert res.no_more 

149 

150 

151def test_list_group_chats_ordering(db, moderator): 

152 # user is member of 5 group chats, order them correctly 

153 user, token = generate_user() 

154 user2, token2 = generate_user() 

155 user3, token3 = generate_user() 

156 user4, token4 = generate_user() 

157 

158 make_friends(user2, user) 

159 make_friends(user2, user3) 

160 make_friends(user2, user4) 

161 make_friends(user3, user) 

162 make_friends(user3, user4) 

163 make_friends(user, user4) 

164 

165 chat_ids = [] 

166 

167 with conversations_session(token2) as c: 

168 res = c.CreateGroupChat( 

169 conversations_pb2.CreateGroupChatReq( 

170 recipient_user_ids=[user.id], title=wrappers_pb2.StringValue(value="Chat 0") 

171 ) 

172 ) 

173 chat_ids.append(res.group_chat_id) 

174 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message")) 

175 res = c.CreateGroupChat( 

176 conversations_pb2.CreateGroupChatReq( 

177 recipient_user_ids=[user.id, user3.id], title=wrappers_pb2.StringValue(value="Chat 1") 

178 ) 

179 ) 

180 chat_ids.append(res.group_chat_id) 

181 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message")) 

182 res = c.CreateGroupChat( 

183 conversations_pb2.CreateGroupChatReq( 

184 recipient_user_ids=[user.id, user3.id], title=wrappers_pb2.StringValue(value="Chat 2") 

185 ) 

186 ) 

187 chat_ids.append(res.group_chat_id) 

188 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message")) 

189 

190 with conversations_session(token3) as c: 

191 res = c.CreateGroupChat( 

192 conversations_pb2.CreateGroupChatReq( 

193 recipient_user_ids=[user.id, user2.id, user4.id], title=wrappers_pb2.StringValue(value="Chat 3") 

194 ) 

195 ) 

196 chat_ids.append(res.group_chat_id) 

197 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message")) 

198 

199 for chat_id in chat_ids: 

200 moderator.approve_group_chat(chat_id) 

201 

202 with conversations_session(token) as c: 

203 res = c.CreateGroupChat( 

204 conversations_pb2.CreateGroupChatReq( 

205 recipient_user_ids=[user2.id, user3.id, user4.id], title=wrappers_pb2.StringValue(value="Chat 4") 

206 ) 

207 ) 

208 moderator.approve_group_chat(res.group_chat_id) 

209 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message")) 

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

211 assert len(res.group_chats) == 5 

212 assert res.group_chats[0].title == "Chat 4" 

213 assert res.group_chats[1].title == "Chat 3" 

214 assert res.group_chats[2].title == "Chat 2" 

215 assert res.group_chats[3].title == "Chat 1" 

216 assert res.group_chats[4].title == "Chat 0" 

217 

218 c.SendMessage( 

219 conversations_pb2.SendMessageReq(group_chat_id=res.group_chats[3].group_chat_id, text="Test message 2a") 

220 ) 

221 c.SendMessage( 

222 conversations_pb2.SendMessageReq(group_chat_id=res.group_chats[2].group_chat_id, text="Test message 2b") 

223 ) 

224 

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

226 assert len(res.group_chats) == 5 

227 assert res.group_chats[0].title == "Chat 2" 

228 assert res.group_chats[0].latest_message.text.text == "Test message 2b" 

229 assert res.group_chats[1].title == "Chat 1" 

230 assert res.group_chats[1].latest_message.text.text == "Test message 2a" 

231 assert res.group_chats[2].title == "Chat 4" 

232 assert res.group_chats[3].title == "Chat 3" 

233 assert res.group_chats[4].title == "Chat 0" 

234 

235 

236def test_list_group_chats_ordering_after_left(db, moderator): 

237 # user is member to 4 group chats, and has left one. 

238 # The one user left has the most recent message, but user left before then, 

239 # this should display as e.g. 3rd most recent depending on last message when they were in the chat 

240 user, token = generate_user() 

241 user2, token2 = generate_user() 

242 user3, token3 = generate_user() 

243 user4, token4 = generate_user() 

244 

245 make_friends(user2, user) 

246 make_friends(user2, user3) 

247 make_friends(user2, user4) 

248 make_friends(user3, user) 

249 make_friends(user3, user4) 

250 make_friends(user, user4) 

251 

252 chat_ids = [] 

253 

254 with conversations_session(token2) as c: 

255 res = c.CreateGroupChat( 

256 conversations_pb2.CreateGroupChatReq( 

257 recipient_user_ids=[user.id], title=wrappers_pb2.StringValue(value="Chat 0") 

258 ) 

259 ) 

260 chat_ids.append(res.group_chat_id) 

261 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message")) 

262 res = c.CreateGroupChat( 

263 conversations_pb2.CreateGroupChatReq( 

264 recipient_user_ids=[user.id, user3.id], title=wrappers_pb2.StringValue(value="Left Chat 1") 

265 ) 

266 ) 

267 left_chat_id = res.group_chat_id 

268 chat_ids.append(left_chat_id) 

269 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=left_chat_id, text="Test message")) 

270 res = c.CreateGroupChat( 

271 conversations_pb2.CreateGroupChatReq( 

272 recipient_user_ids=[user.id, user3.id], title=wrappers_pb2.StringValue(value="Chat 2") 

273 ) 

274 ) 

275 chat2_id = res.group_chat_id 

276 chat_ids.append(chat2_id) 

277 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=chat2_id, text="Test message")) 

278 

279 with conversations_session(token3) as c: 

280 res = c.CreateGroupChat( 

281 conversations_pb2.CreateGroupChatReq( 

282 recipient_user_ids=[user.id, user2.id, user4.id], title=wrappers_pb2.StringValue(value="Chat 3") 

283 ) 

284 ) 

285 chat_ids.append(res.group_chat_id) 

286 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message")) 

287 

288 for chat_id in chat_ids: 

289 moderator.approve_group_chat(chat_id) 

290 

291 with conversations_session(token) as c: 

292 res = c.CreateGroupChat( 

293 conversations_pb2.CreateGroupChatReq( 

294 recipient_user_ids=[user2.id, user3.id, user4.id], title=wrappers_pb2.StringValue(value="Chat 4") 

295 ) 

296 ) 

297 moderator.approve_group_chat(res.group_chat_id) 

298 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test message")) 

299 

300 # leave chat 

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

302 

303 with conversations_session(token3) as c: 

304 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=chat2_id, text="Test message")) 

305 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=left_chat_id, text="Test message")) 

306 

307 with conversations_session(token2) as c: 

308 # other user sends a message to that chat 

309 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=left_chat_id, text="Another test message")) 

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

311 assert len(res.group_chats) == 5 

312 assert res.group_chats[0].title == "Left Chat 1" 

313 assert res.group_chats[1].title == "Chat 2" 

314 assert res.group_chats[2].title == "Chat 4" 

315 assert res.group_chats[3].title == "Chat 3" 

316 assert res.group_chats[4].title == "Chat 0" 

317 

318 with conversations_session(token) as c: 

319 # we can't see the new message since we left before it was sent 

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

321 assert len(res.group_chats) == 5 

322 assert res.group_chats[0].title == "Chat 2" 

323 assert res.group_chats[1].title == "Left Chat 1" 

324 assert res.group_chats[2].title == "Chat 4" 

325 assert res.group_chats[3].title == "Chat 3" 

326 assert res.group_chats[4].title == "Chat 0" 

327 

328 

329def test_get_group_chat_messages(db): 

330 user1, token1 = generate_user() 

331 user2, token2 = generate_user() 

332 user3, token3 = generate_user() 

333 

334 make_friends(user1, user2) 

335 

336 with conversations_session(token1) as c: 

337 # create some threads with messages 

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

339 group_chat_id = res.group_chat_id 

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

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

342 

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

344 # created + 2 normal 

345 assert len(res.messages) == 3 

346 assert res.no_more 

347 

348 assert res.messages[0].text.text == "Test message 2" 

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

350 assert res.messages[2].WhichOneof("content") == "chat_created" 

351 

352 # test that another user can't access the thread 

353 with conversations_session(token3) as c: 

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

355 assert len(res.messages) == 0 

356 

357 

358def test_get_group_chat_messages_pagination(db, moderator): 

359 user1, token1 = generate_user() 

360 user2, token2 = generate_user() 

361 make_friends(user1, user2) 

362 

363 with conversations_session(token1) as c: 

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

365 group_chat_id = res.group_chat_id 

366 for i in range(30): 

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

368 

369 moderator.approve_group_chat(group_chat_id) 

370 

371 with conversations_session(token2) as c: 

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

373 # pagination 

374 assert len(res.messages) == 20 

375 assert res.messages[0].text.text == "29" 

376 assert res.messages[19].text.text == "10" 

377 assert not res.no_more 

378 res = c.GetGroupChatMessages( 

379 conversations_pb2.GetGroupChatMessagesReq( 

380 group_chat_id=group_chat_id, last_message_id=res.messages[19].message_id 

381 ) 

382 ) 

383 assert len(res.messages) == 11 

384 assert res.messages[0].text.text == "9" 

385 assert res.messages[9].text.text == "0" 

386 assert res.no_more 

387 

388 

389def test_get_group_chat_messages_joined_left(db, moderator): 

390 user1, token1 = generate_user() 

391 user2, token2 = generate_user() 

392 user3, token3 = generate_user() 

393 user4, token4 = generate_user() 

394 make_friends(user1, user2) 

395 make_friends(user1, user3) 

396 make_friends(user1, user4) 

397 

398 with conversations_session(token1) as c: 

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

400 group_chat_id = res.group_chat_id 

401 moderator.approve_group_chat(group_chat_id) 

402 

403 for i in range(10): 

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

405 

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

407 

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

409 

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

411 

412 # created + 10 normal + invited + normal 

413 assert len(res.messages) == 13 

414 

415 with conversations_session(token3) as c: 

416 # can only see last message after invited 

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

418 # joined + normal 

419 assert len(res.messages) == 2 

420 assert res.messages[0].text.text == "10" 

421 

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

423 

424 with conversations_session(token1) as c: 

425 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="11")) 

426 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="12")) 

427 

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

429 

430 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="13")) 

431 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="14")) 

432 

433 with conversations_session(token3) as c: 

434 # can only see last message after invited 

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

436 # joined + normal + left + invite + 2 normal 

437 assert len(res.messages) == 6 

438 assert res.messages[0].text.text == "14" 

439 assert res.messages[1].text.text == "13" 

440 assert res.messages[2].WhichOneof("content") == "user_invited" 

441 assert res.messages[3].WhichOneof("content") == "user_left" 

442 assert res.messages[4].text.text == "10" 

443 assert res.messages[5].WhichOneof("content") == "user_invited" 

444 

445 

446def test_get_group_chat_info(db): 

447 user1, token1 = generate_user() 

448 user2, token2 = generate_user() 

449 user3, token3 = generate_user() 

450 

451 make_friends(user1, user2) 

452 make_friends(user3, user1) 

453 

454 with conversations_session(token1) as c: 

455 # create some threads with messages 

456 res = c.CreateGroupChat( 

457 conversations_pb2.CreateGroupChatReq( 

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

459 ) 

460 ) 

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

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

463 group_chat1_id = res.group_chat_id 

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

465 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test group message 1")) 

466 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=res.group_chat_id, text="Test group message 2")) 

467 group_chat2_id = res.group_chat_id 

468 

469 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat1_id)) 

470 assert res.title == "Test title" 

471 assert user2.id in res.member_user_ids 

472 assert user1.id in res.admin_user_ids 

473 assert to_aware_datetime(res.created) <= now() 

474 assert res.only_admins_invite 

475 assert res.is_dm 

476 assert not res.is_archived 

477 

478 res = c.GetGroupChat(conversations_pb2.GetGroupChatReq(group_chat_id=group_chat2_id)) 

479 assert not res.title 

480 assert user2.id in res.member_user_ids 

481 assert user3.id in res.member_user_ids 

482 assert user1.id in res.admin_user_ids 

483 assert to_aware_datetime(res.created) <= now() 

484 assert res.only_admins_invite 

485 assert not res.is_dm 

486 assert not res.is_archived 

487 

488 

489def test_get_group_chat_info_denied(db): 

490 user1, token1 = generate_user() 

491 user2, token2 = generate_user() 

492 user3, token3 = generate_user() 

493 user4, token4 = generate_user() 

494 

495 make_friends(user1, user2) 

496 make_friends(user3, user1) 

497 

498 with conversations_session(token1) as c: 

499 # create a group chat with messages 

500 res = c.CreateGroupChat( 

501 conversations_pb2.CreateGroupChatReq( 

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

503 ) 

504 ) 

505 group_chat_id = res.group_chat_id 

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

507 

508 with conversations_session(token4) as c: 

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

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

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

512 

513 

514def test_get_group_chat_info_left(db, moderator): 

515 user1, token1 = generate_user() 

516 user2, token2 = generate_user() 

517 user3, token3 = generate_user() 

518 user4, token4 = generate_user() 

519 

520 make_friends(user1, user2) 

521 make_friends(user3, user1) 

522 make_friends(user1, user4) 

523 

524 with conversations_session(token1) as c: 

525 # create a group chat with messages 

526 res = c.CreateGroupChat( 

527 conversations_pb2.CreateGroupChatReq( 

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

529 ) 

530 ) 

531 group_chat_id = res.group_chat_id 

532 moderator.approve_group_chat(group_chat_id) 

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

534 

535 with conversations_session(token3) as c: 

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

537 

538 with conversations_session(token1) as c: 

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

540 

541 with conversations_session(token3) as c: 

542 # this user left when user4 wasn't a member, 

543 # so the returned members should be user1, user2, and user3 only 

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

545 assert len(res.member_user_ids) == 3 

546 assert user1.id in res.member_user_ids 

547 assert user2.id in res.member_user_ids 

548 assert user3.id in res.member_user_ids 

549 

550 

551def test_remove_group_chat_user(db): 

552 # create 3 uses and connect them 

553 user1, token1 = generate_user() 

554 user2, token2 = generate_user() 

555 user3, token3 = generate_user() 

556 make_friends(user1, user2) 

557 make_friends(user1, user3) 

558 

559 # using user token, create a Conversations API for testing 

560 with conversations_session(token1) as c: 

561 # create a group chat 

562 res = c.CreateGroupChat( 

563 conversations_pb2.CreateGroupChatReq( 

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

565 ) 

566 ) 

567 group_chat_id = res.group_chat_id 

568 

569 # remove a user from group 

570 c.RemoveGroupChatUser(conversations_pb2.RemoveGroupChatUserReq(group_chat_id=group_chat_id, user_id=user2.id)) 

571 assert user3.id in res.member_user_ids # other users are still in the group 

572 

573 # can't remove the same user twice 

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

575 c.RemoveGroupChatUser( 

576 conversations_pb2.RemoveGroupChatUserReq(group_chat_id=group_chat_id, user_id=user2.id) 

577 ) 

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

579 

580 

581def test_edit_group_chat(db, moderator): 

582 user1, token1 = generate_user() 

583 user2, token2 = generate_user() 

584 user3, token3 = generate_user() 

585 make_friends(user1, user2) 

586 

587 with conversations_session(token1) as c: 

588 # create some threads with messages 

589 res = c.CreateGroupChat( 

590 conversations_pb2.CreateGroupChatReq( 

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

592 ) 

593 ) 

594 group_chat_id = res.group_chat_id 

595 moderator.approve_group_chat(group_chat_id) 

596 

597 c.EditGroupChat( 

598 conversations_pb2.EditGroupChatReq( 

599 group_chat_id=group_chat_id, 

600 title=wrappers_pb2.StringValue(value="Modified title"), 

601 only_admins_invite=wrappers_pb2.BoolValue(value=False), 

602 ) 

603 ) 

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

605 assert res.title == "Modified title" 

606 assert not res.only_admins_invite 

607 

608 # make sure non-admin is not allowed to modify 

609 with conversations_session(token2) as c: 

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

611 c.EditGroupChat( 

612 conversations_pb2.EditGroupChatReq( 

613 group_chat_id=group_chat_id, 

614 title=wrappers_pb2.StringValue(value="Other title"), 

615 only_admins_invite=wrappers_pb2.BoolValue(value=True), 

616 ) 

617 ) 

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

619 

620 # make sure non-recipient is not allowed to modify 

621 with conversations_session(token3) as c: 

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

623 c.EditGroupChat( 

624 conversations_pb2.EditGroupChatReq( 

625 group_chat_id=group_chat_id, 

626 title=wrappers_pb2.StringValue(value="Other title"), 

627 only_admins_invite=wrappers_pb2.BoolValue(value=True), 

628 ) 

629 ) 

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

631 

632 

633def test_make_remove_group_chat_admin(db, moderator): 

634 user1, token1 = generate_user() 

635 user2, token2 = generate_user() 

636 user3, token3 = generate_user() 

637 

638 make_friends(user1, user2) 

639 make_friends(user1, user3) 

640 make_friends(user2, user3) 

641 

642 with conversations_session(token1) as c: 

643 # create some threads with messages 

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

645 group_chat_id = res.group_chat_id 

646 moderator.approve_group_chat(group_chat_id) 

647 

648 # shouldn't be able to remove only admin 

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

650 c.RemoveGroupChatAdmin( 

651 conversations_pb2.RemoveGroupChatAdminReq(group_chat_id=group_chat_id, user_id=user1.id) 

652 ) 

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

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

655 

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

657 

658 # shouldn't be able to make admin again 

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

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

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

662 assert e.value.details() == "That user is already an admin." 

663 

664 with conversations_session(token2) as c: 

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

666 assert user1.id in res.admin_user_ids 

667 assert user2.id in res.admin_user_ids 

668 

669 with conversations_session(token1) as c: 

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

671 

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

673 assert user1.id in res.admin_user_ids 

674 assert user2.id not in res.admin_user_ids 

675 

676 with conversations_session(token2) as c: 

677 # shouldn't be able to make admin if not admin 

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

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

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

681 

682 

683def test_send_message(db, moderator: Moderator, push_collector: PushCollector, email_collector: EmailCollector): 

684 user1, token1 = generate_user() 

685 user2, token2 = generate_user() 

686 user3, token3 = generate_user() 

687 make_friends(user1, user2) 

688 make_friends(user1, user3) 

689 

690 # Let user2 receive email notifications for every chat message 

691 with notifications_session(token2) as n: 

692 n.SetNotificationSettings( 

693 notifications_pb2.SetNotificationSettingsReq( 

694 preferences=[ 

695 notifications_pb2.SingleNotificationPreference( 

696 topic=NotificationTopicAction.chat__message.topic, 

697 action=NotificationTopicAction.chat__message.action, 

698 delivery_method="email", 

699 enabled=True, 

700 ) 

701 ] 

702 ) 

703 ) 

704 

705 group_chat_title = "My group chat" 

706 message1 = "Test message 1" 

707 

708 # Let user1 create a group chat with user2 and send a message 

709 with conversations_session(token1) as c: 

710 res = c.CreateGroupChat( 

711 conversations_pb2.CreateGroupChatReq( 

712 title=wrappers_pb2.StringValue(value=group_chat_title), recipient_user_ids=[user2.id] 

713 ) 

714 ) 

715 group_chat_id = res.group_chat_id 

716 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text=message1)) 

717 

718 # Sender can already see group chat 

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

720 assert res.messages[0].text.text == message1 

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

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

723 

724 # user2 sees nothing until the group chat is approved 

725 assert push_collector.count_for_user(user2.id) == 0 

726 assert email_collector.count_for_recipient(user2.email) == 0 

727 

728 moderator.approve_group_chat(group_chat_id) 

729 

730 # user2 gets email and push notifications 

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

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

733 assert message1 in push.content.body 

734 

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

736 assert group_chat_title in email.subject 

737 assert message1 in email.plain 

738 assert message1 in email.html 

739 

740 # can't send message if not in chat 

741 with conversations_session(token3) as c: 

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

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

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

745 

746 make_user_block(user2, user1) 

747 with conversations_session(token1) as c: 

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

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

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

751 

752 

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

754 user1, token1 = generate_user(complete_profile=True) 

755 user2, token2 = generate_user(complete_profile=True) 

756 

757 make_friends(user1, user2) 

758 

759 # Let user2 receive email notifications for every chat message 

760 with notifications_session(token2) as n: 

761 n.SetNotificationSettings( 

762 notifications_pb2.SetNotificationSettingsReq( 

763 preferences=[ 

764 notifications_pb2.SingleNotificationPreference( 

765 topic=NotificationTopicAction.chat__message.topic, 

766 action=NotificationTopicAction.chat__message.action, 

767 delivery_method="email", 

768 enabled=True, 

769 ) 

770 ] 

771 ) 

772 ) 

773 

774 # Let user1 send two DM's to user2 

775 message1 = "Hello, user2!" 

776 message2 = "One more message." 

777 

778 with conversations_session(token1) as c1: 

779 # Send a DM from user1 to user2 

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

781 moderator.approve_group_chat(res.group_chat_id) 

782 

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

784 

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

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

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

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

789 assert push.content.body == message1 

790 

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

792 assert user1.name in email.subject 

793 assert message1 in email.plain 

794 assert message1 in email.html 

795 

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

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

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

799 assert push.content.body == message2 

800 

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

802 assert user1.name in email.subject 

803 assert message2 in email.plain 

804 assert message2 in email.html 

805 

806 with conversations_session(token2) as c2: 

807 # Fetch the chat by ID returned from SendDirectMessage 

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

809 

810 assert chat.is_dm 

811 group_chat_id = chat.group_chat_id 

812 

813 # Verify that the messages was received 

814 messages = c2.GetGroupChatMessages( 

815 conversations_pb2.GetGroupChatMessagesReq(group_chat_id=group_chat_id) 

816 ).messages 

817 

818 assert len(messages) == 2 

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

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

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

822 

823 

824def test_excessive_chat_initiations_are_reported(db, email_collector: EmailCollector): 

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

826 user, token = generate_user() 

827 rate_limit_definition = RATE_LIMIT_DEFINITIONS[RateLimitAction.chat_initiation] 

828 with conversations_session(token) as c: 

829 # Test warning email 

830 for _ in range(rate_limit_definition.warning_limit): 

831 recipient_user, _ = generate_user() 

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

833 

834 assert email_collector.count_for_reports() == 0 

835 

836 recipient_user, _ = generate_user() 

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

838 

839 email = email_collector.pop_for_reports(last=True) 

840 assert email.plain.startswith( 

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

842 ) 

843 

844 # Test new chat initiations fail after exceeding CHAT_INITIATION_HARD_LIMIT 

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

846 recipient_user, _ = generate_user() 

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

848 

849 assert email_collector.count_for_reports() == 0 

850 

851 recipient_user, _ = generate_user() 

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

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

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

855 assert ( 

856 exc_info.value.details() 

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

858 ) 

859 

860 email = email_collector.pop_for_reports(last=True) 

861 assert email.plain.startswith( 

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

863 ) 

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

865 

866 

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

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

869 user, token = generate_user(complete_profile=True) 

870 rate_limit_definition = RATE_LIMIT_DEFINITIONS[RateLimitAction.chat_initiation] 

871 

872 with conversations_session(token) as c: 

873 for _ in range(rate_limit_definition.warning_limit): 

874 recipient, _ = generate_user() 

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

876 

877 assert email_collector.count_for_reports() == 0 

878 

879 recipient, _ = generate_user() 

880 existing_dm_recipient_id = recipient.id 

881 c.SendDirectMessage( 

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

883 ) 

884 

885 email = email_collector.pop_for_reports(last=True) 

886 assert email.plain.startswith( 

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

888 ) 

889 

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

891 recipient, _ = generate_user() 

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

893 

894 assert email_collector.count_for_reports() == 0 

895 

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

897 c.SendDirectMessage( 

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

899 ) 

900 assert email_collector.count_for_reports() == 0 

901 

902 recipient, _ = generate_user() 

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

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

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

906 assert ( 

907 exc_info.value.details() 

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

909 ) 

910 

911 email = email_collector.pop_for_reports(last=True) 

912 assert email.plain.startswith( 

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

914 ) 

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

916 

917 

918def test_leave_invite_to_group_chat(db, moderator): 

919 user1, token1 = generate_user() 

920 user2, token2 = generate_user() 

921 user3, token3 = generate_user() 

922 user4, token4 = generate_user() 

923 user5, token5 = generate_user() 

924 user6, token6 = generate_user(delete_user=True) 

925 user7, token7 = generate_user() 

926 user8, token8 = generate_user() 

927 

928 make_friends(user1, user2) 

929 make_friends(user1, user3) 

930 make_friends(user1, user5) 

931 make_friends(user1, user7) 

932 make_friends(user1, user8) 

933 make_friends(user2, user3) 

934 make_friends(user4, user3) 

935 make_user_block(user1, user7) 

936 make_user_block(user8, user1) 

937 

938 with conversations_session(token1) as c: 

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

940 group_chat_id = res.group_chat_id 

941 moderator.approve_group_chat(group_chat_id) 

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

943 

944 # other user not in chat 

945 with conversations_session(token3) as c: 

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

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

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

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

950 res = c.InviteToGroupChat( 

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

952 ) 

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

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

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

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

957 

958 with conversations_session(token2) as c: 

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

960 assert user3.id not in res.member_user_ids 

961 

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

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

964 res = c.InviteToGroupChat( 

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

966 ) 

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

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

969 

970 with conversations_session(token1) as c: 

971 # invite invisible user fails 

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

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

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

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

976 # invite fake user fails 

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

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

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

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

981 # invite blocked user fails 

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

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

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

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

986 # invite blocking user fails 

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

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

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

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

991 

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

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

994 assert user1.id in res.member_user_ids 

995 assert user5.id in res.member_user_ids 

996 assert user3.id in res.member_user_ids 

997 

998 # test non-admin inviting 

999 c.EditGroupChat( 

1000 conversations_pb2.EditGroupChatReq( 

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

1002 ) 

1003 ) 

1004 

1005 with conversations_session(token3) as c: 

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

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

1008 assert user2.id in res.member_user_ids 

1009 

1010 

1011def test_group_chats_with_messages_before_join(db, moderator): 

1012 """ 

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

1014 should still see the group chat! 

1015 """ 

1016 user1, token1 = generate_user() 

1017 user2, token2 = generate_user() 

1018 user3, token3 = generate_user() 

1019 user4, token4 = generate_user() 

1020 

1021 make_friends(user1, user2) 

1022 make_friends(user1, user3) 

1023 make_friends(user2, user3) 

1024 make_friends(user1, user4) 

1025 

1026 with conversations_session(token1) as c: 

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

1028 group_chat_id = res.group_chat_id 

1029 moderator.approve_group_chat(group_chat_id) 

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

1031 

1032 with conversations_session(token2) as c: 

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

1034 

1035 with conversations_session(token1) as c: 

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

1037 

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

1039 

1040 with conversations_session(token3) as c: 

1041 # should work 

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

1043 

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

1045 assert len(res.group_chats) == 1 

1046 

1047 

1048def test_invite_to_dm(db): 

1049 user1, token1 = generate_user() 

1050 user2, token2 = generate_user() 

1051 user3, token3 = generate_user() 

1052 

1053 make_friends(user1, user2) 

1054 make_friends(user1, user3) 

1055 make_friends(user2, user3) 

1056 

1057 with conversations_session(token1) as c: 

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

1059 group_chat_id = res.group_chat_id 

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

1061 

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

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

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

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

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

1067 

1068 

1069def test_sole_admin_leaves(db, moderator): 

1070 user1, token1 = generate_user() 

1071 user2, token2 = generate_user() 

1072 user3, token3 = generate_user() 

1073 

1074 make_friends(user1, user2) 

1075 make_friends(user1, user3) 

1076 make_friends(user2, user3) 

1077 

1078 with conversations_session(token1) as c: 

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

1080 group_chat_id = res.group_chat_id 

1081 moderator.approve_group_chat(group_chat_id) 

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

1083 

1084 # sole admin can't leave group chat 

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

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

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

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

1089 

1090 with conversations_session(token2) as c: 

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

1092 

1093 with conversations_session(token3) as c: 

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

1095 

1096 # sole admin can leave when last in chat 

1097 with conversations_session(token1) as c: 

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

1099 

1100 

1101def test_search_messages(db, moderator): 

1102 user1, token1 = generate_user() 

1103 user2, token2 = generate_user() 

1104 user3, token3 = generate_user() 

1105 

1106 make_friends(user1, user2) 

1107 make_friends(user1, user3) 

1108 

1109 with conversations_session(token1) as c: 

1110 # create some threads with messages 

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

1112 gc1_id = res.group_chat_id 

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

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

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

1116 gc2_id = res.group_chat_id 

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

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

1119 

1120 # Approve group chats so they appear in search results 

1121 moderator.approve_group_chat(gc1_id) 

1122 moderator.approve_group_chat(gc2_id) 

1123 

1124 with conversations_session(token1) as c: 

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

1126 assert len(res.results) == 4 

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

1128 assert len(res.results) == 2 

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

1130 assert len(res.results) == 0 

1131 

1132 # outside user doesn't get results 

1133 with conversations_session(token3) as c: 

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

1135 assert len(res.results) == 0 

1136 

1137 

1138def test_search_messages_left_joined(db, moderator): 

1139 user1, token1 = generate_user() 

1140 user2, token2 = generate_user() 

1141 user3, token3 = generate_user() 

1142 user4, token4 = generate_user() 

1143 make_friends(user1, user2) 

1144 make_friends(user1, user3) 

1145 make_friends(user1, user4) 

1146 

1147 with conversations_session(token1) as c: 

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

1149 group_chat_id = res.group_chat_id 

1150 moderator.approve_group_chat(group_chat_id) 

1151 for i in range(10): 

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

1153 

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

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

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

1157 

1158 assert len(res.results) == 11 

1159 

1160 with conversations_session(token3) as c: 

1161 # can only see last message after invited 

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

1163 

1164 assert len(res.results) == 1 

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

1166 

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

1168 

1169 with conversations_session(token1) as c: 

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

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

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

1173 c.SendMessage(conversations_pb2.SendMessageReq(group_chat_id=group_chat_id, text="Test message 13")) 

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

1175 

1176 with conversations_session(token3) as c: 

1177 # can only see last message after invited 

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

1179 assert len(res.results) == 3 

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

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

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

1183 

1184 

1185def test_admin_behaviour(db, moderator): 

1186 user1, token1 = generate_user() 

1187 user2, token2 = generate_user() 

1188 user3, token3 = generate_user() 

1189 

1190 make_friends(user1, user2) 

1191 make_friends(user1, user3) 

1192 make_friends(user2, user3) 

1193 

1194 with conversations_session(token1) as c: 

1195 gcid = c.CreateGroupChat( 

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

1197 ).group_chat_id 

1198 moderator.approve_group_chat(gcid) 

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

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

1201 assert len(res.admin_user_ids) == 2 

1202 assert user1.id in res.admin_user_ids 

1203 assert user2.id in res.admin_user_ids 

1204 

1205 with conversations_session(token3) as c: 

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

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

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

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

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

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

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

1213 assert len(res.admin_user_ids) == 2 

1214 assert user1.id in res.admin_user_ids 

1215 assert user2.id in res.admin_user_ids 

1216 

1217 with conversations_session(token2) as c: 

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

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

1220 assert len(res.admin_user_ids) == 3 

1221 assert user1.id in res.admin_user_ids 

1222 assert user2.id in res.admin_user_ids 

1223 assert user3.id in res.admin_user_ids 

1224 

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

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

1227 assert len(res.admin_user_ids) == 2 

1228 assert user2.id in res.admin_user_ids 

1229 assert user3.id in res.admin_user_ids 

1230 

1231 with conversations_session(token1) as c: 

1232 with pytest.raises(grpc.RpcError): 

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

1234 with pytest.raises(grpc.RpcError): 

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

1236 with pytest.raises(grpc.RpcError): 

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

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

1239 assert len(res.admin_user_ids) == 2 

1240 assert user2.id in res.admin_user_ids 

1241 assert user3.id in res.admin_user_ids 

1242 

1243 with conversations_session(token2) as c: 

1244 # can demote self if there are other admins 

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

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

1247 assert len(res.admin_user_ids) == 1 

1248 assert user3.id in res.admin_user_ids 

1249 

1250 with conversations_session(token3) as c: 

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

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

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

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

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

1256 assert len(res.admin_user_ids) == 1 

1257 assert user3.id in res.admin_user_ids 

1258 

1259 # last admin can't leave 

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

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

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

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

1264 

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

1266 

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

1268 

1269 with conversations_session(token2) as c: 

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

1271 

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

1273 with conversations_session(token1) as c: 

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

1275 

1276 

1277def test_add_remove_admin_failures(db): 

1278 user1, token1 = generate_user() 

1279 user2, token2 = generate_user() 

1280 user3, token3 = generate_user() 

1281 user4, token4 = generate_user() 

1282 user5, token5 = generate_user() 

1283 

1284 make_friends(user1, user2) 

1285 make_friends(user1, user3) 

1286 make_friends(user1, user4) 

1287 make_friends(user1, user5) 

1288 

1289 with conversations_session(token1) as c: 

1290 gcid = c.CreateGroupChat( 

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

1292 ).group_chat_id 

1293 

1294 make_user_invisible(user3.id) 

1295 make_user_block(user1, user4) 

1296 make_user_block(user5, user1) 

1297 

1298 # make non-existent user admin 

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

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

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

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

1303 

1304 # make invisible user admin 

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

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

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

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

1309 

1310 # make blocked user admin 

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

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

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

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

1315 

1316 # make blocking user admin 

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

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

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

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

1321 

1322 with session_scope() as session: 

1323 subscriptions = ( 

1324 session.execute( 

1325 select(GroupChatSubscription) 

1326 .where(GroupChatSubscription.group_chat_id == gcid) 

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

1328 ) 

1329 .scalars() 

1330 .all() 

1331 ) 

1332 

1333 for subscription in subscriptions: 

1334 subscription.role = GroupChatRole.admin 

1335 

1336 with conversations_session(token1) as c: 

1337 # remove non-existent user admin 

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

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

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

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

1342 

1343 # remove invisible admin 

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

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

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

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

1348 

1349 # remove blocked admin 

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

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

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

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

1354 

1355 # remove blocking admin 

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

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

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

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

1360 

1361 

1362def test_last_seen(db, moderator): 

1363 user1, token1 = generate_user() 

1364 user2, token2 = generate_user() 

1365 user3, token3 = generate_user() 

1366 

1367 make_friends(user1, user2) 

1368 make_friends(user1, user3) 

1369 make_friends(user2, user3) 

1370 

1371 with conversations_session(token3) as c: 

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

1373 gcid_distraction = c.CreateGroupChat( 

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

1375 ).group_chat_id 

1376 moderator.approve_group_chat(gcid_distraction) 

1377 

1378 with conversations_session(token1) as c: 

1379 gcid = c.CreateGroupChat( 

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

1381 ).group_chat_id 

1382 moderator.approve_group_chat(gcid) 

1383 

1384 message_ids = [] 

1385 

1386 for i in range(6): 

1387 c.SendMessage( 

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

1389 ) 

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

1391 c.SendMessage( 

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

1393 ) 

1394 

1395 message_ids.append( 

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

1397 ) 

1398 

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

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

1401 assert res.unseen_message_count == 0 

1402 

1403 with conversations_session(token2) as c: 

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

1405 # created + 6 normal 

1406 assert res.unseen_message_count == 7 

1407 

1408 backward_offset = 3 

1409 c.MarkLastSeenGroupChat( 

1410 conversations_pb2.MarkLastSeenGroupChatReq( 

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

1412 ) 

1413 ) 

1414 

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

1416 assert res.unseen_message_count == backward_offset 

1417 

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

1419 

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

1421 assert res.unseen_message_count == 0 

1422 

1423 with conversations_session(token3) as c: 

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

1425 # created + 7 normal 

1426 assert res.unseen_message_count == 8 

1427 

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

1429 

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

1431 assert res.unseen_message_count == 0 

1432 

1433 

1434def test_mark_last_seen_clears_notifications(db, moderator): 

1435 user1, token1 = generate_user() 

1436 user2, token2 = generate_user() 

1437 

1438 with conversations_session(token1) as c: 

1439 gcid = c.CreateGroupChat( 

1440 conversations_pb2.CreateGroupChatReq( 

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

1442 ) 

1443 ).group_chat_id 

1444 

1445 moderator.approve_group_chat(gcid) 

1446 

1447 with conversations_session(token1) as c: 

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

1449 

1450 process_jobs() 

1451 

1452 def unseen_notification_count(user_id): 

1453 with session_scope() as session: 

1454 return session.execute( 

1455 select(func.count()) 

1456 .select_from(Notification) 

1457 .where(Notification.user_id == user_id) 

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

1459 .where(Notification.is_seen == False) 

1460 ).scalar_one() 

1461 

1462 assert unseen_notification_count(user2.id) > 0 

1463 

1464 with conversations_session(token2) as c: 

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

1466 

1467 assert unseen_notification_count(user2.id) == 0 

1468 

1469 

1470def test_mark_last_seen_clears_missed_messages_notification(db, moderator): 

1471 """ 

1472 Regression test: chat__missed_messages is a summary keyed with "" rather than a chat id, so it 

1473 needs to be marked seen separately from the per-chat chat__message notifications. 

1474 """ 

1475 user1, token1 = generate_user() 

1476 user2, token2 = generate_user() 

1477 

1478 # this notification is email-only by default, and the in-app feed only shows push-enabled ones 

1479 with notifications_session(token2) as n: 

1480 n.SetNotificationSettings( 

1481 notifications_pb2.SetNotificationSettingsReq( 

1482 preferences=[ 

1483 notifications_pb2.SingleNotificationPreference( 

1484 topic=NotificationTopicAction.chat__missed_messages.topic, 

1485 action=NotificationTopicAction.chat__missed_messages.action, 

1486 delivery_method="push", 

1487 enabled=True, 

1488 ) 

1489 ] 

1490 ) 

1491 ) 

1492 

1493 with conversations_session(token1) as c: 

1494 gcid = c.CreateGroupChat( 

1495 conversations_pb2.CreateGroupChatReq( 

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

1497 ) 

1498 ).group_chat_id 

1499 

1500 moderator.approve_group_chat(gcid) 

1501 

1502 with conversations_session(token1) as c: 

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

1504 

1505 # the job only picks up messages that have been unseen for five minutes 

1506 with patch("couchers.jobs.handlers.now", now_5_min_in_future): 

1507 send_message_notifications(empty_pb2.Empty()) 

1508 process_jobs() 

1509 

1510 def unseen_missed_messages(): 

1511 with notifications_session(token2) as n: 

1512 res = n.ListNotifications(notifications_pb2.ListNotificationsReq(only_unread=True)) 

1513 return [ 

1514 notification 

1515 for notification in res.notifications 

1516 if notification.topic == NotificationTopicAction.chat__missed_messages.topic 

1517 and notification.action == NotificationTopicAction.chat__missed_messages.action 

1518 ] 

1519 

1520 assert len(unseen_missed_messages()) == 1 

1521 

1522 with conversations_session(token2) as c: 

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

1524 

1525 assert unseen_missed_messages() == [] 

1526 

1527 

1528def test_one_dm_per_pair(db, moderator): 

1529 user1, token1 = generate_user() 

1530 user2, token2 = generate_user() 

1531 user3, token3 = generate_user() 

1532 

1533 make_friends(user1, user2) 

1534 make_friends(user1, user3) 

1535 make_friends(user2, user3) 

1536 

1537 with conversations_session(token1) as c: 

1538 # create DM with user 2 

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

1540 assert res.is_dm 

1541 dm_with_user2 = res.group_chat_id 

1542 

1543 # create DM with user 3 

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

1545 assert res.is_dm 

1546 dm_with_user3 = res.group_chat_id 

1547 

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

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

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

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

1552 

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

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

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

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

1557 

1558 # can create joined group chat 

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

1560 assert not res.is_dm 

1561 

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

1563 moderator.approve_group_chat(dm_with_user2) 

1564 

1565 with conversations_session(token2) as c: 

1566 # can create DM with user 3 

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

1568 assert res.is_dm 

1569 

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

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

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

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

1574 

1575 

1576def test_GetDirectMessage(db): 

1577 user1, token1 = generate_user() 

1578 user2, token2 = generate_user() 

1579 user3, token3 = generate_user() 

1580 

1581 make_friends(user1, user2) 

1582 make_friends(user1, user3) 

1583 make_friends(user2, user3) 

1584 

1585 with conversations_session(token1) as c: 

1586 # no group chat with user 2 

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

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

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

1590 

1591 # no group chat with nor user 3 

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

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

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

1595 

1596 # create DM with user 2 

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

1598 assert res.is_dm 

1599 gcid = res.group_chat_id 

1600 

1601 # now should exist 

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

1603 assert res.group_chat_id == gcid 

1604 assert not res.is_archived 

1605 

1606 # create DM with user 3 

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

1608 assert res.is_dm 

1609 

1610 # can create joined group chat 

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

1612 assert not res.is_dm 

1613 

1614 with conversations_session(token2) as c: 

1615 # can create DM with user 3 

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

1617 assert res.is_dm 

1618 assert res.can_message 

1619 gcid = res.group_chat_id 

1620 

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

1622 make_user_block(user3, user2) 

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

1624 assert res.group_chat_id == gcid 

1625 assert not res.can_message 

1626 

1627 

1628def test_total_unseen(db, moderator): 

1629 user1, token1 = generate_user() 

1630 user2, token2 = generate_user() 

1631 user3, token3 = generate_user() 

1632 

1633 # distractions 

1634 user4, token4 = generate_user() 

1635 

1636 make_friends(user1, user2) 

1637 make_friends(user1, user3) 

1638 make_friends(user2, user3) 

1639 

1640 # distractions 

1641 make_friends(user1, user4) 

1642 

1643 with conversations_session(token1) as c: 

1644 # distractions 

1645 gcid_distraction = c.CreateGroupChat( 

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

1647 ).group_chat_id 

1648 moderator.approve_group_chat(gcid_distraction) 

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

1650 

1651 gcid = c.CreateGroupChat( 

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

1653 ).group_chat_id 

1654 moderator.approve_group_chat(gcid) 

1655 

1656 for i in range(6): 

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

1658 

1659 # distractions 

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

1661 

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

1663 with api_session(token1) as api: 

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

1665 

1666 with api_session(token2) as api: 

1667 # chat created + 6 normal messages 

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

1669 

1670 # now leave chat with user2 

1671 with conversations_session(token2) as c: 

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

1673 

1674 with api_session(token2) as api: 

1675 # seen messages becomes 0 when leaving 

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

1677 

1678 with conversations_session(token1) as c: 

1679 # distractions 

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

1681 

1682 # send more stuff without user 2 

1683 for i in range(3): 

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

1685 

1686 # distractions 

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

1688 

1689 with api_session(token2) as api: 

1690 # seen messages becomes 0 when leaving 

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

1692 

1693 with conversations_session(token1) as c: 

1694 # add user 2 back 

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

1696 

1697 # send more stuff with user 2 

1698 for i in range(12): 

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

1700 

1701 # distractions 

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

1703 

1704 with api_session(token2) as api: 

1705 # joined + 12 normal 

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

1707 

1708 

1709def test_regression_ListGroupChats_pagination(db, moderator): 

1710 user1, token1 = generate_user() 

1711 user2, token2 = generate_user() 

1712 user3, token3 = generate_user() 

1713 

1714 make_friends(user1, user2) 

1715 make_friends(user1, user3) 

1716 

1717 with conversations_session(token1) as c: 

1718 # tuples of (group_chat_id, message_id) 

1719 group_chat_and_message_ids = [] 

1720 for i in range(50): 

1721 res1 = c.CreateGroupChat( 

1722 conversations_pb2.CreateGroupChatReq( 

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

1724 ) 

1725 ) 

1726 

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

1728 

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

1730 

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

1732 moderator.approve_group_chat(res1.group_chat_id) 

1733 

1734 seen_group_chat_ids = [] 

1735 

1736 last_message_id = 0 

1737 more = True 

1738 while more: 

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

1740 last_message_id = res.last_message_id 

1741 more = not res.no_more 

1742 

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

1744 

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

1746 

1747 

1748def test_muting(db, moderator): 

1749 user1, token1 = generate_user() 

1750 user2, token2 = generate_user() 

1751 user3, token3 = generate_user() 

1752 

1753 make_friends(user1, user2) 

1754 make_friends(user1, user3) 

1755 make_friends(user2, user3) 

1756 

1757 with conversations_session(token3) as c: 

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

1759 gcid_distraction = c.CreateGroupChat( 

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

1761 ).group_chat_id 

1762 moderator.approve_group_chat(gcid_distraction) 

1763 

1764 with conversations_session(token1) as c: 

1765 gcid = c.CreateGroupChat( 

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

1767 ).group_chat_id 

1768 moderator.approve_group_chat(gcid) 

1769 

1770 with conversations_session(token2) as c: 

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

1772 assert not res.mute_info.muted 

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

1774 

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

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

1777 assert res.mute_info.muted 

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

1779 

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

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

1782 assert not res.mute_info.muted 

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

1784 

1785 c.MuteGroupChat( 

1786 conversations_pb2.MuteGroupChatReq( 

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

1788 ) 

1789 ) 

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

1791 assert res.mute_info.muted 

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

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

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

1795 

1796 

1797def test_archiving(db, moderator): 

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

1799 user1, token1 = generate_user() 

1800 user2, token2 = generate_user() 

1801 user3, token3 = generate_user() 

1802 

1803 make_friends(user1, user2) 

1804 make_friends(user1, user3) 

1805 make_friends(user2, user3) 

1806 

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

1808 with conversations_session(token3) as c: 

1809 gcid_distraction = c.CreateGroupChat( 

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

1811 ).group_chat_id 

1812 moderator.approve_group_chat(gcid_distraction) 

1813 

1814 with conversations_session(token1) as c: 

1815 gcid = c.CreateGroupChat( 

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

1817 ).group_chat_id 

1818 moderator.approve_group_chat(gcid) 

1819 

1820 # Test basic archive/unarchive functionality 

1821 with conversations_session(token2) as c: 

1822 # Initially not archived 

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

1824 assert not res.is_archived 

1825 

1826 # Archive the chat 

1827 archive_res = c.SetGroupChatArchiveStatus( 

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

1829 ) 

1830 assert archive_res.group_chat_id == gcid 

1831 assert archive_res.is_archived 

1832 

1833 # Verify archived via GetGroupChat 

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

1835 assert res.is_archived 

1836 

1837 # Unarchive the chat 

1838 archive_res = c.SetGroupChatArchiveStatus( 

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

1840 ) 

1841 assert archive_res.group_chat_id == gcid 

1842 assert not archive_res.is_archived 

1843 

1844 # Verify unarchived via GetGroupChat 

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

1846 assert not res.is_archived 

1847 

1848 

1849def test_archiving_per_user(db, moderator): 

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

1851 user1, token1 = generate_user() 

1852 user2, token2 = generate_user() 

1853 

1854 make_friends(user1, user2) 

1855 

1856 with conversations_session(token1) as c: 

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

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

1859 moderator.approve_group_chat(gcid) 

1860 

1861 # User1 archives the chat 

1862 with conversations_session(token1) as c: 

1863 c.SetGroupChatArchiveStatus( 

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

1865 ) 

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

1867 assert res.is_archived 

1868 

1869 # User2 should NOT see it as archived 

1870 with conversations_session(token2) as c: 

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

1872 assert not res.is_archived 

1873 

1874 # User2 archives it too 

1875 with conversations_session(token2) as c: 

1876 c.SetGroupChatArchiveStatus( 

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

1878 ) 

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

1880 assert res.is_archived 

1881 

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

1883 with conversations_session(token1) as c: 

1884 c.SetGroupChatArchiveStatus( 

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

1886 ) 

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

1888 assert not res.is_archived 

1889 

1890 with conversations_session(token2) as c: 

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

1892 assert res.is_archived 

1893 

1894 

1895def test_archiving_with_list_group_chats(db, moderator): 

1896 """Test archive filtering with ListGroupChats endpoint""" 

1897 user1, token1 = generate_user() 

1898 user2, token2 = generate_user() 

1899 user3, token3 = generate_user() 

1900 

1901 make_friends(user1, user2) 

1902 make_friends(user1, user3) 

1903 

1904 with conversations_session(token1) as c: 

1905 # Create 3 chats 

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

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

1908 moderator.approve_group_chat(gcid1) 

1909 

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

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

1912 moderator.approve_group_chat(gcid2) 

1913 

1914 gcid3 = c.CreateGroupChat( 

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

1916 ).group_chat_id 

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

1918 moderator.approve_group_chat(gcid3) 

1919 

1920 with conversations_session(token1) as c: 

1921 # Initially all 3 chats should be visible 

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

1923 assert len(res.group_chats) == 3 

1924 

1925 # All should be non-archived 

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

1927 assert len(res.group_chats) == 3 

1928 

1929 # None should be archived 

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

1931 assert len(res.group_chats) == 0 

1932 

1933 # Archive chat 1 

1934 c.SetGroupChatArchiveStatus( 

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

1936 ) 

1937 

1938 # Without filter, still see all 3 

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

1940 assert len(res.group_chats) == 3 

1941 

1942 # Non-archived should show 2 

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

1944 assert len(res.group_chats) == 2 

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

1946 

1947 # Archived should show 1 

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

1949 assert len(res.group_chats) == 1 

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

1951 assert res.group_chats[0].is_archived 

1952 

1953 # Archive chat 2 as well 

1954 c.SetGroupChatArchiveStatus( 

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

1956 ) 

1957 

1958 # Non-archived should show 1 

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

1960 assert len(res.group_chats) == 1 

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

1962 

1963 # Archived should show 2 

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

1965 assert len(res.group_chats) == 2 

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

1967 assert archived_ids == {gcid1, gcid2} 

1968 

1969 # Unarchive chat 1 

1970 c.SetGroupChatArchiveStatus( 

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

1972 ) 

1973 

1974 # Non-archived should show 2 

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

1976 assert len(res.group_chats) == 2 

1977 

1978 # Archived should show 1 

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

1980 assert len(res.group_chats) == 1 

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

1982 

1983 

1984def test_archiving_chat_not_found(db, moderator): 

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

1986 user1, token1 = generate_user() 

1987 user2, token2 = generate_user() 

1988 user3, token3 = generate_user() 

1989 

1990 make_friends(user1, user2) 

1991 

1992 with conversations_session(token1) as c: 

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 # User3 is not in the chat - should get NOT_FOUND 

1998 with conversations_session(token3) as c: 

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

2000 c.SetGroupChatArchiveStatus( 

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

2002 ) 

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

2004 

2005 # Non-existent chat ID should fail 

2006 with conversations_session(token1) as c: 

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

2008 c.SetGroupChatArchiveStatus( 

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

2010 ) 

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

2012 

2013 

2014def test_archiving_after_leaving_chat(db, moderator): 

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

2016 user1, token1 = generate_user() 

2017 user2, token2 = generate_user() 

2018 user3, token3 = generate_user() 

2019 

2020 make_friends(user1, user2) 

2021 make_friends(user1, user3) 

2022 make_friends(user2, user3) 

2023 

2024 with conversations_session(token1) as c: 

2025 gcid = c.CreateGroupChat( 

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

2027 ).group_chat_id 

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

2029 moderator.approve_group_chat(gcid) 

2030 

2031 # User2 leaves the chat 

2032 with conversations_session(token2) as c: 

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

2034 

2035 # User2 should not be able to archive it now 

2036 with conversations_session(token2) as c: 

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

2038 c.SetGroupChatArchiveStatus( 

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

2040 ) 

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

2042 

2043 

2044def test_archiving_dm(db, moderator): 

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

2046 user1, token1 = generate_user() 

2047 user2, token2 = generate_user() 

2048 

2049 make_friends(user1, user2) 

2050 

2051 with conversations_session(token1) as c: 

2052 # Create a DM 

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

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

2055 moderator.approve_group_chat(gcid) 

2056 

2057 # Verify it's a DM 

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

2059 assert res.is_dm 

2060 assert not res.is_archived 

2061 

2062 # Archive the DM 

2063 c.SetGroupChatArchiveStatus( 

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

2065 ) 

2066 

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

2068 assert res.is_archived 

2069 

2070 # Unarchive it 

2071 c.SetGroupChatArchiveStatus( 

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

2073 ) 

2074 

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

2076 assert not res.is_archived 

2077 

2078 

2079def test_archiving_idempotent(db, moderator): 

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

2081 user1, token1 = generate_user() 

2082 user2, token2 = generate_user() 

2083 

2084 make_friends(user1, user2) 

2085 

2086 with conversations_session(token1) as c: 

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

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

2089 moderator.approve_group_chat(gcid) 

2090 

2091 # Archive twice - should work without error 

2092 c.SetGroupChatArchiveStatus( 

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

2094 ) 

2095 c.SetGroupChatArchiveStatus( 

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

2097 ) 

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

2099 assert res.is_archived 

2100 

2101 # Unarchive twice - should work without error 

2102 c.SetGroupChatArchiveStatus( 

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

2104 ) 

2105 c.SetGroupChatArchiveStatus( 

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

2107 ) 

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

2109 assert not res.is_archived 

2110 

2111 

2112def test_chat_notifications(db, moderator): 

2113 user1, token1 = generate_user() 

2114 user2, token2 = generate_user() 

2115 # notifs off 

2116 user3, token3 = generate_user() 

2117 user4, token4 = generate_user() 

2118 user5, token5 = generate_user() 

2119 user6, token6 = generate_user() 

2120 

2121 make_friends(user1, user2) 

2122 make_friends(user1, user3) 

2123 make_friends(user1, user4) 

2124 make_friends(user4, user5) 

2125 make_friends(user4, user6) 

2126 

2127 # have some of them enable/disable notifs 

2128 topic_action = NotificationTopicAction.chat__message 

2129 for token, enabled in [ 

2130 (token1, True), 

2131 (token2, True), 

2132 (token3, False), 

2133 (token4, True), 

2134 (token5, True), 

2135 (token6, True), 

2136 ]: 

2137 with notifications_session(token) as notifications: 

2138 notifications.SetNotificationSettings( 

2139 notifications_pb2.SetNotificationSettingsReq( 

2140 preferences=[ 

2141 notifications_pb2.SingleNotificationPreference( 

2142 topic=topic_action.topic, 

2143 action=topic_action.action, 

2144 delivery_method=delivery_method, 

2145 enabled=enabled, 

2146 ) 

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

2148 ], 

2149 ) 

2150 ) 

2151 

2152 group_chat_id = None 

2153 

2154 def send_msg(c, i): 

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

2156 

2157 with conversations_session(token1) as c: 

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

2159 group_chat_id = res.group_chat_id 

2160 moderator.approve_group_chat(group_chat_id) 

2161 c.EditGroupChat( 

2162 conversations_pb2.EditGroupChatReq( 

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

2164 ) 

2165 ) 

2166 send_msg(c, i=1) 

2167 send_msg(c, i=2) 

2168 

2169 with conversations_session(token4) as c: 

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

2171 send_msg(c, i=3) 

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

2173 send_msg(c, i=4) 

2174 send_msg(c, i=5) 

2175 

2176 with conversations_session(token3) as c: 

2177 send_msg(c, i=6) 

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

2179 

2180 with conversations_session(token2) as c: 

2181 send_msg(c, i=7) 

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

2183 

2184 with conversations_session(token6) as c: 

2185 send_msg(c, i=8) 

2186 

2187 # go through all bg jobs 

2188 while process_job(): 

2189 pass 

2190 

2191 # now check notifs... 

2192 expected_notifs = [ 

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

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

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

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

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

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

2199 ] 

2200 

2201 with session_scope() as session: 

2202 for user, label, expected_msgs in expected_notifs: 

2203 deliv = ( 

2204 session.execute( 

2205 select(Notification.data) 

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

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

2208 .where(Notification.topic_action == topic_action) 

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

2210 .order_by(Notification.created) 

2211 ) 

2212 .scalars() 

2213 .all() 

2214 ) 

2215 

2216 def parse_message_payload(data): 

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

2218 

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

2220 

2221 print(contents) 

2222 

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

2224 

2225 

2226def test_incomplete_profile(db): 

2227 user1, token1 = generate_user(complete_profile=True) 

2228 user2, token2 = generate_user(complete_profile=False) 

2229 user3, token3 = generate_user(complete_profile=True) 

2230 make_friends(user1, user2) 

2231 make_friends(user1, user3) 

2232 

2233 # user 1 can make a chat 

2234 with conversations_session(token1) as c: 

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

2236 group_chat_id = res.group_chat_id 

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

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

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

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

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

2242 

2243 # user 2 cannot 

2244 with conversations_session(token2) as c: 

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

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

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

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