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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

405 statements  

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 generate_user, 

26 make_user_block, 

27 references_session, 

28 requests_session, 

29 testconfig, 

30) 

31 

32 

33@pytest.fixture(autouse=True) 

34def _(testconfig): 

35 pass 

36 

37 

38def create_host_request( 

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

40): 

41 """ 

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

43 """ 

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

45 to_date = today() - host_request_age 

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

47 conversation = Conversation() 

48 session.add(conversation) 

49 session.flush() 

50 session.add( 

51 Message( 

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

53 conversation_id=conversation.id, 

54 author_id=surfer_user_id, 

55 message_type=MessageType.chat_created, 

56 ) 

57 ) 

58 message = Message( 

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

60 conversation_id=conversation.id, 

61 author_id=surfer_user_id, 

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

63 message_type=MessageType.text, 

64 ) 

65 session.add(message) 

66 session.flush() 

67 host_request = HostRequest( 

68 conversation_id=conversation.id, 

69 surfer_user_id=surfer_user_id, 

70 host_user_id=host_user_id, 

71 from_date=from_date, 

72 to_date=to_date, 

73 status=status, 

74 surfer_last_seen_message_id=message.id, 

75 ) 

76 session.add(host_request) 

77 session.commit() 

78 return host_request.conversation_id 

79 

80 

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

82 if host_request_id: 

83 actual_host_request_id = host_request_id 

84 else: 

85 if surfing: 

86 actual_host_request_id = host_request_id or create_host_request( 

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

88 ) 

89 else: 

90 actual_host_request_id = host_request_id or create_host_request( 

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

92 ) 

93 

94 host_request = session.execute( 

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

96 ).scalar_one() 

97 

98 reference = Reference( 

99 time=now() - reference_age, 

100 from_user_id=from_user_id, 

101 host_request_id=host_request.conversation_id, 

102 text="Dummy reference", 

103 rating=0.5, 

104 was_appropriate=True, 

105 ) 

106 

107 if host_request.surfer_user_id == from_user_id: 

108 reference.reference_type = ReferenceType.surfed 

109 reference.to_user_id = host_request.host_user_id 

110 assert from_user_id == host_request.surfer_user_id 

111 else: 

112 reference.reference_type = ReferenceType.hosted 

113 reference.to_user_id = host_request.surfer_user_id 

114 assert from_user_id == host_request.host_user_id 

115 

116 session.add(reference) 

117 session.commit() 

118 return reference.id, actual_host_request_id 

119 

120 

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

122 reference = Reference( 

123 time=now() - reference_age, 

124 from_user_id=from_user_id, 

125 to_user_id=to_user_id, 

126 reference_type=ReferenceType.friend, 

127 text="Test friend request", 

128 rating=0.4, 

129 was_appropriate=True, 

130 ) 

131 session.add(reference) 

132 session.commit() 

133 return reference.id 

134 

135 

136def test_ListPagination(db): 

137 user1, token1 = generate_user() 

138 user2, token2 = generate_user() 

139 user3, token3 = generate_user() 

140 user4, token4 = generate_user() 

141 user5, token5 = generate_user() 

142 user6, token6 = generate_user() 

143 user7, token7 = generate_user() 

144 user8, token8 = generate_user() 

145 user9, token9 = generate_user() 

146 

147 with session_scope() as session: 

148 # bidirectional references 

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

150 ref2b, _ = create_host_reference( 

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

152 ) 

153 

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

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

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

157 

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

159 ref5b, _ = create_host_reference( 

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

161 ) 

162 

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

164 

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

166 

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

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

169 

170 # should be visible even under 2 weeks 

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

172 

173 # hidden because it's less than 2 weeks 

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

175 

176 # visible because both were written 

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

178 ref8c, _ = create_host_reference( 

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

180 ) 

181 

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

183 

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

185 

186 with references_session(token2) as api: 

187 # written by user1 

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

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

190 

191 res = api.ListReferences( 

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

193 ) 

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

195 

196 res = api.ListReferences( 

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

198 ) 

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

200 assert not res.next_page_token 

201 

202 # received by user1 

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

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

205 

206 res = api.ListReferences( 

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

208 ) 

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

210 assert not res.next_page_token 

211 

212 # same thing but with filters 

213 res = api.ListReferences( 

214 references_pb2.ListReferencesReq( 

215 to_user_id=user1.id, 

216 reference_type_filter=[ 

217 references_pb2.REFERENCE_TYPE_HOSTED, 

218 references_pb2.REFERENCE_TYPE_SURFED, 

219 references_pb2.REFERENCE_TYPE_FRIEND, 

220 ], 

221 page_size=5, 

222 ) 

223 ) 

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

225 

226 res = api.ListReferences( 

227 references_pb2.ListReferencesReq( 

228 to_user_id=user1.id, 

229 reference_type_filter=[ 

230 references_pb2.REFERENCE_TYPE_HOSTED, 

231 references_pb2.REFERENCE_TYPE_SURFED, 

232 references_pb2.REFERENCE_TYPE_FRIEND, 

233 ], 

234 page_token=res.next_page_token, 

235 page_size=5, 

236 ) 

237 ) 

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

239 assert not res.next_page_token 

240 

241 # received hosting references 

242 res = api.ListReferences( 

243 references_pb2.ListReferencesReq( 

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

245 ) 

246 ) 

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

248 

249 res = api.ListReferences( 

250 references_pb2.ListReferencesReq( 

251 to_user_id=user1.id, 

252 reference_type_filter=[references_pb2.REFERENCE_TYPE_HOSTED], 

253 page_token=res.next_page_token, 

254 page_size=3, 

255 ) 

256 ) 

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

258 assert not res.next_page_token 

259 

260 # written friend references 

261 res = api.ListReferences( 

262 references_pb2.ListReferencesReq( 

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

264 ) 

265 ) 

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

267 assert not res.next_page_token 

268 

269 # written surfing references 

270 res = api.ListReferences( 

271 references_pb2.ListReferencesReq( 

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

273 ) 

274 ) 

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

276 assert not res.next_page_token 

277 

278 with references_session(token7) as api: 

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

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

281 api.ListReferences( 

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

283 ) 

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

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

286 

287 with references_session(token5) as api: 

288 # from user1 to user2 

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

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

291 assert not res.next_page_token 

292 

293 # from user5 to user1 

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

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

296 assert not res.next_page_token 

297 

298 

299def test_ListReference_banned_deleted_users(db): 

300 user1, token1 = generate_user() 

301 user2, token2 = generate_user() 

302 user3, token3 = generate_user() 

303 

304 with session_scope() as session: 

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

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

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

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

309 

310 with references_session(token1) as api: 

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

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

313 assert len(refs_rec) == 2 

314 assert len(refs_sent) == 2 

315 

316 # ban user2 

317 with session_scope() as session: 

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

319 user2.is_banned = True 

320 session.commit() 

321 

322 # reference to and from banned user is hidden 

323 with references_session(token1) as api: 

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

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

326 assert len(refs_rec) == 1 

327 assert len(refs_sent) == 1 

328 

329 # delete user3 

330 with session_scope() as session: 

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

332 user3.is_deleted = True 

333 session.commit() 

334 

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

336 with references_session(token1) as api: 

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

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

339 assert len(refs_rec) == 1 

340 assert len(refs_sent) == 1 

341 

342 

343def test_WriteFriendReference(db): 

344 user1, token1 = generate_user() 

345 user2, token2 = generate_user() 

346 user3, token3 = generate_user() 

347 

348 with references_session(token1) as api: 

349 # can write normal friend reference 

350 res = api.WriteFriendReference( 

351 references_pb2.WriteFriendReferenceReq( 

352 to_user_id=user2.id, 

353 text="A test reference", 

354 was_appropriate=True, 

355 rating=0.5, 

356 ) 

357 ) 

358 assert res.from_user_id == user1.id 

359 assert res.to_user_id == user2.id 

360 assert res.reference_type == references_pb2.REFERENCE_TYPE_FRIEND 

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

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

363 assert not res.host_request_id 

364 

365 with references_session(token3) as api: 

366 # check it shows up 

367 res = api.ListReferences( 

368 references_pb2.ListReferencesReq( 

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

370 ) 

371 ) 

372 assert len(res.references) == 1 

373 ref = res.references[0] 

374 assert ref.from_user_id == user1.id 

375 assert ref.to_user_id == user2.id 

376 assert ref.reference_type == references_pb2.REFERENCE_TYPE_FRIEND 

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

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

379 assert not ref.host_request_id 

380 

381 with references_session(token1) as api: 

382 # can't write a second friend reference 

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

384 api.WriteFriendReference( 

385 references_pb2.WriteFriendReferenceReq( 

386 to_user_id=user2.id, 

387 text="A test reference", 

388 was_appropriate=True, 

389 rating=0.5, 

390 ) 

391 ) 

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

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

394 

395 with references_session(token2) as api: 

396 # can't write a reference about yourself 

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

398 api.WriteFriendReference( 

399 references_pb2.WriteFriendReferenceReq( 

400 to_user_id=user2.id, 

401 text="I'm really awesome", 

402 was_appropriate=True, 

403 rating=1.0, 

404 ) 

405 ) 

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

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

408 

409 

410def test_WriteFriendReference_with_empty_text(db): 

411 user1, token1 = generate_user() 

412 user2, token2 = generate_user() 

413 

414 with references_session(token1) as api: 

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

416 api.WriteFriendReference( 

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

418 ) 

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

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

421 

422 

423def test_WriteFriendReference_with_private_text(db): 

424 user1, token1 = generate_user() 

425 user2, token2 = generate_user() 

426 

427 with references_session(token1) as api: 

428 with patch("couchers.email.queue_email") as mock: 

429 api.WriteFriendReference( 

430 references_pb2.WriteFriendReferenceReq( 

431 to_user_id=user2.id, 

432 text="They were nice!", 

433 was_appropriate=True, 

434 rating=0.6, 

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

436 ) 

437 ) 

438 

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

440 assert mock.call_count == 2 

441 

442 

443def test_host_request_states_references(db): 

444 user1, token1 = generate_user() 

445 user2, token2 = generate_user() 

446 

447 with session_scope() as session: 

448 # can't write ref 

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

450 # can write ref 

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

452 # can't write ref 

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

454 # can write ref 

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

456 # can't write ref 

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

458 

459 with references_session(token1) as api: 

460 # pending 

461 api.WriteHostRequestReference( 

462 references_pb2.WriteHostRequestReferenceReq( 

463 host_request_id=hr2, 

464 text="Should work!", 

465 was_appropriate=True, 

466 rating=0.9, 

467 ) 

468 ) 

469 

470 # accepted 

471 api.WriteHostRequestReference( 

472 references_pb2.WriteHostRequestReferenceReq( 

473 host_request_id=hr4, 

474 text="Should work!", 

475 was_appropriate=True, 

476 rating=0.9, 

477 ) 

478 ) 

479 

480 # rejected 

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

482 api.WriteHostRequestReference( 

483 references_pb2.WriteHostRequestReferenceReq( 

484 host_request_id=hr1, 

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

486 was_appropriate=True, 

487 rating=0.9, 

488 ) 

489 ) 

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

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

492 

493 # confirmed 

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

495 api.WriteHostRequestReference( 

496 references_pb2.WriteHostRequestReferenceReq( 

497 host_request_id=hr3, 

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

499 was_appropriate=True, 

500 rating=0.9, 

501 ) 

502 ) 

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

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

505 

506 # cancelled 

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

508 api.WriteHostRequestReference( 

509 references_pb2.WriteHostRequestReferenceReq( 

510 host_request_id=hr5, 

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

512 was_appropriate=True, 

513 rating=0.9, 

514 ) 

515 ) 

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

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

518 

519 

520def test_WriteHostRequestReference(db): 

521 user1, token1 = generate_user() 

522 user2, token2 = generate_user() 

523 user3, token3 = generate_user() 

524 

525 with session_scope() as session: 

526 # too old 

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

528 # valid host req 

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

530 # valid surfing req 

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

532 # not yet complete 

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

534 

535 with references_session(token3) as api: 

536 # can write for this one 

537 api.WriteHostRequestReference( 

538 references_pb2.WriteHostRequestReferenceReq( 

539 host_request_id=hr3, 

540 text="Should work!", 

541 was_appropriate=True, 

542 rating=0.9, 

543 ) 

544 ) 

545 

546 with references_session(token1) as api: 

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

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

549 api.WriteHostRequestReference( 

550 references_pb2.WriteHostRequestReferenceReq( 

551 host_request_id=hr4, 

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

553 was_appropriate=True, 

554 rating=0.9, 

555 ) 

556 ) 

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

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

559 

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

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

562 api.WriteHostRequestReference( 

563 references_pb2.WriteHostRequestReferenceReq( 

564 host_request_id=hr1, 

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

566 was_appropriate=True, 

567 rating=0.9, 

568 ) 

569 ) 

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

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

572 

573 # can write for this one 

574 api.WriteHostRequestReference( 

575 references_pb2.WriteHostRequestReferenceReq( 

576 host_request_id=hr2, 

577 text="Should work!", 

578 was_appropriate=True, 

579 rating=0.9, 

580 ) 

581 ) 

582 

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

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

585 api.WriteHostRequestReference( 

586 references_pb2.WriteHostRequestReferenceReq( 

587 host_request_id=hr2, 

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

589 was_appropriate=True, 

590 rating=0.9, 

591 ) 

592 ) 

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

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

595 

596 # can write for this one too 

597 api.WriteHostRequestReference( 

598 references_pb2.WriteHostRequestReferenceReq( 

599 host_request_id=hr3, 

600 text="Should work!", 

601 was_appropriate=True, 

602 rating=0.9, 

603 ) 

604 ) 

605 

606 

607def test_WriteHostRequestReference_private_text(db): 

608 user1, token1 = generate_user() 

609 user2, token2 = generate_user() 

610 

611 with session_scope() as session: 

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

613 

614 with references_session(token1) as api: 

615 with patch("couchers.email.queue_email") as mock: 

616 api.WriteHostRequestReference( 

617 references_pb2.WriteHostRequestReferenceReq( 

618 host_request_id=hr, 

619 text="Should work!", 

620 was_appropriate=True, 

621 rating=0.9, 

622 private_text="Something", 

623 ) 

624 ) 

625 

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

627 assert mock.call_count == 2 

628 

629 

630def test_AvailableWriteReferences_and_ListPendingReferencesToWrite(db): 

631 user1, token1 = generate_user() 

632 user2, token2 = generate_user() 

633 user3, token3 = generate_user() 

634 user4, token4 = generate_user() 

635 user5, token5 = generate_user(delete_user=True) 

636 user6, token6 = generate_user() 

637 user7, token7 = generate_user() 

638 make_user_block(user1, user6) 

639 make_user_block(user7, user1) 

640 

641 with session_scope() as session: 

642 # too old 

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

644 

645 # already wrote friend ref to user3 

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

647 

648 # already given 

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

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

651 

652 # valid hosted 

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

654 

655 # valid surfed 

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

657 

658 # not yet complete 

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

660 

661 # already wrote friend ref to user2 

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

663 

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

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

666 

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

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

669 

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

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

672 

673 with references_session(token1) as api: 

674 # can't write reference for invisible user 

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

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

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

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

679 

680 # can't write reference for blocking user 

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

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

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

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

685 

686 # can't write reference for blocked user 

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

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

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

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

691 

692 # can't write anything to myself 

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

694 assert not res.can_write_friend_reference 

695 assert len(res.available_write_references) == 0 

696 

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

698 # can't write friend ref to user2 

699 assert not res.can_write_friend_reference 

700 # none we can write for user2 

701 assert len(res.available_write_references) == 0 

702 

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

704 # can't write friend ref to user3 

705 assert not res.can_write_friend_reference 

706 # can write one reference because we hosted user3 

707 assert len(res.available_write_references) == 1 

708 w = res.available_write_references[0] 

709 assert w.host_request_id == hr3 

710 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED 

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

712 

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

714 # can write friend ref to user4 

715 assert res.can_write_friend_reference 

716 # can write one reference because we surfed with user4 

717 assert len(res.available_write_references) == 1 

718 w = res.available_write_references[0] 

719 assert w.host_request_id == hr4 

720 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED 

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

722 

723 # finally check the general list 

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

725 assert len(res.pending_references) == 2 

726 w = res.pending_references[0] 

727 assert w.host_request_id == hr3 

728 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED 

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

730 w = res.pending_references[1] 

731 assert w.host_request_id == hr4 

732 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED 

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

734 

735 

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

737def test_regression_disappearing_refs(db, hs): 

738 """ 

739 Roughly the reproduction steps are: 

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

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

742 * On the surfer account, leave a reference 

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

744 """ 

745 user1, token1 = generate_user() 

746 user2, token2 = generate_user() 

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

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

749 with requests_session(token1) as api: 

750 res = api.CreateHostRequest( 

751 requests_pb2.CreateHostRequestReq( 

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

753 ) 

754 ) 

755 host_request_id = res.host_request_id 

756 assert ( 

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

758 .host_requests[0] 

759 .latest_message.text.text 

760 == "Test request" 

761 ) 

762 

763 with requests_session(token2) as api: 

764 api.RespondHostRequest( 

765 requests_pb2.RespondHostRequestReq( 

766 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED 

767 ) 

768 ) 

769 

770 with requests_session(token1) as api: 

771 api.RespondHostRequest( 

772 requests_pb2.RespondHostRequestReq( 

773 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED 

774 ) 

775 ) 

776 

777 with references_session(token1) as api: 

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

779 assert len(res.pending_references) == 0 

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

781 assert len(res.available_write_references) == 0 

782 

783 with references_session(token2) as api: 

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

785 assert len(res.pending_references) == 0 

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

787 assert len(res.available_write_references) == 0 

788 

789 # hack the time backwards 

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

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

792 with session_scope() as session: 

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

794 assert host_request.conversation_id == host_request_id 

795 host_request.from_date = hack_req_start 

796 host_request.to_date = hack_req_end 

797 

798 with references_session(token1) as api: 

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

800 assert len(res.pending_references) == 1 

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

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

803 

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

805 assert len(res.available_write_references) == 1 

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

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

808 

809 with references_session(token2) as api: 

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

811 assert len(res.pending_references) == 1 

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

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

814 

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

816 assert len(res.available_write_references) == 1 

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

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

819 

820 if hs == "host": 

821 with references_session(token2) as api: 

822 api.WriteHostRequestReference( 

823 references_pb2.WriteHostRequestReferenceReq( 

824 host_request_id=host_request_id, 

825 text="Good stuff", 

826 was_appropriate=True, 

827 rating=0.86, 

828 ) 

829 ) 

830 

831 with references_session(token2) as api: 

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

833 assert len(res.pending_references) == 0 

834 

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

836 assert len(res.available_write_references) == 0 

837 

838 with references_session(token1) as api: 

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

840 assert len(res.pending_references) == 1 

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

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

843 

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

845 assert len(res.available_write_references) == 1 

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

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

848 else: 

849 with references_session(token1) as api: 

850 api.WriteHostRequestReference( 

851 references_pb2.WriteHostRequestReferenceReq( 

852 host_request_id=host_request_id, 

853 text="Good stuff", 

854 was_appropriate=True, 

855 rating=0.86, 

856 ) 

857 ) 

858 

859 with references_session(token1) as api: 

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

861 assert len(res.pending_references) == 0 

862 

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

864 assert len(res.available_write_references) == 0 

865 

866 with references_session(token2) as api: 

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

868 assert len(res.pending_references) == 1 

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

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

871 

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

873 assert len(res.available_write_references) == 1 

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

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