Coverage for src/tests/test_references.py: 100%

417 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-07-20 21:46 +0000

1from datetime import timedelta 

2from unittest.mock import patch 

3 

4import grpc 

5import pytest 

6from google.protobuf import empty_pb2 

7 

8from couchers import errors 

9from couchers.db import session_scope 

10from couchers.models import ( 

11 Conversation, 

12 HostRequest, 

13 HostRequestStatus, 

14 Message, 

15 MessageType, 

16 Reference, 

17 ReferenceType, 

18 User, 

19) 

20from couchers.sql import couchers_select as select 

21from couchers.utils import now, to_aware_datetime, today 

22from proto import conversations_pb2, references_pb2, requests_pb2 

23from tests.test_fixtures import ( # noqa 

24 db, 

25 email_fields, 

26 generate_user, 

27 make_user_block, 

28 mock_notification_email, 

29 push_collector, 

30 references_session, 

31 requests_session, 

32 testconfig, 

33) 

34 

35 

36@pytest.fixture(autouse=True) 

37def _(testconfig): 

38 pass 

39 

40 

41def create_host_request( 

42 session, surfer_user_id, host_user_id, host_request_age=timedelta(days=15), status=HostRequestStatus.confirmed 

43): 

44 """ 

45 Create a host request that's `host_request_age` old 

46 """ 

47 from_date = today() - host_request_age - timedelta(days=2) 

48 to_date = today() - host_request_age 

49 fake_created = now() - host_request_age - timedelta(days=3) 

50 conversation = Conversation() 

51 session.add(conversation) 

52 session.flush() 

53 session.add( 

54 Message( 

55 time=fake_created + timedelta(seconds=1), 

56 conversation_id=conversation.id, 

57 author_id=surfer_user_id, 

58 message_type=MessageType.chat_created, 

59 ) 

60 ) 

61 message = Message( 

62 time=fake_created + timedelta(seconds=2), 

63 conversation_id=conversation.id, 

64 author_id=surfer_user_id, 

65 text="Hi, I'm requesting to be hosted.", 

66 message_type=MessageType.text, 

67 ) 

68 session.add(message) 

69 session.flush() 

70 host_request = HostRequest( 

71 conversation_id=conversation.id, 

72 surfer_user_id=surfer_user_id, 

73 host_user_id=host_user_id, 

74 from_date=from_date, 

75 to_date=to_date, 

76 status=status, 

77 surfer_last_seen_message_id=message.id, 

78 ) 

79 session.add(host_request) 

80 session.commit() 

81 return host_request.conversation_id 

82 

83 

84def create_host_reference(session, from_user_id, to_user_id, reference_age, *, surfing=True, host_request_id=None): 

85 if host_request_id: 

86 actual_host_request_id = host_request_id 

87 else: 

88 if surfing: 

89 actual_host_request_id = host_request_id or create_host_request( 

90 session, from_user_id, to_user_id, reference_age + timedelta(days=1) 

91 ) 

92 else: 

93 actual_host_request_id = host_request_id or create_host_request( 

94 session, to_user_id, from_user_id, reference_age + timedelta(days=1) 

95 ) 

96 

97 host_request = session.execute( 

98 select(HostRequest).where(HostRequest.conversation_id == actual_host_request_id) 

99 ).scalar_one() 

100 

101 reference = Reference( 

102 time=now() - reference_age, 

103 from_user_id=from_user_id, 

104 host_request_id=host_request.conversation_id, 

105 text="Dummy reference", 

106 rating=0.5, 

107 was_appropriate=True, 

108 ) 

109 

110 if host_request.surfer_user_id == from_user_id: 

111 reference.reference_type = ReferenceType.surfed 

112 reference.to_user_id = host_request.host_user_id 

113 assert from_user_id == host_request.surfer_user_id 

114 else: 

115 reference.reference_type = ReferenceType.hosted 

116 reference.to_user_id = host_request.surfer_user_id 

117 assert from_user_id == host_request.host_user_id 

118 

119 session.add(reference) 

120 session.commit() 

121 return reference.id, actual_host_request_id 

122 

123 

124def create_friend_reference(session, from_user_id, to_user_id, reference_age): 

125 reference = Reference( 

126 time=now() - reference_age, 

127 from_user_id=from_user_id, 

128 to_user_id=to_user_id, 

129 reference_type=ReferenceType.friend, 

130 text="Test friend request", 

131 rating=0.4, 

132 was_appropriate=True, 

133 ) 

134 session.add(reference) 

135 session.commit() 

136 return reference.id 

137 

138 

139def test_ListPagination(db): 

140 user1, token1 = generate_user() 

141 user2, token2 = generate_user() 

142 user3, token3 = generate_user() 

143 user4, token4 = generate_user() 

144 user5, token5 = generate_user() 

145 user6, token6 = generate_user() 

146 user7, token7 = generate_user() 

147 user8, token8 = generate_user() 

148 user9, token9 = generate_user() 

149 

150 with session_scope() as session: 

151 # bidirectional references 

152 ref2, hr2 = create_host_reference(session, user2.id, user1.id, timedelta(days=16, seconds=110), surfing=True) 

153 ref2b, _ = create_host_reference( 

154 session, user1.id, user2.id, timedelta(days=16, seconds=100), host_request_id=hr2 

155 ) 

156 

157 ref3, _ = create_host_reference(session, user3.id, user1.id, timedelta(days=16, seconds=90), surfing=False) 

158 ref4, _ = create_host_reference(session, user4.id, user1.id, timedelta(days=16, seconds=80), surfing=True) 

159 ref4b = create_friend_reference(session, user1.id, user4.id, timedelta(days=16, seconds=70)) 

160 

161 ref5, hr5 = create_host_reference(session, user5.id, user1.id, timedelta(days=16, seconds=60), surfing=False) 

162 ref5b, _ = create_host_reference( 

163 session, user1.id, user5.id, timedelta(days=16, seconds=50), host_request_id=hr5 

164 ) 

165 

166 ref6, _ = create_host_reference(session, user6.id, user1.id, timedelta(days=16, seconds=40), surfing=True) 

167 

168 ref7 = create_friend_reference(session, user7.id, user1.id, timedelta(days=16, seconds=30)) 

169 

170 ref8, _ = create_host_reference(session, user8.id, user1.id, timedelta(days=16, seconds=20), surfing=False) 

171 ref9, _ = create_host_reference(session, user9.id, user1.id, timedelta(days=16, seconds=10), surfing=False) 

172 

173 # should be visible even under 2 weeks 

174 ref7b = create_friend_reference(session, user1.id, user7.id, timedelta(days=9)) 

175 

176 # hidden because it's less than 2 weeks 

177 ref6hidden, _ = create_host_reference(session, user6.id, user1.id, timedelta(days=5), surfing=False) 

178 

179 # visible because both were written 

180 ref8b, hr8 = create_host_reference(session, user8.id, user1.id, timedelta(days=3, seconds=20), surfing=False) 

181 ref8c, _ = create_host_reference( 

182 session, user1.id, user8.id, timedelta(days=3, seconds=10), host_request_id=hr8 

183 ) 

184 

185 # note that visibility tests don't really test real logic 

186 

187 # these check the right refs are in the right requests and appear in the right order (latest first) 

188 

189 with references_session(token2) as api: 

190 # written by user1 

191 res = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id, page_size=2)) 

192 assert [ref.reference_id for ref in res.references] == [ref8c, ref7b] 

193 

194 res = api.ListReferences( 

195 references_pb2.ListReferencesReq(from_user_id=user1.id, page_token=res.next_page_token, page_size=2) 

196 ) 

197 assert [ref.reference_id for ref in res.references] == [ref5b, ref4b] 

198 

199 res = api.ListReferences( 

200 references_pb2.ListReferencesReq(from_user_id=user1.id, page_token=res.next_page_token, page_size=2) 

201 ) 

202 assert [ref.reference_id for ref in res.references] == [ref2b] 

203 assert not res.next_page_token 

204 

205 # received by user1 

206 res = api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user1.id, page_size=5)) 

207 assert [ref.reference_id for ref in res.references] == [ref8b, ref9, ref8, ref7, ref6] 

208 

209 res = api.ListReferences( 

210 references_pb2.ListReferencesReq(to_user_id=user1.id, page_token=res.next_page_token, page_size=5) 

211 ) 

212 assert [ref.reference_id for ref in res.references] == [ref5, ref4, ref3, ref2] 

213 assert not res.next_page_token 

214 

215 # same thing but with filters 

216 res = api.ListReferences( 

217 references_pb2.ListReferencesReq( 

218 to_user_id=user1.id, 

219 reference_type_filter=[ 

220 references_pb2.REFERENCE_TYPE_HOSTED, 

221 references_pb2.REFERENCE_TYPE_SURFED, 

222 references_pb2.REFERENCE_TYPE_FRIEND, 

223 ], 

224 page_size=5, 

225 ) 

226 ) 

227 assert [ref.reference_id for ref in res.references] == [ref8b, ref9, ref8, ref7, ref6] 

228 

229 res = api.ListReferences( 

230 references_pb2.ListReferencesReq( 

231 to_user_id=user1.id, 

232 reference_type_filter=[ 

233 references_pb2.REFERENCE_TYPE_HOSTED, 

234 references_pb2.REFERENCE_TYPE_SURFED, 

235 references_pb2.REFERENCE_TYPE_FRIEND, 

236 ], 

237 page_token=res.next_page_token, 

238 page_size=5, 

239 ) 

240 ) 

241 assert [ref.reference_id for ref in res.references] == [ref5, ref4, ref3, ref2] 

242 assert not res.next_page_token 

243 

244 # received hosting references 

245 res = api.ListReferences( 

246 references_pb2.ListReferencesReq( 

247 to_user_id=user1.id, reference_type_filter=[references_pb2.REFERENCE_TYPE_HOSTED], page_size=3 

248 ) 

249 ) 

250 assert [ref.reference_id for ref in res.references] == [ref8b, ref9, ref8] 

251 

252 res = api.ListReferences( 

253 references_pb2.ListReferencesReq( 

254 to_user_id=user1.id, 

255 reference_type_filter=[references_pb2.REFERENCE_TYPE_HOSTED], 

256 page_token=res.next_page_token, 

257 page_size=3, 

258 ) 

259 ) 

260 assert [ref.reference_id for ref in res.references] == [ref5, ref3] 

261 assert not res.next_page_token 

262 

263 # written friend references 

264 res = api.ListReferences( 

265 references_pb2.ListReferencesReq( 

266 from_user_id=user1.id, reference_type_filter=[references_pb2.REFERENCE_TYPE_FRIEND] 

267 ) 

268 ) 

269 assert [ref.reference_id for ref in res.references] == [ref7b, ref4b] 

270 assert not res.next_page_token 

271 

272 # written surfing references 

273 res = api.ListReferences( 

274 references_pb2.ListReferencesReq( 

275 from_user_id=user1.id, reference_type_filter=[references_pb2.REFERENCE_TYPE_SURFED] 

276 ) 

277 ) 

278 assert [ref.reference_id for ref in res.references] == [ref8c, ref5b] 

279 assert not res.next_page_token 

280 

281 with references_session(token7) as api: 

282 # need to set at least one of from or to user 

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

284 api.ListReferences( 

285 references_pb2.ListReferencesReq(reference_type_filter=[references_pb2.REFERENCE_TYPE_SURFED]) 

286 ) 

287 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

288 assert e.value.details() == errors.NEED_TO_SPECIFY_AT_LEAST_ONE_USER 

289 

290 with references_session(token5) as api: 

291 # from user1 to user2 

292 res = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id, to_user_id=user2.id)) 

293 assert [ref.reference_id for ref in res.references] == [ref2b] 

294 assert not res.next_page_token 

295 

296 # from user5 to user1 

297 res = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user5.id, to_user_id=user1.id)) 

298 assert [ref.reference_id for ref in res.references] == [ref5] 

299 assert not res.next_page_token 

300 

301 

302def test_ListReference_banned_deleted_users(db): 

303 user1, token1 = generate_user() 

304 user2, token2 = generate_user() 

305 user3, token3 = generate_user() 

306 

307 with session_scope() as session: 

308 create_friend_reference(session, user2.id, user1.id, timedelta(days=15)) 

309 create_friend_reference(session, user3.id, user1.id, timedelta(days=16)) 

310 create_friend_reference(session, user1.id, user2.id, timedelta(days=15)) 

311 create_friend_reference(session, user1.id, user3.id, timedelta(days=16)) 

312 

313 with references_session(token1) as api: 

314 refs_rec = api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user1.id)).references 

315 refs_sent = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id)).references 

316 assert len(refs_rec) == 2 

317 assert len(refs_sent) == 2 

318 

319 # ban user2 

320 with session_scope() as session: 

321 user2 = session.execute(select(User).where(User.username == user2.username)).scalar_one() 

322 user2.is_banned = True 

323 session.commit() 

324 

325 # reference to and from banned user is hidden 

326 with references_session(token1) as api: 

327 refs_rec = api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user1.id)).references 

328 refs_sent = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id)).references 

329 assert len(refs_rec) == 1 

330 assert len(refs_sent) == 1 

331 

332 # delete user3 

333 with session_scope() as session: 

334 user3 = session.execute(select(User).where(User.username == user3.username)).scalar_one() 

335 user3.is_deleted = True 

336 session.commit() 

337 

338 # doesn't change; references to and from deleted users remain 

339 with references_session(token1) as api: 

340 refs_rec = api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user1.id)).references 

341 refs_sent = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id)).references 

342 assert len(refs_rec) == 1 

343 assert len(refs_sent) == 1 

344 

345 

346def test_WriteFriendReference(db): 

347 user1, token1 = generate_user() 

348 user2, token2 = generate_user() 

349 user3, token3 = generate_user() 

350 

351 with references_session(token1) as api: 

352 # can write normal friend reference 

353 res = api.WriteFriendReference( 

354 references_pb2.WriteFriendReferenceReq( 

355 to_user_id=user2.id, 

356 text="A test reference", 

357 was_appropriate=True, 

358 rating=0.5, 

359 ) 

360 ) 

361 assert res.from_user_id == user1.id 

362 assert res.to_user_id == user2.id 

363 assert res.reference_type == references_pb2.REFERENCE_TYPE_FRIEND 

364 assert res.text == "A test reference" 

365 assert now() - timedelta(hours=24) <= to_aware_datetime(res.written_time) <= now() 

366 assert not res.host_request_id 

367 

368 with references_session(token3) as api: 

369 # check it shows up 

370 res = api.ListReferences( 

371 references_pb2.ListReferencesReq( 

372 from_user_id=user1.id, to_user_id=user2.id, reference_type_filter=[references_pb2.REFERENCE_TYPE_FRIEND] 

373 ) 

374 ) 

375 assert len(res.references) == 1 

376 ref = res.references[0] 

377 assert ref.from_user_id == user1.id 

378 assert ref.to_user_id == user2.id 

379 assert ref.reference_type == references_pb2.REFERENCE_TYPE_FRIEND 

380 assert ref.text == "A test reference" 

381 assert now() - timedelta(hours=24) <= to_aware_datetime(ref.written_time) <= now() 

382 assert not ref.host_request_id 

383 

384 with references_session(token1) as api: 

385 # can't write a second friend reference 

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

387 api.WriteFriendReference( 

388 references_pb2.WriteFriendReferenceReq( 

389 to_user_id=user2.id, 

390 text="A test reference", 

391 was_appropriate=True, 

392 rating=0.5, 

393 ) 

394 ) 

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

396 assert e.value.details() == errors.REFERENCE_ALREADY_GIVEN 

397 

398 with references_session(token2) as api: 

399 # can't write a reference about yourself 

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

401 api.WriteFriendReference( 

402 references_pb2.WriteFriendReferenceReq( 

403 to_user_id=user2.id, 

404 text="I'm really awesome", 

405 was_appropriate=True, 

406 rating=1.0, 

407 ) 

408 ) 

409 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

410 assert e.value.details() == errors.CANT_REFER_SELF 

411 

412 

413def test_WriteFriendReference_with_empty_text(db): 

414 user1, token1 = generate_user() 

415 user2, token2 = generate_user() 

416 

417 with references_session(token1) as api: 

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

419 api.WriteFriendReference( 

420 references_pb2.WriteFriendReferenceReq(to_user_id=user2.id, text=" ", was_appropriate=True, rating=0.8) 

421 ) 

422 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

423 assert e.value.details() == errors.REFERENCE_NO_TEXT 

424 

425 

426def test_WriteFriendReference_with_private_text(db, push_collector): 

427 user1, token1 = generate_user() 

428 user2, token2 = generate_user() 

429 

430 with references_session(token1) as api: 

431 with patch("couchers.email.queue_email") as mock1: 

432 with mock_notification_email() as mock2: 

433 api.WriteFriendReference( 

434 references_pb2.WriteFriendReferenceReq( 

435 to_user_id=user2.id, 

436 text="They were nice!", 

437 was_appropriate=True, 

438 rating=0.6, 

439 private_text="A bit of an odd ball, but a nice person nonetheless.", 

440 ) 

441 ) 

442 

443 # make sure an email was sent to the user receiving the ref as well as the mods 

444 assert mock1.call_count == 1 

445 assert mock2.call_count == 1 

446 e = email_fields(mock2) 

447 assert e.subject == f"[TEST] You've received a friend reference from {user1.name}!" 

448 assert e.recipient == user2.email 

449 

450 push_collector.assert_user_has_single_matching( 

451 user2.id, 

452 title=f"You've received a friend reference from {user1.name}!", 

453 body="They were nice!", 

454 ) 

455 

456 

457def test_host_request_states_references(db): 

458 user1, token1 = generate_user() 

459 user2, token2 = generate_user() 

460 

461 with session_scope() as session: 

462 # can't write ref 

463 hr1 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.pending) 

464 # can write ref 

465 hr2 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.accepted) 

466 # can't write ref 

467 hr3 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.rejected) 

468 # can write ref 

469 hr4 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.confirmed) 

470 # can't write ref 

471 hr5 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.cancelled) 

472 

473 with references_session(token1) as api: 

474 # pending 

475 api.WriteHostRequestReference( 

476 references_pb2.WriteHostRequestReferenceReq( 

477 host_request_id=hr2, 

478 text="Should work!", 

479 was_appropriate=True, 

480 rating=0.9, 

481 ) 

482 ) 

483 

484 # accepted 

485 api.WriteHostRequestReference( 

486 references_pb2.WriteHostRequestReferenceReq( 

487 host_request_id=hr4, 

488 text="Should work!", 

489 was_appropriate=True, 

490 rating=0.9, 

491 ) 

492 ) 

493 

494 # rejected 

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

496 api.WriteHostRequestReference( 

497 references_pb2.WriteHostRequestReferenceReq( 

498 host_request_id=hr1, 

499 text="Shouldn't work...", 

500 was_appropriate=True, 

501 rating=0.9, 

502 ) 

503 ) 

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

505 assert e.value.details() == errors.CANT_WRITE_REFERENCE_FOR_REQUEST 

506 

507 # confirmed 

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

509 api.WriteHostRequestReference( 

510 references_pb2.WriteHostRequestReferenceReq( 

511 host_request_id=hr3, 

512 text="Shouldn't work...", 

513 was_appropriate=True, 

514 rating=0.9, 

515 ) 

516 ) 

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

518 assert e.value.details() == errors.CANT_WRITE_REFERENCE_FOR_REQUEST 

519 

520 # cancelled 

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

522 api.WriteHostRequestReference( 

523 references_pb2.WriteHostRequestReferenceReq( 

524 host_request_id=hr5, 

525 text="Shouldn't work...", 

526 was_appropriate=True, 

527 rating=0.9, 

528 ) 

529 ) 

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

531 assert e.value.details() == errors.CANT_WRITE_REFERENCE_FOR_REQUEST 

532 

533 

534def test_WriteHostRequestReference(db): 

535 user1, token1 = generate_user() 

536 user2, token2 = generate_user() 

537 user3, token3 = generate_user() 

538 

539 with session_scope() as session: 

540 # too old 

541 hr1 = create_host_request(session, user3.id, user1.id, timedelta(days=20)) 

542 # valid host req 

543 hr2 = create_host_request(session, user3.id, user1.id, timedelta(days=10)) 

544 # valid surfing req 

545 hr3 = create_host_request(session, user1.id, user3.id, timedelta(days=7)) 

546 # not yet complete 

547 hr4 = create_host_request(session, user2.id, user1.id, timedelta(days=1), status=HostRequestStatus.pending) 

548 

549 with references_session(token3) as api: 

550 # can write for this one 

551 api.WriteHostRequestReference( 

552 references_pb2.WriteHostRequestReferenceReq( 

553 host_request_id=hr3, 

554 text="Should work!", 

555 was_appropriate=True, 

556 rating=0.9, 

557 ) 

558 ) 

559 

560 with references_session(token1) as api: 

561 # can't write reference for a HR that's not yet finished 

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

563 api.WriteHostRequestReference( 

564 references_pb2.WriteHostRequestReferenceReq( 

565 host_request_id=hr4, 

566 text="Shouldn't work...", 

567 was_appropriate=True, 

568 rating=0.9, 

569 ) 

570 ) 

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

572 assert e.value.details() == errors.CANT_WRITE_REFERENCE_FOR_REQUEST 

573 

574 # can't write reference that's more than 2 weeks old 

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

576 api.WriteHostRequestReference( 

577 references_pb2.WriteHostRequestReferenceReq( 

578 host_request_id=hr1, 

579 text="Shouldn't work...", 

580 was_appropriate=True, 

581 rating=0.9, 

582 ) 

583 ) 

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

585 assert e.value.details() == errors.CANT_WRITE_REFERENCE_FOR_REQUEST 

586 

587 # can write for this one 

588 api.WriteHostRequestReference( 

589 references_pb2.WriteHostRequestReferenceReq( 

590 host_request_id=hr2, 

591 text="Should work!", 

592 was_appropriate=True, 

593 rating=0.9, 

594 ) 

595 ) 

596 

597 # but can't write a second one for the same one 

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

599 api.WriteHostRequestReference( 

600 references_pb2.WriteHostRequestReferenceReq( 

601 host_request_id=hr2, 

602 text="Shouldn't work...", 

603 was_appropriate=True, 

604 rating=0.9, 

605 ) 

606 ) 

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

608 assert e.value.details() == errors.REFERENCE_ALREADY_GIVEN 

609 

610 # can write for this one too 

611 api.WriteHostRequestReference( 

612 references_pb2.WriteHostRequestReferenceReq( 

613 host_request_id=hr3, 

614 text="Should work!", 

615 was_appropriate=True, 

616 rating=0.9, 

617 ) 

618 ) 

619 

620 

621def test_WriteHostRequestReference_private_text(db, push_collector): 

622 user1, token1 = generate_user() 

623 user2, token2 = generate_user() 

624 

625 with session_scope() as session: 

626 hr = create_host_request(session, user1.id, user2.id, timedelta(days=10)) 

627 

628 with references_session(token1) as api: 

629 with patch("couchers.email.queue_email") as mock1: 

630 with mock_notification_email() as mock2: 

631 api.WriteHostRequestReference( 

632 references_pb2.WriteHostRequestReferenceReq( 

633 host_request_id=hr, 

634 text="Should work!", 

635 was_appropriate=True, 

636 rating=0.9, 

637 private_text="Something", 

638 ) 

639 ) 

640 

641 # make sure an email was sent to the user receiving the ref as well as the mods 

642 assert mock1.call_count == 1 

643 assert mock2.call_count == 1 

644 

645 e = email_fields(mock2) 

646 assert e.subject == f"[TEST] You've received a reference from {user1.name}!" 

647 assert e.recipient == user2.email 

648 

649 push_collector.assert_user_has_single_matching( 

650 user2.id, 

651 title=f"You've received a reference from {user1.name}!", 

652 body="Please go and write a reference for them too. It's a nice gesture and helps us build a community together!", 

653 ) 

654 

655 

656def test_AvailableWriteReferences_and_ListPendingReferencesToWrite(db): 

657 user1, token1 = generate_user() 

658 user2, token2 = generate_user() 

659 user3, token3 = generate_user() 

660 user4, token4 = generate_user() 

661 user5, token5 = generate_user(delete_user=True) 

662 user6, token6 = generate_user() 

663 user7, token7 = generate_user() 

664 make_user_block(user1, user6) 

665 make_user_block(user7, user1) 

666 

667 with session_scope() as session: 

668 # too old 

669 hr1 = create_host_request(session, user3.id, user1.id, timedelta(days=20)) 

670 

671 # already wrote friend ref to user3 

672 create_friend_reference(session, user1.id, user3.id, timedelta(days=15, seconds=70)) 

673 

674 # already given 

675 _, hr2 = create_host_reference(session, user2.id, user1.id, timedelta(days=10, seconds=110), surfing=True) 

676 create_host_reference(session, user1.id, user2.id, timedelta(days=10, seconds=100), host_request_id=hr2) 

677 

678 # valid hosted 

679 hr3 = create_host_request(session, user3.id, user1.id, timedelta(days=8)) 

680 

681 # valid surfed 

682 hr4 = create_host_request(session, user1.id, user4.id, timedelta(days=5)) 

683 

684 # not yet complete 

685 hr5 = create_host_request(session, user2.id, user1.id, timedelta(days=2), status=HostRequestStatus.pending) 

686 

687 # already wrote friend ref to user2 

688 create_friend_reference(session, user1.id, user2.id, timedelta(days=1)) 

689 

690 # user5 deleted, reference won't show up as pending 

691 create_host_request(session, user1.id, user5.id, timedelta(days=5)) 

692 

693 # user6 blocked, reference won't show up as pending 

694 create_host_request(session, user1.id, user6.id, timedelta(days=5)) 

695 

696 # user7 blocking, reference won't show up as pending 

697 create_host_request(session, user1.id, user7.id, timedelta(days=5)) 

698 

699 with references_session(token1) as api: 

700 # can't write reference for invisible user 

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

702 api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user5.id)) 

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

704 assert e.value.details() == errors.USER_NOT_FOUND 

705 

706 # can't write reference for blocking user 

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

708 api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user7.id)) 

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

710 assert e.value.details() == errors.USER_NOT_FOUND 

711 

712 # can't write reference for blocked user 

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

714 api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user6.id)) 

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

716 assert e.value.details() == errors.USER_NOT_FOUND 

717 

718 # can't write anything to myself 

719 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id)) 

720 assert not res.can_write_friend_reference 

721 assert len(res.available_write_references) == 0 

722 

723 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id)) 

724 # can't write friend ref to user2 

725 assert not res.can_write_friend_reference 

726 # none we can write for user2 

727 assert len(res.available_write_references) == 0 

728 

729 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user3.id)) 

730 # can't write friend ref to user3 

731 assert not res.can_write_friend_reference 

732 # can write one reference because we hosted user3 

733 assert len(res.available_write_references) == 1 

734 w = res.available_write_references[0] 

735 assert w.host_request_id == hr3 

736 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED 

737 assert now() + timedelta(days=6) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=7) 

738 

739 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user4.id)) 

740 # can write friend ref to user4 

741 assert res.can_write_friend_reference 

742 # can write one reference because we surfed with user4 

743 assert len(res.available_write_references) == 1 

744 w = res.available_write_references[0] 

745 assert w.host_request_id == hr4 

746 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED 

747 assert now() + timedelta(days=9) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=10) 

748 

749 # finally check the general list 

750 res = api.ListPendingReferencesToWrite(empty_pb2.Empty()) 

751 assert len(res.pending_references) == 2 

752 w = res.pending_references[0] 

753 assert w.host_request_id == hr3 

754 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED 

755 assert now() + timedelta(days=6) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=7) 

756 w = res.pending_references[1] 

757 assert w.host_request_id == hr4 

758 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED 

759 assert now() + timedelta(days=9) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=10) 

760 

761 

762@pytest.mark.parametrize("hs", ["host", "surfer"]) 

763def test_regression_disappearing_refs(db, hs): 

764 """ 

765 Roughly the reproduction steps are: 

766 * Send a host request, then have both host and surfer accept 

767 * Wait for it to elapse (or hack it with SQL like what you told me to do) 

768 * On the surfer account, leave a reference 

769 * Then on the host account, the option to leave a reference is then not available 

770 """ 

771 user1, token1 = generate_user() 

772 user2, token2 = generate_user() 

773 req_start = (today() + timedelta(days=2)).isoformat() 

774 req_end = (today() + timedelta(days=3)).isoformat() 

775 with requests_session(token1) as api: 

776 res = api.CreateHostRequest( 

777 requests_pb2.CreateHostRequestReq( 

778 host_user_id=user2.id, from_date=req_start, to_date=req_end, text="Test request" 

779 ) 

780 ) 

781 host_request_id = res.host_request_id 

782 assert ( 

783 api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_sent=True)) 

784 .host_requests[0] 

785 .latest_message.text.text 

786 == "Test request" 

787 ) 

788 

789 with requests_session(token2) as api: 

790 api.RespondHostRequest( 

791 requests_pb2.RespondHostRequestReq( 

792 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED 

793 ) 

794 ) 

795 

796 with requests_session(token1) as api: 

797 api.RespondHostRequest( 

798 requests_pb2.RespondHostRequestReq( 

799 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED 

800 ) 

801 ) 

802 

803 with references_session(token1) as api: 

804 res = api.ListPendingReferencesToWrite(empty_pb2.Empty()) 

805 assert len(res.pending_references) == 0 

806 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id)) 

807 assert len(res.available_write_references) == 0 

808 

809 with references_session(token2) as api: 

810 res = api.ListPendingReferencesToWrite(empty_pb2.Empty()) 

811 assert len(res.pending_references) == 0 

812 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id)) 

813 assert len(res.available_write_references) == 0 

814 

815 # hack the time backwards 

816 hack_req_start = today() - timedelta(days=10) + timedelta(days=2) 

817 hack_req_end = today() - timedelta(days=10) + timedelta(days=3) 

818 with session_scope() as session: 

819 host_request = session.execute(select(HostRequest)).scalar_one() 

820 assert host_request.conversation_id == host_request_id 

821 host_request.from_date = hack_req_start 

822 host_request.to_date = hack_req_end 

823 

824 with references_session(token1) as api: 

825 res = api.ListPendingReferencesToWrite(empty_pb2.Empty()) 

826 assert len(res.pending_references) == 1 

827 assert res.pending_references[0].host_request_id == host_request_id 

828 assert res.pending_references[0].reference_type == references_pb2.REFERENCE_TYPE_SURFED 

829 

830 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id)) 

831 assert len(res.available_write_references) == 1 

832 assert res.available_write_references[0].host_request_id == host_request_id 

833 assert res.available_write_references[0].reference_type == references_pb2.REFERENCE_TYPE_SURFED 

834 

835 with references_session(token2) as api: 

836 res = api.ListPendingReferencesToWrite(empty_pb2.Empty()) 

837 assert len(res.pending_references) == 1 

838 assert res.pending_references[0].host_request_id == host_request_id 

839 assert res.pending_references[0].reference_type == references_pb2.REFERENCE_TYPE_HOSTED 

840 

841 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id)) 

842 assert len(res.available_write_references) == 1 

843 assert res.available_write_references[0].host_request_id == host_request_id 

844 assert res.available_write_references[0].reference_type == references_pb2.REFERENCE_TYPE_HOSTED 

845 

846 if hs == "host": 

847 with references_session(token2) as api: 

848 api.WriteHostRequestReference( 

849 references_pb2.WriteHostRequestReferenceReq( 

850 host_request_id=host_request_id, 

851 text="Good stuff", 

852 was_appropriate=True, 

853 rating=0.86, 

854 ) 

855 ) 

856 

857 with references_session(token2) as api: 

858 res = api.ListPendingReferencesToWrite(empty_pb2.Empty()) 

859 assert len(res.pending_references) == 0 

860 

861 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id)) 

862 assert len(res.available_write_references) == 0 

863 

864 with references_session(token1) as api: 

865 res = api.ListPendingReferencesToWrite(empty_pb2.Empty()) 

866 assert len(res.pending_references) == 1 

867 assert res.pending_references[0].host_request_id == host_request_id 

868 assert res.pending_references[0].reference_type == references_pb2.REFERENCE_TYPE_SURFED 

869 

870 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id)) 

871 assert len(res.available_write_references) == 1 

872 assert res.available_write_references[0].host_request_id == host_request_id 

873 assert res.available_write_references[0].reference_type == references_pb2.REFERENCE_TYPE_SURFED 

874 else: 

875 with references_session(token1) as api: 

876 api.WriteHostRequestReference( 

877 references_pb2.WriteHostRequestReferenceReq( 

878 host_request_id=host_request_id, 

879 text="Good stuff", 

880 was_appropriate=True, 

881 rating=0.86, 

882 ) 

883 ) 

884 

885 with references_session(token1) as api: 

886 res = api.ListPendingReferencesToWrite(empty_pb2.Empty()) 

887 assert len(res.pending_references) == 0 

888 

889 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id)) 

890 assert len(res.available_write_references) == 0 

891 

892 with references_session(token2) as api: 

893 res = api.ListPendingReferencesToWrite(empty_pb2.Empty()) 

894 assert len(res.pending_references) == 1 

895 assert res.pending_references[0].host_request_id == host_request_id 

896 assert res.pending_references[0].reference_type == references_pb2.REFERENCE_TYPE_HOSTED 

897 

898 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id)) 

899 assert len(res.available_write_references) == 1 

900 assert res.available_write_references[0].host_request_id == host_request_id 

901 assert res.available_write_references[0].reference_type == references_pb2.REFERENCE_TYPE_HOSTED